home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / amiga / utils / exp_iv / audiosft.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-16  |  9.8 KB  |  437 lines

  1. /* audio_soft.c */
  2.  
  3. /* $Author: Espie $
  4.  * $Date: 91/05/16 15:04:36 $
  5.  * $Revision: 1.22 $
  6.  * $Log:    audio_soft.c,v $
  7.  * Revision 1.22  91/05/16  15:04:36  Espie
  8.  * *** empty log message ***
  9.  * 
  10.  * Revision 1.21  91/05/12  19:56:53  Espie
  11.  * Added some logic so that play can be triggered by the availability
  12.  * of a song now.
  13.  * 
  14.  * Revision 1.20  91/05/12  16:00:59  Espie
  15.  * Tells the interface when we own the audio.device and when not.
  16.  * 
  17.  * Revision 1.19  91/05/07  12:14:13  Espie
  18.  * *** empty log message ***
  19.  * 
  20.  * Revision 1.18  91/05/06  15:14:12  Espie
  21.  * Added a measure of real-life error checking.
  22.  * 
  23.  * Revision 1.17  91/05/05  04:00:05  Espie
  24.  * Suppressed recursive calls.
  25.  * 
  26.  * Revision 1.16  91/05/02  23:29:22  Espie
  27.  * Checked the automaton. Now reliable.
  28.  * There definitely seems to be a bug in the audio.device...
  29.  * 
  30.  * Revision 1.15  91/05/02  11:19:42  Espie
  31.  * Checked out the new automaton. Still needs some error checking.
  32.  * 
  33.  * Revision 1.14  91/05/02  01:29:49  Espie
  34.  * The automaton is now implemented as an automaton, instead
  35.  * of a program calling the events handling from time to time.
  36.  * 
  37.  * Revision 1.13  91/04/30  00:35:41  Espie
  38.  * Stable version III.
  39.  * 
  40.  * Revision 1.12  91/04/29  23:55:36  Espie
  41.  * Cleaned up the allocation key bunch.
  42.  * This version exhibits a small problem with the OS.
  43.  * When there are too many openers around, AbortIO() will
  44.  * sometimes wait for something to happen...
  45.  * 
  46.  * Revision 1.11  91/04/29  15:03:22  Espie
  47.  * Corrected a small problem: safeabort() should be called BEFORE
  48.  * closing the audio.device.
  49.  * 
  50.  * Revision 1.10  91/04/29  13:02:53  Espie
  51.  * Completely clean audio_soft.
  52.  * (Except maybe for the request duplication).
  53.  * 
  54.  * Revision 1.9  91/04/29  03:53:39  Espie
  55.  * Encapsulating more and more, not yet ready.
  56.  * 
  57.  * Revision 1.8  91/04/29  02:22:50  Espie
  58.  * Suppressed critical sections, should be cleaner now,
  59.  * and easier to debug.
  60.  * 
  61.  * Revision 1.7  91/04/28  20:35:59  Espie
  62.  * Small changes.
  63.  * 
  64.  * Revision 1.6  91/04/27  16:45:23  Espie
  65.  * Moved interrupt disabling to audio_hard.
  66.  * 
  67.  * Revision 1.5  91/04/27  04:01:28  Espie
  68.  * 
  69.  * NOW DISABLES THE AUDIO.DEVICE INTERRUPTS.
  70.  * DON'T KNOW IF THAT IS NECESSARY, DON'T KNOW
  71.  * IF THAT IS VERY CLEAN EITHER.
  72.  * 
  73.  * Revision 1.4  91/04/26  16:31:51  Espie
  74.  * Completely new audio_soft.c.
  75.  * The RKM is not very clear.
  76.  * If a channel is stolen, you don't have to free the channel,
  77.  * a CMD_RESET is enough.
  78.  * To get back your channel, don't change the allocation key,
  79.  * just ask a CMD_ALLOCATE again.
  80.  * So that there are now two behaviours: either we allocate/deallocate channels
  81.  * (pauses), or we get a channel stolen, in which case, we just CMD_RESET
  82.  * and CMD_ALLOCATE again with the same key/CMD_LOCK again.
  83.  * The ``critical section'' parts aren't really handled yet (read: kludge).
  84.  * 
  85.  * Revision 1.3  91/04/24  16:09:40  Espie
  86.  * Corrected a big hanging bug (CTRL C while waiting for a request triggered
  87.  * wrong logic).
  88.  * 
  89.  * Revision 1.2  91/04/24  15:23:42  Espie
  90.  * Arbitration between different users of the audio channel.
  91.  * A bit crude yet.
  92.  * Amazingly enough, it works.
  93.  * Should provide a way to release the CIA timer too, might just
  94.  * be useful...
  95.  * There are lots of critical sections, not sure if everything is ok right now.
  96.  * 
  97.  * Revision 1.1  91/04/23  21:31:03  Espie
  98.  * Initial revision
  99.  * 
  100.  *
  101.  */
  102.  
  103. /* how to obtain access to the audio hardware, and release it gracefully
  104.  */
  105.  
  106. #include <exec/types.h>
  107. #include <exec/memory.h>
  108. #include <devices/audio.h>
  109. #include <proto/exec.h>
  110. #include <custom/cleanup.h>
  111. #include <dos/dos.h>
  112. #include <stdio.h>
  113. #include "proto.h"
  114. #include "player.h"
  115.  
  116. FORWARD LOCAL void free_channels();
  117. FORWARD LOCAL void process_messages();
  118.  
  119. LOCAL struct IOAudio *req, *lock;
  120. LOCAL struct MsgPort *port;
  121. LOCAL ULONG portmask;
  122.  
  123.  
  124. /* state of the know requests */
  125. LOCAL BOOL pending_request, pending_lock;
  126. LOCAL BYTE last_error;
  127.  
  128. /* My audio state machine.
  129.  */    
  130. LOCAL enum {BEGIN, ALLOCATING, AWAIT_STOLEN, STOLEN, FREEING } state;
  131. LOCAL BOOL should_run, song_avail, playing;
  132.  
  133. LOCAL CLEAN audclean;
  134.  
  135. /* setup_hard(). reset the audio hardware to a nice starting state.
  136.  */
  137. LOCAL void setup_hard(void)
  138.     {
  139.         audclean = AllocClean(NIL);
  140.         ToClean0L(audclean, reset_audio);
  141.         save_filter();
  142.         ToClean0L(audclean, restore_filter);
  143.         reset_audio();
  144.     }
  145.  
  146. /* clean up after one self
  147.  */     
  148. LOCAL void safeabort()
  149.     {
  150.             /* critical section: DON'T try to abort a success allocation */
  151.         Forbid();
  152.         process_messages();
  153.         if (state == ALLOCATING)
  154.             {
  155.                 if (pending_request)
  156.                     {
  157.                             /* AbortIO() always succeeds */
  158.                         AbortIO(req);
  159.                         while(pending_request)
  160.                             {
  161.                                 WaitPort(port);
  162.                                 process_messages();
  163.                             }
  164.                     }
  165.                 else
  166.                     state = AWAIT_STOLEN;
  167.             }
  168.         Permit();
  169.         if (state == AWAIT_STOLEN && !last_error)
  170.             {
  171.                 free_channels();
  172.             }
  173.         while(pending_request || pending_lock)
  174.             {
  175.                 WaitPort(port);
  176.                 process_messages();
  177.             }
  178.     }
  179.  
  180. /* obtain_audio(): allocate all the structures we will need to
  181.  * play with the audio device, and reset the state machine.
  182.  */ 
  183. ULONG obtain_audio(int priority)
  184.     {
  185.     BYTE fail;
  186.         pending_request = FALSE;
  187.         pending_lock = FALSE;
  188.         state = BEGIN;
  189.         port = CreatePort(NULL, 0);
  190.         if (port)
  191.             ToClean(DeletePort, port);
  192.         else
  193.             mayPanic("Couldn't allocate port");
  194.         portmask = 1L<<port->mp_SigBit;
  195.     /* should be replaced by a call to CreateExtIO(), except that it isn't
  196.      *    really easy (2.0 system/ 1.3 link library)        
  197.      */
  198.         req = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), 
  199.             MEMF_CLEAR|MEMF_PUBLIC);
  200.         if (req)
  201.             ToClean2(FreeMem, req, sizeof(struct IOAudio));
  202.         else
  203.             mayPanic("Couldn't allocate first request");
  204.         lock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), 
  205.             MEMF_CLEAR|MEMF_PUBLIC);
  206.         if (lock)
  207.             ToClean2(FreeMem, lock, sizeof(struct IOAudio));
  208.         else
  209.             mayPanic("Couldn't allocate first request");
  210.         req->ioa_Request.io_Message.mn_ReplyPort = port;
  211.         req->ioa_Request.io_Message.mn_Node.ln_Pri = priority;
  212.         req->ioa_AllocKey = 0; 
  213.             /* Note that OpenDevice returns 0 on success
  214.              */
  215.         fail = OpenDevice("audio.device", 0L, (struct IORequest *)req, 0L);
  216.         if (fail)
  217.             mayPanic("Couldn't open audio device");
  218.         else
  219.             ToClean(CloseDevice, req);
  220.         ToClean0(safeabort);
  221.         state = BEGIN;
  222. /*        song_avail = FALSE;
  223.  */
  224.         return portmask;
  225.     }
  226.  
  227. LOCAL void void_pending(struct Message *msg)
  228.     {
  229.         if (msg == &(lock->ioa_Request.io_Message))
  230.             {
  231.                 pending_lock = FALSE;
  232.                 last_error = lock->ioa_Request.io_Error;
  233.             }
  234.         else
  235.         if (msg == &(req->ioa_Request.io_Message))
  236.             {
  237.                 pending_request = FALSE;
  238.                 last_error = req->ioa_Request.io_Error;
  239.             }
  240.     }
  241.     
  242. LOCAL void process_messages(void)
  243.     {
  244.     struct Message *msg;
  245.         while(msg = GetMsg(port))
  246.             void_pending(msg);
  247.     }
  248.     
  249. /***
  250.  *
  251.  *         basic communication with the audio hardware.
  252.  *     You shouldn't use these directly.
  253.  *
  254.  ***/
  255.  
  256. LOCAL UBYTE whichchannel[] = {15};
  257.  
  258. /* allocate_channels(): sends a request to the audio.device for
  259.  * the channels.
  260.  */
  261.  
  262. LOCAL void allocate_channels(void)
  263.     { 
  264.         /* All channels !
  265.           */
  266.         req->ioa_Request.io_Command = ADCMD_ALLOCATE;
  267.         req->ioa_Data = whichchannel;
  268.         req->ioa_Length = sizeof(whichchannel);
  269.         BeginIO(req);
  270.         pending_request = TRUE;
  271.         state = ALLOCATING;
  272.     }
  273.     
  274.  
  275. /* lock_channels(): access to the audio device has been granted to us,
  276.  * we lock the channels */
  277.  
  278. LOCAL void lock_channels(void)
  279.     {
  280.         *lock = *req;
  281.         lock->ioa_Request.io_Command = ADCMD_LOCK;
  282.         BeginIO(lock);
  283.         pending_lock = TRUE;
  284.     }        
  285.  
  286. /* free_channels(): give back the channels.
  287.  */ 
  288. LOCAL void free_channels(void)
  289.     {
  290.         req->ioa_Request.io_Command = ADCMD_FREE;
  291.         BeginIO(req);
  292.         pending_request = TRUE;
  293.     }
  294.  
  295. LOCAL void own_audio(void)
  296.     {
  297.         setup_hard();
  298.         start_timer();
  299.         have_audio(TRUE);
  300.         state = AWAIT_STOLEN;
  301.     }
  302.             
  303. LOCAL void release_audio(void)
  304.     {
  305.         stop_timer();
  306.         CleanUp(audclean);
  307.         have_audio(FALSE);
  308.         free_channels();
  309.     }
  310.  
  311. /* how to make things happen */
  312.  
  313. LOCAL void trigger_player()
  314.     {
  315.         should_run = playing & song_avail;
  316.         if (should_run)
  317.             {
  318.                 if (state == BEGIN)
  319.                     {
  320.                         allocate_channels();
  321.                     }
  322.             }
  323.         else
  324.             {
  325.                 if (state == AWAIT_STOLEN)
  326.                     {
  327.                         release_audio();
  328.                         state = FREEING;
  329.                     }
  330.             }
  331.     }
  332.                         
  333.  
  334. /* the state machine itself, complete with error checking... */
  335.  
  336. void handle_audio()
  337.     {
  338.         /* this is a terminal recursive function,
  339.          * we optimize by hand since the compiler doesn't do it for us...
  340.          */
  341. term_rec:
  342.         process_messages();
  343.         switch(state)
  344.             {
  345.             case ALLOCATING:
  346.                 if (pending_request)
  347.                     break;
  348.                 if (last_error)
  349.                     /* this one we don't know how to process */
  350.                     mayPanic("audio channel error");
  351.                 lock_channels();
  352.                 process_messages();
  353.                 if (last_error)
  354.                     {
  355.                         /* could not lock the channel */
  356.                         state = STOLEN;
  357.                         goto term_rec;
  358.                     }
  359.                 if (should_run)
  360.                     {
  361.                         own_audio();
  362.                     }
  363.                 else
  364.                     trigger_player();
  365.                 goto term_rec;
  366.                 break;
  367.             case AWAIT_STOLEN:
  368.                 if (pending_lock)
  369.                     {
  370.                         if (!should_run)
  371.                             {
  372.                                 trigger_player();
  373.                                 goto term_rec;
  374.                             }
  375.                     }
  376.                 else
  377.                     {
  378.                         if (last_error == ADIOERR_CHANNELSTOLEN)
  379.                             {
  380.                                 release_audio();
  381.                                 state = STOLEN;
  382.                             }
  383.                         else
  384.                             mayPanic("Audio Lock aborted");
  385.                     }
  386.                 break;
  387.             case STOLEN:
  388.                 if (pending_request)
  389.                     break;
  390.                 if (last_error)
  391.                     mayPanic("Could not free channel properly");
  392.                 state = BEGIN;
  393.                 if (should_run)
  394.                     {
  395.                         trigger_player();
  396.                         goto term_rec;
  397.                     }
  398.                 break;
  399.             case FREEING:
  400.                 if (pending_request || pending_lock)
  401.                     break;
  402.                 if (last_error)
  403.                     mayPanic("Another audio.device problem");
  404.                 state = BEGIN;
  405.                 if (should_run)
  406.                     {
  407.                         trigger_player();
  408.                         goto term_rec;
  409.                     }
  410.                 break;
  411.             case BEGIN:
  412.                 break;
  413.             }
  414.     }
  415.  
  416. void start_player()
  417.     {
  418.         playing = TRUE;
  419.         trigger_player();
  420.         handle_audio();
  421.     }
  422.  
  423. void stop_player()
  424.     {
  425.         playing = FALSE;
  426.         trigger_player();
  427.         handle_audio();
  428.     }
  429.     
  430. void song_available(BOOL really)
  431.     {
  432.         song_avail = really;
  433.         trigger_player();
  434.         handle_audio();
  435.     }
  436.  
  437.