La gestion du SON |
|
|
|
Le son est l'une des caractéristiques essentielles des applications d'aujourd'hui, voyons donc comment SDL tire son épingle du jeu de ce côté ! |
|
|
|
Du SON !!! |
|
|
|
Je vais de suite décevoir certains : il n'existe pas de primitive simple pour jouer un son ! Qu'est ce que je veux dire par là ? Je m'explique ! SDL est construite d'une manière générale sur les fonctions de rappelles et fonctionne via ces dernières. Donc dans le cas du son, vous devez passer par une petite étape de création d'une fonction qui fournira à SDL des tampons audio lorsque SDL en a besoin ! Cela va s'éclairer très vite ! Je vais pour une fois détailler les strcutures SDL liées à la gestion audios ! (Je tiens ici vivement à remercier Stephane Peter pour ses articles sur SDL notamment pour cette même partie) typedef struct { int freq; Uint16 format; Uint8 channels; Uint16 samples; Uint32 size; void (*callback)(void *userdata, Uint8 *stream, int len); void *userdata; }SDL_AudioSpec; freq indique la fréquence que de l'échantillon sonore (par ex 44100) format indique à SDL le format d'enregistrement du tampon, ainsi ce format peut-il prendre les valeurs suivantes :
channels vaut 1 si l'on veut du mono ou 2 pour de la stéréo. samples c'est la taille du tampn audio. Il s'agit de la taille du tampon utilisé en interne. Les valeurs générales sont 512 et 1024. Plus grand risque d'entrainer un décalage dans la sortie des échantillons, et un tampon de taille inférieure demandera une consommation CPU accrue. size taille en octets du tampon, calculés par SDL_OpenAudio(). (*callback)(void *userdata, Uint8 *stream, int len) adresse de la fonction de rappelle appelé lorsque le tampon est vide. *userdata argument de la fonction ci-dessus, à la charge de l'utilisateur.
Voilà la structure générale de chargement d'un tampon audio. Vous devez tout d'abord initialiser le canal audio en passant le drapeau SDL_INIT_AUDIO à la fonction SDL_Init(), (il semble cependant que ce ne soit plus une obligation) puis appeler la fonction suivante : int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained); Alors que fait cette foncion ? Elle vous permet d'ouvrir le périphérique audio. Vous remplissez les champs de la structure avec les valeurs que vous désirez et SDL tente au vue des contraintes matérielles de satisfaire à votre requête en plaçant dans la variable *obtained les paramètres effectivement alloués pour le périphérique. Si obtained vaut NULL après un appel à cette fonction, alors les valeurs effectivement demandées dans desired seront utilisées ! Ainsi vous pouvez forcer l'application de vos paramètres en passant direcement NULL en second paramètre, SDL fera alors son possible pour pallier les déficiences matérielles. La fonction retourne -1 si l'ouverture du périphérique à échouer ou si l'initialisation audio à échouer. Un exemple semble judicieux maintenant :-) SDL_AudioSpec desired, obtained; desired.freq = 44100; desired.format = AUDIO_S16LSB; desired.channels = 2; desired.samples = 1024; desired.callback = remplit_tampon_audio; //fonction de rappelle desired.userdata = NULL; Finalement quand on y regarde de plus près c'es très simple, on le fait une fois avant d'essayer de toucher aux tampons et puis c'est tout ! Puis on appelle SDL_OpenAudio ainsi : if(SDL_OpenAudio(&desired,&obtained) == -1){ fprintf(stderr,"Impossible d'ouvrir le périphérique audio %s\n",SDL_GetError(); return -1; } Là il est recommandé de vérifier les valeurs fournies par obtained. Pour l'instant le remplissage par la fonction de rappelle du tampon n'a pas débuté. Il ne commence qu'après l'appel de SDL_PauseAudio(0) , le passage du 0 signale à SDL de débuter le remplissage du tampon.
Bien maintenant que notre périphérique audio est ouvert que l'on est assuré que le format audio demandé fonctionne et que l'on a lancé le remplissage, on se demande avec quoi on va le remplir ! Avec un fichier WAV pardi ! OK tout comme SDL ne gère que le format BMP directement, SDL ne gère que le format WAV directement. Mais l'utilisation d'une librairie additionnelle basée sur SDL vous fournira l'accès aux formats MP3, MOD et MIDI ! Winamp tiens toi bien ;-) Cette lib s'appelle SDL_Mixer et se trouve bien entendue sur le site de SDL. Je ferai une annexe la dessus bientôt ! On va donc charger en mémoire un fichier WAV, mais on le fera d'une manière générale avant l'initialisation ci-dessus, afin que SDL n'attende pas le chargement du fichier avant de lancer la fonction de rappelle. Pour ça on utilise : SDL_AudioSpec *SDL_LoadWAV(const char *file, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); KaBoom... ça c'est de la fonction :) file vous l'aurez deviné, il s'agit du chemin d'accès au fichier WAV (données brutes ou format compressé ADPCM). *spec correspond au données décrivant le format du fichier, c'est également cette adresse qui sera retournée par la fonction **audio_buff correspond l'adresse du tampon. *audio_len est la taille en octets du tampon. Si l'appel de cette fonction vous retourne NULL, le chargement à échouer. Appelez alors la fonction SDL_FreeWAV(), afin de librérer les ressources. Voilà enfin nous savons charger un fichier WAV et ouvrir un accès vers le périphérique audio, que nous reste-t-il à faire maintenant ...? La fonction de rappelle bien sur ! En fait, mise à part son prototype obligatoire où seul son nom est laissée à votre choix, c'est à vous qu'il revient de savoir quoi faire de cette fonction. Pour le moment vous ne désirez certainement que jouer un fichier WAV, et bien si l'on regarde dans la doc, on trouve un exemple qui convient magiquement ! Je vous le met : void remplit_tampon_audio(void *udata, Uint8* stream, int len){ if ( audio_len == 0 ) return; len = ( len > audio_len ? audio_len : len ); SDL_MixAudio(stream, audio_buf, len, SDL_MIX_MAXVOLUME); audio_buf += len; audio_len -= len; } Bon comment ça marche ? Je précise que audio_len et audio_buf sont des variables globales à l'application déclarées comme suit :
On regarde d'abord que le tampon n'est pas à la fin. Si ce n'est pas le cas on positionne le tampon à la bonne destination. On appelle ensuite la fonction suivante : void SDL_MixAudio(Uint8 *dst, Uint8 *src, Uint32 len, int volume); Cette fonction permet de copier le flux actuel du tampon vers sa destination avec la taille désirée de données et le volume. Sachant que SDL_MIX_MAXVOLUME est une macro pour le colume maximum. Il s'agit d'un entier de 0 à 128. Enfin on place le tampon au prochain endroit que l'on prendra. Je dois reconnaître que là ce ne sera peut être pas simple au premier abord, mais plongez vous dans le code que je vous proposerai ci dessous et vous verrez ça viendra vite ! |
|
|
|
La conversion audio |
|
|
|
SDL est étonnante ! L'API fournie en effet des routines et une structure de conversion de données audios. Ce qui représente un réel avantage par rapport à d'autres APIs. Voilà la structure : typedef struct SDL_AudioCVT{ int needed; Uint16 src_format; Uint16 dst_format; double rate_incr; Uint8 *buf; int len; int len_cvt; int len_mult; double len_ratio; void (*filters[10])(struct SDL_AudioCVT *cvt, Uint16 format); int filter_index; }SDL_AudioCVT; Ouf ! Beaucoup de champ, n'est-ce pas ? needed si la conversion est réalisable la valeur sera de 1 src_format format audio de la source dst_format format audio de la destination rate_incr rapport de conversion pour la fréquence *buf tampon contenant les données audios len longueur du tampon d'origine len_cvt longueur du tampon de destination len_mult c'est un coefficiant tel que le tampon aura pour longueur : len*len_mult len_ratio ratio entre la taille source et destination, la taille finale est donc de len_ratio*len (*filters[10])(struct SDL AudioCVT *cvt, Uint16 format) ensemble des filtres de conversions sur le tampon filter_index filtre de conversion en cours
Vous n'aurez pas à remplir manuellement la plupart de ces champs en général ! En fait vous appelez la fonction suivante : int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, Uint16 src_format, Uint8 src_channels, int src_rate, Uint16 dst_format, Uint8 dst_channels, int dst_rate); On fournit d'abord un objet SDL_AudioCVT non initialisé mais juste déclaré, puis son format, le nombre de canaux afféctés et enfin sa fréquence d'échantillonage comme ci-dessus. Si la fonction réussie elle retourne NULL. Bien mais ce n'est pas tout vous devez aussi préciser l'adresse et la taille du tampon que vous désirez convertir. Vous devez donc allouer un espace suffisant pour recevoir le tampon convertit, il arrivera souvent que celui-ci soi plus grand que son orginal. Donc vous devrez appeler la fonction malloc() (ou un simile) comme ceci : adr_tampon = (Uint8 *)malloc(len*len_mult); Pourquoi len*len_mult ? En passant len*len_mult plutot que len vous etes sur d'allouer la place necessaire à la transformation, ainsi vous évitez des erreurs de mémoires. Finalement il ne vous rest plus qu'à appeler la fonction suivante pour réaliser la conversion : int SDL_ConvertAudio(SDL_AudioCVT *cvt); En passant comme paramètre l'objet SDL_AudioCVT que vous venez de formatter.
Voilà je finirai ce cours sur la prtie SON par deux dernières fonctions :
Fonction que l'on appel à la fin du service audio afin de libérer le périphérique audio.
Cette fonction vous retourne une valeur vous indiquant si le périphérique est en SDL_AUDIO_PAUSED, en SDL_AUDIO_STOPPED ou SDL_AUDIO_PLAYING.
J'ai donc fini avec la parti son, je vous fournit ici un ensemble de deux fichiers sources. L'un tout simple ne s'occupant que de l'initialisation et le lancement d'un fichier WAV, le second est écrit par Stephane Peter et correspond à une conversion audio. J'oubliais une chose... pensez à inclure le fichier SDL_audio.h dans vos programmes ! | |
|
|
|