/* Simple sample player for AHI using the low-level API.
   Hardcoded sample names.

   This software is Public Domain. */

#include <devices/ahi.h>
#include <exec/exec.h>
#include <proto/ahi.h>
#include <proto/dos.h>
#include <proto/exec.h>

#define USE_AHI_V4 TRUE

#define CHANNELS   4
#define MAXSAMPLES 16

#define INT_FREQ   50

char *ID = "$VER: SimplePlay 1.2 (2.4.97)\r\n";

APTR samples[MAXSAMPLES] = { 0 };

struct {
  BOOL      FadeVolume;
  Fixed     Volume;
  sposition Position;
} channelstate[CHANNELS];

struct Library      *AHIBase;
struct MsgPort      *AHImp     = NULL;
struct AHIRequest   *AHIio     = NULL;
BYTE                 AHIDevice = -1;
struct AHIAudioCtrl *actrl     = NULL;


/* Prototypes */

BOOL  OpenAHI(void);
void  CloseAHI(void);
BOOL  AllocAudio(void);
void  FreeAudio(void);
UWORD LoadSample(unsigned char * , ULONG );
void  UnloadSample(UWORD );
int   main(void);

/******************************************************************************
** PlayerFunc *****************************************************************
******************************************************************************/

__asm __interrupt __saveds static void PlayerFunc(
    register __a0 struct Hook *hook,
    register __a2 struct AHIAudioCtrl *actrl,
    register __a1 APTR ignored) {

  int i;
  
  for(i = 0; i < CHANNELS; i++) {

    if(channelstate[i].FadeVolume) {

      channelstate[i].Volume = (channelstate[i].Volume * 90) / 100; // Fade volume

      if(channelstate[i].Volume == 0) {
        channelstate[i].FadeVolume = FALSE;
      }

      AHI_SetVol(i, channelstate[i].Volume, channelstate[i].Position,
          actrl, AHISF_IMM);
    }
  }
  return;
}

struct Hook PlayerHook = {
  0,0,
  (ULONG (* )()) PlayerFunc,
  NULL,
  NULL,
};

/******************************************************************************
**** OpenAHI ******************************************************************
******************************************************************************/

/* Open the device for low-level usage */

BOOL OpenAHI(void) {

  if(AHImp = CreateMsgPort()) {
    if(AHIio = (struct AHIRequest *)CreateIORequest(
        AHImp,sizeof(struct AHIRequest))) {

#if USE_AHI_V4
      AHIio->ahir_Version = 4;
#else
      AHIio->ahir_Version = 2;
#endif

      if(!(AHIDevice = OpenDevice(AHINAME, AHI_NO_UNIT,
          (struct IORequest *) AHIio,NULL))) {
        AHIBase = (struct Library *) AHIio->ahir_Std.io_Device;
        return TRUE;
      }
    }
  }
  FreeAudio();
  return FALSE;
}


/******************************************************************************
**** CloseAHI *****************************************************************
******************************************************************************/

/* Close the device */

void CloseAHI(void) {

  if(! AHIDevice)
    CloseDevice((struct IORequest *)AHIio);
  AHIDevice=-1;
  DeleteIORequest((struct IORequest *)AHIio);
  AHIio=NULL;
  DeleteMsgPort(AHImp);
  AHImp=NULL;
}


/******************************************************************************
**** AllocAudio ***************************************************************
******************************************************************************/

/* Ask user for an audio mode and allocate it */

BOOL AllocAudio(void) {
  struct AHIAudioModeRequester *req;
  BOOL   rc = FALSE;

  req = AHI_AllocAudioRequest(
      AHIR_PubScreenName, NULL,
      AHIR_TitleText,     "Select a mode and rate",
      AHIR_DoMixFreq,     TRUE,
      TAG_DONE);

  if(req) {
    if(AHI_AudioRequest(req, TAG_DONE)) {
      actrl = AHI_AllocAudio(
          AHIA_AudioID,         req->ahiam_AudioID,
          AHIA_MixFreq,         req->ahiam_MixFreq,
          AHIA_Channels,        CHANNELS,
          AHIA_Sounds,          MAXSAMPLES,
          AHIA_PlayerFunc,      &PlayerHook,
          AHIA_PlayerFreq,      INT_FREQ<<16,
          AHIA_MinPlayerFreq,   INT_FREQ<<16,
          AHIA_MaxPlayerFreq,   INT_FREQ<<16,
          TAG_DONE);
      if(actrl) {
        rc = TRUE;
      }
    }
    AHI_FreeAudioRequest(req);
  }
  return rc;
}


/******************************************************************************
**** FreeAudio ****************************************************************
******************************************************************************/

/* Release the audio hardware */

void FreeAudio() {

  AHI_FreeAudio(actrl);
  actrl = NULL;
}

