/**********************************************************************
** Play Sound V1.0 - written with the aid of the 2.0 RKRMs in 1993 for
** my PingPong game. You may use it in your programs for free. Please
** read the docs for more information.
**********************************************************************/

/// "Includes, Defines, Typedefs, ..."
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <graphics/gfxbase.h>
#include <stdio.h>
#include "easysound.h"

#define Prototype   extern

#define CLOCK_PAL   3579545
#define CLOCK_NTSC  3546895

struct IOAudio *IO[] = {
    NULL,       // All channels are switched off by default
    NULL, 
    NULL, 
    NULL
};

typedef struct {
    ULONG  oneShotHiSamples;  // high octave 1-shot samples
    ULONG  repeatHiSamples;   // high octave repeat samples
    ULONG  samplesPerHiCycle; // high octave samples per cycle
    UWORD  samplesPerSec;     // sampling rate
    UBYTE  ctOctave;          // number of octaves
    UBYTE  sCompression;      // Compression mode
    LONG   vol;               // Playback volume
} Voice8Header;

UBYTE version_tag[] = "\0$VER: EasySound V1.0 (2.8.94) ";
///
/// "Prototypes"
Prototype BOOL PlayIff (struct SoundInfo *info, UWORD vol, UBYTE chan, BYTE prio, WORD drate, UWORD repeat, ULONG start, ULONG time, BOOL wait);

Prototype void StopIff (UBYTE chan);

