home *** CD-ROM | disk | FTP | other *** search
- /* audiotools.c */
-
- #define DEBUG 1
-
- #include "exec/types.h"
- #include "exec/memory.h"
- #include "devices/audio.h"
- #include "ram:audiotools.h"
- #include "ram:globals.c"
- extern APTR AllocMem();
- extern struct Message *GetMsg();
-
- main()
- {
- LONG i, channel, error;
-
- InitAudio();
-
- for(i=0; i<4; i++)
- { channel = GetChannel(-1);
- if(channel == -1) finishup("cannot get a channel!");
-
- /* At this point, must save globals from gotkey, gotunit */
- key[i] = gotkey; /* save allocation key */
- unit[i] = gotunit; /* save unit value */
-
- error = StopChannel(channel);
- if(error)
- { printf("error in stopping channel = %ld\n",error);
- finishup("StopChannel did not work as expected");
- }
- }
- /* (channel, note, waveform, vol, duration, priority,message) */
-
- for(i=0; i<95; i++)
- {
- PlayNote(0, i, w1, 32, 250, 0, 0); /* all notes, 1/4 sec. */
- }
- error = StartChannel(0);
- Delay(800); /* let most of them play... this waits 16 seconds */
-
- for(i=1; i<4; i++)
- { error = StartChannel(i);
- if(error) printf("error starting channel = %ld\n",error);
- }
- PlayNote(0, 23, w1, 32, 2000, 0, 0);
- PlayNote(1, 27, w2, 32, 2300, 0, 0);
- PlayNote(2, 30, w3, 32, 2600, 0, 0);
- PlayNote(3, 35, w1, 32, 2900, 0, 0);
-
- FinishAudio();
- return(0);
- } /* end of main() */
-
- InitAudio()
- {
- int error,i;
-
- /* Declare all message blocks available */
- for(i=0; i<AUDBUFFERS; i++) { inuse[i] = NO; }
-
- /* Open device but don't allocate channels */
- openIOB.ioa_Length = 0; /* (no allocation table) */
-
- error = OpenDevice("audio.device",0,&openIOB,0);
- if(error) finishup ("audio device won't open!");
-
- /* Get the device address for later use */
- device = openIOB.ioa_Request.io_Device;
-
- /* Create ports for replies from each channel as well as
- * one port to be used for the control and synchonous functions */
- for(i=0; i<4; i++)
- { auReplyPort = CreatePort(0,0);
- replyPort[i] = auReplyPort;
- if(auReplyPort == 0) finishup("cannot create a port!");
- chipaudio[i] = 0; /* have not yet created the waves */
- datalength[i] = 1; /* used for custom sound samples */
- }
- controlPort = CreatePort(0,0);
- if(controlPort == 0) finishup("can't create control port");
- error = MakeWaves();
- if(error == -1) finishup("waves won't fit in RAM!");
-
- for(i=0; i<4; i++)
- { dynamix[i] = 0; } /* no dynamic I/O blocks allocated
- * for any channel thus far */
- return(0);
- }
-
- FinishAudio()
- {
- LONG i;
- struct ExtIOB *iob;
- for(i=0; i<AUDBUFFERS; i++)
- { if(inuse[i] == YES)
- { /* make sure all global blocks are done */
- WaitIO(&audbuffer[i]);
- }
- }
- #ifdef DEBUG
- printf("All global I/O blocks are done\n");
- printf("channels 0,1,2,3 have %ld,%ld,%ld,%ld blocks in play\n",
- dynamix[0], dynamix[1], dynamix[2], dynamix[3]);
- #endif DEBUG
- for(i=0; i<4; i++)
-
- { if(dynamix[i]) /* If this channel still playing a */
- /* dynamically allocated block, wait */
- /* for all messages to return before */
- /* the program exits. */
- {
- emptyit:
- iob = (struct ExtIOB *)GetMsg(replyPort[i]);
- if(iob == 0 && dynamix[i] != 0) /* if no message arrived... */
- { WaitPort(replyPort); /* wait for I/O done */
- goto emptyit; /* and empty the port */
- }
- FreeIOB(iob,i);
- if(dynamix[i] != 0) goto emptyit;
- }
- }
- for(i=0; i<4; i++) FreeChannel(i);
- finishup("Done!\n");
- return(0);
- }
-
- finishup(string)
- char *string;
- {
- int i;
-
- if(device) CloseDevice(&openIOB);
- printf("closed the device\n");
-
- for(i=0; i<4; i++)
- { if(chipaudio[i]) FreeMem(chipaudio[i],WAVES_TOTAL);
- if(replyPort[i])
- DeletePort(replyPort[i]);
- }
- if(controlPort) DeletePort(controlPort);
- printf("%ls\n",string);
- exit(0);
- return(0);
- }
-
- int
- ControlChannel(channel, command)
- WORD channel;
- WORD command;
- {
- LONG rtn;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,channel); /* init it for CMD_WRITE, then change */
-
- iob->ioa_Request.io_Command = command;
- iob->ioa_Request.io_Flags = IOF_QUICK;
-
- BeginIO(iob);
- WaitIO(iob);
- rtn = ((LONG)(iob->ioa_Request.io_Error));
- return(rtn);
- }
-
- struct ExtIOB *
- GetIOB(ch)
- LONG ch;
- {
- WORD i,use_reply;
- struct ExtIOB *iob; /* in case we need to allocate one */
- ReEmployIOB(); /* find already used ones and free them */
- /* so that when we do a get... */
- if(ch == -1) use_reply = 0; /* which reply port to use */
- else use_reply = ch;
-
- for(i=0; i<AUDBUFFERS; i++)
- { if(inuse[i] == NO)
- { inuse[i] = YES;
- audbuffer[i].ioa_Request.io_Device = device;
- audbuffer[i].ioa_Request.io_Message.mn_ReplyPort =
- replyPort[use_reply];
- audbuffer[i].ioa_Request.io_Message.mn_Length = i;
- audbuffer[i].ioa_Request.io_Message.mn_Node.ln_Name =
- globalname;
- #ifdef DEBUG
- printf("Using a global iob\n");
- #endif DEBUG
- return(&audbuffer[i]);
- }
- }
- /* if all globals are in use, have to allocate one */
- iob = (struct ExtIOB *)AllocMem(sizeof(struct ExtIOB),
- MEMF_CLEAR);
- if(iob == 0) return(0); /* out of memory */
- else
- { iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort =
- replyPort[use_reply];
- iob->ioa_Request.io_Message.mn_Node.ln_Name =
- dynamicname;
- iob->ioa_Request.io_Message.mn_Length = dynamix[use_reply];
- dynamix[use_reply] += 1; /* add one to number allocated
- * for a specific channel */
- #ifdef DEBUG
- printf("Allocated a new dynamic iob\n");
- #endif DEBUG
- return(iob);
- }
- return(0);
- }
-
- /* ReEmployIOB - look at all of the reply ports and if any IOBs
- * hanging around with nothing to do, free them.
- */
- ReEmployIOB()
- {
- LONG i;
- struct MsgPort *mp;
- struct ExtIOB *iob;
-
- for(i=0; i<4; i++) /* remove all iob's from all ports */
- { mp = replyPort[i];
- while( (iob = (struct ExtIOB *)GetMsg(mp)) != 0)
- {
- #ifdef DEBUG
- printf("type of iob freed is: %ls\n",
- iob->ioa_Request.io_Message.mn_Node.ln_Name);
- printf("its identifier value is: %ld\n",
- iob->ioa_Request.io_Message.mn_Length);
- #endif DEBUG
- FreeIOB(iob, i);
- }
- }
- return(0);
- }
-
- /* Free a global or an allocated IOB */
- int
- FreeIOB(iob, ch)
- struct ExtIOB *iob;
- LONG ch; /* which channel was it attached to? */
- {
- WORD i;
-
- if(iob->ioa_Request.io_Message.mn_Node.ln_Name == dynamicname)
- { FreeMem(iob, sizeof(struct ExtIOB));
- if(dynamix[ch]) dynamix[ch] -= 1; /* subtract one if nonzero */
- return(0L);
- }
- else if(iob->ioa_Request.io_Message.mn_Node.ln_Name == globalname)
- { i = iob->ioa_Request.io_Message.mn_Length;
- #ifdef DEBUG
- printf("Freeing global buffer numbered %ld\n",i);
- #endif DEBUG
- if(i < AUDBUFFERS)
- { inuse[i] = NO; /* frees this one for reuse */
- }
- return(0L);
- }
- /* if get here, the names don't match... something is wrong.*/
- else { printf("FreeIOB: names don't match...unknown error\n");
- return(-1); /* unknown source of IOB fed to routine. */
- }
- return(0);
- }
-
- /* Initialize an audio I/O block for default CMD_WRITE operation. */
- int
- InitBlock(iob, channel)
- struct ExtIOB *iob;
- WORD channel;
- {
- /* Device and ReplyPort fields have been initialized by GetIOB */
- iob->ioa_Request.io_Unit = unit[channel];
-
- /* Allocation key */
- iob->ioa_AllocKey = key[channel];
-
- /* Where is the waveform? Just be sure is in MEMF_CHIP!!! */
- /* USER initializes datalength[ch] before calling this; */
- /* for sampled sound command write operation. */
- iob->ioa_Data = chipaudio[channel];
- iob->ioa_Length = datalength[channel];
-
- /* Another routine, must initialize:
-
- period ioa_Period
- volume ioa_Volume
- cycles ioa_Cycles
- message ioa_WriteMessage
- */
- /* Default command type is CMD_WRITE */
- iob->ioa_Request.io_Command = CMD_WRITE;
-
- /* If IOF_QUICK is zeroed, this would affect the
- * period and volume. If a CMD_WRITE, it queues if
- * another note is already playing. We queue CMD_WRITES.
- */
- iob->ioa_Request.io_Flags = ADIOF_PERVOL;
- return(0);
- }
-
- /* To request "any" stereo pair, use pair = -1;
- * To request a specific stereo pair, use pair = {0, 1, 2 or 3}
- * corresponding to channels 0 and 1, 0 and 2, 1 and 2 or 1 and 3
- * respectively.
- */
- int
- GetStereoPair(pair)
- LONG pair;
- {
- int error, value;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,0); /* init it for CMD_WRITE, then change */
-
- /* set precedence of the request for a channel */
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
-
- /* Type of command is ALLOCATE */
- iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
- if(pair == -1)
- { /* Point to the allocation map */
- iob->ioa_Data = (UBYTE *)stereostuff;
-
- /* It contains 4 entries */
- iob->ioa_Length = 4;
- }
- else if(pair >=0 && pair <= 3)
- { iob->ioa_Data = (UBYTE *)(&stereostuff[pair]);
- iob->ioa_Length = 1;
- }
- else /* chose a bad channel pair; cannot allocate it */
- {
- return(-1);
- }
- /* Don't wait for allocation, channels
- * should be available! If we don't set
- * ADIOF_NOWAIT, the task will idle waiting
- * for a chance to allocate the channel,
- * looking again each time another task
- * allocates or frees a channel.
- */
- iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
-
- BeginIO(iob);
-
- error = WaitIO(iob); /* returns nonzero if error */
- if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
- { /* if flag not set, then the message
- * was appended to the reply port
- * (was not quick I/O after all) */
- GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
- }
- if(error)
- { return(-1);
- }
- /* Save the values... freeing the IOB on exit */
- gotunit = (iob->ioa_Request.io_Unit);
- gotkey = (iob->ioa_AllocKey);
-
- switch((LONG)(iob->ioa_Request.io_Unit))
- { case 3: value = 0; break;
- case 5: value = 1; break;
- case 10: value = 2; break;
- case 12: value = 3; break;
- default: value = -1; break;
- }
- return(value);
- }
-
- /* To request "any" channel, use ch = -1;
- * To request a specific channel, use ch = {0, 1, 2 or 3};
- */
- int
- GetChannel(ch)
- LONG ch;
- {
- int error, value;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,0); /* init it for CMD_WRITE, then change */
-
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
- iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
-
- if(ch == -1)
- { iob->ioa_Data = (UBYTE *)anychan;
- iob->ioa_Length = 4;
- }
- else if(ch >=0 && ch <= 3)
- { iob->ioa_Data = (UBYTE *)(&anychan[ch]);
- iob->ioa_Length = 1;
- }
- else /* chose a bad channel number; cannot allocate it */
- { return(-1);
- }
- iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
- BeginIO(iob);
- error = WaitIO(iob); /* returns nonzero if error */
- if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
- { GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
- }
- if(error)
- { return(-1);
- }
- gotunit = (iob->ioa_Request.io_Unit);
- gotkey = (iob->ioa_AllocKey);
- switch((LONG)(iob->ioa_Request.io_Unit))
- { case 1: value = 0; break;
- case 2: value = 1; break;
- case 4: value = 2; break;
- case 8: value = 3; break;
- default: value = -1; break;
- }
- return(value);
- }
-
- int
- FreeChannel(ch)
- LONG ch;
- {
- int error;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,ch); /* init it for CMD_WRITE, then change */
- /* (pick up unit and key value for channel) */
- iob->ioa_Request.io_Command = ADCMD_FREE;
- iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
- BeginIO(iob);
- error = WaitIO(iob); /* returns nonzero if error */
-
- if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
- { GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
- }
- if(error)
- { return(-1);
- }
- return(0);
- }
- /* NOTE: FreeChannel should work as FreeStereoPair(pair) too! */
-
- /* THE FOLLOWING ROUTINES ARE PARAPHRASED FROM A USENET and BIX
- * POSTING MADE IN 1985 BY STEVEN A. BENNETT.
- */
- /* I have modified his routines to queue the audio commands in
- * place of starting forever-duration and canceling each note.
- * Many of his original comments have been incorporated into
- * the article.
- */
-
- /* PlayNote(...) */
- /* Starts a sound on the channel with specified period and volume. */
- /* This nice little routine takes a note and plays it on the given
- * voice. The note is basically an integer from
- * 0 to 11 (c to b) plus 12 per octave above the first and lowest.
- *
- * The waveform to use is determined by adding an index (woffsets[])
- * dependant on the octave.
- *
- * The length of the waveform (in wlen[]) is likewise dependant on
- * the octave. Note that octaves start with zero, not one.
- */
- int
- PlayNote(channel, note, wf, vol, duration, priority, message)
- char *wf; /* waveform to use */
- LONG vol, channel, duration, note; /* specific note number */
- LONG priority;
- struct Message *message;
- {
- LONG per, len, oct; /* period, length of waveform, which octave */
- char *wavepointer; /* where to find start of waveform */
- struct ExtIOB *iob;
- int frequency;
- iob = GetIOB(channel);
-
- if(iob != 0)
- {
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- oct = note / 12;
- wavepointer = wf + woffsets[oct];
- len = wlen[oct];
- per = perval[note % 12];
-
- /* Set the parameters */
- iob->ioa_Data = (UBYTE *)wavepointer;
- iob->ioa_Length = len;
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
-
- /* PlayNote (continued) */
-
- /* Look at the frequency that it is to play by backwards calc. */
- frequency = 3579545 / (len * per);
-
- /* Calculate cycles from duration in 1000ths of a second */
- /* Multiply all-in-one to maintain max precision possible */
- /* (all integer arithmetic.) */
-
- iob->ioa_Cycles = ((LONG)(frequency * duration)/1000);
- BeginIO(iob);
- return(0); /* all went ok */
- }
- else
- { return(-1); /* couldnt get IOB */
- }
- return(0);
- }
-
- /* SetPV(channel, per, vol)
- * int channel, per, vol;
- */
- int
- SetPV(channel, per, vol)
- int channel, per, vol;
- {
- int error;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
- iob->ioa_Request.io_Command = ADCMD_PERVOL;
- iob->ioa_Request.io_Flags = IOF_QUICK | ADIOF_PERVOL;
- BeginIO(iob); /* This one will be synchronous; affects whatever
- * is playing on this channel at this time.
- */
- error = WaitIO(iob); /* OK to wait, since it will return */
- return(error); /* copy of io_Error field; should be 0 */
- }
-
- /* SetWaves(w1, w2, w3): create first sawtooth, triangle and square wave */
-
- SetWaves(w1, w2, w3)
- UBYTE *w1, *w2, *w3;
- {
- int i, increment, value, sqvalue;
- value = 0; increment = 2;
- sqvalue = 127;
-
- for (i = 0; i < BIG_WAVE; ++i)
- {
- w1[i] = i; /* do the sawtooth */
-
- if(i > 62 && i < 180) increment = -2;
- else
- if(i >= 180) increment = 2;
-
- w2[i] = value; value += increment; /* triangle wave */
-
- if(i > 126) sqvalue = -127;
-
- w3[i] = sqvalue;
- }
- return(0);
- }
-
- /* ExpandWave(wfp) - replicate waves in decreasing sample sizes
- * BYTE *wfp;
- */
-
- ExpandWave(wfp)
- BYTE *wfp;
- {
- int i, j, rate;
- BYTE *tptr;
-
- rate = 1;
- tptr = wfp + BIG_WAVE;
- for (i = 0; i < NBR_WAVES - 1; ++i)
- {
- rate *= 2;
- for (j = 0; j < BIG_WAVE; j += rate)
- *tptr++ = wfp[j];
- }
- return(0);
- }
-
- /* MakeWaves()
- *
- * Just makes a sawtooth, triangle and square wave in chip mem
- * and expands them.
- */
- int
- MakeWaves()
- {
- /* allocate the memory for the waveforms.
- */
- w1 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
- w2 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
- w3 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
-
- if (w1 == NULL || w2 == NULL || w3 == NULL)
- return(-1); /* ran out of memory! */
-
- /* get and expand the waveforms */
-
- SetWaves(w1, w2, w3);
- ExpandWave(w1); chipaudio[0]=w1;
- ExpandWave(w2); chipaudio[1]=w2;
- ExpandWave(w3); chipaudio[2]=w3;
- return(0);
- }
-