Vidéo sur vinyle : essai
Si l’on considère qu’un vinyl sait monter à 22 kHz en stéréo, on peut déduire qu’on peut envoyer un fichier numérique en 44 kbps, en mettant 1 bit par signal, soit 2 en stéréo. Et si l’on pousse un peu plus loin, en en mettant 2 par signal (soit 4 valeurs possibles), on peut monter à 88 kbps…
Étape 1 : encodage de la vidéo
De ce fait, voici un script pour convertir une vidéo en son mono et en débit souhaité (largeur : 320px) :
ffmpeg
-i
input.mp4
-c:v
libx264
-b:v
66150
-b:a
22050
-ac
1
-maxrate
88200
-bufsize
88200
-vf
scale=320:-1 output_88k.mp4
Voici un exemple de qualité de vidéo avec la commande ci-dessus :
Une amélioration notable de la qualité, en utilisant le codec AV1 (après recomplilation de FFMpeg), beaucoup plus long à l’encodage (je passe de 2 minutes à presque deux heures) :
ffmpeg -i input.mp4 -c:v libaom-av1 -strict -2 -b:v 66150 -b:a 22050 -ac 1 -maxrate 88200 -bufsize 88200 -vf scale=640:-1 output_88k_av1.mp4
Et voici la requête de conversion la plus optimisée, notamment pour les films à hauts contrastes lumineux , en 2 passes (limitée à 30 secondes, ce qui prend déjà 25 minutes de temps de conversion sur mon i5) :
ffmpeg -i input.mp4 \
-pass 1 \
-ss 00:01:00 -t 00:00:30 \
-c:v libaom-av1 -strict -2 -b:v 60k \
-c:a libfdk_aac -ar 22050 -b:a 22050 -ac 2 \
-minrate 45k -maxrate 75k -bufsize 33075 \
-vf scale=-1:360 -movflags +faststart -threads 1 -f mp4 \
-y /dev/null && _
ffmpeg -i input.mp4 \
-pass 2 \
-ss 00:01:00 -t 00:00:30 \
-c:v libaom-av1 -strict -2 -b:v 60k \
-c:a libfdk_aac -ar 22050 -b:a 22050 -ac 2 \
-minrate 45k -maxrate 75k -bufsize 33075 \
-vf scale=-1:360 -movflags +faststart -threads 1 -f mp4 \
-y output.mp4
On se rend compte des limites sur les scènes d’action, même en baissant les paramètres (et encore, on est à plus de 100 kbps…) ; cette solution n’est dont pas adaptée aux amoureux du Marvel Cinematic Universe !
Étape 2 : transcription audio
Ensuite, pour convertir le code binaire en son WAV pour la gravure sur vinyle, on s’inspire de la structure d’un WAV (Wikipedia) et on ajoute les données. C’est faisable en PHP, à condition d’allouer un max de mémoire dans php.ini.
J’ai choisi de ne pas avoir de valeur de son à 0, afin de distinguer le manque de signal (début de piste) au signal lui-même. J’ai donc, sur chaque piste, 4 valeurs d’intensité sonore :
- +50% = valeur binaire 00
- +100% = valeur binaire 01
- -50% = valeur binaire 10
- -100% = valeur binaire 11
Il me faut donc 2 cycles de lecture, sur les 2 pistes, pour obtenir un octet (8 valeurs binaires). Ce qui fait 11025 octets/secondes, soit 88200 bps, ou 88 kbps.
Problème possiblement lié au signal
Divers essais ont montré que le signal généré est davantage triangulaire que rectangulaire, ce qui pour sa relecture risque d’être un casse-tête.
Le palliatif :
- Changer la base de 11025 à 12000 Hz, ce qui permet une vidéo à 96kbps au lieu de 88kbps
- Multiplier l’enregistrement d’une donnée par 4
- Sauver le fichier audio à 96000 Hz au lieu de 22050 Hz
Problème lié à la stéréophonie
Si l’on inverse la gauche et la droite (simplement en inversant le branchement des RCA), les données ne peuvent plus être proprement décodées. Un palliatif possible serait d’avoir le signal du bit 1 en positif sur un des canaux, et du bit 2 en négatif sur l’autre. Est-ce que le graveur de vinyle saura gérer ?
Étape 3 : décodage audio en vidéo
Enfin, lors de la lecture du vinyle, il faut décoder en 3 étapes :
- Transformer l’audio en buffer vidéo
- Créer un composant de capture (style webcam virtuelle) qui val lire ce buffer et transmettre le flux
- Lire le flux du composant virtuel dans VLC via la capture d’un flux
Je pense que je travaillerai sur un système de lecture audio haute fréquence (192 kHz), pour déterminer la meilleure valeur depuis la transcription analogique. Je pars du postulat que ce n’est pas parce que j’enregistre un signal carré que j’aurai un signal carré sur le disque.
Pour mes tests, je lis le fichier « binaire » depuis mon ordinateur, et du même ordinateur, par un câblé jack-jack, j’écoute le son joué.
arecord
Une solution serait de créer un script sur la base du programme arecord de ALSA Audio, qui permet un enregistrement en continu :
arecord -t raw -r 192000 --max-file-time 1 -c 2 --use-strftime /tmp/%Y%m%d%H%M%S%v.raw
Cela crée un fichier brut d’1 seconde de capture audio.
La comparaison entre la source et l’enregistrement fait apparaître 2 points importants :
- L’intensité des courbes est inversée, mais la stéréophonie est respectée
- Au bout d’un moment, le son ne reste plus sur un des côtés du neutre, et je perds la spécificité de lecture (si je prends un vinyle depuis le milieu, je ne sais pas quelle sera la stéréophonie)…
En refaisant le test avec un signal de part et d’autre, on se rend compte qu’il y a une tendance à chercher un point d’équilibre qui fausse l’interprétation, et je ne sais pas encore si ce problème est au niveau de la lecture du son ou de son enregistrement, ni si je rencontrerai un problème similaire depuis un vinyle…
Enfin, en alternant un bit positif et un bit négatif, j’obtiens une cohérence bien meilleure… Je dois déterminer, étant donné qu’un octet est divisé en 4, quelle onde correspond au début de l’octet, chose qui sera facilitée par l’alternance d’un « duet » montant et d’un « duet » descendant.
On pourra imaginer un bouton Switch stéréo sur le préampli-décodeur.
Autre approche : Photosounder
Photosounder, comme son nom l’indique, transforme les images en son. L’idée est de créer un fichier son où on associe un bit à une fréquence donnée. On génère ainsi un son comportant un nombre fini de fréquences, que l’on a à relire à intervalles réguliers.
On se rend rapidement compte que, même si l’on exploite un large spectre de fréquences, on ne peut décemment pas stocker énormément de données par seconde (si l’on part sur 2400 pixels/seconde, et 32 fréquences entre 2 et 10 kHz, on obtient 76kbps, soit 1.3x moins d’informations que le minimum)…
Buffer vidéo
Ensuite, on peut lire le fichier correspondant au temps t-1 seconde, pour décrypter son contenu et l’inclure dans un buffer vidéo.
Extraction des données binaires
Afin d’assurer le décryptage, et en tenant compte que tous les tourne-disques ne tournent pas précisément à la même vitesse, il me faut déterminer la fréquence porteuse du signal :
sox arecord.wav -r 30k -n rate spectrogram
Autre approche, indépendante de la fréquence, déterminer les valeurs des seuils (points rouges) :
Note importante : le son nouvellement enregistré est en 8 bits non signés (valeurs de 0 à 255) et non en 8 bits signés (valeurs de -128 à 127). L’algorithme de test en PHP semble bien retranscrire en binaire les données analogiques. L’idéal serait de créer un interpréteur compilé appelable de la sorte :
wav2binary /tmp/201910051437300001.wav /tmp/binarybuffer
Lecture par un lecteur vidéo
On pourrait exploiter la fonction SMEM (Stream to Memory) de VLC, pour injecter les données binaires directement extraites.
Réalisation technique
Interfaces externes :
- Entrée : 2 RCA mono
- Switch : pré-ampli ou non
- Switch : audio ou vidéo
- Sortie : 2 RCA mono pour la sortie pur audio
- Switch d’inversion de signal (inverser le positif du négatif)
- Switch d’inversion de la stéréophonie
- Sortie : HDMI pour la vidéo
- Alimentation : 5V / 12V / 220V (à voir selon le pré-ampli)
- Prise terre pour le pré-ampli
Composants internes :
- Pré-ampli, alimenté en 5V, 12V ou 220V
- Raspberry Pi, voire Zero
- Vu-mètres stéréo (optionnel)
- Adaptateur mini-HDMI vers HDMI à intégrer
- Adaptateur alimentation Raspberry Pi
- Fils électriques
- Transformateur de modulation pour jeux de lumière (inversion de polarité) ou transfo micro adaptateur d’impédance
Schéma de principe prévu :