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/memory.h>
24 #include <exec/semaphores.h>
25 #include <dos/dostags.h>
27 #include <libraries/iffparse.h>
29 #include <clib/alib_protos.h>
30 #include <proto/exec.h>
31 #include <proto/dos.h>
32 #include <proto/iffparse.h>
33 #include <proto/utility.h>
35 #define __NOGLOBALIFACE__
36 #include <proto/ahi.h>
38 #undef __NOGLOBALIFACE__
39 #include <proto/ahi_sub.h>
49 static ULONG
AddModeFile ( UBYTE
*filename
);
57 /******************************************************************************
58 ** Audio Database *************************************************************
59 ******************************************************************************/
63 struct MinNode ahidbn_MinNode
;
64 struct TagItem ahidbn_Tags
[0];
66 /* Taglist, mode name and driver name follows.
67 Size variable. Use FreeVec() to free node. */
71 ** Lock the database for read access. Return NULL if database not present.
74 struct AHI_AudioDatabase
*
77 struct AHI_AudioDatabase
*audiodb
;
81 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
85 ObtainSemaphoreShared((struct SignalSemaphore
*) audiodb
);
94 ** Lock the database for write access. Create it if not present.
97 struct AHI_AudioDatabase
*
98 LockDatabaseWrite(void)
100 struct AHI_AudioDatabase
*audiodb
;
104 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
108 ObtainSemaphore((struct SignalSemaphore
*) audiodb
);
112 audiodb
= (struct AHI_AudioDatabase
*)
113 AllocVec(sizeof(struct AHI_AudioDatabase
), MEMF_PUBLIC
|MEMF_CLEAR
);
118 NewList( (struct List
*) &audiodb
->ahidb_AudioModes
);
120 audiodb
->ahidb_Semaphore
.ss_Link
.ln_Name
= audiodb
->ahidb_Name
;
121 audiodb
->ahidb_Semaphore
.ss_Link
.ln_Pri
= 20;
122 strcpy(audiodb
->ahidb_Semaphore
.ss_Link
.ln_Name
, ADB_NAME
);
124 AddSemaphore((struct SignalSemaphore
*) audiodb
);
125 ObtainSemaphore((struct SignalSemaphore
*) audiodb
);
134 UnlockDatabase ( struct AHI_AudioDatabase
*audiodb
)
138 ReleaseSemaphore((struct SignalSemaphore
*) audiodb
);
143 GetDBTagList ( struct AHI_AudioDatabase
*audiodb
,
146 struct AHI_AudioMode
*node
;
147 struct TagItem
*rc
= NULL
;
149 if((audiodb
!= NULL
) && (id
!= AHI_INVALID_ID
))
151 for(node
=(struct AHI_AudioMode
*)audiodb
->ahidb_AudioModes
.mlh_Head
;
152 node
->ahidbn_MinNode
.mln_Succ
;
153 node
=(struct AHI_AudioMode
*)node
->ahidbn_MinNode
.mln_Succ
)
155 if(id
== GetTagData(AHIDB_AudioID
,AHI_INVALID_ID
,node
->ahidbn_Tags
))
157 rc
= node
->ahidbn_Tags
;
167 /******************************************************************************
168 ** AHI_NextAudioID ************************************************************
169 ******************************************************************************/
171 /****** ahi.device/AHI_NextAudioID ******************************************
174 * AHI_NextAudioID -- iterate current audio mode identifiers
177 * next_ID = AHI_NextAudioID( last_ID );
180 * ULONG AHI_NextAudioID( ULONG );
183 * This function is used to iterate through all current AudioIDs in
184 * the audio database.
187 * last_ID - previous AudioID or AHI_INVALID_ID if beginning iteration.
190 * next_ID - subsequent AudioID or AHI_INVALID_ID if no more IDs.
199 * AHI_GetAudioAttrsA(), AHI_BestAudioIDA()
201 ****************************************************************************
206 _AHI_NextAudioID( ULONG id
,
207 struct AHIBase
* AHIBase
)
209 struct AHI_AudioDatabase
*audiodb
;
210 struct AHI_AudioMode
*node
;
211 ULONG nextid
=AHI_INVALID_ID
;
213 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
215 Debug_NextAudioID(id
);
218 audiodb
= LockDatabase();
222 node
= (struct AHI_AudioMode
*) audiodb
->ahidb_AudioModes
.mlh_Head
;
224 if(id
!= AHI_INVALID_ID
)
230 thisid
= GetTagData(AHIDB_AudioID
,AHI_INVALID_ID
,node
->ahidbn_Tags
);
231 node
= (struct AHI_AudioMode
*) node
->ahidbn_MinNode
.mln_Succ
;
240 while(node
&& node
->ahidbn_MinNode
.mln_Succ
)
242 if( GetTagData( AHIDB_MultTable
, FALSE
, node
->ahidbn_Tags
) )
244 // Pretend the "Fast" modes are not here
245 node
= (struct AHI_AudioMode
*) node
->ahidbn_MinNode
.mln_Succ
;
249 nextid
= GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, node
->ahidbn_Tags
);
254 UnlockDatabase(audiodb
);
257 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
259 KPrintF("=>0x%08lx\n",nextid
);
266 /******************************************************************************
267 ** AHI_AddAudioMode ***********************************************************
268 ******************************************************************************/
270 /****i* ahi.device/AHI_AddAudioMode *****************************************
273 * AHI_AddAudioMode -- add an audio mode to the database (V4)
276 * success = AHI_AddAudioMode( DBtags );
279 * ULONG AHI_AddAudioMode( struct TagItem *, UBYTE *, UBYTE *);
282 * Adds the audio mode described by a taglist to the audio mode
283 * database. If the database does not exists, it will be created.
286 * DBtags - Tags describing the properties of this mode.
289 * success - FALSE if the mode could not be added.
299 ****************************************************************************
304 _AHI_AddAudioMode( struct TagItem
* DBtags
,
305 struct AHIBase
* AHIBase
)
307 struct AHI_AudioDatabase
*audiodb
;
308 struct AHI_AudioMode
*node
;
309 ULONG nodesize
= sizeof(struct AHI_AudioMode
), tagitems
= 0;
310 ULONG datalength
= 0, namelength
= 0, driverlength
= 0, dvrbasenamelength
= 0;
311 struct TagItem
*tstate
= DBtags
, *tp
, *tag
;
314 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
316 Debug_AddAudioMode(DBtags
);
319 // Remove old mode if present in database
320 AHI_RemoveAudioMode( GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, DBtags
));
322 // Now add the new mode
324 audiodb
= LockDatabaseWrite();
331 while( (tag
= NextTagItem(&tstate
)) != NULL
)
334 if(tag
->ti_Data
) switch(tag
->ti_Tag
)
337 datalength
= ((ULONG
*)tag
->ti_Data
)[0];
338 nodesize
+= datalength
;
342 namelength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
343 nodesize
+= namelength
;
347 driverlength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
348 nodesize
+= driverlength
;
351 case AHIDB_DriverBaseName
:
352 dvrbasenamelength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
353 nodesize
+= dvrbasenamelength
;
357 nodesize
+= sizeof(struct TagItem
);
361 nodesize
+= sizeof(struct TagItem
); // The last TAG_END
364 node
= AllocVec(nodesize
, MEMF_PUBLIC
|MEMF_CLEAR
);
368 tp
= node
->ahidbn_Tags
;
370 while( (tag
= NextTagItem(&tstate
)) != NULL
)
372 if(tag
->ti_Data
) switch(tag
->ti_Tag
)
375 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]);
376 CopyMem((APTR
)tag
->ti_Data
, (APTR
)tp
->ti_Data
, datalength
);
380 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
;
381 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
385 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
+ namelength
;
386 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
389 case AHIDB_DriverBaseName
:
390 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
+ namelength
+ driverlength
;
391 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
395 tp
->ti_Data
= tag
->ti_Data
;
398 tp
->ti_Tag
= tag
->ti_Tag
;
401 tp
->ti_Tag
= TAG_DONE
;
403 AddHead((struct List
*) &audiodb
->ahidb_AudioModes
, (struct Node
*) node
);
407 UnlockDatabase(audiodb
);
410 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
412 KPrintF("=>%ld\n",rc
);
419 /******************************************************************************
420 ** AHI_RemoveAudioMode ********************************************************
421 ******************************************************************************/
423 /****i* ahi.device/AHI_RemoveAudioMode **************************************
426 * AHI_RemoveAudioMode -- remove a audio mode to the database (V4)
429 * success = AHI_RemoveAudioMode( ID );
432 * ULONG AHI_RemoveAudioMode( ULONG );
435 * Removes the audio mode from the audio mode database.
438 * ID - The audio ID of the mode to be removed, or AHI_INVALID_ID.
441 * success - FALSE if the mode could not be removed.
451 ****************************************************************************
456 _AHI_RemoveAudioMode( ULONG id
,
457 struct AHIBase
* AHIBase
)
459 struct AHI_AudioMode
*node
;
460 struct AHI_AudioDatabase
*audiodb
;
463 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
465 Debug_RemoveAudioMode(id
);
471 audiodb
= LockDatabaseWrite();
475 UnlockDatabase(audiodb
);
478 audiodb
= LockDatabaseWrite();
482 if(id
!= AHI_INVALID_ID
)
484 for(node
=(struct AHI_AudioMode
*)audiodb
->ahidb_AudioModes
.mlh_Head
;
485 node
->ahidbn_MinNode
.mln_Succ
;
486 node
=(struct AHI_AudioMode
*)node
->ahidbn_MinNode
.mln_Succ
)
488 if(id
== GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, node
->ahidbn_Tags
))
490 Remove((struct Node
*) node
);
497 // Remove the entire database if it's empty
501 if(audiodb
->ahidb_AudioModes
.mlh_Head
->mln_Succ
== NULL
)
503 UnlockDatabase(audiodb
);
505 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
509 RemSemaphore((struct SignalSemaphore
*) audiodb
);
520 UnlockDatabase(audiodb
);
523 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
525 KPrintF("=>%ld\n",rc
);
532 /******************************************************************************
533 ** AHI_LoadModeFile ***********************************************************
534 ******************************************************************************/
536 /****i* ahi.device/AHI_LoadModeFile *****************************************
539 * AHI_LoadModeFile -- Add all modes in a mode file to the database (V4)
542 * success = AHI_LoadModeFile( name );
545 * ULONG AHI_LoadModeFile( STRPTR );
548 * This function takes the name of a file or a directory and either
549 * adds all modes in the file or the modes of all files in the
550 * directory to the audio mode database. Directories inside the
551 * given directory will not be recursed. The file format is IFF-AHIM.
554 * name - A pointer to the name of a file or directory.
557 * success - FALSE on error. Check dos.library/IOErr() for more
568 ****************************************************************************
573 _AHI_LoadModeFile( UBYTE
* name
,
574 struct AHIBase
* AHIBase
)
577 struct FileInfoBlock
*fib
;
580 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
582 Debug_LoadModeFile(name
);
587 fib
= AllocDosObject(DOS_FIB
, TAG_DONE
);
591 lock
= Lock(name
, ACCESS_READ
);
595 if(Examine(lock
,fib
))
597 if(fib
->fib_DirEntryType
>0) // Directory?
599 thisdir
= CurrentDir(lock
);
601 while(ExNext(lock
, fib
))
603 if(fib
->fib_DirEntryType
>0)
605 continue; // AHI_LoadModeFile(fib->fib_FileName); for recursion
610 rc
= AddModeFile(fib
->fib_FileName
);
617 // Try to load. Just continue if failing.
618 AddModeFile(fib
->fib_FileName
);
623 if(IoErr() == ERROR_NO_MORE_ENTRIES
)
632 rc
= AddModeFile(name
);
639 FreeDosObject(DOS_FIB
,fib
);
642 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
644 KPrintF("=>%ld\n",rc
);
650 /* AddModeFile **********************************************************/
653 AddModeFile ( UBYTE
*filename
)
655 struct IFFHandle
*iff
;
656 struct StoredProperty
*name
,*data
;
657 struct CollectionItem
*ci
;
658 struct TagItem
*tag
,*tstate
;
659 struct TagItem extratags
[]=
663 { AHIDB_DriverBaseName
, (IPTR
)(IS_MORPHOS
? "MOSSYS:DEVS/AHI" : "DEVS:AHI") },
673 iff
->iff_Stream
= (IPTR
)Open(filename
, MODE_OLDFILE
);
675 if(iff
->iff_Stream
!= 0)
679 if(!OpenIFF(iff
, IFFF_READ
))
682 if(!(PropChunk(iff
, ID_AHIM
, ID_AUDN
)
683 || PropChunk(iff
, ID_AHIM
, ID_AUDD
)
684 || CollectionChunk(iff
, ID_AHIM
, ID_AUDM
)
685 || StopOnExit(iff
, ID_AHIM
, ID_FORM
)))
687 if(ParseIFF(iff
, IFFPARSE_SCAN
) == IFFERR_EOC
)
689 name
= FindProp(iff
, ID_AHIM
, ID_AUDN
);
690 data
= FindProp(iff
, ID_AHIM
, ID_AUDD
);
691 ci
= FindCollection(iff
, ID_AHIM
, ID_AUDM
);
697 char driver_name
[ 128 ];
698 struct Library
* driver_base
;
702 if( name
->sp_Size
<= 0 )
704 Req( "%s:\nAUDN chunk has illegal size: %ld.",
705 (IPTR
)filename
, name
->sp_Size
);
711 // Make sure string is NUL-terminated
713 for( s
= (STRPTR
) name
->sp_Data
;
714 (APTR
) s
< name
->sp_Data
+ name
->sp_Size
;
726 Req( "%s:\nAUDN chunk is not NUL-terminated.",
731 extratags
[0].ti_Data
= (IPTR
) name
->sp_Data
;
733 // Now verify that the driver can really be opened
735 strcpy( driver_name
, IS_MORPHOS
? "MOSSYS:DEVS/AHI/" : "DEVS:AHI/" );
736 strncat( driver_name
, name
->sp_Data
, 100 );
737 strcat( driver_name
, ".audio" );
739 driver_base
= OpenLibrary( driver_name
, DriverVersion
);
740 if( driver_base
== NULL
)
748 // Make it MOSSYS:DEVS:AHI/...
750 driver_name
[7 + 4] = ':';
752 // Try "DEVS:AHI/...."
754 driver_base
= OpenLibrary( driver_name
+ 7, DriverVersion
);
755 if( driver_base
== NULL
)
761 // It is a DEVS:AHI driver!
762 extratags
[2].ti_Data
= (IPTR
) "DEVS:AHI";
764 CloseLibrary( driver_base
);
770 CloseLibrary( driver_base
);
776 if( name
->sp_Size
<= 0 )
778 Req( "%s:\nAUDD chunk has illegal size: %ld.",
779 (IPTR
)filename
, name
->sp_Size
);
784 extratags
[1].ti_Data
= (IPTR
) data
->sp_Data
;
787 while(rc
&& ci
!= NULL
)
789 // Relocate loaded taglist
791 tstate
= (struct TagItem
*) ci
->ci_Data
;
793 while( rc
&& ( tag
= NextTagItem( &tstate
) ) != NULL
)
795 EndianSwap( sizeof tag
->ti_Tag
, &tag
->ti_Tag
);
796 EndianSwap( sizeof tag
->ti_Data
, &tag
->ti_Data
);
798 if(tag
->ti_Tag
& (AHI_TagBaseR
^ AHI_TagBase
))
800 tag
->ti_Data
+= (IPTR
)ci
->ci_Data
;
805 switch( tag
->ti_Tag
)
809 // Make sure the string is within the chunk and NUL-term.
811 if( tag
->ti_Data
< (IPTR
) ci
->ci_Data
||
812 tag
->ti_Data
>= (IPTR
) ci
->ci_Data
+ ci
->ci_Size
)
814 Req( "%s:\nAUDM chunk contains an invalid string.",
821 // Make sure string is NUL-terminated
823 for( s
= (STRPTR
) tag
->ti_Data
;
824 (APTR
) s
< ci
->ci_Data
+ ci
->ci_Size
;
845 Req( "%s:\nAUDM chunk contains a string that is not "
846 "NUL-terminated.", (IPTR
)filename
);
854 extratags
[3].ti_Data
= (IPTR
) ci
->ci_Data
;
856 rc
= AHI_AddAudioMode(extratags
);
865 Close((BPTR
) iff
->iff_Stream
);