/*
 * play.c - routines to load and play an IFF 8SVX sound
 *
 * Bruno Costa - 19 Feb 91 - 19 Feb 91
 *
 * (based on ARKM 1.3 example audio2.c)
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <libraries/dos.h>
#include <graphics/gfxbase.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <stdlib.h>
#include <stdio.h>

#include "iff.h"

#define VHDR MakeID('V','H','D','R')
#define BODY MakeID('B','O','D','Y')
#define MY8S MakeID('8','S','V','X')


static struct MsgPort *port;		/* audio device port */

static UBYTE *fbuf;			/* For sample memory allocation */
static ULONG fsize;			/* and freeing                  */

static Voice8Header *header;
static UBYTE *sample;			/* one-shot sample data        */
static ULONG length;			/* Sample length               */
static ULONG clock;

static struct IOAudio *AIOreq;
static int posted = FALSE;


static void setclock (void)
{
 struct GfxBase *gbase;

 clock = 3579545L;		/* NTSC clock */

 gbase = (struct GfxBase *) OpenLibrary ("graphics.library", 0L);
 if (gbase)
 {
   if (gbase->DisplayFlags & PAL)
     clock = 3546895L;		/* PAL clock */

   CloseLibrary ((struct Library *) gbase);
 }
}


static void abort (void)
{
 if (posted && !CheckIO ((struct IORequest *) AIOreq))
   AbortIO ((struct IORequest *) AIOreq);
}


static void wait (void)
{
 if (posted)
 {
   WaitPort (port);
   (void) GetMsg (port);
   posted = FALSE;
 }
}


static int sopen (void)
{
 static UBYTE chan1[]  = { 1 };		/* Audio channel allocation arrays */
 static UBYTE chan2[]  = { 2 };
 static UBYTE chan3[]  = { 4 };
 static UBYTE chan4[]  = { 8 };
 static UBYTE *chans[] = {chan1,chan2,chan3,chan4};

 ULONG speed = clock / header->samplesPerSec;

 AIOreq = (struct IOAudio *) AllocMem (sizeof (*AIOreq), MEMF_PUBLIC | MEMF_CLEAR);
 if (AIOreq)
 {
   port = CreatePort (0, 0);
   if (port)
   {
     int err;
     int c = 0;		/* start by channel 0 */

     do
     {
       AIOreq->ioa_Request.io_Message.mn_ReplyPort   = port;
       AIOreq->ioa_Request.io_Message.mn_Node.ln_Pri = 128;  /* No stealing! */
       AIOreq->ioa_AllocKey                          = 0;
       AIOreq->ioa_Data                              = chans[c];
       AIOreq->ioa_Length                            = 1;

       err = OpenDevice ("audio.device", 0L, (struct IORequest *) AIOreq, 0L);
       ++c;
     } while (err != 0  &&  c < 4);

     if (!err)
     {
       AIOreq->ioa_Request.io_Command = CMD_WRITE;
       AIOreq->ioa_Request.io_Flags = ADIOF_PERVOL;
       AIOreq->ioa_Volume = 60;

       AIOreq->ioa_Period = (UWORD)speed;
       AIOreq->ioa_Cycles = 1;

       AIOreq->ioa_Request.io_Message.mn_ReplyPort = port;

       AIOreq->ioa_Data = sample;

       return TRUE;		/* normal return is here */
     }
     DeletePort (port);
   }
   FreeMem (AIOreq, sizeof (*AIOreq));
 }

 return FALSE;
}


static void sclose (void)
{
 abort ();
 wait ();

 CloseDevice ((struct IORequest *) AIOreq);
 if (port)
   DeletePort (port);
 if (AIOreq)
   FreeMem (AIOreq, sizeof (*AIOreq));
}


void play (void)
{
 abort ();
 wait ();

 if (length > 0  &&  length <= 102400)
 {
   AIOreq->ioa_Length = length;
   BeginIO ((struct IORequest *) AIOreq);
   posted = TRUE;
 }
}


int load (char *fname)
{
 UBYTE *data;		/* data ptr for file read    */
 BYTE iobuffer[8];	/* Buffer for 8SVX header    */
 Chunk *chunk;		/* Pointers for 8SVX parsing */
 BPTR f;

 setclock ();

 f = Open (fname, MODE_OLDFILE);
 if (f)
 {
   long count = Read (f, iobuffer, 8L);
   if (count == 8)
   {
     chunk = (Chunk *)iobuffer;
     if(chunk->ckID == FORM)
     {
       fsize = chunk->ckSize;
       fbuf = AllocMem (fsize, MEMF_CHIP | MEMF_CLEAR);
       if (fbuf)
       {
         data = fbuf;
         count = Read (f, fbuf, chunk->ckSize);
         if (count == chunk->ckSize)
         {
           if (MakeID (*data, *(data+1), *(data+2), *(data+3)) == MY8S)
           {
             data += 4;

             while (data < fbuf + fsize)
             {
               chunk = (Chunk *)data;

               switch (chunk->ckID)
               {
                 case VHDR:
                   header = (Voice8Header *) (data + 8);
                   break;

                 case BODY:
                   sample = (BYTE *)(data + 8);
                   length = header->oneShotHiSamples;
                   if (length == 0)
                     length = header->repeatHiSamples;
                   break;
               }

               data += 8 + chunk->ckSize;

               if (chunk->ckSize & 1L == 1)
                 data++;
             }
             Close (f);

             if (sopen ())
               return TRUE;	/* normal exit is here */
           }
         }
         FreeMem (fbuf, fsize);
       }
     }
   }
   Close (f);
 }

 return FALSE;
}


void unload (void)
{
 sclose ();

 if (fbuf)
   FreeMem (fbuf, fsize);

 fbuf = NULL;
 fsize = 0;
}


#ifdef TEST
void main (int argc, char *argv[])
{
 if (argc != 2)
 {
   printf ("usage: %s <sound file>\n", argv[0]);
   exit (10);
 }

 if (load (argv[1]))
 {
   play ();
   wait ();
   unload ();
 }
}
#endif
