#include "commun.h"
#include "gba.h"

//Taille du buffer
#define MIXBUF_SIZE 352
//Temps (nombre de cycles) par échantillon
#define SAMPLE_TIME (280896/MIXBUF_SIZE)

//Buffer du mixer
signed char IN_EWRAM mixbuf[2][MIXBUF_SIZE];
//On alterne celui qu'on lit et celui qu'on écrit (normal!)
int cur_mixbuf=0;
//Activation de la musique et du son par défaut
int bMusique=1, bSons=1;

//Pour la lecture ADPCM
typedef struct ADGlobals
{
	const unsigned char *data;
	int last_sample;
	int last_index;
	const unsigned char *debut, *fin;
} ADGlobals;

ADGlobals ad;

//Buffer ne contenant que des zéros. Les canaux de son qui ne font rien pointent là-dessus
signed char IN_EWRAM buffer_zero[MIXBUF_SIZE];
//Pour les bruitages
signed char *__son=buffer_zero, *__fin_son;
//Décompresse un échantillon ADPCM et le mixe un son PCM.
void IN_IWRAM decode_ad(signed char *dst, const unsigned char *src, const signed char *son2, unsigned int len);

//Paramètre le DMA pour qu'il copie le buffer son.
static void dsound_set_buffer(const signed char *src)
{
	DMA[1].control = 0;

    //Pour laisser au DMA reprendre ses esprits
	asm volatile ("eor r0, r0, r0; eor r0, r0, r0" ::: "r0");

	DMA[1].src = src;
	DMA[1].dst = (void *)0x040000a0;           //Adresse du FIFO A
	DMA[1].count = 1;
	DMA[1].control = DMA_DSTUNCH | DMA_SRCINC | DMA_REPEAT | DMA_U32 | DMA_SPECIAL | DMA_ENABLE;
}

//A chaque période (ici vblank), on va réalimenter le buffer son.
void dsound_vblank(void)
{
    //Le son est terminé?
    if (__son+MIXBUF_SIZE>=__fin_son)
        __son=buffer_zero;                  //Il ne sera plus joué
        
    if (!bMusique)                          //Musique désactivée?
        ad.data=NULL;                       //Oui -> pas de données ADPCM

    else if (ad.data==NULL && ad.debut)         {       //Si la musique est réactivée et le fichier valide
        ad.data=ad.debut;                   //On recommence à jouer la musique au début
        ad.last_sample=0;
        ad.last_index=0;
    }

    if (!bSons)                             //Bruitages désactivés
        __son=buffer_zero;                  //Bruitages placés dans un buffer qui ne fait rien

    if (ad.data)        {                   //Données ADPCM présentes?
        if ((ad.data+(MIXBUF_SIZE>>2))>ad.fin)        {         //Morceau terminé?
            ad.data=ad.debut;               //Et c'est reparti pour un tour!
            ad.last_sample=0;
            ad.last_index=0;
        }
        //Décode le morceau en mixant avec le son. Le son sera de toutes façons mixé, mais s'il pointe sur
        //buffer_zero qui ne contient que des zéros, une addition du son, bien qu'ayant lieu, n'aura aucun
        //effet sur la musique.
    	decode_ad(mixbuf[cur_mixbuf], ad.data, __son, MIXBUF_SIZE/2);
    	//Pour pointer sur le prochain échantillon (taille du buffer divisée par 4), le taux de la musique
        //étant deux fois plus petit que celui des bruitages d'une part, et l'ADPCM divisant la taille du
        //fichier par deux d'une autre part.
    	ad.data += MIXBUF_SIZE >> 2;
	}
	else
        //Mixe le son avec de l'ADPCM nul.
    	decode_ad(mixbuf[cur_mixbuf], buffer_zero, __son, MIXBUF_SIZE/2);

    //Si un bruitage est en cours, on lui ajoute la taille du buffer utilisée
	if (__son!=buffer_zero)
    	__son+=MIXBUF_SIZE;

    //Lance la copie DMA 1
	dsound_set_buffer(mixbuf[cur_mixbuf]);
	//Une fois l'un, une fois l'autre buffer.
	cur_mixbuf = !cur_mixbuf;
}

