Réparer un fichier .wav trop long ou comment réussir sa captation audio en 24/7

Table des matières

RIFF WAVE est un format de fichier sonore simple à étudier, largement utilisé et bien documenté. Il possède quelques limitations dont une taille maximum de fichier. Certains programmes d’enregistrement ne se formalisent pas de cette contrainte et peuvent générer des fichiers corrompus. Étudions ce cas particulier en examinnt leur entête et essayons de les réparer.

Les signaux de l’espace

En cette semaine du 21 juin 2021, la Station spatiale internationale 🛰️ via son programme radio amateur ARISS 📡 émet en continu une série de 12 photos 🖼️ rendant hommage aux astronautes de l’ISS, Mir et Shuttle. On peut s’amuser à les collectionner à la manière des albums Panini. Les images sont transmises via le protocole SSTV 📺. Chaque transmission dure 2 minutes 🐌, alternée de 2 minutes de pause. Sachant que la station 🛰️ vole à 400km au dessus de nos têtes et à 28000km/h 🚀 (d’ouest en est) on ne reçoit le signal que durant 6 à 8 minutes par passage, et encore uniquement quand les conditions d’élévation et de météo sont satisfaisantes ☀️. On peut anticiper la trajectoire et les passages via des sites comme Heavens Above ou le logiciel gpredict. Le signal est émis sur la fréquence 145.800MHz (± effet doppler), on peut le recevoir avec une clé rtl-sdr et un logiciel de réception / démodulation FM des signaux RF comme GQRX.

Portée du signal ISS via gpredict

Une des méthodes d’enregistrement est de lancer le programme juste avant le passage et de l’arrêter juste après, mais pour celà il faut être présent à l’instant T. Une autre méthode est d’enregistrer le signal en continu, il y aura 99% de bruit blanc mais on est assuré de ne louper aucun passage. On traite l’enregistrement après coup quand on est disponible. La conséquence est que si le programme ne scinde pas l’enregistrement à intervalle régulier, on peut se retrouver avec un énorme fichier .wav de taille potentiellement infinie.

Voici un exemple de réception/enregistrement d’un signal SSTV et de son décodage laissant apparaitre l’image transmise.

Vous avez maintenant le contexte, on a enregistré une longue pige dans un fichier unique et on veut le traiter.

Le problème de la lecture d’un gros fichier

Après une captation nocturne, réalisée avec le logiciel GQRX, je me retrouve avec un fichier .wav 🔈 de 7Go 🐘 .

7111568168 gros-fichier.wav

Je connais ses caractéristiques :

  • stéréo (2 pistes)
  • 16 bits de résolution
  • 48000 Hz de fréquence d’échantillonage

Le format RIFF WAVE est linéaire et sans compression. Après une “entête” de quelques octets, les échantillons sont ajoutés au fur et à mesure en fin de fichier dans le “bloc de données”. Un compteur dans l’entête stockant la taille du bloc de données est alors incrémenté. S’il y a plusieurs pistes, les échantillons sont entrelacés G / D / G / D …

Format simplifié d’un fichier .wav :

| entête | ...données... |

Il est possible de calculer la taille théorique du fichier à partir de sa durée d’enregistrement et ses caractéristiques, et réciproquement. Faisons le calcul pour estimer la durée à partir de la taille du fichier (on néglige la taille de l’entête).

7111568168 # taille du fichier .wav en octets (7Go)
/ 2        # 2 pistes (stéréo)
/ 2        # 16 bits par échantillon (2 octets)
/ 48000    # nombre d'échantillons par seconde
/ 60       # conversion des secondes en minutes
/ 60       # conversion des minutes en heures
= 10.29h

Notre fichier .wav fait donc théoriquement plus de 10h, ce qui correspond à la durée entre laquelle j’ai débuté et terminé l’enregistrement, bref rien d’anormal.

Essayons maintenant d’ouvrir le fichier avec Audacity

Lecture du fichier .wav avec Audacity

Saperlipopette 🤦, le son est coupé après environ 4 heures ! Et évidemment c’était les dernières heures d’enregistrement qui m’intéressaient. vlc, mpv, ffprobe, tous ces outils de confiance me disent la même chose: le fichier fait environ 4 heures. Alors ? Notre enregistrement est définitivement perdu ? Pas si sur 🕵️‍♂️ …

Lançons la commande hexdump -C gros-fichier.wav pour voir si le fichier était réellement vide après 4 heures. Ça ne semble pas le cas. On remarque au passage que les 2 pistes gauche et droite sont rigoureusement identiques. Voici un extrait du dump, il est intéressant car on peut voir que chaque échantillon de 16 bits (ex: 06 4c) est en double. On aurait pu enregistrer en mono plutôt que stéréo pour gagner 50% d’espace et/ou doubler le temps d’enregistrement sans corruption de données…

00165450  06 4c 06 4c fc 16 fc 16  ce 12 ce 12 13 e9 13 e9  |.L.L............|
00165460  17 82 17 82 00 80 00 80  2a 9e 2a 9e 2f ad 2f ad  |........*.*././.|
00165470  92 b9 92 b9 30 c2 30 c2  05 bf 05 bf ab 94 ab 94  |....0.0.........|
00165480  d3 82 d3 82 f9 c0 f9 c0  03 fc 03 fc 0a 0a 0a 0a  |................|
00165490  62 02 62 02 b6 6c b6 6c  ff 7f ff 7f ff 7f ff 7f  |b.b..l.l........|

