|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Decode MPEG audio files with MAD (via pymad).""" |
|
import mad |
|
|
|
from . import DecodeError |
|
from .base import AudioFile |
|
|
|
|
|
class UnsupportedError(DecodeError): |
|
"""The file is not readable by MAD.""" |
|
|
|
|
|
class MadAudioFile(AudioFile): |
|
"""MPEG audio file decoder using the MAD library.""" |
|
def __init__(self, filename): |
|
self.fp = open(filename, 'rb') |
|
self.mf = mad.MadFile(self.fp) |
|
if not self.mf.total_time(): |
|
self.fp.close() |
|
raise UnsupportedError() |
|
|
|
def close(self): |
|
if hasattr(self, 'fp'): |
|
self.fp.close() |
|
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 bytes(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: |
|
|
|
return 2 |
|
|
|
def __del__(self): |
|
self.close() |
|
|
|
|
|
def __iter__(self): |
|
return self.read_blocks() |
|
|
|
|
|
def __enter__(self): |
|
return self |
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb): |
|
self.close() |
|
return False |
|
|