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,
24 #include <exec/memory.h>
25 #include <exec/semaphores.h>
26 #include <dos/dostags.h>
28 #include <libraries/iffparse.h>
30 #include <clib/alib_protos.h>
31 #include <proto/exec.h>
32 #include <proto/dos.h>
33 #include <proto/iffparse.h>
34 #include <proto/utility.h>
36 #define __NOGLOBALIFACE__
37 #include <proto/ahi.h>
39 #undef __NOGLOBALIFACE__
40 #include <proto/ahi_sub.h>
50 static ULONG
AddModeFile ( UBYTE
*filename
);
58 #if !defined( WORDS_BIGENDIAN )
64 struct TagItem32
*AHINextTagItem(struct TagItem32
**tagListPtr
)
66 ULONG tagTag
, tagData
;
73 tagTag
= (*tagListPtr
)->ti_Tag
;
74 EndianSwap( sizeof tagTag
, &tagTag
);
79 (*tagListPtr
)->ti_Tag
= tagTag
;
80 tagData
= (*tagListPtr
)->ti_Data
;
81 EndianSwap( sizeof tagData
, &tagData
);
82 (*tagListPtr
)->ti_Data
= tagData
;
83 *(IPTR
*)tagListPtr
+= tagData
;
87 (*tagListPtr
)->ti_Tag
= tagTag
;
91 (*tagListPtr
)->ti_Tag
= tagTag
;
96 (*tagListPtr
)->ti_Tag
= tagTag
;
97 tagData
= (*tagListPtr
)->ti_Data
;
98 EndianSwap( sizeof tagData
, &tagData
);
99 (*tagListPtr
)->ti_Data
= tagData
;
100 (*tagListPtr
) += tagData
+ 1;
104 (*tagListPtr
)->ti_Tag
= tagTag
;
105 tagData
= (*tagListPtr
)->ti_Data
;
106 EndianSwap( sizeof tagData
, &tagData
);
107 (*tagListPtr
)->ti_Data
= tagData
;
108 return (*tagListPtr
)++;
116 #define TagItem32 TagItem
117 #define AHINextTagItem NextTagItem
120 /******************************************************************************
121 ** Audio Database *************************************************************
122 ******************************************************************************/
126 struct MinNode ahidbn_MinNode
;
127 struct TagItem ahidbn_Tags
[0];
129 /* Taglist, mode name and driver name follows.
130 Size variable. Use FreeVec() to free node. */
134 ** Lock the database for read access. Return NULL if database not present.
137 struct AHI_AudioDatabase
*
140 struct AHI_AudioDatabase
*audiodb
;
144 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
148 ObtainSemaphoreShared((struct SignalSemaphore
*) audiodb
);
157 ** Lock the database for write access. Create it if not present.
160 struct AHI_AudioDatabase
*
161 LockDatabaseWrite(void)
163 struct AHI_AudioDatabase
*audiodb
;
167 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
171 ObtainSemaphore((struct SignalSemaphore
*) audiodb
);
175 audiodb
= (struct AHI_AudioDatabase
*)
176 AllocVec(sizeof(struct AHI_AudioDatabase
), MEMF_PUBLIC
|MEMF_CLEAR
);
181 NewList( (struct List
*) &audiodb
->ahidb_AudioModes
);
183 audiodb
->ahidb_Semaphore
.ss_Link
.ln_Name
= audiodb
->ahidb_Name
;
184 audiodb
->ahidb_Semaphore
.ss_Link
.ln_Pri
= 20;
185 strcpy(audiodb
->ahidb_Semaphore
.ss_Link
.ln_Name
, ADB_NAME
);
187 AddSemaphore((struct SignalSemaphore
*) audiodb
);
188 ObtainSemaphore((struct SignalSemaphore
*) audiodb
);
197 UnlockDatabase ( struct AHI_AudioDatabase
*audiodb
)
201 ReleaseSemaphore((struct SignalSemaphore
*) audiodb
);
206 GetDBTagList ( struct AHI_AudioDatabase
*audiodb
,
209 struct AHI_AudioMode
*node
;
210 struct TagItem
*rc
= NULL
;
212 if((audiodb
!= NULL
) && (id
!= AHI_INVALID_ID
))
214 for(node
=(struct AHI_AudioMode
*)audiodb
->ahidb_AudioModes
.mlh_Head
;
215 node
->ahidbn_MinNode
.mln_Succ
;
216 node
=(struct AHI_AudioMode
*)node
->ahidbn_MinNode
.mln_Succ
)
218 if(id
== GetTagData(AHIDB_AudioID
,AHI_INVALID_ID
,node
->ahidbn_Tags
))
220 rc
= node
->ahidbn_Tags
;
230 /******************************************************************************
231 ** AHI_NextAudioID ************************************************************
232 ******************************************************************************/
234 /****** ahi.device/AHI_NextAudioID ******************************************
237 * AHI_NextAudioID -- iterate current audio mode identifiers
240 * next_ID = AHI_NextAudioID( last_ID );
243 * IPTR AHI_NextAudioID( IPTR );
246 * This function is used to iterate through all current AudioIDs in
247 * the audio database.
250 * last_ID - previous AudioID or AHI_INVALID_ID if beginning iteration.
253 * next_ID - subsequent AudioID or AHI_INVALID_ID if no more IDs.
262 * AHI_GetAudioAttrsA(), AHI_BestAudioIDA()
264 ****************************************************************************
269 _AHI_NextAudioID( IPTR id
,
270 struct AHIBase
* AHIBase
)
272 struct AHI_AudioDatabase
*audiodb
;
273 struct AHI_AudioMode
*node
;
274 IPTR nextid
=AHI_INVALID_ID
;
276 ahibug("[AHI:Device] %s(%08x)\n", __func__
, id
);
278 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
280 Debug_NextAudioID(id
);
283 audiodb
= LockDatabase();
287 node
= (struct AHI_AudioMode
*) audiodb
->ahidb_AudioModes
.mlh_Head
;
289 if(id
!= AHI_INVALID_ID
)
295 ahibug("[AHI:Device] %s: node @ 0x%p)\n", __func__
, node
);
297 thisid
= GetTagData(AHIDB_AudioID
,AHI_INVALID_ID
,node
->ahidbn_Tags
);
298 node
= (struct AHI_AudioMode
*) node
->ahidbn_MinNode
.mln_Succ
;
307 while(node
&& node
->ahidbn_MinNode
.mln_Succ
)
309 if( GetTagData( AHIDB_MultTable
, FALSE
, node
->ahidbn_Tags
) )
311 // Pretend the "Fast" modes are not here
312 node
= (struct AHI_AudioMode
*) node
->ahidbn_MinNode
.mln_Succ
;
316 nextid
= GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, node
->ahidbn_Tags
);
321 UnlockDatabase(audiodb
);
324 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
326 KPrintF("=>0x%08lx\n",nextid
);
333 /******************************************************************************
334 ** AHI_AddAudioMode ***********************************************************
335 ******************************************************************************/
337 /****i* ahi.device/AHI_AddAudioMode *****************************************
340 * AHI_AddAudioMode -- add an audio mode to the database (V4)
343 * success = AHI_AddAudioMode( DBtags );
346 * ULONG AHI_AddAudioMode( struct TagItem *, UBYTE *, UBYTE *);
349 * Adds the audio mode described by a taglist to the audio mode
350 * database. If the database does not exists, it will be created.
353 * DBtags - Tags describing the properties of this mode.
356 * success - FALSE if the mode could not be added.
366 ****************************************************************************
371 _AHI_AddAudioMode( struct TagItem
* DBtags
,
372 struct AHIBase
* AHIBase
)
374 struct AHI_AudioDatabase
*audiodb
;
375 struct AHI_AudioMode
*node
;
376 ULONG nodesize
= sizeof(struct AHI_AudioMode
), tagitems
= 0;
377 ULONG datalength
= 0, namelength
= 0, driverlength
= 0, dvrbasenamelength
= 0;
378 struct TagItem
*tstate
= DBtags
, *tp
, *tag
;
381 ahibug("[AHI:Device] %s()\n", __func__
);
383 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
385 Debug_AddAudioMode(DBtags
);
388 // Remove old mode if present in database
389 AHI_RemoveAudioMode( GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, DBtags
));
391 // Now add the new mode
393 audiodb
= LockDatabaseWrite();
400 while( (tag
= NextTagItem(&tstate
)) != NULL
)
403 if(tag
->ti_Data
) switch(tag
->ti_Tag
)
406 datalength
= ((ULONG
*)tag
->ti_Data
)[0];
407 nodesize
+= datalength
;
411 namelength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
412 nodesize
+= namelength
;
416 driverlength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
417 nodesize
+= driverlength
;
420 case AHIDB_DriverBaseName
:
421 dvrbasenamelength
= strlen((UBYTE
*)tag
->ti_Data
)+1;
422 nodesize
+= dvrbasenamelength
;
426 nodesize
+= sizeof(struct TagItem
);
430 nodesize
+= sizeof(struct TagItem
); // The last TAG_END
433 node
= AllocVec(nodesize
, MEMF_PUBLIC
|MEMF_CLEAR
);
437 tp
= node
->ahidbn_Tags
;
439 while( (tag
= NextTagItem(&tstate
)) != NULL
)
441 if(tag
->ti_Data
) switch(tag
->ti_Tag
)
444 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]);
445 CopyMem((APTR
)tag
->ti_Data
, (APTR
)tp
->ti_Data
, datalength
);
449 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
;
450 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
454 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
+ namelength
;
455 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
458 case AHIDB_DriverBaseName
:
459 tp
->ti_Data
= ((IPTR
) &node
->ahidbn_Tags
[tagitems
]) + datalength
+ namelength
+ driverlength
;
460 strcpy((UBYTE
*)tp
->ti_Data
, (UBYTE
*)tag
->ti_Data
);
464 tp
->ti_Data
= tag
->ti_Data
;
467 tp
->ti_Tag
= tag
->ti_Tag
;
470 tp
->ti_Tag
= TAG_DONE
;
472 AddHead((struct List
*) &audiodb
->ahidb_AudioModes
, (struct Node
*) node
);
476 UnlockDatabase(audiodb
);
479 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
481 KPrintF("=>%ld\n",rc
);
488 /******************************************************************************
489 ** AHI_RemoveAudioMode ********************************************************
490 ******************************************************************************/
492 /****i* ahi.device/AHI_RemoveAudioMode **************************************
495 * AHI_RemoveAudioMode -- remove a audio mode to the database (V4)
498 * success = AHI_RemoveAudioMode( ID );
501 * ULONG AHI_RemoveAudioMode( IPTR );
504 * Removes the audio mode from the audio mode database.
507 * ID - The audio ID of the mode to be removed, or AHI_INVALID_ID.
510 * success - FALSE if the mode could not be removed.
520 ****************************************************************************
525 _AHI_RemoveAudioMode( IPTR id
,
526 struct AHIBase
* AHIBase
)
528 struct AHI_AudioMode
*node
;
529 struct AHI_AudioDatabase
*audiodb
;
532 ahibug("[AHI:Device] %s(%08x)\n", __func__
, id
);
534 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
536 Debug_RemoveAudioMode(id
);
542 audiodb
= LockDatabaseWrite();
546 UnlockDatabase(audiodb
);
549 audiodb
= LockDatabaseWrite();
553 if(id
!= AHI_INVALID_ID
)
555 for(node
=(struct AHI_AudioMode
*)audiodb
->ahidb_AudioModes
.mlh_Head
;
556 node
->ahidbn_MinNode
.mln_Succ
;
557 node
=(struct AHI_AudioMode
*)node
->ahidbn_MinNode
.mln_Succ
)
559 if(id
== GetTagData(AHIDB_AudioID
, AHI_INVALID_ID
, node
->ahidbn_Tags
))
561 Remove((struct Node
*) node
);
568 // Remove the entire database if it's empty
572 if(audiodb
->ahidb_AudioModes
.mlh_Head
->mln_Succ
== NULL
)
574 UnlockDatabase(audiodb
);
576 audiodb
= (struct AHI_AudioDatabase
*) FindSemaphore(ADB_NAME
);
580 RemSemaphore((struct SignalSemaphore
*) audiodb
);
591 UnlockDatabase(audiodb
);
594 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
596 KPrintF("=>%ld\n",rc
);
603 /******************************************************************************
604 ** AHI_LoadModeFile ***********************************************************
605 ******************************************************************************/
607 /****i* ahi.device/AHI_LoadModeFile *****************************************
610 * AHI_LoadModeFile -- Add all modes in a mode file to the database (V4)
613 * success = AHI_LoadModeFile( name );
616 * ULONG AHI_LoadModeFile( STRPTR );
619 * This function takes the name of a file or a directory and either
620 * adds all modes in the file or the modes of all files in the
621 * directory to the audio mode database. Directories inside the
622 * given directory will not be recursed. The file format is IFF-AHIM.
625 * name - A pointer to the name of a file or directory.
628 * success - FALSE on error. Check dos.library/IOErr() for more
639 ****************************************************************************
644 _AHI_LoadModeFile( UBYTE
* name
,
645 struct AHIBase
* AHIBase
)
648 struct FileInfoBlock
*fib
;
651 ahibug("[AHI:Device] %s('%s')\n", __func__
, name
);
653 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
655 Debug_LoadModeFile(name
);
660 fib
= AllocDosObject(DOS_FIB
, TAG_DONE
);
664 lock
= Lock(name
, ACCESS_READ
);
668 if(Examine(lock
,fib
))
670 if(fib
->fib_DirEntryType
>0) // Directory?
672 thisdir
= CurrentDir(lock
);
674 while(ExNext(lock
, fib
))
676 if(fib
->fib_DirEntryType
>0)
678 continue; // AHI_LoadModeFile(fib->fib_FileName); for recursion
683 rc
= AddModeFile(fib
->fib_FileName
);
690 // Try to load. Just continue if failing.
691 AddModeFile(fib
->fib_FileName
);
696 if(IoErr() == ERROR_NO_MORE_ENTRIES
)
705 rc
= AddModeFile(name
);
712 FreeDosObject(DOS_FIB
,fib
);
715 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
717 KPrintF("=>%ld\n",rc
);
723 /* AddModeFile **********************************************************/
726 AddModeFile ( UBYTE
*filename
)
728 struct IFFHandle
*iff
;
729 struct StoredProperty
*name
,*data
;
730 struct CollectionItem
*ci
;
731 struct TagItem32
*tag
,*tstate
;
732 #if defined(__AROS__) && (__WORDSIZE==64)
733 struct TagItem
*dstTag
;
737 struct TagItem extratags
[]=
741 { AHIDB_DriverBaseName
, (IPTR
)(IS_MORPHOS
? "MOSSYS:DEVS/AHI" : "DEVS:AHI") },
746 ahibug("[AHI:Device] %s('%s')\n", __func__
, filename
);
753 iff
->iff_Stream
= (IPTR
)Open(filename
, MODE_OLDFILE
);
755 if(iff
->iff_Stream
!= 0)
759 if(!OpenIFF(iff
, IFFF_READ
))
762 if(!(PropChunk(iff
, ID_AHIM
, ID_AUDN
)
763 || PropChunk(iff
, ID_AHIM
, ID_AUDD
)
764 || CollectionChunk(iff
, ID_AHIM
, ID_AUDM
)
765 || StopOnExit(iff
, ID_AHIM
, ID_FORM
)))
767 if(ParseIFF(iff
, IFFPARSE_SCAN
) == IFFERR_EOC
)
769 name
= FindProp(iff
, ID_AHIM
, ID_AUDN
);
770 data
= FindProp(iff
, ID_AHIM
, ID_AUDD
);
771 ci
= FindCollection(iff
, ID_AHIM
, ID_AUDM
);
775 ahibug("[AHI:Device] %s: ci @ 0x%p, name @ 0x%p, data @ 0x%p\n", __func__
, ci
, name
, data
);
779 char driver_name
[ 128 ];
780 struct Library
* driver_base
;
784 if( name
->sp_Size
<= 0 )
786 Req( "%s:\nAUDN chunk has illegal size: %ld.",
787 (IPTR
)filename
, name
->sp_Size
);
793 // Make sure string is NUL-terminated
795 for( s
= (STRPTR
) name
->sp_Data
;
796 (APTR
) s
< name
->sp_Data
+ name
->sp_Size
;
808 Req( "%s:\nAUDN chunk is not NUL-terminated.",
813 extratags
[0].ti_Data
= (IPTR
) name
->sp_Data
;
815 // Now verify that the driver can really be opened
817 strcpy( driver_name
, IS_MORPHOS
? "MOSSYS:DEVS/AHI/" : "DEVS:AHI/" );
818 strncat( driver_name
, name
->sp_Data
, 100 );
819 strcat( driver_name
, ".audio" );
821 driver_base
= OpenLibrary( driver_name
, DriverVersion
);
822 if( driver_base
== NULL
)
830 // Make it MOSSYS:DEVS:AHI/...
832 driver_name
[7 + 4] = ':';
834 // Try "DEVS:AHI/...."
836 driver_base
= OpenLibrary( driver_name
+ 7, DriverVersion
);
837 if( driver_base
== NULL
)
843 // It is a DEVS:AHI driver!
844 extratags
[2].ti_Data
= (IPTR
) "DEVS:AHI";
846 CloseLibrary( driver_base
);
852 CloseLibrary( driver_base
);
858 if( data
->sp_Size
<= 0 )
860 Req( "%s:\nAUDD chunk has illegal size: %ld.",
861 (IPTR
)filename
, data
->sp_Size
);
866 extratags
[1].ti_Data
= (IPTR
) data
->sp_Data
;
869 while(rc
&& ci
!= NULL
)
873 // Relocate loaded taglist
874 ahibug("[AHI:Device] %s: relocating taglist @ 0x%p, %d bytes\n", __func__
, ci
->ci_Data
, ci
->ci_Size
);
876 tstate
= (struct TagItem32
*) ci
->ci_Data
;
878 #if defined(__AROS__) && (__WORDSIZE==64)
879 driverTags
= AllocVec(sizeof(struct TagItem
) * 128, MEMF_CLEAR
);
882 driverTags
= ci
->ci_Data
;
884 while( rc
&& ( tag
= AHINextTagItem( &tstate
) ) != NULL
)
886 #if defined(__AROS__) && (__WORDSIZE==64)
887 dstTag
->ti_Tag
= (IPTR
)tag
->ti_Tag
;
888 dstTag
->ti_Data
= (IPTR
)tag
->ti_Data
;
890 ahibug("[AHI:Device] %s: %08x = %p\n", __func__
, dstTag
->ti_Tag
, dstTag
->ti_Data
);
892 if(tag
->ti_Tag
& (AHI_TagBaseR
^ AHI_TagBase
))
894 dstTag
->ti_Data
+= (IPTR
)ci
->ci_Data
;
899 switch( tag
->ti_Tag
)
903 // Make sure the string is within the chunk and NUL-term.
905 if( dstTag
->ti_Data
< (IPTR
) ci
->ci_Data
||
906 dstTag
->ti_Data
>= (IPTR
) ci
->ci_Data
+ ci
->ci_Size
)
908 Req( "%s:\nAUDM chunk contains an invalid string.",
915 // Make sure string is NUL-terminated
917 for( s
= (STRPTR
) dstTag
->ti_Data
;
918 (APTR
) s
< ci
->ci_Data
+ ci
->ci_Size
;
939 Req( "%s:\nAUDM chunk contains a string that is not "
940 "NUL-terminated.", (IPTR
)filename
);
942 #if defined(__AROS__) && (__WORDSIZE==64)
951 extratags
[3].ti_Data
= (IPTR
)driverTags
;
953 rc
= AHI_AddAudioMode(extratags
);
957 #if defined(__AROS__) && (__WORDSIZE==64)
965 Close((BPTR
) iff
->iff_Stream
);