Permalink
Browse files

simple pymad wrapper

  • Loading branch information...
1 parent 3c70565 commit 88bd48f81b667c6457570581e2514018e85b6e2c @sampsyo sampsyo committed Nov 9, 2011
Showing with 82 additions and 3 deletions.
  1. +13 −3 README.md
  2. +12 −0 audioread/__init__.py
  3. +57 −0 audioread/maddec.py
View
@@ -6,11 +6,14 @@ supports:
* [Gstreamer][] via [gst-python][].
* [Core Audio][] on Mac OS X via [ctypes][]. (PyObjC not required.)
+* [MAD][] via the [pymad][] bindings.
[Gstreamer]: http://gstreamer.freedesktop.org/
[gst-python]: http://gstreamer.freedesktop.org/modules/gst-python.html
[Core Audio]: http://developer.apple.com/technologies/mac/audio-and-video.html
[ctypes]: http://docs.python.org/library/ctypes.html
+[pymad]: http://spacepants.org/src/pymad/
+[MAD]: http://www.underbit.com/products/mad/
Use the library like so:
@@ -29,8 +32,15 @@ Additional values are available as fields on the audio file object:
* `samplerate` is given in Hz (an integer).
* `duration` is the length of the audio in seconds (a float).
-I'd also like to add these backends in the future:
+The library currently only selects the backend based on which supporting
+libraries are available. In the future, it should "fall back" to a different
+library when one decoder fails -- for instance, when one library supports a
+certain audio format but another does not. I'd also like to add these backends
+in the future:
-* MAD via pymad.
* FFmpeg via the command line tools.
-* Mplayer in a similar way?
+* PyOgg?
+
+An alternative to this module is [decoder.py][].
+
+[decoder.py]: http://www.brailleweb.com/cgi-bin/python.py
View
@@ -17,6 +17,15 @@ def _ca_available():
lib = ctypes.util.find_library('AudioToolbox')
return lib is not None
+def _mad_available():
+ """Determines whether the pymad bindings are available."""
+ try:
+ import mad
+ except ImportError:
+ return False
+ else:
+ return True
+
def audio_open(path):
"""Open an audio file using a library that is available on this
system.
@@ -27,3 +36,6 @@ def audio_open(path):
elif _gst_available():
from . import gstdec
return gstdec.GstAudioFile(path)
+ elif _mad_available():
+ from . import maddec
+ return maddec.MadAudioFile(path)
View
@@ -0,0 +1,57 @@
+"""Decode MPEG audio files with MAD (via pymad)."""
+import mad
+
+class MadAudioFile(object):
+ """MPEG audio file decoder using the MAD library."""
+ def __init__(self, filename):
+ self.mf = mad.MadFile(filename)
+
+ def close(self):
+ if hasattr(self, 'mf'):
+ del self.mf
+
+ def read_blocks(self, block_size=4096):
+ """Generates buffers containing PCM data for the audio file.
+ """
+ while True:
+ out = self.mf.read(block_size)
+ if not out:
+ break
+ yield out
+
+ @property
+ def samplerate(self):
+ """Sample rate in Hz."""
+ return self.mf.samplerate()
+
+ @property
+ def duration(self):
+ """Length of the audio in seconds (a float)."""
+ return float(self.mf.total_time()) / 1000
+
+ @property
+ def channels(self):
+ """The number of channels."""
+ if self.mf.mode() == mad.MODE_SINGLE_CHANNEL:
+ return 1
+ elif self.mf.mode() in (mad.MODE_DUAL_CHANNEL,
+ mad.MODE_JOINT_STEREO,
+ mad.MODE_STEREO):
+ return 2
+ else:
+ # Other mode?
+ return 2
+
+ def __del__(self):
+ self.close()
+
+ # Iteration.
+ def __iter__(self):
+ return self.read_blocks()
+
+ # Context manager.
+ def __enter__(self):
+ return self
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.close()
+ return False

0 comments on commit 88bd48f

Please sign in to comment.