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,
25 #include <exec/errors.h>
26 #include <exec/tasks.h>
28 #include <exec/devices.h>
29 #include <exec/memory.h>
31 #include <clib/alib_protos.h>
32 #include <proto/exec.h>
33 #include <proto/dos.h>
35 #define __NOGLOBALIFACE__
36 #include <proto/ahi.h>
38 #undef __NOGLOBALIFACE__
39 #include <proto/ahi_sub.h>
46 #include "devcommands.h"
52 #define IAHIsub ((struct AHIPrivAudioCtrl *) iounit->AudioCtrl)->ahiac_IAHIsub
55 static void TermIO(struct AHIRequest
*, struct AHIBase
*);
56 static void Devicequery(struct AHIRequest
*, struct AHIBase
*);
57 static void ResetCmd(struct AHIRequest
*, struct AHIBase
*);
58 static void ReadCmd(struct AHIRequest
*, struct AHIBase
*);
59 static void WriteCmd(struct AHIRequest
*, struct AHIBase
*);
60 static void StopCmd(struct AHIRequest
*, struct AHIBase
*);
61 static void StartCmd(struct AHIRequest
*, struct AHIBase
*);
62 static void FlushCmd(struct AHIRequest
*, struct AHIBase
*);
64 static void FillReadBuffer(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
66 static void NewWriter(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
67 static void AddWriter(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
68 static void PlayRequest(int, struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
69 static void RemPlayers( struct List
*, struct AHIDevUnit
*, struct AHIBase
*);
71 static void UpdateMasterVolume( struct AHIDevUnit
*, struct AHIBase
* );
73 #define MultFixed( a, b ) ( (unsigned long) ( ( ( (unsigned long long) a ) << 16 ) / b ) )
75 /******************************************************************************
76 ** DevBeginIO *****************************************************************
77 ******************************************************************************/
79 // This function is called by the system each time exec.library/DoIO()
83 _DevBeginIO( struct AHIRequest
* ioreq
,
84 struct AHIBase
* AHIBase
)
86 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
88 KPrintF("BeginIO(0x%P)\n", (IPTR
)ioreq
);
91 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
93 switch(ioreq
->ahir_Std
.io_Command
)
97 case NSCMD_DEVICEQUERY
:
100 PerformIO(ioreq
,AHIBase
);
108 ioreq
->ahir_Std
.io_Flags
&= ~IOF_QUICK
;
109 PutMsg(&ioreq
->ahir_Std
.io_Unit
->unit_MsgPort
,&ioreq
->ahir_Std
.io_Message
);
114 ioreq
->ahir_Std
.io_Error
= IOERR_NOCMD
;
115 TermIO(ioreq
,AHIBase
);
121 /******************************************************************************
122 ** AbortIO ********************************************************************
123 ******************************************************************************/
125 // This function is called by the system each time exec.library/AbortIO()
129 _DevAbortIO( struct AHIRequest
* ioreq
,
130 struct AHIBase
* AHIBase
)
133 struct AHIDevUnit
*iounit
;
135 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
137 KPrintF("AbortIO(0x%P)", (IPTR
)ioreq
);
140 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
142 AHIObtainSemaphore(&iounit
->Lock
);
144 if(ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Type
!= NT_REPLYMSG
)
146 switch(ioreq
->ahir_Std
.io_Command
)
150 if(FindNode((struct List
*) &iounit
->ReadList
, (struct Node
*) ioreq
))
152 Remove((struct Node
*) ioreq
);
153 ioreq
->ahir_Std
.io_Error
= IOERR_ABORTED
;
154 TermIO(ioreq
,AHIBase
);
160 if(FindNode((struct List
*) &iounit
->PlayingList
, (struct Node
*) ioreq
)
161 || FindNode((struct List
*) &iounit
->SilentList
, (struct Node
*) ioreq
)
162 || FindNode((struct List
*) &iounit
->WaitingList
, (struct Node
*) ioreq
))
164 struct AHIRequest
*nextreq
;
165 struct AHIRequest
*io
;
169 Remove((struct Node
*) ioreq
);
171 // Now check if any other request ahir_Link to us. If so,
172 // we need to clear that field, since this request is no
176 for (io
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
177 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
178 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
179 if (io
->ahir_Link
== ioreq
) {
180 io
->ahir_Link
= NULL
;
185 for (io
= (struct AHIRequest
*) iounit
->SilentList
.mlh_Head
;
186 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
187 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
188 if (io
->ahir_Link
== ioreq
) {
189 io
->ahir_Link
= NULL
;
194 for (io
= (struct AHIRequest
*) iounit
->WaitingList
.mlh_Head
;
195 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
196 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
197 if (io
->ahir_Link
== ioreq
) {
198 io
->ahir_Link
= NULL
;
204 if(ioreq
->ahir_Extras
&& (GetExtras(ioreq
)->Channel
!= NOCHANNEL
))
206 struct Library
*AHIsubBase
= NULL
;
208 if( iounit
->AudioCtrl
!= NULL
)
210 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)
214 if( AHIsubBase
!= NULL
)
216 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
219 iounit
->Voices
[GetExtras(ioreq
)->Channel
].PlayingRequest
= NULL
;
220 iounit
->Voices
[GetExtras(ioreq
)->Channel
].QueuedRequest
= NULL
;
221 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextRequest
= NULL
;
223 if(iounit
->AudioCtrl
)
225 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextOffset
= MUTE
;
226 AHI_SetSound(GetExtras(ioreq
)->Channel
,AHI_NOSOUND
,0,0,
227 iounit
->AudioCtrl
,AHISF_IMM
);
231 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextOffset
= FREE
;
234 if( AHIsubBase
!= NULL
)
236 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
240 ioreq
->ahir_Std
.io_Command
= CMD_WRITE
;
241 ioreq
->ahir_Std
.io_Error
= IOERR_ABORTED
;
242 nextreq
= ioreq
->ahir_Link
;
243 TermIO(ioreq
,AHIBase
);
255 AHIReleaseSemaphore(&iounit
->Lock
);
257 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
259 KPrintF("=>%ld\n",rc
);
266 /******************************************************************************
267 ** TermIO *********************************************************************
268 ******************************************************************************/
270 // This functions returns an IO request back to the sender.
273 TermIO ( struct AHIRequest
*ioreq
,
274 struct AHIBase
*AHIBase
)
276 struct AHIDevUnit
*iounit
;
277 ULONG error
= ioreq
->ahir_Std
.io_Error
;
279 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
281 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
283 KPrintF("Terminating IO Request 0x%p", (IPTR
)ioreq
);
286 if( ioreq
->ahir_Std
.io_Command
== CMD_WRITE
)
288 // Update master volume if we're terminating a write request
289 UpdateMasterVolume( iounit
, AHIBase
);
291 // Convert io_Actual to bytes
292 ioreq
->ahir_Std
.io_Actual
*= AHI_SampleFrameSize(ioreq
->ahir_Type
);
295 if(ioreq
->ahir_Extras
!= 0)
297 int sound
= GetExtras(ioreq
)->Sound
;
298 APTR extras
= (APTR
) ioreq
->ahir_Extras
;
300 if((sound
!= AHI_NOSOUND
) && (sound
< MAXSOUNDS
))
302 AHI_UnloadSound(sound
, iounit
->AudioCtrl
);
303 iounit
->Sounds
[sound
] = SOUND_FREE
;
306 ioreq
->ahir_Extras
= 0;
310 if( ! (ioreq
->ahir_Std
.io_Flags
& IOF_QUICK
))
312 ReplyMsg(&ioreq
->ahir_Std
.io_Message
);
315 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
317 KPrintF("=>%ld\n", error
);
322 /******************************************************************************
323 ** PerformIO ******************************************************************
324 ******************************************************************************/
327 PerformIO ( struct AHIRequest
*ioreq
,
328 struct AHIBase
*AHIBase
)
330 struct AHIDevUnit
*iounit
;
332 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
333 ioreq
->ahir_Std
.io_Error
= 0;
335 // Just to make sure TermIO won't free a bad address
336 ioreq
->ahir_Extras
= 0;
338 switch(ioreq
->ahir_Std
.io_Command
)
340 case NSCMD_DEVICEQUERY
:
341 Devicequery(ioreq
, AHIBase
);
345 ResetCmd(ioreq
, AHIBase
);
349 ReadCmd(ioreq
, AHIBase
);
353 AHIObtainSemaphore(&iounit
->Lock
);
357 AddTail((struct List
*) &iounit
->RequestQueue
,(struct Node
*) ioreq
);
361 WriteCmd(ioreq
, AHIBase
);
364 AHIReleaseSemaphore(&iounit
->Lock
);
368 StopCmd(ioreq
, AHIBase
);
372 StartCmd(ioreq
, AHIBase
);
376 FlushCmd(ioreq
, AHIBase
);
380 ioreq
->ahir_Std
.io_Error
= IOERR_NOCMD
;
381 TermIO(ioreq
, AHIBase
);
387 /******************************************************************************
388 ** Devicequery ****************************************************************
389 ******************************************************************************/
391 /****** ahi.device/NSCMD_DEVICEQUERY ***************************************
394 * NSCMD_DEVICEQUERY -- Query the device for its capabilities (V4)
397 * Fills an initialized NSDeviceQueryResult structure with
398 * information about the device.
401 * io_Device Preset by the call to OpenDevice().
402 * io_Unit Preset by the call to OpenDevice().
403 * io_Command NSCMD_DEVICEQUERY
404 * io_Data Pointer to the NSDeviceQueryResult structure,
405 * initialized as follows:
406 * DevQueryFormat - Set to 0
407 * SizeAvailable - Must be cleared.
408 * It is probably good manners to clear all other
410 * io_Length Size of the NSDeviceQueryResult structure.
413 * io_Error 0 for success, or an error code as defined in
414 * <ahi/devices.h> and <exec/errors.h>.
415 * io_Actual If io_Error is 0, the value in
416 * NSDeviceQueryResult.SizeAvailable.
418 * The NSDeviceQueryResult structure now contains valid information.
420 * The other fields, except io_Device, io_Unit and io_Command, are
430 * <ahi/devices.h>, <exec/errors.h>
432 ****************************************************************************
450 Devicequery ( struct AHIRequest
*ioreq
,
451 struct AHIBase
*AHIBase
)
453 struct NSDeviceQueryResult
*dqr
;
455 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
457 KPrintF("NSCMD_DEVICEQUERY\n");
460 dqr
= ioreq
->ahir_Std
.io_Data
;
461 if(ioreq
->ahir_Std
.io_Length
>= 16)
463 dqr
->SizeAvailable
= 16;
464 dqr
->DeviceType
= NSDEVTYPE_UNKNOWN
;
465 dqr
->DeviceSubType
= 0;
466 dqr
->SupportedCommands
= commandlist
;
469 ioreq
->ahir_Std
.io_Actual
= dqr
->SizeAvailable
;
470 TermIO(ioreq
, AHIBase
);
474 /******************************************************************************
475 ** StopCmd ********************************************************************
476 ******************************************************************************/
478 /****** ahi.device/CMD_STOP ************************************************
481 * CMD_STOP -- stop device processing (like ^S) (V4)
484 * Stops all CMD_WRITE processing. All writes will be queued, and
485 * are not processed until CMD_START. This is useful for synchronizing
486 * two or more CMD_WRITE's.
489 * io_Device Preset by the call to OpenDevice().
490 * io_Unit Preset by the call to OpenDevice().
491 * io_Command CMD_STOP
494 * io_Error 0 for success, or an error code as defined in
495 * <ahi/devices.h> and <exec/errors.h>.
497 * The other fields, except io_Device, io_Unit and io_Command, are
503 * This command affects ALL writes, even those sent by other
504 * applications. Make sure the code between CMD_STOP and CMD_START
505 * runs as fast as possible!
507 * Unlike most (all?) other devices, CMD_STOP and CMD_START do nest in
513 * CMD_START, <ahi/devices.h>, <exec/errors.h>
515 ****************************************************************************
520 StopCmd ( struct AHIRequest
*ioreq
,
521 struct AHIBase
*AHIBase
)
523 struct AHIDevUnit
*iounit
;
525 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
527 KPrintF("CMD_STOP\n");
530 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
532 AHIObtainSemaphore(&iounit
->Lock
);
536 AHIReleaseSemaphore(&iounit
->Lock
);
538 TermIO(ioreq
,AHIBase
);
542 /******************************************************************************
543 ** FlushCmd *******************************************************************
544 ******************************************************************************/
546 /****** ahi.device/CMD_FLUSH ************************************************
549 * CMD_FLUSH -- Cancel all I/O requests (V4)
552 * Aborts ALL current requests, both active and waiting, even
553 * other programs requests!
556 * io_Device Preset by the call to OpenDevice().
557 * io_Unit Preset by the call to OpenDevice().
558 * io_Command CMD_FLUSH
561 * io_Error 0 for success, or an error code as defined in
562 * <ahi/devices.h> and <exec/errors.h>.
563 * io_Actual If io_Error is 0, number of requests actually
566 * The other fields, except io_Device, io_Unit and io_Command, are
572 * This command should only be used in very rare cases, like AHI
573 * system utilities. Never use this command in an application.
578 * CMD_RESET, <ahi/devices.h>, <exec/errors.h>
580 ****************************************************************************
585 FlushCmd ( struct AHIRequest
*ioreq
,
586 struct AHIBase
*AHIBase
)
588 struct AHIDevUnit
*iounit
;
589 struct AHIRequest
*ior
;
591 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
593 KPrintF("CMD_FLUSH\n");
596 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
598 ioreq
->ahir_Std
.io_Actual
= 0;
600 // Abort all current IO-requests
601 while((ior
= (struct AHIRequest
*) iounit
->ReadList
.mlh_Head
))
603 _DevAbortIO(ior
, AHIBase
);
604 ioreq
->ahir_Std
.io_Actual
++;
606 while((ior
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
))
608 _DevAbortIO(ior
, AHIBase
);
609 ioreq
->ahir_Std
.io_Actual
++;
611 while((ior
= (struct AHIRequest
*) iounit
->SilentList
.mlh_Head
))
613 _DevAbortIO(ior
, AHIBase
);
614 ioreq
->ahir_Std
.io_Actual
++;
616 while((ior
= (struct AHIRequest
*) iounit
->WaitingList
.mlh_Head
))
618 _DevAbortIO(ior
, AHIBase
);
619 ioreq
->ahir_Std
.io_Actual
++;
621 TermIO(ioreq
,AHIBase
);
626 /* All the following functions are called within the unit process context */
629 /******************************************************************************
630 ** ResetCmd *******************************************************************
631 ******************************************************************************/
633 /****** ahi.device/CMD_RESET ************************************************
636 * CMD_RESET -- Restore device to a known state (V4)
639 * Aborts all current requests, even other programs requests
640 * (CMD_FLUSH), rereads the configuration file and resets the hardware
641 * to its initial state
645 * io_Device Preset by the call to OpenDevice().
646 * io_Unit Preset by the call to OpenDevice().
647 * io_Command CMD_RESET
650 * io_Error 0 for success, or an error code as defined in
651 * <ahi/devices.h> and <exec/errors.h>.
653 * The other fields, except io_Device, io_Unit and io_Command, are
659 * This command should only be used in very rare cases, like AHI
660 * system utilities. Never use this command in an application.
665 * CMD_FLUSH, <ahi/devices.h>, <exec/errors.h>
667 ****************************************************************************
672 ResetCmd ( struct AHIRequest
*ioreq
,
673 struct AHIBase
*AHIBase
)
675 struct AHIDevUnit
*iounit
;
677 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
679 KPrintF("CMD_RESET\n");
682 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
684 // Remove all requests (beware, invalid IORequest to FlushCmd!)
685 FlushCmd(ioreq
, AHIBase
);
687 // Reset the hardware
688 ReadConfig(iounit
, AHIBase
);
689 FreeHardware(iounit
, AHIBase
);
690 AllocHardware(iounit
, AHIBase
);
691 TermIO(ioreq
,AHIBase
);
695 /******************************************************************************
696 ** ReadCmd ********************************************************************
697 ******************************************************************************/
699 /****** ahi.device/CMD_READ *************************************************
702 * CMD_READ -- Read raw samples from audio input (V4)
705 * Reads samples from the users prefered input to memory. The sample
706 * format and frequency will be converted on the fly.
709 * io_Device Preset by the call to OpenDevice().
710 * io_Unit Preset by the call to OpenDevice().
711 * io_Command CMD_READ
712 * io_Data Pointer to the buffer where the data should be put.
713 * io_Length Number of bytes to read, must be a multiple of the
714 * sample frame size (see ahir_Type).
715 * io_Offset Set to 0 when you use for the first time or after
717 * ahir_Type The desired sample format, see <ahi/devices.h>.
718 * ahir_Frequency The desired sample frequency in Hertz.
721 * io_Error 0 for success, or an error code as defined in
722 * <ahi/devices.h> and <exec/errors.h>.
723 * io_Actual If io_Error is 0, number of bytes actually
724 * transferred. Starting with V6, io_Actual is also
725 * valid if io_Error is not 0 (like if the request
727 * io_Offset Updated to be used as input next time.
729 * The other fields, except io_Device, io_Unit and io_Command, are
735 * It's only possible to read signed mono or stereo samples.
740 * <ahi/devices.h>, <exec/errors.h>
742 ****************************************************************************
747 ReadCmd ( struct AHIRequest
*ioreq
,
748 struct AHIBase
*AHIBase
)
750 struct AHIDevUnit
*iounit
;
751 ULONG error
=AHIE_OK
,mixfreq
= 0;
753 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
755 KPrintF("CMD_READ\n");
758 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
760 ioreq
->ahir_Std
.io_Actual
= 0;
762 /* Start recording if neccessary */
763 if( ! iounit
->IsRecording
)
765 if( (! iounit
->FullDuplex
) && iounit
->IsPlaying
)
767 error
= AHIE_HALFDUPLEX
; // FIXIT!
770 { static const Tag tags
[] = { AHIC_Record
,TRUE
,TAG_DONE
};
771 error
= AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
776 iounit
->IsRecording
= TRUE
;
780 if(iounit
->IsRecording
)
782 AHI_ControlAudio(iounit
->AudioCtrl
,
783 AHIC_MixFreq_Query
, (IPTR
)&mixfreq
,
786 /* Initialize ahir_Frequency for the assembler record routines */
787 if(ioreq
->ahir_Frequency
&& mixfreq
)
788 ioreq
->ahir_Frequency
= ((mixfreq
<< 15) / ioreq
->ahir_Frequency
) << 1;
790 ioreq
->ahir_Frequency
= 0x00010000; // Fixed 1.0
792 AHIObtainSemaphore(&iounit
->Lock
);
794 /* Add the request to the list of readers */
795 AddTail((struct List
*) &iounit
->ReadList
,(struct Node
*) ioreq
);
797 /* Copy the current buffer contents */
798 FillReadBuffer(ioreq
, iounit
, AHIBase
);
800 AHIReleaseSemaphore(&iounit
->Lock
);
804 ioreq
->ahir_Std
.io_Error
= error
;
805 TermIO(ioreq
, AHIBase
);
810 /******************************************************************************
811 ** WriteCmd *******************************************************************
812 ******************************************************************************/
814 /****** ahi.device/CMD_WRITE ************************************************
817 * CMD_WRITE -- Write raw samples to audio output (V4)
820 * Plays the samples to the users prefered audio output.
823 * io_Device Preset by the call to OpenDevice().
824 * io_Unit Preset by the call to OpenDevice().
825 * io_Command CMD_WRITE
826 * io_Data Pointer to the buffer of samples to be played.
827 * io_Length Number of bytes to play, must be a multiple of the
828 * sample frame size (see ahir_Type).
829 * io_Offset Must be 0.
830 * ahir_Type The desired sample format, see <ahi/devices.h>.
831 * ahir_Frequency The desired sample frequency in Hertz.
832 * ahir_Volume The desired volume. The range is 0 to 0x10000, where
833 * 0 means muted and 0x10000 (== 1.0) means full volume.
834 * ahir_Position Defines the stereo balance. 0 is far left, 0x8000 is
835 * center and 0x10000 is far right.
836 * ahir_Link If non-zero, pointer to a previously sent AHIRequest
837 * which this AHIRequest will be linked to. This
838 * request will be delayed until the old one is
839 * finished (used for double buffering). Must be set
840 * to NULL if not used.
843 * io_Error 0 for success, or an error code as defined in
844 * <ahi/devices.h> and <exec/errors.h>.
845 * io_Actual If io_Error is 0, number of bytes actually
846 * played. Starting with V6, io_Actual is also valid
847 * if io_Error is not 0 (like if the request was
850 * The other fields, except io_Device, io_Unit and io_Command, are
856 * 32 bit samples (ahir_Type) is only available in V6 and later.
861 * <ahi/devices.h>, <exec/errors.h>
863 ****************************************************************************
868 WriteCmd ( struct AHIRequest
*ioreq
,
869 struct AHIBase
*AHIBase
)
871 struct AHIDevUnit
*iounit
;
874 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
876 KPrintF("CMD_WRITE\n");
879 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
881 ioreq
->ahir_Std
.io_Actual
= 0;
883 /* Start playback if neccessary */
884 if( ! iounit
->IsPlaying
)
886 if( (! iounit
->FullDuplex
) && iounit
->IsRecording
)
888 error
= AHIE_HALFDUPLEX
; // FIXIT!
891 { static const Tag tags
[] = { AHIC_Play
,TRUE
,TAG_DONE
};
892 error
= AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
897 iounit
->IsPlaying
= TRUE
;
901 ioreq
->ahir_Extras
= (IPTR
) AllocVec(sizeof(struct Extras
), MEMF_PUBLIC
|MEMF_CLEAR
);
903 if(ioreq
->ahir_Extras
== 0)
909 // Initialize the structure
910 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
911 GetExtras(ioreq
)->Sound
= AHI_NOSOUND
;
912 GetExtras(ioreq
)->VolumeScale
= 0x10000;
915 if(iounit
->IsPlaying
&& !error
)
917 // Convert length in bytes to length in samples
919 ioreq
->ahir_Std
.io_Length
/= AHI_SampleFrameSize(ioreq
->ahir_Type
);
921 NewWriter(ioreq
, iounit
, AHIBase
);
926 ioreq
->ahir_Std
.io_Error
= error
;
927 TermIO(ioreq
, AHIBase
);
932 /******************************************************************************
933 ** StartCmd *******************************************************************
934 ******************************************************************************/
936 /****** ahi.device/CMD_START ************************************************
939 * CMD_START -- start device processing (like ^Q) (V4)
942 * All CMD_WRITE's that has been sent to the device since CMD_STOP
943 * will be started at once, synchronized.
946 * io_Device Preset by the call to OpenDevice().
947 * io_Unit Preset by the call to OpenDevice().
948 * io_Command CMD_START
951 * io_Error 0 for success, or an error code as defined in
952 * <ahi/devices.h> and <exec/errors.h>.
954 * The other fields, except io_Device, io_Unit and io_Command, are
960 * Unlike most (all?) other devices, CMD_STOP and CMD_START do nest in
966 * CMD_STOP, <ahi/devices.h>, <exec/errors.h>
968 ****************************************************************************
973 StartCmd ( struct AHIRequest
*ioreq
,
974 struct AHIBase
*AHIBase
)
976 struct AHIDevUnit
*iounit
;
977 struct AHIPrivAudioCtrl
*audioctrl
;
978 struct Library
*AHIsubBase
;
980 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
982 KPrintF("CMD_START\n");
985 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
986 audioctrl
= (struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
;
988 AHIObtainSemaphore(&iounit
->Lock
);
994 if((AHIsubBase
= audioctrl
->ahiac_SubLib
))
996 if(iounit
->StopCnt
== 0)
998 struct AHIRequest
* ior
;
1001 // Boost us some, so we don't spend too much time in
1002 // audio disabled state (we could miss an interrupt!)
1003 // and disable audio interrupts.
1005 old_pri
= SetTaskPri( FindTask( NULL
), 128 );
1006 AHIsub_Disable((struct AHIAudioCtrlDrv
*) audioctrl
);
1009 // Now "start" all sounds (they won't really start until
1010 // the audio interrupts are enabled).
1012 while((ior
= (struct AHIRequest
*) RemHead(
1013 (struct List
*) &iounit
->RequestQueue
)))
1015 WriteCmd(ior
, AHIBase
);
1019 AHIsub_Enable((struct AHIAudioCtrlDrv
*) audioctrl
);
1020 SetTaskPri( FindTask( NULL
), old_pri
);
1026 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1029 AHIReleaseSemaphore(&iounit
->Lock
);
1031 TermIO(ioreq
,AHIBase
);
1035 /******************************************************************************
1036 ** FeedReaders ****************************************************************
1037 ******************************************************************************/
1039 // This function is called by DevProc or ReadCmd to scan the list of waiting
1040 // readers, and fill their buffers. When a buffer is full, the IORequest is
1044 FeedReaders ( struct AHIDevUnit
*iounit
,
1045 struct AHIBase
*AHIBase
)
1047 struct AHIRequest
*ioreq
;
1049 AHIObtainSemaphore(&iounit
->Lock
);
1051 for(ioreq
= (struct AHIRequest
*)iounit
->ReadList
.mlh_Head
;
1052 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1053 ioreq
= (struct AHIRequest
*)ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1055 FillReadBuffer(ioreq
, iounit
, AHIBase
);
1058 // Check if Reader-list is empty. If so, stop recording (after a small delay).
1060 if( ! iounit
->ReadList
.mlh_Head
->mln_Succ
)
1062 if(--iounit
->RecordOffDelay
== 0)
1063 { static const Tag tags
[] = { AHIC_Record
,FALSE
,TAG_DONE
};
1064 AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
1065 iounit
->IsRecording
= FALSE
;
1070 iounit
->RecordOffDelay
= 2;
1073 AHIReleaseSemaphore(&iounit
->Lock
);
1077 /******************************************************************************
1078 ** FillReadBuffer *************************************************************
1079 ******************************************************************************/
1081 // Handles a read request. Note that the request MUST be in a list, and the
1082 // list must be semaphore locked!
1085 FillReadBuffer ( struct AHIRequest
*ioreq
,
1086 struct AHIDevUnit
*iounit
,
1087 struct AHIBase
*AHIBase
)
1089 ULONG length
,length2
;
1093 if(iounit
->ValidRecord
) // Make sure we have a valid source buffer
1095 oldaddress
= ioreq
->ahir_Std
.io_Data
;
1097 length
= (ioreq
->ahir_Std
.io_Length
- ioreq
->ahir_Std
.io_Actual
)
1098 / AHI_SampleFrameSize(ioreq
->ahir_Type
);
1100 length2
= (iounit
->RecordSize
- ioreq
->ahir_Std
.io_Offset
)
1101 / AHI_SampleFrameSize(AHIST_S16S
);
1102 length2
= MultFixed(length2
, (Fixed
) ioreq
->ahir_Frequency
);
1104 if(length
<= length2
)
1114 switch (ioreq
->ahir_Type
)
1117 RecM8S(length
,ioreq
->ahir_Frequency
,
1118 iounit
->RecordBuffer
,
1119 &ioreq
->ahir_Std
.io_Offset
,
1120 &ioreq
->ahir_Std
.io_Data
);
1123 RecS8S(length
,ioreq
->ahir_Frequency
,
1124 iounit
->RecordBuffer
,
1125 &ioreq
->ahir_Std
.io_Offset
,
1126 &ioreq
->ahir_Std
.io_Data
);
1129 RecM16S(length
,ioreq
->ahir_Frequency
,
1130 iounit
->RecordBuffer
,
1131 &ioreq
->ahir_Std
.io_Offset
,
1132 &ioreq
->ahir_Std
.io_Data
);
1135 RecS16S(length
,ioreq
->ahir_Frequency
,
1136 iounit
->RecordBuffer
,
1137 &ioreq
->ahir_Std
.io_Offset
,
1138 &ioreq
->ahir_Std
.io_Data
);
1141 RecM32S(length
,ioreq
->ahir_Frequency
,
1142 iounit
->RecordBuffer
,
1143 &ioreq
->ahir_Std
.io_Offset
,
1144 &ioreq
->ahir_Std
.io_Data
);
1147 RecS32S(length
,ioreq
->ahir_Frequency
,
1148 iounit
->RecordBuffer
,
1149 &ioreq
->ahir_Std
.io_Offset
,
1150 &ioreq
->ahir_Std
.io_Data
);
1153 ioreq
->ahir_Std
.io_Error
= AHIE_BADSAMPLETYPE
;
1158 ioreq
->ahir_Std
.io_Actual
+= ((IPTR
) ioreq
->ahir_Std
.io_Data
- (IPTR
) oldaddress
);
1162 Remove((struct Node
*) ioreq
);
1163 TermIO(ioreq
, AHIBase
);
1167 ioreq
->ahir_Std
.io_Offset
= 0;
1172 ioreq
->ahir_Std
.io_Offset
= 0;
1177 /******************************************************************************
1178 ** NewWriter ******************************************************************
1179 ******************************************************************************/
1181 // This function is called by WriteCmd when a new write request comes.
1184 NewWriter ( struct AHIRequest
*ioreq
,
1185 struct AHIDevUnit
*iounit
,
1186 struct AHIBase
*AHIBase
)
1190 struct AHISampleInfo si
;
1191 struct Library
*AHIsubBase
;
1193 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1195 si
.ahisi_Type
= ioreq
->ahir_Type
;
1196 si
.ahisi_Address
= ioreq
->ahir_Std
.io_Data
;
1197 si
.ahisi_Length
= ioreq
->ahir_Std
.io_Length
;
1201 for(sound
= 0; sound
< MAXSOUNDS
; sound
++)
1203 if(iounit
->Sounds
[sound
] == SOUND_FREE
)
1205 iounit
->Sounds
[sound
] = SOUND_IN_USE
;
1210 if((sound
< MAXSOUNDS
) &&
1211 (AHI_LoadSound(sound
, AHIST_DYNAMICSAMPLE
, &si
, iounit
->AudioCtrl
)
1214 GetExtras(ioreq
)->Sound
= sound
;
1216 AHIObtainSemaphore(&iounit
->Lock
);
1218 if(ioreq
->ahir_Link
)
1220 // See if the linked request is playing, silent or waiting...
1222 if(FindNode((struct List
*) &iounit
->PlayingList
,
1223 (struct Node
*) ioreq
->ahir_Link
))
1227 else if(FindNode((struct List
*) &iounit
->SilentList
,
1228 (struct Node
*) ioreq
->ahir_Link
))
1232 else if(FindNode((struct List
*) &iounit
->WaitingList
,
1233 (struct Node
*) ioreq
->ahir_Link
))
1239 // NOTE: ahir_Link changes direction here. When the user set's it, she makes a new
1240 // request point to an old. We let the old point to the next (that's more natural,
1241 // anyway...) It the user tries to link more than one request to another, we fail.
1245 if( ! ioreq
->ahir_Link
->ahir_Link
)
1247 struct AHIRequest
*otherioreq
= ioreq
->ahir_Link
;
1249 channel
= GetExtras( otherioreq
)->Channel
;
1250 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
1252 otherioreq
->ahir_Link
= ioreq
;
1253 ioreq
->ahir_Link
= NULL
;
1254 Enqueue((struct List
*) &iounit
->WaitingList
,(struct Node
*) ioreq
);
1256 if(channel
!= NOCHANNEL
)
1258 // Attach the request to the currently playing one
1260 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1262 // Make SURE the current sound isn't already finished!
1264 if(otherioreq
->ahir_Std
.io_Command
== AHICMD_WRITTEN
)
1266 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1268 // OOPS! It's finished! Undo...
1269 Remove((struct Node
*) ioreq
);
1271 // Start sound as if it wasn't delayed (see below);
1272 AddWriter(ioreq
, iounit
, AHIBase
);
1276 if( iounit
->Voices
[channel
].Flags
& VF_STARTED
)
1278 // There is a sound already playing. Attach this sound
1279 // after the current.
1281 iounit
->Voices
[channel
].QueuedRequest
= ioreq
;
1282 iounit
->Voices
[channel
].NextOffset
= PLAY
;
1283 iounit
->Voices
[channel
].NextRequest
= NULL
;
1285 AHI_Play(iounit
->AudioCtrl
,
1286 AHIP_BeginChannel
, channel
,
1287 AHIP_LoopFreq
, ioreq
->ahir_Frequency
,
1288 AHIP_LoopVol
, (IPTR
) (((long long) ioreq
->ahir_Volume
*
1289 GetExtras(ioreq
)->VolumeScale
) >> 16),
1290 AHIP_LoopPan
, ioreq
->ahir_Position
,
1291 AHIP_LoopSound
, GetExtras(ioreq
)->Sound
,
1292 AHIP_LoopOffset
, ioreq
->ahir_Std
.io_Actual
,
1293 AHIP_LoopLength
, ioreq
->ahir_Std
.io_Length
-
1294 ioreq
->ahir_Std
.io_Actual
,
1300 // The current sound has not yet been started, and the loop
1301 // part is not set either. Let the SoundFunc() handle the
1304 iounit
->Voices
[channel
].NextSound
= GetExtras( ioreq
)->Sound
;
1305 iounit
->Voices
[channel
].NextVolume
= ioreq
->ahir_Volume
;
1306 iounit
->Voices
[channel
].NextPan
= ioreq
->ahir_Position
;
1307 iounit
->Voices
[channel
].NextFrequency
= ioreq
->ahir_Frequency
;
1308 iounit
->Voices
[channel
].NextOffset
= ioreq
->ahir_Std
.io_Actual
;
1309 iounit
->Voices
[channel
].NextLength
= ioreq
->ahir_Std
.io_Length
-
1310 ioreq
->ahir_Std
.io_Actual
;
1311 iounit
->Voices
[channel
].NextRequest
= ioreq
;
1314 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1318 else // She tried to add more than one request to another one
1320 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1321 TermIO(ioreq
, AHIBase
);
1324 else // Sound is not delayed
1326 ioreq
->ahir_Link
=NULL
;
1327 AddWriter(ioreq
, iounit
, AHIBase
);
1330 AHIReleaseSemaphore(&iounit
->Lock
);
1332 else // No free sound found, or sound failed to load
1334 if( sound
< MAXSOUNDS
)
1336 // Clean up and set error code
1337 iounit
->Sounds
[sound
] = SOUND_FREE
;
1338 ioreq
->ahir_Std
.io_Error
= AHIE_BADSAMPLETYPE
;
1342 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1345 TermIO(ioreq
, AHIBase
);
1350 /******************************************************************************
1351 ** AddWriter ******************************************************************
1352 ******************************************************************************/
1354 // This function is called by NewWriter and RethinkPlayers. It adds an
1355 // initialized request to either the playing or waiting list, and starts
1356 // the sound it if possible
1359 AddWriter ( struct AHIRequest
*ioreq
,
1360 struct AHIDevUnit
*iounit
,
1361 struct AHIBase
*AHIBase
)
1365 // Search for a free channel, and use if found
1367 for(channel
= 0; channel
< iounit
->Channels
; channel
++)
1369 if(iounit
->Voices
[channel
].NextOffset
== (ULONG
) FREE
)
1371 Enqueue((struct List
*) &iounit
->PlayingList
,(struct Node
*) ioreq
);
1372 UpdateMasterVolume( iounit
, AHIBase
);
1373 PlayRequest(channel
, ioreq
, iounit
, AHIBase
);
1379 if(channel
== iounit
->Channels
)
1381 struct AHIRequest
*ioreq2
;
1383 // No free channel found. Check if we can kick the last one out...
1384 // The last one, if it exists, has lowest priority.
1386 // Note that it is quite possible that there is no request in the list,
1387 // even though there was no free sound channel. This can happen if
1388 // AbortIO() has been called, and marked the channel MUTE, but the
1389 // SoundFunc() has yet not been called to move the chennel into the
1392 ioreq2
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_TailPred
;
1394 if( ( iounit
->PlayingList
.mlh_Head
->mln_Succ
!= NULL
) &&
1395 ( ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Pri
>
1396 ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Pri
) )
1398 // Let's steal his place!
1400 RemTail((struct List
*) &iounit
->PlayingList
);
1401 channel
= GetExtras(ioreq2
)->Channel
;
1402 GetExtras(ioreq2
)->Channel
= NOCHANNEL
;
1403 Enqueue((struct List
*) &iounit
->SilentList
,(struct Node
*) ioreq2
);
1404 Enqueue((struct List
*) &iounit
->PlayingList
,(struct Node
*) ioreq
);
1405 PlayRequest(channel
, ioreq
, iounit
, AHIBase
);
1409 // Let's be quiet for a while.
1410 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
1411 Enqueue((struct List
*) &iounit
->SilentList
,(struct Node
*) ioreq
);
1417 /******************************************************************************
1418 ** PlayRequest ****************************************************************
1419 ******************************************************************************/
1421 // This begins to play an AHIRequest (starting at sample io_Actual).
1424 PlayRequest ( int channel
,
1425 struct AHIRequest
*ioreq
,
1426 struct AHIDevUnit
*iounit
,
1427 struct AHIBase
*AHIBase
)
1429 struct Library
*AHIsubBase
;
1431 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1435 GetExtras(ioreq
)->Channel
= channel
;
1437 if(ioreq
->ahir_Link
)
1439 struct Voice
*v
= &iounit
->Voices
[channel
];
1440 struct AHIRequest
*r
= ioreq
->ahir_Link
;
1442 v
->NextSound
= GetExtras(r
)->Sound
;
1443 v
->NextVolume
= r
->ahir_Volume
;
1444 v
->NextPan
= r
->ahir_Position
;
1445 v
->NextFrequency
= r
->ahir_Frequency
;
1446 v
->NextOffset
= r
->ahir_Std
.io_Actual
;
1447 v
->NextLength
= r
->ahir_Std
.io_Length
1448 - r
->ahir_Std
.io_Actual
;
1453 iounit
->Voices
[channel
].NextOffset
= PLAY
;
1454 iounit
->Voices
[channel
].NextRequest
= NULL
;
1457 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1459 iounit
->Voices
[channel
].PlayingRequest
= NULL
;
1460 iounit
->Voices
[channel
].QueuedRequest
= ioreq
;
1461 iounit
->Voices
[channel
].Flags
&= ~VF_STARTED
;
1463 AHI_Play(iounit
->AudioCtrl
,
1464 AHIP_BeginChannel
, channel
,
1465 AHIP_Freq
, ioreq
->ahir_Frequency
,
1466 AHIP_Vol
, (IPTR
) (((long long) ioreq
->ahir_Volume
*
1467 GetExtras(ioreq
)->VolumeScale
) >> 16),
1468 AHIP_Pan
, ioreq
->ahir_Position
,
1469 AHIP_Sound
, GetExtras(ioreq
)->Sound
,
1470 AHIP_Offset
, ioreq
->ahir_Std
.io_Actual
,
1471 AHIP_Length
, ioreq
->ahir_Std
.io_Length
-ioreq
->ahir_Std
.io_Actual
,
1475 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1478 // This is a workaround for a race condition.
1479 // The problem can occur if a delayed request follows immediately after
1480 // this one, before the sample interrupt routine has been called, and
1481 // overwrites QueuedRequest. The result is that this sound is never
1482 // marked as finished, and the application will wait forever on the
1483 // IO Request. Quite ugly, no?
1485 Wait(1L << iounit
->SampleSignal
);
1487 // Set signal again...
1488 Signal((struct Task
*) iounit
->Master
, (1L << iounit
->SampleSignal
));
1490 // while(((volatile UBYTE) (iounit->Voices[channel].Flags) & VF_STARTED) == 0);
1495 /******************************************************************************
1496 ** RethinkPlayers *************************************************************
1497 ******************************************************************************/
1499 // When a playing sample has reached the end, this function is called.
1500 // It finds and terminates all finished requests, and moves their 'children'
1501 // from the waiting list.
1502 // Then it tries to restart all silent sounds.
1505 RethinkPlayers ( struct AHIDevUnit
*iounit
,
1506 struct AHIBase
*AHIBase
)
1508 struct MinList templist
;
1509 struct AHIRequest
*ioreq
;
1511 NewList((struct List
*) &templist
);
1513 AHIObtainSemaphore(&iounit
->Lock
);
1515 RemPlayers((struct List
*) &iounit
->PlayingList
, iounit
, AHIBase
);
1516 RemPlayers((struct List
*) &iounit
->SilentList
, iounit
, AHIBase
);
1518 // Move all silent requests to our temporary list
1520 while((ioreq
= (struct AHIRequest
*) RemHead((struct List
*) &iounit
->SilentList
)))
1522 AddTail((struct List
*) &templist
, (struct Node
*) ioreq
);
1525 // And add them back...
1526 while((ioreq
= (struct AHIRequest
*) RemHead((struct List
*) &templist
)))
1528 AddWriter(ioreq
, iounit
, AHIBase
);
1531 AHIReleaseSemaphore(&iounit
->Lock
);
1535 /******************************************************************************
1536 ** RemPlayers *****************************************************************
1537 ******************************************************************************/
1539 // Removes all finished play requests from a list. The lists must be locked!
1542 RemPlayers ( struct List
*list
,
1543 struct AHIDevUnit
*iounit
,
1544 struct AHIBase
*AHIBase
)
1546 struct AHIRequest
*ioreq
, *node
;
1548 node
= (struct AHIRequest
*) list
->lh_Head
;
1550 while(node
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1553 node
= (struct AHIRequest
*) node
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1555 if(ioreq
->ahir_Std
.io_Command
== AHICMD_WRITTEN
)
1557 Remove((struct Node
*) ioreq
);
1559 if(ioreq
->ahir_Link
)
1561 // Move the attached one to the list
1562 Remove((struct Node
*) ioreq
->ahir_Link
);
1564 // FIXME: 2002-10-13: I have a bug report that claims
1565 // GetExtras(ioreq->ahir_Link) returns NULL here. How did that
1568 // FIXED: 2005-09-26: The app AbortIO()'ed a request that was
1569 // attached to another request using ahir_Link. When we then
1570 // arrived here, ioreq->ahir_Link would point to a terminated
1571 // request and possibly even deallocated memory. Now AbortIO()
1572 // clears ahir_Link.
1574 GetExtras(ioreq
->ahir_Link
)->Channel
= GetExtras(ioreq
)->Channel
;
1575 Enqueue(list
, (struct Node
*) ioreq
->ahir_Link
);
1576 // We have to go through the whole procedure again, in case
1577 // the child is finished, too.
1578 node
= (struct AHIRequest
*) list
->lh_Head
;
1581 ioreq
->ahir_Std
.io_Error
= AHIE_OK
;
1582 ioreq
->ahir_Std
.io_Command
= CMD_WRITE
;
1583 ioreq
->ahir_Std
.io_Actual
= ioreq
->ahir_Std
.io_Length
;
1584 TermIO(ioreq
, AHIBase
);
1590 /******************************************************************************
1591 ** UpdateSilentPlayers ********************************************************
1592 ******************************************************************************/
1594 // Updates the io_Actual field of all silent requests. The lists must be locked.
1595 // This function is either called from the interrupt or DevProc.
1598 UpdateSilentPlayers ( struct AHIDevUnit
*iounit
,
1599 struct AHIBase
*AHIBase
)
1601 struct AHIRequest
*ioreq
;
1603 for(ioreq
= (struct AHIRequest
*)iounit
->SilentList
.mlh_Head
;
1604 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1605 ioreq
= (struct AHIRequest
*)ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1609 ioreq
->ahir_Std
.io_Actual
+= ((ioreq
->ahir_Frequency
<< 14) / PLAYERFREQ
) >> 14;
1611 // Check if the whole sample has been "played"
1612 if(ioreq
->ahir_Std
.io_Actual
>= ioreq
->ahir_Std
.io_Length
)
1614 // Mark request as finished
1615 ioreq
->ahir_Std
.io_Command
= AHICMD_WRITTEN
;
1617 // Make us call Rethinkplayers later
1618 Signal((struct Task
*) iounit
->Master
, (1L << iounit
->SampleSignal
));
1624 /******************************************************************************
1625 ** UpdateMasterVolume ********************************************************
1626 ******************************************************************************/
1628 // Updated the master volume so all sounds are played as loud as possible
1629 // without risking clipping.
1630 // This function is called from AddWriter() and TermIO().
1632 static void UpdateMasterVolume( struct AHIDevUnit
*iounit
,
1633 struct AHIBase
*AHIBase
)
1635 struct AHIRequest
* ioreq1
;
1636 struct AHIRequest
* ioreq2
;
1637 struct Library
* AHIsubBase
;
1639 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1641 AHIObtainSemaphore(&iounit
->Lock
);
1643 for(ioreq1
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1644 ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1645 ioreq1
= (struct AHIRequest
*) ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1647 ULONG id
= ioreq1
->ahir_Private
[1];
1649 LONG minscale
= 0x10000;
1651 /* KPrintF( "Checking id %08lx on request %08lx... ", id, ioreq1 ); */
1653 for(ioreq2
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1654 ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1655 ioreq2
= (struct AHIRequest
*) ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1657 if( ioreq2
->ahir_Private
[1] == id
)
1661 if( GetExtras(ioreq2
) && GetExtras(ioreq2
)->VolumeScale
< minscale
)
1663 minscale
= GetExtras(ioreq2
)->VolumeScale
;
1668 if( minscale
> 0x10000 / c
)
1670 minscale
= 0x10000 / c
;
1673 switch( AHIBase
->ahib_ScaleMode
)
1675 case AHI_SCALE_DYNAMIC_SAFE
:
1676 if( GetExtras(ioreq1
)->VolumeScale
> minscale
)
1678 GetExtras(ioreq1
)->VolumeScale
= minscale
;
1682 case AHI_SCALE_FIXED_SAFE
:
1683 case AHI_SCALE_FIXED_0_DB
:
1684 case AHI_SCALE_FIXED_3_DB
:
1685 case AHI_SCALE_FIXED_6_DB
:
1686 GetExtras(ioreq1
)->VolumeScale
= 0x10000;
1690 /* KPrintF( "%ld requests, maxdiv = %ld -> Vol %05lx => %05lx\n", */
1691 /* c, maxdiv, ioreq1->ahir_Volume, */
1692 /* ioreq1->ahir_Volume / GetExtras(ioreq1)->VolumeDiv ); */
1695 // Now update the volume as quickly as possible ...
1697 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1699 for(ioreq1
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1700 ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1701 ioreq1
= (struct AHIRequest
*) ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1703 if( GetExtras(ioreq1
)->Channel
!= NOCHANNEL
)
1705 AHI_SetVol( GetExtras(ioreq1
)->Channel
,
1706 (IPTR
) (((long long) ioreq1
->ahir_Volume
*
1707 GetExtras(ioreq1
)->VolumeScale
) >> 16),
1708 ioreq1
->ahir_Position
,
1714 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1716 // And now the real master volume ...
1718 if( iounit
->Unit
.unit_OpenCnt
== 0 )
1720 struct AHIEffMasterVolume vol
= {
1721 AHIET_MASTERVOLUME
| AHIET_CANCEL
,
1725 AHI_SetEffect( &vol
, iounit
->AudioCtrl
);
1729 struct AHIEffMasterVolume vol
= {
1734 switch( AHIBase
->ahib_ScaleMode
)
1736 case AHI_SCALE_FIXED_SAFE
:
1737 vol
.ahiemv_Volume
= 0x10000;
1740 case AHI_SCALE_DYNAMIC_SAFE
:
1741 vol
.ahiemv_Volume
= iounit
->Channels
* 0x10000 / iounit
->Unit
.unit_OpenCnt
;
1744 case AHI_SCALE_FIXED_0_DB
:
1745 vol
.ahiemv_Volume
= iounit
->Channels
* 0x10000;
1748 case AHI_SCALE_FIXED_3_DB
:
1749 vol
.ahiemv_Volume
= iounit
->Channels
* 0xB505;
1752 case AHI_SCALE_FIXED_6_DB
:
1753 vol
.ahiemv_Volume
= iounit
->Channels
* 0x8000;
1757 if( iounit
->PseudoStereo
)
1759 vol
.ahiemv_Volume
= vol
.ahiemv_Volume
/ 2;
1762 AHI_SetEffect( &vol
, iounit
->AudioCtrl
);
1765 AHIReleaseSemaphore(&iounit
->Lock
);