Introduction
Un son bref peut signaler qu'une tâche est terminée. Dans ce laboratoire, vous créerez le son lui-même : une boucle C calcule une suite de nombres qui deviennent un fichier WAV lisible. Écoutez d'abord un exemple fonctionnel à 440 Hz, changez uniquement sa hauteur, puis complétez votre propre fonction d'échantillonnage réutilisable.
Vous devez connaître les variables, l'arithmétique, les boucles, les tableaux et les fonctions C, ainsi que la navigation de base dans un terminal. L'échantillonnage audio, les radians et la sortie binaire sont présentés ici ; les pointeurs et le traitement du signal ne sont pas des prérequis. Le compilateur, un petit module transparent d'écriture WAV et un lecteur web limité aux fichiers sont préparés. Tout l'audio est une synthèse originale ; aucun dispositif d'enregistrement n'est nécessaire. Les 50 minutes sont une estimation de l'auteur incluant la lecture et les erreurs courantes.
Écouter un premier son fonctionnel
Dans cette étape, vous lirez un son fonctionnel d'une demi-seconde, puis changerez une valeur pour le rendre plus aigu. Commencez dans Terminal. cd change de répertoire ; ce chemin absolu permet aussi de retrouver le bon répertoire de travail en cas d'erreur. Travaillez ici.
cd /home/labex/project/sound-studio
Un échantillon est un nombre signé décrivant la forme d'onde à un instant. PCM signifie modulation par impulsions codées : le fichier stocke directement ces valeurs. Ce laboratoire utilise un canal, dit mono, et 16 000 échantillons par seconde. Ses échantillons signés sur 16 bits représentent les entiers de -32 768 à 32 767. Les valeurs positives et négatives correspondent aux deux côtés de l'onde ; zéro en est le centre. L'amplitude détermine leur éloignement de zéro. Si vous écoutez avec un haut-parleur ou un casque, commencez à un volume confortable.
Le fichier préparé sample.c produit une sinusoïde à 440 Hz, soit 440 cycles par seconde. sin, déclaré dans <math.h>, reçoit un angle en radians et renvoie une valeur entre -1 et 1. Un cycle complet vaut 2 * pi radians. Multiplier par 10 000 donne une amplitude modérée à cette forme. Nous examinerons le calcul du temps après avoir écouté le résultat.
Compilez l'exemple fourni. gcc compile le C ; -std=c11 sélectionne C11, -Wall -Wextra active des avertissements utiles et -o sample nomme l'exécutable. pcm.c fournit le module d'écriture WAV. -lm, placé après les fichiers source, lie la bibliothèque mathématique utilisée par sin. Ces fichiers ne devraient produire aucun avertissement de compilation.
gcc -std=c11 -Wall -Wextra sample.c pcm.c -lm -o sample
./sample lance l'exécutable du répertoire courant. Il écrit des octets WAV binaires sur la sortie standard. > redirige ces octets vers un fichier ; ne lancez pas ce programme sans redirection, car les octets audio ne sont pas du texte lisible dans le terminal. outputs est un répertoire déjà préparé. Utilisez ce nouveau nom :
./sample > outputs/sample-440.wav
En cas de réussite, aucun texte n'est affiché. Passez à l'onglet Sound Studio. Cliquez sur Refresh files, choisissez sample-440.wav sous WAV file, puis cliquez sur Load sound. La page doit indiquer Ready to play et 8000 samples · 16000 Hz · 0.500 s. Cliquez sur Play, puis essayez Pause et Playback position. Vous pouvez cibler le réglage de position avec Tab, puis utiliser Home, End et les flèches. Les contrôles audio natifs peuvent arrondir une durée inférieure à une seconde à 0:00 ; le texte au-dessus et la position en secondes donnent la durée précise.
Cliquez sur Download WAV pour enregistrer le vrai fichier. Le lecteur lit le fichier créé ; il ne génère aucun échantillon manquant. Cannot load sound signale un fichier absent ou non pris en charge. Ce n'est pas une erreur de connexion empêchant l'ouverture de la page du studio. En cas d'erreur de chemin, revenez dans Terminal, utilisez le cd ci-dessus, vérifiez le nom, relancez la commande correspondante et actualisez la liste.

