Pierre Ficheux (pficheux@com1.fr)
Janvier 2000
Pour effectuer cela, la carte audio est basée sur une paire de circuits électroniques appelés Convertisseur Analogique Numérique et Convertisseur Numérique Analogique, ou en anglais ADC (Analog to Digital Converter) et DAC (Digital to Analog Converter).
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 présent, la configuration est la suivante:
La compilation du noyau se fera ensuite par:
make dep; make clean; make zlilo; make modules; make modules_install
/dev/dsp | Ce device permet de lire/écrire les échantillons traités par la carte son. Dans ce cas la les données sont échantillonnées sur 8 bits. |
/dev/dspW | Identique au device précédent, sauf que les données sont échantillonnées sur 16 bits, lorque la carte le permet. |
/dev/audio | Ce device permet de lire/écrire des données au format mu-Law (encodage logarithmique). Pour info, ce device est compatible avec celui des stations de travail de type SUN. |
/dev/mixer | Ce device permet de controler l'entrée/sortie audio, par exemple règler le volume de sortie, lorsque la carte le permet. |
/dev/sndstat | Ce device permet d'obtenir des infos concernant la configuration audio; |
cat fichier.au > /dev/audioet pour enregistrer:
cat /dev/audio > fichier.au ^CPour 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
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 même, on pourra jouer les échantillons mu-Law en utilisant la portion de code ci-dessous
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <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 paramètres 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 même visualiser et modifier le canal d'enregistrement (par exemple mic ou line):
static char *dev_names[] = SOUND_DEVICE_NAMES; int fd_mixer, mask, i; 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 sélection du canal d'enregistrement en utilisant:
static char *dev_names[] = SOUND_DEVICE_NAMES; int fd_mixer, mask, i; 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);
L'application qui suit est un petit serveur audioserver permettant de diffuser à travers le réseau les échantillons audios provenant de l'entrée d'une carte son d'un PC LINUX. Le principe de fonctionnement est le suivant:
Pour installer une application de ce type on devra:
audio 5555/tcpsi le port à utiliser est le 5555.
audio stream tcp nowait root /usr/local/bin/audioserver audioserver
kill -1 `cat /var/run/inetd.pid`
Cette application à but pédagogique est volontairement simplifiée:
Le code source commenté du serveur est donné ci-dessous:
Cette première partie comprends les includes et la définition de quelques constantes. La réponse HTTP est principalement constituée du code de réponse correcte HTTP/1.0 200 OK et du type des données Content-Type: audio/ulaw
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <syslog.h> #include <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 1024 #define BUF_SIZE 1024La fonction ci-dessous effectue la lecture de la requête HTTP du navigateur
static char http_request[MAXREQ]; /* Lecture de la requête 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++; /* Dépassement de taille de requète 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 précédente puis envoit la réponse HTTP au client.
/* Dialogue HTTPD ? */ static void http_dialog (int fd_in, int fd_out) { int n; /* Lecture requête */ if ((n = get_http_request (fd_in, http_request, sizeof(http_request))) <= 0) return; /* Envoi réponse HTTP */ write (fd_out, HTTP_REPLY, strlen(HTTP_REPLY)); }La suite décrit 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 données audios et on les envoit sur le réseau. La condition d'arrêt est en général 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); }
Copyright (c) 2001 Linux Magazine France. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".