home *** CD-ROM | disk | FTP | other *** search
- /* audio_soft.c */
-
- /* $Author: Espie $
- * $Date: 91/05/16 15:04:36 $
- * $Revision: 1.22 $
- * $Log: audio_soft.c,v $
- * Revision 1.22 91/05/16 15:04:36 Espie
- * *** empty log message ***
- *
- * Revision 1.21 91/05/12 19:56:53 Espie
- * Added some logic so that play can be triggered by the availability
- * of a song now.
- *
- * Revision 1.20 91/05/12 16:00:59 Espie
- * Tells the interface when we own the audio.device and when not.
- *
- * Revision 1.19 91/05/07 12:14:13 Espie
- * *** empty log message ***
- *
- * Revision 1.18 91/05/06 15:14:12 Espie
- * Added a measure of real-life error checking.
- *
- * Revision 1.17 91/05/05 04:00:05 Espie
- * Suppressed recursive calls.
- *
- * Revision 1.16 91/05/02 23:29:22 Espie
- * Checked the automaton. Now reliable.
- * There definitely seems to be a bug in the audio.device...
- *
- * Revision 1.15 91/05/02 11:19:42 Espie
- * Checked out the new automaton. Still needs some error checking.
- *
- * Revision 1.14 91/05/02 01:29:49 Espie
- * The automaton is now implemented as an automaton, instead
- * of a program calling the events handling from time to time.
- *
- * Revision 1.13 91/04/30 00:35:41 Espie
- * Stable version III.
- *
- * Revision 1.12 91/04/29 23:55:36 Espie
- * Cleaned up the allocation key bunch.
- * This version exhibits a small problem with the OS.
- * When there are too many openers around, AbortIO() will
- * sometimes wait for something to happen...
- *
- * Revision 1.11 91/04/29 15:03:22 Espie
- * Corrected a small problem: safeabort() should be called BEFORE
- * closing the audio.device.
- *
- * Revision 1.10 91/04/29 13:02:53 Espie
- * Completely clean audio_soft.
- * (Except maybe for the request duplication).
- *
- * Revision 1.9 91/04/29 03:53:39 Espie
- * Encapsulating more and more, not yet ready.
- *
- * Revision 1.8 91/04/29 02:22:50 Espie
- * Suppressed critical sections, should be cleaner now,
- * and easier to debug.
- *
- * Revision 1.7 91/04/28 20:35:59 Espie
- * Small changes.
- *
- * Revision 1.6 91/04/27 16:45:23 Espie
- * Moved interrupt disabling to audio_hard.
- *
- * Revision 1.5 91/04/27 04:01:28 Espie
- *
- * NOW DISABLES THE AUDIO.DEVICE INTERRUPTS.
- * DON'T KNOW IF THAT IS NECESSARY, DON'T KNOW
- * IF THAT IS VERY CLEAN EITHER.
- *
- * Revision 1.4 91/04/26 16:31:51 Espie
- * Completely new audio_soft.c.
- * The RKM is not very clear.
- * If a channel is stolen, you don't have to free the channel,
- * a CMD_RESET is enough.
- * To get back your channel, don't change the allocation key,
- * just ask a CMD_ALLOCATE again.
- * So that there are now two behaviours: either we allocate/deallocate channels
- * (pauses), or we get a channel stolen, in which case, we just CMD_RESET
- * and CMD_ALLOCATE again with the same key/CMD_LOCK again.
- * The ``critical section'' parts aren't really handled yet (read: kludge).
- *
- * Revision 1.3 91/04/24 16:09:40 Espie
- * Corrected a big hanging bug (CTRL C while waiting for a request triggered
- * wrong logic).
- *
- * Revision 1.2 91/04/24 15:23:42 Espie
- * Arbitration between different users of the audio channel.
- * A bit crude yet.
- * Amazingly enough, it works.
- * Should provide a way to release the CIA timer too, might just
- * be useful...
- * There are lots of critical sections, not sure if everything is ok right now.
- *
- * Revision 1.1 91/04/23 21:31:03 Espie
- * Initial revision
- *
- *
- */
-
- /* how to obtain access to the audio hardware, and release it gracefully
- */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <devices/audio.h>
- #include <proto/exec.h>
- #include <custom/cleanup.h>
- #include <dos/dos.h>
- #include <stdio.h>
- #include "proto.h"
- #include "player.h"
-
- FORWARD LOCAL void free_channels();
- FORWARD LOCAL void process_messages();
-
- LOCAL struct IOAudio *req, *lock;
- LOCAL struct MsgPort *port;
- LOCAL ULONG portmask;
-
-
- /* state of the know requests */
- LOCAL BOOL pending_request, pending_lock;
- LOCAL BYTE last_error;
-
- /* My audio state machine.
- */
- LOCAL enum {BEGIN, ALLOCATING, AWAIT_STOLEN, STOLEN, FREEING } state;
- LOCAL BOOL should_run, song_avail, playing;
-
- LOCAL CLEAN audclean;
-
- /* setup_hard(). reset the audio hardware to a nice starting state.
- */
- LOCAL void setup_hard(void)
- {
- audclean = AllocClean(NIL);
- ToClean0L(audclean, reset_audio);
- save_filter();
- ToClean0L(audclean, restore_filter);
- reset_audio();
- }
-
- /* clean up after one self
- */
- LOCAL void safeabort()
- {
- /* critical section: DON'T try to abort a success allocation */
- Forbid();
- process_messages();
- if (state == ALLOCATING)
- {
- if (pending_request)
- {
- /* AbortIO() always succeeds */
- AbortIO(req);
- while(pending_request)
- {
- WaitPort(port);
- process_messages();
- }
- }
- else
- state = AWAIT_STOLEN;
- }
- Permit();
- if (state == AWAIT_STOLEN && !last_error)
- {
- free_channels();
- }
- while(pending_request || pending_lock)
- {
- WaitPort(port);
- process_messages();
- }
- }
-
- /* obtain_audio(): allocate all the structures we will need to
- * play with the audio device, and reset the state machine.
- */
- ULONG obtain_audio(int priority)
- {
- BYTE fail;
- pending_request = FALSE;
- pending_lock = FALSE;
- state = BEGIN;
- port = CreatePort(NULL, 0);
- if (port)
- ToClean(DeletePort, port);
- else
- mayPanic("Couldn't allocate port");
- portmask = 1L<<port->mp_SigBit;
- /* should be replaced by a call to CreateExtIO(), except that it isn't
- * really easy (2.0 system/ 1.3 link library)
- */
- req = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
- MEMF_CLEAR|MEMF_PUBLIC);
- if (req)
- ToClean2(FreeMem, req, sizeof(struct IOAudio));
- else
- mayPanic("Couldn't allocate first request");
- lock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
- MEMF_CLEAR|MEMF_PUBLIC);
- if (lock)
- ToClean2(FreeMem, lock, sizeof(struct IOAudio));
- else
- mayPanic("Couldn't allocate first request");
- req->ioa_Request.io_Message.mn_ReplyPort = port;
- req->ioa_Request.io_Message.mn_Node.ln_Pri = priority;
- req->ioa_AllocKey = 0;
- /* Note that OpenDevice returns 0 on success
- */
- fail = OpenDevice("audio.device", 0L, (struct IORequest *)req, 0L);
- if (fail)
- mayPanic("Couldn't open audio device");
- else
- ToClean(CloseDevice, req);
- ToClean0(safeabort);
- state = BEGIN;
- /* song_avail = FALSE;
- */
- return portmask;
- }
-
- LOCAL void void_pending(struct Message *msg)
- {
- if (msg == &(lock->ioa_Request.io_Message))
- {
- pending_lock = FALSE;
- last_error = lock->ioa_Request.io_Error;
- }
- else
- if (msg == &(req->ioa_Request.io_Message))
- {
- pending_request = FALSE;
- last_error = req->ioa_Request.io_Error;
- }
- }
-
- LOCAL void process_messages(void)
- {
- struct Message *msg;
- while(msg = GetMsg(port))
- void_pending(msg);
- }
-
- /***
- *
- * basic communication with the audio hardware.
- * You shouldn't use these directly.
- *
- ***/
-
- LOCAL UBYTE whichchannel[] = {15};
-
- /* allocate_channels(): sends a request to the audio.device for
- * the channels.
- */
-
- LOCAL void allocate_channels(void)
- {
- /* All channels !
- */
- req->ioa_Request.io_Command = ADCMD_ALLOCATE;
- req->ioa_Data = whichchannel;
- req->ioa_Length = sizeof(whichchannel);
- BeginIO(req);
- pending_request = TRUE;
- state = ALLOCATING;
- }
-
-
- /* lock_channels(): access to the audio device has been granted to us,
- * we lock the channels */
-
- LOCAL void lock_channels(void)
- {
- *lock = *req;
- lock->ioa_Request.io_Command = ADCMD_LOCK;
- BeginIO(lock);
- pending_lock = TRUE;
- }
-
- /* free_channels(): give back the channels.
- */
- LOCAL void free_channels(void)
- {
- req->ioa_Request.io_Command = ADCMD_FREE;
- BeginIO(req);
- pending_request = TRUE;
- }
-
- LOCAL void own_audio(void)
- {
- setup_hard();
- start_timer();
- have_audio(TRUE);
- state = AWAIT_STOLEN;
- }
-
- LOCAL void release_audio(void)
- {
- stop_timer();
- CleanUp(audclean);
- have_audio(FALSE);
- free_channels();
- }
-
- /* how to make things happen */
-
- LOCAL void trigger_player()
- {
- should_run = playing & song_avail;
- if (should_run)
- {
- if (state == BEGIN)
- {
- allocate_channels();
- }
- }
- else
- {
- if (state == AWAIT_STOLEN)
- {
- release_audio();
- state = FREEING;
- }
- }
- }
-
-
- /* the state machine itself, complete with error checking... */
-
- void handle_audio()
- {
- /* this is a terminal recursive function,
- * we optimize by hand since the compiler doesn't do it for us...
- */
- term_rec:
- process_messages();
- switch(state)
- {
- case ALLOCATING:
- if (pending_request)
- break;
- if (last_error)
- /* this one we don't know how to process */
- mayPanic("audio channel error");
- lock_channels();
- process_messages();
- if (last_error)
- {
- /* could not lock the channel */
- state = STOLEN;
- goto term_rec;
- }
- if (should_run)
- {
- own_audio();
- }
- else
- trigger_player();
- goto term_rec;
- break;
- case AWAIT_STOLEN:
- if (pending_lock)
- {
- if (!should_run)
- {
- trigger_player();
- goto term_rec;
- }
- }
- else
- {
- if (last_error == ADIOERR_CHANNELSTOLEN)
- {
- release_audio();
- state = STOLEN;
- }
- else
- mayPanic("Audio Lock aborted");
- }
- break;
- case STOLEN:
- if (pending_request)
- break;
- if (last_error)
- mayPanic("Could not free channel properly");
- state = BEGIN;
- if (should_run)
- {
- trigger_player();
- goto term_rec;
- }
- break;
- case FREEING:
- if (pending_request || pending_lock)
- break;
- if (last_error)
- mayPanic("Another audio.device problem");
- state = BEGIN;
- if (should_run)
- {
- trigger_player();
- goto term_rec;
- }
- break;
- case BEGIN:
- break;
- }
- }
-
- void start_player()
- {
- playing = TRUE;
- trigger_player();
- handle_audio();
- }
-
- void stop_player()
- {
- playing = FALSE;
- trigger_player();
- handle_audio();
- }
-
- void song_available(BOOL really)
- {
- song_avail = really;
- trigger_player();
- handle_audio();
- }
-
-