py_mpgedit SDK

beta 0.3

Python bindings to the mpgedit audio file editor


Summary

py_mpgedit is a python extension module that wraps the mpgedit sdk. py_mpgedit tries to keep the look and feel of the mpgedit API and be as python friendly as possible. Eventhough py_mpgedit is versioned at beta 0.3, the mpgedit SDK that it wraps is at dev 0.73, has been out for quite a while now and is very stable.

Currently simply_play.py and simple_edit.py are the only example which are included in the SDK.

If you have any questions, suggestions, want to report a bug, need an enhancement or feature, please feel free to email me.

The latest version can be downloaded here.

Requirements

py_mpgedit is a python wrapper that uses the ctypes module. Ctypes is officially available in Python version 2.5. The ctypes module is available for Python 2.4 and earlier releases from SourceForget.net.

Classes

Samples:

Terms and conditions for accessing or otherwise using py_mpgedit is here.


mpgedit.Play

class Play(mp3file)
Constructor for the Play class. mp3file is the name of the mp3 file to play. mp3file may contain a relative or absolute path. To close or free a Play instance, either have the object instance go out of scope, explicitly decrement the reference count with the del command, or call this object's close() method.

play(callback=None)
Play the mp3 file. If a callback is provided, play will call the function callback with two arguments, sec and msec, which is the time offset of the current frame being played. The callback function must return a boolean True value to tell play to continue playing, otherwise, play will stop.

stop()
Notify the file currently playing in a thread to stop. The file will continue playing from the point it was stopped by calling play. Calling stop has no effect when a file is not playing.

seek_time(sec, msec)
Sets the mp3 file's current position to the frame corresponding to sec and msec.

total_size()
Returns the total size in bytes of the mp3 file.

total_time()
Returns the total time of the mp3 file. The return value is a 2-tuple (sec, msec).

total_info()
Returns the total size and total time of the mp3 file. The return value is a 2-tuple (size, (sec, msec)).

current_time()
Returns the current time of the mp3 file. The return value is a 2-tuple (sec, msec).

get_volume()
Returns the current volume of the system. The return value is a 2-tuple (lvol, rvol) and both value are in the range 0 to 100.

set_volume(lvol, rvol=-1)
Sets the current volume of the system. Both lvol and rvol are in the range 0 to 100. If only lvol is specified, both left and right volumes are set to lvol.

Note: In the current Windows implementation, the right volume is always set to lvol. This will be fixed in a future version of the mpgedit SDK. If this is an issue for anyone at this time, please let us know.

close()
Frees all resources of this object. After this method is called, this object instance cannot be used again. This objects __del__ method calls close(). So having the object go out of scope is usually good enough and an explicit call to close in not required.

mpgedit.Index

class Index(mp3file)
Constructor for the Index class. mp3file is the name of the mp3 file to index. mp3file may contain a relative or absolute path. To close or free a Index instance, either have the object instance go out of scope, explicitly decrement the reference count with the del command, or call this object's close() method.

Indexing is required anytime you want to access an mp3 file by an edit time, as specified by an mpgedit.Spec. Normally, all operations require the index file. Play does not when you are playing from the start of the file. However, when you want to seek to a time, then play, you must have the index file.

index(callback=None)
Creates an index file. If a callback is pass in, index will call the function callback with the arguments (sec, msec, offset). The callback function must return a boolean True value to tell index to continue indexing, otherwise, index will stop.

frames()
Returns the number of the frames indexed at this position.

time()
Returns the time offset for the current indexed position. The return value is a 2-tuple (sec, msec).

offset()
Returns the byte offset for the current indexed position.

mpgedit.EditSpec

class EditSpec()
Constructor for the EditSpec class. To close or free a EditSpec instance, either have the object instance go out of scope, explicitly decrement the reference count with the del command, or call this object's close() method.