Prototype LONG GetClockSpeed(void);
///
/// "PlayIff"
/****** EasySound/PlayIff **********************************************
*
*   NAME
*       PlayIff -- Play an IFF sample
*
*   SYNOPSIS
*       success = PlayIff(info, vol, chan, prio, drate, times, start,
*                         time, wait)
*       BOOL PlayIff
*           (struct SoundInfo *, UWORD, UBYTE, BYTE, WORD, UWORD, ULONG,
*            ULONG, BOOL)
*
*   FUNCTION
*       Plays an IFF sample. The sample has to be converted to an include
*       file with the aid of the external program 'iff2src'. The resulting 
*       sound data has to be placed in chip memory using e.g. the __chip 
*       keyword.
*
*   INPUTS
*       info        - A (struct SoundInfo *) pointer to the structure.
*       vol         - The volume of the sound (between 0 and 64).
*       chan        - The channel on which the sample will be played, can
*                     be either left or right ;-) . You may use the magic
*                     cookies L0, L1, R0, R1.
*       prio        - State a priority for the sample. (may be something
*                     between -127 and 128.
*       drate       - The sample rate is already stored in the SoundInfo
*                     structure. If you don't want to change the rate,
*                     leave this value 0.
*       times       - How many times do you want this sound to be played.
*                     If you set this value to 0 the sound will be played
*                     nonstop. (You may stop it with StopIff)
*       start       - Where do you want to start the sample, leave this
*                     value to 0 if you want to start at the beginning.
*       time        - How long do you want to play this sound, leave this
*                     value to 0 if you want to play the whole sample.
*       wait        - If you set this value to TRUE your program will wait
*                     until the Sample is played. If it's set to FALSE your
*                     program will continue to run while the sample is
*                     played.
*
*   RESULT
*       success     - Returns TRUE if the sound was played with success,
*                     otherwise the function will return FALSE.
*
*   EXAMPLE
*       PlayIff(&splat_data,64,L0,-35,0,1,0,0,0);
*
*   SEE ALSO
*       StopIff()
*************************************************************************/
BOOL PlayIff (struct SoundInfo *info, UWORD vol, UBYTE chan, BYTE prio,
              WORD drate, UWORD times, ULONG start, ULONG time, BOOL wait) {

    LONG clock;
    BYTE error;
    struct MsgPort *port;
    UWORD period;

    /*
     * Stop the sound on this channel first (if one is still in use)
     */
    StopIff(chan);

    /*
     * Get the ClockSpeed depend on wheter you use a PAL or NTSC Amiga
     */
    clock = GetClockSpeed();

    /*
     * Prepare the complete device stuff.
     */

    period = clock/info->RecordRate+drate;
    
    /*
     * Create a message port
     */
    port = (struct MsgPort *)CreatePort(NULL, 0);
    if(!port) {
        return(FALSE);
    }

    /*
     * Create an IO request:
     */
    IO[chan]=(struct IOAudio *)CreateExtIO(port,sizeof(struct IOAudio));
    if(!IO[chan]) {
      DeletePort(port);
      return(FALSE);
    }
    
    /*
     * Init Audio struct
     */
    IO[chan]->ioa_Request.io_Message.mn_Node.ln_Pri = prio;
    info->channel_bit = 1<<chan;
    IO[chan]->ioa_Data = &(info->channel_bit);
    IO[chan]->ioa_Length = sizeof(UBYTE);

    /*
     * And now open the Audio Device
     */
    error =  OpenDevice(AUDIONAME, 0, (struct IORequest *)IO[chan], 0);
    if(error) {
        DeleteExtIO((struct IORequest *)IO[chan]);
        DeletePort(port);
        IO[chan] = NULL;
        return(FALSE);
    }

    IO[chan]->ioa_Request.io_Flags = ADIOF_PERVOL;
    IO[chan]->ioa_Request.io_Command = CMD_WRITE;
    IO[chan]->ioa_Period = period;
    IO[chan]->ioa_Volume = vol;
    IO[chan]->ioa_Cycles = times;

    if(time)
        IO[chan]->ioa_Length = time;
    else
        IO[chan]->ioa_Length = info->FileLength;

    IO[chan]->ioa_Data = info->SoundBuffer + start;

    /*
     * Here's the output stuff
     */
    BeginIO((struct IORequest *)IO[chan]);
    if(wait)
        WaitIO((struct IORequest *)IO[chan]);
    return(TRUE);
}
///
/// "StopIff"
/****** EasySound/StopIff **********************************************
*
*   NAME
*       StopIff -- Stop an IFF sample
*
*   SYNOPSIS
*       StopIff(chan)
*
*       void StopIff (UBYTE)
*
*   FUNCTION
*       Will stop the sound on the specified audio channel. It'll close the
*       the devices and ports and free the allocated memory. If you call 
*       this function without any sound being played it'll simply return.
*
*   INPUTS
*       chan        - The channel that should be stopped. You may use the
*                     magic cookies L0,L1,R0,R1 for this task.
*
*   SEE ALSO
*       PlayIff()
************************************************************************/
void StopIff (UBYTE chan) {

    /*
     * Test if this channel is REALLY(!) in use, otherwise this function
     * will simply return without doing anything.
     */
    if(IO[chan]) {
        AbortIO((struct IORequest *)IO[chan]);

        if(IO[chan]->ioa_Request.io_Device)
            CloseDevice((struct IORequest *)IO[chan]);

        if(IO[chan]->ioa_Request.io_Message.mn_ReplyPort)
            DeletePort(IO[chan]->ioa_Request.io_Message.mn_ReplyPort);

        DeleteExtIO((struct IORequest *)IO[chan]);
        IO[chan] = NULL;
    }
}
///
/// "GetClockSpeed"
/****** EasySound/GetClockSpeed *****************************************
*
*   NAME
*       GetClockSpeed -- Determine the clock speed for PAL / NTSC
*
*   SYNOPSIS
*       clock = GetClockSpeed()
*
*       LONG GetClockSpeed( void )
*
*   FUNCTION
*       This function returns the clockspeed for e.g samples depending
*       on the system you use (NTSC or PAL).
*
*   SEE ALSO
*       RKRM, Guru Book page 62/63
*************************************************************************/
LONG GetClockSpeed() {

    struct GfxBase *GfxBase;
    LONG clockspeed = CLOCK_PAL;

    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);

    if (GfxBase) {
        if (GfxBase->DisplayFlags & NTSC)
            clockspeed = CLOCK_NTSC;

        if (GfxBase->DisplayFlags & PAL)
            clockspeed = CLOCK_PAL;

        CloseLibrary((struct Library *)GfxBase);
    }

    return clockspeed;
}
///

