Il y a environ un an, j'ai travaillé avec des fichiers WAV générés, il y en avait plusieurs milliers. J'ai essayé de les marquer, de les trier dans des dossiers, de créer des métadonnées. Dans la foulée, j'ai écouté quelques morceaux, et, à mon grand regret, il s'est avéré qu'ils commencent tous par un assez long silence. C'était très ennuyeux, surtout lorsque vous écoutez une série de fichiers d'affilée et que vous trébuchez constamment sur des pauses avant de lire chacun d'entre eux. Génial, ce qui signifie que vous devez également faire quelque chose à ce sujet.
J'avais déjà passé du temps à chercher des solutions pour supprimer le silence des fichiers lorsqu'il m'est soudain venu à l'esprit : c'est WAV ! Les données des fichiers WAV sont généralement audio PCM, c'est-à-dire que chaque valeur du fichier spécifie l'amplitude du son à un moment donné. Par conséquent, si nous avons vraiment un silence complet là-bas, et non du bruit blanc, alors des zéros solides devraient correspondre à ce silence dans le fichier, non ?
$ xxd testfile1.wav | head -n 100
00000000: 5249 4646 64b9 0e00 5741 5645 666d 7420 RIFFd...WAVEfmt
00000010: 1000 0000 0100 0200 44ac 0000 10b1 0200 ........D.......
00000020: 0400 1000 6461 7461 40b9 0e00 0000 0000 ....data@.......
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# ... and a lot more zeros below
Et voici. Eh bien, cela signifie que c'est plus facile qu'il n'y paraît. Il suffit de lire les fichiers, de trouver l'endroit où se terminent ces zéros et de supprimer le fragment correspondant.
Comment les fichiers WAV sont lus
Tout d'abord, je devais me familiariser avec le format WAV afin de comprendre comment travailler avec de tels fichiers et gérer les données qu'ils contiennent. J'ai rassemblé plusieurs sources ; l'une des plus utiles s'est avérée être l' ancienne page de stanford.edu (le site n'est plus disponible, mais, heureusement, il a survécu sur la Wayback Machine). Il y avait un schéma très clair :
Ainsi, la structure du fichier WAV semble être assez simple : d'abord, un en-tête de 44 octets, puis les données réelles. Avec ces informations, il était déjà possible de démarrer le code. Il suffisait de sauter les 44 premiers octets, de supprimer la séquence de zéros au début de la section de données et d'envoyer tout le reste pour la lecture dans sa forme originale. Bien que je ne puisse m'empêcher d'ajouter que dans une autre source, je suis tombé sur les informations suivantes :
« Certains programmes supposent (et c'est très naïf de leur part) que le préambule dans l'en-tête est toujours exactement de 44 octets (comme indiqué dans le tableau ci-dessus) et que le reste du fichier ne contient que des données audio. Il n'est pas prudent de faire de telles suppositions."
Eh bien, j'ai décidé que tout allait bien : j'ai écrit le programme en C, donc il n'y avait pas besoin de trop s'inquiéter de la sécurité.
Le code
Le code était simple, en moins d'une centaine de lignes. En fait, il a parcouru tout le fichier octet par octet, à l'exception des quarante-quatre premiers, et a compté les zéros consécutifs. Dès qu'il rencontrait quelque chose qui n'était pas nul, le programme s'arrêtait, enregistrait l'index approprié et reprenait la lecture du fichier depuis le début. Cette fois, il a ignoré tout ce qui précède l'index (sans compter l'en-tête) et a sorti tous les autres octets de la manière standard.
Inutile de citer le code en entier, mais voici la partie qui va nous intéresser :
// index was calculated above to be the index of
// the last consecutive zero byte
FILE *f = fopen(argv[1], "rb");
int ind = 0;
int current_byte;
while ((current_byte = fgetc(f)) != EOF) {
if (ind < 44 || ind >= index) {
fputc(current_byte, stdout);
}
ind += 1;
}
fclose(f);
Tout est cool, tout est simple. Il est temps de tester. J'ai exécuté le programme sur l'un des fichiers avec une pause particulièrement longue.
./strip_audio testfile1.wav > testfile1.nosilence.wav
Vérifié ce que xxd produit pour testfile1.nosilence.wav. Super, pas de zéros non significatifs. Donc ça a marché. Pour être sûr, je vais rapidement ouvrir le fichier dans mon lecteur audio.
Source
Immédiatement, le bruit statique le plus puissant que j'ai entendu dans ma vie m'a frappé dans les oreilles. J'ai failli tomber de la chaise et j'ai désespérément essayé de retirer mes écouteurs. Je me souviens que c'était au milieu de la nuit, et le chien est venu en courant pour vérifier ce qui n'allait pas chez moi.
Où est-ce que je me suis trompé?
Mes oreilles bourdonnaient toujours, et je me suis assis et j'ai essayé de comprendre mes décisions irréfléchies.
- Erreur numéro 1 : il a fallu baisser le son.
- Erreur n°2 : vous n'auriez pas dû porter d'écouteurs.
- Erreur n°3 : unité non enregistrée.
Avez-vous remarqué la troisième erreur dans le code que j'ai donné ci-dessus? Astuce : regardez le commentaire. J'ai calculé l'index variable comme l'index du dernier octet représentant des zéros. Cela signifie que, moins 44 octets de l'en-tête, nous ne reproduisons désormais que ce qui suit ou chevauche l'index. index est au dernier zéro de la série, c'est-à-dire que nous incluons un octet zéro supplémentaire dans la section de données.
Cela peut être corrigé comme suit :
// replaced >= with just >
if (ind < 44 || ind > index) {
fputc(current_byte, stdout);
}
Maintenant, il n'y a plus de zéros supplémentaires dans la sortie, et si vous lisez le fichier, rien de grave ne se produira. J'ai tout réparé... Mais arrête.
Dans les fichiers WAV, nous avons de l'audio PCM, et les zéros dans ce type de données audio correspondent à un silence complet. Alors cet octet supplémentaire ne devrait-il pas être complètement silencieux ? Pourquoi était-ce si bruyant et si statique ?
Commençons par comparer un fichier audio normal avec le monstre que j'ai créé avec Audacity :
Devinez où est le monstre ? Oui, c'est celui dont l'amplitude est stable presque au maximum. Pourquoi donc?
Comment les échantillons audio sont lus
Je suis retourné aux sources que j'avais sélectionnées et j'ai essayé de comprendre comment une erreur d'une unité pouvait conduire à une telle explosion d'amplitude. Je savais que dans mes fichiers l'échantillon contenait 16 bits, et qu'il y avait deux canaux (stéréo), alors j'ai commencé à chercher les informations appropriées. Voici ce que j'ai dit dans la section sur l'audio PCM stéréo 16 bits :
« Chaque échantillon est contenu dans un entier i, qui représente le nombre minimum d'octets suffisant pour stocker une taille d'échantillon donnée. L'octet le moins significatif est placé en premier dans le magasin. "
"Le nombre minimum d'octets suffisant pour stocker une taille donnée" - la formulation prête inutilement à confusion. i correspond au nombre de bits contenus dans l'échantillon. Dans notre cas, il y en a seize. En conséquence, si nous avons une certaine valeur d'une longueur de 16 bits, bien sûr, elle sera stockée sur deux octets. Et puis un point important : le moins significatif des octets se trouve en premier dans le stockage. C'est ici.
Jetez un œil au graphique que j'ai fait pour montrer ce qui a causé un signal si fort :
La partie supérieure montre mon fichier monstre, dans lequel j'ai accidentellement laissé un octet supplémentaire avec des zéros. Chacun des trois échantillons - s1, s2 et s3 - contient deux octets, et le second est plus significatif. Par conséquent, lors de la conversion de ces paires d'octets en décimal, nous obtenons une amplitude très élevée.
Dans le même temps, en bas, vous pouvez voir que si vous supprimez l'octet zéro, les échantillons sont lus comme il se doit et les valeurs du fichier audio sont dans des limites raisonnables.
Il s'avère que si j'avais de l'audio 8 bits, l'octet supplémentaire manquant ne poserait aucun problème. Mais c'était 16 bits, et par conséquent, j'ai décalé toute la séquence en échantillons, de sorte que l'octet le moins significatif soit lu comme le plus significatif.
conclusions
- Vérifiez l'onde sonore d'un fichier audio avant de le lire au volume maximum
- ( )
- ,