append(mp3file, timespec)
Appends an edit definition for an mp3 file to the editspec. mp3file is the name of an mp3 file to edit. mp3file may contain a relative or absolute path. timespec is a string that specifies the start and stop times of the edit to perform on the named mp3 file. The general form of this time specification is 'MM:ss.mmm-MM:ss.mmm' where MM is minutes, ss is seconds and mmm is milliseconds. Any of these fields may be omitted. When the start time specification is omitted, the edit is performed from the start of the mp3 file to the specified end time. When the end time is omitted, the edit is performed from the specified start time to the end of the mp3 file. The entire file may be included in the edit by specifying "-". For example, the timespec string "20-30.500" defines an edit starting 20 seconds from the start of mp3file, and ending at 30 seconds 500 milliseconds. The econds specifier can be greater than 60. For example, "95-1000" is equivalent to "1:35-16:40".

Please refer to the mpgedit sdk documentation for the complete format.

__iadd__(editspec, start_timespec)
Similar to append but takes either a single start_timespec or the two start_timespec end_timespec values.

filename(offset)
Returns the file name associated with editspec at the location specified by offset. The first entry in editSpec is accessed when the value of offset is zero. An empty string is returned when offset is out of bounds.

length()
Returns the number of edit definitions appended to editspec.

start_time(offset)
Returns the start time associated with the editspec at the location specified by offset. The return value is 2-tuple (sec, msec). An IndexError exception is raised if offset is out of bounds.

end_time(offset)
Returns the end time associated with the editspec at the location specified by offset. The return value is 2-tuple (sec, msec). An IndexError exception is raised if offset is out of bounds.

__getitem__(offset)
Returns the filename, start time and end time associated with the editspec at the location specified by offset. The return value is 3-tuple (filename, (start_sec, start_msec), (end_sec, end_msec)) .
__iter__()
Returns an iterator over all entries in EditSpec. Each iteration returns a 3-tuple of (filename, (start_sec, start_msec), (end_sec, end_msec))

close()
Frees all resources of this object. After this method is called, this object instance cannot be used again. This objects __del__ method calls close(). So having the object go out of scope is usually good enough and an explicit call to close in not required.

mpgedit.Edit

class Edit(spec, outfile, flags=0)
Constructor for the Edit class. spec is an instance of Spec. outfile is the name of an output mp3 file that will be generated. outfile may contain a relative or absolute path. flags is optional and defaults to zero. If flags is 0, a outfile will be created and outfile must not exist. If flags is 1, Edit will append to outfile. To close or free an Edit instance, either have the object instance go out of scope, explicitly decrement the reference count with the del command, or call this object's close() method.

edit(callback=None)
Edits an mp3 file. If a callback is specified, edit will call the function callback with the arguments (frame, sec, offset). The callback function must return a boolean True value to tell play to continue editing, otherwise, edit will stop. An MpgeditError will be raised on errors.

frames()
Returns the frame number which is currently being edited.

sec()
Returns the number of seconds which is currently being edited.

offset()
Returns the current byte offset in the file being edited.

sec()
Returns the frame number, number of seconds, and offset which is currently being edited. The return value is a 3-tuple (frame, sec, offset).

