Vous êtes sur la page 1sur 10

Programmation audio sous LINUX

Pierre Ficheux (pficheux@com1.fr) Janvier 2000

0. Rsum
Cette article est une introduction la ralisation d'applications audios sous LINUX. Nous prsenterons successivement la configuration du noyau LINUX pour le support audio, l'utilisation des fichiers spciaux (devices) utiliss par le noyau et une introduction la programmation en C de l'API audio de LINUX. Dans une dernire partie, nous prsenterons un petit exemple d'application ralisant du streaming audio. Les archives des programmes d'exemples sont disponibles sur http://www.com1.fr/~pficheux/articles/lmf/audio/exemples/exemples.tar.gz

1. Principe d'une carte son


La carte son est un priphrique effectuant des conversions entre des donnes analogiques (un signal audio en entre) et des donnes numriques (un fichier informatique de stockage). On a alors affaire un chantillonnage du signal audio. La conversion peut galement se faire dans l'autre sens, lorsque l'on veut jouer un fichier audio sur la sortie audio du PC. Pour effectuer cela, la carte audio est base sur une paire de circuits lectroniques appels Convertisseur Analogique Numrique et Convertisseur Numrique Analogique, ou en anglais ADC (Analog to Digital Converter) et DAC (Digital to Analog Converter).

2. Configuration du noyau LINUX


Le multimdia est une partie importante de l'informatique d'aujourd'hui. Les dveloppeurs du noyau LINUX ont raliss des prouesses en permettant d'utiliser une bonne partie des chipsets audios actuels. La configuration du noyau LINUX passe comme d'habitude par la suite de commande:
cd /usr/src/linux make config

On pourra avantageusement remplacer le make config par un make menuconfig (configuration en mode texte plein cran) ou un make xconfig (configuration par des scripts TCL/TK). Si on prend pour exemple le support d'une bonne vieille carte Sound Blaster de Creative Labs, on devra valider les options suivantes:
Sound card support (CONFIG_SOUND) [M/n/y/?] m ProAudioSpectrum 16 support (CONFIG_PAS) [N/y/?] Sound Blaster (SB, SBPro, SB16, clones) support (CONFIG_SB) [Y/n/?] ... /dev/dsp and /dev/audio support (CONFIG_AUDIO) [Y/n/?] MIDI interface support (CONFIG_MIDI) [N/y/?]

FM synthesizer (YM3812/OPL-3) support (CONFIG_YM3812) [N/y/?] I/O base for SB Check from manual of the card (SBC_BASE) [220] Sound Blaster IRQ Check from manual of the card (SBC_IRQ) [5] Sound Blaster DMA 0, 1 or 3 (SBC_DMA) [1] Sound Blaster 16 bit DMA (_REQUIRED_for SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards) (SB_DMA2) [1] MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card (SB_MPU_BASE) [0] SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Use -1 with SB16 (SB_MPU_IRQ) [-1] Audio DMA buffer size 4096, 16384, 32768 or 65536 (DSP_BUFFSIZE) [65536]

Dans le cas prsent, la configuration est la suivante: Support audio en module La carte est un SB ou une SB16 On supporte les fichiers spciaux /dev/audio et /dev/dsp La carte est l'adresse de base 220, interruption 5 et canal DMA 1

La compilation du noyau se fera ensuite par:


make dep; make clean; make zlilo; make modules; make modules_install

3. Les fichiers spciaux de l'API audio de LINUX


Nous nous bornerons ici dcrire les principaux fichiers spciaux utiliss par le noyau LINUX, en l'occurence: /dev/dsp Ce device permet de lire/crire les chantillons traits par la carte son. Dans ce cas la les donnes sont chantillonnes sur 8 bits. / Identique au device prcdent, sauf que les donnes sont chantillonnes sur 16 bits, lorque dev/dspW la carte le permet. /dev/audio Ce device permet de lire/crire des donnes au format mu-Law (encodage logarithmique). Pour info, ce device est compatible avec celui des stations de travail de type SUN. / Ce device permet de controler l'entre/sortie audio, par exemple rgler le volume de sortie, dev/mixer lorsque la carte le permet. / Ce device permet d'obtenir des infos concernant la configuration audio; dev/sndsta t L'utilisation de ces devices dans une session shell est extrmement simple. Par exemple pour jouer un fichier audio mu-Law (en gnral suffix .au) on fera:
cat fichier.au > /dev/audio

