home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / amiga / utils / atools / audiotol.c < prev    next >
C/C++ Source or Header  |  1988-03-23  |  36KB  |  1,085 lines

  1. /* audiotools2.c  */
  2.  
  3. /* audiotools.h built in here so that audiotools is a package deal */ 
  4.  
  5. #define StartChannel(c) ControlChannel(c, CMD_START) 
  6. #define StopChannel(c)  ControlChannel(c, CMD_STOP) 
  7. #define ResetChannel(c) ControlChannel(c, CMD_RESET) 
  8. #define FlushChannel(c) ControlChannel(c, CMD_FLUSH) 
  9.  
  10. #define  BIG_WAVE          256L   /* size of biggest waveform */ 
  11. #define  NBR_WAVES         7L     /* number of waves per instrument */ 
  12. #define  WAVES_TOTAL       1024L  /* alloc size for instrument's waves */ 
  13.  
  14. #define  DEFAULT_DURATION  500L   /* 500/1000ths of a second default */ 
  15. #define  AUDBUFFERS        20L    /* iob msg packets before need to allot */ 
  16. #define  YES               1L 
  17. #define  NO                0L 
  18.  
  19. #define  BAD_CHANNEL_SELECTED -1L /* channel # out of range */ 
  20. #define  NOT_YOUR_CHANNEL     -2L /* not owned by your task */ 
  21. #define  OUT_OF_RANGE_FREQ    -3L /* frequency that we cannot play */ 
  22. #define  OUT_OF_RANGE_PRI     -4L /* priority value wont fit in 1 byte */ 
  23.  
  24. /* REDEFINITION from article - now contains one new field at the bottom 
  25.  * of the data structure. 
  26.  */ 
  27. struct ExtIOB { 
  28.     struct      IORequest ioa_Request; 
  29.     WORD        ioa_AllocKey; 
  30.     UBYTE       *ioa_Data; 
  31.     ULONG       ioa_Length; 
  32.     UWORD       ioa_Period; 
  33.     UWORD       ioa_Volume; 
  34.     UWORD       ioa_Cycles; 
  35.     struct      Message ioa_WriteMsg;   /* up to here, same as IOAudio */ 
  36.     LONG        iob_Identifier;         /* This field is added */ 
  37. }; 
  38.  
  39. /* a few forward declarations */ 
  40.  
  41. extern struct ExtIOB    *GetIOB(); 
  42. extern int              FreeIOB(); 
  43. extern int              GetChannel(); 
  44. extern int              GetStereoPair(); 
  45. extern int              InitBlock(); 
  46. extern struct MsgPort   *CreatePort(); 
  47.  
  48. extern APTR             AllocMem(); 
  49. extern struct Message   *GetMsg(); 
  50. extern struct Task      *FindTask(); 
  51.  
  52. struct auMsg { 
  53.    struct Message au_Message; 
  54.    LONG aum_Identifier;   /* matches the bottom of ExtIOB */ 
  55. }; 
  56.  
  57. /* Note that the definition of ExtIOB has now been changed (added to) */ 
  58.  
  59. /* forward declaration */ 
  60. extern struct MsgPort *InitAudio(); 
  61.  
  62. /* these are used to keep track of allocated channels */ 
  63.  
  64. struct Unit     *unit[4];       /* global pointers to Units        */ 
  65. WORD            key[4];         /* global value for alloc keys     */ 
  66. struct Task     *usertask[4];   /* in preparation for making this 
  67.                                  * a shared library of routines 
  68.                                  * (loadable from disk), keep track 
  69.                                  * of which user actually owns a 
  70.                                  * channel currently. 
  71.                                  */ 
  72.  
  73. struct  IOAudio         openIOB;      /* IOB to open and close the device */ 
  74. struct  MsgPort         *auReplyPort; /* temporary pointer */ 
  75. struct  MsgPort         *controlPort; /* Port for ControlChannel functions */ 
  76.  
  77. char    *globalname     = "global";     /* the name for global IOB's  */ 
  78. char    *dynamicname    = "dynamic";    /* the name for dynamic IOB's */ 
  79.  
  80. UBYTE stereostuff[4]    = { 3, 5, 10, 12 };     /* channel masks for stereo */ 
  81. UBYTE anychan[4]        = { 1, 2, 4, 8 };       /* channel masks for mono */ 
  82.  
  83. /* Resolve most all externals */ 
  84.  
  85. struct ExtIOB           audbuffer[AUDBUFFERS];  /* globals to build-in       */ 
  86. UBYTE                   *chipaudio[4];  /* pointers to waveforms in CHIP RAM */ 
  87. struct Device           *device;        /* global pointer to audio device  */ 
  88. LONG                    datalength[4];  /* length of the data for a wave   */ 
  89. struct MsgPort          *replyPort[4];  /* one ReplyPort per channel       */ 
  90. BYTE                    inuse[AUDBUFFERS]; /* keep track of globals in-use */ 
  91. LONG                    dynamix[4];     /* counters for how many 
  92.                                          * dynamically allocated audio 
  93.                                          * message I/O blocks  
  94.                                          */ 
  95.  
  96.  
  97. /* Each waveform buffer contains 8 octaves of the wave.   
  98.  * The offset values specify where in the buffer the 
  99.  * proper waveform table for that octave begins. 
  100.  */ 
  101. int woffsets[]  = { 0, 256, 384, 448, 480, 496, 504, 508, 510 }; 
  102.  
  103. /* Length of each waveform within a buffer */ 
  104. int wlen[]      = { 256, 128, 64, 32, 16, 8, 4, 2, 1 }; 
  105.  
  106. /* Period value to go with particular notes within an octave. */ 
  107. int perval[]    = { 428, 404, 381, 360, 339, 320,  
  108.                     302, 285, 269, 254, 240, 226, 214 }; 
  109.  
  110. UBYTE *w1, *w2, *w3; 
  111. BYTE *owptr[4]  = { NULL, NULL, NULL, NULL }; 
  112.  
  113. /* InitAudio is different from that published in the article - 
  114.   
  115. InitAudio now returns a value, an address of a message port at which 
  116. your task receives a message when a particular note BEGINS to play. 
  117. You must save this value somewhere, and use it to call MayGetNote 
  118. or FinishAudio.  MayGetNote is the name of the routine that you call 
  119. to check if a note has begun to play. 
  120.  
  121. InitAudio also has been modified to return to its caller when there 
  122. are problems, rather than calling finishup routines itself.  This is better 
  123. programming practice. 
  124.  
  125. */ 
  126.  
  127. struct MsgPort *     
  128. InitAudio() 
  129.       LONG error,i; 
  130.       struct MsgPort *userport; /* NEW item */ 
  131.  
  132.       LONG firstuser;   /* THIS WILL GET MOVED when shared library is made */ 
  133.       firstuser = TRUE; 
  134.  
  135.       /* Declare all message blocks available */ 
  136.       for(i=0; i<AUDBUFFERS; i++)  {   inuse[i] = NO;   } 
  137.  
  138.       /* Open device but don't allocate channels     */ 
  139.       openIOB.ioa_Length = 0;   /* (no allocation table) */ 
  140.  
  141.       error = OpenDevice("audio.device",0,&openIOB,0); 
  142.       if(error) return((struct MsgPort *)0); 
  143.  
  144.       /* Get the device address for later use */ 
  145.       device = openIOB.ioa_Request.io_Device; 
  146.     
  147.    /* Create ports for replies from each channel as well as 
  148.     * one port to be used for the control and synchonous functions */ 
  149.  
  150.    for(i=0; i<4; i++)  
  151.    {    
  152.       auReplyPort = CreatePort(0,0); 
  153.       replyPort[i] = auReplyPort; 
  154.       if(auReplyPort == 0) return((struct MsgPort *)0); 
  155.       chipaudio[i] = 0;  /* have not yet created the waves */ 
  156.  
  157.       datalength[i] = 1; /* might use for custom sound samples  */ 
  158.  
  159.       /* Also, zero out key values for each channel, as well as 
  160.        * unit value and usertask value (no channel owned by any task) 
  161.        */ 
  162.  
  163.         /* When implemented as a shared library, "firstuser" will only  
  164.          * be true when the library is first opened. 
  165.          */ 
  166.       if(firstuser)      
  167.       { 
  168.         key[i]  = 0; 
  169.         unit[i] = 0; 
  170.         usertask[i] = 0; 
  171.       } 
  172.    } 
  173.    controlPort = CreatePort(0,0); 
  174.    if(controlPort == 0) return((struct MsgPort *)0); 
  175.  
  176.    error = MakeWaves(); 
  177.    if(error == -1) return((struct MsgPort *)0); 
  178.  
  179.    for(i=0; i<4; i++) 
  180.    { dynamix[i] = 0; }   /* no dynamic I/O blocks allocated  
  181.                           * for any channel thus far */ 
  182.  
  183.    userport = CreatePort(0,0); 
  184.  
  185. return(userport); 
  186.  
  187. int  
  188. CheckIOBDone() 
  189.    LONG i, status; 
  190.  
  191.    status = 0;   /* means there are still some iob's in play */ 
  192.          /* when status = 5, then everything is free */ 
  193.  
  194.    for(i=0; i<AUDBUFFERS; i++) 
  195.    {   if(inuse[i] == YES) 
  196.        {    
  197.          /* Sooner or later, this will catch both 
  198.           * the statics and dynamics.  Note that 
  199.           * this will only work if NO (REPEAT: NO) 
  200.           * iob's sent off with a duration value 
  201.           * of "0", because zero means "forever" 
  202.           */ 
  203.          ReEmployIOB(); 
  204.       } 
  205.    } 
  206.    /* Note to implementors... maintaining inuse[i] now seems 
  207.     * like a lousy idea, unless it is accompanied by a variable 
  208.     * statics_inplay that decrements to zero when all statics 
  209.     * are done.  That makes it much easier to check than going 
  210.     * through all of the inuse[]'s. 
  211.     */ 
  212.  
  213.    for(i=0; i<4; i++) 
  214.    {    
  215.       if(dynamix[i] > 0)   
  216.             /* If this channel still playing a   */ 
  217.                  /* dynamically allocated block, wait */ 
  218.                  /* for all messages to return before */ 
  219.                  /* the program exits.                */ 
  220.       { 
  221.                 ReEmployIOB();  /* take another shot at freeing it all */   
  222.       } 
  223.    } 
  224.    for(i=0; i<4; i++)   /* Check again as we nearly exit */ 
  225.    { 
  226.       if(dynamix[i] == 0) status++; 
  227.    } 
  228.    if(status == 4)      /* All dynamics are free, now check 
  229.              * the statics.  Any not free force 
  230.              * an early return. */ 
  231.    { 
  232. #ifdef DEBUG 
  233.       printf("ch0,1,2,3 have %ld,%ld,%ld,%ld dyn.blocks playing\n", 
  234.          dynamix[0], dynamix[1], dynamix[2], dynamix[3]); 
  235. #endif DEBUG 
  236.       for(i=0; i<AUDBUFFERS; i++) 
  237.       { 
  238.          if(inuse[i] == YES) 
  239.          { 
  240. #ifdef DEBUG 
  241.          printf("iob still in use is: %ld\n",i); 
  242. #endif DEBUG 
  243.          return(0); 
  244.          } 
  245.       } 
  246.       printf("All global I/O blocks are done\n"); 
  247.  
  248.       return(1);   /* DONE! */ 
  249.    } 
  250.    else 
  251.    { 
  252.       return(0);   /* still some out there! */ 
  253.    } 
  254.  
  255.  
  256. FinishAudio(uport) 
  257. struct MsgPort *uport; 
  258.    LONG i; 
  259.    struct auMsg *aum;       /* A little bigger than a standard 
  260.                              * message, but this routine will 
  261.                              * not really know (or care) about 
  262.                              * the difference. 
  263.                              */ 
  264.    if(uport == 0) 
  265.    { 
  266.         goto no_init;       /* InitAudio didn't work, bypass 
  267.                              * the usual port emptying and so on. 
  268.                              */ 
  269.    } 
  270.    /* If the user says FinishAudio, IT MEANS FINISH AUDIO. 
  271.     * Flush anything that is still in play, NOW.  You can 
  272.     * use "CheckIOBDone()" to see if everything is finished 
  273.     * BEFORE you call FinishAudio.  If CheckIOBDone() is 
  274.     * equal to zero (FALSE), it means that something is still 
  275.     * playing. 
  276.     */ 
  277.    for(i=0; i<4; i++)   FlushChannel(i); 
  278.  
  279.    while(CheckIOBDone() == 0) 
  280.    { 
  281.       Delay(12);   /* Be a good multitasking neighbor; 
  282.                     * sleep a little before trying again */ 
  283.    } 
  284.    /* Empty the port if the user has not yet done so */ 
  285.    while((aum = (struct auMsg *)GetMsg(uport)) != NULL) 
  286.    { 
  287.       aum->au_Message.mn_ReplyPort = 0;   /* let system deallocate it */ 
  288.    } 
  289.    ReEmployIOB();   /* free all static and dynamic messages */ 
  290.  
  291.    for(i=0; i<4; i++)   FreeChannel(i); 
  292.  
  293. no_init: 
  294.    if(device) CloseDevice(&openIOB); 
  295.    printf("closed the device\n"); 
  296.  
  297.    for(i=0; i<4; i++) 
  298.    { 
  299.         if(chipaudio[i]) FreeMem(chipaudio[i],WAVES_TOTAL); 
  300.         if(replyPort[i])  
  301.         DeletePort(replyPort[i]); 
  302.    } 
  303.    if(controlPort) DeletePort(controlPort); 
  304.  
  305.    return(0);      /* no errors */ 
  306.   
  307.   
  308. int 
  309. ControlChannel(channel, command) 
  310.    WORD channel; 
  311.    WORD command; 
  312.    LONG rtn; 
  313.    struct ExtIOB *iob, controlIOB; 
  314.  
  315.    if(channel < 0 || channel > 3)       return(BAD_CHANNEL_SELECTED); 
  316.    if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL); 
  317.  
  318.    iob = &controlIOB; 
  319.    iob->ioa_Request.io_Device    = device; 
  320.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort; 
  321.  
  322.    InitBlock(iob,channel);   /* init it for CMD_WRITE, then change */ 
  323.  
  324.    iob->ioa_Request.io_Command = command; 
  325.    iob->ioa_Request.io_Flags   = IOF_QUICK; 
  326.  
  327.    BeginIO(iob); 
  328.    WaitIO(iob); 
  329.    rtn = ((LONG)(iob->ioa_Request.io_Error)); 
  330.    return(rtn); 
  331.   
  332. struct ExtIOB * 
  333. GetIOB(ch) 
  334.    LONG ch; 
  335.    WORD i,use_reply; 
  336.    struct ExtIOB *iob;  /* in case we need to allocate one */ 
  337.    ReEmployIOB();    /* find already used ones and free them */ 
  338.               /* so that when we do a get... */ 
  339.    if(ch == -1)  use_reply = 0;  /* which reply port to use */ 
  340.    else          use_reply = ch; 
  341.  
  342.    for(i=0; i<AUDBUFFERS; i++) 
  343.    {    
  344.         if(inuse[i] == NO) 
  345.         {    
  346.                 inuse[i] = YES; 
  347.  
  348.                 audbuffer[i].ioa_Request.io_Device    = device; 
  349.                 audbuffer[i].ioa_Request.io_Message.mn_ReplyPort =  
  350.                                                 replyPort[use_reply]; 
  351.                 audbuffer[i].ioa_Request.io_Message.mn_Length = i; 
  352.                 audbuffer[i].ioa_Request.io_Message.mn_Node.ln_Name =  
  353.                                                 globalname; 
  354. #ifdef DEBUG 
  355.       printf("Using global iob\n"); 
  356. #endif DEBUG 
  357.       return(&audbuffer[i]); 
  358.        } 
  359.    } 
  360.    /* if all globals are in use, have to allocate one */ 
  361.    iob = (struct ExtIOB *)AllocMem(sizeof(struct ExtIOB), 
  362.                      MEMF_CLEAR | MEMF_FAST ); 
  363.    if(iob == 0) return(0);   /* out of memory */ 
  364.    else 
  365.    {    
  366.         iob->ioa_Request.io_Device = device; 
  367.         iob->ioa_Request.io_Message.mn_ReplyPort =  
  368.                replyPort[use_reply]; 
  369.         iob->ioa_Request.io_Message.mn_Node.ln_Name =  
  370.                dynamicname; 
  371.         iob->ioa_Request.io_Message.mn_Length = dynamix[use_reply]; 
  372.         dynamix[use_reply] += 1; /* add one to number allocated 
  373.                                   * for a specific channel */ 
  374. #ifdef DEBUG 
  375.         printf("Using dynamic iob\n"); 
  376. #endif DEBUG 
  377.         return(iob); 
  378.         } 
  379. return(0); 
  380.   
  381.   
  382. /* Free a global or an allocated IOB */ 
  383. int 
  384. FreeIOB(iob, ch) 
  385.    struct ExtIOB *iob; 
  386.    LONG ch;   /* which channel was it attached to? */ 
  387.    WORD i; 
  388.  
  389.    if(iob->ioa_Request.io_Message.mn_Node.ln_Name == dynamicname) 
  390.    {    
  391.         FreeMem(iob, sizeof(struct ExtIOB)); 
  392.         if(dynamix[ch]) dynamix[ch] -= 1; /* subtract one if nonzero */ 
  393.         return(0L); 
  394.    } 
  395.    else if(iob->ioa_Request.io_Message.mn_Node.ln_Name == globalname) 
  396.    {     
  397.         i = iob->ioa_Request.io_Message.mn_Length; 
  398.  
  399.         if(i < AUDBUFFERS) 
  400.         {    
  401.                 inuse[i] = NO;   /* frees this one for reuse */ 
  402.         } 
  403.         return(0L); 
  404.    } 
  405.    /* if get here, the names don't match... something is wrong.*/ 
  406.    else  
  407.    {    
  408.                 printf("FreeIOB: names don't match...unknown error\n"); 
  409.                 return(-1);   /* unknown source of IOB fed to routine. */ 
  410.    } 
  411. return(0); 
  412.   
  413. /* Initialize an audio I/O block for default CMD_WRITE operation. */ 
  414. int 
  415. InitBlock(iob, channel) 
  416.      struct ExtIOB *iob; 
  417.    WORD channel; 
  418.    /* Device and ReplyPort fields have been initialized by GetIOB */ 
  419.    iob->ioa_Request.io_Unit = unit[channel]; 
  420.  
  421.    /* Allocation key */ 
  422.    iob->ioa_AllocKey = key[channel]; 
  423.  
  424.    /* Where is the waveform?  Just be sure is in MEMF_CHIP!!! */ 
  425.  
  426.    /* USER initializes datalength[ch] before calling this;    */ 
  427.    /* for sampled sound command write operation.              */ 
  428.    iob->ioa_Data    = chipaudio[channel]; 
  429.    iob->ioa_Length = datalength[channel]; 
  430.  
  431.    /* Another routine, must initialize: 
  432.  
  433.       period      ioa_Period 
  434.       volume      ioa_Volume 
  435.       cycles      ioa_Cycles 
  436.       message      ioa_WriteMsg 
  437.    */ 
  438.    /* Default command type is CMD_WRITE */ 
  439.    iob->ioa_Request.io_Command = CMD_WRITE; 
  440.  
  441.    /* If IOF_QUICK is zeroed, this would affect the 
  442.     * period and volume.  If a CMD_WRITE, it queues if 
  443.     * another note is already playing.  We queue CMD_WRITES. 
  444.     */ 
  445.    iob->ioa_Request.io_Flags = ADIOF_PERVOL; 
  446.    return(0); 
  447.   
  448.   
  449. /* To request "any" channel, use ch = -1; 
  450.  * To request a specific channel, use ch = {0, 1, 2 or 3}; 
  451.  * Again NOTE, this returns two globals as well as the channel number! 
  452.  */ 
  453.  
  454. int 
  455. GetChannel(ch) 
  456.    LONG ch; 
  457.    int error, value; 
  458.    struct ExtIOB *iob, controlIOB; 
  459.  
  460.    iob = &controlIOB; 
  461.    iob->ioa_Request.io_Device    = device; 
  462.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort; 
  463.  
  464.    InitBlock(iob,0);   /* init it for CMD_WRITE, then change */ 
  465.  
  466.    iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20; 
  467.    iob->ioa_Request.io_Command = ADCMD_ALLOCATE; 
  468.  
  469.    if(ch == -1) 
  470.    {    
  471.         iob->ioa_Data = (UBYTE *)anychan; 
  472.         iob->ioa_Length = 4; 
  473.    } 
  474.    else if(ch >=0 && ch <= 3) 
  475.    {     
  476.         /* Test to be sure that this channel is now free.  If 
  477.          * usertask[i] is not zero, either the current task  
  478.          * has already allocated this channel, or a different 
  479.          * task is now using it.  
  480.          */ 
  481.  
  482.         /* NOTE ***** ENHANCEMENT COMING HERE ***** */ 
  483.  
  484.         if(usertask[ch] != 0) return(-1); 
  485.  
  486.         /* Enhancement might be: look at the running priority 
  487.          * of the current task as compared to the running priority 
  488.          * of the task in usertask[i].  If not same task and if 
  489.          * the current task has a higher priority, STEAL the channel! 
  490.          * Alternative (seems better) is to have a global variable 
  491.          * called audPriority to be set by a new function SetAudPriority 
  492.          * (for a given task only), and that global priority value 
  493.          * would be used for GetChannel and LockChannel requests. 
  494.          */ 
  495.         iob->ioa_Data = (UBYTE *)(&anychan[ch]); 
  496.         iob->ioa_Length = 1; 
  497.    } 
  498.    else   /* chose a bad channel number; cannot allocate it */ 
  499.    {    
  500.         return(-1); 
  501.    } 
  502.    iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK; 
  503.    BeginIO(iob);  
  504.    error = WaitIO(iob);  /* returns nonzero if error */ 
  505.    if(!(iob->ioa_Request.io_Flags & IOF_QUICK)) 
  506.    {    
  507.         GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort); 
  508.    } 
  509.    if(error) 
  510.    {    
  511.         return(-1); 
  512.    } 
  513.    switch((LONG)(iob->ioa_Request.io_Unit)) 
  514.    {    
  515.         case  1:   value = 0;   break; 
  516.         case  2:   value = 1;   break; 
  517.         case  4:   value = 2;   break; 
  518.         case  8:   value = 3;   break; 
  519.         default:   value = -1;  break; 
  520.    } 
  521.    if(value == -1) return(-1L); 
  522.  
  523.    unit[value]     = (iob->ioa_Request.io_Unit); 
  524.    key[value]      = (iob->ioa_AllocKey); 
  525.    usertask[value] = FindTask(0);       /* THIS user task owns it now */ 
  526.  
  527.    return(value); 
  528.  
  529. int 
  530. FreeChannel(channel) 
  531.    LONG channel; 
  532.    int error; 
  533.    struct ExtIOB *iob, controlIOB; 
  534.  
  535.    if(channel < 0 || channel > 3)       return(BAD_CHANNEL_SELECTED); 
  536.    if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL); 
  537.  
  538.    iob = &controlIOB; 
  539.    iob->ioa_Request.io_Device    = device; 
  540.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort; 
  541.  
  542.    InitBlock(iob,channel);      /* init it for CMD_WRITE, then change it */ 
  543.                                 /* (pick up unit, key value for channel) */ 
  544.    iob->ioa_Request.io_Command = ADCMD_FREE; 
  545.    iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK; 
  546.    BeginIO(iob);  
  547.    error = WaitIO(iob);  /* returns nonzero if error */ 
  548.  
  549.    /* Educational note - the docs indicate that audio, even though  
  550.     * told to do an io item as "quickio", can, if it chooses, not do 
  551.     * it quick.  In that case, the reply is queued to the 
  552.     * reply port and it has to be removed.  That is why 
  553.     * the following status check has been installed here. 
  554.     * (Actually this check probably can be removed because 
  555.     * WaitIO, just above, removes the message from the port 
  556.     * if it does arrive there after all.) 
  557.     */ 
  558.    if(!(iob->ioa_Request.io_Flags & IOF_QUICK)) 
  559.    {     
  560.         GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort); 
  561.    } 
  562.    usertask[channel] = 0;       /* free again... */ 
  563.    if(error) 
  564.    {    
  565.         return(error); 
  566.    } 
  567.    return(0); 
  568.   
  569. /* SOME OF THE FOLLOWING ROUTINES ARE PARAPHRASED FROM A USENET and BIX 
  570.  * POSTING MADE IN 1985 BY STEVEN A. BENNETT.   
  571.  */ 
  572. /* I have modified his routines to queue the audio commands in  
  573.  * place of starting forever-duration and canceling each note. 
  574.  * Many of his original comments have been incorporated into 
  575.  * the article.  
  576.  */ 
  577.  
  578. /* PlayNote(...) */ 
  579.  
  580. /* ******************************************************************* */ 
  581. /* NOTE: There are some differences in PlayNote as compared to the article. 
  582.  * See the audiotools.DOC for details. 
  583.  */ 
  584. /* ******************************************************************* */ 
  585.  
  586. /* Starts a sound on the channel with specified period and volume. */ 
  587. /* This nice little routine takes a note and plays it on the given 
  588.  * voice.  The note is basically an integer from 
  589.  * 0 to 11 (c to b) plus 12 per octave above the first and lowest.  
  590.  * 
  591.  * The waveform to use is determined by adding an index (woffsets[])  
  592.  * dependant on the octave. 
  593.  * 
  594.  * The length of the waveform (in wlen[]) is likewise dependant on 
  595.  * the octave.  Note that octaves start with zero, not one. 
  596.  */ 
  597. int  
  598. PlayNote(channel, note, wf, vol, duration, priority, messageport, id) 
  599.    char *wf;   /* waveform to use */ 
  600.    LONG vol, channel, duration, note;   /* specific note number */ 
  601.    LONG priority; 
  602.    struct MsgPort *messageport; 
  603.    LONG id; 
  604.    LONG per, len, oct;   /* period, length of waveform, which octave */ 
  605.    char *wavepointer;   /* where to find start of waveform */ 
  606.    struct ExtIOB *iob; 
  607.    int frequency; 
  608.  
  609.    if(channel < 0 || channel > 3)       return(BAD_CHANNEL_SELECTED); 
  610.    if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL); 
  611.    if(note < 0 || note > 95)            return(OUT_OF_RANGE_FREQ); 
  612.    if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI); 
  613.  
  614.    iob = GetIOB(channel); 
  615.   
  616.    if(iob != 0) 
  617.    { 
  618.         InitBlock(iob, channel);   /* set up for CMD_WRITE */ 
  619.     
  620.         oct = note / 12; 
  621.         wavepointer = wf + woffsets[oct]; 
  622.         len = wlen[oct]; 
  623.         per = perval[note % 12]; 
  624.  
  625.         /* Set the parameters */ 
  626.         iob->ioa_Data = (UBYTE *)wavepointer; 
  627.         iob->ioa_Length = len; 
  628.         iob->ioa_Period = per; 
  629.         iob->ioa_Volume = vol; 
  630.  
  631.         iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority; 
  632.  
  633.         /* additions for support of tell-me-when-note-starts */ 
  634.  
  635.         iob->iob_Identifier = id; 
  636.  
  637.         /* Initialize message port.  If 0, then no pushing back 
  638.          * of a message.  If nonzero, message gets recirculated 
  639.          * by ReEmployIOB until the user finally acknowledges it 
  640.          * by using MayGetNote. 
  641.          */ 
  642.         iob->ioa_WriteMsg.mn_ReplyPort = messageport; 
  643.  
  644.         if(messageport != 0) 
  645.         { 
  646.                 /* If 0, no sending message when note plays; 
  647.                  * if nonzero, user gets a message that can 
  648.                  * be read by MayGetNote.  uport, received from  
  649.                  * InitAudio, is where the message goes */ 
  650.  
  651.             /* Tell the audio device to "reply" to this message */ 
  652.             iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE; 
  653.         } 
  654.   
  655.    /* Look at the frequency that it is to play by backwards calc. */ 
  656.    frequency = 3579545 / (len * per); 
  657.  
  658.    /* Calculate cycles from duration in 1000ths of a second */ 
  659.    /* Multiply all-in-one to maintain max precision possible */ 
  660.    /* (all integer arithmetic.) */ 
  661.  
  662.    iob->ioa_Cycles = ((LONG)(frequency * duration)/1000); 
  663.    BeginIO(iob); 
  664.    return(0);      /* all went ok */ 
  665.    } 
  666. return(-1);     /* (else-part) iob was zero, couldn't do the above. */ 
  667.  
  668.    
  669. /* SetPV(channel, per, vol) 
  670.  *   int channel, per, vol; 
  671.  */ 
  672. int  
  673. SetPV(channel, per, vol) 
  674.    int channel, per, vol; 
  675.    { 
  676.    int error; 
  677.    struct ExtIOB *iob, controlIOB; 
  678.  
  679.    if(channel < 0 || channel > 3)       return(BAD_CHANNEL_SELECTED); 
  680.    if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL); 
  681.  
  682.    iob = &controlIOB; 
  683.    iob->ioa_Request.io_Device    = device; 
  684.    iob->ioa_Request.io_Message.mn_ReplyPort = controlPort; 
  685.  
  686.    InitBlock(iob, channel);   /* set up for CMD_WRITE */ 
  687.     
  688.    iob->ioa_Period = per; 
  689.    iob->ioa_Volume = vol; 
  690.    iob->ioa_Request.io_Command = ADCMD_PERVOL; 
  691.    iob->ioa_Request.io_Flags   = IOF_QUICK | ADIOF_PERVOL; 
  692.    BeginIO(iob);   /* This one will be synchronous; affects whatever 
  693.           * is playing on this channel at this time. 
  694.           */ 
  695.    error = WaitIO(iob);   /* OK to wait, since it will return */ 
  696.    return(error);      /* copy of io_Error field; should be 0 */ 
  697.   
  698. /* SetWaves(w1, w2, w3): create first sawtooth, triangle and square wave */ 
  699.  
  700. SetWaves(w1, w2, w3) 
  701.    UBYTE *w1, *w2, *w3; 
  702.    int i, increment, value, sqvalue; 
  703.    value = 0; increment = 2; 
  704.    sqvalue = 127; 
  705.  
  706.    for (i = 0; i < BIG_WAVE; ++i) 
  707.    { 
  708.    w1[i] = i;   /* do the sawtooth */ 
  709.  
  710.    if(i > 62 && i < 180) increment = -2; 
  711.    else 
  712.    if(i >= 180) increment = 2; 
  713.  
  714.    w2[i] = value;  value += increment;  /* triangle wave */ 
  715.  
  716.    if(i > 126) sqvalue = -127; 
  717.  
  718.    w3[i] = sqvalue; 
  719.    } 
  720. return(0); 
  721.    
  722. /* ExpandWave(wfp) - replicate waves in decreasing sample sizes 
  723.  *   BYTE *wfp; 
  724.  */ 
  725.   
  726. ExpandWave(wfp) 
  727.    BYTE *wfp; 
  728.    { 
  729.    int i, j, rate; 
  730.    BYTE *tptr; 
  731.   
  732.    rate = 1; 
  733.    tptr = wfp + BIG_WAVE; 
  734.    for (i = 0; i < NBR_WAVES - 1; ++i) 
  735.       { 
  736.       rate *= 2; 
  737.       for (j = 0; j < BIG_WAVE; j += rate) 
  738.          *tptr++ = wfp[j]; 
  739.       } 
  740.    return(0); 
  741.    } 
  742.    
  743. /* MakeWaves() 
  744.  * 
  745.  *   Just makes a sawtooth, triangle and square wave in chip mem  
  746.  * and expands them. 
  747.  */ 
  748. int  
  749. MakeWaves() 
  750.    /* allocate the memory for the waveforms. 
  751.     */ 
  752.    w1 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP); 
  753.    w2 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP); 
  754.    w3 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP); 
  755.  
  756.    if (w1 == NULL || w2 == NULL || w3 == NULL) 
  757.    return(-1);   /* ran out of memory! */ 
  758.   
  759.    /* get and expand the waveforms    */ 
  760.  
  761.    SetWaves(w1, w2, w3); 
  762.    ExpandWave(w1);   chipaudio[0]=w1; 
  763.    ExpandWave(w2);   chipaudio[1]=w2; 
  764.    ExpandWave(w3);   chipaudio[2]=w3; 
  765.    return(0); 
  766.  
  767. /* =================================================================== */ 
  768. /* Here are NEW routines that did not appear in the AmigaWorld article. 
  769.  * These are in addition to the changes noted above for InitAudio and 
  770.  * FinishAudio, and changes in main() to accommodate them. 
  771.  */ 
  772.  
  773. /* PlayFreq in this version is for INTEGER values of frequency only. 
  774.  * 
  775.  * To be more precise, you'll have to convert the integer calculations 
  776.  * to floating point.   Almost identical to PlayNote, however, had to 
  777.  * add some error checking to the parameters AHEAD OF GetIOB because 
  778.  * if the frequency is out of range of what we have in our wave tables 
  779.  * currently, we have to reject the command.  If it got rejected 
  780.  * after Getting an IOB, then we'd have to free it. 
  781.  */ 
  782. int  
  783. PlayFreq(channel, freq, wf, vol, duration, priority, messageport, id) 
  784.    char *wf;   /* waveform to use */ 
  785.    LONG vol, channel, duration, freq;   /* specific integer frequency */ 
  786.    LONG priority; 
  787.    struct MsgPort *messageport; 
  788.    LONG id; 
  789.    LONG per, len, oct;   /* period, length of waveform, which octave */ 
  790.    char *wavepointer;   /* where to find start of waveform */ 
  791.    struct ExtIOB *iob; 
  792.    LONG i, isave, accept; 
  793.  
  794.    if(channel < 0 || channel > 3)       return(BAD_CHANNEL_SELECTED); 
  795.    if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL); 
  796.    if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI); 
  797.  
  798.    /* see if we CAN represent this frequency, if not, reject it */ 
  799.  
  800.    for(i=0; i<9; i++) 
  801.    { 
  802.            /* Now figure out which waveform to use... 
  803.             * start with the first wlen value because 
  804.             * we want to use the longest waveform we can. 
  805.             */ 
  806.         isave = i;    
  807.  
  808.         accept = FALSE; 
  809.  
  810.         per = 3579545 / (freq * wlen[i]); 
  811.  
  812.         if(per > 127 && per < 500) 
  813.         { 
  814.                 accept = TRUE; 
  815.                 break; 
  816.         } 
  817.    } 
  818.    if(accept != TRUE)                   return(OUT_OF_RANGE_FREQ); 
  819.  
  820.    iob = GetIOB(channel); 
  821.  
  822.    if(iob != 0) 
  823.    { 
  824.         InitBlock(iob, channel);   /* set up for CMD_WRITE */ 
  825.  
  826.         oct = isave;         /* show the octave we are in */ 
  827.         wavepointer = wf + woffsets[oct];   /* point to longest wave */ 
  828.         len = wlen[oct];      /* show its length */ 
  829.  
  830.         /* Set the parameters */ 
  831.         iob->ioa_Data = (UBYTE *)wavepointer; 
  832.         iob->ioa_Length = len; 
  833.         iob->ioa_Period = per; 
  834.         iob->ioa_Volume = vol; 
  835.  
  836.         iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority; 
  837.  
  838.         /* additions for support of tell-me-when-note-starts */ 
  839.         iob->iob_Identifier = id; 
  840.  
  841.         /* see note on same line for PlayNote above */ 
  842.         iob->ioa_WriteMsg.mn_ReplyPort = messageport; 
  843.  
  844.         if(messageport != 0) 
  845.         { 
  846.                 /* If 0, no sending message when note plays; 
  847.                  * if nonzero, user gets a message that can 
  848.                  * be read by MayGetNote.  uport, received from  
  849.                  * InitAudio, is what should be the value of 
  850.                  * messageport.  
  851.                  */  
  852.  
  853.             /* Tell the audio device to "reply" to this message */ 
  854.             iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE; 
  855.         } 
  856.  
  857.    /* Calculate cycles from duration in 1000ths of a second */ 
  858.    /* Multiply all-in-one to maintain max precision possible */ 
  859.    /* (all integer arithmetic.) */ 
  860.  
  861.    iob->ioa_Cycles = ((LONG)(freq * duration)/1000); 
  862.    BeginIO(iob); 
  863.    return(0);      /* all went ok */ 
  864.    } 
  865. return(-1L);    /* (else-part) iob value was zero, out of memory probably */ 
  866.  
  867.  
  868. /* Another new routine called MayGetNote.  Its syntax is: 
  869.  * 
  870.  *      note = MayGetNote(uport, flag) 
  871.  * 
  872.  * where uport is the address of the port you received from InitAudio. 
  873.  * 
  874.  *       flag  is a value of 0 or 1. 
  875.  * 
  876.  *          when flag = 0, the routine returns immediately, with 
  877.  *               a note value of 0 (no note available), or 
  878.  *               the value of the first note notification  
  879.  *               to arrive at the port. 
  880.  * 
  881.  *          when flag = nonzero, the routine will wait if (and only if) 
  882.  *               there is no note to be had.  In other words, 
  883.  *               you can cause your task to go to sleep until 
  884.  *               the next note begins to play.  You decide 
  885.  *               what to do for a specific note value. 
  886.  * 
  887.  *     note  is the value you placed into the PlayNote or PlayFreq 
  888.  *               identifier field.  A report of this type 
  889.  *               is ONLY generated when "messageport" is nonzero.  
  890.  *    
  891.  *   CAUTION - if there are no more notes with messageport nonzero in 
  892.  *         the queue and you specify TRUE for the flag, you can cause your 
  893.  *         task to sleep forever!! 
  894.  */ 
  895.  
  896. LONG 
  897. MayGetNote(uport, flag) 
  898.    struct MsgPort *uport; 
  899.    LONG flag; 
  900.    struct auMsg *aum; 
  901.    LONG wakemask; 
  902.    struct Message *writemsg; 
  903.    
  904. grabmessage: 
  905.    aum = (struct auMsg *)GetMsg(uport);   /* is a message there? */ 
  906.  
  907.    if(aum)   /* There was a message! */  
  908.    { 
  909.       /* The user has seen this msg, so the system can deallocate 
  910.        * the iob in which it occurs anytime in the future. 
  911.        * Now that we have received the message at our own reply 
  912.        * port, it belongs to us and we can do whatever we want 
  913.        * to it.  Set the reply port value to zero now, as a signal 
  914.        * to FreeIOB that it can really do that!  
  915.        */ 
  916.       aum->au_Message.mn_ReplyPort = 0; 
  917.  
  918.       return(aum->aum_Identifier);  /* return the value */ 
  919.    } 
  920.    if(flag) /* If nonzero, we cause caller to sleep while 
  921.              * waiting for any identified iob to appear. 
  922.              */ 
  923.    { 
  924.       wakemask = WaitPort(uport);  /* not using wakemask */ 
  925.       flag = 0; 
  926.       goto grabmessage;   /* a goto!  aaarrrggghhh! */ 
  927.    } 
  928. return(0L);    
  929.  
  930. /* ReEmployIOB is not new (as compared to article) but it 
  931.  * has changed a lot since the article was written. 
  932.  */ 
  933.  
  934. /* ReEmployIOB - look at ALL of the reply ports and if any IOBs 
  935.  *        hanging around with nothing to do, free them. 
  936.  * 
  937.  *       In the article, this routine removed iob's from the 
  938.  *       reply ports via the usual GetMsg function.  This 
  939.  *       was not practical in this case when adding the support 
  940.  *       for audio messages from the device.   
  941.  * 
  942.  *  Problem was this: 
  943.  *       Audio may still be playing the waveform as we get a 
  944.  *       message through MayGetNote.  MayGetNote marks the 
  945.  *       iob message block as free-able, (when it finds that 
  946.  *       the identifier field is set to zero) but we have to have a 
  947.  *       way of recirculating in this list of messages. 
  948.  * 
  949.  *       In other words, if something is free-able, free it, 
  950.  *       otherwise leave it on the list.  So rather than 
  951.  *       removing things from the front of the list, lets just 
  952.  *       walk through the message list, Remove (dequeue) what is  
  953.  *       freeable and leave the rest there to look at the next time. 
  954.  */ 
  955.  
  956. ReEmployIOB() 
  957.    LONG i; 
  958.    struct MsgPort *mp; 
  959.    struct ExtIOB *iob; 
  960.  
  961.    /* now declare a pointer to "the first iob pushed back onto the port" */ 
  962.  
  963.    struct ExtIOB *pushback;      
  964.  
  965.    /* What happens here is that iob's are removed from the message port 
  966.     * when they come back from the audio device.   If YOU have set the 
  967.     * messageport nonzero, it means that you wanted to know when 
  968.     * this note began to play.  The WriteMsg part of the iob is then 
  969.     * linked, as a message, onto your user port.  So this routine here 
  970.     * cannot free the iob until it is certain that YOU have finished 
  971.     * using it.  The iob_Priority field is READ here.  If it still 
  972.     * nonzero, the iob is pushed back onto the message port (on the 
  973.     * end of the message queue) to be read again.  We hold a pointer 
  974.     * named pushback that lets us keep track of when we see that 
  975.     * again.  If we see it twice, it means we have completed a full 
  976.     * circle through the queue of messages and have freed everything 
  977.     * that we can this time.  Therefore, we examine it and either 
  978.     * free it or push it back again, then exit. 
  979.     */ 
  980.  
  981.    /* Tiny drawback to the pushback that we do is that there can 
  982.     * be more than one dynamic IOB with the same identifier value. 
  983.     * The id value is not used anywhere, execept for the debug 
  984.     * reporting, so it is not critical.  Note that this is not 
  985.     * a problem with a pushback of a static iob because the inuse[] 
  986.     * array is being maintained for the statics. 
  987.     */ 
  988.    for(i=0; i<4; i++)   /* remove all iob's from ALL ports */ 
  989.                         /* (that is, unless we have to push one back) */ 
  990.    {    
  991.       mp = replyPort[i]; 
  992.  
  993.       pushback = (struct ExtIOB *)0;    /* nothing pushed back so far */ 
  994.  
  995.       while((iob = (struct ExtIOB *)GetMsg(mp)) != 0) 
  996.       {  
  997.             /* Here is what triggers the Identifier message for MayGetNote */ 
  998.  
  999.             /* First see if messageport in WriteMsg is greater than zero; 
  1000.              * if so, audio device is done, but user hasnt acknowledged 
  1001.              * this message yet (by using MayGetNote). 
  1002.              */ 
  1003.             if(iob->ioa_WriteMsg.mn_ReplyPort != 0) 
  1004.             { 
  1005.                 /* If we get here, we already know we will push it back */ 
  1006.                 /* because User has not yet freed it (using MayGetNote) */ 
  1007.  
  1008.                 PutMsg(mp, iob); 
  1009.  
  1010.                 if(iob == pushback) /* If so, we saw it already */ 
  1011.                 { 
  1012.                         break;     /* Go out to empty next port */ 
  1013.                 } 
  1014.                 if(pushback == 0) 
  1015.                 { 
  1016.                         pushback = iob; /* Remember FIRST one pushed back */ 
  1017.                 } 
  1018.             } 
  1019.             else        /* messageport value is zero, can free the iob */ 
  1020.             { 
  1021. #ifdef DEBUG 
  1022.                 printf("freeing %ls iob; ID=%ld\n", 
  1023.                       iob->ioa_Request.io_Message.mn_Node.ln_Name, 
  1024.                       iob->ioa_Request.io_Message.mn_Length); 
  1025. #endif DEBUG 
  1026.                 FreeIOB(iob,i); 
  1027.             } 
  1028.       } 
  1029.  
  1030.    } 
  1031.    return(0); 
  1032.  
  1033. /* Use this next function to determine if you (still) own a particular channel. 
  1034.  * The audio device has an arrangement by which a higher priority request 
  1035.  * for a channel than the one that already owns it can be made.  The higher 
  1036.  * priority request can actually cause a channel to be stolen from a user. 
  1037.  * This feature may be implemented in a future version of audiotools, 
  1038.  * (shared library version), in which, depending on the task's running 
  1039.  * priority itself, a higher priority task could succeed at GetChannel 
  1040.  * for a channel that is already owned by another task. 
  1041.  */ 
  1042.  
  1043. int 
  1044. IsThatMyChan(channel)   /* returns nonzero if YOU still own the channel */ 
  1045.         LONG channel; 
  1046.         if(channel < 0 || channel > 3)          return(0); 
  1047.         if(usertask[channel] == FindTask(0))    return(TRUE); 
  1048.         return(0); 
  1049.  
  1050.  
  1051. /*  end of file */
  1052.