Je me souviens alors que le champ de l’entête d’un fichier RIFF WAVE stockant la taille en octets du bloc de données est codé sur 32 bits, ce qui signifie qu’on peut y stocker une valeur entre 0 et 4294967295. Le bloc de données ne peut donc dépasser 4Go ! 😨 Je passe mon fichier .wav à la moulinette de mon parser maison qui décode l’entête :

$ WAVEDump.php gros-fichier.wav
array(2) {
  ["FileSize"]=>
  int(2816600872)
  ["RealFileSize"]=>
  int(7111568168)
}

Il y a bien une incohérence. Les 2 valeurs devraient être identiques. L’hypothèse la plus probable est donc bien que gqrx l’enregistreur n’a pas incrémenté le compteur arrivé à un certain stade, ce qui a corrompu le fichier.

Couper / Regénérer les entêtes

La technique de réparation est la suivante:

  • On coupe ✂️ aux ciseaux le fichier en plusieurs morceaux de 2Go
  • On supprime 🧹 l’entête du 1er morceau
  • On regénère les entêtes 🏷 de tous les morceaux

avant le découpage :

| gros-fichier.wav                |
| entête | données...             |

La commande pour couper les fichiers en blocs de 2Go :

split -b 2147483648 gros-fichier.wav decoupe-

Voici les tailles et noms des fichiers après découpage :

2147483648 decoupe-aa
2147483648 decoupe-ab
2147483648 decoupe-ac
 669117224 decoupe-ad

La somme des tailles des 4 fichiers est bien la taille du fichier initial. Par ailleurs si on lance la commande suivante, on va retrouver le même fichier qu’à l’origine :

cat decoupe-aa decoupe-ab decoupe-ac decoupe-ad > gros-fichier-réassemblé.wav

Oui mais la taille du bloc de données étant dans l’entête, il faudra recalculer le compteur de l’entête de decoupe-aa. Les 3 autres fichiers n’étant plus que des données brutes (format dit raw ou pcm), il faudra regénérer leurs entêtes pour les re-rendre valide au sens .wav.

Retirons l’entête de petit-fichier-aa (conversion wav -> pcm) et renommons le fichier en .pcm

ffmpeg -i decoupe-aa -f s16le -acodec pcm_s16le decoupe-aa.pcm

Renommons les nouveaux fichiers en .pcm pour plus de cohérence car ce ne sont plus que des données brutes

mv decoupe-ab decoupe-ab.pcm
mv decoupe-ac decoupe-ac.pcm
mv decoupe-ad decoupe-ad.pcm

Examinons à nouveau la taille des fichiers, on remarque que le 1er fichier ayant perdu son entête a aussi perdu 44 octets, et cela correspond à la taille (minimale) d’une entête RIFF WAVE.

2147483604 decoupe-aa.pcm
2147483648 decoupe-ab.pcm
2147483648 decoupe-ac.pcm
 669117224 decoupe-ad.pcm

Maintenant regénérons toutes les entêtes (conversion pcm -> wav) avec les caractéristiques de l’enregistrement d’origine

ffmpeg -f s16le -ar 48k -ac 2 -i decoupe-aa.pcm decoupe-aa.wav
ffmpeg -f s16le -ar 48k -ac 2 -i decoupe-ab.pcm decoupe-ab.wav
ffmpeg -f s16le -ar 48k -ac 2 -i decoupe-ac.pcm decoupe-ac.wav
ffmpeg -f s16le -ar 48k -ac 2 -i decoupe-ad.pcm decoupe-ad.wav

Nous avons désormais 4 fichiers .wav de 2Go max et parfaitement valides et lisibles partout. Recheckons la cohérence des entêtes :

$ WAVEDump.php decoupe-aa.wav
array(2) {
  ["FileSize"]=>
  int(2147483682)
  ["RealFileSize"]=>
  int(2147483682)
}

$ WAVEDump.php decoupe-ab.wav
array(2) {
  ["FileSize"]=>
  int(2147483726)
  ["RealFileSize"]=>
  int(2147483726)
}

$ WAVEDump.php decoupe-ac.wav
array(2) {
  ["FileSize"]=>
  int(2147483726)
  ["RealFileSize"]=>
  int(2147483726)
}

$ WAVEDump.php decoupe-ad.wav
array(2) {
  ["FileSize"]=>
  int(669117302)
  ["RealFileSize"]=>
  int(669117302)
}

Fin de l’histoire

Avant de mettre votre enregistrement à la poubelle par ce qu’il n’est pas lisible, essayez de comprendre où se situe la panne. Si le format est libre et ouvert (ou au moins documenté), vous trouverez surement des outils pour les analyser.

La conclusion de tout ceci est que mon album panini n’est pas complet même après une semaine de captation, mais que la prochaine fois je serai prêt à capter les piges en continu, et avec découpage horaire des fichiers captés 😎.

Les presque 12 images de la série 18

Ressources

comments powered by Disqus