4 ** Copyright (C) 1994,95 by Bernardo Innocenti
6 ** audio.device interface routines.
9 ** A separate Process gets the AudioMsg messages from its pr_MsgPort
10 ** and dialogues with audio.device. Once executed, the AudioMsg's are
11 ** replied to the AudioReply port, and the main program frees them.
12 ** Each IOAudio request has a special USERDATA field which points back
13 ** to the original AudioMsg structure. This way it is possible to easly
14 ** track the status of each command being executed.
16 ** +------------+ +-------------+ +------------+
17 ** |Main Process|<-->AudioMsg<-->|Audio Process|<-->IOAudio<-->|audio.device|
18 ** +------------+ +-------------+ +------------+
21 #include <exec/memory.h>
22 #include <devices/audio.h>
24 #include <dos/dostags.h>
26 #include <proto/exec.h>
27 #include <proto/dos.h>
29 #include "XModulePriv.h"
34 /* This is the (maximum) size of sample chunks sent to the audio.device */
36 #define SAMPBUFSIZE 16768
37 #define MAXAUDIOSIZE 65534
39 /* Get/store pointer to original AudioMsg into an extended IOAudio structure */
41 #define AUDIOUSERDATA(ioa) (*((struct AudioMsg **)(((struct IOAudio *)ioa) + 1)))
46 struct Message am_Message;
56 struct IOAudio *am_IOA[2];
60 /* Values for AudioMsg->am_Command */
61 #define ACMD_PLAY_SAMPLE 1 /* am_Data points to 8 bit sample data */
62 #define ACMD_PLAY_INSTR 1 /* am_Data points to an Instrument structure */
66 /* Local functions prototypes */
68 static void ReplyAudioMsg (struct AudioMsg *am);
69 static void _PlaySample (struct AudioMsg *am);
70 static UWORD FindFreeChannel (void);
71 static ULONG AllocChannels (void);
72 static void FreeChannels (void);
78 static struct Process *AudioTask = NULL;
79 /* Audio IO requests. Two for each channel (double buffer). */
80 static struct IOAudio *AudioReq[4][2] = { 0 };
81 static struct MsgPort *AudioReply = NULL;
87 XDEF ULONG AudioSig = 0;
91 HOOKCALL static void AudioProcess (void)
93 ULONG audiosig, cmdsig, recsig, signals, err = 0;
97 struct MsgPort *AudioPort; /* Audio reply port */
100 struct Process *thistask = (struct Process *)FindTask (NULL);
102 if (AudioPort = CreateMsgPort())
104 /* Create IOAudio requests */
106 for (i = 0; i < 4 ; i++)
107 for (j = 0; j < 2; j++)
108 if (!(AudioReq[i][j] = (struct IOAudio *)CreateIORequest (AudioPort, sizeof (struct IOAudio) + 4)))
109 err = ERROR_NO_FREE_STORE;
113 /* Open audio.device */
114 if (!(err = OpenDevice ("audio.device", 0, (struct IORequest *)AudioReq[0][0], 0)))
115 err = AllocChannels();
118 else err = ERROR_NO_FREE_STORE;
121 /* Wait for startup message */
123 WaitPort (&thistask->pr_MsgPort);
124 am = (struct AudioMsg *) GetMsg (&thistask->pr_MsgPort);
126 /* Reply startup message */
128 ReplyMsg ((struct Message *)am);
132 Wait (SIGBREAKF_CTRL_C);
136 cmdsig = 1 << thistask->pr_MsgPort.mp_SigBit;
137 audiosig = 1 << AudioPort->mp_SigBit;
139 signals = cmdsig | audiosig | SIGBREAKF_CTRL_C;
143 recsig = Wait (signals);
145 if (recsig & audiosig)
147 /* Collect previously sent requests */
148 while (ioa = (struct IOAudio *) GetMsg (AudioPort))
150 if (am = AUDIOUSERDATA (ioa))
152 if (am->am_Actual < am->am_Len)
157 if (am->am_TmpBuf[am->am_Status])
159 len = min (SAMPBUFSIZE, am->am_Len - am->am_Actual);
160 samp = am->am_TmpBuf[am->am_Status];
161 CopyMem (((BYTE *)am->am_Data) + am->am_Actual, samp, len);
165 samp = ((BYTE *)am->am_Data) + am->am_Actual;
166 len = min (MAXAUDIOSIZE, am->am_Len - am->am_Actual);
169 /**/ioa->io_Command = CMD_WRITE;
170 /**/ioa->io_Flags = ADIOF_PERVOL;
171 ioa->ioa_Data = samp;
172 ioa->ioa_Length = len;
173 /**/ioa->ioa_Period = am->am_Per;
174 /**/ioa->ioa_Volume = am->am_Vol;
175 /**/ioa->ioa_Cycles = 1;
177 BeginIO ((struct IORequest *)ioa);
178 am->am_Actual += len;
183 am = AUDIOUSERDATA(ioa);
184 AUDIOUSERDATA(am->am_IOA[0]) = NULL;
185 AUDIOUSERDATA(am->am_IOA[1]) = NULL;
195 /* Get Command and execute it */
196 while (am = (struct AudioMsg *)GetMsg (&thistask->pr_MsgPort))
198 switch (am->am_Command)
200 case ACMD_PLAY_SAMPLE:
210 while (!(recsig & SIGBREAKF_CTRL_C));
220 for (i = 3; i >= 0 ; i--)
221 for (j = 1; j >= 0; j--)
224 if ((j == 0) && (AudioReq[i][j]->io_Device))
226 if (am = AUDIOUSERDATA(AudioReq[i][j]))
230 CloseDevice ((struct IORequest *)AudioReq[0][0]);
232 DeleteIORequest ((struct IORequest *)AudioReq[i][j]); AudioReq[i][j] = NULL;
235 DeleteMsgPort (AudioPort);
238 /* Signal that we are done.
239 * We Forbid() here to avoid being unloaded until exit.
242 Signal ((struct Task *)ThisTask, SIGF_SINGLE);
247 static void ReplyAudioMsg (struct AudioMsg *am)
249 FreeVec (am->am_TmpBuf[0]);
250 FreeVec (am->am_TmpBuf[1]);
251 AUDIOUSERDATA(am->am_IOA[0]) = NULL;
252 AUDIOUSERDATA(am->am_IOA[1]) = NULL;
253 ReplyMsg ((struct Message *)am);
258 static void _PlaySample (struct AudioMsg *am)
260 BYTE *samp = am->am_Data;
261 ULONG len = am->am_Len;
262 ULONG ch = FindFreeChannel ();
263 BOOL multi = FALSE, transfer = FALSE;
265 am->am_TmpBuf[0] = NULL;
266 am->am_TmpBuf[1] = NULL;
268 am->am_IOA[0] = AudioReq[ch][0];
269 am->am_IOA[1] = AudioReq[ch][1];
271 if (!(TypeOfMem (samp) & MEMF_CHIP))
274 if (am->am_Len > MAXAUDIOSIZE)
284 if (am->am_TmpBuf[0] = AllocVec (len, MEMF_CHIP))
286 CopyMem (samp, am->am_TmpBuf[0], len);
287 samp = am->am_TmpBuf[0];
291 am->am_IOA[0]->io_Command = CMD_WRITE;
292 am->am_IOA[0]->io_Flags = ADIOF_PERVOL;
293 am->am_IOA[0]->ioa_Data = samp;
294 am->am_IOA[0]->ioa_Length = len;
295 am->am_IOA[0]->ioa_Period = am->am_Per;
296 am->am_IOA[0]->ioa_Volume = am->am_Vol;
297 am->am_IOA[0]->ioa_Cycles = 1;
298 AUDIOUSERDATA(am->am_IOA[0]) = am;
300 BeginIO ((struct IORequest *)am->am_IOA[0]);
309 len = min(SAMPBUFSIZE, am->am_Len - SAMPBUFSIZE);
311 if (am->am_TmpBuf[1] = AllocVec (len, MEMF_CHIP))
313 CopyMem (samp, am->am_TmpBuf[1], len);
314 samp = am->am_TmpBuf[1];
318 else len = min (MAXAUDIOSIZE, am->am_Len - MAXAUDIOSIZE);
320 am->am_IOA[1]->io_Command = CMD_WRITE;
321 am->am_IOA[1]->io_Flags = ADIOF_PERVOL;
322 am->am_IOA[1]->ioa_Data = samp;
323 am->am_IOA[1]->ioa_Length = len;
324 am->am_IOA[1]->ioa_Period = am->am_Per;
325 am->am_IOA[1]->ioa_Volume = am->am_Vol;
326 am->am_IOA[1]->ioa_Cycles = 1;
327 AUDIOUSERDATA(am->am_IOA[1]) = am;
329 BeginIO ((struct IORequest *)am->am_IOA[1]);
330 am->am_Actual += len;
336 GLOBALCALL void HandleAudio (void)
340 while (msg = GetMsg (AudioReply))
341 FreeMem (msg, sizeof (struct AudioMsg));
346 GLOBALCALL void PlaySample (BYTE *samp, ULONG len, UWORD vol, UWORD per)
353 if (SetupAudio()) return;
355 if (!(am = AllocMem (sizeof (struct AudioMsg), MEMF_PUBLIC)))
358 am->am_Message.mn_ReplyPort = AudioReply;
359 am->am_Command = ACMD_PLAY_SAMPLE;
366 PutMsg (&AudioTask->pr_MsgPort, (struct Message *)am);
371 static UWORD FindFreeChannel (void)
375 for (ch = 0; ch < 4 ; ch++)
376 if (CheckIO ((struct IORequest *)AudioReq[ch][0]) &&
377 CheckIO ((struct IORequest *)AudioReq[ch][1]))
383 AbortIO ((struct IORequest *)AudioReq[0][0]);
384 AbortIO ((struct IORequest *)AudioReq[0][1]);
385 WaitIO ((struct IORequest *)AudioReq[0][0]);
386 WaitIO ((struct IORequest *)AudioReq[0][1]);
388 if (am = AUDIOUSERDATA(AudioReq[0][0]))
391 AUDIOUSERDATA(AudioReq[0][0]) = NULL;
392 AUDIOUSERDATA(AudioReq[0][1]) = NULL;
400 static ULONG AllocChannels (void)
405 /* Allocate channels */
407 for (i = 0 ; i < 4; i++)
409 static UBYTE channels[] = {1, 2, 4, 8};
411 ioa = AudioReq[i][0];
413 ioa->ioa_Request.ln_Pri = 1;
414 ioa->io_Device = AudioReq[0][0]->io_Device;
415 ioa->io_Command = ADCMD_ALLOCATE;
416 ioa->io_Flags = ADIOF_NOWAIT | IOF_QUICK;
417 ioa->ioa_AllocKey = AudioReq[0][0]->ioa_AllocKey;
418 ioa->ioa_Data = channels;
421 /* Using DoIO() here is not possible because the
422 * io_Flags field would be cleared.
424 BeginIO ((struct IORequest *)ioa);
425 WaitIO ((struct IORequest *)ioa);
427 /* Initailize other request */
428 CopyMem (ioa, AudioReq[i][1], sizeof (struct IOAudio));
431 return ioa->io_Error;
439 static void FreeChannels (void)
443 for (i = 3; i >= 0; i--)
447 AbortIO ((struct IORequest *)AudioReq[i][0]);
448 WaitIO ((struct IORequest *)AudioReq[i][0]);
452 AbortIO ((struct IORequest *)AudioReq[i][1]);
453 WaitIO ((struct IORequest *)AudioReq[i][1]);
456 AudioReq[i][0]->io_Command = ADCMD_FREE;
457 DoIO ((struct IORequest *)AudioReq[i][0]);
464 GLOBALCALL ULONG SetupAudio (void)
466 struct AudioMsg audiomsg;
468 if (!(AudioReply = CreateMsgPort ()))
469 return ERROR_NO_FREE_STORE;
471 AudioSig = 1 << AudioReply->mp_SigBit;
474 /* Create Audio Process */
475 if (!(AudioTask = CreateNewProcTags (
476 NP_Entry, AudioProcess,
477 NP_Name, "XModule Audio Process",
478 NP_WindowPtr, ThisTask->pr_WindowPtr,
487 return ERROR_NO_FREE_STORE;
491 /* Send Startup Message */
493 audiomsg.am_Message.mn_ReplyPort = AudioReply;
494 PutMsg (&(AudioTask->pr_MsgPort), (struct Message *)&audiomsg);
495 WaitPort (AudioReply);
498 if (audiomsg.am_Error)
501 return (audiomsg.am_Error);
509 GLOBALCALL void CleanupAudio (void)
513 /* Tell audio task to give up */
514 SetSignal (0, SIGF_SINGLE);
515 Signal ((struct Task *)AudioTask, SIGBREAKF_CTRL_C);
517 /* Wait until the audio task quits */
526 while (msg = GetMsg (AudioReply))
527 FreeMem (msg, sizeof (struct AudioMsg));
529 DeleteMsgPort (AudioReply); AudioReply = NULL;
530 Signals &= ~AudioSig; AudioSig = 0;