All pastes #1590184 Raw Edit

Something

public python v1 · immutable
#1590184 ·published 2009-10-03 04:24 UTC
rendered paste body
#!/usr/bin/python# vim: set expandtab tabstop=4 shiftwidth=4:# Note - currently this util is hard-coded to only accept# MPEG Version 1 Layer 3 files, with no CRC prodection.  It# won't recognize a file with any other kind of audio frames.## It should handle id3v1 and id3v2 frames as well, though.## http://www.mp3-tech.org/programmer/frame_header.html# http://en.wikipedia.org/wiki/MP3# http://upload.wikimedia.org/wikipedia/commons/0/01/Mp3filestructure.svg# http://www.id3.org/id3v2.4.0-structure# http://en.wikipedia.org/wiki/ID3# http://www.mp3-converter.com/mp3codec/frames.htm <- not entirely accurateimport sys, termios, fcntlfrom struct import pack, unpackfrom math import log, exp# Global varshist = {}framecount = 0# Lookup tablesbrlookup = {        0x0: 0,        0x1: 32,        0x2: 40,        0x3: 48,        0x4: 56,        0x5: 64,        0x6: 80,        0x7: 96,        0x8: 112,        0x9: 128,        0xA: 160,        0xB: 192,        0xC: 224,        0xD: 256,        0xE: 320,        0xF: 999    }samplookup = {        0x0: 44100,        0x1: 48000,        0x2: 32000,        0x3: 99999    }def handle_mp3(df, data):    global hist, framecount    padded = 0    if (data & 0x0200 == 0x0200):        padded = 1    bitrate = brlookup[(data & 0xFF00) >> 12]    samprate = samplookup[(data & 0x0F00) >> 10]    # Ordinarily we should check for CRC here, but we're    # just assuming that it doesn't have it right now    # (ie: the pattern 0xFFFB that we test for in the main loop)    datasize = int((144000*bitrate)/samprate) + padded - 4    data = df.read(datasize)    if (len(data) != datasize):        print 'WARNING: Couldn\'t read mp3 data'        return False    hist[bitrate] = hist[bitrate]+1    framecount = framecount+1    return Truedef unsyncsafe(var, bytes):    retvar = 0    for i in range(bytes):        tempval = var >> (i*8)        tempval = tempval & 0x7F        tempval = tempval << (i*7)        retvar = retvar + tempval    return retvardef handle_id3v2(df, data):    (flags, size) = unpack('>x B I', df.read(6))    size = unsyncsafe(size, 4)    data = df.read(size)    if (flags & 0x10 == 0x10):        size = size + 10    if (len(data) != size):        print 'WARNING: Couldn\'t read id3v2 frame'        return False    return Truedef handle_id3v1(df, data):    data = df.read(124)    if (len(data) != 124):        print 'WARNING: Couldn\'t read id3v1 tag'        return False    return Truedef processfile(filename):    global hist, framecount    hist = {}    tagcount_v1 = 0    tagcount_v2 = 0    framecount = 0    for rate in brlookup.values():        hist[rate] = 0    valid = True    # Initial setup    try:        df = open(filename, 'rb')    except IOError:        print '"' + filename + '" could not be opened.'        return False    # Start processing frames    data = df.read(4)    while (len(data) == 4):        data = unpack('>I', data)[0]        if (data & 0xFFFF0000 == 0xFFFB0000):            rv = handle_mp3(df, data)            pass        elif (data & 0xFFFFFF00 == 0x49443300):            rv = handle_id3v2(df, data)            tagcount_v2 += 1            pass        elif (data & 0xFFFFFF00 == 0x54414700):            rv = handle_id3v1(df, data)            tagcount_v1 += 1            pass        else:            valid = False            rv = None            print 'Unknown data in mp3: %0X' % data            break        if (not rv):            print 'Errors processing file, skipping'            valid = False            break        data = df.read(4)    # Clean up    df.close()    # Calculate some stats    max = 0    amean = 0    gmean = 0    secs = 0    mins = 0    for rate in hist.keys():        if (hist[rate] > max):            max = hist[rate]        amean = amean + float(rate*hist[rate])/framecount        if (rate != 0 and hist[rate] != 0):            gmean = gmean + hist[rate] * log(rate)    gmean = exp(gmean/framecount)    # .026 secs per frame is *wrong* - it's actually 1152 samples    # per frame, which you want to divide by our 44100Hz.  Which    # simplified leaves us with 32/1225, btw (common factor = 36).    # (or, if you want to be pedantic, .02612244897959183673....)    #secs = int(framecount*26/1000)    secs = int(framecount*32/1225)    milli = (framecount*32.0/1225.0)-secs    mins = int(secs/60)    secs = secs - (mins*60)    # Window geometry    numdigits = len(str(max))    temp = pack('HHHH', 0, 0, 0, 0)    try:        cols = unpack('xxHxxxx', fcntl.ioctl(sys.stdout.fileno(),            termios.TIOCGWINSZ, temp))[0]    except IOError:        cols = 80    if (cols < 10):        cols = 80    cols = cols - (9 + numdigits)    formatstr = '%3d [%' + str(numdigits) + 'd] '    # report    if (valid):        print '%s VBR layout:' % (filename)        print ' * Total track time: %d:%02d.%04d' % (mins, secs, round(milli*10000))        print ' * Arithmetic mean: %dkbps' % (amean)        print ' * Geometric mean: %dkbps' % (gmean)        #print '(Total tag counts - v1: %d | v2: %d)' % (tagcount_v1, tagcount_v2)        print 'Total frames: %d' % (framecount)        histkeys = hist.keys()        histkeys.sort()        for rate in histkeys:            if (hist[rate] != 0):                # we need to cast to float, otherwise the expression returns 0 for all but the                # longest row                print (formatstr + '*'*int((float(hist[rate])/max)*cols)) % (rate, hist[rate])        print    # ... and return    return Truedef main(argv=None):    if argv is None:        argv = sys.argv    for file in sys.argv[1:]:        processfile(file)    return 0if __name__ == '__main__':    sys.exit(main())