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