Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: FLAC bitrate histogram (shell script) (Read 5246 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

FLAC bitrate histogram (shell script)

In the meantime, does anyone know of a program that displays a histogram of bitrates from flac and hopefully some of the other more popular lossless formats?

No, but I vaguely recall wanting precisely this sort of information before. It's a holiday weekend, and I was bored, so I OCD'd out and hacked this together. Obviously, this probably requires a Unix system of some sort.

Code: [Select]
#!/bin/bash

set -e

if [ ! -f "$1" -o $# != "2" ]; then
    echo "Usage: $0 <flac> <histogram-png>" >&2
    exit 1
fi

FLAC_PATH=$1
FLAC_FILE=`basename "$FLAC_PATH"`
# uncomment this to autodect
#HISTOGRAM=`sed -e 's/\(.*\)\.flac/histogram-\1\.png/' <<< "$FLAC_FILE"`
HISTOGRAM=$2
BITS_ORIG=/tmp/bits-orig.txt
BITS=/tmp/bits.txt

read minblk maxblk origminframe origmaxframe channels bps rate <<< \
    `metaflac --show-min-blocksize --show-max-blocksize \
    --show-min-framesize --show-max-framesize \
    --show-channels --show-bps --show-sample-rate "$FLAC_PATH"`

# $blocksize is as reported by metaflac. Must be constant for entire file
if [ "$minblk" != "$maxblk" ]; then
    echo "ERROR: min/max blocksizes differ ($minblk, $maxblk)." >&2
    exit 1
fi
blocksize=$minblk

# $rawblocksize: uncompressed size of block, in bits, including all channels
rawblocksize=$(($blocksize*$channels*$bps))

# Analyze FLAC, extract "bits=" property of each frame
flac -sa "$FLAC_PATH" -o- \
    | grep "blocksize=$BLOCKSIZE" \
    | sed -e 's/.*bits=\([0-9]*\).*/\1/' \
    >& $BITS_ORIG

# The "actual" minimum/maximum frame sizes appear to be outliers; e.g. for one
# FLAC with average frame size of roughly 70kb, the very first frame was 203kb
# and was nearly 2x the size of the next largest frame; the 2nd and 3rd frame
# were 112 bits long and were 135x smaller than the next smallest frame. For the
# sake of the histogram, drop those out.

echo "Dropping min frame size $(($origminframe*8))" \
    "and first frame size `head -1 $BITS_ORIG`."

tail -n +2 $BITS_ORIG | grep -v "^$(($origminframe*8))$" > $BITS
#cat $BITS_ORIG > $BITS # uncomment this to include min/max frame sizes

read minframesize maxframesize <<< \
    `awk -e 'BEGIN { max=0; min=999999; } min > $0 {min=$0} \
    max < $0 {max=$0} END { print min " " max }' \
    < $BITS`

gnuplot <<EOF
bin(x,width)=width*floor(x/width)
binwidth=25 # in kbits/s

set xlabel "Frame effective bitrate (kbps)"
set ylabel "frequency (frames)"
set xrange [$minbits:$maxbits]

# comment this out to display gnuplot window instead of writing to png
set term png size 500, 300 font "DejaVu Sans,8"
set output "$HISTOGRAM"

set yrange [0.0:]
plot "$BITS" using (bin(\$1/(1.e3*$blocksize/$rate),binwidth)):(1.0) title "$FLAC_FILE" smooth freq with boxes

# if commenting out above png output lines, uncomment this
# pause mouse
EOF

echo "Done, output in $HISTOGRAM"
rm -f $BITS $BITS_ORIG


Sample output...

Tame Impala, "Elephant":


Merzbow, "I Lead You Towards Glorious Times":


Autechre & Hafler Trio, "æ³o":



I'm not aware of any way to get this sort of information from a format-independent utility like sox. Which is unfortunate.

FLAC bitrate histogram (shell script)

Reply #1
Nice 

FLAC bitrate histogram (shell script)

Reply #2
Yeah nice. If it could also specify file average and 1-second peak?

I have posted this a few timed, but Merzbow: I Lead You Towards Glorious Times is the least-compressible piece of music I have ever come across. FLAC -8 doesn't manage to get it below 1395, which in the very least beats TAK, which even at -p4m returns more than 100 percent of uncompressed filesize. (WavPack extra high saves some 7 percent I think.)

 

FLAC bitrate histogram (shell script)

Reply #3
Like it too
Then made Windows version.

I doubt what to do for histogram, then I guess ascii is universal.
It accepts optional parameter for number of bins (default 32), unlike binwidth in Axon's version.
Path to executables should be set according user preferance.

Example:
Code: [Select]
C:\>cscript flachist.vbs "10 - Hysteria.flac"
Microsoft (R) Windows Script Host Version 5.7
Copyright (C) Microsoft Corporation. All rights reserved.

10 - Hysteria.flac

   0:  36>
  36:  73>
  73: 110>
110: 147>
147: 183>
183: 220>
220: 257>
257: 294>
294: 330>
330: 367>
367: 404>
404: 441> *
441: 478> *
478: 514> *
514: 551> *
551: 588> *
588: 625> *
625: 661> **
661: 698> ***
698: 735> *****
735: 772> **********
772: 808> *************
808: 845> **************
845: 882> ***************************
882: 919> *************************************
919: 956> *******************************************************
956: 992> *************************************************************
992:1029> ********************************************************************
1029:1066> *********************************************************
1066:1103> ************************
1103:1139> ****
1139:1176>


flachist.vbs
Code: [Select]
' Path to executables
flac = "C:\Program Files\foobar2000\tools\flac.exe"
metaflac = "C:\Program Files\foobar2000\tools\metaflac.exe"

Set WshShell = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")

If WScript.Arguments.Count And fso.FileExists(WScript.Arguments(0)) Then
  flacArgs = " -asc "
  metaflacArgs = " --show-min-blocksize --show-max-blocksize --show-min-framesize" &_
                 " --show-max-framesize --show-channels --show-bps --show-sample-rate "
  q = Chr(34) : flac = q & flac & q : metaflac = q & metaflac & q

  Set cmd = WshShell.exec(metaflac & metaflacArgs & q & WScript.Arguments(0) & q)
  info = Split(cmd.StdOut.ReadAll(), vbCrLf)

  If info(0) <> info(1) Then
    WScript.Echo "ERROR: min/max blocksizes differ: " & info(0) & "<>" & info(1)
    WScript.Quit
  Else
    blocksize = info(0) : origminframe = info(2) : rate = info(6)
  End If

  ' Write bits info data to csv file
  Set csv = fso.CreateTextFile(WScript.Arguments(0) & ".csv")
  Set cmd = WshShell.exec(flac & flacArgs & q & WScript.Arguments(0) & q)
  Do While cmd.StdOut.AtEndOfStream <> True
    readline = cmd.StdOut.ReadLine
    If InStr(readline, "blocksize=" & blocksize) Then
      bits = Split(Split(readline, vbTab)(2), "=")(1)
      If Int(bits) <> origminframe*8 Then csv.WriteLine(bits)
    End If
  Loop

  ' Read csv
  Set csv = fso.OpenTextFile(WScript.Arguments(0) & ".csv")
  lines = Split(csv.ReadAll(), vbCrLf)
  max = 0 : bins = 32
  If WScript.Arguments.Count > 1 Then bins = CInt(WScript.Arguments(1))

  For Each line In lines
    If line <> "" Then If Int(line) > max Then max = Int(line)
  Next

  ' Print ascii histogram
  bin = Array() : ReDim bin(bins)
  maxbin = 0
  For Each line In lines
    If line <> "" Then idx = Fix(bins*Int(line)/(max+1)) : bin(idx) = bin(idx) + 1
    If bin(idx) > maxbin Then maxbin = bin(idx)
  Next

  WScript.Echo WScript.Arguments(0) & vbCrLf
  m = max*rate/bins/blocksize/1000
  For i=0 To bins-1
    WScript.Echo Right("      " & Int(i*m), 4) & ":" &_
                 Right("      " & Int((i+1)*m), 4) & "> " &_
                 String(Int(bin(i)*68/maxbin), "*")
  Next
Else
  WScript.Echo "Usage: cscript flachist.vbs flac-file-path [bins]"
End If