/* SDL Simple Player : V2.0 frederic.meslin@laposte.net Un lecteur / chargeur de sons simples pour SDL. Mise à jour : 21/06/07 */ //Headers #include #include "mtypes.h" #include "SDL_SmpPlay.h" #include "math.h" //Variables globales SMPPLAYLECTEUR Lecteur; SMPPLAYSON * CollectionSons; double PhaserTable[100]; //Routines sonores bool SMPPlay_InitAudio (uint Frequence, uint16 Format) { /* Routine principale qui initialise le lecteur */ int16 Index; double Angle; //Créé le lecteur Lecteur.AudioSpec.freq = Frequence; Lecteur.AudioSpec.format = Format; Lecteur.AudioSpec.channels = 2; Lecteur.AudioSpec.samples = TAILLESAMPLE; Lecteur.AudioSpec.callback = _AudioCallBack; Lecteur.AudioSpec.userdata = NULL; //Démarrage if (SDL_OpenAudio(&Lecteur.AudioSpec, NULL) != 0) return false; CollectionSons = NULL; //Initialise les canaux for (Index = 0; Index < MAXCANAUX; ++Index) _InitCanal (Index); //Initialise le phaser Angle = 0; for (Index = 0; Index < 100; ++Index) { PhaserTable[Index] = 1 + sin (Angle); Angle += 0.03142; } Lecteur.Etat = SMPPLAYPause; return true; } void SMPPlay_QuitAudio() { /* Routine principale qui termine le lecteur et vide les sons chargés */ SDL_PauseAudio(1); SDL_CloseAudio(); _ViderSons(); Lecteur.Etat = SMPPLAYBrut; } bool SMPPlay_LireSonC (SMPPLAYSON * Son, uint Index) { /* Joue un son sur un canal particulier */ //Vérifications SMPPLAYCANAL * Canal; if (Index > MAXCANAUX) return false; if (Son->Charge == false) return false Canal = &Lecteur.Canaux[Index]; if (Canal->Actif) return false; //Lecture SDL_LockAudio(); Canal->Tampon = Son->Tampon; Canal->Longueur = Son->Longueur; Canal->LongueurInit = Son->Longueur; Canal->Position = 0; Canal->Actif = true; SDL_UnlockAudio(); if (Lecteur.Etat == SMPPLAYPause) { Lecteur.Etat = SMPPLAYLecture; SDL_PauseAudio(0); } return true; } bool SMPPlay_LireSonP (SMPPLAYSON * Son, uint IndexD, uint IndexF) { /* Joue un son en choisissant un canal libre parmis la plage [IndexD-IndexF] */ int Index; //Vérifications if (IndexD >= IndexF) return false; if (IndexD > MAXCANAUX) return false; if (IndexF > MAXCANAUX) return false; //Recherche Index = IndexD; while ((Index != IndexF) && (SMPPlay_LireSonC (Son, Index) == false)) ++Index; if (Index = IndexF) return false; else return true; } void SMPPlay_ActPhaserCanal (uint Index, bool Actif) { /* Active ou désactive le phaser d'un canal */ if (Index > MAXCANAUX) return; Lecteur.Canaux[Index].Phaser = Actif; } void SMPPlay_ActEchoCanal (uint Index, bool Actif) { /* Active ou désactive l'écho d'un canal */ if (Index > MAXCANAUX) return; Lecteur.Canaux[Index].Echo = Actif; } void SMPPlay_PhaserCanal (uint Index, uint8 Vitesse, uint8 Intensite) { /* Rêgle les paramêtres du phaser d'un canal */ if (Index > MAXCANAUX) return; if (Vitesse != 0) Lecteur.Canaux[Index].PhasVit = (double) Vitesse / 2550.0; if (Intensite != 0) Lecteur.Canaux[Index].PhasInt = (double) Intensite / 2.55; } void SMPPlay_EchoCanal (uint Index, uint8 Dec, uint8 NB) { /* Rêgle les paramêtres de l'écho d'un canal */ if (Index > MAXCANAUX) return; Lecteur.Canaux[Index].EchoNB = NB; Lecteur.Canaux[Index].EchoDec = Dec; } void SMPPlay_VolumeCanal (uint Index, uint8 Volume) { /* Rêgle le volume d'un canal */ if (Index > MAXCANAUX) return; Lecteur.Canaux[Index].Volume = Volume; Lecteur.Canaux[Index].VolumeInit = Volume; } void SMPPlay_BalanceCanal (uint Index, uint8 Balance) { /* Rêgle la balance d'un canal */ if (Index > MAXCANAUX) return; Lecteur.Canaux[Index].Balance = Balance; } SMPPLAYSON * SMPPlay_ChargerWAV(char * Fichier) { /* Chargement d'un éffet sonore en Wav */ SMPPLAYSON * Son; //Création d'un son Son = _CreerSon(); if (Son == NULL) return NULL; //Chargement du fichier if (SDL_LoadWAV(Fichier, &Son->AudioSpec, &Son->Tampon, &Son->Longueur) == NULL) { printf "[SMPPLAY] Impossible du charger le son %s !\n", Fichier); Son->Charge = false; Son->Tampon = NULL; }else Son->Charge = true; return Son; } void SMPPLAY_Decharger(SMPPLAYSON * Son) { /* Décharge un éffet sonore */ if (Son->Tampon != NULL) SDL_FreeWAV (Son->Tampon); Son->Tampon = NULL; Son->Charge = false; } /* Routines internes */ void _AudioCallBack(void *userdata, Uint8 *stream, int len) { /* Procédure appelée pour le remplissage de la mémoire sonore et qui éffectue le mixage des canaux */ SMPPLAYCANAL * Canal; static int16 * Buffer1S; static uint16 * Buffer1U; static int16 * Buffer2S; static uint16 * Buffer2U; static uint8 * Buffer; int32 Longueur; int32 Reste; int32 Offset1; int32 Offset2; int32 Index; int32 Temp; uint16 Fact1; uint16 Fact2; uint16 Lim; uint16 Cumul; //Initilisation Lecteur.CanauxOcc = 0; len >>= 2; //Vérifie les canaux for (Index = 0; Index < MAXCANAUX; ++Index) { //Vérifications Canal = &Lecteur.Canaux[Index]; if (Canal->Actif) if (Canal->Tampon != NULL) { //Initialisation Lecteur.CanauxOcc++; Buffer1S = (int16 *) stream; Buffer1U = (uint16 *) stream; Offset1 = 0; Cumul = 0; //Mixage Mixage : Buffer = Canal->Tampon + Canal->Position; Fact1 = Canal->Volume * Canal->Balance; Fact2 = Canal->Volume * (256 - Canal->Balance); Longueur = Canal->Longueur >> 1; Offset2 = 0; switch (Lecteur.AudioSpec.format) { case AUDIO_S16 : //Remplissage du morceau //principal Buffer2S = (int16 *) Buffer; while ((Offset1 < len) && (Offset2 < Longueur)) { //Mixage Droite Temp = *Buffer1S + ((*Buffer2S * Fact1) >> 16); if (Temp > 32766) Temp = 32767; else if (Temp < -32766) Temp =-32767; *Buffer1S = (uint16) Temp; ++Buffer1S; //Mixage Gauche Temp = *Buffer1S + ((*Buffer2S * Fact2) >> 16); if (Temp > 32766) Temp = 32767; else if (Temp < -32766) Temp =-32767; *Buffer1S = (uint16) Temp; ++Buffer1S; //Décalage + Effet phaser if (Canal->Phaser) { //Angle du phaser Canal->PhasAng += Canal->PhasVit; if (Canal->PhasAng > 99) Canal->PhasAng = Canal->PhasAng - 100; //Décalage Lim = (uint16) (PhaserTable[(uint16) Canal->PhasAng] * Canal->PhasInt); if (Cumul < Lim) { Buffer2S++; Offset2++; Cumul++; }else Cumul = 0; }else{ Buffer2S++; Offset2++; } Offset1++; } break; case AUDIO_U16 : //Remplissage du morceau //principal Buffer2U = (uint16 *) Buffer; while ((Offset1 < len) && (Offset2 < Longueur)) { //Mixage Droite Temp = *Buffer1U + ((*Buffer2U * Fact1) >> 16); if (Temp > 65534) Temp = 65535; *Buffer1U = (uint16) Temp; ++Buffer1U; //Mixage Gauche Temp = *Buffer1U + ((*Buffer2U * Fact2) >> 16); if (Temp > 65534) Temp = 65535; *Buffer1U = (uint16) Temp; ++Buffer1U; //Décalage + Effet phaser if (Canal->Phaser) { //Angle du phaser Canal->PhasAng += Canal->PhasVit; if (Canal->PhasAng > 99) Canal->PhasAng = Canal->PhasAng - 100; //Décalage Lim = (uint16) (PhaserTable[(uint16) Canal->PhasAng] * Canal->PhasInt); if (Cumul < Lim) { Buffer2U++; Offset2++; Cumul++; }else Cumul = 0; }else{ Buffer2U++; Offset2++; } Offset1++; } break; default : break; } //Actualisation Offset2 <<= 1; Reste = len - Offset1; Canal->Position += Offset2; Canal->Longueur -= Offset2; //Ajout du rajout //si tampon non remplit if (Canal->Longueur == 0) { if (Canal->Echo) { if (Canal->EchoPos < Canal->EchoNB) { //Une réverbération Canal->Longueur = Canal->LongueurInit; Canal->Position = 0; Canal->Volume = (uint8) (((uint16)Canal->Volume * Canal->EchoDec) >> 8); Canal->EchoPos++; if (Reste > 0) goto Mixage; }else{ //Fin du cycle Canal->EchoPos = 0; Canal->Volume = Canal->VolumeInit; Canal->Actif = false; } }else Canal->Actif = false; }else{ if (Canal->Phaser) if (Reste > 0) goto Mixage; } } } //Arrête l'audio if (Lecteur.CanauxOcc == 0) { SDL_PauseAudio(1); Lecteur.Etat = SMPPLAYPause; } } void _InitCanal (uint Index) { /* Initialise un canal */ SMPPLAYCANAL * Canal; Canal = &Lecteur.Canaux[Index]; //Configuration par défaut Canal->Tampon = NULL; Canal->LongueurInit = 0; Canal->Longueur = 0; Canal->Position = 0; Canal->Balance = 128; Canal->VolumeInit = 64; Canal->Volume = 64; Canal->Echo = false; Canal->EchoPos = 0; Canal->EchoNB = 5; Canal->EchoDec = 128; Canal->Phaser = false; Canal->PhasVit = 0.001; Canal->PhasInt = 5.0; Canal->PhasAng = 0; Canal->Actif = false; } SMPPLAYSON * _CreerSon() { /* Créé un son dans la collection */ SMPPLAYSON * Son; Son = (SMPPLAYSON *) malloc (sizeof (SMPPLAYSON)); if (Son == NULL) { printf "[SMPPLAY] Impossible de créér un son!\n"); return NULL; } Son->Precedent = CollectionSons; Son->Charge = false; CollectionSons = Son; return Son; } void _ViderSons() { /* Vide la collection */ SMPPLAYSON * Son; SMPPLAYSON * SonP; //Parcours la collection Son = CollectionSons; while (Son != NULL) { //Détruit les objets SonP = Son->Precedent; if (Son->Tampon != NULL) SDL_FreeWAV (Son->Tampon); free (Son); Son = SonP; } CollectionSons = NULL; return; }