xing_header()
Returns a dictionary containing the xing header infomation. Here is an example returned by this method:
{'h_layer': 3, 'bytes': 596332, 'h_protect': 0, 'vbr_scale': 66, flags': 15, 'frames': 1264, 'h_id': 3, 'samprate': 44100}

close()
Frees all resources of this object. After this method is called, this object instance cannot be used again. This objects __del__ method calls close(). So having the object go out of scope is usually good enough and an explicit call to close in not required.

mpgedit.MpgeditError

exception MpgeditError
This exception is raised when there is a problem reading or appending to a file. The associated value of the exception is a 2-tuple that contains the error number a message string. Possible error values are:
  • (1, 'Unable to append to output file')
  • (2, 'Output file exists')
  • (3, 'Unable to read input file')
  • (4, 'Unable to read index file')
  • (5, 'Unable to open output file')
  • (6, 'Invalid Edit object'}
  • (7, 'Bad context'}

Editing an mp3 file

>>> import pympgedit as mpgedit
>>>
>>> # Create a spec that a time slice of an mp3 file
>>> spec = mpgedit.EditSpec()
>>> spec.append('test1.mp3', 6, 12)
>>>
>>> # Create an index for the mp3 file which will contain frame and time data
>>> mpgedit.Index('test1.mp3').index()
>>>
>>> # Create a new mp3 file based on the spec  
>>> mpgedit.Edit(spec, 'new.mp3').edit()
>>>
>>> # Play the new mp3 file
>>> mpgedit.Play('new.mp3').play()

Playing an mp3 file

#!/usr/bin/python
import sys
import time
import pympgedit as mpgedit

class DemoPlay(mpgedit.Play):
    def __init__(self, mp3file):
        mpgedit.Index(mp3file).index()
        mpgedit.Play.__init__(self, mp3file)
        self.count = 0
        ttime = self.total_time()
        print 'Total: size=%d time=%d.%03d' % (self.total_size(), ttime[0], ttime[1])
        print 'Previous volume: left=%d right=%d' % self.get_volume()
        self.set_volume(25, 25)
        print 'New volume: left=%d right=%d' % self.get_volume()

    def status(self, sec, msec):
        self.count += 1
        sys.stdout.write('Frame %5d: %02d.%03d sec\r' % (self.count, sec, msec))
        sys.stdout.flush()
        return 1

    def play(self):
        mpgedit.Play.play(self, self.status)
        #
        # playback happens in a new thread; sleep for playback to finish...
        time.sleep(self.total_time()[0] + 1)

    def stop(self):
        print 'stopping playback...'
        mpgedit.Play.stop(self)


p = DemoPlay(sys.argv[1])
try:
    p.play()

except KeyboardInterrupt:
    print 'KeyboardInterrupt received, calling p.stop...'
    p.stop()

Editing an mp3 file

#!/usr/bin/python
import sys
import pympgedit as mpgedit

def index_callback(frame, sec, msec, offset):
    print 'frame=%4d  time=%2d:%02d  offset=%6d' % (frame, sec, msec, offset)
    return True
 
def edit_callback(frame, sec, offset):
    print 'frame=%4d  sec=%2d  offset=%6d' % (frame, sec, offset)
    return True

def editif_test(mp3file):
    # This test edits a file by scrambling it, then puts it back together
    
    # Create a spec that contains several time slices of the original file
    spec = mpgedit.EditSpec()
    spec.append(mp3file, '15.986-19.983')
    spec.append(mp3file, '11.989-15.986')
    spec.append(mp3file, '7.993-11.989')
    spec.append(mp3file, '3.996-7.993')
    spec.append(mp3file, '0.0-3.996')
    spec.append(mp3file, '27.976-31.999')
    spec.append(mp3file, '23.979-27.976')
    spec.append(mp3file, '19.983-23.979')
    spec.append(mp3file, '31.999-34.0')

    # Create an index for the original mp3 file which will contain frame and time data
    print '\nCreating index for %s...' % mp3file
    mpgedit.Index(mp3file).index(index_callback)

    
    print '\nSpec entries' 
    print 'length=%d' % spec.length()    
    for s in spec:
        print 'filename=%s, stime=%s, etime=%s' % s
        
    # Create a new mp3 file based on the spec above      
    edit = mpgedit.Edit(spec, 'editif_test.mp3')
    print '\nCreating editif_test.mp3...'
    edit.edit()
    
    print 'info: frames=%d, sec=%d, offset=%d' % (edit.frames(), edit.sec(), edit.offset())
    print 'xing header:', edit.xing_header()

    # Create a new spec that will unscramble the scrambled mp3 file
    spec = mpgedit.EditSpec()
    tmpfile = 'editif_test.mp3'
    spec.append(tmpfile, "15.986-19.983")
    spec.append(tmpfile, "11.989-15.986")
    spec.append(tmpfile, "7.993-11.989")
    spec.append(tmpfile, "3.996-7.993")
    spec.append(tmpfile, "0.0-3.996")
    spec.append(tmpfile, "28.002-31.999")
    spec.append(tmpfile, "24.006-28.002")
    spec.append(tmpfile, "19.983-24.006")
    spec.append(tmpfile, "31.999-")
    
    # Create an index for scrambled mp3 file
    mpgedit.Index(tmpfile).index()    
    print '\nCreating editif_test2.mp3...'
    
    # Create a new mp3 file which will be exactly the same as the original.
    mpgedit.Edit(spec, 'editif_test2.mp3').edit(edit_callback)
    
                                                                          
if __name__ == '__main__':
    editif_test(sys.argv[1])