Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / devs / AHI / Device / device.c
blob02e336e5c3f7b586c8f09214af28e9688277f411
1 /*
2 AHI - Hardware independent audio subsystem
3 Copyright (C) 1996-2005 Martin Blom <martin@blom.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge,
18 MA 02139, USA.
21 #include <config.h>
23 #include <exec/alerts.h>
24 #include <exec/errors.h>
25 #include <exec/tasks.h>
26 #include <exec/io.h>
27 #include <exec/devices.h>
28 #include <exec/memory.h>
29 #include <dos/dos.h>
30 #include <dos/dostags.h>
31 #include <libraries/iffparse.h>
32 #include <prefs/prefhdr.h>
34 #include <clib/alib_protos.h>
35 #include <proto/exec.h>
36 #include <proto/dos.h>
37 #include <proto/iffparse.h>
38 #define __NOLIBBASE__
39 #define __NOGLOBALIFACE__
40 #include <proto/ahi.h>
41 #undef __NOLIBBASE__
42 #undef __NOGLOBALIFACE__
43 #include <proto/ahi_sub.h>
45 #include <stddef.h>
47 #include "ahi_def.h"
48 #include "debug.h"
49 #include "device.h"
50 #include "devcommands.h"
51 #include "gateway.h"
52 #include "header.h"
53 #include "misc.h"
55 #ifdef __MORPHOS__
56 #define IS_MORPHOS 1
57 #else
58 #define IS_MORPHOS 0
59 #endif
62 ** Message passed to the Unit Process at
63 ** startup time.
66 struct StartupMessage
68 struct Message Msg;
69 struct AHIDevUnit *Unit;
73 static struct
74 AHIDevUnit *InitUnit( ULONG , struct AHIBase * );
76 static void
77 ExpungeUnit( struct AHIDevUnit *, struct AHIBase * );
79 static void
80 PlayerFunc( struct Hook* hook,
81 struct AHIAudioCtrl* actrl,
82 APTR null );
85 static ULONG
86 RecordFunc( struct Hook* hook,
87 struct AHIAudioCtrl* actrl,
88 struct AHIRecordMessage* recmsg );
91 static void
92 SoundFunc( struct Hook* hook,
93 struct AHIAudioCtrl* actrl,
94 struct AHISoundMessage* sndmsg );
97 static void
98 ChannelInfoFunc( struct Hook* hook,
99 struct AHIAudioCtrl* actrl,
100 struct AHIEffChannelInfo* cimsg );
103 /***** ahi.device/--background-- *******************************************
105 * PURPOSE
107 * The 'ahi.device' was first created because the lack of standards
108 * when it comes to sound cards on the Amiga. Another reason was to
109 * make it easier to write multi-channel music programs.
111 * This device is by no means the final and perfect solution. But
112 * hopefully, it can evolve into something useful until AT brings you
113 * The Real Thing (TM).
115 * OVERVIEW
117 * Please see the document "AHI Developer's Guide" for more
118 * information.
121 * * Driver based
123 * Each supported sound card is controlled by a library-based audio
124 * driver. For a 'dumb' sound card, a new driver could be written in
125 * a few hours. For a 'smart' sound card, it is possible to utilize an
126 * on-board DSP, for example, to maximize performance and sound quality.
127 * For sound cards with own DSP but little or no memory, it is possible
128 * to use the main CPU to mix channels and do the post-processing
129 * with the DSP. Drivers are available for most popular sound cards,
130 * as well as an 8SVX (mono) and AIFF/AIFC (mono & stereo) sample render
131 * driver.
133 * * Fast, powerful mixing routines (yeah, right... haha)
135 * The device's mixing routines mix 8- or 16-bit signed samples,
136 * both mono, stereo and 7.1, located in Fast-RAM and outputs
137 * 16-bit mono or stereo (with stereo panning if desired) data,
138 * using any number of channels (as long as 'any' means less than
139 * 128). Tables can be used speed the mixing up (especially when
140 * using 8-bit samples). The samples can have any length
141 * (including odd) and can have any number of loops. There are
142 * also so-called HiFi mixing routines that can be used, that use
143 * linear interpolation and gives 32 bit output.
145 * * Support for non-realtime mixing
147 * By providing a timing feature, it is possible to create high-
148 * quality output even if the processing power is lacking, by saving
149 * the output to disk, for example as an IFF AIFF or 8SXV file.
151 * * Audio database
153 * Uses ID codes, much like Screenmode IDs, to select the many
154 * parameters that can be set. The functions to access the audio
155 * database are not too different from those in 'graphics.library'.
156 * The device also features a requester to get an ID code from the
157 * user.
159 * * Both high- and low-level protocol
161 * By acting both like a device and a library, AHI gives the programmer
162 * a choice between full control and simplicity. The device API allows
163 * several programs to use the audio hardware at the same time, and
164 * the AUDIO: dos-device driver makes playing and recording sound very
165 * simple for both the programmer and user.
167 * * Future Compatible
169 * When AmigaOS gets device-independent audio worth it's name, it should
170 * not be too difficult to write a driver for AHI, allowing applications
171 * using 'ahi.device' to automatically use the new OS interface. At
172 * least I hope it wont.
175 ****************************************************************************
180 /******************************************************************************
181 ** DevOpen ********************************************************************
182 ******************************************************************************/
184 /****** ahi.device/OpenDevice **********************************************
186 * NAME
187 * OpenDevice -- Open the device
189 * SYNOPSIS
190 * error = OpenDevice(AHINAME, unit, ioRequest, flags)
191 * D0 A0 D0 A1 D1
193 * BYTE OpenDevice(STRPTR, ULONG, struct AHIRequest *, ULONG);
195 * FUNCTION
196 * This is an exec call. Exec will search for the ahi.device, and
197 * if found, will pass this call on to the device.
199 * INPUTS
200 * AHINAME - pointer to the string "ahi.device".
201 * unit - Either AHI_DEFAULT_UNIT (0), AHI_NO_UNIT (255) or any other
202 * unit the user has requested, for example with a UNIT tooltype.
203 * AHI_NO_UNIT should be used when you're using the low-level
204 * API.
205 * ioRequest - a pointer to a struct AHIRequest, initialized by
206 * exec.library/CreateIORequest(). ahir_Version *must* be preset
207 * to the version you need!
208 * flags - There is only one flag defined, AHIDF_NOMODESCAN, which
209 * asks ahi.device not to build the audio mode database if not
210 * already initialized. It should not be used by applications
211 * without good reasons (AddAudioModes uses this flag).
213 * RESULT
214 * error - Same as io_Error.
215 * io_Error - If the call succeeded, io_Error will be 0, else
216 * an error code as defined in <exec/errors.h> and
217 * <devices/ahi.h>.
218 * io_Device - A pointer to the device base, which can be used
219 * to call the functions the device provides.
221 * EXAMPLE
223 * NOTES
225 * BUGS
227 * SEE ALSO
228 * CloseDevice(), exec.library/OpenDevice(), <exec/errors.h>,
229 * <devices/ahi.h>.
231 ****************************************************************************
235 // This function is called by the system each time a unit is opened with
236 // exec.library/OpenDevice().
238 ULONG
239 _DevOpen ( struct AHIRequest* ioreq,
240 ULONG unit,
241 ULONG flags,
242 struct AHIBase* AHIBase )
244 ULONG rc = 0;
245 BOOL error = FALSE;
246 struct AHIDevUnit *iounit=NULL;
248 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
250 KPrintF("OpenDevice(%ld, 0x%08lx, %ld)", unit, (ULONG) ioreq, flags);
253 // Check if size includes the ahir_Version field
255 if(ioreq->ahir_Std.io_Message.mn_Length < (sizeof(struct IOStdReq) + 2))
257 Req( "Bad parameters to OpenDevice()." );
258 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
259 return IOERR_OPENFAIL;
262 // One more check...
264 if((unit != AHI_NO_UNIT) && (ioreq->ahir_Version >= 4))
266 if(ioreq->ahir_Std.io_Message.mn_Length < sizeof(struct AHIRequest))
268 Req( "Bad parameters to OpenDevice()." );
269 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
270 return IOERR_OPENFAIL;
272 else
274 /* KPrintF( "Tagging %08lx on task %08lx\n", ioreq, FindTask(0)); */
275 ioreq->ahir_Private[1] = (IPTR) ioreq;
279 AHIBase->ahib_Library.lib_OpenCnt++;
281 ObtainSemaphore(&AHIBase->ahib_Lock);
283 if( ! (flags & AHIDF_NOMODESCAN))
285 // Load database if not already loaded
287 if(AHI_NextAudioID(AHI_INVALID_ID) == (ULONG) AHI_INVALID_ID)
289 AHI_LoadModeFile("DEVS:AudioModes");
291 // Be quiet here. - Piru
292 if (IS_MORPHOS)
294 APTR *windowptr = &((struct Process *) FindTask(NULL))->pr_WindowPtr;
295 APTR oldwindowptr = *windowptr;
296 *windowptr = (APTR) -1;
298 AHI_LoadModeFile("MOSSYS:DEVS/AudioModes");
300 *windowptr = oldwindowptr;
303 // Load Void driver if no real hardware was found
305 if(AHI_NextAudioID(AHI_INVALID_ID) == (ULONG) AHI_INVALID_ID)
307 AHI_LoadModeFile("SYS:Storage/AudioModes/VOID");
312 if( ioreq->ahir_Version > AHIBase->ahib_Library.lib_Version)
313 error=TRUE;
314 else
316 if(unit < AHI_UNITS)
318 iounit=InitUnit(unit,AHIBase);
319 if(!iounit)
320 error=TRUE;
322 else if(unit == AHI_NO_UNIT)
323 InitUnit(unit,AHIBase);
326 if(!error)
328 ioreq->ahir_Std.io_Unit=(struct Unit *) iounit;
329 if(iounit) // Is NULL for AHI_NO_UNIT
330 iounit->Unit.unit_OpenCnt++;
331 AHIBase->ahib_Library.lib_OpenCnt++;
332 AHIBase->ahib_Library.lib_Flags &=~LIBF_DELEXP;
334 else
336 rc=IOERR_OPENFAIL;
337 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
338 ioreq->ahir_Std.io_Device=(struct Device *) -1;
339 ioreq->ahir_Std.io_Unit=(struct Unit *) -1;
342 ReleaseSemaphore(&AHIBase->ahib_Lock);
344 AHIBase->ahib_Library.lib_OpenCnt--;
346 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
348 KPrintF("=>%ld\n",rc);
351 return rc;
355 /******************************************************************************
356 ** DevClose *******************************************************************
357 ******************************************************************************/
359 /****** ahi.device/CloseDevice *********************************************
361 * NAME
362 * CloseDevice -- Close the device
364 * SYNOPSIS
365 * CloseDevice(ioRequest)
366 * A1
368 * void CloseDevice(struct IORequest *);
370 * FUNCTION
371 * This is an exec call that closes the device. Every OpenDevice()
372 * must be matched with a call to CloseDevice().
374 * The user must ensure that all outstanding IO Requests have been
375 * returned before closing the device.
377 * INPUTS
378 * ioRequest - a pointer to the same struct AHIRequest that was used
379 * to open the device.
381 * RESULT
383 * EXAMPLE
385 * NOTES
387 * BUGS
389 * SEE ALSO
390 * OpenDevice(), exec.library/CloseDevice()
392 ****************************************************************************
396 // This function is called by the system each time a unit is closed with
397 // exec.library/CloseDevice().
399 BPTR
400 _DevClose ( struct AHIRequest* ioreq,
401 struct AHIBase* AHIBase )
403 struct AHIDevUnit *iounit;
404 BPTR seglist=0;
406 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
408 KPrintF("CloseDevice(0x%P)\n", (IPTR)ioreq);
410 if( ioreq->ahir_Private[1] != (IPTR) ioreq )
412 KPrintF( "Warning: Expected I/O request 0x%P.\n",
413 ioreq->ahir_Private[1] );
417 ObtainSemaphore(&AHIBase->ahib_Lock);
419 iounit= (struct AHIDevUnit *) ioreq->ahir_Std.io_Unit;
420 ioreq->ahir_Std.io_Device = (struct Device *) -1;
421 ioreq->ahir_Std.io_Unit = (struct Unit *) -1;
423 if(iounit)
425 iounit->Unit.unit_OpenCnt--;
426 if(!iounit->Unit.unit_OpenCnt)
427 ExpungeUnit(iounit,AHIBase);
430 AHIBase->ahib_Library.lib_OpenCnt--;
432 ReleaseSemaphore(&AHIBase->ahib_Lock);
434 if(!AHIBase->ahib_Library.lib_OpenCnt)
436 if(AHIBase->ahib_Library.lib_Flags & LIBF_DELEXP)
437 seglist=_DevExpunge(AHIBase);
439 return seglist;
443 /******************************************************************************
444 ** InitUnit *******************************************************************
445 ******************************************************************************/
447 // This function is called by DevOpen() to initialize a unit
449 static struct AHIDevUnit *
450 InitUnit ( ULONG unit,
451 struct AHIBase *AHIBase )
453 struct AHIDevUnit *iounit;
455 if( unit == AHI_NO_UNIT )
457 ReadConfig(NULL,AHIBase);
458 return NULL;
460 else if(!AHIBase->ahib_DevUnits[unit])
462 if((iounit = AllocVec(sizeof(struct AHIDevUnit), MEMF_CLEAR|MEMF_PUBLIC)))
464 NewList(&iounit->Unit.unit_MsgPort.mp_MsgList);
466 iounit->Unit.unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
467 iounit->Unit.unit_MsgPort.mp_Flags = PA_IGNORE;
468 iounit->Unit.unit_MsgPort.mp_Node.ln_Name = AHINAME " Unit";
469 iounit->UnitNum = unit;
470 AHIInitSemaphore(&iounit->Lock);
471 NewList((struct List *)&iounit->ReadList);
472 NewList((struct List *)&iounit->PlayingList);
473 NewList((struct List *)&iounit->SilentList);
474 NewList((struct List *)&iounit->WaitingList);
475 NewList((struct List *)&iounit->RequestQueue);
477 if(ReadConfig(iounit,AHIBase))
479 if((iounit->Voices = AllocVec(
480 sizeof(struct Voice)*iounit->Channels,MEMF_PUBLIC|MEMF_CLEAR)))
482 int i;
483 struct Voice *v = iounit->Voices;
484 struct MsgPort *replyport;
486 // Mark all channels as free
487 for(i = 0 ; i < iounit->Channels; i++)
489 v->NextOffset = FREE;
490 v++;
493 replyport = CreateMsgPort();
495 if( replyport != NULL )
497 struct StartupMessage sm =
500 { NULL, NULL, NT_UNKNOWN, 0, NULL },
501 replyport, sizeof(struct StartupMessage),
503 iounit
506 iounit->Process = CreateNewProcTags( NP_Entry, (IPTR) &m68k_DevProc,
507 NP_Name, (IPTR) AHINAME " Unit Process",
508 NP_Priority, AHI_PRI,
509 TAG_DONE );
511 if( iounit->Process != NULL )
514 PutMsg( &iounit->Process->pr_MsgPort,
515 &sm.Msg );
517 WaitPort(replyport);
518 GetMsg(replyport);
520 DeleteMsgPort(replyport);
525 if(!iounit->Process)
526 FreeVec(iounit);
527 else
528 AHIBase->ahib_DevUnits[unit] = iounit;
532 return AHIBase->ahib_DevUnits[unit];
536 /******************************************************************************
537 ** ExpungeUnit ****************************************************************
538 ******************************************************************************/
540 // This function is called by DevClose() to remove a unit.
542 static void
543 ExpungeUnit ( struct AHIDevUnit *iounit,
544 struct AHIBase *AHIBase )
546 struct Task *unittask;
547 BYTE signal;
549 signal = AllocSignal(-1);
550 if(signal == -1)
552 /* Fallback */
553 signal = SIGB_SINGLE;
554 SetSignal(0, SIGF_SINGLE);
557 unittask = (struct Task *) iounit->Process;
558 iounit->Process = (struct Process *) FindTask(NULL);
559 iounit->SyncSignal = signal;
560 Signal(unittask,SIGBREAKF_CTRL_F);
561 Wait(1UL << signal);
562 AHIBase->ahib_DevUnits[iounit->UnitNum]=NULL;
563 FreeVec(iounit->Voices);
564 FreeVec(iounit);
566 if(signal != SIGB_SINGLE)
568 FreeSignal(signal);
573 /******************************************************************************
574 ** ReadConfig *****************************************************************
575 ******************************************************************************/
577 // This functions loads the users settings for AHI.
579 BOOL
580 ReadConfig ( struct AHIDevUnit *iounit,
581 struct AHIBase *AHIBase )
583 struct IFFHandle *iff;
584 struct StoredProperty *ahig;
585 struct CollectionItem *ci;
586 ULONG *mode;
588 if(iounit)
590 /* Internal defaults for device unit */
591 iounit->AudioMode = AHI_INVALID_ID; // See at the end of the function!
592 iounit->Frequency = 44100;
593 iounit->Channels = 4;
594 iounit->MonitorVolume = ~0;
595 iounit->InputGain = ~0;
596 iounit->OutputVolume = ~0;
597 iounit->Input = ~0;
598 iounit->Output = ~0;
600 else
602 /* Internal defaults for low-level mode */
603 AHIBase->ahib_AudioMode = AHI_INVALID_ID;
604 AHIBase->ahib_Frequency = 44100;
605 AHIBase->ahib_MonitorVolume = 0x00000;
606 AHIBase->ahib_InputGain = 0x10000;
607 AHIBase->ahib_OutputVolume = 0x10000;
608 AHIBase->ahib_Input = 0;
609 AHIBase->ahib_Output = 0;
612 if((iff=AllocIFF()))
614 iff->iff_Stream=(IPTR) Open("ENV:Sys/ahi.prefs", MODE_OLDFILE);
615 if(iff->iff_Stream)
617 InitIFFasDOS(iff);
618 if(!OpenIFF(iff,IFFF_READ))
620 if(!(PropChunk(iff,ID_PREF,ID_PRHD)
621 || PropChunk(iff,ID_PREF,ID_AHIG)
622 || CollectionChunk(iff,ID_PREF,ID_AHIU)
623 || StopOnExit(iff,ID_PREF,ID_FORM)))
625 if(ParseIFF(iff,IFFPARSE_SCAN) == IFFERR_EOC)
627 ahig=FindProp(iff,ID_PREF,ID_AHIG);
629 if(ahig)
631 struct AHIGlobalPrefs *globalprefs;
632 UWORD debug_level;
634 globalprefs = (struct AHIGlobalPrefs *)ahig->sp_Data;
636 debug_level = globalprefs->ahigp_DebugLevel;
637 EndianSwap( sizeof (UWORD), &debug_level );
638 AHIBase->ahib_DebugLevel = debug_level;
640 AHIBase->ahib_Flags = 0;
642 /* Not used in version 5:
644 * if(globalprefs->ahigp_DisableSurround)
645 * AHIBase->ahib_Flags |= AHIBF_NOSURROUND;
647 * if(globalprefs->ahigp_DisableEcho)
648 * AHIBase->ahib_Flags |= AHIBF_NOECHO;
650 * if(globalprefs->ahigp_FastEcho)
651 * AHIBase->ahib_Flags |= AHIBF_FASTECHO;
655 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
656 ahigp_MaxCPU) )
658 AHIBase->ahib_MaxCPU = globalprefs->ahigp_MaxCPU;
659 EndianSwap( sizeof (Fixed), &AHIBase->ahib_MaxCPU );
661 else
663 AHIBase->ahib_MaxCPU = 0x10000 * 90 / 100;
666 /* In version 5: Clipping is always used
668 * if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
669 * ahigp_ClipMasterVolume) )
671 * if(globalprefs->ahigp_ClipMasterVolume)
672 * AHIBase->ahib_Flags |= AHIBF_CLIPPING;
676 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
677 ahigp_AntiClickTime ) )
679 AHIBase->ahib_AntiClickTime = globalprefs->ahigp_AntiClickTime;
680 EndianSwap( sizeof (Fixed), &AHIBase->ahib_AntiClickTime );
682 else
684 AHIBase->ahib_AntiClickTime = 0;
687 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
688 ahigp_ScaleMode ) )
690 AHIBase->ahib_ScaleMode = globalprefs->ahigp_ScaleMode;
691 EndianSwap( sizeof (UWORD), &AHIBase->ahib_ScaleMode );
693 else
695 AHIBase->ahib_ScaleMode = AHI_SCALE_FIXED_0_DB;
698 ci=FindCollection(iff,ID_PREF,ID_AHIU);
699 while(ci)
701 struct AHIUnitPrefs *unitprefs;
703 unitprefs = (struct AHIUnitPrefs *)ci->ci_Data;
705 if(iounit)
707 if(unitprefs->ahiup_Unit == iounit->UnitNum)
709 iounit->AudioMode = unitprefs->ahiup_AudioMode;
710 iounit->Frequency = unitprefs->ahiup_Frequency;
711 iounit->Channels = unitprefs->ahiup_Channels;
712 iounit->MonitorVolume = unitprefs->ahiup_MonitorVolume;
713 iounit->InputGain = unitprefs->ahiup_InputGain;
714 iounit->OutputVolume = unitprefs->ahiup_OutputVolume;
715 iounit->Input = unitprefs->ahiup_Input;
716 iounit->Output = unitprefs->ahiup_Output;
718 EndianSwap( sizeof (ULONG), &iounit->AudioMode );
719 EndianSwap( sizeof (ULONG), &iounit->Frequency );
720 EndianSwap( sizeof (UWORD), &iounit->Channels );
721 EndianSwap( sizeof (Fixed), &iounit->MonitorVolume );
722 EndianSwap( sizeof (Fixed), &iounit->InputGain );
723 EndianSwap( sizeof (Fixed), &iounit->OutputVolume );
724 EndianSwap( sizeof (ULONG), &iounit->Input );
725 EndianSwap( sizeof (ULONG), &iounit->Output );
728 else
730 if(unitprefs->ahiup_Unit == AHI_NO_UNIT)
732 AHIBase->ahib_AudioMode = unitprefs->ahiup_AudioMode;
733 AHIBase->ahib_Frequency = unitprefs->ahiup_Frequency;
734 AHIBase->ahib_MonitorVolume = unitprefs->ahiup_MonitorVolume;
735 AHIBase->ahib_InputGain = unitprefs->ahiup_InputGain;
736 AHIBase->ahib_OutputVolume = unitprefs->ahiup_OutputVolume;
737 AHIBase->ahib_Input = unitprefs->ahiup_Input;
738 AHIBase->ahib_Output = unitprefs->ahiup_Output;
740 EndianSwap( sizeof (ULONG), &AHIBase->ahib_AudioMode);
741 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Frequency);
742 EndianSwap( sizeof (Fixed), &AHIBase->ahib_MonitorVolume);
743 EndianSwap( sizeof (Fixed), &AHIBase->ahib_InputGain);
744 EndianSwap( sizeof (Fixed), &AHIBase->ahib_OutputVolume);
745 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Input);
746 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Output);
750 ci=ci->ci_Next;
754 CloseIFF(iff);
756 Close((BPTR) iff->iff_Stream);
758 FreeIFF(iff);
761 // Avoids calling AHI_BestAudioID if not neccessary (faster startup time,
762 // since doesn't open all sub libraries.
764 if(iounit)
765 mode = &iounit->AudioMode;
766 else
767 mode = &AHIBase->ahib_AudioMode;
768 if(mode[0] == (ULONG) AHI_INVALID_ID)
769 { static const Tag tags[] = { AHIDB_Realtime,TRUE,TAG_DONE };
770 mode[0] = AHI_BestAudioIDA((struct TagItem *)tags);
773 return TRUE;
777 /******************************************************************************
778 ** AllocHardware **************************************************************
779 ******************************************************************************/
781 // Allocates the audio hardware
783 BOOL
784 AllocHardware ( struct AHIDevUnit *iounit,
785 struct AHIBase *AHIBase )
787 BOOL rc = FALSE;
788 ULONG fullduplex = FALSE;
789 ULONG stereo = FALSE;
790 ULONG panning = FALSE;
792 /* Allocate the hardware */
793 iounit->AudioCtrl = AHI_AllocAudio(
794 AHIA_AudioID, (IPTR)iounit->AudioMode,
795 AHIA_MixFreq, (IPTR)iounit->Frequency,
796 AHIA_Channels, (IPTR)iounit->Channels,
797 AHIA_Sounds, MAXSOUNDS,
798 AHIA_PlayerFunc, (IPTR)&iounit->PlayerHook,
799 AHIA_RecordFunc, (IPTR)&iounit->RecordHook,
800 AHIA_SoundFunc, (IPTR)&iounit->SoundHook,
801 TAG_DONE);
803 if(iounit->AudioCtrl != NULL)
805 /* Full duplex? */
806 AHI_GetAudioAttrs(AHI_INVALID_ID,iounit->AudioCtrl,
807 AHIDB_FullDuplex, (IPTR)&fullduplex,
808 AHIDB_Stereo, (IPTR)&stereo,
809 AHIDB_Panning, (IPTR)&panning,
810 TAG_DONE);
811 iounit->FullDuplex = fullduplex;
812 iounit->PseudoStereo = stereo && !panning;
814 /* Set hardware properties */
815 AHI_ControlAudio(iounit->AudioCtrl,
816 (iounit->MonitorVolume == ~0 ? TAG_IGNORE : AHIC_MonitorVolume),
817 iounit->MonitorVolume,
819 (iounit->InputGain == ~0 ? TAG_IGNORE : AHIC_InputGain),
820 iounit->InputGain,
822 (iounit->OutputVolume == ~0 ? TAG_IGNORE : AHIC_OutputVolume),
823 iounit->OutputVolume,
825 (iounit->Input == ~0 ? TAG_IGNORE : AHIC_Input),
826 iounit->Input,
828 (iounit->Output == ~0 ? TAG_IGNORE : AHIC_Output),
829 iounit->Output,
831 TAG_DONE);
833 iounit->ChannelInfoStruct->ahie_Effect = AHIET_CHANNELINFO;
834 iounit->ChannelInfoStruct->ahieci_Func = &iounit->ChannelInfoHook;
835 iounit->ChannelInfoStruct->ahieci_Channels = iounit->Channels;
836 if(!AHI_SetEffect(iounit->ChannelInfoStruct, iounit->AudioCtrl))
838 rc = TRUE;
841 return rc;
845 /******************************************************************************
846 ** FreeHardware ***************************************************************
847 ******************************************************************************/
849 // Take a wild guess!
851 void
852 FreeHardware ( struct AHIDevUnit *iounit,
853 struct AHIBase *AHIBase )
855 if(iounit->AudioCtrl)
857 if(iounit->ChannelInfoStruct)
859 iounit->ChannelInfoStruct->ahie_Effect = (AHIET_CANCEL | AHIET_CHANNELINFO);
860 AHI_SetEffect(iounit->ChannelInfoStruct, iounit->AudioCtrl);
862 AHI_FreeAudio(iounit->AudioCtrl);
863 iounit->AudioCtrl = NULL;
864 iounit->IsRecording = FALSE;
865 iounit->IsPlaying = FALSE;
866 iounit->ValidRecord = FALSE;
871 /******************************************************************************
872 ** DevProc ********************************************************************
873 ******************************************************************************/
875 void
876 DevProc( void )
878 struct Process *proc;
879 struct StartupMessage *sm;
880 struct AHIDevUnit *iounit;
881 BYTE signalbit;
883 proc = (struct Process *)FindTask(NULL);
884 WaitPort(&proc->pr_MsgPort);
885 sm = (struct StartupMessage *)GetMsg(&proc->pr_MsgPort);
886 iounit = sm->Unit;
888 iounit->Process = NULL;
890 iounit->PlayerHook.h_Entry = (HOOKFUNC) HookEntry;
891 iounit->PlayerHook.h_SubEntry = (HOOKFUNC) PlayerFunc;
892 iounit->PlayerHook.h_Data = iounit;
894 iounit->RecordHook.h_Entry = (HOOKFUNC) HookEntry;
895 iounit->RecordHook.h_SubEntry = (HOOKFUNC) RecordFunc;
896 iounit->RecordHook.h_Data = iounit;
898 iounit->SoundHook.h_Entry = (HOOKFUNC) HookEntry;
899 iounit->SoundHook.h_SubEntry = (HOOKFUNC) SoundFunc;
900 iounit->SoundHook.h_Data = iounit;
902 iounit->ChannelInfoHook.h_Entry = (HOOKFUNC) HookEntry;
903 iounit->ChannelInfoHook.h_SubEntry = (HOOKFUNC) ChannelInfoFunc;
904 iounit->ChannelInfoHook.h_Data = iounit;
906 iounit->ChannelInfoStruct = AllocVec(
907 sizeof(struct AHIEffChannelInfo) + (iounit->Channels * sizeof(ULONG)),
908 MEMF_PUBLIC | MEMF_CLEAR);
910 iounit->Master=proc;
912 signalbit = AllocSignal(-1);
913 iounit->PlaySignal = AllocSignal(-1);
914 iounit->RecordSignal = AllocSignal(-1);
915 iounit->SampleSignal = AllocSignal(-1);
917 if((signalbit != -1)
918 && (iounit->PlaySignal != -1)
919 && (iounit->RecordSignal != -1)
920 && (iounit->SampleSignal != -1)
921 && (iounit->ChannelInfoStruct != NULL)
924 /* Set up our Unit's MsgPort. */
925 iounit->Unit.unit_MsgPort.mp_SigBit = signalbit;
926 iounit->Unit.unit_MsgPort.mp_SigTask = (struct Task *)proc;
927 iounit->Unit.unit_MsgPort.mp_Flags = PA_SIGNAL;
929 /* Allocate the hardware */
930 if(AllocHardware(iounit, AHIBase))
933 /* Set iounit->Process to pointer to our unit process.
934 This will let the Unit init code know that were
935 are okay. */
936 iounit->Process = proc;
940 /* Reply to our startup message */
941 ReplyMsg(&sm->Msg);
943 if(iounit->Process)
945 ULONG waitmask,signals;
947 waitmask = (1L << signalbit)
948 | SIGBREAKF_CTRL_E // Dummy signal to wake up task
949 | SIGBREAKF_CTRL_F // Quit signal
950 | (1L << iounit->PlaySignal)
951 | (1L << iounit->RecordSignal)
952 | (1L << iounit->SampleSignal);
954 while(TRUE)
956 signals = Wait(waitmask);
958 /* Have we been signaled to shut down? */
959 if(signals & SIGBREAKF_CTRL_F)
960 break;
962 if(signals & (1L << iounit->SampleSignal))
964 RethinkPlayers(iounit,AHIBase);
967 if(signals & (1L << signalbit))
969 struct AHIRequest *ioreq;
971 while((ioreq = (struct AHIRequest *) GetMsg(&iounit->Unit.unit_MsgPort)))
973 PerformIO(ioreq,AHIBase);
977 if(signals & (1L << iounit->PlaySignal))
979 AHIObtainSemaphore(&iounit->Lock);
981 UpdateSilentPlayers(iounit,AHIBase);
983 AHIReleaseSemaphore(&iounit->Lock);
986 if(signals & (1L << iounit->RecordSignal))
988 iounit->ValidRecord = TRUE;
989 FeedReaders(iounit,AHIBase);
994 FreeHardware(iounit, AHIBase);
995 FreeSignal(iounit->SampleSignal);
996 iounit->SampleSignal = -1;
997 FreeSignal(iounit->RecordSignal);
998 iounit->RecordSignal = -1;
999 FreeSignal(iounit->PlaySignal);
1000 iounit->PlaySignal = -1;
1001 FreeVec(iounit->ChannelInfoStruct);
1003 if(iounit->Process)
1005 Forbid();
1006 Signal((struct Task *) iounit->Process, 1UL << iounit->SyncSignal);
1008 FreeSignal(signalbit);
1012 /******************************************************************************
1013 ** PlayerFunc *****************************************************************
1014 ******************************************************************************/
1016 static void
1017 PlayerFunc( struct Hook* hook,
1018 struct AHIAudioCtrl* actrl,
1019 APTR null )
1021 struct AHIDevUnit *iounit = (struct AHIDevUnit *) hook->h_Data;
1023 if(AHIAttemptSemaphore(&iounit->Lock))
1025 UpdateSilentPlayers(iounit,AHIBase);
1026 AHIReleaseSemaphore(&iounit->Lock);
1028 else
1029 { // Do it later instead
1030 Signal((struct Task *) iounit->Master, (1L << iounit->PlaySignal));
1033 return;
1037 /******************************************************************************
1038 ** RecordFunc *****************************************************************
1039 ******************************************************************************/
1041 static ULONG
1042 RecordFunc( struct Hook* hook,
1043 struct AHIAudioCtrl* actrl,
1044 struct AHIRecordMessage* recmsg )
1046 struct AHIDevUnit *iounit;
1048 if(recmsg->ahirm_Type == AHIST_S16S)
1050 iounit = (struct AHIDevUnit *) hook->h_Data;
1051 iounit->RecordBuffer = recmsg->ahirm_Buffer;
1052 iounit->RecordSize = recmsg->ahirm_Length<<2;
1053 Signal((struct Task *) iounit->Master, (1L << iounit->RecordSignal));
1055 return 0;
1059 /******************************************************************************
1060 ** SoundFunc ******************************************************************
1061 ******************************************************************************/
1063 static void
1064 SoundFunc( struct Hook* hook,
1065 struct AHIAudioCtrl* actrl,
1066 struct AHISoundMessage* sndmsg )
1068 struct AHIDevUnit* iounit;
1069 struct Voice* voice;
1070 struct AHIRequest* playreq;
1072 iounit = (struct AHIDevUnit *) hook->h_Data;
1073 voice = &iounit->Voices[(WORD)sndmsg->ahism_Channel];
1075 Disable(); // Not needed?
1077 playreq = voice->PlayingRequest;
1079 if( playreq != NULL )
1081 playreq->ahir_Std.io_Command = AHICMD_WRITTEN;
1084 Enable();
1086 voice->PlayingRequest = voice->QueuedRequest;
1087 voice->Flags |= VF_STARTED;
1088 voice->QueuedRequest = NULL;
1090 switch(voice->NextOffset)
1092 case FREE:
1093 break;
1095 case MUTE:
1096 /* A AHI_NOSOUND is done, channel is silent */
1097 voice->NextOffset = FREE;
1098 break;
1100 case PLAY:
1101 /* A normal sound is done and playing, no other sound is queued */
1102 AHI_SetSound(sndmsg->ahism_Channel,AHI_NOSOUND,0,0,actrl,AHISF_NONE);
1103 voice->NextOffset = MUTE;
1104 break;
1106 default:
1107 /* A normal sound is done, and another is waiting */
1108 AHI_SetSound(sndmsg->ahism_Channel,
1109 voice->NextSound,
1110 voice->NextOffset,
1111 voice->NextLength,
1112 actrl,AHISF_NONE);
1113 AHI_SetFreq(sndmsg->ahism_Channel,
1114 voice->NextFrequency,
1115 actrl,AHISF_NONE);
1116 AHI_SetVol(sndmsg->ahism_Channel,
1117 voice->NextVolume,
1118 voice->NextPan,
1119 actrl,AHISF_NONE);
1120 voice->QueuedRequest = voice->NextRequest;
1121 voice->NextRequest = NULL;
1122 voice->NextOffset = PLAY;
1123 break;
1126 Signal((struct Task *) iounit->Master, (1L << iounit->SampleSignal));
1130 /******************************************************************************
1131 ** ChannelInfoFunc ************************************************************
1132 ******************************************************************************/
1134 // This hook keeps updating the io_Actual field of each playing requests
1136 static void
1137 ChannelInfoFunc( struct Hook* hook,
1138 struct AHIAudioCtrl* actrl,
1139 struct AHIEffChannelInfo* cimsg )
1141 struct AHIDevUnit *iounit = (struct AHIDevUnit *) hook->h_Data;
1142 struct Voice *voice;
1143 ULONG *offsets = (ULONG *) &cimsg->ahieci_Offset;
1144 int i;
1146 Disable(); // Not needed?
1148 voice = iounit->Voices;
1150 for(i = 0; i < iounit->Channels; i++)
1152 struct AHIRequest* playreq;
1154 playreq = voice->PlayingRequest;
1156 if( playreq != NULL )
1158 playreq->ahir_Std.io_Actual = *offsets;
1161 voice++;
1162 offsets++;
1165 Enable();
1167 return;