Générer une vidéo du spectre sonore avec ffmpeg

Comment générer une vidéo du spectre sonore d'un fichier de façon un peu sympa avec ffmpeg ? Voici une méthode.

J’ai toujours trouvé les possibilités natives de ffmpeg pour générer des formes d’onde biens mais pas top. J’avais en tête un type de forme que je vois souvent à la télévision ou dans des podcasts, mais qui n’était pas proposé. Tous les exemples que j’ai trouvé utilisaient After Effects ou Premiere. Je n’ai ni les compétences ni les licences d’utilisation de ces logiciels propriétaires. Finalement, avec un peu de recherches j’ai réussi à reproduire globalement ce que je voulais, et même un peu plus.

L’effet désiré :

l’effet spectre audio

Avec ffmpeg, on peut réaliser un effet proche avec le filtre vidéo showfreqs, mais il ne présente que la partie supérieure :

l’effet showfreqs de ffmpeg

L’astuce est de simplement dupliquer ce calque, en lui appliquant un vflip (miroir vertical) puis une translation.

C’est là qu’on rentre dans l’utilisation des filtres complexes de composition avec ffmpeg et l’outil FFmpeg explorer peut aider à mieux appréhender le chaînage des traitements, ce de façon graphique.

Pour résumer, on crop, on duplique le calque, on translate, on ajoute un fond coloré et on exporte le tout.

En ajustant quelques paramètres (mode, averaging, ascale, fscale), on peut rendre l’effet plus joli. cf. la doc du filtre showfreqs

On peut aussi ne pas générer de couleur de fond, mais le laisser transparent (canal alpha). Pour celà, on n’utilise plus l’espace de couleur classique yuv420p mais le yuva444p10le. D’autre part, comme l’idée est d’utiliser pour un montage, on choisit comme codec intermédiaire de montage Apple ProRes plutôt qu’un format de diffusion h264 (attention le débit n'est pas le même). À noter que seul le conteneur mov est compatible avec le codec ProRes, pas le conteneur mp4.

Final Cut Pro que j’utilise pour mes montages un peu plus complexes est bien sûr compatible avec le ProRes et les vidéos avec couche alpha. L’avantage c’est qu'on va utiliser un effet de colorisation intégré pour choisir complètement la couleur d’affichage de notre forme d’onde générée. Le prérequis étant que la couleur de premier plan de la forme d’onde soit le blanc.

le choix de la couleur de la forme d'onde dans Final Cut Pro

Le rendu d'une vidéo

codec h264 / colorimétrie yuv420p

codec ProRes / colorimétrie yuva444p10le

Le script

Voici le script complet ffmpeg-waveform.sh, commenté, encore à debuguer et à optimiser ;)

 1#!/usr/bin/env bash
 2
 3##
 4# Génération d'une forme d'onde audio spectrale avec ffmpeg
 5#
 6#      |
 7#    | |  | || | 
 8# ---|||||||||||||--
 9#    | |  | || |
10#      |
11#
12# Testé uniquement testé avec des fichiers stéréo
13#
14# Usage: ffmpeg-waveform.sh filename.mp3
15# génère un fichier filename.mp3.wf.mov
16#
17# TODO variabiliser la taille (largeur x hauteur)
18##
19
20if [[ -z "$1" ]]; then
21  echo "Usage: ffmpeg-waveform.sh filename.mp3"
22  exit 1
23fi
24
25INPUT=$1
26OUTPUT="$1.wf.mov"
27
28# soit on exporte avec un fond coloré, et une couleur d'onde, avec le codec h264 (format final de diffusion)
29BGCOLOR="0x292933@1.0"
30FGCOLOR="yellow"
31VCODEC="-c:v libx264 -pix_fmt yuv420p -b:v 5000k"
32
33# soit on exporte avec couche alpha, onde de couleur blanche, avec le codec prores (format de montage)
34#BGCOLOR="0x000000@0.0"
35#FGCOLOR="white"
36#VCODEC="-c:v prores_ks -pix_fmt yuva444p10le -alpha_bits 16 -profile:v 4444"
37
38# Bug: bloqué à 25fps, génère des frames dupliquées sinon...
39FPS=25
40
41# le routage :
42# input --> [0:a] --> showfreqs --> [sf] --> crop --> [sfc] --> split --> [sfc1] --> vflip --> [sfc1v]
43#                                                                     --> [sfc2]
44
45# note, j'apprends l'existence du filtre vstack qui permet de superposer 2 vidéos et qui éviterait un filtre de translation/overlay
46
47# ajout d'un masque à ajouter en dernier input (ajouter une ligne après -f lavfi ... et avant -filter_complex)
48# cf. chaptire suivant "Ajouter un masque de type bargraph
49# -i "mask.png"
50
51ffmpeg -y \
52    -i "${INPUT}" \
53    -f lavfi -i color=c=${BGCOLOR}:s=1280x720,format=rgba \
54    -filter_complex "[0:a]showfreqs=size=1280x720:rate=${FPS}:mode=bar:averaging=1:ascale=sqrt:fscale=log:colors=${FGCOLOR}|${FGCOLOR}[sf]; \
55    [sf]crop=1280:360:0:360[sfc]; \
56    [sfc]split[sfc1][sfc2]; \
57    [sfc1]vflip[sfc1v]; \
58    [1][sfc1v]overlay=x=0:y=360[o1]; \
59    [o1][sfc2]overlay=x=0:y=0[o2];" \
60    -map "0:a" \
61    -map "[o2]" \
62    -r ${FPS} \
63    -shortest \
64    ${VCODEC} \
65    -c:a copy \
66    "${OUTPUT}"

Bonus: ajouter un masque de type bargraph

Dans l’exemple initial, on voit des bandes verticales, tel un bargraph. J’ai essayé de reproduire ça avec un masque que l’on génère de la façon suivante :

1genmask.php > mask.png

Le script genmask.php :

 1#!/usr/bin/env php
 2<?php
 3
 4const WIDTH = 1280;
 5const HEIGHT = 720;
 6const GUTTER = 8;
 7
 8$im = imagecreatetruecolor(WIDTH, HEIGHT);
 9imagecolortransparent($im, imagecolorallocate($im, 0, 0, 0));
10
11for ($x = 0; $x < WIDTH; $x += GUTTER * 2) {
12    imagefilledrectangle($im, $x, 0, $x + GUTTER - 1, HEIGHT - 1, imagecolorallocate($im, 255, 255, 255));
13}
14
15imagepng($im);

L’image représente une succession de lignes verticales de largeur 8 pixels alternativement blanches et transparentes.

Ressources

comments powered by Disqus