/*
    Load and convert a WAV file, play it with 'ahi.device'

    Written by Oliver Gantert

    31.04.2000 - Yeah, this works!
*/

#include "orbit.h"
#include <devices/ahi.h>
#include <clib/ahi_protos.h>
#include <stdio.h>

struct RIFF_Header
{
  ULONG ChunkID;          /*  must be 0x52494646  */
  ULONG ChunkSize;        /*  little endian!  */
  ULONG Format;           /*  must be 0x57415645  */
  ULONG Subchunk1ID;      /*  must be 0x666d7420  */
  ULONG Subchunk1Size;    /*  little endian, must be 16  */
  UWORD AudioFormat;      /*  little endian, must be 1  */
  UWORD NumChannels;      /*  little endian, Mono = 1, Stereo = 2  */
  ULONG SampleRate;       /*  little endian, 44100 etc.  */
  ULONG ByteRate;         /*  little endian, == SampleRate * NumChannels * BitsPerSample/8  */
  UWORD BlockAlign;       /*  little endian, == NumChannels * BitsPerSample/8  */
  UWORD BitsPerSample;    /*  little endian, 8, 16, etc.  */
  ULONG Subchunk2ID;      /*  must be 0x64617461  */
  ULONG Subchunk2Size;    /*  little endian, == NumSamples * NumChannels * BitsPerSample/8  */
};

struct ASample
{
  ULONG SampleRate;
  ULONG Size;
  UBYTE *SampleData;
  ULONG Type;
};

struct MsgPort *AHImp = NULL;
struct AHIRequest *AHIio = NULL;
struct ASample *smp[3] = {NULL, NULL, NULL};
ULONG ahi_available = 0,
      ahi_open = 0;

UWORD ChangeEndianW(UWORD wert)
{
  UBYTE t;
  union
  {
    UBYTE c[2];
    UWORD w;
  } u;

  u.w = wert;
  t = u.c[0];
  u.c[0] = u.c[1];
  u.c[1] = t;

  return(u.w);
}

ULONG ChangeEndianL(ULONG wert)
{
  UBYTE t;
  union
  {
    UBYTE c[4];
    ULONG w;
  } u;

  u.w = wert;
  t = u.c[0];
  u.c[0] = u.c[3];
  u.c[3] = t;
  t = u.c[1];
  u.c[1] = u.c[2];
  u.c[2] = t;

  return(u.w);
}

struct ASample *LoadWAVSample(STRPTR filename)
{
  struct ASample *smp = NULL;
  struct RIFF_Header *riff;
  FILE *file;
  UWORD *smpdata;
  ULONG i;

  if (file = fopen(filename, "rb"))
  {
    if (riff = (struct RIFF_Header *)malloc(sizeof(struct RIFF_Header)))
    {
      fread(riff, sizeof(struct RIFF_Header), 1, file);
      /*
          Check if it's a standard RIFF WAVE file.
      */
      if ((riff->ChunkID      == 0x52494646) && (riff->Format       == 0x57415645) &&
          (riff->Subchunk1ID  == 0x666d7420) && (riff->Subchunk2ID  == 0x64617461))
      {
        riff->Subchunk1Size = ChangeEndianL(riff->Subchunk1Size);
        riff->AudioFormat   = ChangeEndianW(riff->AudioFormat);
        riff->NumChannels   = ChangeEndianW(riff->NumChannels);
        riff->SampleRate    = ChangeEndianL(riff->SampleRate);
        riff->BitsPerSample = ChangeEndianW(riff->BitsPerSample);
        riff->Subchunk2Size = ChangeEndianL(riff->Subchunk2Size);
        /*
            More checks.
        */
        if ((riff->Subchunk1Size == 16) && (riff->AudioFormat == 1))
        {
          if (smp = (struct ASample *)malloc(sizeof(struct ASample)))
          {
            smp->SampleRate    = riff->SampleRate;
            smp->Size          = riff->Subchunk2Size;
            if (riff->NumChannels == 1)
            {
              if (riff->BitsPerSample == 8)
                smp->Type = AHIST_M8S;
              else
                smp->Type = AHIST_M16S;
            }
            else
            {
              if (riff->BitsPerSample == 8)
                smp->Type = AHIST_S8S;
              else
                smp->Type = AHIST_S16S;
            }
            if (smp->SampleData = (UBYTE *)malloc(smp->Size))
            {
              fread(smp->SampleData, smp->Size, 1, file);
              /*
                  Change endian of 16bit data
              */
              if (riff->BitsPerSample > 8)
              {
                smpdata = (UWORD *)smp->SampleData;
                for (i = 0; i < (smp->Size>>1); i++)
                {
                  smpdata[i] = ChangeEndianW(smpdata[i]);
                }
              }
            }
            else
            {
              free(smp);
              smp = NULL;
            }
          }
        }
      }
      free(riff);
    }
    fclose(file);
  }
  return(smp);
}

VOID FreeASample(struct ASample *smp)
{
  if (smp)
  {
    if (smp->SampleData)
      free(smp->SampleData);
    free(smp);
  }
}

/*
    ==============
    THE ORBIT PART
    ==============
*/

int InitSound()
{
  if (AHImp = (struct MsgPort *)CreateMsgPort())
  {
    if (AHIio = (struct AHIRequest *)CreateIORequest(AHImp, sizeof(struct AHIRequest)))
    {
      AHIio->ahir_Version = 4;
      if (!OpenDevice(AHINAME, 0, (struct IORequest *)AHIio, 0))
      {
        ahi_open = 1;
        if (smp[0] = LoadWAVSample("sounds/phaser.wav"))
        {
          if (smp[1] = LoadWAVSample("sounds/explosion1.wav"))
          {
            if (smp[2] = LoadWAVSample("sounds/communicator.wav"))
            {
              AHIio->ahir_Std.io_Message.mn_Node.ln_Pri = 0;
              AHIio->ahir_Std.io_Command  = CMD_WRITE;
              AHIio->ahir_Std.io_Offset   = 0;
              AHIio->ahir_Volume          = 0x10000;
              AHIio->ahir_Position        = 0x8000;
              AHIio->ahir_Link            = NULL;
              ahi_available = 1;
              return(1);
            }
            FreeASample(smp[1]);
          }
          FreeASample(smp[0]);
        }
        CloseDevice((struct IORequest *)AHIio);
      }
      DeleteIORequest((struct IORequest *)AHIio);
    }
    DeleteMsgPort(AHImp);
  }
  return(0);
}

int PlayAudio(enum sounds nSound)
{
  if (ahi_available)
  {
    AHIio->ahir_Std.io_Data     = smp[nSound]->SampleData;
    AHIio->ahir_Std.io_Length   = smp[nSound]->Size;
    AHIio->ahir_Frequency       = smp[nSound]->SampleRate;
    AHIio->ahir_Type            = smp[nSound]->Type;
    SendIO((struct IORequest *)AHIio);
    WaitIO((struct IORequest *)AHIio);
    return(1);
  }
  return(0);
}

void FinishSound()
{
  ahi_available = 0;
  if (AHIio)
  {
    if (ahi_open)
    {
      CloseDevice((struct IORequest *)AHIio);
      ahi_open = 0;
    }
    DeleteIORequest((struct IORequest *)AHIio);
    AHIio = NULL;
  }
  if (AHImp)
  {
    DeleteMsgPort(AHImp);
    AHImp = NULL;
  }
  if (smp[0])
    FreeASample(smp[0]);
  if (smp[1])
    FreeASample(smp[1]);
  if (smp[2])
    FreeASample(smp[2]);
}
