Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / devs / AHI / Device / devcommands.c
blob10fdd9865c78eab56999e19152ef639ac3bf73f6
1 /*
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,
18 MA 02139, USA.
21 #include <config.h>
23 #include <dos/dos.h>
24 #include <exec/errors.h>
25 #include <exec/tasks.h>
26 #include <exec/io.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>
33 #define __NOLIBBASE__
34 #define __NOGLOBALIFACE__
35 #include <proto/ahi.h>
36 #undef __NOLIBBASE__
37 #undef __NOGLOBALIFACE__
38 #include <proto/ahi_sub.h>
40 #include <math.h>
42 #include "ahi_def.h"
43 #include "debug.h"
44 #include "misc.h"
45 #include "devcommands.h"
46 #include "device.h"
47 #include "devsupp.h"
50 #ifdef __AMIGAOS4__
51 #define IAHIsub ((struct AHIPrivAudioCtrl *) iounit->AudioCtrl)->ahiac_IAHIsub
52 #endif
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()
79 // is called.
81 void
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)
95 // Immediate commands
96 case NSCMD_DEVICEQUERY:
97 case CMD_STOP:
98 case CMD_FLUSH:
99 PerformIO(ioreq,AHIBase);
100 break;
102 // Queued commands
103 case CMD_RESET:
104 case CMD_READ:
105 case CMD_WRITE:
106 case CMD_START:
107 ioreq->ahir_Std.io_Flags &= ~IOF_QUICK;
108 PutMsg(&ioreq->ahir_Std.io_Unit->unit_MsgPort,&ioreq->ahir_Std.io_Message);
109 break;
111 // Unknown commands
112 default:
113 ioreq->ahir_Std.io_Error = IOERR_NOCMD;
114 TermIO(ioreq,AHIBase);
115 break;
120 /******************************************************************************
121 ** AbortIO ********************************************************************
122 ******************************************************************************/
124 // This function is called by the system each time exec.library/AbortIO()
125 // is called.
127 ULONG
128 _DevAbortIO( struct AHIRequest* ioreq,
129 struct AHIBase* AHIBase )
131 ULONG rc = 0;
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)
148 case CMD_READ:
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);
155 break;
157 case CMD_WRITE:
158 case AHICMD_WRITTEN:
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;
166 while(ioreq)
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
172 // longer valid.
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;
180 goto cleared;
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;
189 goto cleared;
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;
198 goto cleared;
201 cleared:
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)
210 ->ahiac_SubLib;
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);
228 else
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);
243 ioreq = nextreq;
246 break;
248 default:
249 rc = IOERR_NOCMD;
250 break;
254 AHIReleaseSemaphore(&iounit->Lock);
256 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_LOW)
258 KPrintF("=>%ld\n",rc);
261 return rc;
265 /******************************************************************************
266 ** TermIO *********************************************************************
267 ******************************************************************************/
269 // This functions returns an IO request back to the sender.
271 static void
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;
306 FreeVec( extras );
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 ******************************************************************************/
325 void
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);
341 break;
343 case CMD_RESET:
344 ResetCmd(ioreq, AHIBase);
345 break;
347 case CMD_READ:
348 ReadCmd(ioreq, AHIBase);
349 break;
351 case CMD_WRITE:
352 AHIObtainSemaphore(&iounit->Lock);
354 if(iounit->StopCnt)
356 AddTail((struct List *) &iounit->RequestQueue,(struct Node *) ioreq);
358 else
360 WriteCmd(ioreq, AHIBase);
363 AHIReleaseSemaphore(&iounit->Lock);
364 break;
366 case CMD_STOP:
367 StopCmd(ioreq, AHIBase);
368 break;
370 case CMD_START:
371 StartCmd(ioreq, AHIBase);
372 break;
374 case CMD_FLUSH:
375 FlushCmd(ioreq, AHIBase);
376 break;
378 default:
379 ioreq->ahir_Std.io_Error = IOERR_NOCMD;
380 TermIO(ioreq, AHIBase);
381 break;
386 /******************************************************************************
387 ** Devicequery ****************************************************************
388 ******************************************************************************/
390 /****** ahi.device/NSCMD_DEVICEQUERY ***************************************
392 * NAME
393 * NSCMD_DEVICEQUERY -- Query the device for its capabilities (V4)
395 * FUNCTION
396 * Fills an initialized NSDeviceQueryResult structure with
397 * information about the device.
399 * IO REQUEST INPUT
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
408 * fields as well.
409 * io_Length Size of the NSDeviceQueryResult structure.
411 * IO REQUEST RESULT
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
420 * trashed.
422 * EXAMPLE
424 * NOTES
426 * BUGS
428 * SEE ALSO
429 * <ahi/devices.h>, <exec/errors.h>
431 ****************************************************************************
435 static UWORD
436 commandlist[] =
438 NSCMD_DEVICEQUERY,
439 CMD_RESET,
440 CMD_READ,
441 CMD_WRITE,
442 CMD_STOP,
443 CMD_START,
444 CMD_FLUSH,
448 static void
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 ************************************************
479 * NAME
480 * CMD_STOP -- stop device processing (like ^S) (V4)
482 * FUNCTION
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.
487 * IO REQUEST INPUT
488 * io_Device Preset by the call to OpenDevice().
489 * io_Unit Preset by the call to OpenDevice().
490 * io_Command CMD_STOP
492 * IO REQUEST RESULT
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
497 * trashed.
499 * EXAMPLE
501 * NOTES
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
507 * ahi.device.
509 * BUGS
511 * SEE ALSO
512 * CMD_START, <ahi/devices.h>, <exec/errors.h>
514 ****************************************************************************
518 static void
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);
533 iounit->StopCnt++;
535 AHIReleaseSemaphore(&iounit->Lock);
537 TermIO(ioreq,AHIBase);
541 /******************************************************************************
542 ** FlushCmd *******************************************************************
543 ******************************************************************************/
545 /****** ahi.device/CMD_FLUSH ************************************************
547 * NAME
548 * CMD_FLUSH -- Cancel all I/O requests (V4)
550 * FUNCTION
551 * Aborts ALL current requests, both active and waiting, even
552 * other programs requests!
554 * IO REQUEST INPUT
555 * io_Device Preset by the call to OpenDevice().
556 * io_Unit Preset by the call to OpenDevice().
557 * io_Command CMD_FLUSH
559 * IO REQUEST RESULT
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
563 * flushed.
565 * The other fields, except io_Device, io_Unit and io_Command, are
566 * trashed.
568 * EXAMPLE
570 * NOTES
571 * This command should only be used in very rare cases, like AHI
572 * system utilities. Never use this command in an application.
574 * BUGS
576 * SEE ALSO
577 * CMD_RESET, <ahi/devices.h>, <exec/errors.h>
579 ****************************************************************************
583 static void
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 ************************************************
634 * NAME
635 * CMD_RESET -- Restore device to a known state (V4)
637 * FUNCTION
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
643 * IO REQUEST INPUT
644 * io_Device Preset by the call to OpenDevice().
645 * io_Unit Preset by the call to OpenDevice().
646 * io_Command CMD_RESET
648 * IO REQUEST RESULT
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
653 * trashed.
655 * EXAMPLE
657 * NOTES
658 * This command should only be used in very rare cases, like AHI
659 * system utilities. Never use this command in an application.
661 * BUGS
663 * SEE ALSO
664 * CMD_FLUSH, <ahi/devices.h>, <exec/errors.h>
666 ****************************************************************************
670 static void
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 *************************************************
700 * NAME
701 * CMD_READ -- Read raw samples from audio input (V4)
703 * FUNCTION
704 * Reads samples from the users prefered input to memory. The sample
705 * format and frequency will be converted on the fly.
707 * IO REQUEST INPUT
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
715 * a delay.
716 * ahir_Type The desired sample format, see <ahi/devices.h>.
717 * ahir_Frequency The desired sample frequency in Hertz.
719 * IO REQUEST RESULT
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
725 * was aborted).
726 * io_Offset Updated to be used as input next time.
728 * The other fields, except io_Device, io_Unit and io_Command, are
729 * trashed.
731 * EXAMPLE
733 * NOTES
734 * It's only possible to read signed mono or stereo samples.
736 * BUGS
738 * SEE ALSO
739 * <ahi/devices.h>, <exec/errors.h>
741 ****************************************************************************
745 static void
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!
768 else
769 { static const Tag tags[] = { AHIC_Record,TRUE,TAG_DONE };
770 error = AHI_ControlAudioA(iounit->AudioCtrl, (struct TagItem *)tags);
773 if( ! error)
775 iounit->IsRecording = TRUE;
779 if(iounit->IsRecording)
781 AHI_ControlAudio(iounit->AudioCtrl,
782 AHIC_MixFreq_Query, (IPTR)&mixfreq,
783 TAG_DONE);
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;
788 else
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);
801 else
803 ioreq->ahir_Std.io_Error = error;
804 TermIO(ioreq, AHIBase);
809 /******************************************************************************
810 ** WriteCmd *******************************************************************
811 ******************************************************************************/
813 /****** ahi.device/CMD_WRITE ************************************************
815 * NAME
816 * CMD_WRITE -- Write raw samples to audio output (V4)
818 * FUNCTION
819 * Plays the samples to the users prefered audio output.
821 * IO REQUEST INPUT
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.
841 * IO REQUEST RESULT
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
847 * aborted).
849 * The other fields, except io_Device, io_Unit and io_Command, are
850 * trashed.
852 * EXAMPLE
854 * NOTES
855 * 32 bit samples (ahir_Type) is only available in V6 and later.
857 * BUGS
859 * SEE ALSO
860 * <ahi/devices.h>, <exec/errors.h>
862 ****************************************************************************
866 static void
867 WriteCmd ( struct AHIRequest *ioreq,
868 struct AHIBase *AHIBase )
870 struct AHIDevUnit *iounit;
871 ULONG error = 0;
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!
889 else
890 { static const Tag tags[] = { AHIC_Play,TRUE,TAG_DONE };
891 error = AHI_ControlAudioA(iounit->AudioCtrl, (struct TagItem *)tags);
894 if( ! error)
896 iounit->IsPlaying = TRUE;
900 ioreq->ahir_Extras = (IPTR) AllocVec(sizeof(struct Extras), MEMF_PUBLIC|MEMF_CLEAR);
902 if(ioreq->ahir_Extras == 0)
904 error = AHIE_NOMEM;
906 else
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);
923 if(error)
925 ioreq->ahir_Std.io_Error = error;
926 TermIO(ioreq, AHIBase);
931 /******************************************************************************
932 ** StartCmd *******************************************************************
933 ******************************************************************************/
935 /****** ahi.device/CMD_START ************************************************
937 * NAME
938 * CMD_START -- start device processing (like ^Q) (V4)
940 * FUNCTION
941 * All CMD_WRITE's that has been sent to the device since CMD_STOP
942 * will be started at once, synchronized.
944 * IO REQUEST INPUT
945 * io_Device Preset by the call to OpenDevice().
946 * io_Unit Preset by the call to OpenDevice().
947 * io_Command CMD_START
949 * IO REQUEST RESULT
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
954 * trashed.
956 * EXAMPLE
958 * NOTES
959 * Unlike most (all?) other devices, CMD_STOP and CMD_START do nest in
960 * ahi.device.
962 * BUGS
964 * SEE ALSO
965 * CMD_STOP, <ahi/devices.h>, <exec/errors.h>
967 ****************************************************************************
971 static void
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);
989 if(iounit->StopCnt)
991 iounit->StopCnt--;
993 if((AHIsubBase = audioctrl->ahiac_SubLib))
995 if(iounit->StopCnt == 0)
997 struct AHIRequest* ior;
998 LONG old_pri;
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);
1006 //Disable();
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);
1017 //Enable();
1018 AHIsub_Enable((struct AHIAudioCtrlDrv *) audioctrl);
1019 SetTaskPri( FindTask( NULL ), old_pri );
1023 else
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
1040 // terminated.
1042 void
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;
1067 else
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!
1083 static void
1084 FillReadBuffer ( struct AHIRequest *ioreq,
1085 struct AHIDevUnit *iounit,
1086 struct AHIBase *AHIBase )
1088 ULONG length,length2;
1089 APTR oldaddress;
1090 BOOL remove;
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)
1105 remove=TRUE;
1107 else
1109 length = length2;
1110 remove = FALSE;
1113 switch (ioreq->ahir_Type)
1115 case AHIST_M8S:
1116 RecM8S(length,ioreq->ahir_Frequency,
1117 iounit->RecordBuffer,
1118 &ioreq->ahir_Std.io_Offset,
1119 &ioreq->ahir_Std.io_Data);
1120 break;
1121 case AHIST_S8S:
1122 RecS8S(length,ioreq->ahir_Frequency,
1123 iounit->RecordBuffer,
1124 &ioreq->ahir_Std.io_Offset,
1125 &ioreq->ahir_Std.io_Data);
1126 break;
1127 case AHIST_M16S:
1128 RecM16S(length,ioreq->ahir_Frequency,
1129 iounit->RecordBuffer,
1130 &ioreq->ahir_Std.io_Offset,
1131 &ioreq->ahir_Std.io_Data);
1132 break;
1133 case AHIST_S16S:
1134 RecS16S(length,ioreq->ahir_Frequency,
1135 iounit->RecordBuffer,
1136 &ioreq->ahir_Std.io_Offset,
1137 &ioreq->ahir_Std.io_Data);
1138 break;
1139 case AHIST_M32S:
1140 RecM32S(length,ioreq->ahir_Frequency,
1141 iounit->RecordBuffer,
1142 &ioreq->ahir_Std.io_Offset,
1143 &ioreq->ahir_Std.io_Data);
1144 break;
1145 case AHIST_S32S:
1146 RecS32S(length,ioreq->ahir_Frequency,
1147 iounit->RecordBuffer,
1148 &ioreq->ahir_Std.io_Offset,
1149 &ioreq->ahir_Std.io_Data);
1150 break;
1151 default:
1152 ioreq->ahir_Std.io_Error = AHIE_BADSAMPLETYPE;
1153 remove = TRUE;
1154 break;
1157 ioreq->ahir_Std.io_Actual += ((IPTR) ioreq->ahir_Std.io_Data - (IPTR) oldaddress);
1159 if(remove)
1161 Remove((struct Node *) ioreq);
1162 TermIO(ioreq, AHIBase);
1164 else
1166 ioreq->ahir_Std.io_Offset = 0;
1169 else
1171 ioreq->ahir_Std.io_Offset = 0;
1176 /******************************************************************************
1177 ** NewWriter ******************************************************************
1178 ******************************************************************************/
1180 // This function is called by WriteCmd when a new write request comes.
1182 static void
1183 NewWriter ( struct AHIRequest *ioreq,
1184 struct AHIDevUnit *iounit,
1185 struct AHIBase *AHIBase )
1187 int channel, sound;
1188 BOOL delay = FALSE;
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;
1198 // Load the sound
1200 for(sound = 0; sound < MAXSOUNDS; sound++)
1202 if(iounit->Sounds[sound] == SOUND_FREE)
1204 iounit->Sounds[sound] = SOUND_IN_USE;
1205 break;
1209 if((sound < MAXSOUNDS) &&
1210 (AHI_LoadSound(sound, AHIST_DYNAMICSAMPLE, &si, iounit->AudioCtrl)
1211 == AHIE_OK)) {
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))
1224 delay = TRUE;
1226 else if(FindNode((struct List *) &iounit->SilentList,
1227 (struct Node *) ioreq->ahir_Link))
1229 delay = TRUE;
1231 else if(FindNode((struct List *) &iounit->WaitingList,
1232 (struct Node *) ioreq->ahir_Link))
1234 delay = TRUE;
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.
1242 if(delay)
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);
1273 else
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,
1294 AHIP_EndChannel, 0,
1295 TAG_DONE);
1297 else
1299 // The current sound has not yet been started, and the loop
1300 // part is not set either. Let the SoundFunc() handle the
1301 // attaching.
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;
1339 else
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
1357 static void
1358 AddWriter ( struct AHIRequest *ioreq,
1359 struct AHIDevUnit *iounit,
1360 struct AHIBase *AHIBase )
1362 int channel;
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);
1373 break;
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
1389 // FREE state.
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);
1406 else
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).
1422 static void
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;
1432 // Start the sound
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;
1448 v->NextRequest = r;
1450 else
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,
1471 AHIP_EndChannel, 0,
1472 TAG_DONE);
1474 AHIsub_Enable((struct AHIAudioCtrlDrv *) iounit->AudioCtrl);
1476 #if 0
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);
1490 #endif
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.
1503 void
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!
1540 static void
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)
1551 ioreq = node;
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
1565 // happen??
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.
1596 void
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)
1607 // Update io_Actual
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];
1647 int c = 0;
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 )
1658 ++c;
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;
1679 break;
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;
1686 break;
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,
1708 iounit->AudioCtrl,
1709 AHISF_IMM );
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,
1721 0x10000
1724 AHI_SetEffect( &vol, iounit->AudioCtrl );
1726 else
1728 struct AHIEffMasterVolume vol = {
1729 AHIET_MASTERVOLUME,
1730 0x10000
1733 switch( AHIBase->ahib_ScaleMode )
1735 case AHI_SCALE_FIXED_SAFE:
1736 vol.ahiemv_Volume = 0x10000;
1737 break;
1739 case AHI_SCALE_DYNAMIC_SAFE:
1740 vol.ahiemv_Volume = iounit->Channels * 0x10000 / iounit->Unit.unit_OpenCnt;
1741 break;
1743 case AHI_SCALE_FIXED_0_DB:
1744 vol.ahiemv_Volume = iounit->Channels * 0x10000;
1745 break;
1747 case AHI_SCALE_FIXED_3_DB:
1748 vol.ahiemv_Volume = iounit->Channels * 0xB505;
1749 break;
1751 case AHI_SCALE_FIXED_6_DB:
1752 vol.ahiemv_Volume = iounit->Channels * 0x8000;
1753 break;
1756 if( iounit->PseudoStereo )
1758 vol.ahiemv_Volume = vol.ahiemv_Volume / 2;
1761 AHI_SetEffect( &vol, iounit->AudioCtrl );
1764 AHIReleaseSemaphore(&iounit->Lock);