et pour enregistrer:
cat /dev/audio > fichier.au ^C

Pour obtenir les infos concernant la configuration audio:


[root@dc2000 /root]# cat /dev/sndstat

Sound Driver:3.5.4-960630 (mar jan 18 17:35:32 CET 2000 root, Linux atkins.local.com1.fr 2.0.36 #5 jeu sep 16 18:38:52 CEST 1999 i686 unknown) Kernel: Linux dc2000.local.com1.fr 2.0.36 #67 mer jan 19 09:13:18 CET 2000 i486 Config options: 0 Installed drivers: Type 2: Sound Blaster Card config: Sound Blaster at 0x220 irq 10 drq 1,5 Audio devices: 0: Sound Blaster 16 (4.12) Synth devices: Midi devices: NOT ENABLED IN CONFIG Timers: 0: System clock Mixers: 0: Sound Blaster

4. Programmation de l'API audio


Comme habituellement sous LINUX, l'API audio sera utilisable travers les devices dcrits ci dessus. On devra donc utiliser les appels systmes classiques comme: open close read write ioctl

L'ouverture d'un device audio en lecture se fera par une squence du type:
int fd_audio; if ((fd_audio = open ("/dev/audio", O_RDONLY)) < 0) { perror ("/dev/audio"); exit (1); } /* Le device audio est disponible */ ...

La lecture des chantillons se fera par:


#define BUF_SIZE 4096 int n; char buf[BUF_SIZE]; if ((n = read (fd_audio, buf, sizeof(buf))) < 0) { perror ("audio read");

exit (1); } /* Les chantillons sont disponibles dans buf */ ...

La fermeture du device se fera par un simple:


close (fd_audio);

De mme, on pourra jouer les chantillons mu-Law en utilisant la portion de code ci-dessous
#include #include #include #include <stdio.h> <unistd.h> <fcntl.h> <sys/soundcard.h>

#define BUF_SIZE 4096 main (int ac, char **av) { int fd_file, fd_audio, n, nleft, nwritten; char buf[BUF_SIZE], *p; if ((fd_file = open (av[1], O_RDONLY)) < 0) { perror (av[1]); exit (1); } if ((fd_audio = open ("/dev/audio", O_WRONLY)) < 0) { perror ("/dev/audio"); exit (1); } /* Lecture de l'chantillon */ while ((n = read (fd_file, buf, sizeof(buf))) > 0) { nleft = n; p = buf; /* On envoit l'chantillon... */ while (nleft) { if ((nwritten = write (fd_audio, p, nleft)) < 0) perror ("/dev/audio"); else printf ("%d/%d written.\n", nwritten, nleft); nleft -= nwritten; p += nwritten;

} }

close (fd_file); close (fd_audio); }

On pourra controler des paramtres tels que le volume de sortie en utilisant le device /dev/mixer. Pour lire le volume de sortie, on utilisera:

int fd_mixer, vol; if ((fd_mixer = open ("/dev/mixer", O_RDWR)) < 0) { perror ("/dev/mixer"); exit (1); } if (ioctl (fd_mixer, SOUND_MIXER_READ_VOLUME, &vol)==-1) { perror ("ioctl"); } else printf ("vol= %d %d\n", vol&255, (vol >> 8)&255); close (fd_mixer);

et pour l'crire:
int fd_mixer, vol, v; if ((fd_mixer = open ("/dev/mixer", O_RDWR)) < 0) { perror ("/dev/mixer"); exit (1); } v = atoi(av[1]); vol = (v << 8) | v; if (ioctl (fd_mixer, SOUND_MIXER_WRITE_VOLUME, &vol)==-1) { perror ("ioctl"); } close (fd_mixer);

