Maitrise d'informatique


Pour télécharger la bibiothèque kwav, "shift-cliquez" sur ce lien: PACK_LIB.tgz

Une fois décompressé, ce repertoire contient les fichiers sources, un fichier README, ainsi qu'un Makefile pour compiler et installer automatiquement les bibliotheques.


Karim Barkati
Maîtrise d'Informatique
Septembre 2000
 
 

Programmation de bibliothèques C compatibles C++ pour la gestion du son sous Linux
 

Directeur de recherche : M. Daniel Goossens
Université Paris 8 / Vincennes - Saint-Denis
 
 



Remerciements

Je remercie les enseignants du département d'Informatique qui ont façonné mon approche de l'informatique : Harald WERTZ, Jean MEHAT, Roger TANGUY, Jean-François DEGREMONT, ALI CHERIF, Vincent BOYER, Daniel HECKER, Eric DAUBRESSE, et plus particulièrement Vincent LESBROS.

Je souhaite aussi remercier les enseignants du département de Musicologie qui m'ont donné le goût de l'informatique musicale, dans l'ordre chronologique : Ricardo JACOBSON, Martin LALIBERTE, Mario MARY, José-Manuel LOPEZ-LOPEZ, Makis SOLOMOS, Anne SEDES, et Horacio VAGGIONE.

Je remercie mes camarades Alain ZHU, Hervé FERDINAND, Benjamin DRIEU, et Arnauld MICHELIZZA, administrateur du " bocal ", pour leurs conseils techniques et leurs relectures éclairées, ainsi qu'Emmanuelle MEUNIER pour ses corrections patientes.

Je remercie enfin Daniel GOOSSENS, mon directeur de recherche, pour sa générosité et sa perspicacité.


Résumé

Ce travail d'une année tente de combler un manque dans notre département d'Informatique : l'absence d'outils facilitant l'utilisation du son dans les programmes en langages C ou C++ sous Linux. Il a abouti à la création des deux bibliothèques kwav et kfft, et du logiciel kediteur. La bibliothèque kwav permet de manipuler du son de manière relativement simple, en gérant automatiquement le pilote de la carte son et le format wav, tandis que kfft propose des fonctions pour l'analyse et la resynthèse du son d'après la transformée de Fourier. Le programme kediteur permet une édition graphique des fichiers sons au format wav, avec des opérations de lecture, d'enregistrement, de sauvegarde, de découpage, d'affichage des paramètres audionumériques, et d'analyse fréquentielle graphique. Ces outils sont en service à l'université depuis un semestre, et sont documentés par un manuel en ligne.

Ce mémoire présente d'une part l'utilisation de ces outils, illustrée par des programmes simples, et d'autre part leur développement, avec les notions et les conventions importantes qui concernent les bibliothèques en langage C et l'audionumérique.



Table des matières

I. Introduction *

II. Développement de bibliothèques *

A. Définitions *
1. Fichiers d'en-tête *
2. Archives *
3. Fichiers partagés *

B. La bibliothèque kwav *
1. Principe *
2. Utilisation *

a) Comment lire un fichier wav ? *
b) Comment enregistrer du son dans un fichier wav ? *
c) Comment modifier du son ? *
d) Comment récupèrer le nom de fichier passé dans la ligne de commande ? *
3. Analyse thématique interne * a) Ergonomie générale *
b) Gestion du flux audionumérique *
c) Gestion des entrées et sorties de la carte son *
d) Gestion du format wav *
e) Gestion de l'écriture et de la lecture sur le disque dur *
f) Gestion de la mémoire *
g) Gestion de la ligne de commande *


C. La bibliothèque kfft *
1. Présentation de la FFT *
2. Utilisation *

D. Le programme kediteur *
1. Visualisation du son *

a) Comment afficher la forme d'onde d'un son ? *
b) Comment afficher le spectre d'un son ? *


E. Pour aller plus loin *
1. Principes de développement d'une bibliothèque sous Linux *

