Source code for hanyuu.streamer.audio.files

"""Module that handles file access and decoding to PCM.

It uses python-audiotools for the majority of the work done."""
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import absolute_import

from . import garbage
import audiotools


[docs]class AudioError(Exception): """Exception raised when an error occurs in this module.""" pass
[docs]class GarbageAudioFile(garbage.Garbage): """Garbage class of the AudioFile class"""
[docs] def collect(self): """Tries to close the AudioFile resources when called.""" try: self.item._reader.close() except (audiotools.DecodingError): pass # Hack to kill zombies below import gc, subprocess try: [item.poll() for item in gc.get_referrers(subprocess.Popen) if isinstance(item, subprocess.Popen)] except: logger.warning("Exception occured in hack.") # Hack to kill zombies above return True # TODO: Add handler hooks.
[docs]class FileSource(object): """ ====== Source ====== The :class:`FileSource` class expects a function as source. This function should return an absolute path to a supported audio file as an :const:`unicode` or :const:`bytes` object. ======= Options ======= No options are supported for this class. ======== Handlers ======== No handlers are supported for this class. """ def __init__(self, source, options, handlers): super(FileSource, self).__init__() self.source = None self.source_function = source self.options = options self.handler = handlers self.eof = False
[docs] def start(self): """Starts the source""" self.eof = False self.source = self.change_source()
[docs] def initialize(self): """Sets the initial source from the source function.""" self.start()
[docs] def change_source(self): """Calls the source function and returns the result if not None.""" if not (self.source is None): self.source.close() filename = self.source_function() if filename is None: return try: audiofile = AudioFile(filename) except (files.AudioError) as err: logger.exception("Unsupported file.") return self.change_source() except (IOError) as err: logger.exception("Failed opening file.") return self.change_source() else: return audiofile
[docs] def read(self, size=4096, timeout=10.0): if self.eof: return b'' try: data = self.source.read(size, timeout) except (ValueError) as err: if err.message == 'MD5 mismatch at end of stream': data = b'' if data == b'': self.source = self.change_source() if self.source == None: self.eof = True return b'' return data
[docs] def skip(self): self.source = self.change_source()
[docs] def close(self): self.eof = True
def __getattr__(self, key): return getattr(self.source, key)
[docs]class AudioFile(object): """A Simple wrapper around the audiotools library. This opens the filename given wraps the file in a PCMConverter that turns it into PCM of format 44.1kHz, Stereo, 24-bit depth.""" def __init__(self, filename): super(AudioFile, self).__init__() self._reader = self._open_file(filename)
[docs] def read(self, size=4096, timeout=0.0): """Returns at most a string of size `size`. The `timeout` argument is unused. But kept in for compatibility with other read methods in the `audio` module.""" return self._reader.read(size).to_bytes(False, True)
[docs] def close(self): """Registers self for garbage collection. This method does not close anything and only registers itself for colleciton.""" GarbageAudioFile(self)
def __getattr__(self, key): try: return getattr(self._reader, key) except (AttributeError): return getattr(self.file, key)
[docs] def progress(self, current, total): """Dummy progress function""" pass
def _open_file(self, filename): """Open a file for reading and wrap it in several helpers.""" try: reader = audiotools.open(filename) except (audiotools.UnsupportedFile) as err: raise AudioError("Unsupported file") self.file = reader total_frames = reader.total_frames() # Wrap in a PCMReader because we want PCM reader = reader.to_pcm() # Wrap in a converter reader = audiotools.PCMConverter(reader, sample_rate=44100, channels=2, channel_mask=audiotools.ChannelMask(0x1 | 0x2), bits_per_sample=24) # And for file progress! reader = audiotools.PCMReaderProgress(reader, total_frames, self.progress) return reader

Project Versions

This Page