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,
24 #include <exec/errors.h>
25 #include <exec/tasks.h>
27 #include <exec/devices.h>
28 #include <exec/memory.h>
30 #include <clib/alib_protos.h>
31 #include <proto/exec.h>
32 #include <proto/dos.h>
34 #define __NOGLOBALIFACE__
35 #include <proto/ahi.h>
37 #undef __NOGLOBALIFACE__
38 #include <proto/ahi_sub.h>
45 #include "devcommands.h"
51 #define IAHIsub ((struct AHIPrivAudioCtrl *) iounit->AudioCtrl)->ahiac_IAHIsub
54 static void TermIO(struct AHIRequest
*, struct AHIBase
*);
55 static void Devicequery(struct AHIRequest
*, struct AHIBase
*);
56 static void ResetCmd(struct AHIRequest
*, struct AHIBase
*);
57 static void ReadCmd(struct AHIRequest
*, struct AHIBase
*);
58 static void WriteCmd(struct AHIRequest
*, struct AHIBase
*);
59 static void StopCmd(struct AHIRequest
*, struct AHIBase
*);
60 static void StartCmd(struct AHIRequest
*, struct AHIBase
*);
61 static void FlushCmd(struct AHIRequest
*, struct AHIBase
*);
63 static void FillReadBuffer(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
65 static void NewWriter(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
66 static void AddWriter(struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
67 static void PlayRequest(int, struct AHIRequest
*, struct AHIDevUnit
*, struct AHIBase
*);
68 static void RemPlayers( struct List
*, struct AHIDevUnit
*, struct AHIBase
*);
70 static void UpdateMasterVolume( struct AHIDevUnit
*, struct AHIBase
* );
72 #define MultFixed( a, b ) ( (unsigned long) ( ( ( (unsigned long long) a ) << 16 ) / b ) )
74 /******************************************************************************
75 ** DevBeginIO *****************************************************************
76 ******************************************************************************/
78 // This function is called by the system each time exec.library/DoIO()
82 _DevBeginIO( struct AHIRequest
* ioreq
,
83 struct AHIBase
* AHIBase
)
85 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
87 KPrintF("BeginIO(0x%P)\n", (IPTR
)ioreq
);
90 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
92 switch(ioreq
->ahir_Std
.io_Command
)
96 case NSCMD_DEVICEQUERY
:
99 PerformIO(ioreq
,AHIBase
);
107 ioreq
->ahir_Std
.io_Flags
&= ~IOF_QUICK
;
108 PutMsg(&ioreq
->ahir_Std
.io_Unit
->unit_MsgPort
,&ioreq
->ahir_Std
.io_Message
);
113 ioreq
->ahir_Std
.io_Error
= IOERR_NOCMD
;
114 TermIO(ioreq
,AHIBase
);
120 /******************************************************************************
121 ** AbortIO ********************************************************************
122 ******************************************************************************/
124 // This function is called by the system each time exec.library/AbortIO()
128 _DevAbortIO( struct AHIRequest
* ioreq
,
129 struct AHIBase
* AHIBase
)
132 struct AHIDevUnit
*iounit
;
134 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
136 KPrintF("AbortIO(0x%P)", (IPTR
)ioreq
);
139 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
141 AHIObtainSemaphore(&iounit
->Lock
);
143 if(ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Type
!= NT_REPLYMSG
)
145 switch(ioreq
->ahir_Std
.io_Command
)
149 if(FindNode((struct List
*) &iounit
->ReadList
, (struct Node
*) ioreq
))
151 Remove((struct Node
*) ioreq
);
152 ioreq
->ahir_Std
.io_Error
= IOERR_ABORTED
;
153 TermIO(ioreq
,AHIBase
);
159 if(FindNode((struct List
*) &iounit
->PlayingList
, (struct Node
*) ioreq
)
160 || FindNode((struct List
*) &iounit
->SilentList
, (struct Node
*) ioreq
)
161 || FindNode((struct List
*) &iounit
->WaitingList
, (struct Node
*) ioreq
))
163 struct AHIRequest
*nextreq
;
164 struct AHIRequest
*io
;
168 Remove((struct Node
*) ioreq
);
170 // Now check if any other request ahir_Link to us. If so,
171 // we need to clear that field, since this request is no
175 for (io
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
176 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
177 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
178 if (io
->ahir_Link
== ioreq
) {
179 io
->ahir_Link
= NULL
;
184 for (io
= (struct AHIRequest
*) iounit
->SilentList
.mlh_Head
;
185 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
186 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
187 if (io
->ahir_Link
== ioreq
) {
188 io
->ahir_Link
= NULL
;
193 for (io
= (struct AHIRequest
*) iounit
->WaitingList
.mlh_Head
;
194 io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
!= NULL
;
195 io
= (struct AHIRequest
*) io
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
) {
196 if (io
->ahir_Link
== ioreq
) {
197 io
->ahir_Link
= NULL
;
203 if(ioreq
->ahir_Extras
&& (GetExtras(ioreq
)->Channel
!= NOCHANNEL
))
205 struct Library
*AHIsubBase
= NULL
;
207 if( iounit
->AudioCtrl
!= NULL
)
209 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)
213 if( AHIsubBase
!= NULL
)
215 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
218 iounit
->Voices
[GetExtras(ioreq
)->Channel
].PlayingRequest
= NULL
;
219 iounit
->Voices
[GetExtras(ioreq
)->Channel
].QueuedRequest
= NULL
;
220 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextRequest
= NULL
;
222 if(iounit
->AudioCtrl
)
224 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextOffset
= MUTE
;
225 AHI_SetSound(GetExtras(ioreq
)->Channel
,AHI_NOSOUND
,0,0,
226 iounit
->AudioCtrl
,AHISF_IMM
);
230 iounit
->Voices
[GetExtras(ioreq
)->Channel
].NextOffset
= FREE
;
233 if( AHIsubBase
!= NULL
)
235 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
239 ioreq
->ahir_Std
.io_Command
= CMD_WRITE
;
240 ioreq
->ahir_Std
.io_Error
= IOERR_ABORTED
;
241 nextreq
= ioreq
->ahir_Link
;
242 TermIO(ioreq
,AHIBase
);
254 AHIReleaseSemaphore(&iounit
->Lock
);
256 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
258 KPrintF("=>%ld\n",rc
);
265 /******************************************************************************
266 ** TermIO *********************************************************************
267 ******************************************************************************/
269 // This functions returns an IO request back to the sender.
272 TermIO ( struct AHIRequest
*ioreq
,
273 struct AHIBase
*AHIBase
)
275 struct AHIDevUnit
*iounit
;
276 ULONG error
= ioreq
->ahir_Std
.io_Error
;
278 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
280 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
282 KPrintF("Terminating IO Request 0x%08lx", (ULONG
) ioreq
);
285 if( ioreq
->ahir_Std
.io_Command
== CMD_WRITE
)
287 // Update master volume if we're terminating a write request
288 UpdateMasterVolume( iounit
, AHIBase
);
290 // Convert io_Actual to bytes
291 ioreq
->ahir_Std
.io_Actual
*= AHI_SampleFrameSize(ioreq
->ahir_Type
);
294 if(ioreq
->ahir_Extras
!= 0)
296 int sound
= GetExtras(ioreq
)->Sound
;
297 APTR extras
= (APTR
) ioreq
->ahir_Extras
;
299 if((sound
!= AHI_NOSOUND
) && (sound
< MAXSOUNDS
))
301 AHI_UnloadSound(sound
, iounit
->AudioCtrl
);
302 iounit
->Sounds
[sound
] = SOUND_FREE
;
305 ioreq
->ahir_Extras
= 0;
309 if( ! (ioreq
->ahir_Std
.io_Flags
& IOF_QUICK
))
311 ReplyMsg(&ioreq
->ahir_Std
.io_Message
);
314 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_LOW
)
316 KPrintF("=>%ld\n", error
);
321 /******************************************************************************
322 ** PerformIO ******************************************************************
323 ******************************************************************************/
326 PerformIO ( struct AHIRequest
*ioreq
,
327 struct AHIBase
*AHIBase
)
329 struct AHIDevUnit
*iounit
;
331 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
332 ioreq
->ahir_Std
.io_Error
= 0;
334 // Just to make sure TermIO won't free a bad address
335 ioreq
->ahir_Extras
= 0;
337 switch(ioreq
->ahir_Std
.io_Command
)
339 case NSCMD_DEVICEQUERY
:
340 Devicequery(ioreq
, AHIBase
);
344 ResetCmd(ioreq
, AHIBase
);
348 ReadCmd(ioreq
, AHIBase
);
352 AHIObtainSemaphore(&iounit
->Lock
);
356 AddTail((struct List
*) &iounit
->RequestQueue
,(struct Node
*) ioreq
);
360 WriteCmd(ioreq
, AHIBase
);
363 AHIReleaseSemaphore(&iounit
->Lock
);
367 StopCmd(ioreq
, AHIBase
);
371 StartCmd(ioreq
, AHIBase
);
375 FlushCmd(ioreq
, AHIBase
);
379 ioreq
->ahir_Std
.io_Error
= IOERR_NOCMD
;
380 TermIO(ioreq
, AHIBase
);
386 /******************************************************************************
387 ** Devicequery ****************************************************************
388 ******************************************************************************/
390 /****** ahi.device/NSCMD_DEVICEQUERY ***************************************
393 * NSCMD_DEVICEQUERY -- Query the device for its capabilities (V4)
396 * Fills an initialized NSDeviceQueryResult structure with
397 * information about the device.
400 * io_Device Preset by the call to OpenDevice().
401 * io_Unit Preset by the call to OpenDevice().
402 * io_Command NSCMD_DEVICEQUERY
403 * io_Data Pointer to the NSDeviceQueryResult structure,
404 * initialized as follows:
405 * DevQueryFormat - Set to 0
406 * SizeAvailable - Must be cleared.
407 * It is probably good manners to clear all other
409 * io_Length Size of the NSDeviceQueryResult structure.
412 * io_Error 0 for success, or an error code as defined in
413 * <ahi/devices.h> and <exec/errors.h>.
414 * io_Actual If io_Error is 0, the value in
415 * NSDeviceQueryResult.SizeAvailable.
417 * The NSDeviceQueryResult structure now contains valid information.
419 * The other fields, except io_Device, io_Unit and io_Command, are
429 * <ahi/devices.h>, <exec/errors.h>
431 ****************************************************************************
449 Devicequery ( struct AHIRequest
*ioreq
,
450 struct AHIBase
*AHIBase
)
452 struct NSDeviceQueryResult
*dqr
;
454 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
456 KPrintF("NSCMD_DEVICEQUERY\n");
459 dqr
= ioreq
->ahir_Std
.io_Data
;
460 if(ioreq
->ahir_Std
.io_Length
>= 16)
462 dqr
->SizeAvailable
= 16;
463 dqr
->DeviceType
= NSDEVTYPE_UNKNOWN
;
464 dqr
->DeviceSubType
= 0;
465 dqr
->SupportedCommands
= commandlist
;
468 ioreq
->ahir_Std
.io_Actual
= dqr
->SizeAvailable
;
469 TermIO(ioreq
, AHIBase
);
473 /******************************************************************************
474 ** StopCmd ********************************************************************
475 ******************************************************************************/
477 /****** ahi.device/CMD_STOP ************************************************
480 * CMD_STOP -- stop device processing (like ^S) (V4)
483 * Stops all CMD_WRITE processing. All writes will be queued, and
484 * are not processed until CMD_START. This is useful for synchronizing
485 * two or more CMD_WRITE's.
488 * io_Device Preset by the call to OpenDevice().
489 * io_Unit Preset by the call to OpenDevice().
490 * io_Command CMD_STOP
493 * io_Error 0 for success, or an error code as defined in
494 * <ahi/devices.h> and <exec/errors.h>.
496 * The other fields, except io_Device, io_Unit and io_Command, are
502 * This command affects ALL writes, even those sent by other
503 * applications. Make sure the code between CMD_STOP and CMD_START
504 * runs as fast as possible!
506 * Unlike most (all?) other devices, CMD_STOP and CMD_START do nest in
512 * CMD_START, <ahi/devices.h>, <exec/errors.h>
514 ****************************************************************************
519 StopCmd ( struct AHIRequest
*ioreq
,
520 struct AHIBase
*AHIBase
)
522 struct AHIDevUnit
*iounit
;
524 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
526 KPrintF("CMD_STOP\n");
529 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
531 AHIObtainSemaphore(&iounit
->Lock
);
535 AHIReleaseSemaphore(&iounit
->Lock
);
537 TermIO(ioreq
,AHIBase
);
541 /******************************************************************************
542 ** FlushCmd *******************************************************************
543 ******************************************************************************/
545 /****** ahi.device/CMD_FLUSH ************************************************
548 * CMD_FLUSH -- Cancel all I/O requests (V4)
551 * Aborts ALL current requests, both active and waiting, even
552 * other programs requests!
555 * io_Device Preset by the call to OpenDevice().
556 * io_Unit Preset by the call to OpenDevice().
557 * io_Command CMD_FLUSH
560 * io_Error 0 for success, or an error code as defined in
561 * <ahi/devices.h> and <exec/errors.h>.
562 * io_Actual If io_Error is 0, number of requests actually
565 * The other fields, except io_Device, io_Unit and io_Command, are
571 * This command should only be used in very rare cases, like AHI
572 * system utilities. Never use this command in an application.
577 * CMD_RESET, <ahi/devices.h>, <exec/errors.h>
579 ****************************************************************************
584 FlushCmd ( struct AHIRequest
*ioreq
,
585 struct AHIBase
*AHIBase
)
587 struct AHIDevUnit
*iounit
;
588 struct AHIRequest
*ior
;
590 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
592 KPrintF("CMD_FLUSH\n");
595 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
597 ioreq
->ahir_Std
.io_Actual
= 0;
599 // Abort all current IO-requests
600 while((ior
= (struct AHIRequest
*) iounit
->ReadList
.mlh_Head
))
602 _DevAbortIO(ior
, AHIBase
);
603 ioreq
->ahir_Std
.io_Actual
++;
605 while((ior
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
))
607 _DevAbortIO(ior
, AHIBase
);
608 ioreq
->ahir_Std
.io_Actual
++;
610 while((ior
= (struct AHIRequest
*) iounit
->SilentList
.mlh_Head
))
612 _DevAbortIO(ior
, AHIBase
);
613 ioreq
->ahir_Std
.io_Actual
++;
615 while((ior
= (struct AHIRequest
*) iounit
->WaitingList
.mlh_Head
))
617 _DevAbortIO(ior
, AHIBase
);
618 ioreq
->ahir_Std
.io_Actual
++;
620 TermIO(ioreq
,AHIBase
);
625 /* All the following functions are called within the unit process context */
628 /******************************************************************************
629 ** ResetCmd *******************************************************************
630 ******************************************************************************/
632 /****** ahi.device/CMD_RESET ************************************************
635 * CMD_RESET -- Restore device to a known state (V4)
638 * Aborts all current requests, even other programs requests
639 * (CMD_FLUSH), rereads the configuration file and resets the hardware
640 * to its initial state
644 * io_Device Preset by the call to OpenDevice().
645 * io_Unit Preset by the call to OpenDevice().
646 * io_Command CMD_RESET
649 * io_Error 0 for success, or an error code as defined in
650 * <ahi/devices.h> and <exec/errors.h>.
652 * The other fields, except io_Device, io_Unit and io_Command, are
658 * This command should only be used in very rare cases, like AHI
659 * system utilities. Never use this command in an application.
664 * CMD_FLUSH, <ahi/devices.h>, <exec/errors.h>
666 ****************************************************************************
671 ResetCmd ( struct AHIRequest
*ioreq
,
672 struct AHIBase
*AHIBase
)
674 struct AHIDevUnit
*iounit
;
676 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
678 KPrintF("CMD_RESET\n");
681 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
683 // Remove all requests (beware, invalid IORequest to FlushCmd!)
684 FlushCmd(ioreq
, AHIBase
);
686 // Reset the hardware
687 ReadConfig(iounit
, AHIBase
);
688 FreeHardware(iounit
, AHIBase
);
689 AllocHardware(iounit
, AHIBase
);
690 TermIO(ioreq
,AHIBase
);
694 /******************************************************************************
695 ** ReadCmd ********************************************************************
696 ******************************************************************************/
698 /****** ahi.device/CMD_READ *************************************************
701 * CMD_READ -- Read raw samples from audio input (V4)
704 * Reads samples from the users prefered input to memory. The sample
705 * format and frequency will be converted on the fly.
708 * io_Device Preset by the call to OpenDevice().
709 * io_Unit Preset by the call to OpenDevice().
710 * io_Command CMD_READ
711 * io_Data Pointer to the buffer where the data should be put.
712 * io_Length Number of bytes to read, must be a multiple of the
713 * sample frame size (see ahir_Type).
714 * io_Offset Set to 0 when you use for the first time or after
716 * ahir_Type The desired sample format, see <ahi/devices.h>.
717 * ahir_Frequency The desired sample frequency in Hertz.
720 * io_Error 0 for success, or an error code as defined in
721 * <ahi/devices.h> and <exec/errors.h>.
722 * io_Actual If io_Error is 0, number of bytes actually
723 * transferred. Starting with V6, io_Actual is also
724 * valid if io_Error is not 0 (like if the request
726 * io_Offset Updated to be used as input next time.
728 * The other fields, except io_Device, io_Unit and io_Command, are
734 * It's only possible to read signed mono or stereo samples.
739 * <ahi/devices.h>, <exec/errors.h>
741 ****************************************************************************
746 ReadCmd ( struct AHIRequest
*ioreq
,
747 struct AHIBase
*AHIBase
)
749 struct AHIDevUnit
*iounit
;
750 ULONG error
=AHIE_OK
,mixfreq
= 0;
752 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
754 KPrintF("CMD_READ\n");
757 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
759 ioreq
->ahir_Std
.io_Actual
= 0;
761 /* Start recording if neccessary */
762 if( ! iounit
->IsRecording
)
764 if( (! iounit
->FullDuplex
) && iounit
->IsPlaying
)
766 error
= AHIE_HALFDUPLEX
; // FIXIT!
769 { static const Tag tags
[] = { AHIC_Record
,TRUE
,TAG_DONE
};
770 error
= AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
775 iounit
->IsRecording
= TRUE
;
779 if(iounit
->IsRecording
)
781 AHI_ControlAudio(iounit
->AudioCtrl
,
782 AHIC_MixFreq_Query
, (IPTR
)&mixfreq
,
785 /* Initialize ahir_Frequency for the assembler record routines */
786 if(ioreq
->ahir_Frequency
&& mixfreq
)
787 ioreq
->ahir_Frequency
= ((mixfreq
<< 15) / ioreq
->ahir_Frequency
) << 1;
789 ioreq
->ahir_Frequency
= 0x00010000; // Fixed 1.0
791 AHIObtainSemaphore(&iounit
->Lock
);
793 /* Add the request to the list of readers */
794 AddTail((struct List
*) &iounit
->ReadList
,(struct Node
*) ioreq
);
796 /* Copy the current buffer contents */
797 FillReadBuffer(ioreq
, iounit
, AHIBase
);
799 AHIReleaseSemaphore(&iounit
->Lock
);
803 ioreq
->ahir_Std
.io_Error
= error
;
804 TermIO(ioreq
, AHIBase
);
809 /******************************************************************************
810 ** WriteCmd *******************************************************************
811 ******************************************************************************/
813 /****** ahi.device/CMD_WRITE ************************************************
816 * CMD_WRITE -- Write raw samples to audio output (V4)
819 * Plays the samples to the users prefered audio output.
822 * io_Device Preset by the call to OpenDevice().
823 * io_Unit Preset by the call to OpenDevice().
824 * io_Command CMD_WRITE
825 * io_Data Pointer to the buffer of samples to be played.
826 * io_Length Number of bytes to play, must be a multiple of the
827 * sample frame size (see ahir_Type).
828 * io_Offset Must be 0.
829 * ahir_Type The desired sample format, see <ahi/devices.h>.
830 * ahir_Frequency The desired sample frequency in Hertz.
831 * ahir_Volume The desired volume. The range is 0 to 0x10000, where
832 * 0 means muted and 0x10000 (== 1.0) means full volume.
833 * ahir_Position Defines the stereo balance. 0 is far left, 0x8000 is
834 * center and 0x10000 is far right.
835 * ahir_Link If non-zero, pointer to a previously sent AHIRequest
836 * which this AHIRequest will be linked to. This
837 * request will be delayed until the old one is
838 * finished (used for double buffering). Must be set
839 * to NULL if not used.
842 * io_Error 0 for success, or an error code as defined in
843 * <ahi/devices.h> and <exec/errors.h>.
844 * io_Actual If io_Error is 0, number of bytes actually
845 * played. Starting with V6, io_Actual is also valid
846 * if io_Error is not 0 (like if the request was
849 * The other fields, except io_Device, io_Unit and io_Command, are
855 * 32 bit samples (ahir_Type) is only available in V6 and later.
860 * <ahi/devices.h>, <exec/errors.h>
862 ****************************************************************************
867 WriteCmd ( struct AHIRequest
*ioreq
,
868 struct AHIBase
*AHIBase
)
870 struct AHIDevUnit
*iounit
;
873 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
875 KPrintF("CMD_WRITE\n");
878 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
880 ioreq
->ahir_Std
.io_Actual
= 0;
882 /* Start playback if neccessary */
883 if( ! iounit
->IsPlaying
)
885 if( (! iounit
->FullDuplex
) && iounit
->IsRecording
)
887 error
= AHIE_HALFDUPLEX
; // FIXIT!
890 { static const Tag tags
[] = { AHIC_Play
,TRUE
,TAG_DONE
};
891 error
= AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
896 iounit
->IsPlaying
= TRUE
;
900 ioreq
->ahir_Extras
= (IPTR
) AllocVec(sizeof(struct Extras
), MEMF_PUBLIC
|MEMF_CLEAR
);
902 if(ioreq
->ahir_Extras
== 0)
908 // Initialize the structure
909 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
910 GetExtras(ioreq
)->Sound
= AHI_NOSOUND
;
911 GetExtras(ioreq
)->VolumeScale
= 0x10000;
914 if(iounit
->IsPlaying
&& !error
)
916 // Convert length in bytes to length in samples
918 ioreq
->ahir_Std
.io_Length
/= AHI_SampleFrameSize(ioreq
->ahir_Type
);
920 NewWriter(ioreq
, iounit
, AHIBase
);
925 ioreq
->ahir_Std
.io_Error
= error
;
926 TermIO(ioreq
, AHIBase
);
931 /******************************************************************************
932 ** StartCmd *******************************************************************
933 ******************************************************************************/
935 /****** ahi.device/CMD_START ************************************************
938 * CMD_START -- start device processing (like ^Q) (V4)
941 * All CMD_WRITE's that has been sent to the device since CMD_STOP
942 * will be started at once, synchronized.
945 * io_Device Preset by the call to OpenDevice().
946 * io_Unit Preset by the call to OpenDevice().
947 * io_Command CMD_START
950 * io_Error 0 for success, or an error code as defined in
951 * <ahi/devices.h> and <exec/errors.h>.
953 * The other fields, except io_Device, io_Unit and io_Command, are
959 * Unlike most (all?) other devices, CMD_STOP and CMD_START do nest in
965 * CMD_STOP, <ahi/devices.h>, <exec/errors.h>
967 ****************************************************************************
972 StartCmd ( struct AHIRequest
*ioreq
,
973 struct AHIBase
*AHIBase
)
975 struct AHIDevUnit
*iounit
;
976 struct AHIPrivAudioCtrl
*audioctrl
;
977 struct Library
*AHIsubBase
;
979 if(AHIBase
->ahib_DebugLevel
>= AHI_DEBUG_HIGH
)
981 KPrintF("CMD_START\n");
984 iounit
= (struct AHIDevUnit
*) ioreq
->ahir_Std
.io_Unit
;
985 audioctrl
= (struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
;
987 AHIObtainSemaphore(&iounit
->Lock
);
993 if((AHIsubBase
= audioctrl
->ahiac_SubLib
))
995 if(iounit
->StopCnt
== 0)
997 struct AHIRequest
* ior
;
1000 // Boost us some, so we don't spend too much time in
1001 // audio disabled state (we could miss an interrupt!)
1002 // and disable audio interrupts.
1004 old_pri
= SetTaskPri( FindTask( NULL
), 128 );
1005 AHIsub_Disable((struct AHIAudioCtrlDrv
*) audioctrl
);
1008 // Now "start" all sounds (they won't really start until
1009 // the audio interrupts are enabled).
1011 while((ior
= (struct AHIRequest
*) RemHead(
1012 (struct List
*) &iounit
->RequestQueue
)))
1014 WriteCmd(ior
, AHIBase
);
1018 AHIsub_Enable((struct AHIAudioCtrlDrv
*) audioctrl
);
1019 SetTaskPri( FindTask( NULL
), old_pri
);
1025 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1028 AHIReleaseSemaphore(&iounit
->Lock
);
1030 TermIO(ioreq
,AHIBase
);
1034 /******************************************************************************
1035 ** FeedReaders ****************************************************************
1036 ******************************************************************************/
1038 // This function is called by DevProc or ReadCmd to scan the list of waiting
1039 // readers, and fill their buffers. When a buffer is full, the IORequest is
1043 FeedReaders ( struct AHIDevUnit
*iounit
,
1044 struct AHIBase
*AHIBase
)
1046 struct AHIRequest
*ioreq
;
1048 AHIObtainSemaphore(&iounit
->Lock
);
1050 for(ioreq
= (struct AHIRequest
*)iounit
->ReadList
.mlh_Head
;
1051 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1052 ioreq
= (struct AHIRequest
*)ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1054 FillReadBuffer(ioreq
, iounit
, AHIBase
);
1057 // Check if Reader-list is empty. If so, stop recording (after a small delay).
1059 if( ! iounit
->ReadList
.mlh_Head
->mln_Succ
)
1061 if(--iounit
->RecordOffDelay
== 0)
1062 { static const Tag tags
[] = { AHIC_Record
,FALSE
,TAG_DONE
};
1063 AHI_ControlAudioA(iounit
->AudioCtrl
, (struct TagItem
*)tags
);
1064 iounit
->IsRecording
= FALSE
;
1069 iounit
->RecordOffDelay
= 2;
1072 AHIReleaseSemaphore(&iounit
->Lock
);
1076 /******************************************************************************
1077 ** FillReadBuffer *************************************************************
1078 ******************************************************************************/
1080 // Handles a read request. Note that the request MUST be in a list, and the
1081 // list must be semaphore locked!
1084 FillReadBuffer ( struct AHIRequest
*ioreq
,
1085 struct AHIDevUnit
*iounit
,
1086 struct AHIBase
*AHIBase
)
1088 ULONG length
,length2
;
1092 if(iounit
->ValidRecord
) // Make sure we have a valid source buffer
1094 oldaddress
= ioreq
->ahir_Std
.io_Data
;
1096 length
= (ioreq
->ahir_Std
.io_Length
- ioreq
->ahir_Std
.io_Actual
)
1097 / AHI_SampleFrameSize(ioreq
->ahir_Type
);
1099 length2
= (iounit
->RecordSize
- ioreq
->ahir_Std
.io_Offset
)
1100 / AHI_SampleFrameSize(AHIST_S16S
);
1101 length2
= MultFixed(length2
, (Fixed
) ioreq
->ahir_Frequency
);
1103 if(length
<= length2
)
1113 switch (ioreq
->ahir_Type
)
1116 RecM8S(length
,ioreq
->ahir_Frequency
,
1117 iounit
->RecordBuffer
,
1118 &ioreq
->ahir_Std
.io_Offset
,
1119 &ioreq
->ahir_Std
.io_Data
);
1122 RecS8S(length
,ioreq
->ahir_Frequency
,
1123 iounit
->RecordBuffer
,
1124 &ioreq
->ahir_Std
.io_Offset
,
1125 &ioreq
->ahir_Std
.io_Data
);
1128 RecM16S(length
,ioreq
->ahir_Frequency
,
1129 iounit
->RecordBuffer
,
1130 &ioreq
->ahir_Std
.io_Offset
,
1131 &ioreq
->ahir_Std
.io_Data
);
1134 RecS16S(length
,ioreq
->ahir_Frequency
,
1135 iounit
->RecordBuffer
,
1136 &ioreq
->ahir_Std
.io_Offset
,
1137 &ioreq
->ahir_Std
.io_Data
);
1140 RecM32S(length
,ioreq
->ahir_Frequency
,
1141 iounit
->RecordBuffer
,
1142 &ioreq
->ahir_Std
.io_Offset
,
1143 &ioreq
->ahir_Std
.io_Data
);
1146 RecS32S(length
,ioreq
->ahir_Frequency
,
1147 iounit
->RecordBuffer
,
1148 &ioreq
->ahir_Std
.io_Offset
,
1149 &ioreq
->ahir_Std
.io_Data
);
1152 ioreq
->ahir_Std
.io_Error
= AHIE_BADSAMPLETYPE
;
1157 ioreq
->ahir_Std
.io_Actual
+= ((IPTR
) ioreq
->ahir_Std
.io_Data
- (IPTR
) oldaddress
);
1161 Remove((struct Node
*) ioreq
);
1162 TermIO(ioreq
, AHIBase
);
1166 ioreq
->ahir_Std
.io_Offset
= 0;
1171 ioreq
->ahir_Std
.io_Offset
= 0;
1176 /******************************************************************************
1177 ** NewWriter ******************************************************************
1178 ******************************************************************************/
1180 // This function is called by WriteCmd when a new write request comes.
1183 NewWriter ( struct AHIRequest
*ioreq
,
1184 struct AHIDevUnit
*iounit
,
1185 struct AHIBase
*AHIBase
)
1189 struct AHISampleInfo si
;
1190 struct Library
*AHIsubBase
;
1192 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1194 si
.ahisi_Type
= ioreq
->ahir_Type
;
1195 si
.ahisi_Address
= ioreq
->ahir_Std
.io_Data
;
1196 si
.ahisi_Length
= ioreq
->ahir_Std
.io_Length
;
1200 for(sound
= 0; sound
< MAXSOUNDS
; sound
++)
1202 if(iounit
->Sounds
[sound
] == SOUND_FREE
)
1204 iounit
->Sounds
[sound
] = SOUND_IN_USE
;
1209 if((sound
< MAXSOUNDS
) &&
1210 (AHI_LoadSound(sound
, AHIST_DYNAMICSAMPLE
, &si
, iounit
->AudioCtrl
)
1213 GetExtras(ioreq
)->Sound
= sound
;
1215 AHIObtainSemaphore(&iounit
->Lock
);
1217 if(ioreq
->ahir_Link
)
1219 // See if the linked request is playing, silent or waiting...
1221 if(FindNode((struct List
*) &iounit
->PlayingList
,
1222 (struct Node
*) ioreq
->ahir_Link
))
1226 else if(FindNode((struct List
*) &iounit
->SilentList
,
1227 (struct Node
*) ioreq
->ahir_Link
))
1231 else if(FindNode((struct List
*) &iounit
->WaitingList
,
1232 (struct Node
*) ioreq
->ahir_Link
))
1238 // NOTE: ahir_Link changes direction here. When the user set's it, she makes a new
1239 // request point to an old. We let the old point to the next (that's more natural,
1240 // anyway...) It the user tries to link more than one request to another, we fail.
1244 if( ! ioreq
->ahir_Link
->ahir_Link
)
1246 struct AHIRequest
*otherioreq
= ioreq
->ahir_Link
;
1248 channel
= GetExtras( otherioreq
)->Channel
;
1249 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
1251 otherioreq
->ahir_Link
= ioreq
;
1252 ioreq
->ahir_Link
= NULL
;
1253 Enqueue((struct List
*) &iounit
->WaitingList
,(struct Node
*) ioreq
);
1255 if(channel
!= NOCHANNEL
)
1257 // Attach the request to the currently playing one
1259 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1261 // Make SURE the current sound isn't already finished!
1263 if(otherioreq
->ahir_Std
.io_Command
== AHICMD_WRITTEN
)
1265 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1267 // OOPS! It's finished! Undo...
1268 Remove((struct Node
*) ioreq
);
1270 // Start sound as if it wasn't delayed (see below);
1271 AddWriter(ioreq
, iounit
, AHIBase
);
1275 if( iounit
->Voices
[channel
].Flags
& VF_STARTED
)
1277 // There is a sound already playing. Attach this sound
1278 // after the current.
1280 iounit
->Voices
[channel
].QueuedRequest
= ioreq
;
1281 iounit
->Voices
[channel
].NextOffset
= PLAY
;
1282 iounit
->Voices
[channel
].NextRequest
= NULL
;
1284 AHI_Play(iounit
->AudioCtrl
,
1285 AHIP_BeginChannel
, channel
,
1286 AHIP_LoopFreq
, ioreq
->ahir_Frequency
,
1287 AHIP_LoopVol
, (ULONG
) (((long long) ioreq
->ahir_Volume
*
1288 GetExtras(ioreq
)->VolumeScale
) >> 16),
1289 AHIP_LoopPan
, ioreq
->ahir_Position
,
1290 AHIP_LoopSound
, GetExtras(ioreq
)->Sound
,
1291 AHIP_LoopOffset
, ioreq
->ahir_Std
.io_Actual
,
1292 AHIP_LoopLength
, ioreq
->ahir_Std
.io_Length
-
1293 ioreq
->ahir_Std
.io_Actual
,
1299 // The current sound has not yet been started, and the loop
1300 // part is not set either. Let the SoundFunc() handle the
1303 iounit
->Voices
[channel
].NextSound
= GetExtras( ioreq
)->Sound
;
1304 iounit
->Voices
[channel
].NextVolume
= ioreq
->ahir_Volume
;
1305 iounit
->Voices
[channel
].NextPan
= ioreq
->ahir_Position
;
1306 iounit
->Voices
[channel
].NextFrequency
= ioreq
->ahir_Frequency
;
1307 iounit
->Voices
[channel
].NextOffset
= ioreq
->ahir_Std
.io_Actual
;
1308 iounit
->Voices
[channel
].NextLength
= ioreq
->ahir_Std
.io_Length
-
1309 ioreq
->ahir_Std
.io_Actual
;
1310 iounit
->Voices
[channel
].NextRequest
= ioreq
;
1313 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1317 else // She tried to add more than one request to another one
1319 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1320 TermIO(ioreq
, AHIBase
);
1323 else // Sound is not delayed
1325 ioreq
->ahir_Link
=NULL
;
1326 AddWriter(ioreq
, iounit
, AHIBase
);
1329 AHIReleaseSemaphore(&iounit
->Lock
);
1331 else // No free sound found, or sound failed to load
1333 if( sound
< MAXSOUNDS
)
1335 // Clean up and set error code
1336 iounit
->Sounds
[sound
] = SOUND_FREE
;
1337 ioreq
->ahir_Std
.io_Error
= AHIE_BADSAMPLETYPE
;
1341 ioreq
->ahir_Std
.io_Error
= AHIE_UNKNOWN
;
1344 TermIO(ioreq
, AHIBase
);
1349 /******************************************************************************
1350 ** AddWriter ******************************************************************
1351 ******************************************************************************/
1353 // This function is called by NewWriter and RethinkPlayers. It adds an
1354 // initialized request to either the playing or waiting list, and starts
1355 // the sound it if possible
1358 AddWriter ( struct AHIRequest
*ioreq
,
1359 struct AHIDevUnit
*iounit
,
1360 struct AHIBase
*AHIBase
)
1364 // Search for a free channel, and use if found
1366 for(channel
= 0; channel
< iounit
->Channels
; channel
++)
1368 if(iounit
->Voices
[channel
].NextOffset
== (ULONG
) FREE
)
1370 Enqueue((struct List
*) &iounit
->PlayingList
,(struct Node
*) ioreq
);
1371 UpdateMasterVolume( iounit
, AHIBase
);
1372 PlayRequest(channel
, ioreq
, iounit
, AHIBase
);
1378 if(channel
== iounit
->Channels
)
1380 struct AHIRequest
*ioreq2
;
1382 // No free channel found. Check if we can kick the last one out...
1383 // The last one, if it exists, has lowest priority.
1385 // Note that it is quite possible that there is no request in the list,
1386 // even though there was no free sound channel. This can happen if
1387 // AbortIO() has been called, and marked the channel MUTE, but the
1388 // SoundFunc() has yet not been called to move the chennel into the
1391 ioreq2
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_TailPred
;
1393 if( ( iounit
->PlayingList
.mlh_Head
->mln_Succ
!= NULL
) &&
1394 ( ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Pri
>
1395 ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Pri
) )
1397 // Let's steal his place!
1399 RemTail((struct List
*) &iounit
->PlayingList
);
1400 channel
= GetExtras(ioreq2
)->Channel
;
1401 GetExtras(ioreq2
)->Channel
= NOCHANNEL
;
1402 Enqueue((struct List
*) &iounit
->SilentList
,(struct Node
*) ioreq2
);
1403 Enqueue((struct List
*) &iounit
->PlayingList
,(struct Node
*) ioreq
);
1404 PlayRequest(channel
, ioreq
, iounit
, AHIBase
);
1408 // Let's be quiet for a while.
1409 GetExtras(ioreq
)->Channel
= NOCHANNEL
;
1410 Enqueue((struct List
*) &iounit
->SilentList
,(struct Node
*) ioreq
);
1416 /******************************************************************************
1417 ** PlayRequest ****************************************************************
1418 ******************************************************************************/
1420 // This begins to play an AHIRequest (starting at sample io_Actual).
1423 PlayRequest ( int channel
,
1424 struct AHIRequest
*ioreq
,
1425 struct AHIDevUnit
*iounit
,
1426 struct AHIBase
*AHIBase
)
1428 struct Library
*AHIsubBase
;
1430 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1434 GetExtras(ioreq
)->Channel
= channel
;
1436 if(ioreq
->ahir_Link
)
1438 struct Voice
*v
= &iounit
->Voices
[channel
];
1439 struct AHIRequest
*r
= ioreq
->ahir_Link
;
1441 v
->NextSound
= GetExtras(r
)->Sound
;
1442 v
->NextVolume
= r
->ahir_Volume
;
1443 v
->NextPan
= r
->ahir_Position
;
1444 v
->NextFrequency
= r
->ahir_Frequency
;
1445 v
->NextOffset
= r
->ahir_Std
.io_Actual
;
1446 v
->NextLength
= r
->ahir_Std
.io_Length
1447 - r
->ahir_Std
.io_Actual
;
1452 iounit
->Voices
[channel
].NextOffset
= PLAY
;
1453 iounit
->Voices
[channel
].NextRequest
= NULL
;
1456 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1458 iounit
->Voices
[channel
].PlayingRequest
= NULL
;
1459 iounit
->Voices
[channel
].QueuedRequest
= ioreq
;
1460 iounit
->Voices
[channel
].Flags
&= ~VF_STARTED
;
1462 AHI_Play(iounit
->AudioCtrl
,
1463 AHIP_BeginChannel
, channel
,
1464 AHIP_Freq
, ioreq
->ahir_Frequency
,
1465 AHIP_Vol
, (ULONG
) (((long long) ioreq
->ahir_Volume
*
1466 GetExtras(ioreq
)->VolumeScale
) >> 16),
1467 AHIP_Pan
, ioreq
->ahir_Position
,
1468 AHIP_Sound
, GetExtras(ioreq
)->Sound
,
1469 AHIP_Offset
, ioreq
->ahir_Std
.io_Actual
,
1470 AHIP_Length
, ioreq
->ahir_Std
.io_Length
-ioreq
->ahir_Std
.io_Actual
,
1474 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1477 // This is a workaround for a race condition.
1478 // The problem can occur if a delayed request follows immediately after
1479 // this one, before the sample interrupt routine has been called, and
1480 // overwrites QueuedRequest. The result is that this sound is never
1481 // marked as finished, and the application will wait forever on the
1482 // IO Request. Quite ugly, no?
1484 Wait(1L << iounit
->SampleSignal
);
1486 // Set signal again...
1487 Signal((struct Task
*) iounit
->Master
, (1L << iounit
->SampleSignal
));
1489 // while(((volatile UBYTE) (iounit->Voices[channel].Flags) & VF_STARTED) == 0);
1494 /******************************************************************************
1495 ** RethinkPlayers *************************************************************
1496 ******************************************************************************/
1498 // When a playing sample has reached the end, this function is called.
1499 // It finds and terminates all finished requests, and moves their 'children'
1500 // from the waiting list.
1501 // Then it tries to restart all silent sounds.
1504 RethinkPlayers ( struct AHIDevUnit
*iounit
,
1505 struct AHIBase
*AHIBase
)
1507 struct MinList templist
;
1508 struct AHIRequest
*ioreq
;
1510 NewList((struct List
*) &templist
);
1512 AHIObtainSemaphore(&iounit
->Lock
);
1514 RemPlayers((struct List
*) &iounit
->PlayingList
, iounit
, AHIBase
);
1515 RemPlayers((struct List
*) &iounit
->SilentList
, iounit
, AHIBase
);
1517 // Move all silent requests to our temporary list
1519 while((ioreq
= (struct AHIRequest
*) RemHead((struct List
*) &iounit
->SilentList
)))
1521 AddTail((struct List
*) &templist
, (struct Node
*) ioreq
);
1524 // And add them back...
1525 while((ioreq
= (struct AHIRequest
*) RemHead((struct List
*) &templist
)))
1527 AddWriter(ioreq
, iounit
, AHIBase
);
1530 AHIReleaseSemaphore(&iounit
->Lock
);
1534 /******************************************************************************
1535 ** RemPlayers *****************************************************************
1536 ******************************************************************************/
1538 // Removes all finished play requests from a list. The lists must be locked!
1541 RemPlayers ( struct List
*list
,
1542 struct AHIDevUnit
*iounit
,
1543 struct AHIBase
*AHIBase
)
1545 struct AHIRequest
*ioreq
, *node
;
1547 node
= (struct AHIRequest
*) list
->lh_Head
;
1549 while(node
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1552 node
= (struct AHIRequest
*) node
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1554 if(ioreq
->ahir_Std
.io_Command
== AHICMD_WRITTEN
)
1556 Remove((struct Node
*) ioreq
);
1558 if(ioreq
->ahir_Link
)
1560 // Move the attached one to the list
1561 Remove((struct Node
*) ioreq
->ahir_Link
);
1563 // FIXME: 2002-10-13: I have a bug report that claims
1564 // GetExtras(ioreq->ahir_Link) returns NULL here. How did that
1567 // FIXED: 2005-09-26: The app AbortIO()'ed a request that was
1568 // attached to another request using ahir_Link. When we then
1569 // arrived here, ioreq->ahir_Link would point to a terminated
1570 // request and possibly even deallocated memory. Now AbortIO()
1571 // clears ahir_Link.
1573 GetExtras(ioreq
->ahir_Link
)->Channel
= GetExtras(ioreq
)->Channel
;
1574 Enqueue(list
, (struct Node
*) ioreq
->ahir_Link
);
1575 // We have to go through the whole procedure again, in case
1576 // the child is finished, too.
1577 node
= (struct AHIRequest
*) list
->lh_Head
;
1580 ioreq
->ahir_Std
.io_Error
= AHIE_OK
;
1581 ioreq
->ahir_Std
.io_Command
= CMD_WRITE
;
1582 ioreq
->ahir_Std
.io_Actual
= ioreq
->ahir_Std
.io_Length
;
1583 TermIO(ioreq
, AHIBase
);
1589 /******************************************************************************
1590 ** UpdateSilentPlayers ********************************************************
1591 ******************************************************************************/
1593 // Updates the io_Actual field of all silent requests. The lists must be locked.
1594 // This function is either called from the interrupt or DevProc.
1597 UpdateSilentPlayers ( struct AHIDevUnit
*iounit
,
1598 struct AHIBase
*AHIBase
)
1600 struct AHIRequest
*ioreq
;
1602 for(ioreq
= (struct AHIRequest
*)iounit
->SilentList
.mlh_Head
;
1603 ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1604 ioreq
= (struct AHIRequest
*)ioreq
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1608 ioreq
->ahir_Std
.io_Actual
+= ((ioreq
->ahir_Frequency
<< 14) / PLAYERFREQ
) >> 14;
1610 // Check if the whole sample has been "played"
1611 if(ioreq
->ahir_Std
.io_Actual
>= ioreq
->ahir_Std
.io_Length
)
1613 // Mark request as finished
1614 ioreq
->ahir_Std
.io_Command
= AHICMD_WRITTEN
;
1616 // Make us call Rethinkplayers later
1617 Signal((struct Task
*) iounit
->Master
, (1L << iounit
->SampleSignal
));
1623 /******************************************************************************
1624 ** UpdateMasterVolume ********************************************************
1625 ******************************************************************************/
1627 // Updated the master volume so all sounds are played as loud as possible
1628 // without risking clipping.
1629 // This function is called from AddWriter() and TermIO().
1631 static void UpdateMasterVolume( struct AHIDevUnit
*iounit
,
1632 struct AHIBase
*AHIBase
)
1634 struct AHIRequest
* ioreq1
;
1635 struct AHIRequest
* ioreq2
;
1636 struct Library
* AHIsubBase
;
1638 AHIsubBase
= ((struct AHIPrivAudioCtrl
*) iounit
->AudioCtrl
)->ahiac_SubLib
;
1640 AHIObtainSemaphore(&iounit
->Lock
);
1642 for(ioreq1
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1643 ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1644 ioreq1
= (struct AHIRequest
*) ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1646 ULONG id
= ioreq1
->ahir_Private
[1];
1648 LONG minscale
= 0x10000;
1650 /* KPrintF( "Checking id %08lx on request %08lx... ", id, ioreq1 ); */
1652 for(ioreq2
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1653 ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1654 ioreq2
= (struct AHIRequest
*) ioreq2
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1656 if( ioreq2
->ahir_Private
[1] == id
)
1660 if( GetExtras(ioreq2
) && GetExtras(ioreq2
)->VolumeScale
< minscale
)
1662 minscale
= GetExtras(ioreq2
)->VolumeScale
;
1667 if( minscale
> 0x10000 / c
)
1669 minscale
= 0x10000 / c
;
1672 switch( AHIBase
->ahib_ScaleMode
)
1674 case AHI_SCALE_DYNAMIC_SAFE
:
1675 if( GetExtras(ioreq1
)->VolumeScale
> minscale
)
1677 GetExtras(ioreq1
)->VolumeScale
= minscale
;
1681 case AHI_SCALE_FIXED_SAFE
:
1682 case AHI_SCALE_FIXED_0_DB
:
1683 case AHI_SCALE_FIXED_3_DB
:
1684 case AHI_SCALE_FIXED_6_DB
:
1685 GetExtras(ioreq1
)->VolumeScale
= 0x10000;
1689 /* KPrintF( "%ld requests, maxdiv = %ld -> Vol %05lx => %05lx\n", */
1690 /* c, maxdiv, ioreq1->ahir_Volume, */
1691 /* ioreq1->ahir_Volume / GetExtras(ioreq1)->VolumeDiv ); */
1694 // Now update the volume as quickly as possible ...
1696 AHIsub_Disable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1698 for(ioreq1
= (struct AHIRequest
*) iounit
->PlayingList
.mlh_Head
;
1699 ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
;
1700 ioreq1
= (struct AHIRequest
*) ioreq1
->ahir_Std
.io_Message
.mn_Node
.ln_Succ
)
1702 if( GetExtras(ioreq1
)->Channel
!= NOCHANNEL
)
1704 AHI_SetVol( GetExtras(ioreq1
)->Channel
,
1705 (ULONG
) (((long long) ioreq1
->ahir_Volume
*
1706 GetExtras(ioreq1
)->VolumeScale
) >> 16),
1707 ioreq1
->ahir_Position
,
1713 AHIsub_Enable((struct AHIAudioCtrlDrv
*) iounit
->AudioCtrl
);
1715 // And now the real master volume ...
1717 if( iounit
->Unit
.unit_OpenCnt
== 0 )
1719 struct AHIEffMasterVolume vol
= {
1720 AHIET_MASTERVOLUME
| AHIET_CANCEL
,
1724 AHI_SetEffect( &vol
, iounit
->AudioCtrl
);
1728 struct AHIEffMasterVolume vol
= {
1733 switch( AHIBase
->ahib_ScaleMode
)
1735 case AHI_SCALE_FIXED_SAFE
:
1736 vol
.ahiemv_Volume
= 0x10000;
1739 case AHI_SCALE_DYNAMIC_SAFE
:
1740 vol
.ahiemv_Volume
= iounit
->Channels
* 0x10000 / iounit
->Unit
.unit_OpenCnt
;
1743 case AHI_SCALE_FIXED_0_DB
:
1744 vol
.ahiemv_Volume
= iounit
->Channels
* 0x10000;
1747 case AHI_SCALE_FIXED_3_DB
:
1748 vol
.ahiemv_Volume
= iounit
->Channels
* 0xB505;
1751 case AHI_SCALE_FIXED_6_DB
:
1752 vol
.ahiemv_Volume
= iounit
->Channels
* 0x8000;
1756 if( iounit
->PseudoStereo
)
1758 vol
.ahiemv_Volume
= vol
.ahiemv_Volume
/ 2;
1761 AHI_SetEffect( &vol
, iounit
->AudioCtrl
);
1764 AHIReleaseSemaphore(&iounit
->Lock
);