a) Conventions de nommage et de compilation *
b) Le manuel *
2. Fonctions sytèmes pour la gestion de la carte son *
III. Conclusion *


  1. Introduction

  2. Le son reste encore le parent pauvre de l'informatique, comparé au graphisme notamment. En général, les ordinateurs sont mal ou non équipés pour le son. Les outils logiciels aussi font défaut, en particulier pour la programmation, puisqu'il existe de nombreuses bibliothèques graphiques, comme X11, QT, ou TK, mais une seule bibliothèque sonore à ma connaissance sous Unix. Elle se nomme AudioFile (AUDIOFILE, 1993), et repose sur une architecture complexe client / serveur, semblable à celle de X11. Or, plusieurs domaines de recherche en informatique nécessitent des outils sonores simples pour s'épanouir, comme la synthèse sonore, la reconnaissance vocale, ou l'informatique musicale.

    Le département d'informatique de l'université Paris 8 ne possède pas de bibliothèque sonore. L'un des objectifs de ce travail est de proposer aux étudiants de ce département des outils qui leur permettent de gérer et d'analyser du son simplement dans leurs programmes en langage C ou C++. Matériellement, nos ordinateurs alpha du " bocal " (notre salle d'informatique) sont déjà aptes à gérer du son, puisqu'ils sont tous équipés d'une carte son avec une entrée et une sortie audio, d'un petit haut-parleur et des périphériques systèmes nécessaires (TRANTER 1999). Cependant, lorsque l'on souhaite programmer du son sur ces machines, deux difficultés surgissent : la première réside dans l'appel de primitives assez absconses pour la configuration la carte son, et la seconde dans l'absence de format standard pour les fichiers audionumériques.

    Afin de parer à ces difficultés, cette maîtrise comprend deux bibliothèques, kwav et kfft, un éditeur graphique de fichiers audio, kediteur, ainsi que ce doument qui détaille l'utilisation et le fonctionnement de ces programmes. L'éditeur de sons permet à la fois de démontrer les potentialités de ces bibliothèques, et de témoigner de leur bon fonctionnement lors des opérations de maintenance. Les bibliothèques sont en libre-service à l'université depuis le mois de février 2000. Elles sont documentées par un manuel en ligne (taper man kwav sous shell), et les administrateurs possèdent une version complète pour réinstaller, en cas de besoin, tout ou partie des bibliothèques. Quelques étudiants les ont d'ailleurs utilisées ce semestre pour leurs projets de licence.

    Comme l'expliquent Brian KERNIGHAN et Denis RITCHIE, les principaux créateurs du C, " les fonctions partitionnent les gros traitements en tâches plus petites, et elles permettent de construire des programmes à partir de briques déjà écrites, au lieu de tout recommencer à zéro. Les fonctions bien conçues cachent les détails de leur fonctionnement aux parties du programme qui n'ont pas besoin de les connaître, ce qui clarifie l'ensemble et facilite les modifications ultérieures. " (KERNIGHAN et RITCHIE 1997, p. 67). Dans ce sens, le développement d'une bibliothèque constitue un exercice difficile, car il faut dépasser ses propres réflexes de programmation pour imaginer les besoins des autres programmeurs. Une attention particulière a donc été portée à la conception de l'ensemble : traitements à regrouper, noms des fonctions et des variables, pertinence des arguments, des valeurs de retour, et de leurs types.
     


  1. Développement de bibliothèques

  2.  
    1. Définitions

    2. Le terme " bibliothèque " est employé ici au sens du langage C : il s'agit d'un fichier regroupant un ensemble de fonctions et de données utilisables par d'autres programmes. " Bibliothèque " est la traduction correcte de l'anglais " library ", même si l'on emploie souvent à tort le terme " librairie ". On utilise souvent les bibliothèques standards de la norme ANSI du langage C, comme stdio pour la célèbre fonction printf(). En pratique, les bibliothèques comportent plusieurs fichiers : les fichiers d'en-tête, les archives, et les fichiers partagés. Ces fichiers sont placés dans des répertoires accessibles à tous les utilisateurs, ici /usr/local/lib et /usr/local/include.
       

      1. Fichiers d'en-tête

      2. Les fichiers dont le nom se termine par .h sont appelés " fichiers d'en-tête ", " header " en anglais. Ils doivent être inclus au début des programmes, par une simple ligne :

        #include "/usr/local/include/kwav.h"

        Cette ligne sera remplacée par le contenu du fichier d'en-tête lui-même (KERNIGHAN et RITCHIE 1997). Ces fichiers contiennent tout ce dont le programme aura potentiellement besoin de connaître, comme le nom des fonctions externes, les structures, les macros, et les constantes et variables globales.

        La directive d'inclusion #include peut prendre deux formes différentes. Le nom du fichier d'en-tête est entouré par les caractères < > :

        #include <stdio.h>

        Cette forme est utilisée pour les fichiers d'en-tête standard, où la recherche du fichier se fait dans un emplacement défini par l'implémentation (généralement /usr/include sous Unix).

        Le nom du fichier d'en-tête est entouré par les caractères " " :

        #include "/usr/local/include/kwav.h"

        Cette forme est plutôt réservée aux fichiers d'en-tête non standards, et leur recherche se fait par défaut dans le répertoire du fichier source que l'on est en train de compiler. Pour l'utilisation de kwav ou kfft, il est préférable d'indiquer le chemin absolu du fichier d'en-tête, comme dans l'exemple précédent, sauf si l'on a copié les fichiers d'en-tête dans son propre répertoire.
         

      3. Archives

      4. Les fichiers dont le nom se termine par .a sont appelés " archives statiques ", et contiennent les fonctions elles-mêmes, sauf la fonction main(). Ils s'utilisent comme des fichiers .c ordinaires lors de la compilation, c'est-à-dire qu'il suffit de les ajouter à la suite du nom du programme principal :

        gcc prog.c /usr/local/lib/kwav.a /usr/local/lib/kfft.a -lm

        Ces fichiers sont dits " statiques " car le code complet de l'archive est inclus dans le fichier exécutable produit, ce qui peut présenter un inconvénient au niveau de la mémoire si l'archive est importante. En revanche, le fichier exécutable ainsi produit fonctionnera même si la bibliothèque est absente ou endommagée. L'option -lm concerne la bibliothèque mathématique standard, utilisée par kwav et kfft.
         

      5. Fichiers partagés


      Les fichiers dont le nom se termine par .so sont appelés " bibliothèques partagées ", " shared object " en anglais. Leur contenu est identique aux archives, mais leur forme précompilée permet une utilisation " dynamique ", ce qui évite le problème de mémoire posé par les archives. Lors d'une compilation dynamique, le code nécessaire n'est pas ajouté au fichier exécutable, il ne sera recherché qu'au moment de son exécution effective. L'aspect dynamique offre un autre avantage : les programmes n'ont pas besoin d'être recompilés pour bénéficier d'une amélioration ou d'une correction. Il suffit que la bibliothèque récemment modifiée porte le même nom et se trouve au même endroit. Par contre, il est indispensable que la bibliothèque soit présente dans son répertoire lors de l'exécution, ce qui n'est pas le cas pour une compilation statique.

      La ligne de commande pour la compilation est moins triviale que pour les archives, puisqu'il faut indiquer deux options : -L, suivie du répertoire où se trouve la bibliothèque, puis -l,suivie du nom de la bibliothèque.

      gcc mon_programme.c -L/usr/local/lib -lkwav
       
       

    3. La bibliothèque kwav


kwav est une bibliothèque C compatible C++. Elle permet de gérer la carte son et les fichiers audionumériques au format wav. Elle comprend cinq fichiers :

La façon la plus simple d'enregistrer du son est d'utiliser les commandes normales d'Unix, comme cat (TRANTER, 2000). Par exemple, cat /dev/dsp > toto enregistre les données transmises par la carte son dans un fichier nommé toto, jusqu'à ce que cette commande soit interrompue (ctrl-C). La commande réciproque cat toto > /dev/dsp peut être utilisée pour jouer ce fichier audio. Cependant, ces méthodes présentent un risque important de saturation de la mémoire, à cause de l'attente de l'interruption. De plus, si le paramètrage du périphérique /dev/dsp est modifié entre temps, la commande de relecture ne jouera pas correctement le fichier toto. Pour résoudre ces problèmes, il faut pouvoir à la fois :
      1. Principe
La bibliothèque kwav contient à ce jour 31 fonctions, que l'on peut classer comme suit :
 
 
 
 
 
 
 
 
 
 
 
 

Cependant, kwav n'est pas qu'une simple collection de fonctions, mais plutôt un environnement de programmation sonore. En effet, en utilisant cette librairie, le programmeur dipose de structures spécifiques comme kwav_struct et kwav_header, et peut manipuler des variables globales que nous appelons globales d'environnement. Celles-ci représentent les paramètres pertinents du signal audionumérique, comme la fréquence d'échantillonnage, le taux d'échantillonnage, ou le nombre de voies. Cet environnement est préconfiguré avec les valeurs par défaut indiquées dans le manuel en ligne, mais le programmeur peut le reconfigurer, ou laisser la possibilité à l'utilisateur de choisir les valeurs qu'il souhaite. La lecture d'un fichier dont les valeurs propres sont différentes des globales d'environnement se déroule correctement, sans perturber l'environnement. Nous détaillerons les moyens employés pour arriver à ce résultat plus loin. La compréhension de ces mécanismes de paramètrage de l'environnement audio n'est pas nécessaire pour la plupart des programmes.
 
 

      1. Utilisation

      2.  

         
         
         
         
         
         
         
         

        La bibliothèque kwav permet de lire des fichiers wav, d'enregistrer du son dans des fichiers wav, et de manipuler le son lui-même, en se déplaçant dans une suite d'échantillons. La plupart des programmes peuvent être ainsi implémentés, simplement en ouvrant le périphérique de la carte son, en lisant ou en écrivant en continu, puis en refermant le périphérique (TRANTER, 2000).

        L'utilisation d'un programme de mixage comme xmix peut être nécessaire avant de lire ou d'enregistrer du son, pour régler les niveau sonores (CHUNG, 1998).
         
         

        1. Comment lire un fichier wav ?

        2.  

           
           
           
           
           
           
           
           

          Le petit programme jouerwav.c lit un fichier wav en utilisant la bibliothèque kwav :

          #include <stdio.h>

          #include "/usr/local/include/kwav.h"

          main (int argc, char *argv[])

          {

          kwav_struct sonwav;

          char nom[256];

          kwav_Init(argc, argv);

          printf("Nom du fichier a lire : ");

          scanf("%s", nom);

          kwav_Load(&sonwav, nom);

          kwav_Play(sonwav);

          kwav_Release();

          }

          Ce programme témoigne d'une volonté de simplicité explicite. En effet, il suffit d'inclure le fichier d'en-tête kwav.h, de déclarer une structure kwav_struct, et d'appeler quatre fonctions. La première, kwav_Init(argc, argv), initialise l'environnement en ouvrant la carte son, et en positionnant les variables globales en fonction de la ligne de commande (cf. Gestion de la ligne de commande). La dernière, kwav_Release(), referme la carte son. Les deux fonctions intermédiaires effectuent le travail central. kwav_Load(&sonwav, nom) ouvre le fichier demandé en lecture, alloue une structure kwav_struct en mémoire vive, puis la remplit à partir du fichier. Enfin, kwav_Play(sonwav) configure la carte son d'après l'en-tête de la structure, puis envoie les échantillons à la carte son.

          Pour la compilation, on peut choisir indifféremment les deux possibilités évoquées précédemment, mais les autres exemples seront fournis en compilation statique :

          alpha6[/home/barkati/SON/KWAV/MYLIB6]: gcc jouerwav.c -L/usr/local/lib -lkwav

          alpha6[/home/barkati/SON/KWAV/MYLIB6]: gcc jouerwav.c /usr/local/lib/kwav.a -lm

          Lors de l'exécution, il faut taper le nom du fichier que l'on souhaite entendre, pour qu'il soit joué aussitôt que l'on valide ce nom :

          alpha6[/home/barkati/SON/KWAV/MYLIB6]: a.out

          Nom du fichier a lire : son.wav

          alpha6[/home/barkati/SON/KWAV/MYLIB6]:
           
           

          Si l'on passe l'option -v lors de l'exécution, ou --verbose pour " verbeux ", on peut alors observer l'évolution de ces quatres étapes et des valeurs importantes :

          alpha5:~/temp/PACK_LIB# ./jouerwav -v

          Option(s) demandee(s): -v

          Valeurs par defaut :

          stereo = 1 voie

          freq = 22050 Hertz

          bits = 16 bits

          duree = 1000 ms

          nom = son.wav

          kwav_Init_dsp :

          stereophonie = 1 voie(s)

          frequence d'echantillonnage = 22094 hertz

          taille d'echantillon = 16 bits

          taille de bloc = 4096 octets

          Nom du fichier a lire : son.wav

          lecture du fichier "son.wav":

          lecture header...

          { main_chunk = 'RIFF'

          { length = 44144 octets

          { chunk_type = 'WAVE'

          { sub_chunk = 'fmt'

          { length_chunk = 16 octets

          { format = 1 (PCM-code)

          { modus = 1 (mono)

          { sample_fq = 22050 echantillons/s

          { byte_p_sec = 44100 octets/s

          { byte_p_spl = 2 octets/echantillon

          { bit_p_spl = 16 bits/echantillon

          { data_chunk = 'data'

          { data_length = 44100 octets

          lecture data...

          allouer...

          ->fin allouer

          ->fin lecture "son.wav"

          kwav_Reconfig_dsp :

          stereophonie = 1 voie(s)

          frequence d'echantillonnage = 22094 Hertz

          taille d'echantillon = 16 bits

          jouer...

          ->fin jouer

          alpha5:~/temp/PACK_LIB#
           
           

        3. Comment enregistrer du son dans un fichier wav ?

        4.  

           
           
           
           
           
           
           
           

          Voici le programme enregistrerwav.c qui enregistre du son dans un fichier wav. Ce programme ressemble fort au précédent :

          #include "/usr/local/include/kwav.h"

          #include <stdio.h>

          main (int argc, char *argv[])

          {

          kwav_struct son;

          char nom[256];

          int duree;

          kwav_Init(argc, argv);

          printf("Nom du fichier a enregistrer : ");

          scanf("%s", nom);

          printf("duree en millisecondes : ");

          scanf("%d", &duree);

          kwav_Record(&son, kwav_ms2oct(duree));

          kwav_Play(son);

          kwav_Save(son, nom);

          kwav_Release();

          }

          Ce programme demande à l'utilisateur le temps d'enregistrement qu'il souhaite en millisecondes, et le stocke dans la variable duree. Celle-ci est convertie en octets par la fonction kwav_ms2oct. L'enregistrement est réalisé par la fonction kwav_Record. Afin que la taille du futur fichier ne dépende pas d'un message d'arrêt, et risque ainsi de saturer la mémoire, la fonction kwav_Record demande en argument cette taille, exprimée en octets. kwav_Record alloue une zone de la mémoire en conséquence, puis y écrit les données qui proviennent de la carte son. A cette étape de l'exécution, le son est seulement enregistré en mémoire vive, dans une structure de type kwav_struct. L'enregistrement sur le disque dur nécessite l'appel de la fonction kwav_Save, qui formate automatiquement le fichier en wav.

          La compilation, puis l'exécution, peuvent se faire de la manière suivante :

          alpha5[/home/barkati/SON/KWAV/PACK_LIB]: gcc -L/usr/local/lib -lkwav

          enregistrerwav.c

          alpha5[/home/barkati/SON/KWAV/PACK_LIB]: a.out

          Nom du fichier a enregistrer : test

          duree en millisecondes : 2000

          alpha5[/home/barkati/SON/KWAV/PACK_LIB]: ls -l

          -rw------- 1 barkati users 88244 Aug 5 20:45 test.wav

          On remarque que l'enregistrement commence dès que la durée est validée, que les permissions sont attribuées à l'utilisateur en lecture et écriture, et que l'extension .wav est ajouté automatiquement au nom du fichier.

          L'option -v permet de suivre là encore les étapes importantes de l'exécution :

          alpha5[/home/barkati/SON/KWAV/PACK_LIB]: a.out -v

          Option(s) demandee(s): -v

          Valeurs par defaut :

          stereo = 1 voie

          freq = 22050 Hertz

          bits = 16 bits

          duree = 1000 ms

          nom = son.wav

          Init_dsp :

          stereophonie = 1 voie(s)

          frequence d'echantillonnage = 22094 Hertz

          taille d'echantillon = 16 bits

          taille de bloc = 4096 octets

          Nom du fichier a enregistrer : test2

          duree en millisecondes : 1000

          allouer...

          ->fin allouer

          enregistrer...

          ->fin enregistrer

          Reconfig_dsp :

          stereophonie = 1 voie(s)

          frequence d'echantillonnage = 22094 Hertz

          taille d'echantillon = 16 bits

          jouer...

          ->fin jouer

          ecriture du fichier "test2.wav"...

          header... data...

          ->fin d'ecriture du fichier "test2.wav"

          L'astuce employée fréquement pour enregistrer du son sans microphone consiste à utiliser l'oreillette d'un casque audio. En effet, les deux prises sont au même format mini-jack, et l'oreillette possède un mécanisme transducteur semblable à un microphone. Sa membrane peut convertir elle aussi les variations de pression acoustique en courant éléctrique. Bien sûr, la qualité de l'enregistrement sera médiocre, néanmoins reconnaissable.
           
           

        5. Comment modifier du son ?

        6.  

           
           
           
           
           
           
           
           

          Voici un programme concis (amplifier.c) qui amplifie un son lu depuis le disque dur, puis joue ce son amplifié :

          #include <stdio.h>

          #include "/usr/local/include/kwav.h"

          main (int argc, char *argv[])

          {

          kwav_struct sonwav;

          char nom[256];

          int nb_ech;

          int coeff;

          int i;

          kwav_Init(argc, argv);

          printf("Nom du fichier a lire : ");

          scanf("%s", nom);

          kwav_Load(&sonwav, nom);

          printf("Valeur du coefficient : ");

          scanf("%d", &coeff);

          nb_ech = (sonwav.head.data_length) / (sonwav.head.byte_p_spl);

          for(i=0; i<nb_ech; i++)

          sonwav.data[i] *= coeff;

          kwav_Play(sonwav);

          kwav_Release();

          }

          Le principe consiste à calculer le nombre d'échantillons à traiter d'après les informations contenues dans le champs head de la structure, puis de parcourir et modifier échantillon par échantillon la suite d'échantillons pointée par le champs data :

          nb_ech = (sonwav.head.data_length) / (sonwav.head.byte_p_spl);

          for(i=0; i<nb_ech; i++)

          sonwav.data[i] *= coeff;

          De nombreuses opérations de traitement du signal peuvent être programmées à partir de ce modèle, notamment certains filtrages, avec des opérations un peu plus complexes. La modification est ici une simple multiplication de l'amplitude, échantillon par échantillon. Le coefficient est demandé à l'utilisateur, puis une boucle remplace chaque échantillon par son ancienne valeur multipliée par le coefficient, du premier jusqu'au dernier. Le nombre d'échantillons est déduit des informations contenues dans l'en-tête du fichier wav, recopiées dans la structure de type kwav_struct.

          La compilation et l'éxecution restent aussi simples que pour les programmes précédents :

          alpha6[/home/barkati/SON/KWAV/PACK_LIB]: gcc -L/usr/local/lib -lkwav

          amplifier.c -o amplifier

          alpha6[/home/barkati/SON/KWAV/PACK_LIB]: amplifier

          Nom du fichier a lire : son.wav

          Valeur du coefficient : 2

          alpha6[/home/barkati/SON/KWAV/PACK_LIB]:
           
           

        7. Comment récupèrer le nom de fichier passé dans la ligne de commande ?
        Lorsque les arguments de la ligne de commande sont transmis à la fonction kwav_Init (cf. Gestion de la ligne de commande), il est possible de récupèrer ces paramètrages de variables globales avec les fonctions accesseurs, du type kwav_get_gfilename. Ceci est particulièrement intéressant pour les noms de fichiers, car la ligne de commande permet d'utiliser la complétion.

        #include <stdio.h>

        #include <string.h>

        #include "/usr/local/include/kwav.h"

        main (int argc, char *argv[])

        {

        kwav_struct sonwav;

        char nom[256];

        kwav_Init(argc, argv);

        strcpy(nom, kwav_get_gfilename());

        kwav_Load(&sonwav, nom);

        kwav_Play(sonwav);

        kwav_Release();

        }

        Ainsi, ce programme jouerwav2.c ne propose plus comme jouerwav.c d'entrer le nom du fichier à lire. Il lira le fichier désigné par kwav_get_gfilename : soit le fichier son.wav par défaut ou suite à une saisie incorrecte ou absente, soit, suite à une saisie correcte, le nom de fichier passé dans la ligne de commande. Pour que ce nom soit accepté, il faut qu'il contienne une extension .wav, sauf si l'on précise l'option -n. Un argument incompris par kwav_Init sera tout simplement ignoré, ce qui a pour effet dans le cas présent de lire le son par défaut.
         
         

      3. Analyse thématique interne

      4.  

         
         
         
         
         
         
         

        1. Ergonomie générale
Les bibliothèques doivent répondre à un certain nombre de contraintes et de coutumes plus ou moins officielles. Le site de GNU propose d'adopter quelques conventions à l'adresse http://www.gnu.org/prep/standards_toc.html (STALLMAN 2000), afin d'éviter la pollution de l'espace de nommage, d'éventuels conflits avec d'autres bibliothèques, et des difficultés de lecture : Le choix des noms des fonctions et des variables doit donc être cohérent et intuitif. Notre syntaxe propose un préfixe général, kwav_, une majuscule au début du nom de la fonction, une extension _from_header ou dsp à la fin si nécessaire, et un nom évocateur complet en anglais. Les fonctions de conversion comme kwav_ms2oct, signifiant " conversion de millisecondes vers octets ", ont une orthographe spéciale inspirée des célèbres fonctions itoa (" integer to ascii ") ou a2ps (" ascii to postscript "). Elles n'ont pas de majuscules, pour suggérer que ce sont des fonctions " utilitaires " qui n'ont pas de lien direct avec la carte son ou le format wav.

La cohérence du choix des arguments et des valeurs de retour demande aussi une grande rigueur (KOENIG 1991). Le passage par adresse est utilisé au minimum, uniquement quand une fonction doit modifier une variable de la fonction appelante (cf. Gestion de l'écriture et de la lecture sur le disque dur). Les risques posés par les variables globales (DELANNOY 1992) sont contournés par implémentation d'accesseurs et de modificateurs spécifiques, comme kwav_get_gfreq ou kwav_set_gfreq.

En cas de problème, un mécanisme peut activer l'affichage des étapes et des valeurs en cours, car un affichage systématique est encombrant lorsque tout se déroule correctement. C'est le rôle de la variable globale d'environnement gverbose, qui autorise, lorsqu'elle vaut _KWAV_OUI ou 1, l'exécution des fonctions printf disséminées dans le code.
 
 

        1. Gestion du flux audionumérique

        2.  

           
           
           
           
           
           
           
           

          Vincent LESBROS propose une définition intuitive des sons échantillonnés (LEBROS 1995) : " Les sons échantillonnés (ou signaux sonores échantillonnés), ou sons numériques, sont conservés sous la forme amplitude / temps. Un son est alors représenté par une suite de valeurs numériques décrivant les variations de pression de l'air par rapport à la pression normale. Une façon intuitive de la présenter est de dire que les valeurs indiquent la position de la membrane du haut-parleur (ou du micro, ou encore du tympan) à chaque instant. ". Nous appelons " échantillon " une seule de ces mesures de pression acoustique, codée numériquement sur un nombre entier plus ou moins grand selon la résolution, et " flux audionumérique " le déplacement d'une suite d'échantillons, c'est-à-dire leur lecture ou leur écriture, selon le sens du déplacement. La fréquence d'échantillonnage indique le nombre de mesures effectuées par seconde, c'est-à-dire le débit du flux audionumérique par voie, exprimé en hertz. Les sons sont monophoniques s'ils ne contiennent qu'une voie, et stéréophoniques s'ils en contiennent deux, une voie à droite, et l'autre à gauche.

          Puisque le son numérique est une suite d'échantillons, une façon évidente de les représenter en langage C consiste à définir un type pour les échantillons, KWAV_ECH, puis d'utiliser un tableau d'échantillons. En fait, nous utilisons un pointeur data sur des échantillons, pour faciliter les allocations dynamiques :

          typedef struct {

          kwav_header head;

          KWAV_ECH *data;

          } kwav_struct;

          Les échantillons d'un signal sonore numérique peuvent être codés sur 8, 12, 16, 24, ou 32 bits. Parmi ces résolutions, le type KWAV_ECH ne peut prendre qu'une seule valeur, que nous avons fixé à 16 bits par défaut, car c'est la valeur la plus répandue. Cependant, les sons codés sur 8 bits sont aussi très répandus, et le typage rigide du langage C ne permet pas à un type de changer de taille aisément. Les unions ne résolvent pas le problème, puisqu'elles réservent l'espace de leur membre le plus grand, ne gérant donc pas les plus petites tailles. Les conversions de type proposée par le C avec des " casts " aboutissent à des syntaxes très complexes, comportant beaucoup d'étoiles et de parenthèses. Nous proposons une solution plus élégante grâce à la définition de plusieurs types, qui produit cependant une surcharge de code :

          typedef signed short KWAV_ECH16; /* 16 bits par echantillon */

          typedef unsigned char KWAV_ECH8; /* 8 bits par echantillon */

          typedef KWAV_ECH16 KWAV_ECH;

          Nous définissons donc deux types, KWAV_ECH16 et KWAV_ECH8, car les autres résolutions sont beaucoup moins répandues, puis nous positionnons par défaut notre type standard KWAV_ECH sur KWAV_ECH16. Celui-ci occupe deux octets, ce qui correspond à un type signed short (nombre entier court signé) sur nos machines, et le KWAV_ECH8 occupe un seul octet, soit un type unsignedchar (caractère non signé). " Les variables de types unsigned char peuvent prendre les valeurs de 0 à 255, tandis que les signed char vont de -128 à 127 (sur une machine fonctionnant en complément à deux). " (KERNIGHAN et RITCHIE 1997, p.36). Notre carte son requièrt des short signés mais des char non signés.

          Finalement, un signal sonore est représenté par une suite d'échantillons codés sur 16 bits par défaut, soit 2 octets, dans un tableau alloué dynamiquement, le champs data de la structure kwav_struct contenant l'adresse de ce tableau.

          Pour illustrer l'utilisation de cette représentation, prenons un exemple: augmenter ou diminuer l'amplitude d'un son. Ce code est extrait de la fonction Normalize de la bibliothèque. Le traitement de signal audionumérique suivant s'effectue en parcourant simplement les échantillons un à un, et en modifiant leur valeur de façon systématique :

          for(i=0; i<nb_ech; i++)

          wavptr->data[i] *= coeff;

          Cet algorithme fontionne avec des sons codés sur 16 bits, en incrémentant l'adresse du pointeur de 2 octets à chaque boucle. Il multiplie simplement la valeur qui se trouve à cette adresse par le coefficient. Pour que cet algorithme fonctionne aussi avec les sons codés sur 8 bits, il faut que l'adresse soit incrémentée de seulement 1 octet par boucle, en utilisant un pointeur sur des KWAV_ECH8. Il suffit de déclarer un pointeur de type KWAV_ECH8, de lui affecter l'adresse du début du son après conversion de type, et d'orienter l'exécution vers le bon algorithme, en fonction de l'information de résolution du son courant, contenue dans le champs header.bit_p_spl (nombre de bits par échantillon) :

          KWAV_ECH8* ptr8;

          ptr8 = (KWAV_ECH8*)wavptr->data;

          for(i=0; i<nb_ech; i++)

          {

          if(wavptr->header.bit_p_spl==16)

          wavptr->ptrson[i] *= coeff;

          else

          ptr8[i] *= coeff;

          }

          Ces structures de données permettent de représenter informatiquement des flux audionumériques, et donc de faire de la synthèse et du traitement du signal au sens donné par Curtis ROADS : " La synthèse est le procédé de génération de flux d'échantillons grâce à des outils algorithmiques. [...] Le traitement du signal transforme les flux d'échantillons. " (ROADS 1998, p.48). Les applications les plus typiques de ces domaines sont variées : citons les synthèses additive, granulaire, soustractive, formantique, par modulation, par tables d'ondes, par modèles physiques, et différents traitements tels que l'amplification, le mixage, les filtrage et égalisation, les retards, la projection spaciale, la réduction de bruit, ou la conversion de taux d'échantillonnage. Les traitements comme l'analyse / resynthèse ou la compression / expansion temporelle seront facilités par les outils proposés dans la bibliothèque kfft.
           
           

        3. Gestion des entrées et sorties de la carte son
Sous Unix, tout est fichier, la carte son aussi. Ce fichier se nomme dsp pour " digital signal processor " ou " traitement numérique du signal " en français, et se trouve dans le répertoire /dev de " device " en anglais, que l'on traduit par " périphérique ". Il faut ouvrir ce fichier en lecture et en écriture, respectivement pour enregistrer et jouer du son, avant toute opération sur la carte son. C'est pourquoi tout programme utilisant kwav doit appeler la fonction kwav_Init, qui appelle kwav_Init_dsp pour ouvrir le fichier /dev/dsp et configurer la carte son. Nous avons nommé le descripteur de fichier de la carte son kwav_fdsp. Il est déclaré dans le fichier kwav.h afin que l'utilisateur y ait accès s'il voulait effectuer des opérations qui ne se trouvent pas dans cette bibliothèque.

/* ouvre un fichier dsp en lecture et en ecriture */

if ((kwav_fdsp = open("/dev/dsp", O_RDWR))<0){

perror("Ouverture du fichier /dev/dsp");

return;}

La configuration de la carte son se fait par l'appel de fonctions ioctl, de " input/output control " en anglais, " contrôle des entrées / sorties " en français, qui proviennent de la bibliothèque spécifique à Linux sys/ioctl.h.

ioctl(kwav_fdsp, SNDCTL_DSP_STEREO, &en_stereo);

ioctl(kwav_fdsp, SNDCTL_DSP_SPEED, &freq_ech);

ioctl(kwav_fdsp, SNDCTL_DSP_SAMPLESIZE, &bits_par_ech);

ioctl(kwav_fdsp, SNDCTL_DSP_GETBLKSIZE, &gtaillebloc);

Ces fonctions permettent de contrôler des périphériques, plus exactement les paramètres sous-jacents des fichiers spéciaux. Elles ne bénéficient pas d'une standardisation unique, car elles ont été conçues comme un " fourre-tout " pour les opérations qui ne correspondent pas au modèle Unix des flux d'entrées/sorties, à partir de la version 7 d'Unix AT&T (IOCTL MAN PAGE, 1993). La syntaxe est la suivante :

int ioctl(int d, int request, ...)

Le premier argument d doit être un descripteur de fichier ouvert, et request un numéro de requête prédéfini, que l'on peut trouver dans le manuel ioctl_list. Le troisième argument est variable.

Voici le détail des requêtes que nous utilisons :

La bibliothèque kwav utilise une autre requête avant et après les opérations de lecture ou d'écriture sur la carte son :

ioctl(kwav_fdsp, SNDCTL_DSP_SYNC, 0);

Celle-ci se distingue des précédentes, car elle ne paramètre pas le périphérique, ni ne récupère une valeur, mais valide le paramétrage et demande au périphérique de lire l'intégralité des données sans s'interrompre. Elle fonctionne comme un verrou en bloquant l'accès pendant les opérations read ou write sur le périphérique. Ce mécanisme a l'avantage de réduire les risques d'interruption de lecture ou d'écriture si désagréables pour le son, mais possède l'inconvénient de limiter à un seul le nombre de fichiers écoutables simultanément.

Des conseils d'utilisation de ces fonctions ioctl et de programmation d'applications pour le son sur Unix sont disponibles sur le site d'Opensound, aux adesses www.se.opensound.com/pguide/audio.html et www.se.opensound.com/pguide/audio2.html.
 
 

        1. Gestion du format wav

        2.  

           
           
           
           
           
           
           
           

          Le format wav se distingue d'un fichier audio brut par l'ajout d'un en-tête qui contient des informations essentielles à la manipulation des échantillons. La bibliothèque kwav propose donc deux structures, kwav_struct et kwav_header, ainsi qu'un type KWAV_ECH qui définit la taille des échantillons :

          typedef signed short KWAV_ECH16; /* 16 bits par echantillon */

          typedef unsigned char KWAV_ECH8; /* 8 bits par echantillon */

          typedef KWAV_ECH16 KWAV_ECH;

          typedef struct {

          kwav_header head;

          KWAV_ECH *data;

          } kwav_struct ;

          La structure kwav_header représente un en-tête wav standard de 44 octets. Elle contient quelques marqueurs d'identification du format, la taille des données en octets, et les informations audionumériques suivantes : le mode, monophonique ou stéréophonique, la fréquence d'échantillonnage en hertz, et le taux d'échantillonnage en nombre de bits par échantillon. Sans ces informations, la lecture d'un son se ferait rarement correctement, et poserait entre autres des problèmes de vitesse de relecture.

          typedef struct { /* header for WAV-Files */

          char main_chunk[4]; /* 'RIFF' */

          int length; /* length of file */

          char chunk_type[4]; /* 'WAVE' */

          char sub_chunk[4]; /* 'fmt' */

          int length_chunk; /* length sub_chunk, always 16 bytes */

          short format; /* always 1 = PCM-Code */

          short modus; /* 1 = Mono, 2 = Stereo */

          int sample_fq; /* Sample Freq */

          int byte_p_sec; /* Data per sec */

          short byte_p_spl; /* bytes per sample,

          1=8 bit, 2=16 bit (mono)

          2=8 bit, 4=16 bit (stereo) */

          short bit_p_spl; /* bits per sample, 8, 12, 16 */

          char data_chunk[4]; /* 'data' */

          int data_length; /* length of data */

          } kwav_header;

          Les programmes utilisant kwav manipulent des structures kwav_struct plutôt que directement du son brut, afin que les fonctions connaissent toutes les caractéristiques du signal courant. Toutes ces informations sont contenues dans la structure kwav_header et sont vraiment indispensables. Typiquement, on a besoin de connaître le nombre d'échantillons d'un son, pour le parcourir du début à la fin :

          nb_ech = (wavptr->head.data_length) / (wavptr->head.byte_p_spl);

          for(i=0; i<nb_ech; i++)

          wavptr->data[i] *= coeff;

          Il existe quelques versions différentes du format wav, notamment avec des en-têtes plus importants. Pour l'instant, ceux-ci ne sont pas pris en compte par notre bibliothèque. Des informations complémentaires sur l'implémentation et l'utilisation du format wav sont disponibles aux adresses suivantes :

          http://www.lightlink.com/tjweber/StripWav/WAVE.html

          http://www.geocities.com/~vmushinskiy/fformats/files/wave.htm
           
           

        3. Gestion de l'écriture et de la lecture sur le disque dur

        4.  

           
           
           
           
           
           
           
           

          La fonction kwav_Save gère l'écriture d'une structure kwav_struct dans un fichier au format wav, tandis que la fonction kwav_Load lit ce type de fichier sur le disque dur pour remplir une telle structure en mémoire vive. La bibliothèque kwav manipule localement un seul descripteur de fichier pour ces opérations, nommé fdwav. Pour kwav_Save comme pour kwav_Load, ce descripteur est ouvert puis refermé dans la même fonction. Ces deux fonctions utilisent open et close plutôt que fopen et fclose, afin que les permissions ne dépendent pas de l'environnement.

          Voici le code complet de la fonction kwav_Save :

          void kwav_Save (kwav_struct sonwav, char nom_fichier[])

          {

          int i;

          int fdwav;

          char wav[] = ".wav";

          /* recherche de l'extension, sinon ajout */

          for(i=0; i<4; i++)

          if(nom_fichier[strlen(nom_fichier)-4+i] != wav[i])

          strcat(nom_fichier, wav);

          /* ecrit un fichier son.wav sur le disque et le relit */

          if((fdwav = open(nom_fichier, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD)) == -1){

          perror("kwav: Ouverture du fichier wav");

          return;}

          if(gverbose == _KWAV_OUI){

          printf("ecriture du fichier \"%s\"... \n", nom_fichier);

          printf(" header...");}

          write(fdwav, &sonwav.head, sizeof(sonwav.head));

          if(gverbose == _KWAV_OUI)

          printf(" data...\n");

          write(fdwav, sonwav.data, sonwav.head.data_length);

          if(gverbose == _KWAV_OUI)

          printf("->fin d'ecriture du fichier \"%s\"\n", nom_fichier);

          if((close(fdwav)) == -1){

          perror("kwav: Fermeture du fichier wav");

          return;}

          }

          On peut résumer cette fonction à quatre instructions :

          fdwav = open(nom_fichier, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);

          write(fdwav, &sonwav.header, sizeof(sonwav.header));

          write(fdwav, sonwav.ptrson, sonwav.header.data_length);

          close(fdwav);

          La première ouvre le fichier demandé en écriture s'il existe, le créé sinon, avec les permissions adéquates. La constante S_IRUSR (ou S_IREAD) donne le droit de lecture du fichier à l'utilisateur, et S_IWUSR (ou S_IWRITE) celui d'écriture. La deuxième fonction écrit dans ce fichier le contenu de l'en-tête, et la troisième le son proprement dit, octets par octets. La dernière, enfin, ferme le descripteur de ce fichier.

          La lecture d'un fichier wav repose sur un modèle presque identique à l'écriture. La fonction kwav_Load ouvre le fichier demandé en lecture, lit l'en-tête champs par champs, lit le son, puis referme le fichier :

          void kwav_Load (kwav_struct* psonwav, char nom_fichier[])

          {

          int fdwav;

          /* ouverture du fichier */

          if((fdwav = open(nom_fichier, O_RDONLY)) == -1){

          perror("kwav: Ouverture du fichier en lecture");

          return;}

          if(gverbose == _KWAV_OUI){

          printf("lecture du fichier \"%s\": \n", nom_fichier);

          printf(" header...\n");}

          read (fdwav, &psonwav->head.main_chunk, 4*sizeof(char) ); /* 'RIFF' */

          read (fdwav, &psonwav->head.length, sizeof(int) ); /* length of file */

          read (fdwav, &psonwav->head.chunk_type, 4*sizeof(char) ); /* 'WAVE' */

          read (fdwav, &psonwav->head.sub_chunk, 4*sizeof(char) ); /* 'fmt' */

          read (fdwav, &psonwav->head.length_chunk, sizeof(int) ); /* 16 bytes */

          read (fdwav, &psonwav->head.format, sizeof(short)); /* 1=PCM-Code */

          read (fdwav, &psonwav->head.modus, sizeof(short)); /* 1=Mono, 2=St. */

          read (fdwav, &psonwav->head.sample_fq, sizeof(int) ); /* Sample Freq */

          read (fdwav, &psonwav->head.byte_p_sec, sizeof(int) ); /* Data per sec */

          read (fdwav, &psonwav->head.byte_p_spl, sizeof(short)); /* 1, 2, 3, 4 */

          read (fdwav, &psonwav->head.bit_p_spl, sizeof(short)); /* 8, 12, 16 */

          read (fdwav, &psonwav->head.data_chunk, 4*sizeof(char) ); /* 'data' */

          read (fdwav, &psonwav->head.data_length, sizeof(int) ); /* length of data */

          if(gverbose == _KWAV_OUI){

          kwav_Print_header (psonwav->head);

          printf(" data...\n");}

          psonwav->data = kwav_Alloc (psonwav->data, psonwav->head.data_length);

          read (fdwav, psonwav->data, psonwav->head.data_length); /* chargement du son lui-meme */

          if(gverbose == _KWAV_OUI)

          printf("->fin lecture \"%s\"\n", nom_fichier);

          if((close(fdwav)) == -1){

          perror("kwav: Fermeture du fichier wav");

          return;}

          }

          Il y a cependant quelques différences. La première tient bien sûr dans l'utilisation de la fonction read à la place de write. La deuxième tient dans l'appel de la fonction kwav_Alloc, car la structure kwav_struct manipulée contient un pointeur KWAV_ECH* data qui n'est pas censé avoir déjà été alloué. En conséquence, si la même structure est utilisée plusieurs fois, il est préférable de libérer ce pointeur avant d'appeler kwav_Load. La troisième différence concerne les arguments de kwav_Load  : le passage de la structure kwav_struct se fait par adresse. " Comme le langage C passe les arguments des fonctions par valeur, la fonction appelée n'a aucun moyen direct de modifier une variable de la fonction appelante. [...] Les arguments de type pointeur permettent à une fonction d'accéder aux objets de la fonction appelante et de les modifier. " (KERNIGHAN et RITCHIE 1997, p. 93) Ainsi, les opérations ont bien lieu sur la strucure kwav_struct d'origine, et non pas sur sa copie, afin d'allouer puis d'écrire les informations et le son directement à partir de son adresse en mémoire vive.
           
           

        5. Gestion de la mémoire

        6.  

           
           
           
           
           
           
           
           

          La taille des fichiers audio peut varier beaucoup, et être très importante si le fichier dure plusieurs minutes, surtout au format wav non compressé, tel que nous l'utilisons. C'est pourquoi les structures kwav_struct contiennent un pointeur vers une zone mémoire que l'on peut allouer et désallouer :

          KWAV_ECH *kwav_Alloc (KWAV_ECH* pson, int nboctets)

          {

          if(gverbose == _KWAV_OUI)

          printf("allouer... \n");

          pson = malloc (nboctets); /* allocation en octets */

          if (pson==NULL){

          perror("kwav: Allocation du pointeur");

          return pson;}

          if(gverbose == _KWAV_OUI)

          printf("->fin allouer\n");

          return pson;

          }
           
           

          void kwav_Free (KWAV_ECH* pson)

          {

          free(pson);

          }

          Ces fonctions utilisent malloc et free de la bibliothèque standard stdlib.h. La fonction kwav_Alloc renvoie l'adresse du début de la zone mémoire allouée afin qu'elle soit affectée au pointeur, conformément à l'écriture d'un malloc. Elle vérifie s'il y a une erreur en testant si la valeur retournée par malloc vaut NULL, ce que ne peut pas faire la fonction kwav_Free puisque free ne retourne pas de valeur.
           
           

        7. Gestion de la ligne de commande
Sous Unix, gérer la ligne de commande présente plusieurs avantages. Typiquement, on entre des options suivies d'une valeur, pour transmettre des paramètres à un programme. Un autre avantage réside dans l'option qui renseigne sur les options : traditionnellement -h ou --help. La possiblité d'utiliser les facilités offertes par l'environnement pour la ligne de commande représente le dernier avantage, en particulier la complétion, c'est-à-dire la possibilité de complèter un nom de fichier à partir des premières lettres avec la seule touche de tabulation. La ligne de commande est transmise à la fonction kwav_Init, grâce aux arguments argc et argv.

N'importe quel programme utilisant kwav et transmettant ces arguments peut recevoir l'option d'aide -h ou --help, qui liste alors toutes les options possibles :

alpha5:~/temp/PACK_LIB# ./jouerwav -h

Option(s) demandee(s): -h

<fichier.wav>

-b <resolution en bits par echantillon>, 8 ou 16

-d <duree en millisecondes>

-f <frequence d'echantillonnage>, 11025 ou 22050 ou 44100

-n <nom du fichier>

-s <stereophonie>, 1 ou 2

-v ou --verbose

-h ou --help

alpha5:~/temp/PACK_LIB#

On discerne bien ici les trois types d'utilisations : le passage de nom de fichier, le paramètrage, et l'aide, qui peut se faire avec ou sans l'option -n. Le paramètrage positionne les variables d'environnement suivantes, telles qu'elles sont décrites dans le fichier d'en-tête kwavlib.h :

int gstereo; /* stereo globale */

int gfreq; /* frequence globale */

int gbits; /* resolution globale */

int gtaille; /* taille globale en octet */

char gnom_fichier[256]; /* nom global */

int gverbose; /* drapeau global pour les printf */

Si la ligne de commande ne précise pas certaines options, la fonction kwav_Init positionne ces variables aux valeurs par défaut décrites dans le même fichier :

#define _KWAV_DEFAULT_STEREO 1

#define _KWAV_DEFAULT_FREQ_ECH 22050

#define _KWAV_DEFAULT_BITS (8 * sizeof(KWAV_ECH))

#define _KWAV_DEFAULT_TAILLE (_KWAV_DEFAULT_FREQ_ECH * sizeof(KWAV_ECH) * _KWAV_DEFAULT_STEREO) /* 1 sec de son en oct */

#define _KWAV_DEFAULT_FILE_NAME "son.wav"

Voici une illustration typique de l'utilisation de la ligne de commande. Cette ligne permet de positionner des variables d'environnement à d'autres valeurs que les valeurs par défaut, simplement :

alpha6:~/temp/PACK_LIB# ./enregistrerwav -b 8 -f 44100 -s 2

Option(s) demandee(s): -b 8 -f 44100 -s 2

Nom du fichier a enregistrer : options

duree en millisecondes : 2000

alpha6:~/temp/PACK_LIB# ls -l options.wav

-rw------- 1 root root 176444 Aug 10 00:55 options.wav

L'option -v ou --verbose fait partie des options de paramètrage en tant qu'interrupteur, puisqu'elle positionne la variable d'environnement gverbose à 1. Elle signifie " verbeux ", pour suggérer que le programme va afficher des commentaires sur la sortie standard pendant son exécution. Cependant, elle s'utilise différemment des options -b, -f, -s, ou -d, puisqu'elle ne prend pas de valeur, et qu'elle possède deux noms. Son utilisation sur l'exemple précédent montre que la ligne de commande opère une substitution des valeurs par défaut, qui sont : mono 22050 Hz, 16 bits.

alpha6:~/temp/PACK_LIB# ./enregistrerwav -b 8 -f 44100 -s 2 -v

Option(s) demandee(s): -b 8 -f 44100 -s 2 -v

Valeurs par defaut :

stereo = 2 voie

freq = 44100 Hertz

bits = 8 bits

duree = 500 ms

nom = son.wav

Init_dsp :

stereophonie = 2 voie(s)

frequence d'echantillonnage = 44188 hertz

taille d'echantillon = 8 bits

taille de bloc = 4096 octets

Nom du fichier a enregistrer : options2

duree en millisecondes : 1000

allouer...

->fin allouer

enregistrer...

->fin enregistrer

Reconfig_dsp :

stereophonie = 2 voie(s)

frequence d'echantillonnage = 44188 Hertz

taille d'echantillon = 8 bits

jouer...

->fin jouer

ecriture du fichier "options2.wav"...

header... data...

->fin d'ecriture du fichier "options2.wav"

alpha6:~/temp/PACK_LIB#

Ces mécanismes de paramétrage sont décrits dans la fonction void kwav_Init (int argc, char *argv[]), qui récupère dans ces arguments la ligne de commande complète. " Quand on appelle main, deux arguments lui sont passés. Le premier (baptisé conventionnellement argc signifiant nombre d'arguments - argument count) représente le nombre d'arguments de la ligne de commande qui a appelé le programme ; le second (argv, signifiant vecteur d'arguments - argument vector) est un pointeur sur un tableau de chaînes de caractères qui contiennent les arguments, à raison de un par chaîne. [...] Par convention, argv[0] est le nom par lequel le programme a été appelé, argc vaut au moins 1." (KERNIGHAN et RITCHIE 1997, p. 112). La fonction kwav_Init positionne d'abord les variables d'environnement aux valeurs par défaut, puis, si la ligne de commande contient autre chose que le nom du programme appelé, teste si elle reconnaît une option parmi les chaînes de caractères contenues dans argv pour changer la valeur de la variable qui lui correspond. Elle appelle enfin la fonction kwav_Init_dsp avec certaines de ces valeurs pour configurer le périphérique de la carte son, et rend la main à la fonction appelante.

void kwav_Init (int argc, char *argv[])

{

int i;

char wav[] = ".wav";

char ext[4];

/* Parametrage par defaut */

kwav_set_gfreq (_KWAV_DEFAULT_FREQ_ECH);

kwav_set_gbits (8 * sizeof(KWAV_ECH));

kwav_set_gstereo (_KWAV_DEFAULT_STEREO);

kwav_set_gsize (_KWAV_DEFAULT_TAILLE);

kwav_set_gverbose (KWAV_FALSE);

kwav_set_gfilename (_KWAV_DEFAULT_FILE_NAME);

/* Verification des options */

if(argc > 1 && argv[1] != '\0')

{

for(i=1; i<argc; i++)

if(strcmp(argv[i], "-v")==0 || strcmp(argv[i], "--verbose")==0)

kwav_set_gverbose (KWAV_TRUE);

if(gverbose == KWAV_TRUE)

printf("\tOption(s) demandee(s): ");

for(i=1; i<argc; i++)

{

if(gverbose == KWAV_TRUE)

printf("%s ", argv[i]); /* simple echo */

if(strcmp(argv[i], "-s")==0)

kwav_set_gstereo (atoi(argv[i+1])); /* m=1 et s=2 */

if(strcmp(argv[i], "-f")==0)

kwav_set_gfreq (atoi(argv[i+1])); /* en hertz */

if(strcmp(argv[i], "-b")==0){

kwav_set_gbits (atoi(argv[i+1]));

if (gbits != 8) gbits = 16;} /* seulement 8 ou 16 */

if(strcmp(argv[i], "-d")==0)

kwav_set_gsize (kwav_ms2oct(atoi(argv[i+1]))); /* en ms */

if(strcmp(argv[i], "-n")==0)

kwav_set_gfilename (argv[i+1]);

if(strcmp(argv[i], "-h")==0 || strcmp(argv[i],"--help")==0)

{

printf("\n"

"<fichier.wav>\n"

"-b <resolution en bits par echantillon>, 8 ou 16\n"

"-d <duree en millisecondes>\n"

"-f <frequence d'echantillonnage>, 11025 ou 22050 ou 44100\n"

"-n <nom du fichier>\n"

"-s <stereophonie>, 1 ou 2\n"

"-v ou --verbose\n"

"-h ou --help\n");

exit(0);

}

/* recherche de l'extension */

strcpy(ext, argv[i]+strlen(argv[i])-4);

if(strcmp(ext, wav)==0)

strcpy(gnom_fichier, argv[i]);

}

if(gverbose == KWAV_TRUE)

printf("\n");

}

if(gverbose == KWAV_TRUE) {

printf("\tValeurs par defaut :\n");

printf("stereo = %d voie\n", gstereo);

printf("freq = %d Hertz\n", gfreq);

printf("bits = %d bits\n", gbits);

printf("duree = %d ms\n", kwav_oct2ms(gtaille));

printf("nom = %s\n", gnom_fichier);

}

/* Parametrage du peripherique */

kwav_Init_dsp(gfreq, gbits, gstereo);

}

Afin de désactiver ce système de gestion de la ligne de commande, pour créer son propre système par exemple, il suffit de ne pas transmettre ces arguments à la fonction kwav_Init, en l'appelant de la façon suivante :

kwav_Init(0, NULL);
 
 

    1. La bibliothèque kfft

 
 

Cette bibliothèque propose des fonctions d'analyse et de resynthèse du son basées sur un algorithme de Transformée Rapide de Fourier, TFR ou FFT (Fast Fourier Transform) en anglais, à partir d'un code source proposé par Daniel GOOSSENS dans son cours sur l'intelligence artificielle (GOOSSENS 1999). Nous ne détaillerons donc pas ici le fonctionnement du programme, déjà largement décrit dans le document précité.

kfft est une bibliothèque C compatible C++. Elle permet d'analyser et de resynthétiser du signal audionumérique. Elle nécessite la bibliothèque kwav pour fonctionner, et comprend cinq fichiers :

      1. Présentation de la FFT

      2.  

         
         
         
         
         
         
         
         

        " Le musicien créatif ne sera-t-il pas un maître plus puissant s'il est également informé de la science pure des méthodes et des matériaux de son art ? Ne sera-t-il pas capable de mélanger les couleurs sonores avec une plus grande habileté s'il comprend la nature des ingrédients et des effets qu'ils produisent ? " (MILLER 1916).

        L'analyse spectrale constitue un outil essentiel pour les acousticiens, psychoacousticiens, musicologues, et compositeurs, en révélant la microstructure des sons. C'est toujours un domaine en évolution, car elle contient des problèmes intrinsèques irrésolus à ce jour, au point que Curtis ROADS parle " d'estimation spectrale " (ROADS 1998). Beaucoup de méthodes ont été mises au point, dont les analyses par banques de filtres à Q constant, par ondelettes, par distribution de Wigner, ou par autorégression. L'analyse de Fourier est elle-même une famille de techniques différentes qui continuent d'évoluer.

        Les méthodes basées sur l'analyse de Fourier décomposent le son fourni en entrée sous forme d'une somme de sinusoïdes également espacées entre 0 Hertz et la fréquence d'échantillonnage. Comme nous traitons de sons numériques, notre méthode isole une courte portion de son ou fenêtre, en général de 512 ou 1024 échantillons. La FFT est une simple accélération de l'algorithme de la transformée discrète de Fourier ou DFT, de Discrete Fourier Transform en anglais (REINHARD 1997). Elle demande que la taille de la fenêtre soit une puissance de 2.
         
         

      3. Utilisation
Un exemple d'utilisation d'analyse fréquentielle avec la bibliothèque kfft est présenté au chapitre " Comment visualiser le spectre d'un son ? ". Il faut inclure le fichier kfft.h dans le programme, mais aussi kwav.h, car kfft a besoin des structures de kwav pour manipuler le signal audionumérique. Elle fonctionne pour les sons codés sur 16 bits, avec des fenêtres de 1024 points par défaut.

Le nombre de fonctions est très limité pour l'utilisateur :

L'utilisateur manipule donc trois tableaux en plus des six fonctions : Une partie intéressante du travail de l'utilisateur se situe entre l'analyse et la resynthèse. En effet, le fichier d'analyse peut être modifié à plusieurs fins, les applications les plus complexes étant sans doute les techniques de synthèse croisée. Les objectifs recherchés restent souvent les compessions et extensions temporelles, et les transpositions fréquentielles.
 
 
    1. Le programme kediteur

    2.  

       
       
       
       
       
       
       

      1. Visualisation du son

      2.  

         
         
         
         
         
         
         
         

        En informatique, la visualisation et autres GUI (Graphic User Interface) se sont imposés ces dernières années. En musique, les représentations du son amplitude / temps et fréquence / temps ont changé certaines habitudes de pensée, sortant de la traditionnelle partition. La manipulation informatique des sons gagne une précision inégalée grâce à la visualisation de ceux-ci. Nous proposons deux algorithmes de visualisation, écrits à l'aide de la bibliothèque graphique X11 (MEHAT, 1991), kwav, et kfft.
         
         

        1. Comment afficher la forme d'onde d'un son ?

        2.  

           
           
           
           
           
           
           
           

          Voici le résultat graphique d'un algorithme basé sur un dessin par lignes. Celui-ci reste le plus répandu pour les représentations amplitude / temps, c'est pourquoi nous l'utilisons ici, bien qu'il soit trompeur. En effet, à cette échelle temporelle d'environ une seconde, il y a beaucoup plus d'échantillons que de points en largeur d'écran.

          Dans cet extrait du programme kediteur.c, le cas des sons codés sur 8 bits est traité en sus du cas normal de ceux codés sur 16 bits (cf. Gestion du flux audionumérique). En revanche, le cas des sons stéréophoniques n'est pas traité, car cela demanderait deux fenêtres au lieu d'une. On suppose que le fichier wav est déjà chargé, ainsi que la bibliothèque kwav.

          void Dessine_wav(Window win, kwav_struct sonwav)

          {

          int i;

          int nb_ech;

          int zero;

          double pas;

          unsigned int h;

          unsigned int l;

          double zoomy;

          KWAV_ECH8* ptr8;

          h = getHeight(win);

          l = getWidth(win);

          nb_ech = (sonwav.head.data_length) / (sonwav.head.byte_p_spl);

          pas = nb_ech / (double)l;

          zoomy = h / pow(2, sonwav.head.bit_p_spl);

          ptr8 = (KWAV_ECH8*)sonwav.data;

          XSetForeground(dpy, gc, rouge); /* horizontale */

          XDrawLine(dpy, win, gc, 0, h/2, l, h/2);

          XSetForeground(dpy, gc, jaune); /* signal */

          if(sonwav.head.bit_p_spl != 8)

          {

          zero = (h/2);

          for(i=0; (i+1)*pas<nb_ech; i++)

          XDrawLine(dpy, win, gc,

          i, sonwav.data[(int)(i*pas)] * zoomy + zero,

          i+1, sonwav.data[(int)((i+1)*pas)] * zoomy + zero);

          }

          else

          {

          zero = 0;

          for(i=0; (i+1)*pas<nb_ech; i++)

          XDrawLine(dpy, win, gc,

          i, ptr8[(int)(i*pas)] * zoomy + zero,

          i+1, ptr8[(int)((i+1)*pas)] * zoomy + zero);

          }

          XFlush(dpy);

          }

          Cet algorithme commence par récupérer la taille de la fenêtre dans laquelle il doit dessiner, pour en déduire le pas qu'il devra faire dans le son pour aller d'un échantillon au suivant. Cette variable est de type double pour pouvoir afficher des sons qui seraient plus courts que la largeur de la fenêtre, une sorte d'échelle microscopique. Le zoomy est nivelé par la résolution maximum du son.

          Après avoir tracé une ligne horizontale en rouge, le programme commence à tracer les lignes jaunes qui représentent le signal lui-même, lorsqu'il n'est pas codé sur 8 bits. La boucle trace des lignes qui relient le pixel courant à son voisin horizontal de droite, dont les ordonnées sont relevées dans le son à l'échantillon correspondant, puis nivelées par le zoomy et le zero. Cette boucle s'arrête dès que le numéro de l'hypothétique échantillon qui correspond au pixel suivant dépasse le nombre d'échantillons de ce son.

          L'algorithme des sons 8 bits est identique, à ceci près que zero vaut 0 au lieu de h/2, à cause du caractère non signé du type KWAV_ECH8 qui vaut un unsigned char au lieu d'un signed short (cf. Gestion du flux audionumérique).
           
           

        3. Comment afficher le spectre d'un son ?
      La représentation suivante s'appelle sonogramme ou sonagramme (LESBROS 1992). Il s'agit d'une représentation en trois dimensions : le temps en abscisse, la fréquence en ordonnée, et l'amplitude en luminosité.

      La fonction suivante trace un sonogramme du son passé en argument. La fonction kfft_Init est supposée avoir déjà été appelée, et les bibliothèques kwav et kfft inclues. Cet algorithme est donc basé sur la transformée de Fourier (cf. La bibliothèque kfft).

      void Dessine_sono(Window win, kwav_struct sonwav)

      {

      int i, j, amp, inc, nbfreqs;

      unsigned int h;

      unsigned int l;

      h = getHeight(win);

      l = getWidth (win);

      nbfreqs = kfft_get_nbfreqs();

      XClearWindow (dpy, win);

      inc = selwav.head.data_length / sonwav.head.byte_p_spl / l;

      for(i=0; i<l; i++)

      {

      kfft_Analyse(sonwav.data + i*inc);

      for(j=0; j<h; j++)

      {

      amp = kfft_modules_reels[j*nbfreqs/h/2];

      XSetForeground(dpy, gc, conversion((amp>512)?255:0, (amp>256)?255:amp, 0));

      XDrawPoint(dpy, win, gc, i, h-j);

      XFlush(dpy);

      }

      }

      }

      Comme le programme précédent, celui-ci récupère les dimensions de la fenêtre graphique, et calcule un incrément inc pour la correspondance entre le nombre de pixels en abscisse et le nombre d'échantillons. Il récupère aussi le nombre de fréquences qui seront renvoyées par l'analyse, pour la correspondance en ordonnée. La boucle principale se déplace de gauche à droite, pixel par pixel. Pour chacune de ces tranches verticales d'un pixel d'épaisseur, elle lance une analyse du son à partir de l'échantillon qui correspond au pixel courant :

      kfft_Analyse(sonwav.data + i*inc);

      La boucle secondaire balaye les ordonnées de ces tranches verticales de bas en haut. L'énergie amp de la bande de fréquences qui correspond à l'ordonnée courante est récupérée, sachant que seule la première moitié du tableau des amplitudes est pertinente :

      amp = kfft_modules_reels[j*nbfreqs/h/2];

      Cette énergie est traduite en couleurs du jaune au vert, grâce à la fonction de conversion du codage en rouge, vert, bleu en code couleur, puis affichée :

      XSetForeground(dpy, gc, conversion((amp>512)?255:0, (amp>256)?255:amp, 0));

      XDrawPoint(dpy, win, gc, i, h-j);
       
       

    3. Pour aller plus loin

    4.  

       
       
       
       
       
       
       

      1. Principes de développement d'une bibliothèque sous Linux

      2.  

         
         
         
         
         
         
         
         

        D'abord, il faut ne pas oublier que l'on développe pour d'autres programmeurs, d'où la nécessité d'une pensée rigueureuse et cohérente (cf. Ergonomie générale).
         
         

        1. Conventions de nommage et de compilation

        2.  

           
           
           
           
           
           
           
           

          Pour les bibliothèques partagées, le nom doit commencer par lib pour library, même si ce préfixe disparaît lorsqu'on appelle une bibliothèque, et contenir l'extension .so pour " shared object ", qui disparaît aussi :

          libkwav.so

          gcc mon_programme.c -L/usr/local/ -lkwav

          Ceci n'est pas vrai pour les archives, qui ne prennent pas de préfixe, et dont l'extension .a pour archive reste lors de l'appel à la compilation :

          kwav.a

          gcc mon_programme.c kwav.a

          Pour générer une bibliothèque partagée, le compilateur gcc prend l'option -shared " partagé " en français, et l'option -o suivie du nom du fichier ainsi compilé :

          gcc -shared -Wall -lm libkwav3.c -o libkwav.so

          Les options -Wall et -lm concernent respectivement le signalement d'avertissements supplémentaires, et l'appel de la bibliothèque contenant les fonctions mathématiques standards.

          La génération d'une archive s'effectue en deux temps. Le premier compile le fichier source libkwav.c en un fichier objet libkwav.o, à l'aide de l'option -c de gcc :

          gcc -c libkwav.c

          Le deuxième créé l'archive à partir de ce fichier objet :

          ar -cr kwav.a libkwav.o

          Le programme ar permet de créer, modifier, et extraire des archives. L'option -c créé une archive, et -d insère les fichiers dans l'archive, en les remplaçant s'ils existent déjà.
           
           

        3. Le manuel
Le manuel en ligne est très utile. Il existe traditionnellement autant de maunels que de fonctions. Cependant, comme nos bibliothèques contiennent relativement peu de fonctions, il n'y a qu'un manuel nommé kwav.3. L'extension .3 indique la section à laquelle appartient ce manuel, qui doit donc être placé dans un répertoire man3, ici /usr/man/man3. Cette troisième section correspond aux manuels des fonctions de bibliothèques, et aux manuels personnels (se reporter au manuel de man pour les autres sections). Il est possible de les compresser car la fonction man est capable de décompresser à la volée les manuels au format .gz. Le langage troff (OSSANNA et KERNIGHAN, 1992) dans lequel ils sont écrits utilise des balises, un peu comme le langage HTML (HyperText Markup Langage), mais en quantité restreinte. C'est un système de formatage de documents, écrit d'abord en langage assembleur par Joseph OSSANNA pour les machines PDP-11 en 1973, puis réécrit en langage C et largement modifié (http://www.kohala.com/start/troff/troff.html). Les principales balises sont : Les premières versions des bibliothèques étaient placées dans le répertoire free, car il suffisait de se connecter en tant qu'utilisateur free pour copier les fichiers des bibliothèques dans les repertoires idoines. Maintenant que ces bibliothèques sont abouties, elles sont installées dans le répertoire usuel local, ce qui nécessite de se connecter en tant que root.
 
 
      1. Fonctions sytèmes pour la gestion de la carte son
Voici la liste complète des requêtes des fonctions ioctl qui concernent le son, telle qu'elle est présentée dans le manuel ioctl_list de Linux/i386 kernel 1.3.27. Ce manuel, daté du 17 septembre 1995, dresse la liste non exhaustive de 421 requêtes, dont 128 sont dédiées au son. Elles contrôlent le paramétrage de la carte son pour le MIDI, la synthèse par modulation de fréquence, le processeur de signal (DSP), le coprocesseur, et le mixage. Les fonctions du lecteur de cd-roms ne sont pas rappelées ici. Lorsqu'une erreur se produit, la fonction ioctl renvoie en général la valeur -1. Dans ce manuel, un argument du type 'const struct foo *' signifie qu'il est en entrée du noyau. 'struct foo *' signifie que le noyau sort un argument de ce type. Si le noyau utilise l'argument à la fois en entrée et en sortie, il est noté I-O.

IOCTL_LIST(2) Linux Programmer's Manual IOCTL_LIST(2)

// <include/linux/soundcard.h>

0x00005100 SNDCTL_SEQ_RESET void

0x00005101 SNDCTL_SEQ_SYNC void

0xC08C5102 SNDCTL_SYNTH_INFO struct synth_info * // I-O

0xC0045103 SNDCTL_SEQ_CTRLRATE int * // I-O

0x80045104 SNDCTL_SEQ_GETOUTCOUNT int *

0x80045105 SNDCTL_SEQ_GETINCOUNT int *

0x40045106 SNDCTL_SEQ_PERCMODE void

0x40285107 SNDCTL_FM_LOAD_INSTR const struct sbi_instrument *

0x40045108 SNDCTL_SEQ_TESTMIDI const int *

0x40045109 SNDCTL_SEQ_RESETSAMPLES const int *

0x8004510A SNDCTL_SEQ_NRSYNTHS int *

0x8004510B SNDCTL_SEQ_NRMIDIS int *

0xC074510C SNDCTL_MIDI_INFO struct midi_info * // I-O

0x4004510D SNDCTL_SEQ_THRESHOLD const int *

0xC004510E SNDCTL_SYNTH_MEMAVL int * // I-O

0x4004510F SNDCTL_FM_4OP_ENABLE const int *

0xCFB85110 SNDCTL_PMGR_ACCESS struct patmgr_info * // I-O

0x00005111 SNDCTL_SEQ_PANIC void

0x40085112 SNDCTL_SEQ_OUTOFBAND const struct seq_event_rec *

0xC0045401 SNDCTL_TMR_TIMEBASE int * // I-O

0x00005402 SNDCTL_TMR_START void

0x00005403 SNDCTL_TMR_STOP void

0x00005404 SNDCTL_TMR_CONTINUE void

0xC0045405 SNDCTL_TMR_TEMPO int * // I-O

0xC0045406 SNDCTL_TMR_SOURCE int * // I-O

0x40045407 SNDCTL_TMR_METRONOME const int *

0x40045408 SNDCTL_TMR_SELECT int * // I-O

0xCFB85001 SNDCTL_PMGR_IFACE struct patmgr_info * // I-O

0xC0046D00 SNDCTL_MIDI_PRETIME int * // I-O

0xC0046D01 SNDCTL_MIDI_MPUMODE const int *

0xC0216D02 SNDCTL_MIDI_MPUCMD struct mpu_command_rec * // I-O

0x00005000 SNDCTL_DSP_RESET void

0x00005001 SNDCTL_DSP_SYNC void

0xC0045002 SNDCTL_DSP_SPEED int * // I-O

0xC0045003 SNDCTL_DSP_STEREO int * // I-O

0xC0045004 SNDCTL_DSP_GETBLKSIZE int * // I-O

0xC0045006 SOUND_PCM_WRITE_CHANNELS int * // I-O

0xC0045007 SOUND_PCM_WRITE_FILTER int * // I-O

0x00005008 SNDCTL_DSP_POST void

0xC0045009 SNDCTL_DSP_SUBDIVIDE int * // I-O

0xC004500A SNDCTL_DSP_SETFRAGMENT int * // I-O

0x8004500B SNDCTL_DSP_GETFMTS int *

0xC0045005 SNDCTL_DSP_SETFMT int * // I-O

0x800C500C SNDCTL_DSP_GETOSPACE struct audio_buf_info *

0x800C500D SNDCTL_DSP_GETISPACE struct audio_buf_info *

0x0000500E SNDCTL_DSP_NONBLOCK void

0x80045002 SOUND_PCM_READ_RATE int *

0x80045006 SOUND_PCM_READ_CHANNELS int *

0x80045005 SOUND_PCM_READ_BITS int *

0x80045007 SOUND_PCM_READ_FILTER int *

0x00004300 SNDCTL_COPR_RESET void

0xCFB04301 SNDCTL_COPR_LOAD const struct copr_buffer *

0xC0144302 SNDCTL_COPR_RDATA struct copr_debug_buf * // I-O

0xC0144303 SNDCTL_COPR_RCODE struct copr_debug_buf * // I-O

0x40144304 SNDCTL_COPR_WDATA const struct copr_debug_buf *

0x40144305 SNDCTL_COPR_WCODE const struct copr_debug_buf *

0xC0144306 SNDCTL_COPR_RUN struct copr_debug_buf * // I-O

0xC0144307 SNDCTL_COPR_HALT struct copr_debug_buf * // I-O

0x4FA44308 SNDCTL_COPR_SENDMSG const struct copr_msg *

0x8FA44309 SNDCTL_COPR_RCVMSG struct copr_msg *

0x80044D00 SOUND_MIXER_READ_VOLUME int *

0x80044D01 SOUND_MIXER_READ_BASS int *

0x80044D02 SOUND_MIXER_READ_TREBLE int *

0x80044D03 SOUND_MIXER_READ_SYNTH int *

0x80044D04 SOUND_MIXER_READ_PCM int *

0x80044D05 SOUND_MIXER_READ_SPEAKER int *

0x80044D06 SOUND_MIXER_READ_LINE int *

0x80044D07 SOUND_MIXER_READ_MIC int *

0x80044D08 SOUND_MIXER_READ_CD int *

0x80044D09 SOUND_MIXER_READ_IMIX int *

0x80044D0A SOUND_MIXER_READ_ALTPCM int *

0x80044D0B SOUND_MIXER_READ_RECLEV int *

0x80044D0C SOUND_MIXER_READ_IGAIN int *

0x80044D0D SOUND_MIXER_READ_OGAIN int *

0x80044D0E SOUND_MIXER_READ_LINE1 int *

0x80044D0F SOUND_MIXER_READ_LINE2 int *

0x80044D10 SOUND_MIXER_READ_LINE3 int *

0x80044D1C SOUND_MIXER_READ_MUTE int *

0x80044D1D SOUND_MIXER_READ_ENHANCE int *

0x80044D1E SOUND_MIXER_READ_LOUD int *

0x80044DFF SOUND_MIXER_READ_RECSRC int *

0x80044DFE SOUND_MIXER_READ_DEVMASK int *

0x80044DFD SOUND_MIXER_READ_RECMASK int *

0x80044DFB SOUND_MIXER_READ_STEREODEVS int *

0x80044DFC SOUND_MIXER_READ_CAPS int *

0xC0044D00 SOUND_MIXER_WRITE_VOLUME int * // I-O

0xC0044D01 SOUND_MIXER_WRITE_BASS int * // I-O

0xC0044D02 SOUND_MIXER_WRITE_TREBLE int * // I-O

0xC0044D03 SOUND_MIXER_WRITE_SYNTH int * // I-O

0xC0044D04 SOUND_MIXER_WRITE_PCM int * // I-O

0xC0044D05 SOUND_MIXER_WRITE_SPEAKER int * // I-O

0xC0044D06 SOUND_MIXER_WRITE_LINE int * // I-O

0xC0044D07 SOUND_MIXER_WRITE_MIC int * // I-O

0xC0044D08 SOUND_MIXER_WRITE_CD int * // I-O

0xC0044D09 SOUND_MIXER_WRITE_IMIX int * // I-O

0xC0044D0A SOUND_MIXER_WRITE_ALTPCM int * // I-O

0xC0044D0B SOUND_MIXER_WRITE_RECLEV int * // I-O

0xC0044D0C SOUND_MIXER_WRITE_IGAIN int * // I-O

0xC0044D0D SOUND_MIXER_WRITE_OGAIN int * // I-O

0xC0044D0E SOUND_MIXER_WRITE_LINE1 int * // I-O

0xC0044D0F SOUND_MIXER_WRITE_LINE2 int * // I-O

0xC0044D10 SOUND_MIXER_WRITE_LINE3 int * // I-O

0xC0044D1C SOUND_MIXER_WRITE_MUTE int * // I-O

0xC0044D1D SOUND_MIXER_WRITE_ENHANCE int * // I-O

0xC0044D1E SOUND_MIXER_WRITE_LOUD int * // I-O

0xC0044DFF SOUND_MIXER_WRITE_RECSRC int * // I-O
 
 

  1. Conclusion

 
 

Dès les débuts de l'informatique, la musique a cherché à mettre l'ordinateur à son service, notamment avec des programmes de synthèse sonore (POPE 1993). Certains ordinateurs ont ensuite marqué l'informatique musicale pour le grand public, comme l'Atari, ou les Macintoshs, qui possèdent des API (Application Programming Interface) de haut niveau, qui facilitent le développement d'applications audionumériques. Les systèmes Unix sont capables de gèrer du son, mais il faut utiliser des fonctions de plutôt bas niveau comme les ioctl. Les deux bibliothèques développées pour ce travail de maîtrise proposent des fonctions d'un niveau d'abstraction supérieur, de manière à simplifier la programmation de logiciels audionumériques sur ce système d'exploitation, que nous utilisons à l'université.

Ce mémoire présente le travail d'une année, et porte sur la gestion du son sous Linux. Il contient la bibliothèque kwav, qui gère le son d'une manière robuste en ne manipulant qu'un son à la fois, grâce à un mécanisme de verrouillage, et en utilisant le format wav. La bibliothèque kfft est un complément de kwav, et permet d'analyser un son en fréquence, de le resynthétiser, et d'avoir accès à la taille de la fenêtre d'analyse. L'application kediteur permet de lire, d'enregistrer, et de visualiser du son, et démontre donc les fonctionnalités des deux bibliothèques. Ce projet comprend enfin un manuel en ligne, et quelques programmes simples à vocation pédagogique.

Ce travail pourrait être complété par une démonstration des fonctions de resynthèse et de la manipulation des fichiers stéréophoniques, et par un perfectionnement de l'éditeur, comme la saisie des chaînes de caractères directement dans l'interface graphique ; ce serait utile pour les noms de fichiers, et le reparamètrage de l'environnement audio de kwav. Pour que l'internationalisation du projet soit totale, il faudrait traduire les commentaires, les noms de certaines variables, les messages d'erreurs, ainsi que le manuel en anglais. D'ailleurs, les outils GNU permettent de traduire automatiquement beaucoup de messages de l'anglais vers la langue souhaitée, grâce à la fonction gettext et à la variable d'environnement LANG.

Malgré ces possibilités de développement de ce projet, il s'agit d'un outil fonctionnel et prometteur. Des améliorations par d'autres programmeurs sont envisageables, car le code source est disponible sur le réseau internet depuis le serveur de l'université, à l'adresse http://inferno.cs.univ-paris8.fr/~barkati. Ces programmes pourraient constituer le point de départ d'une bibliothèque de plus grande portée, l'écriture essayant de répondre aux critères répandus au sein de la communauté informatique (GOURDIN 1991). Nous souhaitons que la diffusion de tels travaux favorise le développement des applications sonores, à l'université et peut-être ailleurs.
 
 


Références bibliographiques


Annexes






Ci-joint une impression du manuel en ligne de la bibliothèque kwav (taper man kwav sous shell), ainsi que le code source des programmes suivants :