Overview

HTTPS SSH

decoder.py

decoder.py is a cross-platform module for decoding compressed audio files. It uses external decoders by turning their stdout into file-like object which is completely compatible with wave.py module.

decoder.py is the result of author's knowledge, skill and creativity and a lot of assembled code legally obtained from the internet. Thanks to all who put them there!

Usage:

>>> import decoder
>>>
>>> mp3 = decoder.open("something.mp3")
>>> print "Channels:", mp3.getnchannels()
Channels: 2
>>> print "Framerate:", mp3.getframerate()
Framerate: 44100
>>> print "Duration:", mp3.getnframes()/mp3.getframerate()
Duration: 206
>>>
>>> import wave
>>>
>>> wf = wave.open("something.wav", "w")
>>> wf.setnchannels(mp3.getnchannels())
>>> wf.setsampwidth(mp3.getsampwidth())
>>> wf.setnframes(mp3.getnframes())
>>> wf.setframerate(mp3.getframerate())
>>> data = mp3.readframes(1024)
>>> while data!="":
...     wf.writeframes(data)
...     data = mp3.readframes(1024)
...
>>> mp3.close()
>>> wf.close()
>>>
>>> # You can abbreviate the above code as this:
>>> mp3 = decoder.open("something.mp3")
>>> wf = wave.open("something.wav", "w")
>>> decoder.copywaveobj(mp3, wf)
>>> mp3.close()
>>> wf.close()
>>>

The "from decoder import *" is also supported, but the function open() will then become acopen(), so to avoid mixing with __builtin__.open().

You can use the audiodev module or PyAudio library or sys.stdout or some external command line player like aplay in the same way as shown above.

FUNCTIONS:

open(name, fh=0) --> fakewave / wave.Wave_read / aifc.Aifc_read object The name argument is the path to file you wish to decode. Supported filetypes: *.mp1, *.mp2, *.mp3 *.mp4, *.m4a, *.m4b, *.aac *.ogg *.flac *.oga *.wma *.wav, *.aif (uncompressed files only)

fh (force header) argument can be one, zero, boolean or string representing the valid WAVE header. It is used if an external decoder writes only raw data to stdout. For instance, "faad" under Linux.

The open() returns an instance of a class with the following public methods:
getnchannels() -- returns number of audio channels (1 for
mono, 2 for stereo)

getsampwidth() -- returns sample width in bytes getframerate() -- returns sampling frequency getnframes() -- returns number of audio frames getcomptype() -- returns compression type (always 'NONE' - linear samples) getcompname() -- returns human-readable version of

compression type (always 'not compressed' linear samples)
getparams() -- returns a tuple consisting of all of the
above in the above order
getmarkers() -- returns None (for compatibility with the
aifc module) The exception goes when aiff file is used.
getmark(id) -- raises an error since the mark does not
exist (for compatibility with the aifc module) The exception goes when aiff file is used.

readframes(n) -- returns at most n frames of decoded audio rewind() -- rewind to the beginning of the audio stream setpos(pos) -- seek to the specified position tell() -- return the current position close() -- close the instance (make it unusable)

The position returned by tell() and the position given to setpos() are compatible and have nothing to do with the actual position on stdout. The close() method is called automatically when the class instance is destroyed.

NOTA BENE:

The setpos() and rewind() are usually not accessible because of on-fly decoding and use of one of them often raises the IOError: [Erno 29] Illegal seek.

Unfortunately, you cannot give an open read file pointer to open(), not yet.

getnframes() returns correct number of frames, but decoders nearly always cut off some unimportant frames and therefore be careful about this. In this case fakewave.readframes() returns empty frames where the "" (empty string) would be returned if asking the external decoder.

CreateWaveHeader(nchannels=2, sampwidth=2, samplerate=44100) --> string
Returns the WAVE header string which can be used for force header argument of function open(). You are advised to use it when the specifications of the output of external decoder are static and known to you. This function is called (without arguments) if the force header argument of open() function is True or 1.
CreateWaveHeaderFromFile(name) --> string
Returns the WAVE header string which can be used for force header argument of function open(). The generated header does not always match the output of the external decoder and it is also not 100% correct. So you are advised to use CreateWaveHeader() instead. (When possible)

The following functions have to be called with decoder.py imported as "import decoder", not "from decoder import *".

CheckForUpdates() --> dictionary or None
Checks for updates of decoder.py on the official page and mirror. If you are not connected to the internet or both servers are unavailable or the downloaded update script is corrupted, None is returned. If everything went fine, the dictionary of update script options, with their values, is returned. To check for new version do: >>> nv = decoder.CheckForUpdates() >>> if nv: >>> if nv["__version__"] <= decoder.__version__: >>> print "No new version available!" >>> else: >>> print "A new version is", nv["__version__"] >>> else: >>> print "Something wrong with net connection, DNS, servers, uscripts etc."
update (udict=0) --> integer

