/*
 * Esempio di utilizzo del dispositivo audio di Amiga
 *
 * Scritto da Antonello Biancalana, ProMIND per AmigaMagazine
 *
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <proto/all.h>
#include <stdlib.h>
#include <stdio.h>

void ExitPrg(void);

unsigned char ChanMask[] = { 1,2,4,8 }; /* maschere di allocazione dei canali */

struct IOAudio *AData=NULL;         /* struttura dati per audio.device */
struct IOAudio *Control=NULL;       /* struttura dati usata per il controllo del device */
struct MsgPort *AudioPort=NULL;     /* porta messaggi */
struct MsgPort *ControlPort=NULL;   /* porta messaggi per richiesta controllo */
struct Message *AudioMsg=NULL;      /* usato per i messaggi trasmessi da audio.device */
short error=TRUE;                   /* flag di dispositivo aperto */
char *samples=NULL;                 /* puntatore alla forma d'onda */
long Nofsamples;                    /* numero di campioni della forma d'onda */

void main(int argc, char **argv)
{
    int i, j, wave;
    char *copy;

    Nofsamples=4096;

    /* creazione porta messaggi */

    AudioPort=CreatePort("Porta_Audio",0);

    if(!AudioPort)
    {
        printf("Impossibile allocare una porta\n");
        ExitPrg();
    }

    ControlPort=CreatePort("Control_Port",0);

    if(!ControlPort)
    {
        printf("Impossibile allocare una porta\n");
        ExitPrg();
    }

    /* Allocazione memoria per la struttura dati */

    AData=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);

    if(!AData)
    {
        printf("memoria insufficiente per la struttura AData\n");
        ExitPrg();
    }
        
    Control=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);

    if(!Control)
    {
        printf("memoria insufficiente per la struttura Control\n");
        ExitPrg();
    }
        
    /* preparazione della struttura con i dati */
    /* necessari all'apertura del dispositivo audio */

    AData->ioa_Request.io_Message.mn_ReplyPort=AudioPort;   /* porta messaggi */
    AData->ioa_Request.io_Message.mn_Node.ln_Pri=0;         /* priorità */
    AData->ioa_Request.io_Command=ADCMD_ALLOCATE;           /* desideriamo allocare un canale */
    AData->ioa_Request.io_Flags=ADIOF_NOWAIT;               /* senza attendere eventuali liberazioni */
    AData->ioa_AllocKey=0;                                  /* chiave di allocazione canale (usato da audio.device) */
    AData->ioa_Data=ChanMask;                               /* array contenente la maschera di allocazione dei canali */
    AData->ioa_Length=sizeof(ChanMask);                     /* dimensione dell'array di allocazione canali */

    /* apertura del dispositivo audio */

    error=OpenDevice("audio.device",0L,(struct IORequest *)AData,0L);

    if(error)
    {
        printf("Impossibile aprire audio.device\n");
        ExitPrg();
    }

    *Control=*AData;    /* copia della struttura AData su Control */

    Control->ioa_Request.io_Message.mn_ReplyPort=ControlPort;   /* impostazione della porta */

    /* allocazione della memoria per la forma d'onda */

    samples=(char *)AllocMem(Nofsamples,MEMF_CHIP|MEMF_CLEAR);

    if(!samples)
    {
        printf("Memoria insufficiente per la forma d'onda\n");
        ExitPrg();
    }

    /* costruzione di una forma d'onda quadra */

    copy=samples;   /* prendiamo l'indirizzo iniziale della forma d'onda */
    wave=127;       /* valore del campione iniziale */

    for(i=0; i<32; i++) /* forma d'onda costituita da 16 cicli completi (32/2) */
    {
        for(j=0; j<128; j++)    /* preparazione di un mezzo ciclo dell'onda */
            *copy++=wave;

        wave = -wave;           /* preparazione del valore inerente il prossimo mezzo ciclo */
    }

    /* inviamo un comando di riproduzione audio al canale allocato */

    AData->ioa_Request.io_Command=CMD_WRITE;    /* comando di riproduzione audio */
    AData->ioa_Request.io_Flags=ADIOF_PERVOL;   /* impostare il valore del periodo e del volume */
    AData->ioa_Data=(char *)samples;            /* puntatore alla forma d'onda */
    AData->ioa_Length=Nofsamples;               /* lunghezza della forma d'onda */
    AData->ioa_Period=200;                      /* periodo di riproduzione */
    AData->ioa_Volume=64;                       /* volume */
    AData->ioa_Cycles=3;                        /* numero di ripetizioni */

    /* Invio del comando ad audio.device */

    printf("Inizio riproduzione del suono\n");

    BeginIO((struct IORequest *)AData);

    /* attendere il completamento della riproduzione */

    Wait(1L<<AudioPort->mp_SigBit);
    AudioMsg=GetMsg(AudioPort);

    printf("Souno terminato\n");

    Delay(150);

    /* inviamo un comando di riproduzione audio al canale allocato */

    printf("Riproduzione suono e alterazione del periodo e del volume\n");

    AData->ioa_Request.io_Command=CMD_WRITE;    /* comando di riproduzione audio */
    AData->ioa_Request.io_Flags=ADIOF_PERVOL;   /* impostare il valore del periodo e del volume */
    AData->ioa_Data=(char *)samples;            /* puntatore alla forma d'onda */
    AData->ioa_Length=Nofsamples;               /* lunghezza della forma d'onda */
    AData->ioa_Period=350;                      /* periodo di riproduzione */
    AData->ioa_Volume=64;                       /* volume */
    AData->ioa_Cycles=6;                        /* numero di ripetizioni */

    BeginIO((struct IORequest *)AData);

    Control->ioa_Request.io_Command=ADCMD_WAITCYCLE;    /* attesa termine primo ciclo */
    Control->ioa_Request.io_Flags=NULL;                 /* nessun flag */
    BeginIO((struct IORequest *)Control);

    /* attesa conclusione comando ADCMD_WAITCYCLE */

    Wait(1L<<ControlPort->mp_SigBit);
    AudioMsg=GetMsg(ControlPort);

    printf("Primo ciclo concluso\nVariazione del periodo e del volume\n");

    Control->ioa_Request.io_Command=ADCMD_PERVOL;       /* variazione periodo e volume */
    Control->ioa_Period=250;                            /* Nuovo periodo */
    Control->ioa_Volume=40;                             /* Nuovo volume */
    BeginIO((struct IORequest *)Control);

    Wait(1L<<AudioPort->mp_SigBit);
    AudioMsg=GetMsg(AudioPort);

    printf("Souno terminato\n");

    Delay(150);

    /* inviamo un comando di riproduzione audio al canale allocato */

    printf("Riproduzione suono e alterazione del periodo\ne del volume mediante ADIOF_SYNCCYCLE\n");

    AData->ioa_Request.io_Command=CMD_WRITE;    /* comando di riproduzione audio */
    AData->ioa_Request.io_Flags=ADIOF_PERVOL;   /* impostare il valore del periodo e del volume */
    AData->ioa_Data=(char *)samples;            /* puntatore alla forma d'onda */
    AData->ioa_Length=Nofsamples;               /* lunghezza della forma d'onda */
    AData->ioa_Period=350;                      /* periodo di riproduzione */
    AData->ioa_Volume=40;                       /* volume */
    AData->ioa_Cycles=6;                        /* numero di ripetizioni */

    BeginIO((struct IORequest *)AData);

    Control->ioa_Request.io_Command=ADCMD_PERVOL;       /* variazione periodo e volume */
    Control->ioa_Request.io_Flags=ADIOF_SYNCCYCLE;      /* attendere la fine del ciclo */
    Control->ioa_Period=250;                            /* Nuovo periodo */
    Control->ioa_Volume=64;                             /* Nuovo volume */
    BeginIO((struct IORequest *)Control);

    Wait(1L<<AudioPort->mp_SigBit);
    AudioMsg=GetMsg(AudioPort);

    printf("Souno terminato\n");

    /* fine */

    ExitPrg();
}

void ExitPrg(void)
{
    if(samples)
        FreeMem(samples,Nofsamples);

    if(AudioPort)
        DeletePort(AudioPort);

    if(ControlPort)
        DeletePort(ControlPort);

    if(!error)
        CloseDevice((struct IORequest *)AData);

    if(AData)
        FreeMem(AData,sizeof(struct IOAudio));

    if(Control)
        FreeMem(Control,sizeof(struct IOAudio));

    exit(0);
}

