hmak
hmak

Reputation: 3968

How to get audio peaks with FFmpeg?

I am working on a music app and need to generate audio spectrum for my files. Like this one:

enter image description here

So I tried using audiowaveform like this:

audiowaveform -i music.mp3 --pixels-per-second 1 -o out.dat

which gives me the following results[correct results]: [the first 10 words are meta data]

0000000 0001 0000 0000 0000 bb80 0000 bb80 0000
0000020 00f9 0000 df3e 1fa2 e22c 1ef3 e0bb 1e5a
0000040 e099 1e88 dfcf 1c33 e29f 1d4c e055 1f80
0000060 df63 1e3a e1b4 1f31 e271 1d81 e0e5 1b1c
0000100 e06d 1be4 dee2 1cb0 e118 1da1 e026 1dea
0000120 e055 1dac df9b 1dbf e0c3 2063 ded4 21b2
0000140 dec9 1f8d de5b 20c8 e02d 216a dd7e 21af
0000160 dea1 20ac de6c 2170 de80 1e12 de6f 1fb9
0000200 dde3 2106 e0d9 21be de88 218c de81 1f9f
0000220 decb 20ff deb2 1edc df32 20c4 dde7 ...

But when I do this kind of job with FFmpeg:

ffmpeg -y -i music.mp3 -acodec pcm_s16le -f s16le -ac 1 -ar 1 -v quiet out.pcm

that gives the following results, which is not same at all:

0000000 0001 fffe fffe fffe 0000 ffff fffd 0000
0000020 ffff ffff fffe 0001 0001 fffd 0001 fffe
0000040 0002 fffe fffc 0002 ffff fffc fffe 000b
0000060 0007 fffb 0004 0001 ffff fffd ffff 0002
0000100 0008 0006 fffe ffff 0001 0000 0003 000a
0000120 fffd ffff 0004 ffff 0001 ffff fffd ffff
0000140 fffe ffff 0001 fffd fffe 0000 fffb 0002
0000160 0002 0000 fffe 0000 fffb fffe fffe 0000
0000200 ffff 0000 ffff fffc 0002 0003 0005 0003
0000220 0002 fffb fffb fffa fffa 0004 0009 ...

You may wonder that why am i doing -ar 1 or --pixels-per-second 1? This is because I want to draw a line for each second, so I need to get peak for each second. . I don't know what am I missing there but I expect to get the same results from FFmpeg.

Upvotes: 5

Views: 4406

Answers (1)

hmak
hmak

Reputation: 3968

This is not a solution with FFMPEG, but still results with a wave form array.

My solution was to use the audiowaveform linux package which has a simple cli to extract the waveform data with the desired sample rate.

You can install it on ubuntu like:

sudo add-apt-repository ppa:chris-needham/ppa
sudo apt-get update
sudo apt-get install audiowaveform

Or on macOS with homebrew like:

brew tap bbc/audiowaveform
brew install audiowaveform

First I used the provided command in the question to draw the waveform but it was inaccurate and dirty. Because it takes one sample from each second which is not what I was looking for. So I decided to take 100 samples from each second and get the average with some JS code. So the command to extract the wave form will be:

audiowaveform -i /root/audio.mp3 --pixels-per-second 100 --output-format json -

This will output the audio wave form data along with some metadata on the stdout (That hyphen at the end does the trick). So in my case I used NodeJs to get this output and reduce the waveform array to the average of waveform blocks. Note that I removed the negative numbers from the waveform to work on the upper half of the waveform.

import {exec} from "child_process";

export default function getAudioWaveform(filename, blockSize = 100) {
    return new Promise((resolve, reject) => {
        const command = `audiowaveform -i ${filename} --pixels-per-second ${blockSize} --output-format json -`;

        exec(command, (error, stdout, stderr) => {
            if (error) reject(error, stderr);

            try {
                const data = JSON.parse(stdout).data.filter((_, i) => i % 2 === 1)

                const waveform = [];
                for (let i = 0; i < data.length; i++)
                    waveform[i] = Math.round(data.slice(i * 100, (i + 1) * 100).reduce((s, n) => s + n, 0) / 100);

                resolve(waveform);
            } catch (ex) {
                reject(ex);
            }
        });
    })
}

Upvotes: 7

Related Questions