Repérez le fichier sélectionné sample-440.wav, les métadonnées de la demi-seconde et les contrôles Play, Playback position et Download WAV.
Revenez dans Terminal. cat affiche du texte. Lisez le court exemple :
cat sample.c
La boucle parcourt les indices d'échantillons de 0 à 7999. seconds = (double)i / rate convertit un indice en temps : l'indice 0 correspond à 0 seconde et l'indice 160 à 0,010 seconde. (double) demande une division décimale plutôt qu'entière. La phase, 2.0 * pi * frequency * seconds, mesure l'angle parcouru dans les cycles. (int) convertit le sinus mis à l'échelle en entier en supprimant la partie fractionnaire. La fonction fournie wav_sample écrit les deux octets PCM de ce nombre ; elle ne choisit pas le nombre à votre place.
Changez maintenant uniquement la fréquence. La commande suivante utilise sed, un éditeur de texte permettant une substitution précise. -i modifie sur place le fichier nommé ; l'expression entre guillemets s/old/new/ remplace le texte littéral entre la première paire de barres par celui entre la seconde. Elle ne modifie que sample.c.
sed -i 's/frequency = 440/frequency = 660/' sample.c
Recompilez pour intégrer la modification dans l'exécutable, puis enregistrez un autre fichier. Conservez aussi le premier WAV.
gcc -std=c11 -Wall -Wextra sample.c pcm.c -lm -o sample
./sample > outputs/sample-660.wav
Passez à Sound Studio, actualisez, chargez sample-660.wav et lisez-le. Il possède toujours 8 000 échantillons et une durée d'une demi-seconde, avec un son plus aigu. Seule la fréquence a changé. Conservez sample.c modifié, l'exécutable sample et les deux fichiers. Cette comparaison prépare la suite : votre programme réutilisable et ses résultats distincts.
Calculer chaque échantillon vous-même
Dans cette étape, vous compléterez write_tone afin qu'une fonction C fonctionne avec plusieurs hauteurs, durées et amplitudes. Commencez dans Terminal, dans /home/labex/project/sound-studio.
À 16 000 échantillons par seconde, 300 millisecondes nécessitent 16000 * 300 / 1000 = 4800 échantillons. Une milliseconde est un millième de seconde. À cette fréquence, les durées en millisecondes entières correspondent à des nombres entiers exacts d'échantillons. Une boucle partant de i = 0 et continuant tant que i < count produit exactement count valeurs ; utiliser <= ajouterait un échantillon indésirable.
Considérez un exemple bien plus petit : à huit échantillons par seconde, une sinusoïde d'un cycle par seconde prend approximativement les valeurs 0, 0.71, 1, 0.71, 0, -0.71, -1, -0.71 aux huit premiers indices. L'indice fait avancer le temps et sin fournit la position verticale. Notre fréquence réelle de 16 000 Hz donne beaucoup plus de valeurs par cycle.
Lisez l'interface et la fonction initiale fournies :
cat tone.h tone.c
write_tone(frequency, milliseconds, amplitude) doit générer tous les échantillons. Le code initial écrit des zéros : il peut donc compiler et produire un WAV de structure valide mais silencieux. Un nom, une taille ou une sortie réussie ne prouvent pas l'existence d'un son.
Gardez main.c, tone.h, pcm.h, pcm.c, inspect_wav.py et audio.py inchangés. Le fichier préparé main.c lit trois arguments, les valide, écrit l'en-tête puis appelle votre fonction. Inutile de modifier son analyseur d'arguments fondé sur des pointeurs. tone.c est votre implémentation ; le navigateur est déjà préparé.
Le module d'écriture sépare l'encapsulation de la synthèse. wav_begin(count) écrit une fois l'en-tête ; wav_sample(value) écrit un entier fourni. Aucun des deux ne génère de forme d'onde. Le petit fichier pcm.c utilise putchar pour écrire un octet sur la sortie standard et fputs pour écrire les noms fixes des blocs. Il émet l'octet faible puis l'octet fort, au lieu de copier une structure C brute. Les échantillons négatifs sont encodés dans leur représentation sur 16 bits ; le module refuse les valeurs hors de l'intervalle signé et ne les écrête ni ne les remplace.
Voici les champs qu'il écrit. Le nom fmt inclut son espace final.
| Champ | Signification dans ce laboratoire |
|---|---|
RIFF, size, WAVE |
Identifient un conteneur WAV ; size vaut le nombre d'octets moins les 8 premiers |
fmt, size 16 |
Décrivent les 16 octets de format suivants |
Format 1, channels 1 |
PCM non compressé, mono |
Sample rate 16000 |
Échantillons par seconde |
Byte rate 32000 |
16 000 trames par seconde × 2 octets par trame |
Block alignment 2, sample bits 16 |
Une trame contient un échantillon de deux octets |
data, size 2 * count |
Identifient et comptent les octets PCM réels qui suivent |
Vous pouvez lire ce module fourni avec cat pcm.c. Ses champs décrivent la sortie ; changer seulement l'en-tête ne transforme pas des échantillons silencieux en son.
Implémentez la courte fonction ci-dessous. <math.h> déclare sin. pi est une constante décimale locale : le programme ne dépend donc pas de la macro non standard M_PI. amplitude est un facteur appliqué avant la conversion en entier ; le maximum de 12 000 imposé par l'analyseur maintient ces échantillons dans l'intervalle de 16 bits. Chaque appel commence son indice temporel à zéro.
cat > tone.c remplace uniquement ce fichier source. Le here-document <<'C' transmet les lignes suivantes à cat jusqu'à la dernière ligne contenant seulement C. Les guillemets du délimiteur préservent le texte C littéralement. Collez le bloc entier, dernière ligne comprise.
cat > tone.c <<'C'
#include <math.h>
#include "tone.h"
#include "pcm.h"
void write_tone(int frequency, int milliseconds, int amplitude) {
const int rate = 16000;
const double pi = 3.14159265358979323846;
int count = rate * milliseconds / 1000;
for (int i = 0; i < count; i++) {
double seconds = (double)i / rate;
double phase = 2.0 * pi * frequency * seconds;
int sample = (int)(amplitude * sin(phase));
wav_sample(sample);
}
}
C
Compilez votre fonction avec l'appelant et le module d'écriture préparés. Si le compilateur indique une erreur de ligne, corrigez tone.c ou répétez son bloc complet de remplacement, puis recompilez. Lancer un ancien exécutable après une compilation échouée testerait l'ancien code.
gcc -std=c11 -Wall -Wextra main.c tone.c pcm.c -lm -o tone
L'exécutable reçoit la fréquence en Hz, la durée en millisecondes entières, puis l'amplitude. Les intervalles inclusifs sont la fréquence 200–1200, la durée 100–1000 et l'amplitude 1000–12000. Créez deux nouvelles sorties avec votre programme :
./tone 440 300 8000 > outputs/tone-440.wav
./tone 660 500 10000 > outputs/tone-660.wav
Utilisez le lecteur préparé pour inspecter les octets réellement enregistrés. python3 lance l'outil Python nommé, en lecture seule ; vous n'avez pas à écrire de Python. Il signale les fichiers manquants, incomplets ou non pris en charge et ne les régénère jamais.
python3 inspect_wav.py outputs/tone-440.wav
Résultat attendu :
Format: signed 16-bit mono PCM
Rate: 16000 Hz
Frames: 4800
Duration: 0.300 s
Peak: 8000
Passez à Sound Studio. Actualisez, chargez et lisez tone-440.wav, puis chargez et lisez tone-660.wav. Les durées affichées sont 0,300 et 0,500 seconde. Téléchargez aussi une nouvelle sortie. Ce sont les résultats de votre fonction, distincts de sample-440.wav et sample-660.wav.

