home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol2 / vi-1 / interplay.c < prev    next >
C/C++ Source or Header  |  1996-01-30  |  23KB  |  670 lines

  1. ;/* interplay.c -- execute me to compile me
  2. sc DATA=NEAR NMINC STRMERGE NOSTKCHK IGNORE=73 interplay.c
  3. slink FROM LIB:c.o + interplay.o TO interplay LIBRARY lib:sc.lib+lib:amiga.lib
  4. quit
  5. */
  6.  
  7.  
  8. /* (c)  Copyright 1993 Commodore-Amiga, Inc.   All rights reserved. */
  9. /* The information contained herein is subject to change without    */
  10. /* notice, and is provided "as is" without warranty of any kind,    */
  11. /* either expressed or implied.  The entire risk as to the use of   */
  12. /* this information is assumed by the user.                         */
  13.  
  14.  
  15. /* Interlay.c - Run from Shell (CLI) only.  Given two file names of IFF
  16. ** 8SVX 8-bit sampled audio data, plays the data from both files using just
  17. ** one channel.  This demonstrates how virtual audio channels can be
  18. ** implemented.
  19. **
  20. ** The program supports two different methods for virtual voices.  Method 1
  21. ** (the default method) interleaves bytes from each file so that the data words
  22. ** fed into the Amiga's audio hardware contain one byte each from the given
  23. ** files.  The samples are then played back at twice their normal speed.  Since
  24. ** each sample only gets half of the playback bandwidth, the speed sounds
  25. ** correct.  To the listener, it sounds as if both samples are playing
  26. ** simultaneously even though only one channel is used.
  27. **
  28. ** Normally the maximum playback rate with the Amiga's audio hardware is about
  29. ** 28K bytes/sec.  Since interleaving requires doubling the nominal sampling
  30. ** rate, it will only work with audio data created at a sampling rate of 14K
  31. ** bytes/sec or less.
  32. **
  33. ** Method 2, takes one byte from each file, sums them and divides by two.
  34. ** The resulting byte value is sent to the Amiga's audio hardware.  No speed
  35. ** increase is required for this technique, however some noise  is introduced
  36. ** by the averaging of the byte values.  To use method 2, inlcude the SUM
  37. ** keyword as the last argument typed on the command line.  Examples:
  38. **
  39. **    interplay talk.8svx music.8svx SUM  (Uses method 2, averaging)
  40. **    interplay talk.8svx music.8svx      (Uses method 1, interleaving)
  41. **    interplay talk.8svx                 (Normal single file 8SVX playback)
  42. **
  43. ** For an example of conventional IFF 8SVX audio see the "Amiga ROM Kernel
  44. ** Reference Manual: Devices", 3rd edition (ISBN 0-201-56775-X), page 28 and
  45. ** page 515.
  46. */
  47.  
  48.  
  49. #include <exec/types.h>
  50. #include <exec/devices.h>
  51. #include <exec/memory.h>
  52. #include <devices/audio.h>
  53. #include <dos/dos.h>
  54.  
  55. #include <iff/iff.h>
  56. #include <iff/8svx.h>
  57.  
  58. #include <clib/exec_protos.h>
  59. #include <clib/dos_protos.h>
  60. #include <clib/alib_protos.h>
  61.  
  62. #include <stdio.h>
  63. #include <string.h>
  64. #include <dos.h>      /* This is the dos.h file from SAS/C not Commodore */
  65.  
  66. #ifdef SASC
  67. int CXBRK(VOID)    { return(0); };
  68. int chkabort(VOID) { return(0); };
  69. #endif
  70.  
  71.  
  72. #define BUF_SIZE 1024
  73.  
  74.  
  75. /* Prototypes for functions defined in this program */
  76. struct IOAudio *SiezeChannel( VOID );
  77. VOID            ReleaseChannel( struct IOAudio * );
  78. char           *Parse8svx(char *, struct InterPlay * );
  79. VOID            EndParse( struct InterPlay * );
  80. VOID            FillAudio(struct InterPlay *, struct IOAudio * );
  81.  
  82.  
  83.  
  84. struct InterPlay                       /* This is the main structure used for */
  85.     {                                  /*  storage of playback state info.    */
  86.     ULONG             sample_done;     /* 0=Keep playing, 1=all done playing. */
  87.     UBYTE            *sample_byte;     /* Pointer for going through the data. */
  88.     UBYTE            *sample_loc;      /* Start of 8SVX BODY data in memory.  */
  89.     ULONG             sample_size;     /*  and total size of file for freeing.*/
  90.     struct InterPlay *next_iplay;      /* Link to second data set. NULL means */
  91.                                        /*  no second file name was given.     */
  92.     LONG              offsetBody;      /* Offset into the file of BODY Chunk. */
  93.     UWORD             sample_speed;    /* Value for audio period register.    */
  94.     BOOL              USE_SUMMING;     /* TRUE means use averaging,           */
  95.     };                                 /*  FALSE means use interleaving.      */
  96.  
  97. /* Version string for AmigaDOS VERSION command. */
  98. UBYTE versiontag[] = "$VER: Interplay 1.0 (2.2.93)";
  99.  
  100.  
  101. /*-----------------------------------------
  102. **
  103. **          main()
  104. **
  105. **-----------------------------------------
  106. */
  107.  
  108. VOID main(int argc, char **argv)
  109. {
  110. struct InterPlay  mainplay,otherplay;   /* Two instances of the InterPlay    */
  111.                                         /*  structure, one for each file.    */
  112. struct IOAudio *pIOA_1=NULL,
  113.                *pIOA_2=NULL,            /* Two IOAudio pointers, plus one    */
  114.                *pIOA  =NULL;            /*  for switching back and forth     */
  115. struct MsgPort *mport1=NULL,            /*  during double-buffering.         */
  116.                *mport2=NULL,            /* Two MsgPort pointers, plus one    */
  117.                *mport =NULL;            /*  for switching back and forth.    */
  118.  
  119. struct Message *msg;                    /* For the GetMsg() call.            */
  120.  
  121. LONG aswitch = 0L;                      /* Double-buffering logical switch.  */
  122.  
  123. static BYTE chip playbuffer1[BUF_SIZE]; /* Two buffers, one for each IOAudio */
  124. static BYTE chip playbuffer2[BUF_SIZE]; /*  request.  Play out of one while  */
  125.                                         /*  the other is being set up.       */
  126. char *errormsg;     /* For error returns */
  127. ULONG wakemask=0L;  /* For Wait() call   */
  128.  
  129.  
  130. /* Give an AmigaDOS style help message */
  131. if( (argc == 2) && !strcmp(argv[1],"?\0") )
  132.     printf("8SVX-FILES/M,SUM/S\n");
  133.  
  134. else if(argc>=2) /* OK got at least one argument. */
  135.     {
  136.     /* Get an audio channel at the highest priority */
  137.     if( pIOA_1=SiezeChannel() )
  138.       {
  139.       mport1 = pIOA_1->ioa_Request.io_Message.mn_ReplyPort;
  140.       pIOA_1->ioa_Data = playbuffer1;
  141.  
  142.       /* Get a 2nd MsgPort and 2nd IOAudio structure for double-buffering */
  143.       pIOA_2 = AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR );
  144.       mport2 = CreatePort(0,0);
  145.       if( pIOA_2 && mport2 )
  146.           {
  147.           /* The 2 IOAudio requests should be initialized the same */
  148.           /* except for the buffer and the reply port they use.    */
  149.           *pIOA_2 = *pIOA_1;
  150.           pIOA_2->ioa_Request.io_Message.mn_ReplyPort = mport2;
  151.           pIOA_2->ioa_Data = playbuffer2;
  152.  
  153.           /* Default is to use interleaving, not averaging */
  154.           mainplay.USE_SUMMING = FALSE;
  155.  
  156.           /* Parse the 8SVX file and fill in the InterPlay structure */
  157.           errormsg = Parse8svx( argv[1] , &mainplay );
  158.  
  159.           /* If a second file name was given by the user then this is */
  160.           /* an interleave request, so parse the 2nd 8SVX file.       */
  161.           if( argc>=3 && !errormsg )
  162.               {
  163.               errormsg = Parse8svx( argv[2] , &otherplay );
  164.               mainplay.next_iplay = &otherplay;
  165.  
  166.               /* If the SUM keyword was given in the command line, set the */
  167.               /* SUMMING flag so that averaging, not interleaving, is used.*/
  168.               if( (argc == 4) &&
  169.                   ( !strcmp(argv[3],"SUM\0") || !strcmp(argv[3],"sum\0") )  )
  170.                   mainplay.USE_SUMMING = TRUE;
  171.               }
  172.           else
  173.               otherplay.sample_done = 1;
  174.  
  175.           if(!errormsg) /* File names given parsed OK? */
  176.               {
  177.               /* Fill up the buffer for the first request. */
  178.               FillAudio( &mainplay, pIOA_1);
  179.  
  180.               /* Is there enough data to double-buffer ? */
  181.               if(!mainplay.sample_done || !otherplay.sample_done)
  182.                   {
  183.                   /* OK, enough data to double-buffer; fill up 2nd request */
  184.                   FillAudio( &mainplay, pIOA_2 );
  185.                   BeginIO((struct IORequest *) pIOA_1 );
  186.                   BeginIO((struct IORequest *) pIOA_2 );
  187.  
  188.                   /* Initial state of double-buffering variables */
  189.                   aswitch=0; pIOA=pIOA_2; mport=mport1;
  190.  
  191.                   /*---------------------*/
  192.                   /*  M A I N   L O O P  */
  193.                   /*---------------------*/
  194.                   while(!mainplay.sample_done || !otherplay.sample_done)
  195.                       {
  196.                       wakemask=Wait( (1 << mport->mp_SigBit) |
  197.                                                SIGBREAKF_CTRL_C  );
  198.  
  199.                       if( wakemask & SIGBREAKF_CTRL_C )
  200.                           {
  201.                           otherplay.sample_done = 1;
  202.                           mainplay.sample_done = 1;
  203.                           }
  204.  
  205.                       while((msg=GetMsg(mport))==NULL){}
  206.  
  207.                       /* Toggle double-buffering variables */
  208.                       if  (aswitch) {aswitch=0;pIOA=pIOA_2;mport=mport1;}
  209.                       else          {aswitch=1;pIOA=pIOA_1;mport=mport2;}
  210.  
  211.                       FillAudio( &mainplay, pIOA );
  212.                       BeginIO((struct IORequest *) pIOA );
  213.                       }
  214.  
  215.                   wakemask=Wait( 1 << mport->mp_SigBit );
  216.                   while((msg=GetMsg(mport))==NULL){}
  217.  
  218.                   if  (aswitch) {aswitch=0;pIOA=pIOA_2;mport=mport1;}
  219.                   else          {aswitch=1;pIOA=pIOA_1;mport=mport2;}
  220.  
  221.                   wakemask=Wait( 1 << mport->mp_SigBit );
  222.                   while((msg=GetMsg(mport))==NULL){}
  223.                   }
  224.               else
  225.                   {
  226.                   /* Only enough data to fill up one buffer */
  227.                   BeginIO((struct IORequest *) pIOA_1 );
  228.                   wakemask=Wait( 1 << mport1->mp_SigBit );
  229.                   while((msg=GetMsg(mport1))==NULL){}
  230.                   }
  231.               }
  232.           else
  233.               /* One or the other of the files had a problem in Parse8svx() */
  234.               printf(errormsg);
  235.  
  236.           /* Free the memory used for the 8SVX files in Parse8svx() */
  237.           if(mainplay.next_iplay)
  238.               EndParse( &otherplay );
  239.           EndParse( &mainplay );
  240.  
  241.           }
  242.       else printf("Couldn't get memory for a second IOAudio and MsgPort\n");
  243.  
  244.       /* Free the ports and memory used by the 2 IOAudio requests */
  245.       if(mport2) DeletePort(mport2);
  246.       if(pIOA_2) FreeMem( pIOA_2, sizeof(struct IOAudio) );
  247.  
  248.       ReleaseChannel(pIOA_1);
  249.       }
  250.     else printf("Couldn't get a channel on the audio device\n");
  251.     }
  252. else printf("Enter one or two 8SVX filenames.\n");
  253. }
  254.  
  255.  
  256.  
  257. /*----------------------------------------------------------------------
  258. ** struct IOAudio *res = SiezeChannel( VOID )
  259. **
  260. **   Allocates any channel at the highest priority.  Once allocated,
  261. **   the hardware registers of the given channel can be hit directly
  262. **   without interfering with normal audio.device operation.
  263. **
  264. **   Retruns NULL on failure
  265. **   or returns the address of the IOAudio used to get the channel.
  266. **   If the call to this function succeeds, ReleaseChannel() should
  267. **   be called later to free the channel and memory used for the IOAudio.
  268. **-----------------------------------------------------------------------
  269. */
  270. struct IOAudio *
  271. SiezeChannel( VOID )
  272. {
  273. struct IOAudio *myAIOreq=NULL;
  274. struct MsgPort *myAIOreply=NULL;
  275. UBYTE chans[] = {1,2,4,8};      /* Try to get one channel, any channel */
  276. BYTE dev = -1;
  277.  
  278. myAIOreq=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC );
  279. if(myAIOreq)
  280.     {
  281.     myAIOreply=CreatePort(0,0);
  282.     if(myAIOreply)
  283.         {
  284.         myAIOreq->ioa_Request.io_Message.mn_ReplyPort   = myAIOreply;
  285.         myAIOreq->ioa_Request.io_Message.mn_Node.ln_Pri = 127;
  286.         myAIOreq->ioa_Request.io_Command                = ADCMD_ALLOCATE;
  287.         myAIOreq->ioa_AllocKey                          = 0;
  288.         myAIOreq->ioa_Data                              = chans;
  289.         myAIOreq->ioa_Length                            = sizeof(chans);
  290.  
  291.         dev=OpenDevice("audio.device",0L,(struct IORequest *)myAIOreq,0L);
  292.  
  293.        if(! dev)
  294.             return( myAIOreq ); /* Successful exit */
  295.  
  296.         DeletePort( myAIOreply );
  297.         }
  298.     FreeMem( myAIOreq, sizeof(struct IOAudio) );
  299.     }
  300. return( NULL );
  301. }
  302.  
  303.  
  304. /*---------------------------------------------------------------
  305. ** VOID ReleaseChannel(struct IOAudio *rel );
  306. **
  307. **   Frees the channel and any asociated memory allocated earlier
  308. **   with SiezeChannel().
  309. **---------------------------------------------------------------
  310. */
  311. VOID
  312. ReleaseChannel(struct IOAudio *rel)
  313. {
  314. if(rel)
  315.     {
  316.     CloseDevice( (struct IORequest *) rel );
  317.  
  318.     if(rel->ioa_Request.io_Message.mn_ReplyPort)
  319.         {
  320.         DeletePort(rel->ioa_Request.io_Message.mn_ReplyPort);
  321.         }
  322.     FreeMem( rel, sizeof(struct IOAudio) );
  323.     }
  324. }
  325.  
  326.  
  327.  
  328.  
  329. /*--------------------------------------------------------------------
  330. **
  331. ** char *Parse8svx( char *filename, struct InterPlay *play_state)
  332. **
  333. **   Pass this function the name of an 8svx file.  It opens the file and
  334. **   finds the VHDR and BODY Chunks.  Playback information is stored
  335. **   in the InterPlay structure.
  336. **
  337. **   A NULL return indicates the parse was completely successful.
  338. **   A non-NULL return means the file cannot be played back for
  339. **   some reason.  In that case the return value is a pointer to
  340. **   an error message explaining what went wrong.
  341. **
  342. **   After calling Parse8svx(), End Parse() should be called
  343. **   to free any memory used.
  344. **
  345. **----------------------------------------------------------------------
  346. */
  347. char *
  348. Parse8svx(char *fname, struct InterPlay *play)
  349. {
  350. BYTE iobuffer[12];
  351. LONG rdcount=0L;
  352. Chunk *pChunk=NULL;
  353. GroupHeader *pGH=NULL;
  354.  
  355. Voice8Header *pV8Head = NULL;
  356. char         *error   = NULL;
  357. BPTR         filehandle=NULL;
  358. BOOL         NO_BODY  = TRUE;
  359. BOOL         NO_VHDR  = TRUE;
  360.  
  361.  
  362. /* Under normal operation, this function leaves the file positioned */
  363. /* at the BODY Chunk.  However, for some degenerate 8SVX files, one */
  364. /* additional seek is needed at the end.  In that case this field   */
  365. /* (play->offsetBody) will be changed to the seek offset.           */
  366. play->offsetBody = 0;
  367. play->sample_loc = NULL;   /* Set to non-NULL if memory is allocated  */
  368. play->next_iplay = NULL;   /* Default is no successors, no interleave */
  369. play->sample_done= 0L;     /* Will be set to 1 when playback is done  */
  370.  
  371. filehandle= NULL;          /* Set to non-NULL if the file opens       */
  372.  
  373. NO_BODY=TRUE;
  374. NO_VHDR=TRUE;
  375.  
  376. /* This section just makes sure that the first 12 bytes of the */
  377. /* file conform to the IFF FORM specification, sub-type 8SVX.  */
  378. filehandle = Open( fname, MODE_OLDFILE );
  379. if(filehandle)
  380.     {
  381.     /* Next, read the first 12 bytes to check the type */
  382.     rdcount =Read( filehandle, iobuffer, 12L );
  383.     if(rdcount==12L)
  384.         {
  385.         /* Make sure it is an IFF FORM type */
  386.         pGH = (GroupHeader *)iobuffer;
  387.         if(pGH->ckID == FORM)
  388.             {
  389.             /* Make sure it is an 8SVX sub-type */
  390.             if(pGH->grpSubID != ID_8SVX)
  391.                     error="Not an 8SVX file\n";
  392.             }
  393.         else
  394.             error="Not an IFF FORM\n";
  395.         }
  396.     else
  397.         error="Read error or file too short1\n";
  398.     }
  399. else
  400.     error="Couldn't open that file. Try another.\n";
  401.  
  402.  
  403. /* Read through all Chunks until BODY and VHDR */
  404. /* Chunks are found or until an error occurs.  */
  405. while( !error &&   (NO_BODY  ||  NO_VHDR) )
  406.     {
  407.     /* Read the first 8 bytes of the Chunk to get the type and size */
  408.     rdcount =Read( filehandle, iobuffer, 8L );
  409.     if(rdcount==8L)
  410.         {
  411.         pChunk=(Chunk *)iobuffer;
  412.         switch(pChunk->ckID)
  413.             {
  414.  
  415.             case ID_VHDR:
  416.                 /* AllocMem() ckSize rounded up and read */
  417.                 /* the VHDR, filling in the InterPlay */
  418.                 if(pChunk->ckSize & 1L)
  419.                     pChunk->ckSize++;
  420.  
  421.                 pV8Head = AllocMem(pChunk->ckSize, MEMF_PUBLIC);
  422.                 if(pV8Head)
  423.                     {
  424.                     rdcount=Read(filehandle,pV8Head,pChunk->ckSize);
  425.                     if(rdcount==pChunk->ckSize )
  426.                         {
  427.                         if(pV8Head->sCompression==sCmpNone)
  428.                             {
  429.                             /* Set the playback speed */
  430.                             play->sample_speed = (UWORD)
  431.                                         (3579545L / pV8Head->samplesPerSec);
  432.  
  433.                             /* Set up start, end of sample data */
  434.                             play->sample_size = pV8Head->oneShotHiSamples
  435.                                                    + pV8Head->repeatHiSamples;
  436.                             }
  437.                         else error="Can't read compressed file\n";
  438.                         }
  439.                     else error="Read problem in header\n";
  440.  
  441.                     FreeMem(pV8Head, pChunk->ckSize );
  442.                     }
  443.                 else error="Couldn't get header memory\n";
  444.                 NO_VHDR = FALSE;
  445.                 break;
  446.  
  447.             case ID_BODY:
  448.                 /* Technically, a VHDR could come after a BODY.*/
  449.                 /* This is a pretty unlikely occurence though. */
  450.                 if(NO_VHDR)
  451.                     {
  452.                     if(pChunk->ckSize & 1L)
  453.                         pChunk->ckSize++;
  454.  
  455.                     rdcount = Seek(filehandle, pChunk->ckSize, OFFSET_CURRENT);
  456.                     if(rdcount==-1)
  457.                         error="Problem during BODY-skipping seek\n";
  458.                     else
  459.                         play->offsetBody=rdcount;
  460.                     }
  461.                 NO_BODY = FALSE;
  462.                 break;
  463.  
  464.             default:
  465.                 /* Ignore other Chunks, skipping over them */
  466.                 if(pChunk->ckSize & 1L)
  467.                     pChunk->ckSize++;
  468.  
  469.                 rdcount = Seek(filehandle, pChunk->ckSize, OFFSET_CURRENT);
  470.                 if(rdcount==-1)
  471.                     error="Problem during chunk-skipping seek\n";
  472.                 break;
  473.             }
  474.         }
  475.     else error = "Read error or file too short2\n";
  476.     }
  477.  
  478. if(!error)
  479.     {
  480.     /* In case the VHDR came after the BODY, seek back to the BODY */
  481.     if(play->offsetBody)
  482.         {
  483.         rdcount = Seek(filehandle, play->offsetBody, OFFSET_BEGINNING);
  484.         if(rdcount==-1)
  485.             error="Couldn't seek to BODY\n";
  486.         }
  487.  
  488.     /* OK now get the BODY data into a memory block */
  489.     play->sample_loc = AllocMem( play->sample_size, MEMF_PUBLIC );
  490.     if(play->sample_loc)
  491.         {
  492.         rdcount = Read(filehandle, play->sample_loc, play->sample_size);
  493.         if(rdcount!=play->sample_size)
  494.             error = "Error during BODY read\n";
  495.         else
  496.             play->sample_byte=play->sample_loc;
  497.         }
  498.     else
  499.         error="Couldn't get memory for BODY Chunk\n";
  500.     }
  501.  
  502. if(filehandle)
  503.     Close(filehandle);
  504.  
  505. return(error);
  506. }
  507.  
  508.  
  509.  
  510. /*---------------------------------------------------------------
  511. **
  512. ** VOID EndParse( struct InterPlay * );
  513. **
  514. **   This function simply frees any memory used by an earlier
  515. **   call to Parse8svx().
  516. **
  517. **---------------------------------------------------------------
  518. */
  519. VOID
  520. EndParse( struct InterPlay *play )
  521. {
  522. if(play->sample_loc)
  523.     FreeMem(play->sample_loc, play->sample_size );
  524. }
  525.  
  526.  
  527.  
  528. /*-----------------------------------------------------------------------------
  529. **
  530. ** VOID FillAudio(struct InterPlay *, struct IOAudio * );
  531. **
  532. **   This function gets 512 bytes each from 2 BODY buffers and interleaves
  533. **   the bytes in the audio playback buffer.
  534. **------------------------------------------------------------------------------
  535. */
  536. VOID
  537. FillAudio(struct InterPlay *inplay, struct IOAudio *ioa )
  538. {
  539. struct InterPlay *play1,*play2;
  540. ULONG remainder1,remainder2,x;
  541. UWORD speedfac;
  542. WORD value;
  543.  
  544.  
  545. if(ioa->ioa_Request.io_Command != CMD_WRITE) /* For 1st time callers only */
  546.     {
  547.     /* When two files are played at once, their speeds must match.  Use  */
  548.     /* whichever speed is fastest. Interleaved requests also require the */
  549.     /* speed to be doubled (period is halved).  However, the period      */
  550.     /* cannot be lower than 124 or audio DMA bandwidth will be exceeded. */
  551.     speedfac = inplay->sample_speed;
  552.  
  553.     if(inplay->next_iplay)
  554.         {
  555.         if(inplay->next_iplay->sample_speed < inplay->sample_speed)
  556.             speedfac = inplay->next_iplay->sample_speed;
  557.  
  558.         if ( !(inplay->USE_SUMMING) )
  559.             speedfac /= 2;
  560.         }
  561.  
  562.     if(speedfac < 124)
  563.         speedfac = 124;
  564.  
  565.     ioa->ioa_Request.io_Command = CMD_WRITE;
  566.     ioa->ioa_Request.io_Flags   = ADIOF_PERVOL;
  567.     ioa->ioa_Volume             = 63;
  568.     ioa->ioa_Period             = speedfac;
  569.     ioa->ioa_Length             = BUF_SIZE;
  570.     ioa->ioa_Cycles             = 1;
  571.     }
  572.  
  573. if(inplay->next_iplay)
  574.     {
  575.     play1=inplay;
  576.     play2=inplay->next_iplay;
  577.  
  578.     remainder1 = play1->sample_size - (play1->sample_byte - play1->sample_loc);
  579.     remainder2 = play2->sample_size - (play2->sample_byte - play2->sample_loc);
  580.  
  581.     if(play1->USE_SUMMING)
  582.         {
  583.         /*
  584.         ** AVERAGING LOGIC for playing TWO samples on ONE channel
  585.         */
  586.         for(x=0; x<BUF_SIZE ;x++)
  587.             {
  588.             value = 0;
  589.  
  590.             if( x<remainder1 )
  591.                 {
  592.                 value += *( (BYTE *)(play1->sample_byte) );
  593.                 play1->sample_byte++;
  594.                 }
  595.             else if( x==remainder1 )
  596.                 play1->sample_done=1;
  597.  
  598.             if( x<remainder2 )
  599.                 {
  600.                 value += *( (BYTE *)(play2->sample_byte) );
  601.                 play2->sample_byte++;
  602.                 }
  603.             else if( x==remainder2 )
  604.                 play2->sample_done=1;
  605.  
  606.             *(ioa->ioa_Data + x) = (UBYTE) (value/2);
  607.             }
  608.         }
  609.     else
  610.         {
  611.         /*
  612.         ** INTERLEAVE LOGIC for playing TWO samples on ONE channel
  613.         */
  614.  
  615.         /* If there are more bytes in the 1st sample data file, place them in */
  616.         /* the EVEN positions in the playback buffer of this IOAudio request. */
  617.         for(x=0; (x<BUF_SIZE) && (x<2*remainder1); x+=2 )
  618.             {
  619.             *(ioa->ioa_Data + x) = *(play1->sample_byte);
  620.             play1->sample_byte++;
  621.             }
  622.         /* If there are no more bytes then mark the 1st sample as done */
  623.         if(x<BUF_SIZE)
  624.             play1->sample_done=1L;
  625.  
  626.         while(x<BUF_SIZE)      /* Pad the playback buffer with zeroes. */
  627.             {
  628.             *(ioa->ioa_Data + x) = 0;
  629.             x+=2;
  630.             }
  631.  
  632.         /* If there are more bytes in the 2nd sample data file, place them in */
  633.         /* the ODD positions in the playback buffer of this IOAudio request.  */
  634.         for(x=1; (x<BUF_SIZE) && (x<2*remainder2);x+=2)
  635.             {
  636.             *(ioa->ioa_Data + x) = *(play2->sample_byte);
  637.             play2->sample_byte++;
  638.             }
  639.         /* If there are no more bytes then mark the 2nd sample as done */
  640.         if(x<BUF_SIZE)
  641.             play2->sample_done=1L;
  642.  
  643.         while(x<BUF_SIZE)      /* Pad the playback buffer with zeroes. */
  644.             {
  645.             *(ioa->ioa_Data + x) = 0;
  646.             x+=2;
  647.             }
  648.         }
  649.     }
  650. else
  651.     {
  652.     /*
  653.     ** REGULAR LOGIC for playing a single sample on a single channel.
  654.     */
  655.     remainder1= inplay->sample_size - (inplay->sample_byte-inplay->sample_loc);
  656.     if(remainder1 > BUF_SIZE)
  657.         {
  658.         CopyMem(inplay->sample_byte,ioa->ioa_Data,BUF_SIZE);
  659.         inplay->sample_byte+=BUF_SIZE;
  660.         }
  661.     else
  662.         {
  663.         CopyMem(inplay->sample_byte,ioa->ioa_Data,remainder1);
  664.         ioa->ioa_Length=remainder1;
  665.         inplay->sample_done=1L;
  666.         }
  667.     }
  668. }
  669.  
  670.