home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 348.lha / chatterbox_v1.0 / sources / player.c < prev    next >
C/C++ Source or Header  |  1990-02-14  |  12KB  |  450 lines

  1. /* player - sound playing portion of karl's smus player */
  2. /* hacked to be an effects player */
  3.  
  4.  
  5. /* note - "channels", throughout the code below, are a structure I have
  6.  * invented, a logical channel sort of thing that is useful for what
  7.  * I'm trying to do.  There is not a 1-1 correspondence between these
  8.  * channels and the hardware channels, which hardware channel is used
  9.  * for a given channel is determined by audio.device when we make a
  10.  * request to allocate a channel.
  11.  */
  12.  
  13. #include <exec/types.h>
  14. #include <functions.h>
  15. #include <exec/memory.h>
  16. #include <devices/audio.h>
  17. #include <stdio.h>
  18.  
  19. #include "assert.h"
  20. #include "8svx.h"
  21.  
  22. #include "sample.h"
  23. #include "channels.h"
  24.  
  25. #define YES 1
  26. #define NO 0
  27.  
  28. short audio_up = NO, audio_down = NO;
  29.  
  30. extern int debug;
  31.  
  32. #ifdef DEBUG
  33. #define WHERE fprintf(stderr,"%s %s %d\n",__FILE__,__FUNC__,__LINE__); fflush(stderr);
  34. #define WHAT(x) fprintf(stderr,"%s %s %d %s\n",__FILE__,__FUNC__,__LINE__,x->name);
  35. #else
  36. #define WHERE
  37. #define WHAT(x)
  38. #endif
  39.  
  40. #define CLOCKFREQ 3579545
  41.  
  42. /* define the channel array - this is the top level of data for an audio
  43.  channel */
  44. Channel Channels[MAX_CHANNELS];
  45.  
  46. struct Device *AudioDevice;
  47. struct MsgPort *sfx_port;
  48. struct SMUS_IOAudio *openIOB;
  49.  
  50. short MasterVolume = 127;
  51.  
  52. /* allocation maps for requesting channel assignments from audio.device:
  53.  * I use LeftMap and RightMap to insist on a stereo side - if it can't
  54.  * allocate (at play time) that side it will blow off the note.
  55.  * I use TryLeftMap and TryRightMap to indicate a preference for a
  56.  * stereo side, but if I can't have a channel on that side, it'll
  57.  * give me one from the other side if one is available there
  58.  */
  59. static UBYTE LeftMap[] = {LEFT0, LEFT1};
  60. static UBYTE RightMap[] = {RIGHT0, RIGHT1};
  61. static UBYTE TryLeftMap[] = {LEFT1, LEFT0, RIGHT1, RIGHT0};
  62. static UBYTE TryRightMap[] = {RIGHT1, RIGHT0, LEFT1, LEFT0};
  63.  
  64. /* SMUS_IOBallocate - allocates a SMUS player IOB
  65.  * It shouldn't panic when the allocate fails, and will be eventually
  66.  * made to back out properly.
  67.  * Not much to the routine at all...
  68.  */
  69. struct SMUS_IOAudio *SMUS_IOBallocate()
  70. {
  71.     struct SMUS_IOAudio *IOB;
  72.     extern UBYTE *MyAllocMem();
  73.  
  74.     if ((IOB = (struct SMUS_IOAudio *)MyAllocMem(sizeof(struct SMUS_IOAudio), (long)MEMF_PUBLIC | MEMF_CLEAR)) == (struct SMUS_IOAudio *) NULL)
  75.         panic("SMUS_IOBallocate failed"); 
  76.  
  77.     return(IOB);
  78. }
  79.  
  80. /* free an IOB created by SMUS_IOBallocate */
  81. SMUS_IOBfree(IOBp)
  82. struct SMUS_IOAudio *IOBp;
  83. {
  84.     if (IOBp != NULL)
  85.         FreeMem(IOBp, sizeof(struct SMUS_IOAudio));
  86. }
  87.  
  88. /* Init the IOB for the oneshot portion of the sound */
  89. SMUS_InitShotIOB(channel)
  90. int channel;
  91. {
  92.     struct SMUS_IOAudio *IOB = Channels[channel].IOBs[ONESHOT_IOB];
  93.  
  94.     IOB->smusIOB.ioa_Request.io_Command = CMD_WRITE;
  95.     IOB->smusIOB.ioa_Request.io_Flags = ADIOF_PERVOL;
  96.  
  97.     /* NEEDS WORK - needs to dynamically set volume based on volume
  98.      * commands we receive from the track, and the overall volume
  99.      * from the song structure */
  100.     IOB->smusIOB.ioa_Volume = 127;
  101.  
  102.     /* cycles to repeat is 1, this is a oneshot sound */
  103.     IOB->smusIOB.ioa_Cycles = 1;
  104. }
  105.  
  106. /* Init an IOB that will stop the playing sound */
  107. SMUS_InitStopIOB(channel)
  108. int channel;
  109. {
  110.     struct SMUS_IOAudio *IOB = Channels[channel].IOBs[STOP_IOB];
  111.  
  112.     /* I switched from ADCMD_FINISH to CMD_FLUSH because I
  113.      * can have up to three things queued on a channel
  114.      * and I want to kill them all - ADCMD_FINISH would only
  115.      * kill the one in progress
  116.      * IOB->smusIOB.ioa_Request.io_Command = ADCMD_FINISH;
  117.      */
  118.     IOB->smusIOB.ioa_Request.io_Command = CMD_FLUSH;
  119.     IOB->smusIOB.ioa_Request.io_Flags = IOF_QUICK;
  120. }
  121.  
  122.  
  123. /* Init an IOB that will allocate a channel */
  124. SMUS_InitAllocIOB(channel)
  125. int channel;
  126. {
  127.     struct SMUS_IOAudio *allocIOB = Channels[channel].IOBs[ALLOC_IOB];
  128.  
  129.     static BYTE attack_precedences[] = {50, 40, 30, 20};
  130.  
  131.     allocIOB->smusIOB.ioa_Request.io_Command = ADCMD_ALLOCATE;
  132.  
  133.     allocIOB->smusIOB.ioa_Request.io_Message.mn_Node.ln_Pri = attack_precedences[channel];
  134.  
  135.     /* The IFF SMUS spec says priority is to be highest for track 1 
  136.      * and descending with progressively higher tracks.
  137.      *
  138.      * The audio.device doc in the ROM Kernel Manual says that
  139.      * higher priorities should be given for the attack portions
  140.      * of each note and that priorities for notes in a music
  141.      * program range from -50 (lowest) to 50 (highest)
  142.      *
  143.      * Consequently, since I'm orienting it toward four tracks on the
  144.      * Amiga, and this code is totally Amiga dependent anyway, I
  145.      * am assigning attack priorities of 50 for the first track, and
  146.      * each successive track minus 10, with the sustain portion of
  147.      * the note having its precedence lowered effectively by 50
  148.      *
  149.      */
  150.  
  151.     /* for now, channel 1 and 3 prefer the left channel, while 2 and 4
  152.      * prefer the right
  153.      */
  154.     allocIOB->smusIOB.ioa_Data = (channel) ? TryLeftMap : TryRightMap;
  155.     allocIOB->smusIOB.ioa_Length = 4;
  156.  
  157.     allocIOB->smusIOB.ioa_Request.io_Flags = (ADIOF_NOWAIT | IOF_QUICK);
  158. }
  159.  
  160. /* Init an IOB that will deallocate the channel */
  161. SMUS_InitDeallocIOB(channel)
  162. int channel;
  163. {
  164.     struct SMUS_IOAudio *IOB = Channels[channel].IOBs[FREE_IOB];
  165.  
  166.     IOB->smusIOB.ioa_Request.io_Command = ADCMD_FREE;
  167.     IOB->smusIOB.ioa_Request.io_Flags = IOF_QUICK;
  168. }
  169.  
  170. /* init a channel
  171.  *
  172.  * this guy takes care of allocating and initting all the different IOBs 
  173.  */
  174. InitChannel(channelindex)
  175. int channelindex;
  176. {
  177.     int i;
  178.     struct SMUS_IOAudio *IOB;
  179.     Channel *channelp = &Channels[channelindex];
  180.  
  181.     /* for all the IOBs,
  182.      *   allocate the IOB and set the channel's IOB pointer to it
  183.      *   set the channel pointer field that I tack onto the end
  184.      *     of IOBs to point back to the channel (so that we can 
  185.      *     tell to what channel an IOB we receive on a message 
  186.      *     port belongs, if we need to)
  187.      */
  188.     for (i = 0; i < MAX_CHANNEL_IOBS; i++)
  189.     {
  190.         IOB = channelp->IOBs[i] = SMUS_IOBallocate();
  191.  
  192.         IOB->channelp = channelp;    /* set pointer to IOB's Channel parent */
  193.  
  194.         /* set replyport and device */
  195.         /* see someday if having the reply port is really neccessary */
  196.         IOB->smusIOB.ioa_Request.io_Message.mn_ReplyPort = sfx_port;
  197.  
  198.         /* set the device in the request to the audio device */
  199.         IOB->smusIOB.ioa_Request.io_Device = AudioDevice;
  200.  
  201.         /* let audio.device assign the alloc keys first time through */
  202.         IOB->smusIOB.ioa_AllocKey = 0;
  203.     }
  204.  
  205.     /* initialize the different kinds of IOBs we use */
  206.     SMUS_InitShotIOB(channelindex);
  207.     SMUS_InitStopIOB(channelindex);
  208.     SMUS_InitAllocIOB(channelindex);
  209.     SMUS_InitDeallocIOB(channelindex);
  210.  
  211.     channelp->samptr = NULL;
  212. }
  213.  
  214. InitAudio()
  215. {
  216.     int EndAudio();
  217.     int i;
  218.  
  219.     openIOB = NULL;
  220.     AudioDevice = NULL;
  221.  
  222.     add_cleanup(EndAudio);
  223.  
  224.     openIOB = SMUS_IOBallocate();
  225.  
  226.     if (OpenDevice(AUDIONAME, 0, &openIOB->smusIOB, 0) != 0)
  227.         panic("can't open audio device");
  228.  
  229.     AudioDevice = openIOB->smusIOB.ioa_Request.io_Device;
  230.  
  231.     if ((sfx_port = CreatePort((char *)NULL, 0)) == (struct MsgPort *)NULL)
  232.         panic("can't create sfx_port");
  233.  
  234.     for (i = 0; i < MAX_CHANNELS; i++)
  235.         InitChannel(i);
  236.  
  237.     audio_up = YES;
  238. }
  239.  
  240. EndAudio()
  241. {
  242.     int i, j;
  243.     Channel *channelp;
  244.  
  245. #ifdef DEBUG
  246.     fprintf(stderr,"endaudio cleanup routine executing\n");
  247. #endif
  248.     if(audio_down)
  249.         return;
  250.     audio_down = YES;
  251.  
  252.     if (audio_up)
  253.     {
  254.         for (i = 0; i < MAX_CHANNELS; i++)
  255.             StopChannelNote(&Channels[i]);
  256.     }
  257.     audio_up = NO;
  258.  
  259.     if (sfx_port)
  260.         DeletePort(sfx_port,sizeof(struct MsgPort));
  261.  
  262.     for (i = 0; i < MAX_CHANNELS; i++)
  263.     {
  264.         channelp = &Channels[i];
  265.         for (j = 0; j < MAX_CHANNEL_IOBS; j++) 
  266.         {
  267.             SMUS_IOBfree(channelp->IOBs[j]);
  268.             channelp->IOBs[j] = NULL;
  269.         }
  270.     }
  271.     if(openIOB) 
  272.     {
  273.         if(AudioDevice) 
  274.         {
  275.             CloseDevice(openIOB);
  276.             AudioDevice = NULL;
  277.         }
  278.         SMUS_IOBfree(openIOB);
  279.         openIOB = NULL;
  280.     }
  281. #ifdef DEBUG
  282.     fprintf(stderr,"endaudio cleanup routine complete\n");
  283. #endif
  284. }
  285.  
  286. /* the guts of note playing, right here */
  287.  
  288. PlayChannelSample(samptr,velocity,stereo_side)
  289. Sample *samptr;
  290. int velocity;
  291. int stereo_side;
  292. {
  293.     int i;
  294.     struct SMUS_IOAudio *iobptr;
  295.     Channel *channelp;
  296.     struct OctaveStruct *octave_ptr;
  297.     int octave, noctaves;
  298.     short shift_up_n_octaves = 0;
  299.     short shift_down_n_octaves = 0;
  300.     short period;
  301.     short volume;
  302.     int channel = -1;
  303.     struct SMUS_IOAudio *allocIOB;
  304.     struct SMUS_IOAudio *shotIOB;
  305.  
  306.     /* make relatively certain samptr points to a good sample */
  307.     ASSERT_SAMPLE_MAGIC(samptr);
  308.  
  309.     /* gimme one of my logical channels */
  310.     for (channel = 0; channel < 4; channel++)
  311.     {
  312.         channelp = &Channels[channel];
  313.         if (!(channelp->flags & (LOOP_PLAYING | SHOT_PLAYING)))
  314.             goto got_channel;
  315.     }
  316.  
  317. #ifdef DEBUG
  318.         printf("failed to get a channel, they're all in use\n");
  319. #endif
  320.     return;
  321.  
  322.     got_channel:
  323.     /* cons up some convenient pointers */
  324.     /* these assignments could be moved to where they're needed to prevent
  325.      * their being assigned but unused because of alloc failure,
  326.      * no need for oneshot, etc. */
  327.     allocIOB = channelp->IOBs[ALLOC_IOB];
  328.     shotIOB = channelp->IOBs[ONESHOT_IOB];
  329.  
  330.  
  331.     /* zero the alloc key so we'll get a fresh one - it's the only way
  332.      * to be sure we won't conflict with someone else */
  333.     /* allocIOB->smusIOB.ioa_AllocKey = 0; */
  334.     /* I think we're OK if we alloc it once */
  335.     allocIOB->smusIOB.ioa_Data = (stereo_side) ? TryLeftMap : TryRightMap;
  336.     BeginIO(&allocIOB->smusIOB);
  337.  
  338.     /* the only possible error return is ADIOERR_NOALLOCATION */
  339.     if (allocIOB->smusIOB.ioa_Request.io_Error)
  340.     {
  341.         fprintf(stderr,"alloc failed on channel %d\n",channel);
  342.         return;
  343.     }
  344. #ifdef DEBUG
  345.     fprintf(stderr,"channel %d allockey %d\n",channel,allocIOB->smusIOB.ioa_AllocKey);
  346. #endif
  347.  
  348.     channelp->samptr = samptr;
  349.  
  350.     /* copy the Unit and AllocKey into the other IOBs 
  351.      * This is not the coolest way to go.
  352.      * It doesn't need to copy it to all of them.
  353.      * Perhaps it should only copy it into the channel and
  354.      * the other routines would get it out of the channel and
  355.      * shove it into the IOB - I don't know, maybe later
  356.      * also, AllocKey need only be propogated once
  357.      */
  358.     for (i = 0; i < MAX_CHANNEL_IOBS; i++)
  359.     {
  360.         iobptr = Channels[channel].IOBs[i];
  361.         iobptr->smusIOB.ioa_Request.io_Unit = allocIOB->smusIOB.ioa_Request.io_Unit;
  362.         iobptr->smusIOB.ioa_AllocKey = allocIOB->smusIOB.ioa_AllocKey;
  363.     }
  364.  
  365.  
  366.     assert(samptr->sampleheader.samplesPerSec != 0);
  367.     shotIOB->smusIOB.ioa_Period = (CLOCKFREQ / samptr->sampleheader.samplesPerSec);
  368.  
  369.     if (make_sample_chip(samptr))
  370.     {
  371.         shotIOB->smusIOB.ioa_Data = (UBYTE *)samptr->sampledata;
  372.         shotIOB->smusIOB.ioa_Length = (ULONG)samptr->samplebytes;
  373.         shotIOB->smusIOB.ioa_Volume = 64;
  374.         shotIOB->smusIOB.ioa_Cycles = 1;
  375.  
  376.         BeginIO(&shotIOB->smusIOB);
  377.         /* possible errors are IOERR_ABORTED and ADIOERR_NOALLOCATION */
  378.         if (shotIOB->smusIOB.ioa_Request.io_Error)
  379.             return;
  380.         channelp->flags |= SHOT_PLAYING;
  381. #ifdef DEBUG
  382.         fprintf(stderr,"queued shot on channel %d\n",channel);
  383. #endif
  384.     }
  385.     else
  386.     {
  387.         fprintf(stderr,"chatterbox: chip RAM allocation failed\n");
  388.     }
  389. }
  390.  
  391. /* stop playing the channel if it's playing (we may not have been able to
  392.  * allocate a channel you know) and deallocate it 
  393.  */
  394. /* memo to self:  you should always stop all notes that stop in a 
  395.  * certain time interval prior to starting any new ones 
  396.  */
  397. StopChannelNote(channelp)
  398. Channel *channelp;
  399. {
  400.  
  401.     struct SMUS_IOAudio *stopIOB = channelp->IOBs[STOP_IOB];
  402.     struct SMUS_IOAudio *freeIOB= channelp->IOBs[FREE_IOB];
  403.  
  404.     if (channelp->flags & (LOOP_PLAYING | SHOT_PLAYING))
  405.     {
  406.         BeginIO(&stopIOB->smusIOB);
  407.         /* only error return is ADIOERR_NOALLOCATION */
  408.  
  409.         if (channelp->flags & SHOT_PLAYING)
  410.         {
  411. #ifdef DEBUG
  412.             fprintf(stderr,"SHOT playing\n");
  413. #endif
  414.             WaitIO(&channelp->IOBs[ONESHOT_IOB]->smusIOB);
  415.         }
  416.         if (channelp->flags & LOOP_PLAYING)
  417.         {
  418. #ifdef DEBUG
  419.             fprintf(stderr,"LOOP wait\n");
  420. #endif
  421.             WaitIO(&channelp->IOBs[LOOP_IOB]->smusIOB);
  422.         /* error returns can be IOERR_ABORTED and ADIOERR_NOALLOCATION */
  423.         }
  424.  
  425.         /* there's no point in waiting on precedence, it's synchronous */
  426.         channelp->flags &= ~(SHOT_PLAYING | LOOP_PLAYING);
  427.  
  428. #ifdef DEBUG
  429.     fprintf(stderr,"free channel %d, alloc key %d\n",channel,freeIOB->smusIOB.ioa_AllocKey);
  430. #endif
  431.         BeginIO(&freeIOB->smusIOB);
  432.         /* only possible error return is ADIOERR_NOALLOCATION */
  433. #ifndef NOAUDIT
  434.         if (freeIOB->smusIOB.ioa_Request.io_Error)
  435.             fprintf(stderr,"dealloc i/o error\n");
  436. #endif
  437.  
  438.         free_sample_chip(channelp->samptr);
  439.     }
  440. }
  441. /* I have forgone magic numbers on channel structures because of the static
  442.  * nature of the channels and their small number (4)
  443.  */
  444. struct SMUS_IOAudio *SMUS_GetMsg()
  445. {
  446.     return((struct SMUS_IOAudio *)GetMsg(sfx_port));
  447. }
  448.  
  449. /* end of player.c */
  450.