Le fichier sélectionné et le message Ready to play identifient tone-660.wav ; les métadonnées décrivent les échantillons réellement enregistrés.
Conservez tone.c, l'exécutable fonctionnel tone et les deux nouvelles sorties. La vérification lit les WAV enregistrés, teste l'exécutable livré et recompile votre source inchangée dans un répertoire temporaire avec d'autres paramètres valides. Les trois doivent fonctionner ; la source seule, un ancien exécutable ou un exemple copié ne suffisent pas. Les mathématiques et arrondis entiers équivalents sont acceptés : l'amplitude ajustée peut différer de l'amplitude demandée de 2 % plus 2 unités d'échantillon au maximum ; les crêtes peuvent dépasser la demande de 2 unités au maximum ; le résidu RMS (racine de la moyenne des carrés) par rapport à une sinusoïde à la fréquence demandée est limité à 0,5 % de l'amplitude plus 2 unités. Ce résidu représente une erreur d'échantillon de taille moyenne. La tolérance permet les différences d'arrondi, pas une modification supplémentaire de la hauteur demandée. Le nombre exact de trames et le format PCM restent obligatoires. La formule fournie satisfait largement ces limites.
Vérifier la durée et corriger un refus
Dans cette étape, vous réutiliserez la fonction avec une courte durée non ronde et distinguerez une entrée invalide d'un fichier son incorrect. Commencez dans Terminal.
Générez 101 millisecondes à 733 Hz avec une amplitude de 7 373. Un cycle n'a pas à diviser exactement la durée du segment ; count détermine toujours le nombre d'échantillons.
./tone 733 101 7373 > outputs/short-tone.wav
python3 inspect_wav.py outputs/short-tone.wav
Repérez Frames: 1616 et Duration: 0.101 s. Ils découlent directement de 16000 * 101 / 1000. Le dernier échantillon arrive juste avant la fin du segment ; n'ajoutez pas d'échantillon au point final. Un début ou un arrêt brusque peut produire un clic, car ce premier laboratoire n'applique pas encore de fondu. Le suivant présente une variation progressive du gain et des pauses entre les sons.
La commande suivante demande volontairement 99 millisecondes, moins que le minimum admis. Utilisez un nouveau nom temporaire. Le shell ouvre un fichier redirigé avant la validation des arguments ; un refus peut donc laisser un fichier vide et ne garantit pas de conserver un ancien fichier de même nom.
./tone 440 99 10000 > outputs/rejected.wav
echo $? affiche immédiatement le code de sortie natif de la commande précédente : zéro indique une réussite, une autre valeur un refus ou un échec. Ne lancez aucune commande entre l'essai et cette lecture.
echo $?
Vous devez voir l'explication de l'intervalle publié et le code 2. C'est un refus normal, pas une raison de réinitialiser l'environnement. 99 est l'argument de durée invalide. Les bons WAV existants sont des fichiers distincts et restent disponibles.
Passez à Sound Studio, actualisez, sélectionnez et chargez rejected.wav. La page doit afficher Cannot load sound et préciser que le fichier n'est pas un WAV PCM complet et pris en charge. La lecture et le téléchargement restent indisponibles. Un fichier vide ne devient pas un son parce que son nom finit par .wav.
Revenez dans Terminal. rm -- supprime le fichier exact après --, qui termine les options de commande. Supprimez seulement cette sortie temporaire en échec :
rm -- outputs/rejected.wav
Corrigez l'essai avec le minimum admis et un autre nouveau nom de sortie :
./tone 440 100 10000 > outputs/minimum-tone.wav
python3 inspect_wav.py outputs/minimum-tone.wav
Vous devez maintenant obtenir 1 600 trames et 0,100 seconde. Revenez à Sound Studio, actualisez et lisez minimum-tone.wav, puis chargez short-tone.wav et utilisez Download WAV. Pour vérifier le clavier, parcourez les contrôles avec Tab, activez Play avec Enter et utilisez Home, End et les flèches sur le curseur de position. Mettez en pause avant de changer de fichier.
Vous disposez maintenant d'une source réutilisable, d'un exécutable correspondant et de sorties réellement lisibles. Gardez la comparaison de l'exemple et vos propres sorties. Les vérifications s'exécutent dans des répertoires temporaires et lisent les fichiers en place ; elles ne réparent pas le programme et ne créent pas les éléments manquants. La lecture et le décodage du navigateur ne prouvent pas la connexion d'un haut-parleur physique ; ce sous-ensemble WAV limité n'est pas un encodeur audio généraliste.
Résumé
Vous avez relié l'indice temporel d'une boucle C à un son réel. Vous avez changé la hauteur d'un exemple, implémenté un échantillonnage sinusoïdal réutilisable, limité les amplitudes à l'intervalle signé sur 16 bits et enregistré des WAV contenant un nombre exact de trames. Vous avez inspecté, lu et téléchargé de vrais fichiers, puis corrigé un refus volontaire de paramètre. Vous pourrez ensuite combiner des sons avec du silence et des fondus progressifs.