On pourra de mme visualiser et modifier le canal d'enregistrement (par exemple mic ou line):
static char *dev_names[] = int fd_mixer, mask, i; SOUND_DEVICE_NAMES;

if ((fd_mixer = open ("/dev/mixer", O_RDWR)) < 0) { perror ("/dev/mixer"); exit (1); } if (ioctl (fd_mixer, SOUND_MIXER_READ_RECSRC, &mask)==-1) { perror ("ioctl"); } for (i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { if (mask & (1 << i)) printf ("Enregistrement sur: %s\n", dev_names[i]); } close (fd_mixer);

Idem pour la slection du canal d'enregistrement en utilisant:


static char *dev_names[] = int fd_mixer, mask, i; SOUND_DEVICE_NAMES;

if ((fd_mixer = open ("/dev/mixer", O_RDWR)) < 0) { perror ("/dev/mixer"); exit (1); } mask = 0; for (i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { if (!strcmp (av[1], dev_names[i])) { mask |= (1 << i); break; } } if (!mask) { fprintf (stderr, "Device %s inconnu !\n", av[1]); close (fd_mixer); exit (1); } if (ioctl (fd_mixer, SOUND_MIXER_WRITE_RECSRC, &mask) == -1) { perror ("ioctl"); } close (fd_mixer);

5. Un petit exemple de streaming audio


De plus en plus de sites Internet fournissent des services de streaming vido ou audio. Le principe est simple: il s'agit de jouer en continu grace un navigateur Internet ou bien une application spcialise un flux de donnes provenant d'un serveur. Un exemple est la possibilit d'couter des stations de radios travers un rseau de type TCP/IP et l'aide d'un lecteur spcifique ou bien d'un navigateur Internet type NS Communicator quip d'un plugin. L'application qui suit est un petit serveur audioserver permettant de diffuser travers le rseau les chantillons audios provenant de l'entre d'une carte son d'un PC LINUX. Le principe de fonctionnement est le suivant: Lorsque le client se connecte sur le port associ au service audio, l'application ouvre le device /dev/audio . Si le client est un navigateur, ce dernier envoit une requte HTTP (de type GET) et le client doit rpondre en acquittant cette demande et en renvoyant le type des donnes transmises (ici audio/ulaw). C'est la reception de ce type MIME qui provoquera le chargement du plugin cot navigateur. Lorque le dialogue HTTP est termin, le serveur copie en temps rel les donnes audios sur la connexion rseau. Lorque le client coupe la connexion, le device audio est ferm Pour installer une application de ce type on devra: 1. Modifier le fichier /etc/services en ajoutant une ligne:
audio 5555/tcp

si le port utiliser est le 5555. 2. Modifier le fichier /etc/inetd.conf en ajoutant une ligne:
audio stream audioserver tcp nowait root /usr/local/bin/audioserver

3. Forcer le dmon inetd relire sa config en faisant:


kill -1 `cat /var/run/inetd.pid`

Cette application but pdagogique est volontairement simplifie: Elle utilise le dmon inetd et n'accepte donc qu'une seule connexion. Le donnes audios ne sont pas compresses, ce qui n'est pas envisageable dans le cas d' une application relle. Elle utilise le protocole TCP qui n'est pas le mieux adapt au transfert de donnes en streaming. Dans, ce cas il est plutt conseill d'utiliser le protocole UDP beaucoup moins gourmant en bande passante. Le plugin utiliser cot client n'est pas explicit mais il sera trs simple crire si vous relisez l'article concernant les plugins Netscape dans Linux Magazine numro 10 ou bien sur http://www.com1.fr/~pficheux/articles/lmf/plugins ! Le code source comment du serveur est donn ci-dessous: Cette premire partie comprends les includes et la dfinition de quelques constantes. La rponse HTTP est principalement constitue du code de rponse correcte HTTP/1.0 200 OK et du type des donnes Content-Type: audio/ulaw
#include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <fcntl.h> <syslog.h> <sys/soundcard.h>

extern char *basename(); #define HTTP_REPLY "HTTP/1.0 200 OK\r\nPragma: no-cache\r\nContent-Type: audio/ulaw\r\n\r\n" #define MAXREQ #define BUF_SIZE 1024 1024

La fonction ci-dessous effectue la lecture de la requte HTTP du navigateur


static char http_request[MAXREQ]; /* Lecture de la requte HTTP du client */ int get_http_request (int fd, char *request, int max_length) { char end, c, last_char; int length = 0; end = 0;

c = last_char = 0; while (!end) { if (read (fd, &c, 1) < 0) return -1; /* Test de fin de commande */ if (c == '\r' && last_char == '\n') { if (read (fd, &c, 1) < 0) return -1; if (c == '\n') end = 1; else { syslog (LOG_ERR, "get_http_request: bad end of request!\n"); return -1; } else { if (request) *(request+length) = c; if (length < max_length-1) length++; /* Dpassement de taille de requte ou pas de marqueur de fin */ else { syslog (LOG_ERR, "get_http_request: request too long!\n"); return -1; } } } last_char = c; }

if (request) *(request+length) = 0; } return length;

Cette fonction appelle la fonction prcdente puis envoit la rponse HTTP au client.
/* Dialogue HTTPD ? */ static void http_dialog (int fd_in, int fd_out) { int n; /* Lecture requte */ if ((n = get_http_request (fd_in, http_request, sizeof(http_request))) <= 0) return; /* Envoi rponse HTTP */ write (fd_out, HTTP_REPLY, strlen(HTTP_REPLY));

La suite dcrit le programme principal. On commence par ouvrir de device audio.

main (int ac, char **av) { int n, nleft, nwritten, fd_audio; char buf[BUF_SIZE], *p, *progname = basename(av[0]); openlog (progname, LOG_PID | LOG_CONS, LOG_DAEMON); if ((fd_audio = open ("/dev/audio", O_RDONLY)) < 0) { syslog (LOG_ERR, "Can't open audio device, exiting.\n"); exit (1); }

Puis on effectue le dialogue HTTP.


/* Dialogue HTTP */ http_dialog (0, 1);

Ensuite, on lit des donnes audios et on les envoit sur le rseau. La condition d'arrt est en gnral la coupure de la connexion cot client.
while ((n = read (fd_audio, buf, sizeof(buf))) > 0) { nleft = n; p = buf; /* On envoit l'chantillon... */ while (nleft) { if ((nwritten = write (1, p, nleft)) < 0) { syslog (LOG_ERR, write: %m"); exit (1); } nleft -= nwritten; p += nwritten;

} } }

close (fd_audio);

6. Autres drivers audios sous LINUX


Le driver audio fourni avec le noyau LINUX 2.0 ou 2.2 supporte un bon nombre de chipsets. Il existe cependant d'autres alternatives. On pourra citer: Le driver OSS, qui est une version commerciale du driver audio standard. Il est dvelopp par la socit 4 Front Technologies fonde par l'auteur du driver LINUX. Ce driver supporte un trs grand nombre de chipsets du march et coute entre $10 et $30 suivant les options. L'installation est trs simple et pas mal de chipsets sont dtects automatiquement. On peut tlcharger une version d'essai sur http://www.opensound.com Le driver ALSA (Advanced Linux Sound Architecture) est un driver compltement indpendant du driver standard. Il est diffus sous GPL sur http://www.alsa-project.org. Il faut noter que l'API de programmation est diffrente de celle du driver standard ou

bien d'OSS.

7. Bibliographie
Le Sound-HOWTO sur http://www.freenix.org/unix/linux/HOWTO-vo/Sound-HOWTO.html ou en franais sur http://www.freenix.org/unix/linux/HOWTO/Sound-HOWTO.html Le Sound-Playing-HOWTO sur http://www.freenix.org/unix/linux/HOWTO-vo/Sound-HOWTO.html ou en franais sur http://www.freenix.org/unix/linux/HOWTO/Sound-Playing-HOWTO.html Documentation OSS sur http://www.opensound.com

Vous aimerez peut-être aussi