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,
23 #include <exec/alerts.h>
24 #include <exec/errors.h>
25 #include <exec/tasks.h>
27 #include <exec/devices.h>
28 #include <exec/memory.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>
39 #define __NOGLOBALIFACE__
40 #include <proto/ahi.h>
42 #undef __NOGLOBALIFACE__
43 #include <proto/ahi_sub.h>
50 #include "devcommands.h"
62 ** Message passed to the Unit Process at
69 struct AHIDevUnit
*Unit
;
74 AHIDevUnit
*InitUnit( ULONG
, struct AHIBase
* );
77 ExpungeUnit( struct AHIDevUnit
*, struct AHIBase
* );
80 PlayerFunc( struct Hook
* hook
,
81 struct AHIAudioCtrl
* actrl
,
86 RecordFunc( struct Hook
* hook
,
87 struct AHIAudioCtrl
* actrl
,
88 struct AHIRecordMessage
* recmsg
);
92 SoundFunc( struct Hook
* hook
,
93 struct AHIAudioCtrl
* actrl
,
94 struct AHISoundMessage
* sndmsg
);
98 ChannelInfoFunc( struct Hook
* hook
,
99 struct AHIAudioCtrl
* actrl
,
100 struct AHIEffChannelInfo
* cimsg
);
103 /***** ahi.device/--background-- *******************************************
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).
117 * Please see the document "AHI Developer's Guide" for more
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
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.
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
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 **********************************************
187 * OpenDevice -- Open the device
190 * error = OpenDevice(AHINAME, unit, ioRequest, flags)
193 * BYTE OpenDevice(STRPTR, ULONG, struct AHIRequest *, ULONG);
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.
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
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).
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
218 * io_Device - A pointer to the device base, which can be used
219 * to call the functions the device provides.
228 * CloseDevice(), exec.library/OpenDevice(), <exec/errors.h>,
231 ****************************************************************************
235 // This function is called by the system each time a unit is opened with
236 // exec.library/OpenDevice().
239 _DevOpen ( struct AHIRequest
* ioreq
,
242 struct AHIBase
* AHIBase
)
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
;
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
;
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
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
)
318 iounit
=InitUnit(unit
,AHIBase
);
322 else if(unit
== AHI_NO_UNIT
)
323 InitUnit(unit
,AHIBase
);
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
;
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
);
355 /******************************************************************************
356 ** DevClose *******************************************************************
357 ******************************************************************************/
359 /****** ahi.device/CloseDevice *********************************************
362 * CloseDevice -- Close the device
365 * CloseDevice(ioRequest)
368 * void CloseDevice(struct IORequest *);
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.
378 * ioRequest - a pointer to the same struct AHIRequest that was used
379 * to open the device.
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().
400 _DevClose ( struct AHIRequest
* ioreq
,
401 struct AHIBase
* AHIBase
)
403 struct AHIDevUnit
*iounit
;
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;
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
);
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
);
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
)))
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
;
493 replyport
= CreateMsgPort();
495 if( replyport
!= NULL
)
497 struct StartupMessage sm
=
500 { NULL
, NULL
, NT_UNKNOWN
, 0, NULL
},
501 replyport
, sizeof(struct StartupMessage
),
506 iounit
->Process
= CreateNewProcTags( NP_Entry
, (IPTR
) &m68k_DevProc
,
507 NP_Name
, (IPTR
) AHINAME
" Unit Process",
508 NP_Priority
, AHI_PRI
,
511 if( iounit
->Process
!= NULL
)
514 PutMsg( &iounit
->Process
->pr_MsgPort
,
520 DeleteMsgPort(replyport
);
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.
543 ExpungeUnit ( struct AHIDevUnit
*iounit
,
544 struct AHIBase
*AHIBase
)
546 struct Task
*unittask
;
549 signal
= AllocSignal(-1);
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
);
562 AHIBase
->ahib_DevUnits
[iounit
->UnitNum
]=NULL
;
563 FreeVec(iounit
->Voices
);
566 if(signal
!= SIGB_SINGLE
)
573 /******************************************************************************
574 ** ReadConfig *****************************************************************
575 ******************************************************************************/
577 // This functions loads the users settings for AHI.
580 ReadConfig ( struct AHIDevUnit
*iounit
,
581 struct AHIBase
*AHIBase
)
583 struct IFFHandle
*iff
;
584 struct StoredProperty
*ahig
;
585 struct CollectionItem
*ci
;
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;
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;
614 iff
->iff_Stream
=(IPTR
) Open("ENV:Sys/ahi.prefs", MODE_OLDFILE
);
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
);
631 struct AHIGlobalPrefs
*globalprefs
;
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
,
658 AHIBase
->ahib_MaxCPU
= globalprefs
->ahigp_MaxCPU
;
659 EndianSwap( sizeof (Fixed
), &AHIBase
->ahib_MaxCPU
);
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
);
684 AHIBase
->ahib_AntiClickTime
= 0;
687 if( (ULONG
) ahig
->sp_Size
> offsetof( struct AHIGlobalPrefs
,
690 AHIBase
->ahib_ScaleMode
= globalprefs
->ahigp_ScaleMode
;
691 EndianSwap( sizeof (UWORD
), &AHIBase
->ahib_ScaleMode
);
695 AHIBase
->ahib_ScaleMode
= AHI_SCALE_FIXED_0_DB
;
698 ci
=FindCollection(iff
,ID_PREF
,ID_AHIU
);
701 struct AHIUnitPrefs
*unitprefs
;
703 unitprefs
= (struct AHIUnitPrefs
*)ci
->ci_Data
;
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
);
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
);
756 Close((BPTR
) iff
->iff_Stream
);
761 // Avoids calling AHI_BestAudioID if not neccessary (faster startup time,
762 // since doesn't open all sub libraries.
765 mode
= &iounit
->AudioMode
;
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
);
777 /******************************************************************************
778 ** AllocHardware **************************************************************
779 ******************************************************************************/
781 // Allocates the audio hardware
784 AllocHardware ( struct AHIDevUnit
*iounit
,
785 struct AHIBase
*AHIBase
)
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
,
803 if(iounit
->AudioCtrl
!= NULL
)
806 AHI_GetAudioAttrs(AHI_INVALID_ID
,iounit
->AudioCtrl
,
807 AHIDB_FullDuplex
, (IPTR
)&fullduplex
,
808 AHIDB_Stereo
, (IPTR
)&stereo
,
809 AHIDB_Panning
, (IPTR
)&panning
,
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
),
822 (iounit
->OutputVolume
== ~0 ? TAG_IGNORE
: AHIC_OutputVolume
),
823 iounit
->OutputVolume
,
825 (iounit
->Input
== ~0 ? TAG_IGNORE
: AHIC_Input
),
828 (iounit
->Output
== ~0 ? TAG_IGNORE
: AHIC_Output
),
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
))
845 /******************************************************************************
846 ** FreeHardware ***************************************************************
847 ******************************************************************************/
849 // Take a wild guess!
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 ******************************************************************************/
878 struct Process
*proc
;
879 struct StartupMessage
*sm
;
880 struct AHIDevUnit
*iounit
;
883 proc
= (struct Process
*)FindTask(NULL
);
884 WaitPort(&proc
->pr_MsgPort
);
885 sm
= (struct StartupMessage
*)GetMsg(&proc
->pr_MsgPort
);
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
);
912 signalbit
= AllocSignal(-1);
913 iounit
->PlaySignal
= AllocSignal(-1);
914 iounit
->RecordSignal
= AllocSignal(-1);
915 iounit
->SampleSignal
= AllocSignal(-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
936 iounit
->Process
= proc
;
940 /* Reply to our startup message */
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
);
956 signals
= Wait(waitmask
);
958 /* Have we been signaled to shut down? */
959 if(signals
& SIGBREAKF_CTRL_F
)
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
);
1006 Signal((struct Task
*) iounit
->Process
, 1UL << iounit
->SyncSignal
);
1008 FreeSignal(signalbit
);
1012 /******************************************************************************
1013 ** PlayerFunc *****************************************************************
1014 ******************************************************************************/
1017 PlayerFunc( struct Hook
* hook
,
1018 struct AHIAudioCtrl
* actrl
,
1021 struct AHIDevUnit
*iounit
= (struct AHIDevUnit
*) hook
->h_Data
;
1023 if(AHIAttemptSemaphore(&iounit
->Lock
))
1025 UpdateSilentPlayers(iounit
,AHIBase
);
1026 AHIReleaseSemaphore(&iounit
->Lock
);
1029 { // Do it later instead
1030 Signal((struct Task
*) iounit
->Master
, (1L << iounit
->PlaySignal
));
1037 /******************************************************************************
1038 ** RecordFunc *****************************************************************
1039 ******************************************************************************/
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
));
1059 /******************************************************************************
1060 ** SoundFunc ******************************************************************
1061 ******************************************************************************/
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
;
1086 voice
->PlayingRequest
= voice
->QueuedRequest
;
1087 voice
->Flags
|= VF_STARTED
;
1088 voice
->QueuedRequest
= NULL
;
1090 switch(voice
->NextOffset
)
1096 /* A AHI_NOSOUND is done, channel is silent */
1097 voice
->NextOffset
= FREE
;
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
;
1107 /* A normal sound is done, and another is waiting */
1108 AHI_SetSound(sndmsg
->ahism_Channel
,
1113 AHI_SetFreq(sndmsg
->ahism_Channel
,
1114 voice
->NextFrequency
,
1116 AHI_SetVol(sndmsg
->ahism_Channel
,
1120 voice
->QueuedRequest
= voice
->NextRequest
;
1121 voice
->NextRequest
= NULL
;
1122 voice
->NextOffset
= PLAY
;
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
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
;
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
;