Initial commit.
[amiga/xmodule.git] / Audio.c
1 /*
2 **      Audio.c
3 **
4 **      Copyright (C) 1994,95 by Bernardo Innocenti
5 **
6 **      audio.device interface routines.
7 **
8 **
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.
15 **
16 **      +------------+                +-------------+               +------------+
17 **      |Main Process|<-->AudioMsg<-->|Audio Process|<-->IOAudio<-->|audio.device|
18 **      +------------+                +-------------+               +------------+
19 */
20
21 #include <exec/memory.h>
22 #include <devices/audio.h>
23 #include <dos/dos.h>
24 #include <dos/dostags.h>
25
26 #include <proto/exec.h>
27 #include <proto/dos.h>
28
29 #include "XModulePriv.h"
30 #include "Gui.h"
31
32
33
34 /* This is the (maximum) size of sample chunks sent to the audio.device */
35
36 #define SAMPBUFSIZE             16768
37 #define MAXAUDIOSIZE    65534
38
39 /* Get/store pointer to original AudioMsg into an extended IOAudio structure */
40
41 #define AUDIOUSERDATA(ioa) (*((struct AudioMsg **)(((struct IOAudio *)ioa) + 1)))
42
43
44 struct AudioMsg
45 {
46         struct Message am_Message;
47         void *am_Data;
48         UBYTE am_Command;
49         UBYTE am_Error;
50         UWORD am_Status;
51         ULONG am_Len;
52         ULONG am_Actual;
53         UWORD am_Per;
54         UWORD am_Vol;
55         BYTE *am_TmpBuf[2];
56         struct IOAudio *am_IOA[2];
57 };
58
59
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    */
63
64
65
66 /* Local functions prototypes */
67
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);
73
74
75
76 /* Local data */
77
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;
82
83
84
85 /* Global data */
86
87 XDEF ULONG AudioSig = 0;
88
89
90
91 HOOKCALL static void AudioProcess (void)
92 {
93         ULONG audiosig, cmdsig, recsig, signals, err = 0;
94         LONG i, j;
95         struct IOAudio *ioa;
96         struct AudioMsg *am;
97         struct MsgPort *AudioPort;      /* Audio reply port */
98
99
100         struct Process *thistask = (struct Process *)FindTask (NULL);
101
102         if (AudioPort = CreateMsgPort())
103         {
104                 /* Create IOAudio requests */
105
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;
110
111                 if (!err)
112                 {
113                         /* Open audio.device */
114                         if (!(err = OpenDevice ("audio.device", 0, (struct IORequest *)AudioReq[0][0], 0)))
115                                 err = AllocChannels();
116                 }
117         }
118         else err = ERROR_NO_FREE_STORE;
119
120
121         /* Wait for startup message */
122
123         WaitPort (&thistask->pr_MsgPort);
124         am = (struct AudioMsg *) GetMsg (&thistask->pr_MsgPort);
125
126         /* Reply startup message */
127         am->am_Error = err;
128         ReplyMsg ((struct Message *)am);
129
130         if (err)
131         {
132                 Wait (SIGBREAKF_CTRL_C);
133                 goto exit;
134         }
135
136         cmdsig = 1 << thistask->pr_MsgPort.mp_SigBit;
137         audiosig = 1 << AudioPort->mp_SigBit;
138
139         signals = cmdsig | audiosig | SIGBREAKF_CTRL_C;
140
141         do
142         {
143                 recsig = Wait (signals);
144
145                 if (recsig & audiosig)
146                 {
147                         /* Collect previously sent requests */
148                         while (ioa = (struct IOAudio *) GetMsg (AudioPort))
149                         {
150                                 if (am = AUDIOUSERDATA (ioa))
151                                 {
152                                         if (am->am_Actual < am->am_Len)
153                                         {
154                                                 BYTE *samp;
155                                                 ULONG len;
156
157                                                 if (am->am_TmpBuf[am->am_Status])
158                                                 {
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);
162                                                 }
163                                                 else
164                                                 {
165                                                         samp = ((BYTE *)am->am_Data) + am->am_Actual;
166                                                         len = min (MAXAUDIOSIZE, am->am_Len - am->am_Actual);
167                                                 }
168
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;
176
177                                                 BeginIO ((struct IORequest *)ioa);
178                                                 am->am_Actual += len;
179                                                 am->am_Status ^= 1;
180                                         }
181                                         else
182                                         {
183                                                 am = AUDIOUSERDATA(ioa);
184                                                 AUDIOUSERDATA(am->am_IOA[0]) = NULL;
185                                                 AUDIOUSERDATA(am->am_IOA[1]) = NULL;
186                                                 ReplyAudioMsg (am);
187                                         }
188                                 }
189                         }
190                 }
191
192
193                 if (recsig & cmdsig)
194                 {
195                         /* Get Command and execute it */
196                         while (am = (struct AudioMsg *)GetMsg (&thistask->pr_MsgPort))
197                         {
198                                 switch (am->am_Command)
199                                 {
200                                         case ACMD_PLAY_SAMPLE:
201                                                 _PlaySample (am);
202                                                 break;
203
204                                         default:
205                                                 break;
206                                 }
207                         }
208                 }
209         }
210         while (!(recsig & SIGBREAKF_CTRL_C));
211
212
213
214 exit:
215
216         if (AudioPort)
217         {
218                 FreeChannels();
219
220                 for (i = 3; i >= 0 ; i--)
221                         for (j = 1; j >= 0; j--)
222                                 if (AudioReq[i][j])
223                                 {
224                                         if ((j == 0) && (AudioReq[i][j]->io_Device))
225                                         {
226                                                 if (am = AUDIOUSERDATA(AudioReq[i][j]))
227                                                         ReplyAudioMsg (am);
228
229                                                 if (i == 0)
230                                                         CloseDevice ((struct IORequest *)AudioReq[0][0]);
231                                         }
232                                         DeleteIORequest ((struct IORequest *)AudioReq[i][j]); AudioReq[i][j] = NULL;
233                                 }
234
235                 DeleteMsgPort (AudioPort);
236         }
237
238         /* Signal that we are done.
239          * We Forbid() here to avoid being unloaded until exit.
240          */
241         Forbid();
242         Signal ((struct Task *)ThisTask, SIGF_SINGLE);
243 }
244
245
246
247 static void ReplyAudioMsg (struct AudioMsg *am)
248 {
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);
254 }
255
256
257
258 static void _PlaySample (struct AudioMsg *am)
259 {
260         BYTE *samp = am->am_Data;
261         ULONG len = am->am_Len;
262         ULONG ch = FindFreeChannel ();
263         BOOL multi = FALSE, transfer = FALSE;
264
265         am->am_TmpBuf[0] = NULL;
266         am->am_TmpBuf[1] = NULL;
267         am->am_Status = 0;
268         am->am_IOA[0] = AudioReq[ch][0];
269         am->am_IOA[1] = AudioReq[ch][1];
270
271         if (!(TypeOfMem (samp) & MEMF_CHIP))
272                 transfer = TRUE;
273
274         if (am->am_Len > MAXAUDIOSIZE)
275         {
276                 multi = TRUE;
277                 if (transfer)
278                         len = SAMPBUFSIZE;
279                 else
280                         len = MAXAUDIOSIZE;
281         }
282
283         if (transfer)
284                 if (am->am_TmpBuf[0] = AllocVec (len, MEMF_CHIP))
285                 {
286                         CopyMem (samp, am->am_TmpBuf[0], len);
287                         samp = am->am_TmpBuf[0];
288                 }
289                 else return;
290
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;
299
300         BeginIO ((struct IORequest *)am->am_IOA[0]);
301         am->am_Actual = len;
302
303         if (multi)
304         {
305                 samp += len;
306
307                 if (transfer)
308                 {
309                         len = min(SAMPBUFSIZE, am->am_Len - SAMPBUFSIZE);
310
311                         if (am->am_TmpBuf[1] = AllocVec (len, MEMF_CHIP))
312                         {
313                                 CopyMem (samp, am->am_TmpBuf[1], len);
314                                 samp = am->am_TmpBuf[1];
315                         }
316                         else return;
317                 }
318                 else len = min (MAXAUDIOSIZE, am->am_Len - MAXAUDIOSIZE);
319
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;
328
329                 BeginIO ((struct IORequest *)am->am_IOA[1]);
330                 am->am_Actual += len;
331         }
332 }
333
334
335
336 GLOBALCALL void HandleAudio (void)
337 {
338         struct Message *msg;
339
340         while (msg = GetMsg (AudioReply))
341                 FreeMem (msg, sizeof (struct AudioMsg));
342 }
343
344
345
346 GLOBALCALL void PlaySample (BYTE *samp, ULONG len, UWORD vol, UWORD per)
347 {
348         struct AudioMsg *am;
349
350         if (!samp) return;
351
352         if (!AudioTask)
353                 if (SetupAudio()) return;
354
355         if (!(am = AllocMem (sizeof (struct AudioMsg), MEMF_PUBLIC)))
356                 return;
357
358         am->am_Message.mn_ReplyPort = AudioReply;
359         am->am_Command = ACMD_PLAY_SAMPLE;
360
361         am->am_Data = samp;
362         am->am_Len = len;
363         am->am_Vol = vol;
364         am->am_Per = per;
365
366         PutMsg (&AudioTask->pr_MsgPort, (struct Message *)am);
367 }
368
369
370
371 static UWORD FindFreeChannel (void)
372 {
373         UWORD ch;
374
375         for (ch = 0; ch < 4 ; ch++)
376                 if (CheckIO ((struct IORequest *)AudioReq[ch][0]) &&
377                         CheckIO ((struct IORequest *)AudioReq[ch][1]))
378                         return ch;
379
380         {
381                 struct AudioMsg *am;
382
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]);
387
388                 if (am = AUDIOUSERDATA(AudioReq[0][0]))
389                 {
390                         ReplyAudioMsg (am);
391                         AUDIOUSERDATA(AudioReq[0][0]) = NULL;
392                         AUDIOUSERDATA(AudioReq[0][1]) = NULL;
393                 }
394         }
395
396         return 0;
397 }
398
399
400 static ULONG AllocChannels (void)
401 {
402         struct IOAudio *ioa;
403         ULONG i;
404
405         /* Allocate channels */
406
407         for (i = 0 ; i < 4; i++)
408         {
409                 static UBYTE channels[] = {1, 2, 4, 8};
410
411                 ioa = AudioReq[i][0];
412
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;
419                 ioa->ioa_Length = 4;
420
421                 /* Using DoIO() here is not possible because the
422                  * io_Flags field would be cleared.
423                  */
424                 BeginIO ((struct IORequest *)ioa);
425                 WaitIO ((struct IORequest *)ioa);
426
427                 /* Initailize other request */
428                 CopyMem (ioa, AudioReq[i][1], sizeof (struct IOAudio));
429
430                 if (ioa->io_Error)
431                         return ioa->io_Error;
432         }
433
434         return RETURN_OK;
435 }
436
437
438
439 static void FreeChannels (void)
440 {
441         LONG i;
442
443         for (i = 3; i >= 0; i--)
444         {
445                 if (AudioReq[i][0])
446                 {
447                         AbortIO ((struct IORequest *)AudioReq[i][0]);
448                         WaitIO ((struct IORequest *)AudioReq[i][0]);
449
450                         if (AudioReq[i][1])
451                         {
452                                 AbortIO ((struct IORequest *)AudioReq[i][1]);
453                                 WaitIO ((struct IORequest *)AudioReq[i][1]);
454                         }
455
456                         AudioReq[i][0]->io_Command = ADCMD_FREE;
457                         DoIO ((struct IORequest *)AudioReq[i][0]);
458                 }
459         }
460 }
461
462
463
464 GLOBALCALL ULONG SetupAudio (void)
465 {
466         struct AudioMsg audiomsg;
467
468         if (!(AudioReply = CreateMsgPort ()))
469                 return ERROR_NO_FREE_STORE;
470
471         AudioSig = 1 << AudioReply->mp_SigBit;
472         Signals |= AudioSig;
473
474         /* Create Audio Process */
475         if (!(AudioTask = CreateNewProcTags (
476                 NP_Entry,               AudioProcess,
477                 NP_Name,                "XModule Audio Process",
478                 NP_WindowPtr,   ThisTask->pr_WindowPtr,
479                 NP_Priority,    15,
480                 NP_CopyVars,    FALSE,
481                 // NP_Input,    NULL,
482                 // NP_Output,   NULL,
483                 // NP_Error,    NULL,
484                 TAG_DONE)))
485         {
486                 CleanupAudio();
487                 return ERROR_NO_FREE_STORE;
488         }
489
490
491         /* Send Startup Message */
492
493         audiomsg.am_Message.mn_ReplyPort = AudioReply;
494         PutMsg (&(AudioTask->pr_MsgPort), (struct Message *)&audiomsg);
495         WaitPort (AudioReply);
496         GetMsg (AudioReply);
497
498         if (audiomsg.am_Error)
499         {
500                 CleanupAudio();
501                 return (audiomsg.am_Error);
502         }
503
504         return RETURN_OK;
505 }
506
507
508
509 GLOBALCALL void CleanupAudio (void)
510 {
511         if (AudioTask)
512         {
513                 /* Tell audio task to give up */
514                 SetSignal (0, SIGF_SINGLE);
515                 Signal ((struct Task *)AudioTask, SIGBREAKF_CTRL_C);
516
517                 /* Wait until the audio task quits */
518                 Wait (SIGF_SINGLE);
519                 AudioTask = NULL;
520         }
521
522         if (AudioReply)
523         {
524                 struct Message *msg;
525
526                 while (msg = GetMsg (AudioReply))
527                         FreeMem (msg, sizeof (struct AudioMsg));
528
529                 DeleteMsgPort (AudioReply); AudioReply = NULL;
530                 Signals &= ~AudioSig; AudioSig = 0;
531         }
532 }