Skip to content

Commit 8674d54

Browse files
authored
Add files via upload
1 parent 79316b1 commit 8674d54

12 files changed

Lines changed: 248 additions & 0 deletions

‎docs/wiki/Waveform-wiki.md‎

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
Audio data is often represented by a waveform image. This guide explains how to
2+
easily create such an image or video.
3+
4+
= Waveform image =
5+
6+
The [https://ffmpeg.org/ffmpeg-filters.html#showwavespic showwavespic] filter is the easiest method to create a waveform image.
7+
8+
== All channels ==
9+
10+
[[Image(showwavespic.png)]]
11+
12+
{{{
13+
ffmpeg -i input -filter_complex "showwavespic=s=640x120" -frames:v 1 output.png
14+
}}}
15+
16+
All channels will be represented by various shades in the waveform.
17+
18+
== Downmixed ==
19+
20+
[[Image(showwavespic_mono.png)]]
21+
22+
If you want a simpler representation where all channels are represented by one waveform you can downmix your audio to mono first with [https://ffmpeg.org/ffmpeg-filters.html#aformat aformat]:
23+
24+
{{{
25+
ffmpeg -i input -filter_complex "aformat=channel_layouts=mono,showwavespic=s=640x120" -frames:v 1 output.png
26+
}}}
27+
28+
== Separate channels ==
29+
30+
[[Image(showwavespic_split.png)]]
31+
32+
If you want to split them into separate channels:
33+
34+
{{{
35+
ffmpeg -i input -filter_complex "showwavespic=s=640x240:split_channels=1" -frames:v 1 output.png
36+
}}}
37+
38+
== Changing range ==
39+
40+
[[Image(showwavespic_compand.png)]]
41+
42+
If the waveform looks a little flat you can use the [https://ffmpeg.org/ffmpeg-filters.html#compand compand] filter to expand or compress the dynamic range:
43+
44+
{{{
45+
ffmpeg -i input -filter_complex "compand,showwavespic=s=640x120" -frames:v 1 output.png
46+
}}}
47+
48+
Note that this won't be as accurate of a representation as it would without compand, but for aesthetics it may be preferred.
49+
50+
== Adding a background ==
51+
52+
[[Image(showwavespic_bg.png)]]
53+
54+
Using [https://ffmpeg.org/ffmpeg-filters.html#overlay overlay]:
55+
56+
{{{
57+
ffmpeg -i input -i background.png -filter_complex "[0:a]showwavespic=s=640x240[fg];[1:v][fg]overlay=format=auto" -frames:v 1 output.png
58+
}}}
59+
60+
* This example assumes the background is the same width and height as the waveform. If it is not, you can [https://ffmpeg.org/ffmpeg-filters.html#scale scale], [https://ffmpeg.org/ffmpeg-filters.html#crop crop], or [https://ffmpeg.org/ffmpeg-filters.html#pad pad] the background first.
61+
62+
* The waveform color can be modified with the `colors` option in showwavespic.
63+
64+
=== Add a solid background color ===
65+
66+
[[Image(showwavespic_bg_solid.png)]]
67+
68+
{{{
69+
ffmpeg -i in.flac -f lavfi -i color=c=black:s=640x320 -filter_complex \
70+
"[0:a]showwavespic=s=640x320:colors=white[fg];[1:v][fg]overlay=format=auto" \
71+
-frames:v 1 out.png
72+
}}}
73+
74+
== Audacity-like look ==
75+
76+
[[Image(showwavespic_like_audacity.png)]]
77+
78+
{{{
79+
size="640x240"
80+
bg_col="#c0c0c0"
81+
peak_col="#3232c8"
82+
rms_col="#6464dc"
83+
84+
ffmpeg -i input.wav -f lavfi -i "color=c=${bg_col}:s=${size}" -filter_complex " \
85+
[0:a] showwavespic=s=${size}:split_channels=1:colors=${peak_col}:filter=peak [pk]; \
86+
[0:a] showwavespic=s=${size}:split_channels=1:colors=${rms_col} [rms], \
87+
[pk] [rms] overlay=format=auto [nobg], [1:v] [nobg] overlay=format=auto" \
88+
-frames:v 1 -update true output.png
89+
}}}
90+
91+
= Waveform video =
92+
93+
[[Image(showwaves.png)]]
94+
95+
The [https://ffmpeg.org/ffmpeg-filters.html#showwaves showwaves] filter makes a video waveform.
96+
97+
{{{
98+
ffmpeg -i input -filter_complex "[0:a]showwaves=s=1280x720:mode=line,format=yuv420p[v]" -map "[v]" -map 0:a -c:v libx264 -c:a copy output.mkv
99+
}}}
100+
101+
----
102+
103+
= Using Gnuplot =
104+
105+
== Single Channel ==
106+
107+
Plotting a single channel waveform is easier as the data can be passed to
108+
Gnuplot directly. The basic idea here is to generate a specific binary format
109+
for the audio data which then can be read and interpreted automatically with
110+
Gnuplot.
111+
112+
The following command will generate a stream of raw binary data with two bytes
113+
representing each sample. The input format does not matter as long as there is
114+
an audio stream. If it is a multi-channel audio stream, the channels will be
115+
mixed into one.
116+
117+
{{{
118+
ffmpeg -i in.mkv -ac 1 -map 0:a -c:a pcm_s16le -f data -
119+
}}}
120+
121+
Since audio usually comes with a lot of samples per second (e.g. 44100 samples
122+
per second for CD audio) it is usually a good idea to reduce the amount of data
123+
to increase speed and decrease memory consumption. Note that reducing the
124+
samplerate too much might distort the generated waveform. A good value to use
125+
is usually 8000 samples per second. The modified command line to do that would
126+
look like this:
127+
128+
{{{
129+
ffmpeg -i "$1" -ac 1 -filter:a aresample=8000 -map 0:a -c:a pcm_s16le -f data -
130+
}}}
131+
132+
Now what is left is the letting Gnuplot create the waveform image from these
133+
data. For that we need a plot command that deals with the output from FFmpeg.
134+
135+
{{{
136+
plot '<cat' binary filetype=bin format='%int16' endian=little array=1:0 with lines;
137+
}}}
138+
139+
This plot command reads from stdin where it expects a one dimensional array of
140+
two byte, little endian, signed integer representing the pcm vales. These are
141+
then plotted with lines.
142+
143+
We can already try this out by combining those commands. Note that the live
144+
rendering using the graphical user interface of Gnuplot is rather slow so you
145+
don't want to throw too many data at it. Use a short audio file or limit the
146+
duration using the '-t' option to plot only a part of the file.
147+
148+
{{{
149+
ffmpeg -i in.wav -ac 1 -filter:a aresample=8000 -map 0:a -c:a pcm_s16le -f data - | \
150+
gnuplot -p -e "plot '<cat' binary filetype=bin format='%int16' endian=little array=1:0 with lines;"
151+
}}}
152+
153+
This should result in something like this:
154+
155+
[[Image(gnuplot_window.png)]]
156+
157+
Here we already got the waveform, but usually we don't need axis, labels,
158+
scales etc. Also the aspect ratio is not optimal. It should rather be something
159+
like '1:10'. Hence we need to extend the plot command.
160+
161+
{{{
162+
set terminal png size 5000,500;
163+
set output 'waveform.png';
164+
165+
unset key;
166+
unset tics;
167+
unset border;
168+
set lmargin 0;
169+
set rmargin 0;
170+
set tmargin 0;
171+
set bmargin 0;
172+
173+
plot '<cat' binary filetype=bin format='%int16' endian=little array=1:0 with lines;
174+
}}}
175+
176+
This will make Gnuplot generate a PNG image with the dimension of 5000x500
177+
pixel as output and store it in a file named 'waveform.png'. It also removes all
178+
labels, axis and other non-data from the image and set all margins to zero.
179+
180+
All this can still be specified using the command line, but it is much more
181+
convenient to put all the plot commands in a separate file and pass that one to
182+
Gnuplot. Assuming the plot commands are stored in 'waveform.gnuplot', a valid
183+
command line to generate a waveform image would then be:
184+
185+
{{{
186+
ffmpeg -i in.mp3 -ac 1 -filter:a aresample=8000 -map 0:a -c:a pcm_s16le -f data - | \
187+
gnuplot waveform.gnuplot
188+
}}}
189+
190+
The result should then look somewhat like this.
191+
192+
[[Image(ac1.png)]]
193+
194+
195+
== Multiple Channels ==
196+
197+
While the basic idea and the command for plotting multiple channels remain the
198+
same, we cannot simply pipe the data into Gnuplot since the channels have to be
199+
plotted separately. Thus we first use FFmpeg to extract the data for all
200+
channels and then plot the data in a second step.
201+
202+
An FFmpeg command line to extract the audio channel data into separate files,
203+
prepared for Gnuplot could look like this:
204+
205+
{{{
206+
ffmpeg -i in.mp4 -ac 2 -filter_complex:a '[0:a]aresample=8000,asplit[l][r]' \
207+
-map '[l]' -c:a pcm_s16le -f data /tmp/plot-waveform-ac1 \
208+
-map '[r]' -c:a pcm_s16le -f data /tmp/plot-waveform-ac2
209+
}}}
210+
211+
This would again downsample the data, but then split the audio channels into
212+
separate streams and store them in two files. One for each channel.
213+
214+
A plot command for this would then look like this:
215+
216+
{{{
217+
set terminal png size 5000,1000;
218+
set output 'waveform.png';
219+
220+
unset key;
221+
unset tics;
222+
unset border;
223+
set lmargin 0;
224+
set rmargin 0;
225+
set tmargin 0;
226+
set bmargin 0;
227+
228+
set multiplot layout 2,1;
229+
plot '/tmp/plot-waveform-ac1' binary filetype=bin format='%int16' endian=little array=1:0 with lines;
230+
plot '/tmp/plot-waveform-ac2' binary filetype=bin format='%int16' endian=little array=1:0 with lines;
231+
unset multiplot;
232+
}}}
233+
234+
The resulting image would then look like this:
235+
236+
[[Image(ac2.png)]]
237+
238+
239+
== Additional Hints ==
240+
241+
Sometimes there are a few loud pitches while the rest of the data is relatively
242+
quiet. These pitches would cause the rest of the data to scale down which might
243+
be unwanted. To make the y-axis centered and cut off peaks, add the following
244+
line (adjust the values) to the plot command:
245+
246+
{{{
247+
set yrange [-600:600];
248+
}}}

‎docs/wiki/ac1.png‎

32.5 KB
Loading

‎docs/wiki/ac2.png‎

66.5 KB
Loading

‎docs/wiki/gnuplot_window.png‎

9.07 KB
Loading

‎docs/wiki/showwaves.png‎

9.37 KB
Loading

‎docs/wiki/showwavespic.png‎

2.05 KB
Loading

‎docs/wiki/showwavespic_bg.png‎

11.1 KB
Loading
6.14 KB
Loading

‎docs/wiki/showwavespic_compand.png‎

5.34 KB
Loading
5.3 KB
Loading

0 commit comments

Comments
 (0)