home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d517 / cwtoy.lha / CWToy / src / audiotools2.c next >
C/C++ Source or Header  |  1991-07-20  |  34KB  |  1,093 lines

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