Generating a waveform using ffmpeg
Default waveform
ffmpeg -i input.wav -filter_complex showwavespic -frames:v 1 output.png
Notes
-
Notice the segment of silent audio in the middle (see "Fancy waveform" below if you want to see how to add a line).
-
The background is transparent.
-
Default colors are red (left channel) and green (right channel) for a stereo input. The color is mixed where the channels overlap.
-
You can change the channel colors with the
colors
option, such as"showwavespic=colors=blue|yellow"
. See a list of valid color names or use hexadecimal notation, such as#ffcc99
. -
See the showwavespic filter documentation for additional options.
-
If you want a video instead of an image use the showwaves filter.
Fancy waveform
ffmpeg -i input.mp4 -filter_complex \
"[0:a]aformat=channel_layouts=mono, \
compand=gain=-6, \
showwavespic=s=600x120:colors=#9cf42f[fg]; \
color=s=600x120:color=#44582c, \
drawgrid=width=iw/10:height=ih/5:color=#[email protected][bg]; \
[bg][fg]overlay=format=auto,drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=1:color=#9cf42f" \
-frames:v 1 output.png
Explanation of options
-
aformat downsamples the audio to mono. Otherwise, by default, a stereo input would result in a waveform with a different color for each channel (see Default waveform example above).
-
compand modifies the dynamic range of the audio to make the waveform look less flat. It makes a less accurate representation of the actual audio, but can be more visually appealing for some inputs.
-
showwavespic makes the actual waveform.
-
color source filter is used to make a colored background that is the same size as the waveform.
-
drawgrid adds a grid over the background. The grid does not represent anything, but is just for looks. The grid color is the same as the waveform color (
#9cf42f
), but opacity is set to 10% (@0.1
). -
overlay will place
[bg]
(what I named the filtergraph for the background) behind[fg]
(the waveform). -
Finally, drawbox will make the horizontal line so any silent areas are not blank.
Gradient example
Using gradients filter:
ffmpeg -i input.mp3 -filter_complex "gradients=s=1920x1080:c0=000000:c1=434343:x0=0:x1=0:y0=0:y1=1080,drawbox=x=(iw-w)/2:y=(ih-h)/2:w=iw:h=1:color=#0000ff[bg];[0:a]aformat=channel_layouts=mono,showwavespic=s=1920x1080:colors=#0068ff[fg];[bg][fg]overlay=format=auto" -vframes:v 1 output.png
Color background
ffmpeg -i input.opus -filter_complex "color=c=blue[color];aformat=channel_layouts=mono,showwavespic=s=1280x720:colors=white[wave];[color][wave]scale2ref[bg][fg];[bg][fg]overlay=format=auto" -frames:v 1 output.png
The scale2ref filter automatically makes the background the same size as the waveform.
Image background
Of course you can use an image or video instead for the background:
ffmpeg -i audio.flac -i background.jpg -filter_complex \
"[1:v]scale=600:-1,crop=iw:120[bg]; \
[0:a]showwavespic=s=600x120:colors=cyan|aqua[fg]; \
[bg][fg]overlay=format=auto" \
-q:v 3 showwavespic_bg.jpg
Getting waveform stats and data
Use the astats filter. Many stats are available: RMS, peak, min, max, difference, etc.
RMS level per audio frame
Example to get standard RMS level measured in dBFS per audio frame:
ffprobe -v error -f lavfi -i "amovie=input.wav,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of csv=p=0 > rms.log
Peak level per second
Add the asetnsamples filter.
ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.Peak_level -of csv=p=0
Same as above but with timestamps
ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame=pkt_pts_time:frame_tags=lavfi.astats.Overall.Peak_level -of csv=p=0
Output to file
Just append > output.log
to the end of your command:
ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of csv=p=0 > output.log
JSON
ffprobe -v error -f lavfi -i "amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1" -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of json > output.json
krisph
Updated on June 09, 2022Comments
-
krisph about 2 years
I am trying to generate a waveform image using ffmpeg.
I have successfully made a waveform image, however it doesn't look very nice...
I have been looking around to try and style the image to make it look nicer, however I have been unable to find any information on this or any tutorials on this.
I am using PHP and shell_exec to create the waveform.
I am aware that there are php library that can do this but due to file format this is a lengthy process.
The code I am using is as follows:
$command = 'convertvid\bin\ffmpeg -i Temp\\'.$file.' -y -lavfi showwavespic=split_channels=0:s='.$width.'x50 Temp\\'.$PNGFileName; shell_exec($command);
Basically I would like to add a line through the middle as there are blank spots at the moment and would like to be able to set the background and wave colour.
-
Jeff about 8 yearsThanks LongNeckbeard, you are the FFmpeg master! When I did "boring default wav" command it already had a transparent background and red wave, i didn't have to add the extra stuff which works great.
-
chovy over 7 years
Option 'colors' not found
-- i assume i need a build flag -
llogan over 7 years@chovy Your
ffmpeg
is too old. See the FFmpeg Download page for links to "static builds" for your OS. -
Ankush about 3 years@llogan is there a way to generate waveform data in an array format, so that it can be plotted manually?
-
llogan about 3 years@Ankush Can you show my an example of what you want?
-
Ankush about 3 years@llogan So what I am trying to do is, In Android, I am trying to make a UI that shows one streak for every second of the audio clip. So I was wondering if there's a way to get an array of amplitudes (average) per second. For example, if a clip is 10 seconds long, I want an array of 10 size where each element is the amplitude for every second. Then I can manually plot these 10 array elements in a UI. Have a look at this image and hover over it for viewing comments drive.google.com/file/d/1u375nXkVe8ekiMbQToVNGekgAgLujKjj/view
-
llogan about 3 years@Ankush Assuming an input with a 44100 sample rate try
ffprobe -v error -f lavfi -i amovie=input.wav,asetnsamples=44100,astats=metadata=1:reset=1 -show_entries frame_tags=lavfi.astats.Overall.RMS_level -of csv=p=0
See astats filter for other stats to choose from. -
Ankush about 3 years@llogan Thanks for the response. I'll check it out and will update in case of any queries. Thanks once again.
-
Jérémy over 2 yearsWhat if we don't want a picture of the waveform, but rather the raw output file using
-f data
? Which extension should we put? I always got "Unable to find a suitable output format for ..." no matter the output format. I ideally want it as a JSON, but raw data would be fine. -
llogan over 2 years@Jérémy See the "RMS level per audio frame" command in the answer, but change
-of csv=p=0 > rms.log
to-of json > output.json