Updates decoder.py. This function is designed to help programmers when creating software like players etc. Instead of packing new decoder.py with updates of your program, make it update itself. This way, you can be sure that the users have the latest stable official version. udict argument is a dictionary returned by CheckForUpdates() function. If it is 0 (default) the CheckForUpdates() will be called automatically. Firstly, the update() function, tries to make a backup of important files, before it starts updating them. If something went wrong while backing up, the error will not be raised. After making backup (ZIP file named decoder-__version__-backup.zip) update() tries to download ZIP file and unpack it. The existing files will be replaced with new ones. This function may, also, add some new files and so be careful when you are using it. Keep decoder.py isolated from other parts of your programs and everything will be fine. The updated version will be active when you start your program again. If you/your program discover that new version makes problem, you can easily rollback to the old one using restore() function. You should try the new version right away, and rollback if something is not working, because you cannot be sure if you would be able to access the restore() function after importing a new version. However, it is unlikely that you would ever need to use restore() function. 'decoder.py' is strictly backward compatible and heavily tested for bugs. update() returns 0 if it fails in any sense, -1, if there is no new version available, and 1 if everything went fine. This function requires write permissions on current working directory of decoder.py. WARNING:

It may happen, that someone redirects your DNS to fool your system and tricks it to download the files from another location, pretending to be brailleweb.com. The downloaded files may contain malicious code and harm your system. I hope that this warning would not give somebody an idea.
restore (v=None) --> None
Rolls back the decoder.py from the backup file. If you ran the new version and want to restore it to the previous one, you need to specify the version of previous version. If you test the decoder.py and you want to restore to previous one, the restore() will be enough. The function tries to achieve its goal, but does not raise an error if there is one. The adequate backup file 'decoder-__version__-backup.zip' needs to be present in the directory i.e. the update() must be called first.

CLASSES:

Error --> Exception raised when something is wrong with external decoder's output

fakewave --> Instance returned by open() function if compressed audio file given

copywaveobj --> Instance that copies information from one wave object to another.
__init__(self, src, dst, start=None, end=None, bufsize=1024, blocking=True) --> None
Constructor that initialises copying information from one wave object to another. I.e. from Wave_read/like-Wave_read object to Wave_write/like-Wave_write object. This method also supports copying from file to file and mixed copying: from wave to file and vice versa, but then, you must have in mind that file-like.read() and file-like.seek() differs from wave-like.readframes() and wave-like.setpos(). If you use the file-like/wave-like object that has both readframes() and read() or writeframes() and write() or setpos() and seek() methods, then, this function will mess the objects pretty badly. So be careful. The start argument sets the source object to offset of frames/bytes from which to start copying. If you use fakewave object as a source, do not use this, because it will raise the Illegal seek error. End is the offset of frames/bytes after which the copying will stop. Bufsize is, of course, the number of frames/bytes copied per one iteration. Blocking is the mode of copying information. If it is True, then instance acts like a structural function and you need to wait until the copying is done. Else, the copying is launched in a thread, and you can control it using below described methods.
begin(self) --> None
A method that really does the copying of data. If you call it, and it is already started, nothing will happen. If the process is paused, and you call it, the copying will resume. If you call it after calling the stop() method, the copying will start again (from the beginning), but in structural manner - you will have to wait till the end. If you want to restart the unblocking way of copying, then do: a = copywaveobj(...); a.thread(a.begin, ())
pause(self) --> None
Freezes the copying (unblocking mode).
resume(self) --> None
Resumes the copying (Unblocking mode). If copying is not paused, no problems!
stop(self) --> None
Stops the copying (unblocking mode).
status(self) --> int (1; -1; 0)
Returns -1, when copying is paused, 1 when it is in progress, and 0 when it is stopped.
tell(self) --> int
Returns the number of finished iterations while copying. The approximate number of copied frames (wave-like) or bytes (file-like) is: a = copywaveobj(...); print a.bufsize*a.tell()
wait(self) --> None
Waits until copying STOPS (unblocking mode).
VARIABLES:
wildcard --> A list containing all extensions that open() supports.
When imported with 'from', it is called "decoder_wildcard"

DEPENDENCIES:

decoder.py depends on mutagen library - to calculate the number of frames and create WAVE headers.

In order to make it work (decoder.py), you need external decoders: lame, faad, flac, oggdec, wmadec (on Windows) and ffmpeg (on Unix). To install them on Ubuntu/Debian use: apt-get install lame apt-get install vorbis-tools apt-get install faad apt-get install flac apt-get install ffmpeg

For Windows: As I hate searching for dependencies for years and then finding the wrong ones, you will get them along in the same ZIP as decoder.py. But better be warned that there might be newer versions somewhere on the internet. That especially goes for "wmadec" which is still buggy.

In the "codecs.pdc" file you should put commands for external decoders. How the system reaches them.

Unix common configuration: lame=lame faad=faad flac=flac ffmpeg=ffmpeg oggdec=oggdec wmadec=None

Windows preferable configuration: lame=./codecs/lame.exe faad=./codecs/faad.exe flac=./codecs/flac.exe ffmpeg=None oggdec=./codecs/oggdec.exe wmadec=./codecs/wmadec.exe

If the "./codecs.pdc" does not exist, the above mentioned defaults will be used.

TEST:

decoder.py is tested on: Ubuntu Linux, Windows 98 SE, Windows XP and Windows 7

TODO:

When you use decoder.py on Windows 98, the console window opens regularly regardless the CREATE_NO_WINDOW creation flag. This is Windows 9x natural behaviour, but I am sure the way around must exists. I would be thankful for any help regarding this "problem".