/******************************************************************************
**** LoadSample ***************************************************************
******************************************************************************/

/* Load a (raw) 8 or 16 bit sample from disk. The sample ID is returned
   (or AHI_NOSOUND on error). */

UWORD LoadSample(char *filename, ULONG type) {
  struct AHISampleInfo sample;
  APTR *samplearray = samples;
  UWORD id = 0, rc = AHI_NOSOUND;
  BPTR file;

  // Find a free sample slot

  while(*samplearray) {
    id++;
    samplearray++;
    if(id >= MAXSAMPLES) {
      return AHI_NOSOUND;
    }
  }

  file = Open(filename, MODE_OLDFILE);
  
  if(file) {
    int length;

    Seek(file, 0, OFFSET_END);
    length = Seek(file, 0, OFFSET_BEGINNING);
    *samplearray = AllocVec(length, MEMF_PUBLIC);
    if(*samplearray) {
      Read(file, *samplearray, length);

      sample.ahisi_Type = type;
      sample.ahisi_Address = *samplearray;
#if USE_AHI_V4
      sample.ahisi_Length = length / AHI_SampleFrameSize(type);
#else
      sample.ahisi_Length = length / (type == AHIST_M16S ? 2 : 1);
#endif
      if(! AHI_LoadSound(id, AHIST_SAMPLE, &sample, actrl)) {
        rc = id;
      }
    }
    Close(file);
  }
  return rc;
}


/******************************************************************************
**** UnloadSample *************************************************************
******************************************************************************/

void UnloadSample(UWORD id) {

  AHI_UnloadSound(id, actrl);
  FreeVec(samples[id]);
  samples[id] = NULL;
}

/******************************************************************************
**** main *********************************************************************
******************************************************************************/

int main() {
  UWORD sample, ahem, louise;
  int i;

  for(i = 0; i < CHANNELS; i++) {
    channelstate[i].FadeVolume = FALSE;
  }

  if(OpenAHI()) {
    if(AllocAudio()) {
      ahem   = LoadSample("Projekt:ahi/samples/ASS-14.sb", AHIST_M8S);
      louise = LoadSample("Projekt:ahi/samples/LouiseR.sw", AHIST_M16S);
      if((ahem != AHI_NOSOUND) && (louise != AHI_NOSOUND)) {

        // Start feeding samples to sound hardware
        if(!(AHI_ControlAudio(actrl,
            AHIC_Play, TRUE,
            TAG_DONE)))
        {

#if USE_AHI_V4
          AHI_Play(actrl,
            // A forever-looping sample on channel 0
            AHIP_BeginChannel,  0,
            AHIP_Freq,          17640,
            AHIP_Vol,           0x10000,
            AHIP_Pan,           0xc000,
            AHIP_Sound,         louise,
            AHIP_EndChannel,    NULL,

            // A oneshot sample on channel 1
            AHIP_BeginChannel,  1,
            AHIP_Freq,          22254,
            AHIP_Vol,           0x10000,
            AHIP_Pan,           0x4000,
            AHIP_Sound,         ahem,
            AHIP_LoopSound,     AHI_NOSOUND,
            AHIP_EndChannel,    NULL,

            TAG_DONE);
#else
          // A forever-looping sample on channel 0
          AHI_SetFreq(0, 17640, actrl, AHISF_IMM);
          AHI_SetVol(0, 0x10000, 0xc000, actrl, AHISF_IMM);
          AHI_SetSound(0, louise, 0, 0, actrl, AHISF_IMM);

          // A oneshot sample on channel 1
          AHI_SetFreq(1, 22254, actrl, AHISF_IMM);
          AHI_SetVol(1, 0x10000, 0x4000, actrl, AHISF_IMM);
          AHI_SetSound(1, ahem, 0, 0, actrl, AHISF_IMM);
          AHI_SetSound(1, AHI_NOSOUND, 0, 0, actrl, 0);
#endif
          channelstate[0].Volume   = 0x10000;
          channelstate[0].Position = 0xc000;
          channelstate[1].Volume   = 0x10000;
          channelstate[1].Position = 0x4000;

          // Wait 10 seconds
          
          Delay(10 * TICKS_PER_SECOND);

          // Fade away channel 0
          
          channelstate[0].FadeVolume = TRUE;

          while(channelstate[0].FadeVolume) {
            Delay(1);
          }

          // Wait a sec...

          Delay(TICKS_PER_SECOND);

          // Stop sounds
          AHI_ControlAudio(actrl,
              AHIC_Play, FALSE,
              TAG_DONE);
        }
      }

      // Ask AHI to unload all loaded samples and free the memory for them 
      for(sample = 0; sample < MAXSAMPLES; sample++) {
        UnloadSample(sample);
      }

    }
    FreeAudio();
  }
  CloseAHI();

  return 0;
}
