revert between 56095 -> 55830 in arch
[AROS.git] / workbench / devs / AHI / Device / device.c
blob62074807c84746876cb10057dbc189d0a4793847
1 /*
2 AHI - Hardware independent audio subsystem
3 Copyright (C) 2017 The AROS Dev Team
4 Copyright (C) 1996-2005 Martin Blom <martin@blom.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with this library; if not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge,
19 MA 02139, USA.
22 #include <config.h>
24 #include <exec/alerts.h>
25 #include <exec/errors.h>
26 #include <exec/tasks.h>
27 #include <exec/io.h>
28 #include <exec/devices.h>
29 #include <exec/memory.h>
30 #include <dos/dos.h>
31 #include <dos/dostags.h>
32 #include <libraries/iffparse.h>
33 #include <prefs/prefhdr.h>
35 #include <clib/alib_protos.h>
36 #include <proto/exec.h>
37 #include <proto/dos.h>
38 #include <proto/iffparse.h>
39 #define __NOLIBBASE__
40 #define __NOGLOBALIFACE__
41 #include <proto/ahi.h>
42 #undef __NOLIBBASE__
43 #undef __NOGLOBALIFACE__
44 #include <proto/ahi_sub.h>
46 #include <stddef.h>
48 #include "ahi_def.h"
49 #include "debug.h"
50 #include "device.h"
51 #include "devcommands.h"
52 #include "gateway.h"
53 #include "header.h"
54 #include "misc.h"
56 #ifdef __MORPHOS__
57 #define IS_MORPHOS 1
58 #else
59 #define IS_MORPHOS 0
60 #endif
63 ** Message passed to the Unit Process at
64 ** startup time.
67 struct StartupMessage
69 struct Message Msg;
70 struct AHIDevUnit *Unit;
74 static struct
75 AHIDevUnit *InitUnit( ULONG , struct AHIBase * );
77 static void
78 ExpungeUnit( struct AHIDevUnit *, struct AHIBase * );
80 static void
81 PlayerFunc( struct Hook* hook,
82 struct AHIAudioCtrl* actrl,
83 APTR null );
86 static ULONG
87 RecordFunc( struct Hook* hook,
88 struct AHIAudioCtrl* actrl,
89 struct AHIRecordMessage* recmsg );
92 static void
93 SoundFunc( struct Hook* hook,
94 struct AHIAudioCtrl* actrl,
95 struct AHISoundMessage* sndmsg );
98 static void
99 ChannelInfoFunc( struct Hook* hook,
100 struct AHIAudioCtrl* actrl,
101 struct AHIEffChannelInfo* cimsg );
104 /***** ahi.device/--background-- *******************************************
106 * PURPOSE
108 * The 'ahi.device' was first created because the lack of standards
109 * when it comes to sound cards on the Amiga. Another reason was to
110 * make it easier to write multi-channel music programs.
112 * This device is by no means the final and perfect solution. But
113 * hopefully, it can evolve into something useful until AT brings you
114 * The Real Thing (TM).
116 * OVERVIEW
118 * Please see the document "AHI Developer's Guide" for more
119 * information.
122 * * Driver based
124 * Each supported sound card is controlled by a library-based audio
125 * driver. For a 'dumb' sound card, a new driver could be written in
126 * a few hours. For a 'smart' sound card, it is possible to utilize an
127 * on-board DSP, for example, to maximize performance and sound quality.
128 * For sound cards with own DSP but little or no memory, it is possible
129 * to use the main CPU to mix channels and do the post-processing
130 * with the DSP. Drivers are available for most popular sound cards,
131 * as well as an 8SVX (mono) and AIFF/AIFC (mono & stereo) sample render
132 * driver.
134 * * Fast, powerful mixing routines (yeah, right... haha)
136 * The device's mixing routines mix 8- or 16-bit signed samples,
137 * both mono, stereo and 7.1, located in Fast-RAM and outputs
138 * 16-bit mono or stereo (with stereo panning if desired) data,
139 * using any number of channels (as long as 'any' means less than
140 * 128). Tables can be used speed the mixing up (especially when
141 * using 8-bit samples). The samples can have any length
142 * (including odd) and can have any number of loops. There are
143 * also so-called HiFi mixing routines that can be used, that use
144 * linear interpolation and gives 32 bit output.
146 * * Support for non-realtime mixing
148 * By providing a timing feature, it is possible to create high-
149 * quality output even if the processing power is lacking, by saving
150 * the output to disk, for example as an IFF AIFF or 8SXV file.
152 * * Audio database
154 * Uses ID codes, much like Screenmode IDs, to select the many
155 * parameters that can be set. The functions to access the audio
156 * database are not too different from those in 'graphics.library'.
157 * The device also features a requester to get an ID code from the
158 * user.
160 * * Both high- and low-level protocol
162 * By acting both like a device and a library, AHI gives the programmer
163 * a choice between full control and simplicity. The device API allows
164 * several programs to use the audio hardware at the same time, and
165 * the AUDIO: dos-device driver makes playing and recording sound very
166 * simple for both the programmer and user.
168 * * Future Compatible
170 * When AmigaOS gets device-independent audio worth it's name, it should
171 * not be too difficult to write a driver for AHI, allowing applications
172 * using 'ahi.device' to automatically use the new OS interface. At
173 * least I hope it wont.
176 ****************************************************************************
181 /******************************************************************************
182 ** DevOpen ********************************************************************
183 ******************************************************************************/
185 /****** ahi.device/OpenDevice **********************************************
187 * NAME
188 * OpenDevice -- Open the device
190 * SYNOPSIS
191 * error = OpenDevice(AHINAME, unit, ioRequest, flags)
192 * D0 A0 D0 A1 D1
194 * BYTE OpenDevice(STRPTR, ULONG, struct AHIRequest *, ULONG);
196 * FUNCTION
197 * This is an exec call. Exec will search for the ahi.device, and
198 * if found, will pass this call on to the device.
200 * INPUTS
201 * AHINAME - pointer to the string "ahi.device".
202 * unit - Either AHI_DEFAULT_UNIT (0), AHI_NO_UNIT (255) or any other
203 * unit the user has requested, for example with a UNIT tooltype.
204 * AHI_NO_UNIT should be used when you're using the low-level
205 * API.
206 * ioRequest - a pointer to a struct AHIRequest, initialized by
207 * exec.library/CreateIORequest(). ahir_Version *must* be preset
208 * to the version you need!
209 * flags - There is only one flag defined, AHIDF_NOMODESCAN, which
210 * asks ahi.device not to build the audio mode database if not
211 * already initialized. It should not be used by applications
212 * without good reasons (AddAudioModes uses this flag).
214 * RESULT
215 * error - Same as io_Error.
216 * io_Error - If the call succeeded, io_Error will be 0, else
217 * an error code as defined in <exec/errors.h> and
218 * <devices/ahi.h>.
219 * io_Device - A pointer to the device base, which can be used
220 * to call the functions the device provides.
222 * EXAMPLE
224 * NOTES
226 * BUGS
228 * SEE ALSO
229 * CloseDevice(), exec.library/OpenDevice(), <exec/errors.h>,
230 * <devices/ahi.h>.
232 ****************************************************************************
236 // This function is called by the system each time a unit is opened with
237 // exec.library/OpenDevice().
239 ULONG
240 _DevOpen ( struct AHIRequest* ioreq,
241 ULONG unit,
242 ULONG flags,
243 struct AHIBase* AHIBase )
245 ULONG rc = 0;
246 BOOL error = FALSE;
247 struct AHIDevUnit *iounit=NULL;
249 ahibug("[AHI:Device] %s()\n", __func__);
251 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
253 KPrintF("OpenDevice(%ld, 0x%p, %ld)", unit, (IPTR)ioreq, flags);
256 // Check if size includes the ahir_Version field
258 if(ioreq->ahir_Std.io_Message.mn_Length < (sizeof(struct IOStdReq) + 2))
260 Req( "Bad parameters to OpenDevice()." );
261 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
262 return IOERR_OPENFAIL;
265 // One more check...
267 if((unit != AHI_NO_UNIT) && (ioreq->ahir_Version >= 4))
269 if(ioreq->ahir_Std.io_Message.mn_Length < sizeof(struct AHIRequest))
271 Req( "Bad parameters to OpenDevice()." );
272 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
273 return IOERR_OPENFAIL;
275 else
277 /* KPrintF( "Tagging %08lx on task %08lx\n", ioreq, FindTask(0)); */
278 ioreq->ahir_Private[1] = (IPTR) ioreq;
282 AHIBase->ahib_Library.lib_OpenCnt++;
284 ObtainSemaphore(&AHIBase->ahib_Lock);
286 if( ! (flags & AHIDF_NOMODESCAN))
288 // Load database if not already loaded
290 if(AHI_NextAudioID(AHI_INVALID_ID) == (IPTR) AHI_INVALID_ID)
292 AHI_LoadModeFile("DEVS:AudioModes");
294 // Be quiet here. - Piru
295 if (IS_MORPHOS)
297 APTR *windowptr = &((struct Process *) FindTask(NULL))->pr_WindowPtr;
298 APTR oldwindowptr = *windowptr;
299 *windowptr = (APTR) -1;
301 AHI_LoadModeFile("MOSSYS:DEVS/AudioModes");
303 *windowptr = oldwindowptr;
306 // Load Void driver if no real hardware was found
308 if(AHI_NextAudioID(AHI_INVALID_ID) == (IPTR) AHI_INVALID_ID)
310 AHI_LoadModeFile("SYS:Storage/AudioModes/VOID");
315 if( ioreq->ahir_Version > AHIBase->ahib_Library.lib_Version)
316 error=TRUE;
317 else
319 if(unit < AHI_UNITS)
321 iounit=InitUnit(unit,AHIBase);
322 if(!iounit)
323 error=TRUE;
325 else if(unit == AHI_NO_UNIT)
326 InitUnit(unit,AHIBase);
329 if(!error)
331 ioreq->ahir_Std.io_Unit=(struct Unit *) iounit;
332 if(iounit) // Is NULL for AHI_NO_UNIT
333 iounit->Unit.unit_OpenCnt++;
334 AHIBase->ahib_Library.lib_OpenCnt++;
335 AHIBase->ahib_Library.lib_Flags &=~LIBF_DELEXP;
337 else
339 rc=IOERR_OPENFAIL;
340 ioreq->ahir_Std.io_Error=IOERR_OPENFAIL;
341 ioreq->ahir_Std.io_Device=(struct Device *) -1;
342 ioreq->ahir_Std.io_Unit=(struct Unit *) -1;
345 ReleaseSemaphore(&AHIBase->ahib_Lock);
347 AHIBase->ahib_Library.lib_OpenCnt--;
349 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
351 KPrintF("=>%ld\n",rc);
354 return rc;
358 /******************************************************************************
359 ** DevClose *******************************************************************
360 ******************************************************************************/
362 /****** ahi.device/CloseDevice *********************************************
364 * NAME
365 * CloseDevice -- Close the device
367 * SYNOPSIS
368 * CloseDevice(ioRequest)
369 * A1
371 * void CloseDevice(struct IORequest *);
373 * FUNCTION
374 * This is an exec call that closes the device. Every OpenDevice()
375 * must be matched with a call to CloseDevice().
377 * The user must ensure that all outstanding IO Requests have been
378 * returned before closing the device.
380 * INPUTS
381 * ioRequest - a pointer to the same struct AHIRequest that was used
382 * to open the device.
384 * RESULT
386 * EXAMPLE
388 * NOTES
390 * BUGS
392 * SEE ALSO
393 * OpenDevice(), exec.library/CloseDevice()
395 ****************************************************************************
399 // This function is called by the system each time a unit is closed with
400 // exec.library/CloseDevice().
402 BPTR
403 _DevClose ( struct AHIRequest* ioreq,
404 struct AHIBase* AHIBase )
406 struct AHIDevUnit *iounit;
407 BPTR seglist=0;
409 ahibug("[AHI:Device] %s()\n", __func__);
411 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
413 KPrintF("CloseDevice(0x%P)\n", (IPTR)ioreq);
415 if( ioreq->ahir_Private[1] != (IPTR) ioreq )
417 KPrintF( "Warning: Expected I/O request 0x%P.\n",
418 ioreq->ahir_Private[1] );
422 ObtainSemaphore(&AHIBase->ahib_Lock);
424 iounit= (struct AHIDevUnit *) ioreq->ahir_Std.io_Unit;
425 ioreq->ahir_Std.io_Device = (struct Device *) -1;
426 ioreq->ahir_Std.io_Unit = (struct Unit *) -1;
428 if(iounit)
430 iounit->Unit.unit_OpenCnt--;
431 if(!iounit->Unit.unit_OpenCnt)
432 ExpungeUnit(iounit,AHIBase);
435 AHIBase->ahib_Library.lib_OpenCnt--;
437 ReleaseSemaphore(&AHIBase->ahib_Lock);
439 if(!AHIBase->ahib_Library.lib_OpenCnt)
441 if(AHIBase->ahib_Library.lib_Flags & LIBF_DELEXP)
442 seglist=_DevExpunge(AHIBase);
444 return seglist;
448 /******************************************************************************
449 ** InitUnit *******************************************************************
450 ******************************************************************************/
452 // This function is called by DevOpen() to initialize a unit
454 static struct AHIDevUnit *
455 InitUnit ( ULONG unit,
456 struct AHIBase *AHIBase )
458 struct AHIDevUnit *iounit;
460 ahibug("[AHI:Device] %s()\n", __func__);
462 if( unit == AHI_NO_UNIT )
464 ReadConfig(NULL,AHIBase);
465 return NULL;
467 else if(!AHIBase->ahib_DevUnits[unit])
469 if((iounit = AllocVec(sizeof(struct AHIDevUnit), MEMF_CLEAR|MEMF_PUBLIC)))
471 NewList(&iounit->Unit.unit_MsgPort.mp_MsgList);
473 iounit->Unit.unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
474 iounit->Unit.unit_MsgPort.mp_Flags = PA_IGNORE;
475 iounit->Unit.unit_MsgPort.mp_Node.ln_Name = AHINAME " Unit";
476 iounit->UnitNum = unit;
477 AHIInitSemaphore(&iounit->Lock);
478 NewList((struct List *)&iounit->ReadList);
479 NewList((struct List *)&iounit->PlayingList);
480 NewList((struct List *)&iounit->SilentList);
481 NewList((struct List *)&iounit->WaitingList);
482 NewList((struct List *)&iounit->RequestQueue);
484 if(ReadConfig(iounit,AHIBase))
486 if((iounit->Voices = AllocVec(
487 sizeof(struct Voice)*iounit->Channels,MEMF_PUBLIC|MEMF_CLEAR)))
489 int i;
490 struct Voice *v = iounit->Voices;
491 struct MsgPort *replyport;
493 // Mark all channels as free
494 for(i = 0 ; i < iounit->Channels; i++)
496 v->NextOffset = FREE;
497 v++;
500 replyport = CreateMsgPort();
502 if( replyport != NULL )
504 struct StartupMessage sm =
507 { NULL, NULL, NT_UNKNOWN, 0, NULL },
508 replyport, sizeof(struct StartupMessage),
510 iounit
513 iounit->Process = CreateNewProcTags( NP_Entry, (IPTR) &m68k_DevProc,
514 NP_Name, (IPTR) AHINAME " Unit Process",
515 NP_Priority, AHI_PRI,
516 TAG_DONE );
518 if( iounit->Process != NULL )
521 PutMsg( &iounit->Process->pr_MsgPort,
522 &sm.Msg );
524 WaitPort(replyport);
525 GetMsg(replyport);
527 DeleteMsgPort(replyport);
532 if(!iounit->Process)
533 FreeVec(iounit);
534 else
535 AHIBase->ahib_DevUnits[unit] = iounit;
539 return AHIBase->ahib_DevUnits[unit];
543 /******************************************************************************
544 ** ExpungeUnit ****************************************************************
545 ******************************************************************************/
547 // This function is called by DevClose() to remove a unit.
549 static void
550 ExpungeUnit ( struct AHIDevUnit *iounit,
551 struct AHIBase *AHIBase )
553 struct Task *unittask;
554 BYTE signal;
556 ahibug("[AHI:Device] %s()\n", __func__);
558 signal = AllocSignal(-1);
559 if(signal == -1)
561 /* Fallback */
562 signal = SIGB_SINGLE;
563 SetSignal(0, SIGF_SINGLE);
566 unittask = (struct Task *) iounit->Process;
567 iounit->Process = (struct Process *) FindTask(NULL);
568 iounit->SyncSignal = signal;
569 Signal(unittask,SIGBREAKF_CTRL_F);
570 Wait(1UL << signal);
571 AHIBase->ahib_DevUnits[iounit->UnitNum]=NULL;
572 FreeVec(iounit->Voices);
573 FreeVec(iounit);
575 if(signal != SIGB_SINGLE)
577 FreeSignal(signal);
582 /******************************************************************************
583 ** ReadConfig *****************************************************************
584 ******************************************************************************/
586 // This functions loads the users settings for AHI.
588 BOOL
589 ReadConfig ( struct AHIDevUnit *iounit,
590 struct AHIBase *AHIBase )
592 struct IFFHandle *iff;
593 struct StoredProperty *ahig;
594 struct CollectionItem *ci;
595 IPTR *mode;
597 ahibug("[AHI:Device] %s()\n", __func__);
599 if(iounit)
601 /* Internal defaults for device unit */
602 iounit->AudioMode = AHI_INVALID_ID; // See at the end of the function!
603 iounit->Frequency = 44100;
604 iounit->Channels = 4;
605 iounit->MonitorVolume = ~0;
606 iounit->InputGain = ~0;
607 iounit->OutputVolume = ~0;
608 iounit->Input = ~0;
609 iounit->Output = ~0;
611 else
613 /* Internal defaults for low-level mode */
614 AHIBase->ahib_AudioMode = AHI_INVALID_ID;
615 AHIBase->ahib_Frequency = 44100;
616 AHIBase->ahib_MonitorVolume = 0x00000;
617 AHIBase->ahib_InputGain = 0x10000;
618 AHIBase->ahib_OutputVolume = 0x10000;
619 AHIBase->ahib_Input = 0;
620 AHIBase->ahib_Output = 0;
623 if((iff=AllocIFF()))
625 iff->iff_Stream=(IPTR) Open("ENV:Sys/ahi.prefs", MODE_OLDFILE);
626 if(iff->iff_Stream)
628 InitIFFasDOS(iff);
629 if(!OpenIFF(iff,IFFF_READ))
631 if(!(PropChunk(iff,ID_PREF,ID_PRHD)
632 || PropChunk(iff,ID_PREF,ID_AHIG)
633 || CollectionChunk(iff,ID_PREF,ID_AHIU)
634 || StopOnExit(iff,ID_PREF,ID_FORM)))
636 if(ParseIFF(iff,IFFPARSE_SCAN) == IFFERR_EOC)
638 ahig=FindProp(iff,ID_PREF,ID_AHIG);
640 if(ahig)
642 struct AHIGlobalPrefs *globalprefs;
643 UWORD debug_level;
645 globalprefs = (struct AHIGlobalPrefs *)ahig->sp_Data;
647 debug_level = globalprefs->ahigp_DebugLevel;
648 EndianSwap( sizeof (UWORD), &debug_level );
649 AHIBase->ahib_DebugLevel = debug_level;
651 AHIBase->ahib_Flags = 0;
653 /* Not used in version 5:
655 * if(globalprefs->ahigp_DisableSurround)
656 * AHIBase->ahib_Flags |= AHIBF_NOSURROUND;
658 * if(globalprefs->ahigp_DisableEcho)
659 * AHIBase->ahib_Flags |= AHIBF_NOECHO;
661 * if(globalprefs->ahigp_FastEcho)
662 * AHIBase->ahib_Flags |= AHIBF_FASTECHO;
666 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
667 ahigp_MaxCPU) )
669 AHIBase->ahib_MaxCPU = globalprefs->ahigp_MaxCPU;
670 EndianSwap( sizeof (Fixed), &AHIBase->ahib_MaxCPU );
672 else
674 AHIBase->ahib_MaxCPU = 0x10000 * 90 / 100;
677 /* In version 5: Clipping is always used
679 * if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
680 * ahigp_ClipMasterVolume) )
682 * if(globalprefs->ahigp_ClipMasterVolume)
683 * AHIBase->ahib_Flags |= AHIBF_CLIPPING;
687 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
688 ahigp_AntiClickTime ) )
690 AHIBase->ahib_AntiClickTime = globalprefs->ahigp_AntiClickTime;
691 EndianSwap( sizeof (Fixed), &AHIBase->ahib_AntiClickTime );
693 else
695 AHIBase->ahib_AntiClickTime = 0;
698 if( (ULONG) ahig->sp_Size > offsetof( struct AHIGlobalPrefs,
699 ahigp_ScaleMode ) )
701 AHIBase->ahib_ScaleMode = globalprefs->ahigp_ScaleMode;
702 EndianSwap( sizeof (UWORD), &AHIBase->ahib_ScaleMode );
704 else
706 AHIBase->ahib_ScaleMode = AHI_SCALE_FIXED_0_DB;
709 ci=FindCollection(iff,ID_PREF,ID_AHIU);
710 while(ci)
712 struct AHIUnitPrefs *unitprefs;
714 unitprefs = (struct AHIUnitPrefs *)ci->ci_Data;
716 if(iounit)
718 if(unitprefs->ahiup_Unit == iounit->UnitNum)
720 iounit->AudioMode = unitprefs->ahiup_AudioMode;
721 iounit->Frequency = unitprefs->ahiup_Frequency;
722 iounit->Channels = unitprefs->ahiup_Channels;
723 iounit->MonitorVolume = unitprefs->ahiup_MonitorVolume;
724 iounit->InputGain = unitprefs->ahiup_InputGain;
725 iounit->OutputVolume = unitprefs->ahiup_OutputVolume;
726 iounit->Input = unitprefs->ahiup_Input;
727 iounit->Output = unitprefs->ahiup_Output;
729 EndianSwap( sizeof (ULONG), &iounit->AudioMode );
730 EndianSwap( sizeof (ULONG), &iounit->Frequency );
731 EndianSwap( sizeof (UWORD), &iounit->Channels );
732 EndianSwap( sizeof (Fixed), &iounit->MonitorVolume );
733 EndianSwap( sizeof (Fixed), &iounit->InputGain );
734 EndianSwap( sizeof (Fixed), &iounit->OutputVolume );
735 EndianSwap( sizeof (ULONG), &iounit->Input );
736 EndianSwap( sizeof (ULONG), &iounit->Output );
739 else
741 if(unitprefs->ahiup_Unit == AHI_NO_UNIT)
743 AHIBase->ahib_AudioMode = unitprefs->ahiup_AudioMode;
744 AHIBase->ahib_Frequency = unitprefs->ahiup_Frequency;
745 AHIBase->ahib_MonitorVolume = unitprefs->ahiup_MonitorVolume;
746 AHIBase->ahib_InputGain = unitprefs->ahiup_InputGain;
747 AHIBase->ahib_OutputVolume = unitprefs->ahiup_OutputVolume;
748 AHIBase->ahib_Input = unitprefs->ahiup_Input;
749 AHIBase->ahib_Output = unitprefs->ahiup_Output;
751 EndianSwap( sizeof (ULONG), &AHIBase->ahib_AudioMode);
752 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Frequency);
753 EndianSwap( sizeof (Fixed), &AHIBase->ahib_MonitorVolume);
754 EndianSwap( sizeof (Fixed), &AHIBase->ahib_InputGain);
755 EndianSwap( sizeof (Fixed), &AHIBase->ahib_OutputVolume);
756 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Input);
757 EndianSwap( sizeof (ULONG), &AHIBase->ahib_Output);
761 ci=ci->ci_Next;
765 CloseIFF(iff);
767 Close((BPTR) iff->iff_Stream);
769 FreeIFF(iff);
772 // Avoids calling AHI_BestAudioID if not neccessary (faster startup time,
773 // since doesn't open all sub libraries.
775 if(iounit)
776 mode = &iounit->AudioMode;
777 else
778 mode = &AHIBase->ahib_AudioMode;
779 if(mode[0] == (IPTR) AHI_INVALID_ID)
780 { static const Tag tags[] = { AHIDB_Realtime,TRUE,TAG_DONE };
781 mode[0] = AHI_BestAudioIDA((struct TagItem *)tags);
784 return TRUE;
788 /******************************************************************************
789 ** AllocHardware **************************************************************
790 ******************************************************************************/
792 // Allocates the audio hardware
794 BOOL
795 AllocHardware ( struct AHIDevUnit *iounit,
796 struct AHIBase *AHIBase )
798 BOOL rc = FALSE;
799 ULONG fullduplex = FALSE;
800 ULONG stereo = FALSE;
801 ULONG panning = FALSE;
803 ahibug("[AHI:Device] %s()\n", __func__);
805 /* Allocate the hardware */
806 iounit->AudioCtrl = AHI_AllocAudio(
807 AHIA_AudioID, (IPTR)iounit->AudioMode,
808 AHIA_MixFreq, (IPTR)iounit->Frequency,
809 AHIA_Channels, (IPTR)iounit->Channels,
810 AHIA_Sounds, MAXSOUNDS,
811 AHIA_PlayerFunc, (IPTR)&iounit->PlayerHook,
812 AHIA_RecordFunc, (IPTR)&iounit->RecordHook,
813 AHIA_SoundFunc, (IPTR)&iounit->SoundHook,
814 TAG_DONE);
816 if(iounit->AudioCtrl != NULL)
818 /* Full duplex? */
819 AHI_GetAudioAttrs(AHI_INVALID_ID,iounit->AudioCtrl,
820 AHIDB_FullDuplex, (IPTR)&fullduplex,
821 AHIDB_Stereo, (IPTR)&stereo,
822 AHIDB_Panning, (IPTR)&panning,
823 TAG_DONE);
824 iounit->FullDuplex = fullduplex;
825 iounit->PseudoStereo = stereo && !panning;
827 /* Set hardware properties */
828 AHI_ControlAudio(iounit->AudioCtrl,
829 (iounit->MonitorVolume == ~0 ? TAG_IGNORE : AHIC_MonitorVolume),
830 iounit->MonitorVolume,
832 (iounit->InputGain == ~0 ? TAG_IGNORE : AHIC_InputGain),
833 iounit->InputGain,
835 (iounit->OutputVolume == ~0 ? TAG_IGNORE : AHIC_OutputVolume),
836 iounit->OutputVolume,
838 (iounit->Input == ~0 ? TAG_IGNORE : AHIC_Input),
839 iounit->Input,
841 (iounit->Output == ~0 ? TAG_IGNORE : AHIC_Output),
842 iounit->Output,
844 TAG_DONE);
846 iounit->ChannelInfoStruct->ahie_Effect = AHIET_CHANNELINFO;
847 iounit->ChannelInfoStruct->ahieci_Func = &iounit->ChannelInfoHook;
848 iounit->ChannelInfoStruct->ahieci_Channels = iounit->Channels;
849 if(!AHI_SetEffect(iounit->ChannelInfoStruct, iounit->AudioCtrl))
851 rc = TRUE;
854 return rc;
858 /******************************************************************************
859 ** FreeHardware ***************************************************************
860 ******************************************************************************/
862 // Take a wild guess!
864 void
865 FreeHardware ( struct AHIDevUnit *iounit,
866 struct AHIBase *AHIBase )
868 ahibug("[AHI:Device] %s()\n", __func__);
870 if(iounit->AudioCtrl)
872 if(iounit->ChannelInfoStruct)
874 iounit->ChannelInfoStruct->ahie_Effect = (AHIET_CANCEL | AHIET_CHANNELINFO);
875 AHI_SetEffect(iounit->ChannelInfoStruct, iounit->AudioCtrl);
877 AHI_FreeAudio(iounit->AudioCtrl);
878 iounit->AudioCtrl = NULL;
879 iounit->IsRecording = FALSE;
880 iounit->IsPlaying = FALSE;
881 iounit->ValidRecord = FALSE;
886 /******************************************************************************
887 ** DevProc ********************************************************************
888 ******************************************************************************/
890 void
891 DevProc( void )
893 struct Process *proc;
894 struct StartupMessage *sm;
895 struct AHIDevUnit *iounit;
896 BYTE signalbit;
898 ahibug("[AHI:Device] %s()\n", __func__);
900 proc = (struct Process *)FindTask(NULL);
901 WaitPort(&proc->pr_MsgPort);
902 sm = (struct StartupMessage *)GetMsg(&proc->pr_MsgPort);
903 iounit = sm->Unit;
905 iounit->Process = NULL;
907 iounit->PlayerHook.h_Entry = (HOOKFUNC) HookEntry;
908 iounit->PlayerHook.h_SubEntry = (HOOKFUNC) PlayerFunc;
909 iounit->PlayerHook.h_Data = iounit;
911 iounit->RecordHook.h_Entry = (HOOKFUNC) HookEntry;
912 iounit->RecordHook.h_SubEntry = (HOOKFUNC) RecordFunc;
913 iounit->RecordHook.h_Data = iounit;
915 iounit->SoundHook.h_Entry = (HOOKFUNC) HookEntry;
916 iounit->SoundHook.h_SubEntry = (HOOKFUNC) SoundFunc;
917 iounit->SoundHook.h_Data = iounit;
919 iounit->ChannelInfoHook.h_Entry = (HOOKFUNC) HookEntry;
920 iounit->ChannelInfoHook.h_SubEntry = (HOOKFUNC) ChannelInfoFunc;
921 iounit->ChannelInfoHook.h_Data = iounit;
923 iounit->ChannelInfoStruct = AllocVec(
924 sizeof(struct AHIEffChannelInfo) + (iounit->Channels * sizeof(ULONG)),
925 MEMF_PUBLIC | MEMF_CLEAR);
927 iounit->Master=proc;
929 signalbit = AllocSignal(-1);
930 iounit->PlaySignal = AllocSignal(-1);
931 iounit->RecordSignal = AllocSignal(-1);
932 iounit->SampleSignal = AllocSignal(-1);
934 if((signalbit != -1)
935 && (iounit->PlaySignal != -1)
936 && (iounit->RecordSignal != -1)
937 && (iounit->SampleSignal != -1)
938 && (iounit->ChannelInfoStruct != NULL)
941 /* Set up our Unit's MsgPort. */
942 iounit->Unit.unit_MsgPort.mp_SigBit = signalbit;
943 iounit->Unit.unit_MsgPort.mp_SigTask = (struct Task *)proc;
944 iounit->Unit.unit_MsgPort.mp_Flags = PA_SIGNAL;
946 /* Allocate the hardware */
947 if(AllocHardware(iounit, AHIBase))
950 /* Set iounit->Process to pointer to our unit process.
951 This will let the Unit init code know that were
952 are okay. */
953 iounit->Process = proc;
957 /* Reply to our startup message */
958 ReplyMsg(&sm->Msg);
960 if(iounit->Process)
962 ULONG waitmask,signals;
964 waitmask = (1L << signalbit)
965 | SIGBREAKF_CTRL_E // Dummy signal to wake up task
966 | SIGBREAKF_CTRL_F // Quit signal
967 | (1L << iounit->PlaySignal)
968 | (1L << iounit->RecordSignal)
969 | (1L << iounit->SampleSignal);
971 while(TRUE)
973 signals = Wait(waitmask);
975 /* Have we been signaled to shut down? */
976 if(signals & SIGBREAKF_CTRL_F)
977 break;
979 if(signals & (1L << iounit->SampleSignal))
981 RethinkPlayers(iounit,AHIBase);
984 if(signals & (1L << signalbit))
986 struct AHIRequest *ioreq;
988 while((ioreq = (struct AHIRequest *) GetMsg(&iounit->Unit.unit_MsgPort)))
990 PerformIO(ioreq,AHIBase);
994 if(signals & (1L << iounit->PlaySignal))
996 AHIObtainSemaphore(&iounit->Lock);
998 UpdateSilentPlayers(iounit,AHIBase);
1000 AHIReleaseSemaphore(&iounit->Lock);
1003 if(signals & (1L << iounit->RecordSignal))
1005 iounit->ValidRecord = TRUE;
1006 FeedReaders(iounit,AHIBase);
1011 FreeHardware(iounit, AHIBase);
1012 FreeSignal(iounit->SampleSignal);
1013 iounit->SampleSignal = -1;
1014 FreeSignal(iounit->RecordSignal);
1015 iounit->RecordSignal = -1;
1016 FreeSignal(iounit->PlaySignal);
1017 iounit->PlaySignal = -1;
1018 FreeVec(iounit->ChannelInfoStruct);
1020 if(iounit->Process)
1022 Forbid();
1023 Signal((struct Task *) iounit->Process, 1UL << iounit->SyncSignal);
1025 FreeSignal(signalbit);
1029 /******************************************************************************
1030 ** PlayerFunc *****************************************************************
1031 ******************************************************************************/
1033 static void
1034 PlayerFunc( struct Hook* hook,
1035 struct AHIAudioCtrl* actrl,
1036 APTR null )
1038 struct AHIDevUnit *iounit = (struct AHIDevUnit *) hook->h_Data;
1040 if(AHIAttemptSemaphore(&iounit->Lock))
1042 UpdateSilentPlayers(iounit,AHIBase);
1043 AHIReleaseSemaphore(&iounit->Lock);
1045 else
1046 { // Do it later instead
1047 Signal((struct Task *) iounit->Master, (1L << iounit->PlaySignal));
1050 return;
1054 /******************************************************************************
1055 ** RecordFunc *****************************************************************
1056 ******************************************************************************/
1058 static ULONG
1059 RecordFunc( struct Hook* hook,
1060 struct AHIAudioCtrl* actrl,
1061 struct AHIRecordMessage* recmsg )
1063 struct AHIDevUnit *iounit;
1065 if(recmsg->ahirm_Type == AHIST_S16S)
1067 iounit = (struct AHIDevUnit *) hook->h_Data;
1068 iounit->RecordBuffer = recmsg->ahirm_Buffer;
1069 iounit->RecordSize = recmsg->ahirm_Length<<2;
1070 Signal((struct Task *) iounit->Master, (1L << iounit->RecordSignal));
1072 return 0;
1076 /******************************************************************************
1077 ** SoundFunc ******************************************************************
1078 ******************************************************************************/
1080 static void
1081 SoundFunc( struct Hook* hook,
1082 struct AHIAudioCtrl* actrl,
1083 struct AHISoundMessage* sndmsg )
1085 struct AHIDevUnit* iounit;
1086 struct Voice* voice;
1087 struct AHIRequest* playreq;
1089 iounit = (struct AHIDevUnit *) hook->h_Data;
1090 voice = &iounit->Voices[(WORD)sndmsg->ahism_Channel];
1092 Disable(); // Not needed?
1094 playreq = voice->PlayingRequest;
1096 if( playreq != NULL )
1098 playreq->ahir_Std.io_Command = AHICMD_WRITTEN;
1101 Enable();
1103 voice->PlayingRequest = voice->QueuedRequest;
1104 voice->Flags |= VF_STARTED;
1105 voice->QueuedRequest = NULL;
1107 switch(voice->NextOffset)
1109 case FREE:
1110 break;
1112 case MUTE:
1113 /* A AHI_NOSOUND is done, channel is silent */
1114 voice->NextOffset = FREE;
1115 break;
1117 case PLAY:
1118 /* A normal sound is done and playing, no other sound is queued */
1119 AHI_SetSound(sndmsg->ahism_Channel,AHI_NOSOUND,0,0,actrl,AHISF_NONE);
1120 voice->NextOffset = MUTE;
1121 break;
1123 default:
1124 /* A normal sound is done, and another is waiting */
1125 AHI_SetSound(sndmsg->ahism_Channel,
1126 voice->NextSound,
1127 voice->NextOffset,
1128 voice->NextLength,
1129 actrl,AHISF_NONE);
1130 AHI_SetFreq(sndmsg->ahism_Channel,
1131 voice->NextFrequency,
1132 actrl,AHISF_NONE);
1133 AHI_SetVol(sndmsg->ahism_Channel,
1134 voice->NextVolume,
1135 voice->NextPan,
1136 actrl,AHISF_NONE);
1137 voice->QueuedRequest = voice->NextRequest;
1138 voice->NextRequest = NULL;
1139 voice->NextOffset = PLAY;
1140 break;
1143 Signal((struct Task *) iounit->Master, (1L << iounit->SampleSignal));
1147 /******************************************************************************
1148 ** ChannelInfoFunc ************************************************************
1149 ******************************************************************************/
1151 // This hook keeps updating the io_Actual field of each playing requests
1153 static void
1154 ChannelInfoFunc( struct Hook* hook,
1155 struct AHIAudioCtrl* actrl,
1156 struct AHIEffChannelInfo* cimsg )
1158 struct AHIDevUnit *iounit = (struct AHIDevUnit *) hook->h_Data;
1159 struct Voice *voice;
1160 ULONG *offsets = (ULONG *) &cimsg->ahieci_Offset;
1161 int i;
1163 Disable(); // Not needed?
1165 voice = iounit->Voices;
1167 for(i = 0; i < iounit->Channels; i++)
1169 struct AHIRequest* playreq;
1171 playreq = voice->PlayingRequest;
1173 if( playreq != NULL )
1175 playreq->ahir_Std.io_Actual = *offsets;
1178 voice++;
1179 offsets++;
1182 Enable();
1184 return;