//Joue une musique (sur le canal ADPCM)
void JoueSon(char *nom_fichier)       {
    signed char *son;
    FICHIER *f;
    son=SymFind(nom_fichier,&f);
    if (son)        {
        __son=son;
        __fin_son=son+f->taille;
    }
//    __son+=100;
}

void start_ad(FICHIER *f)
{
	ad.data = gblData+f->offset;
	ad.last_sample = 0;
	ad.last_index = 0;
	ad.debut = gblData+f->offset;
	ad.fin = gblData+f->offset+f->taille;
}

void JoueMusique(char *nom_fichier)         {
    signed char *son;
    FICHIER *f;
    son=SymFind(nom_fichier,&f);
    if (son)
        start_ad(f);
    else
        ad.data=NULL, ad.debut=NULL, ad.fin=NULL;
}

//Règle la résolution du son
static void set_bias(void)
{
//	asm volatile(".THUMB\nmov r2, #2; lsl r2, #8; swi 0x19\n.ARM\n" ::: "r0", "r1", "r2", "r3");
	SETSNDRES(1);
}

//Gestionnaire d'interruptions
void InterruptHandler(void)
{
    u32 Int_Flag;
    //Désactive les interruptions (on ne doit pas être interrompu pendant les interruptions sinon en
    //général, c'est le plantage garanti).
    REG_IME = 0x00;
    //Sauve le contenu de Interrupt Flag pour plus tard.
    Int_Flag = REG_IF;
    //On vérifie quand-même que c'est la bonne interruption même si c'est la seule possible.
    if (REG_IF & INT_VCOUNT)
        dsound_vblank();
    //Comme pour le PIC16F777A du cours uP1, on doit écrire sur IF pour acquiescer l'interruption (sinon
    //elle serait déclenchée en boucle).
    REG_IF = Int_Flag;
    //Réactive Interrupt Master Enable.
    REG_IME = 0x01;
}

//Initialise le système sonore
void init_sound(void)
{
	TIMER[0].control = 0;
	//Allume le circuit son
	SNDSTAT = SNDSTAT_ENABLE;
	//Volume à fond, sur les deux haut-parleurs
	DMGSNDCTRL = DMGSNDCTRL_LVOL(7) | DMGSNDCTRL_RVOL(7) |
		DMGSNDCTRL_LSQR1 | DMGSNDCTRL_RSQR1 |
		DMGSNDCTRL_LSQR2 | DMGSNDCTRL_RSQR2 |
		DMGSNDCTRL_LTRI | DMGSNDCTRL_RTRI |
		DMGSNDCTRL_LNOISE | DMGSNDCTRL_RNOISE;
		
	DSOUNDCTRL = 0xbb0d;
	TIMER[0].count = 0x10000 - SAMPLE_TIME;
	TIMER[0].control = TIMER_16MHZ | TIMER_ENABLE;

	TRICTRL = TRICTRL_2X32 | TRICTRL_BANK(0) | TRICTRL_ENABLE;
	TRILENVOL = 0;
	TRIFREQ = 1046 | FREQ_RESET | FREQ_HOLD;
	SQR1SWEEP = SQR1SWEEP_OFF;
	set_bias();
}

//Active ou désactive le chip son ainsi que les interruptions associées.
void ModeSon(int mode)      {
    if (mode==0)            {
    	REG_IME=0;
    	REG_IE = 0;
    	REG_DISPSTAT = 0;
    	REG_INTERRUPT=0;
    	REG_IME=1;
    }
    else if (mode==1)       {
    	REG_IME=0;
    	REG_IE = INT_VCOUNT;
    	REG_DISPSTAT = (1<<5)|(50<<8);
    	REG_INTERRUPT=(u32)&InterruptHandler;
    	REG_IME=1;
    }
}



