#!/usr/bin/env python decode_version = { 0: 2.5, 2: 2, 3: 1 } decode_layer = { 1: 3, 2: 2, 3: 1 } decode_bitrate_v1l3 = { 1: 32, 2: 40, 3: 48, 4: 56, 5: 64, 6: 80, 7: 96, 8:112, 9:128, 10:160, 11:192, 12:224, 13:256, 14:320 } decode_samplerate = { 0: 44100, 1: 48000, 2: 32000 } class MP3Error(Exception): pass class MP3Frame: def __init__ (self, file): state = 1 self.header = file.read (4) if len (self.header) != 4: raise MP3Error, "unexpected EOF" if (self.header[0] != '\xff') or ((ord (self.header[1]) >> 5) != 0x7): file.seek (-4, 1) raise MP3Error, "no mpeg framestart" version = (ord (self.header[1]) >> 3) & 0x3 layer = (ord (self.header[1]) >> 1) & 0x3 self.protected = ord (self.header[1]) & 0x1 bitrate = (ord (self.header[2]) >> 4) samplerate = (ord (self.header[2]) >> 2) & 0x3 self.padding = (ord (self.header[2]) >> 1) & 0x1 self.private = ord (self.header[2]) & 0x1 try: self.version = decode_version [version] self.layer = decode_layer [layer] if self.version != 1 or self.layer != 3: file.seek (-4, 1) raise MP3Error, "can only handle MPEG Version 1, Layer III" self.bitrate = decode_bitrate_v1l3 [bitrate] * 1000 self.samplerate = decode_samplerate [samplerate] except KeyError: raise MP3Error, "unknown mp3 framedata" # Layer 1 formula... self.framelength = (144 * self.bitrate) / self.samplerate + self.padding if self.protected: self.crc = file.read (2) if len (self.crc) != 2: raise MP3Error, "unexpected EOF" self.data = file.read (self.framelength - 6) if len (self.data) != self.framelength - 6: raise MP3Error, "unexpected EOF" else: self.crc = '' self.data = file.read (self.framelength - 4) if len (self.data) != self.framelength - 4: raise MP3Error, "unexpected EOF" def __repr__(self): return "MP3Frame (mpeg %d.%d, %d kBit/s, %d kHz, %d bytes)" % (self.version, self.layer, self.bitrate / 1000, self.samplerate, self.framelength) def asstring (self): return self.header + self.crc + self.data def playframes (framelist): import os, select (wpipe, rpipe) = os.popen4 ("mpg123 -v -", "w") try: for frame in framelist: wpipe.write (frame.asstring ()) while select.select ([rpipe], [], [], 0)[0]: rpipe.read (1) except KeyboardInterrupt: print "playing interrupted" wpipe.close () rpipe.close () if __name__=='__main__': import sys, time, readline frames = [] count = 0 boundary = 0 if len (sys.argv) <= 1: print "Usage: %s file1.mp3 file2.mp3 ..." % sys.argv[0] sys.exit () for filename in sys.argv[1:]: print "frame %6d: '%s'" % (len (frames), filename) file = open (filename) while 1: try: frame = MP3Frame (file) # print repr (frame) frames.append (frame) count = count + 1 # if not (count % 1000): # print count, file.tell () except MP3Error, e: startskip = file.tell () skipdata = file.read (500) while skipdata: index = skipdata[1:].find ('\xff') if index >= 0: file.seek (- len (skipdata) + index + 1, 1) break skipdata = file.read (500) skipsize = file.tell () - startskip if skipsize: print "Skipping %d bytes at %d (%s)" % (skipsize, startskip, e.args[0]) else: break file.close () if not boundary: boundary = count print "total: %6d Frames" % count help = """ q - Quit h - this help w - write (asks for range as and filename) p - play (asks for range as ) + - move split point frames towards end of files - - move split point frames towards start of files - move split point to frame - listen at current split point """ print help while 1: inputstring = raw_input ("split at: (%d) " % boundary) try: if inputstring == '': pass elif inputstring [0] == 'q': break elif inputstring [0] == 'h': print help continue elif inputstring [0] == '+': boundary += int (inputstring[1:]) elif inputstring [0] == '-': boundary -= int (inputstring[1:]) elif inputstring [0] == 'p': rng = raw_input ("range: start:stop ") sublist = eval ("frames[%s]" % rng) print "playing %d frames" % len (sublist) playframes (sublist) continue elif inputstring [0] == 'w': rng = raw_input ("range: start:stop ") filename = raw_input ("filename: ") sublist = eval ("frames[%s]" % rng) file = open (filename, "w") for frame in sublist: file.write (frame.asstring ()) file.close continue else: boundary = int (inputstring) except ValueError: continue print "playing before split" playframes (frames [:boundary][-300:]) time.sleep (2) print "playing after split" playframes (frames [boundary:][:300])