home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d7xx / d787 / scale.lha / Scale / Source.lha / atool31.c next >
C/C++ Source or Header  |  1992-12-12  |  26KB  |  870 lines

  1. /* Audiotools.c release 3.1 -- by Rob Peck */
  2.  
  3. /* This is a modified version of AudioTools -- changes by Dick Taylor */
  4. /* to use with the SCALE program.    November 1992 */
  5.  
  6. /* Please refer to file scale.doc for AudioTools background. */
  7.  
  8. #include "exec/types.h"
  9. #include "exec/memory.h"
  10. #include "devices/audio.h"
  11.  
  12. /* audiotools.h  -- in original version, this was a separate module */
  13.  
  14. #define StartChannel(c) ControlChannel(c, CMD_START)
  15. #define StopChannel(c)  ControlChannel(c, CMD_STOP)
  16. #define ResetChannel(c) ControlChannel(c, CMD_RESET)
  17. #define FlushChannel(c) ControlChannel(c, CMD_FLUSH)
  18.  
  19. #define  BIG_WAVE          256L   /* size of biggest waveform */
  20. #define  NBR_WAVES         7L     /* number of waves per instrument */
  21. #define  WAVES_TOTAL       1024L  /* alloc size for instrument's waves */
  22.  
  23. #define  DEFAULT_DURATION  500L      /* 500/1000ths of a second default */
  24. #define  AUDBUFFERS        20L      /* iob msg packets before need to allot */
  25. #define  YES               1L
  26. #define  NO                0L
  27.  
  28. #define  BAD_CHANNEL_SELECTED -1L /* channel # out of range */
  29. #define  NOT_YOUR_CHANNEL     -2L /* not owned by your task */
  30. #define  OUT_OF_RANGE_FREQ    -3L /* frequency that we cannot play */
  31. #define     OUT_OF_RANGE_PRI     -4L /* priority value wont fit in 1 byte */
  32.  
  33. /* Note that the definition of ExtIOB has now been changed (added to) */
  34.  
  35. struct ExtIOB {
  36.     struct     IORequest ioa_Request;
  37.     WORD    ioa_AllocKey;
  38.     UBYTE    *ioa_Data;
  39.     ULONG    ioa_Length;
  40.     UWORD    ioa_Period;
  41.     UWORD    ioa_Volume;
  42.     UWORD    ioa_Cycles;
  43.     struct    Message ioa_WriteMsg;    /* up to here, same as IOAudio */
  44.     LONG    iob_Identifier;        /* This field is added */
  45. };
  46.  
  47. /* a few forward declarations */
  48.  
  49. extern struct ExtIOB     *GetIOB();
  50. extern int         FreeIOB();
  51. extern int         GetChannel();
  52. extern int         InitBlock();
  53. extern struct MsgPort     *CreatePort();
  54. extern struct MsgPort   *InitAudio();
  55.  
  56. extern APTR         AllocMem();
  57. extern struct Message     *GetMsg();
  58. extern struct Task    *FindTask();
  59. extern LONG        SetCycles();
  60. extern int    TicksPerSec;    /* Clock ticks per second -- */
  61.                         /* NTSC (American) = 3579545, or */
  62.                         /* PAL (European) = 3546895 */
  63.  
  64. struct auMsg {
  65.    struct Message au_Message;
  66.    LONG aum_Identifier;   /* matches the bottom of ExtIOB */
  67. };
  68.  
  69. /* these are used to keep track of allocated channels */
  70.  
  71. struct Unit    *unit[4];    /* global pointers to Units        */
  72. WORD         key[4];        /* global value for alloc keys     */
  73. struct Task     *usertask[4];    /* in preparation for making this
  74.                  * a shared library of routines
  75.                  * (loadable from disk), keep track
  76.                  * of which user actually owns a
  77.                  * channel currently. */
  78. /* End of audiotools.h, start of former external module globals.c */
  79.  
  80. struct  IOAudio     openIOB;      /* IOB to open and close the device */
  81. struct  MsgPort     *auReplyPort; /* temporary pointer */
  82. struct  MsgPort     *controlPort; /* Port for ControlChannel functions */
  83.  
  84. char    *globalname     = "global";    /* the name for global IOB's  */
  85. char    *dynamicname     = "dynamic";    /* the name for dynamic IOB's */
  86.  
  87. UBYTE anychan[4]     = { 1, 2, 4, 8 };    /* channel masks for mono */
  88.  
  89. /* Resolve most all externals */
  90.  
  91. struct ExtIOB          audbuffer[AUDBUFFERS];    /* globals to build-in       */
  92. UBYTE             *chipaudio[4];    /* pointers to waveforms in CHIP RAM */
  93. struct Device         *device;    /* global pointer to audio device  */
  94. LONG             datalength[4];    /* length of the data for a wave   */
  95. struct MsgPort         *replyPort[4];  /* one ReplyPort per channel       */
  96. BYTE             inuse[AUDBUFFERS]; /* keep track of globals in-use */
  97. LONG            dynamix[4];    /* counters for how many
  98.                      * dynamically allocated audio
  99.                      * message I/O blocks */
  100.  
  101. /* Each waveform buffer contains 8 octaves of the wave.  
  102.  * The offset values specify where in the buffer the
  103.  * proper waveform table for that octave begins.
  104.  */
  105. int woffsets[]     = { 0, 256, 384, 448, 480, 496, 504, 508, 510 };
  106.  
  107. /* Length of each waveform within a buffer */
  108. int wlen[]     = { 256, 128, 64, 32, 16, 8, 4, 2, 1 };
  109.  
  110. /* Period value to go with particular notes within an octave. */
  111.  
  112. int perval[]     = { 428, 404, 381, 360, 339, 320, 
  113.                  302, 285, 269, 254, 240, 226, 214 };
  114. UBYTE *w1, *w2, *w3;
  115. /* End of globals.c */
  116.  
  117. struct MsgPort *    
  118. InitAudio()
  119. {
  120.       LONG error,i;
  121.       struct MsgPort *userport;    /* NEW item */
  122.  
  123.       LONG firstuser;    /* THIS WILL GET MOVED when shared library is made */
  124.       firstuser = TRUE;
  125.  
  126.       /* Declare all message blocks available */
  127.       for(i=0; i<AUDBUFFERS; i++)  {   inuse[i] = NO;   }
  128.  
  129.       /* Open device but don't allocate channels     */
  130.       openIOB.ioa_Length = 0;   /* (no allocation table) */
  131.  
  132.       error = OpenDevice("audio.device",0,&openIOB,0);
  133.       if(error) return((struct MsgPort *)0);
  134.  
  135.       /* Get the device address for later use */
  136.       device = openIOB.ioa_Request.io_Device;
  137.    
  138.    /* Create ports for replies from each channel as well as
  139.     * one port to be used for the control and synchonous functions */
  140.  
  141.    for(i=0; i<4; i++) 
  142.    {   
  143.       auReplyPort = CreatePort(0,0);
  144.       replyPort[i] = auReplyPort;
  145.       if(auReplyPort == 0) return((struct MsgPort *)0);
  146.       chipaudio[i] = 0;  /* have not yet created the waves */
  147.  
  148.       datalength[i] = 1; /* might use for custom sound samples  */
  149.  
  150.       /* Also, zero out key values for each channel, as well as
  151.        * unit value and usertask value (no channel owned by any task)*/
  152.     /* When implemented as a shared library, "firstuser" will only 
  153.       * be true when the library is first opened.*/
  154.       if(firstuser)    
  155.       {
  156.     key[i]  = 0;
  157.     unit[i] = 0;
  158.     usertask[i] = 0;
  159.       }
  160.    }
  161.    controlPort = CreatePort(0,0);
  162.    if(controlPort == 0) return((struct MsgPort *)0);
  163.  
  164.    error = MakeWaves();
  165.    if(error == -1) return((struct MsgPort *)0);
  166.  
  167.    for(i=0; i<4; i++)
  168.    { dynamix[i] = 0; }   /* no dynamic I/O blocks allocated 
  169.                       * for any channel thus far */
  170.    userport = CreatePort(0,0);
  171. return(userport);
  172. }
  173.  
  174. int 
  175. CheckIOBDone()
  176. {
  177.    LONG i, status;
  178.  
  179.    status = 0;   /* means there are still some iob's in play */
  180.          /* when status = 5, then everything is free */
  181.  
  182.    for(i=0; i<AUDBUFFERS; i++)
  183.    {   if(inuse[i] == YES)
  184.        {   
  185.      /* Sooner or later, this will catch both the statics & dynamics.
  186.     * Note that this will only work if NO iob's sent off with a 
  187.     * duration value of "0", because zero means "forever". */
  188.          ReEmployIOB();
  189.       }
  190.    }
  191.    /* Note to implementors... maintaining inuse[i] now seems
  192.     * like a lousy idea, unless it is accompanied by a variable
  193.     * statics_inplay that decrements to zero when all statics
  194.     * are done.  That makes it much easier to check than going
  195.     * through all of the inuse[]'s.*/
  196.  
  197.    for(i=0; i<4; i++)
  198.    {   
  199.       if(dynamix[i] > 0)  
  200.     /* If this channel still playing a dynamically allocated block, */
  201.     /* wait for all messages to return before the program exits. */
  202.       {
  203.     ReEmployIOB();  /* take another shot at freeing it all */  
  204.       }
  205.    }
  206.    for(i=0; i<4; i++)   /* Check again as we nearly exit */
  207.    {
  208.       if(dynamix[i] == 0) status++;
  209.    }
  210.    if(status == 4)      /* All dynamics are free, now check the statics.
  211.              * Any not free force an early return. */
  212.    {
  213. #ifdef DEBUG
  214.       printf("ch0,1,2,3 have %ld,%ld,%ld,%ld dyn.blocks playing\n",
  215.          dynamix[0], dynamix[1], dynamix[2], dynamix[3]);
  216. #endif
  217.       for(i=0; i<AUDBUFFERS; i++)
  218.       {
  219.          if(inuse[i] == YES)
  220.          {
  221. #ifdef DEBUG
  222.          printf("iob still in use is: %ld\n",i);
  223. #endif
  224.          return(0);
  225.          }
  226.       }
  227. #ifdef DEBUG
  228.       printf("All global I/O blocks are done\n");
  229. #endif
  230.       return(1);   /* DONE! */
  231.    }
  232.    else
  233.    {
  234.       return(0);   /* still some out there! */
  235.    }
  236. }
  237.  
  238. FinishAudio(uport)
  239. struct MsgPort *uport;
  240. {
  241.    LONG i;
  242.    struct auMsg *aum;        /* A little bigger than a standard message,
  243.     * but this routine will not know (or care) about the difference.*/
  244.    if(uport == 0)
  245.    {
  246.     goto no_init;        /* InitAudio didn't work, bypass
  247.                  * the usual port emptying and so on. */
  248.    }
  249. /* If the user says FinishAudio, IT MEANS FINISH AUDIO.  Flush anything
  250. * that is still in play, NOW.  You can use "CheckIOBDone()" to see if
  251. * everything is finished BEFORE you call FinishAudio.  If CheckIOBDone() is
  252. * equal to zero (FALSE), it means that something is still playing. */
  253.    for(i=0; i<4; i++)   FlushChannel(i);
  254.  
  255.    while(CheckIOBDone() == 0)
  256.    {
  257.     Delay(12);   /* Be a good multitasking neighbor;
  258.             * sleep a little before trying again */
  259.    }
  260.     /* Empty the port if the user has not yet done so */
  261.    while((aum = (struct auMsg *)GetMsg(uport)) != NULL)
  262.    {
  263.       aum->au_Message.mn_ReplyPort = 0;   /* let system deallocate it */
  264.  
  265. /* *** THIS WILL CHANGE --will be based on the identifier field instead.*/ 
  266.    }
  267.    ReEmployIOB();   /* free all static and dynamic messages */
  268.  
  269.    for(i=0; i<4; i++)   FreeChannel(i);
  270.  
  271.    DeletePort(uport);
  272.  
  273. no_init:
  274.    if(device) CloseDevice(&openIOB);
  275. #ifdef DEBUG
  276.    printf("closed the device\n");
  277. #endif
  278.    for(i=0; i<4; i++)
  279.    {
  280.     if(chipaudio[i]) FreeMem(chipaudio[i],WAVES_TOTAL);
  281.     if(replyPort[i]) 
  282.     DeletePort(replyPort[i]);
  283.    }
  284.    if(controlPort) DeletePort(controlPort);
  285.    return(0);      /* no errors */
  286. }
  287.  
  288.  
  289. int
  290. ControlChannel(channel, command)
  291.    WORD channel;
  292.    WORD command;
  293. {
  294.    LONG rtn;
  295.    struct ExtIOB *iob, controlIOB;
  296.  
  297.    if(channel < 0 || channel > 3)    return(BAD_CHANNEL_SELECTED);
  298.    if(usertask[channel] != FindTask(0))    return(NOT_YOUR_CHANNEL);
  299.  
  300.    iob = &controlIOB;
  301.    iob->ioa_Request.io_Device    = device;
  302.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
  303.  
  304.    InitBlock(iob,channel);   /* init it for CMD_WRITE, then change */
  305.  
  306.    iob->ioa_Request.io_Command = command;
  307.    iob->ioa_Request.io_Flags   = IOF_QUICK;
  308.  
  309.    BeginIO(iob);
  310.    WaitIO(iob);
  311.    rtn = ((LONG)(iob->ioa_Request.io_Error));
  312.    return(rtn);
  313. }
  314.  
  315. struct ExtIOB *
  316. GetIOB(ch)
  317.    LONG ch;
  318. {
  319.    WORD i,use_reply;
  320.    struct ExtIOB *iob;  /* in case we need to allocate one */
  321.    ReEmployIOB();    /* find already used ones and free them */
  322.               /* so that when we do a get... */
  323.    if(ch == -1)  use_reply = 0;  /* which reply port to use */
  324.    else          use_reply = ch;
  325.  
  326.    for(i=0; i<AUDBUFFERS; i++)
  327.    {   
  328.     if(inuse[i] == NO)
  329.            {   
  330.         inuse[i] = YES;
  331.  
  332.         audbuffer[i].ioa_Request.io_Device    = device;
  333.         audbuffer[i].ioa_Request.io_Message.mn_ReplyPort = 
  334.                                   replyPort[use_reply];
  335.         audbuffer[i].ioa_Request.io_Message.mn_Length = i;
  336.         audbuffer[i].ioa_Request.io_Message.mn_Node.ln_Name = 
  337.                                   globalname;
  338. #ifdef DEBUG
  339.       printf("Using global iob\n");
  340. #endif
  341.       return(&audbuffer[i]);
  342.        }
  343.    }
  344.    /* if all globals are in use, have to allocate one */
  345.    iob = (struct ExtIOB *)AllocMem(sizeof(struct ExtIOB),
  346.                      MEMF_CLEAR );
  347.    /* BUG FIX - was MEMF_FAST only, and would not run on 512k machine!! */
  348.  
  349.    if(iob == 0) return(0);   /* out of memory */
  350.    else
  351.    {   
  352.     iob->ioa_Request.io_Device = device;
  353.           iob->ioa_Request.io_Message.mn_ReplyPort = 
  354.                replyPort[use_reply];
  355.           iob->ioa_Request.io_Message.mn_Node.ln_Name = 
  356.                dynamicname;
  357.           iob->ioa_Request.io_Message.mn_Length = dynamix[use_reply];
  358.           dynamix[use_reply] += 1; /* add one to number allocated
  359.                           * for a specific channel */
  360. #ifdef DEBUG
  361.           printf("Using dynamic iob\n");
  362. #endif
  363.           return(iob);
  364.     }
  365. return(0);
  366. }
  367.  
  368.  
  369. /* Free a global or an allocated IOB */
  370. int
  371. FreeIOB(iob, ch)
  372.    struct ExtIOB *iob;
  373.    LONG ch;   /* which channel was it attached to? */
  374. {
  375.    WORD i;
  376.  
  377.    if(iob->ioa_Request.io_Message.mn_Node.ln_Name == dynamicname)
  378.    {   
  379.     FreeMem(iob, sizeof(struct ExtIOB));
  380.           if(dynamix[ch]) dynamix[ch] -= 1; /* subtract one if nonzero */
  381.           return(0L);
  382.    }
  383.    else if(iob->ioa_Request.io_Message.mn_Node.ln_Name == globalname)
  384.    {       
  385.     i = iob->ioa_Request.io_Message.mn_Length;
  386.  
  387.           if(i < AUDBUFFERS)
  388.           {   
  389.         inuse[i] = NO;   /* frees this one for reuse */
  390.           }
  391.           return(0L);
  392.    }
  393.    /* if get here, the names don't match... something is wrong.*/
  394.    else 
  395.    {   
  396.         printf("FreeIOB: names don't match...unknown error\n");
  397.            return(-1);   /* unknown source of IOB fed to routine. */
  398.    }
  399. return(0);
  400. }
  401.  
  402. /* Initialize an audio I/O block for default CMD_WRITE operation. */
  403. int
  404. InitBlock(iob, channel)
  405.      struct ExtIOB *iob;
  406.    WORD channel;
  407. {
  408.    /* Device and ReplyPort fields have been initialized by GetIOB */
  409.    iob->ioa_Request.io_Unit = unit[channel];
  410.  
  411.    /* Allocation key */
  412.    iob->ioa_AllocKey = key[channel];
  413.  
  414.    /* Where is the waveform?  Just be sure is in MEMF_CHIP!!! */
  415.  
  416.    /* USER initializes datalength[ch] before calling this;    */
  417.    /* for sampled sound command write operation.              */
  418.    iob->ioa_Data    = chipaudio[channel];
  419.    iob->ioa_Length = datalength[channel];
  420.  
  421.    /* Another routine, must initialize:
  422.     period      ioa_Period    volume    ioa_Volume
  423.     cycles      ioa_Cycles    message    ioa_WriteMsg */
  424.    /* Default command type is CMD_WRITE */
  425.    iob->ioa_Request.io_Command = CMD_WRITE;
  426.  
  427.    /* If IOF_QUICK is zeroed, this would affect the
  428.     * period and volume.  If a CMD_WRITE, it queues if
  429.     * another note is already playing.  We queue CMD_WRITES. */
  430.    iob->ioa_Request.io_Flags = ADIOF_PERVOL;
  431.    return(0);
  432. }
  433.  
  434. /* To request "any" channel, use ch = -1;
  435.  * To request a specific channel, use ch = {0, 1, 2 or 3};
  436.  * Again NOTE, this returns two globals as well as the channel number! */
  437.  
  438. int
  439. GetChannel(ch)
  440.    LONG ch;
  441. {
  442.    int error, value;
  443.    struct ExtIOB *iob, controlIOB;
  444.  
  445.    iob = &controlIOB;
  446.    iob->ioa_Request.io_Device    = device;
  447.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
  448.  
  449.    InitBlock(iob,0);   /* init it for CMD_WRITE, then change */
  450.  
  451.    iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
  452.    iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
  453.  
  454.    if(ch == -1)
  455.    {   
  456.     iob->ioa_Data = (UBYTE *)anychan;
  457.           iob->ioa_Length = 4;
  458.    }
  459.    else if(ch >=0 && ch <= 3)
  460.    {       
  461.     /* Test to be sure that this channel is now free.  If
  462.      * usertask[i] is not zero, either the current task 
  463.      * has already allocated this channel, or a different
  464.      * task is now using it. */ 
  465.  
  466.     if(usertask[ch] != 0) return(-1);
  467.  
  468.     iob->ioa_Data = (UBYTE *)(&anychan[ch]);
  469.           iob->ioa_Length = 1;
  470.    }
  471.    else   /* chose a bad channel number; cannot allocate it */
  472.    {   
  473.     return(-1);
  474.    }
  475.    iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
  476.    BeginIO(iob); 
  477.    error = WaitIO(iob);  /* returns nonzero if error */
  478.    if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
  479.    {   
  480.     GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
  481.    }
  482.    if(error)
  483.    {   
  484.     return(-1);
  485.    }
  486.    switch((LONG)(iob->ioa_Request.io_Unit))
  487.    {   
  488.     case  1:   value = 0;   break;
  489.           case  2:   value = 1;   break;
  490.           case  4:   value = 2;   break;
  491.           case  8:   value = 3;   break;
  492.           default:   value = -1;  break;
  493.    }
  494.    if(value == -1) return(-1L);
  495.  
  496.    unit[value]     = (iob->ioa_Request.io_Unit);
  497.    key[value]      = (iob->ioa_AllocKey);
  498.    usertask[value] = FindTask(0);    /* THIS user task owns it now */
  499.  
  500.    return(value);
  501. }
  502.  
  503. int
  504. FreeChannel(channel)
  505.    LONG channel;
  506. {
  507.    int error;
  508.    struct ExtIOB *iob, controlIOB;
  509.  
  510.    if(channel < 0 || channel > 3)    return(BAD_CHANNEL_SELECTED);
  511.    if(usertask[channel] != FindTask(0))    return(NOT_YOUR_CHANNEL);
  512.  
  513.    iob = &controlIOB;
  514.    iob->ioa_Request.io_Device    = device;
  515.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
  516.  
  517.    InitBlock(iob,channel);       /* init it for CMD_WRITE, then change it */
  518.                     /* (pick up unit, key value for channel) */
  519.    iob->ioa_Request.io_Command = ADCMD_FREE;
  520.    iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
  521.    BeginIO(iob); 
  522.    error = WaitIO(iob);  /* returns nonzero if error */
  523.  
  524.    if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
  525.    {       
  526.     GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
  527.    }
  528.    usertask[channel] = 0;    /* free again... */
  529.    if(error)
  530.    {   
  531.     return(error);
  532.    }
  533.    return(0);
  534. }
  535.  
  536. /* SOME OF THE FOLLOWING ROUTINES ARE PARAPHRASED FROM A USENET and BIX
  537.  * POSTING MADE IN 1985 BY STEVEN A. BENNETT. */ 
  538. /* I have modified his routines to queue the audio commands in 
  539.  * place of starting forever-duration and canceling each note.
  540.  * Many of his original comments have been incorporated into
  541.  * the article.*/ 
  542.  
  543. /* ******************************************************************* */
  544. /* NOTE: There are some differences in PlayNote as compared to the article.
  545.  * See the audiotools.DOC for details.*/
  546. /* ******************************************************************* */
  547.  
  548. /* Starts a sound on the channel with specified period and volume. */
  549. /* This nice little routine takes a note and plays it on the given
  550.  * voice.  The note is basically an integer from
  551.  * 0 to 11 (c to b) plus 12 per octave above the first and lowest. 
  552.  *
  553.  * The waveform to use is determined by adding an index (woffsets[]) 
  554.  * dependent on the octave.
  555.  *
  556.  * The length of the waveform (in wlen[]) is likewise dependent on
  557.  * the octave.  Note that octaves start with zero, not one.
  558.  */
  559. int 
  560. /*PlayNote(channel, note, wf, vol, duration, priority, messageport, id)*/
  561. PlayNote(channel, note, vol, duration)
  562. /*   char *wf;   /* waveform to use */
  563. /*   LONG vol, channel, duration, note;   /* specific note number */
  564. /*   LONG priority;
  565.    struct MsgPort *messageport;
  566.    LONG id; */
  567.    LONG  channel, note, vol, duration;
  568. {
  569.    LONG per, len, oct;   /* period, length of waveform, which octave */
  570.    char *wavepointer;   /* where to find start of waveform */
  571.    struct ExtIOB *iob;
  572.  
  573.    if(channel < 0 || channel > 3)    return(BAD_CHANNEL_SELECTED);
  574.    if(usertask[channel] != FindTask(0))    return(NOT_YOUR_CHANNEL);
  575.    if(note < 0 || note > 95)         return(OUT_OF_RANGE_FREQ);
  576. /*   if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI);*/
  577.  
  578.    iob = GetIOB(channel);
  579.  
  580.    if(iob != 0)
  581.    {
  582.     InitBlock(iob, channel);   /* set up for CMD_WRITE */
  583.    
  584.     oct = note / 12;
  585. /*       wavepointer = wf + woffsets[oct];*/
  586.        wavepointer = w1 + woffsets[oct];     /* Always waveform #1 */
  587.        len = wlen[oct];
  588.        per = perval[note % 12];
  589.  
  590.        /* Set the parameters */
  591.        iob->ioa_Data = (UBYTE *)wavepointer;
  592.        iob->ioa_Length = len;
  593.        iob->ioa_Period = per;
  594.        iob->ioa_Volume = vol;
  595.  
  596. /*    iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority;*/
  597.     iob->ioa_Request.io_Message.mn_Node.ln_Pri = 0;  /* always pri=0*/
  598.        /* additions for support of tell-me-when-note-starts */
  599.  
  600. /*        iob->iob_Identifier = id;*/
  601.           iob->iob_Identifier = 0;     /* Always id = 0 */
  602.  
  603.     /* Initialize message port.  If 0, then no pushing back
  604.      * of a message.  If nonzero, message gets recirculated
  605.      * by ReEmployIOB until the user finally acknowledges it
  606.      * by using MayGetNote.
  607.       */
  608. /*    iob->ioa_WriteMsg.mn_ReplyPort = messageport;*/
  609.     iob->ioa_WriteMsg.mn_ReplyPort = 0;   /* Always messageport = 0 */
  610.  
  611. /*    if(messageport != 0)
  612.        {  */
  613.         /* If 0, no sending message when note plays;
  614.         * if nonzero, user gets a message that can
  615.         * be read by MayGetNote.  uport, received from 
  616.          * InitAudio, is where the message goes */
  617.  
  618.         /* Tell the audio device to "reply" to this message */
  619. /*        iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE;
  620.         }  */
  621.  
  622.    /* Calculate cycles from duration in 1000ths of a second */
  623.    /* Multiply all-in-one to maintain max precision possible */
  624.    /* (all integer arithmetic.) */
  625.  
  626.    iob->ioa_Cycles = SetCycles(duration,len,per);
  627.    BeginIO(iob);
  628.    return(0L);      /* all went ok */
  629.    }
  630. return(-99L);    /* (else-part) iob was zero, couldn't do the above. */
  631. }
  632.  
  633. /* SetWaves(w1, w2, w3): create first sawtooth, triangle and square wave */
  634.  
  635. SetWaves(w1, w2, w3)
  636.    UBYTE *w1, *w2, *w3;
  637. {
  638.    int i, increment, value, sqvalue;
  639.    value = 0; increment = 2;
  640.    sqvalue = 127;
  641.  
  642.    for (i = 0; i < BIG_WAVE; ++i)
  643.    {
  644.    w1[i] = i;   /* do the sawtooth */
  645.  
  646.    if(i > 62 && i < 180) increment = -2;
  647.    else
  648.    if(i >= 180) increment = 2;
  649.  
  650.    w2[i] = value;  value += increment;  /* triangle wave */
  651.  
  652.    if(i > 126) sqvalue = -127;
  653.  
  654.    w3[i] = sqvalue;
  655.    }
  656. return(0L);
  657. }
  658.   
  659. /* ExpandWave(wfp) - replicate waves in decreasing sample sizes */
  660.  
  661. ExpandWave(wfp)
  662.    BYTE *wfp;
  663.    {
  664.    int i, j, rate;
  665.    BYTE *tptr;
  666.  
  667.    rate = 1;
  668.    tptr = wfp + BIG_WAVE;
  669.    for (i = 0; i < NBR_WAVES - 1; ++i)
  670.       {
  671.       rate *= 2;
  672.       for (j = 0; j < BIG_WAVE; j += rate)
  673.          *tptr++ = wfp[j];
  674.       }
  675.    return(0L);
  676.    }
  677.   
  678. /*  MakeWaves --  Just makes a sawtooth, triangle and square wave
  679.  *   in chip mem and expands them.*/
  680.  
  681. int 
  682. MakeWaves()
  683. {
  684.    /* allocate the memory for the waveforms.
  685.     */
  686.    w1 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
  687.    w2 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
  688.    w3 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
  689.  
  690.    if (w1 == NULL || w2 == NULL || w3 == NULL)
  691.    return(-1L);   /* ran out of memory! */
  692.  
  693.    /* get and expand the waveforms    */
  694.  
  695.    SetWaves(w1, w2, w3);
  696.    ExpandWave(w1);   chipaudio[0]=w1;
  697.    ExpandWave(w2);   chipaudio[1]=w2;
  698.    ExpandWave(w3);   chipaudio[2]=w3;
  699.    return(0L);
  700. }
  701.  
  702. /* ReEmployIOB is not new (as compared to article) but it
  703.  * has changed a lot since the article was written.*/
  704.  
  705. /* ReEmployIOB - look at ALL of the reply ports and if any IOBs
  706.     hanging around with nothing to do, free them.*/
  707.  
  708. ReEmployIOB()
  709. {
  710.    LONG i;
  711.    struct MsgPort *mp;
  712.    struct ExtIOB *iob;
  713.  
  714.    /* now declare a pointer to "the first iob pushed back onto the port" */
  715.  
  716.    struct ExtIOB *pushback;    
  717.  
  718.    /* What happens here is that iob's are removed from the message port
  719.     * when they come back from the audio device.   If YOU have set the
  720.     * messageport nonzero, it means that you wanted to know when
  721.  
  722.     *** THIS WILL CHANGE --- will be based on setting
  723.         the priority value to nonzero.
  724.  
  725.     * this note began to play.  The WriteMsg part of the iob is then
  726.     * linked, as a message, onto your user port.  So this routine here
  727.     * cannot free the iob until it is certain that YOU have finished
  728.     * using it.  The iob_Priority field is READ here.  If it still
  729.     * nonzero, the iob is pushed back onto the message port (on the
  730.     * end of the message queue) to be read again.  We hold a pointer
  731.     * named pushback that lets us keep track of when we see that
  732.     * again.  If we see it twice, it means we have completed a full
  733.     * circle through the queue of messages and have freed everything
  734.     * that we can this time.  Therefore, we examine it and either
  735.     * free it or push it back again, then exit.*/
  736.  
  737.    for(i=0; i<4; i++)   /* remove all iob's from ALL ports */
  738.             /* (that is, unless we have to push one back) */
  739.    {   
  740.       mp = replyPort[i];
  741.  
  742.       pushback = (struct ExtIOB *)0;    /* nothing pushed back so far */
  743.  
  744.       while((iob = (struct ExtIOB *)GetMsg(mp)) != 0)
  745.       { 
  746.         /* Here is what triggers the Identifier message for MayGetNote */
  747.  
  748.         /* First see if messageport in WriteMsg is greater than zero;
  749.          * if so, audio device is done, but user hasnt acknowledged
  750.          * this message yet (by using MayGetNote).*/
  751.         if(iob->ioa_WriteMsg.mn_ReplyPort != 0)
  752.         {
  753.         /* If we get here, we already know we will push it back */
  754.         /* because User has not yet freed it (using MayGetNote) */
  755.  
  756.         PutMsg(mp, iob);
  757.  
  758.         if(iob == pushback) /* If so, we saw it already */
  759.         {
  760.             break;       /* Go out to empty next port */
  761.         }
  762.         if(pushback == 0)
  763.         {
  764.             pushback = iob;    /* Remember FIRST one pushed back */
  765.         }
  766.         }
  767.         else    /* messageport value is zero, can free the iob */
  768.         {
  769. #ifdef DEBUG
  770.             printf("freeing %ls iob; ID=%ld\n",
  771.                   iob->ioa_Request.io_Message.mn_Node.ln_Name,
  772.                         iob->ioa_Request.io_Message.mn_Length);
  773. #endif
  774.                 FreeIOB(iob,i);
  775.         }
  776.       }
  777.  
  778.    }
  779.    return(0L);
  780. }
  781.  
  782. LONG
  783. SetCycles(duration, len, per)
  784.    LONG len, per;   /* period, length of waveform, which octave */ 
  785.    LONG duration;
  786. {
  787.    /* Calculate cycles from duration in 1000ths of a second */
  788.    /* Multiply all-in-one to maintain max precision possible */
  789.    /* (all integer arithmetic.) */
  790.  
  791.    long cycles, ipart;
  792.    long dividend, divisor;
  793.  
  794.    if(duration == 0) return(0);  /* forever is forever */
  795.    ipart = 0;
  796.  
  797.    if(duration > 1000)
  798.    {
  799.     ipart = duration/1000;
  800.         duration = duration - (ipart * 1000);
  801.    }
  802.  
  803.    /* Fool it a little so we don't get integer overflow...
  804.     * 3.5 million times 1000 is about all we can take in a 32 bit word*/
  805.     /* TicksPerSec: either 3579545 (NTSC) or 3546895 (PAL) */
  806.    dividend = (TicksPerSec * ipart) + 
  807.                     ((long)TicksPerSec * (long)duration)/(long)1000 ;
  808.    divisor  = len * per;
  809.  
  810.    cycles   = (((long)dividend *10/ (long)divisor) + 5) / 10; /* Round */
  811.  
  812.    if(duration != 0 && cycles == 0) cycles = 1;
  813.    return(cycles);
  814. }
  815.  
  816. struct MsgPort *myport;
  817.  
  818. atool31(v)
  819.    int v;
  820. {
  821.    LONG i, channel, error;
  822.  
  823.    if (v > 0) goto Fin;
  824.  
  825.    myport = InitAudio();
  826.    if(myport == 0) 
  827.    {
  828.     printf("Problem in InitAudio!");
  829.     FinishAudio(myport);
  830.    }
  831.    for(i=0; i<4; i++) 
  832.    {   
  833.       channel = GetChannel(-1);
  834.       if(channel == -1)
  835.       {
  836.        printf("cannot get a channel!\n");
  837.        FinishAudio(myport);
  838.       }
  839.  
  840.       error = StopChannel(channel);
  841.       if(error) 
  842.       {  
  843.      printf("error in stopping channel = %ld\n",error);
  844.          FinishAudio(myport);
  845.       }
  846.    }
  847.  
  848.    return(0);
  849.  
  850. Fin:
  851.    FinishAudio(myport);   /* NEW parameter for FinishAudio */
  852.  
  853. /*   printf("Done!\n");*/
  854.    return(0);
  855. }         /* end of atool31() */
  856.  
  857. StartChans()
  858. {
  859.  int i, error;
  860.  for(i=0; i<4; i++)
  861.  {   
  862.     error = StartChannel(i);
  863.     if(error)  
  864.     {
  865.         printf("error starting channel = %ld\n",error);
  866.         FinishAudio(myport);
  867.     }
  868.  }
  869. }
  870.