Created
November 6, 2013 17:22
-
-
Save davidblewett/7340369 to your computer and use it in GitHub Desktop.
Simple HTTP FLAC streamer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from hashlib import md5 | |
from audiotools.bitstream import BitstreamReader | |
from audiotools.flac import FlacAudio | |
from audiotools.flac import InvalidFLAC | |
from audiotools.id3 import skip_id3v2_comment | |
from audiotools.py_decoders import FlacDecoder | |
from audiotools.text import ERR_FLAC_INVALID_BLOCK | |
import requests | |
class StreamingFlacDecoder(FlacDecoder): | |
def __init__(self, url, channel_mask): | |
response = requests.get(url, stream=True) | |
self.reader = BitstreamReader(response.raw, 0) | |
if (self.reader.read_bytes(4) != 'fLaC'): | |
raise ValueError("invalid FLAC file") | |
self.current_md5sum = md5() | |
#locate the STREAMINFO, | |
#which is sometimes needed to handle non-subset streams | |
for (block_id, | |
block_size, | |
block_reader) in self.metadata_blocks(self.reader): | |
logger.info('Block info: %s, %d', block_id, block_size) | |
if (block_id == 0): | |
#read STREAMINFO | |
self.minimum_block_size = block_reader.read(16) | |
self.maximum_block_size = block_reader.read(16) | |
self.minimum_frame_size = block_reader.read(24) | |
self.maximum_frame_size = block_reader.read(24) | |
self.sample_rate = block_reader.read(20) | |
self.channels = block_reader.read(3) + 1 | |
self.channel_mask = channel_mask | |
self.bits_per_sample = block_reader.read(5) + 1 | |
self.total_frames = block_reader.read64(36) | |
self.md5sum = block_reader.read_bytes(16) | |
#these are frame header lookup tables | |
#which vary slightly depending on STREAMINFO's values | |
self.BLOCK_SIZE = [self.maximum_block_size, | |
192, 576, 1152, | |
2304, 4608, None, None, | |
256, 512, 1024, 2048, | |
4096, 8192, 16384, 32768] | |
self.SAMPLE_RATE = [self.sample_rate, | |
88200, 176400, 192000, | |
8000, 16000, 22050, 24000, | |
32000, 44100, 48000, 96000, | |
None, None, None, None] | |
self.BITS_PER_SAMPLE = [self.bits_per_sample, | |
8, 12, None, 16, 20, 24, None] | |
class StreamingFlacAudio(FlacAudio): | |
def __init__(self, url): | |
"""url is a plain string""" | |
FlacAudio.__init__(self, url) | |
# FIXME: use metadata from API | |
self.__channels__ = 2 | |
self.__samplerate__ = 44100 | |
def __read_streaminfo__(self): | |
pass | |
def to_pcm(self): | |
"""returns a PCMReader object containing the track's PCM data""" | |
from audiotools import PCMReaderError | |
try: | |
return StreamingFlacDecoder(self.filename, | |
self.channel_mask()) | |
except (IOError, ValueError), msg: | |
#The only time this is likely to occur is | |
#if the FLAC is modified between when FlacAudio | |
#is initialized and when to_pcm() is called. | |
return PCMReaderError(error_message=str(msg), | |
sample_rate=self.sample_rate(), | |
channels=self.channels(), | |
channel_mask=int(self.channel_mask()), | |
bits_per_sample=self.bits_per_sample()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment