3

I have a bunch of audio files and I would like to create the spectrogram for each individual file using Sox. Usually, for a single file, I do this:

sox audiofile.flac -n spectrogram

However I don't know how to extend this method to more than one file. Ideally I would like my output .png file to have a filename associated to its respective audio file; for example audiofile1.png for audiofile1.flac, audiofile2.png for audiofile2.flac and so on.

Does anybody know how to do this?

4

You can enclose the command in a loop:

for file in *.flac
do
    outfile="${file%.*}.png"
    sox "$file" -n spectrogram "$outfile"
done

As for file naming, the sox(1) man page seems to suggest you can explicitly name your output file on the command line so you can use that within the loop.

The first line in the loop makes use of Bash's parameter substitution to strip the .flac extension from the file name and add the .png extension.

4

Thanks to Joseph for his answer. Perhaps it worked at the time of his posting but I had to add -o just after spectrogram for sox to accept the command.

for file in *.flac;do
    outfile="${file%.*}.png"
    sox "$file" -n spectrogram -o "$outfile"
done

I'll be whacking them all in their own folder for archiving. It works!

You could even go a bit further by adding the title of the file above the spectrogram within the image, and making it a little wider to see more details. The default image is a bit small for me.

for file in *.flac;do
    outfile="${file%.*}.png"
    title_in_pic="${file%.*}"
    sox "$file" -n spectrogram -t "$title_in_pic" -o "$outfile" -x 2000
done
0

here is my "get spectrogram" solution to ....

  • handle more codecs like aac, opus
  • handle more and containers like mp4, mkv, avi, m4a
  • normalize spectrogram height to 24kHz
  • plot only one channel = mono
  • normalize volume
  • keep input file extension for output file
#!/bin/bash

# aspec.sh
# get spectrograms of audio streams
#
# usage: aspec.sh a.mp3 b.m4a c.mp4 d.mkv ....
#
# dependencies: sox, ffmpeg
# license: public domain, warranty: none
# version: 2019-05-17 by milahu

ff_args="" # ffmpeg arguments
sx_args="" # sox arguments

ff_args+=" -loglevel error"

ff_astream=0 # only use first audio stream
ff_args+=" -map 0:a:${ff_astream}?"

ff_args+=" -ac 1" # use only one audio channel
sx_args+=" channels 1"

sx_args+=" gain -n -3" # normalize volume to -3dB

# set sampling rate
# only analyze frequencies below f_max = rate / 2
# also normalize spectrogram height to f_max
#sx_args+=" rate 6k"  # only show f <  3kHz "where the human auditory system is most sensitive"
sx_args+=" rate 48k" # only show f < 24kHz

# use wav as temporary format, if sox cant read file
ff_args+=" -c:a pcm_s16le -f wav"
sx_type="wav"

# process files from "argv"
for i in "$@"
do
    echo "$i"
    o="$i.sg.png" # output file
    t=$(basename "$i") # title above spectrogram
    c="spectrogram by SoX, the Sound eXchange tool" # comment below spectrogram

    # try to read original format
    echo analyze
    sox "$i" -n \
        $sx_args \
        spectrogram -o "$o" -c "$c" -t "$t" \
        2>&1 | grep -v "no handler for detected file type"

    if (( ${PIPESTATUS[0]} != 0 ))
    then
        # sox failed. convert audio and retry
        echo convert

        # get duration of stream or container
        # spectrogram filter has no "ignore length" option
        # and without a "duration prediction" will only read 8 seconds
        d=$(ffprobe "$i" -v error -of compact=s=_ \
            -select_streams "0:a:${ff_astream}?" \
            -show_entries stream=duration:format=duration \
            | sort | grep -v =N/A \
            | tail -n 1 | cut -d= -f2)
        # 'tail -n 1' --> prefer stream duration
        # 'head -n 1' --> prefer container duration

        if [[ -z "$d" ]]
        then
            echo -e "skip. duration not found FIXME\n"
            continue
        fi

        # bash "process substitution" magic
        sox \
            --type "$sx_type" \
            --ignore-length \
            <( ffmpeg -i "$i" $ff_args - ) \
            --null \
            $sx_args \
            spectrogram -d "$d" -o "$o" -c "$c" -t "$t"
    fi

    echo -e "done\n$o\n"
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.