716 lines
25 KiB
Python
716 lines
25 KiB
Python
#!/usr/bin/python
|
|
|
|
#Audio Tools, a module and set of tools for manipulating audio data
|
|
#Copyright (C) 2007-2011 Brian Langenberger
|
|
|
|
#This program is free software; you can redistribute it and/or modify
|
|
#it under the terms of the GNU General Public License as published by
|
|
#the Free Software Foundation; either version 2 of the License, or
|
|
#(at your option) any later version.
|
|
|
|
#This program is distributed in the hope that it will be useful,
|
|
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
#GNU General Public License for more details.
|
|
|
|
#You should have received a copy of the GNU General Public License
|
|
#along with this program; if not, write to the Free Software
|
|
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
import array
|
|
import audiotools
|
|
import sys,cStringIO
|
|
|
|
Con = audiotools.Con
|
|
|
|
class UTF8(Con.Struct):
|
|
@classmethod
|
|
def __total_utf8_bytes__(cls, header):
|
|
total = 0
|
|
for b in header:
|
|
if b == '\x01':
|
|
total += 1
|
|
else:
|
|
break
|
|
return max(1,total)
|
|
|
|
@classmethod
|
|
def __calculate_utf8_value__(cls, ctx):
|
|
import operator
|
|
|
|
return Con.lib.bin_to_int(ctx.header[ctx.header.index('\x00') + 1:] + \
|
|
reduce(operator.concat,
|
|
[s[2:] for s in ctx['sub_byte']],
|
|
''))
|
|
|
|
def __init__(self, name):
|
|
Con.Struct.__init__(
|
|
self,name,
|
|
Con.Bytes('header',8),
|
|
Con.Value('total_bytes',
|
|
lambda ctx: self.__total_utf8_bytes__(ctx['header'])),
|
|
Con.MetaRepeater(
|
|
lambda ctx: self.__total_utf8_bytes__(ctx['header']) - 1,
|
|
Con.Bytes('sub_byte',8)),
|
|
Con.Value('value',
|
|
lambda ctx: self.__calculate_utf8_value__(ctx)))
|
|
|
|
class Unary(Con.Adapter):
|
|
def __init__(self, name):
|
|
Con.Adapter.__init__(
|
|
self,
|
|
Con.RepeatUntil(lambda obj,ctx: obj == 1,
|
|
Con.Byte(name)))
|
|
|
|
def _encode(self, value, context):
|
|
if (value > 0):
|
|
return ([0] * (value)) + [1]
|
|
else:
|
|
return [1]
|
|
|
|
def _decode(self, obj, context):
|
|
return len(obj) - 1
|
|
|
|
class PlusOne(Con.Adapter):
|
|
def _encode(self, value, context):
|
|
return value - 1
|
|
|
|
def _decode(self, obj, context):
|
|
return obj + 1
|
|
|
|
class FlacStreamException(Exception): pass
|
|
|
|
class FlacReader:
|
|
FRAME_HEADER = Con.Struct('frame_header',
|
|
Con.Bits('sync',14),
|
|
Con.Bits('reserved',2),
|
|
Con.Bits('block_size',4),
|
|
Con.Bits('sample_rate',4),
|
|
Con.Bits('channel_assignment',4),
|
|
Con.Bits('bits_per_sample',3),
|
|
Con.Padding(1),
|
|
Con.IfThenElse(
|
|
'total_channels',
|
|
lambda ctx1: ctx1['channel_assignment'] <= 7,
|
|
Con.Value('c',lambda ctx2: ctx2['channel_assignment'] + 1),
|
|
Con.Value('c',lambda ctx3: 2)),
|
|
|
|
UTF8('frame_number'),
|
|
|
|
Con.IfThenElse(
|
|
'extended_block_size',
|
|
lambda ctx1: ctx1['block_size'] == 6,
|
|
Con.Bits('b',8),
|
|
Con.If(lambda ctx2: ctx2['block_size'] == 7,
|
|
Con.Bits('b',16))),
|
|
|
|
Con.IfThenElse(
|
|
'extended_sample_rate',
|
|
lambda ctx1: ctx1['sample_rate'] == 12,
|
|
Con.Bits('s',8),
|
|
Con.If(lambda ctx2: ctx2['sample_rate'] in (13,14),
|
|
Con.Bits('s',16))),
|
|
|
|
Con.Bits('crc8',8))
|
|
|
|
UNARY = Con.Struct('unary',
|
|
Con.RepeatUntil(
|
|
lambda obj,ctx: obj == '\x01',
|
|
Con.Field('bytes',1)),
|
|
Con.Value('value',
|
|
lambda ctx: len(ctx['bytes']) - 1)
|
|
)
|
|
|
|
SUBFRAME_HEADER = Con.Struct('subframe_header',
|
|
Con.Padding(1),
|
|
Con.Bits('subframe_type',6),
|
|
Con.Flag('has_wasted_bits_per_sample'),
|
|
Con.IfThenElse(
|
|
'wasted_bits_per_sample',
|
|
lambda ctx: ctx['has_wasted_bits_per_sample'],
|
|
PlusOne(Unary('value')),
|
|
Con.Value('value',lambda ctx2: 0)))
|
|
|
|
|
|
GET_BLOCKSIZE_FROM_STREAMINFO = -1
|
|
GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER = -2
|
|
GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER = -3
|
|
|
|
BLOCK_SIZE = (GET_BLOCKSIZE_FROM_STREAMINFO,
|
|
192,
|
|
576,1152,2304,4608,
|
|
GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER,
|
|
GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER,
|
|
256,512,1024,2048,4096,8192,16384,32768)
|
|
|
|
GET_SAMPLE_SIZE_FROM_STREAMINFO = -1
|
|
SAMPLE_SIZE = (GET_SAMPLE_SIZE_FROM_STREAMINFO,
|
|
8,12,None,16,20,24,None)
|
|
|
|
def FIXED0(subframe,residual,i):
|
|
subframe.insert(i,
|
|
residual[i])
|
|
|
|
def FIXED1(subframe,residual,i):
|
|
subframe.insert(i,
|
|
subframe[i - 1] + residual[i])
|
|
|
|
def FIXED2(subframe,residual,i):
|
|
subframe.insert(i,
|
|
((2 * subframe[i - 1]) - subframe[i - 2] + \
|
|
residual[i]))
|
|
|
|
def FIXED3(subframe,residual,i):
|
|
subframe.insert(i,
|
|
((3 * subframe[i - 1]) - (3 * subframe[i - 2]) + \
|
|
subframe[i - 3] + residual[i]))
|
|
|
|
def FIXED4(subframe,residual,i):
|
|
subframe.insert(i,
|
|
((4 * subframe[i - 1]) - (6 * subframe[i - 2]) + \
|
|
(4 * subframe[i - 3]) - subframe[i - 4] + residual[i]))
|
|
|
|
#iterates over all of the channels, in order
|
|
def MERGE_INDEPENDENT(channel_list):
|
|
channel_data = [iter(c) for c in channel_list]
|
|
|
|
while (True):
|
|
for channel in channel_data:
|
|
yield channel.next()
|
|
|
|
def MERGE_LEFT(channel_list):
|
|
channel_left = iter(channel_list[0])
|
|
channel_side = iter(channel_list[1])
|
|
|
|
while (True):
|
|
left = channel_left.next()
|
|
side = channel_side.next()
|
|
|
|
yield left
|
|
yield left - side
|
|
|
|
|
|
def MERGE_RIGHT(channel_list):
|
|
channel_side = iter(channel_list[0])
|
|
channel_right = iter(channel_list[1])
|
|
|
|
while (True):
|
|
side = channel_side.next()
|
|
right = channel_right.next()
|
|
|
|
yield side + right
|
|
yield right
|
|
|
|
def MERGE_MID(channel_list):
|
|
channel_mid = iter(channel_list[0])
|
|
channel_side = iter(channel_list[1])
|
|
|
|
while (True):
|
|
mid = channel_mid.next()
|
|
side = channel_side.next()
|
|
|
|
mid = mid << 1
|
|
mid |= (side & 0x1)
|
|
|
|
yield (mid + side) >> 1
|
|
yield (mid - side) >> 1
|
|
|
|
|
|
CHANNEL_FUNCTIONS = (MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_INDEPENDENT,
|
|
MERGE_LEFT,
|
|
MERGE_RIGHT,
|
|
MERGE_MID)
|
|
|
|
FIXED_FUNCTIONS = (FIXED0,FIXED1,FIXED2,FIXED3,FIXED4)
|
|
|
|
def __init__(self, flac_stream):
|
|
self.stream = BufferedStream(flac_stream)
|
|
self.streaminfo = None
|
|
self.bitstream = None
|
|
|
|
#ensure the file starts with 'fLaC'
|
|
self.read_stream_marker()
|
|
|
|
#initialize self.bitstream
|
|
self.begin_bitstream()
|
|
|
|
#find self.streaminfo in case we need it
|
|
self.read_metadata_blocks()
|
|
|
|
def close(self):
|
|
if (self.bitstream != None):
|
|
self.bitstream.close()
|
|
else:
|
|
self.stream.close()
|
|
|
|
|
|
def read_stream_marker(self):
|
|
if (self.stream.read(4) != 'fLaC'):
|
|
raise FlacStreamException('invalid stream marker')
|
|
|
|
def read_metadata_blocks(self):
|
|
block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream(self.stream)
|
|
while (block.last_block == 0):
|
|
if (block.block_type == 0):
|
|
self.streaminfo = audiotools.FlacAudio.STREAMINFO.parse_stream(self.stream)
|
|
else:
|
|
self.stream.seek(block.block_length,1)
|
|
|
|
block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream(self.stream)
|
|
self.stream.seek(block.block_length,1)
|
|
|
|
def begin_bitstream(self):
|
|
import bitstream
|
|
|
|
#self.bitstream = Con.BitStreamReader(self.stream)
|
|
self.bitstream = bitstream.BitStreamReader(self.stream)
|
|
|
|
def read_frame(self):
|
|
self.stream.reset_buffer()
|
|
|
|
try:
|
|
header = FlacReader.FRAME_HEADER.parse_stream(self.bitstream)
|
|
except Con.core.FieldError:
|
|
return ""
|
|
|
|
if (header.sync != 0x3FFE):
|
|
raise FlacStreamException('invalid sync')
|
|
|
|
if (crc8(self.stream.getvalue()[0:-1]) != header.crc8):
|
|
raise FlacStreamException('crc8 checksum failed')
|
|
|
|
|
|
#block_size tells us how many samples we need from each subframe
|
|
block_size = FlacReader.BLOCK_SIZE[header.block_size]
|
|
if (block_size == self.GET_BLOCKSIZE_FROM_STREAMINFO):
|
|
block_size = self.streaminfo.maximum_blocksize
|
|
|
|
elif ((block_size == self.GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER) or
|
|
(block_size == self.GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER)):
|
|
block_size = header.extended_block_size + 1
|
|
|
|
|
|
#grab subframe data as 32-bit array objects
|
|
subframe_data = []
|
|
|
|
for channel_number in xrange(header.total_channels):
|
|
subframe_data.append(
|
|
self.read_subframe(header, block_size, channel_number))
|
|
|
|
crc16sum = crc16(self.stream.getvalue())
|
|
|
|
|
|
#try to byte-align the stream
|
|
if (len(self.bitstream.buffer) > 0):
|
|
self.bitstream.read(len(self.bitstream.buffer))
|
|
|
|
|
|
if (crc16sum != Con.Bits('crc16',16).parse_stream(self.bitstream)):
|
|
raise FlacStreamException('crc16 checksum failed')
|
|
|
|
|
|
#convert our list of subframe data arrays into
|
|
#a string of sample data
|
|
if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 16):
|
|
merged_frames = array.array('h',
|
|
FlacReader.CHANNEL_FUNCTIONS[
|
|
header.channel_assignment](subframe_data))
|
|
|
|
if (audiotools.BIG_ENDIAN):
|
|
merged_frames.byteswap()
|
|
|
|
return merged_frames.tostring()
|
|
|
|
elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 8):
|
|
merged_frames = array.array('b',
|
|
FlacReader.CHANNEL_FUNCTIONS[
|
|
header.channel_assignment](subframe_data))
|
|
|
|
return merged_frames.tostring()
|
|
|
|
else:
|
|
if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == \
|
|
self.GET_SAMPLE_SIZE_FROM_STREAMINFO):
|
|
bits_per_sample = self.streaminfo.bits_per_sample + 1
|
|
|
|
elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == None):
|
|
raise FlacStreamException('invalid bits per sample')
|
|
|
|
else:
|
|
bits_per_sample = FlacReader.SAMPLE_SIZE[header.bits_per_sample]
|
|
|
|
stream = Con.GreedyRepeater(
|
|
Con.BitStruct('bits',
|
|
Con.Bits('value',bits_per_sample,
|
|
swapped=True,signed=True)))
|
|
|
|
return stream.build(
|
|
[Con.Container(value=v) for v in
|
|
FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment](
|
|
subframe_data)])
|
|
|
|
|
|
|
|
def read_subframe(self, frame_header, block_size, channel_number):
|
|
subframe_header = \
|
|
FlacReader.SUBFRAME_HEADER.parse_stream(self.bitstream)
|
|
|
|
#figure out the bits-per-sample of this subframe
|
|
if ((frame_header.channel_assignment == 8) and
|
|
(channel_number == 1)):
|
|
#if channel is stored as left+difference
|
|
#and this is the difference, add 1 bit
|
|
bits_per_sample = FlacReader.SAMPLE_SIZE[
|
|
frame_header.bits_per_sample] + 1
|
|
|
|
elif ((frame_header.channel_assignment == 9) and
|
|
(channel_number == 0)):
|
|
#if channel is stored as difference+right
|
|
#and this is the difference, add 1 bit
|
|
bits_per_sample = FlacReader.SAMPLE_SIZE[
|
|
frame_header.bits_per_sample] + 1
|
|
|
|
elif ((frame_header.channel_assignment == 10) and
|
|
(channel_number == 1)):
|
|
#if channel is stored as average+difference
|
|
#and this is the difference, add 1 bit
|
|
bits_per_sample = FlacReader.SAMPLE_SIZE[
|
|
frame_header.bits_per_sample] + 1
|
|
|
|
else:
|
|
#otherwise, use the number from the frame header
|
|
bits_per_sample = FlacReader.SAMPLE_SIZE[
|
|
frame_header.bits_per_sample]
|
|
|
|
|
|
if (subframe_header.has_wasted_bits_per_sample):
|
|
bits_per_sample -= subframe_header.wasted_bits_per_sample
|
|
|
|
if (subframe_header.subframe_type == 0):
|
|
subframe = self.read_subframe_constant(block_size, bits_per_sample)
|
|
|
|
elif (subframe_header.subframe_type == 1):
|
|
subframe = self.read_subframe_verbatim(block_size, bits_per_sample)
|
|
|
|
elif ((subframe_header.subframe_type & 0x38) == 0x08):
|
|
subframe = self.read_subframe_fixed(
|
|
subframe_header.subframe_type & 0x07,
|
|
block_size,
|
|
bits_per_sample)
|
|
|
|
elif ((subframe_header.subframe_type & 0x20) == 0x20):
|
|
subframe = self.read_subframe_lpc(
|
|
(subframe_header.subframe_type & 0x1F) + 1,
|
|
block_size,
|
|
bits_per_sample)
|
|
|
|
else:
|
|
raise FlacStreamException('invalid subframe type')
|
|
|
|
if (subframe_header.has_wasted_bits_per_sample):
|
|
return array.array(
|
|
'i',
|
|
[i << subframe_header.wasted_bits_per_sample
|
|
for i in subframe])
|
|
else:
|
|
return subframe
|
|
|
|
def read_subframe_constant(self, block_size, bits_per_sample):
|
|
sample = Con.Bits('b',bits_per_sample).parse_stream(
|
|
self.bitstream)
|
|
|
|
subframe = array.array('i',[sample] * block_size)
|
|
|
|
return subframe
|
|
|
|
|
|
def read_subframe_verbatim(self, block_size, bits_per_sample):
|
|
return array.array('i',
|
|
Con.StrictRepeater(
|
|
block_size,
|
|
Con.Bits("samples",
|
|
bits_per_sample,
|
|
signed=True)).parse_stream(self.bitstream))
|
|
|
|
|
|
def read_subframe_fixed(self, order, block_size, bits_per_sample):
|
|
samples = Con.StrictRepeater(
|
|
order,
|
|
Con.Bits("warm_up_samples",
|
|
bits_per_sample,
|
|
signed=True))
|
|
|
|
subframe = array.array('i',
|
|
samples.parse_stream(self.bitstream))
|
|
|
|
residual = self.read_residual(block_size,order)
|
|
|
|
fixed_func = self.FIXED_FUNCTIONS[order]
|
|
|
|
for i in xrange(len(subframe),block_size):
|
|
fixed_func(subframe,residual,i)
|
|
|
|
return subframe
|
|
|
|
|
|
def read_subframe_lpc(self, order, block_size, bits_per_sample):
|
|
samples = Con.StrictRepeater(
|
|
order,
|
|
Con.Bits("warm_up_samples",
|
|
bits_per_sample,
|
|
signed=True))
|
|
|
|
subframe = array.array('i',
|
|
samples.parse_stream(self.bitstream))
|
|
|
|
lpc_precision = Con.Bits('lpc_precision',
|
|
4).parse_stream(self.bitstream) + 1
|
|
|
|
lpc_shift = Con.Bits('lpc_shift',
|
|
5).parse_stream(self.bitstream)
|
|
|
|
coefficients = array.array('i',
|
|
Con.StrictRepeater(
|
|
order,
|
|
Con.Bits('coefficients',
|
|
lpc_precision,
|
|
signed=True)).parse_stream(self.bitstream))
|
|
|
|
residual = self.read_residual(block_size, order)
|
|
|
|
for i in xrange(len(subframe),block_size):
|
|
subframe.insert(i,
|
|
(sum(
|
|
[coefficients[j] * subframe[i - j - 1] for j in
|
|
xrange(0,len(coefficients))]) >> lpc_shift) + \
|
|
residual[i])
|
|
|
|
return subframe
|
|
|
|
|
|
def read_residual(self, block_size, predictor_order):
|
|
rice = array.array('i')
|
|
|
|
#add some dummy rice so that the Rice index matches
|
|
#that of the rest of the subframe
|
|
for i in xrange(predictor_order):
|
|
rice.append(0)
|
|
|
|
coding_method = self.bitstream.read(2)
|
|
if (coding_method == '\x00\x00'):
|
|
rice2 = False
|
|
elif (coding_method == '\x00\x01'):
|
|
rice2 = True
|
|
else:
|
|
raise FlacStreamException('invalid residual coding method')
|
|
|
|
partition_order = Con.Bits('partition_order',4).parse_stream(
|
|
self.bitstream)
|
|
|
|
if (partition_order > 0):
|
|
total_samples = ((block_size / 2 ** partition_order) -
|
|
predictor_order)
|
|
rice.extend(self.read_encoded_rice(total_samples,rice2))
|
|
|
|
for i in xrange(1,2 ** partition_order):
|
|
total_samples = (block_size / 2 ** partition_order)
|
|
|
|
rice.extend(self.read_encoded_rice(total_samples,rice2))
|
|
else:
|
|
rice.extend(self.read_encoded_rice(block_size - predictor_order,
|
|
rice2))
|
|
|
|
return rice
|
|
|
|
|
|
def read_encoded_rice(self, total_samples, rice2=False):
|
|
bin_to_int = Con.lib.binary.bin_to_int
|
|
|
|
samples = array.array('i')
|
|
|
|
if (not rice2):
|
|
rice_parameter = Con.Bits('rice_parameter',4).parse_stream(
|
|
self.bitstream)
|
|
else:
|
|
rice_parameter = Con.Bits('rice_parameter',5).parse_stream(
|
|
self.bitstream)
|
|
|
|
if (rice_parameter != 0xF):
|
|
#a Rice encoded residual
|
|
for x in xrange(total_samples):
|
|
|
|
#count the number of 0 bits before the next 1 bit
|
|
#(unary encoding)
|
|
#to find our most significant bits
|
|
msb = 0
|
|
s = self.bitstream.read(1)
|
|
while (s != '\x01'):
|
|
msb += 1
|
|
s = self.bitstream.read(1)
|
|
|
|
#grab the proper number of least significant bits
|
|
lsb = bin_to_int(self.bitstream.read(rice_parameter))
|
|
|
|
#combine msb and lsb to get the Rice-encoded value
|
|
value = (msb << rice_parameter) | lsb
|
|
if ((value & 0x1) == 0x1): #negative
|
|
samples.append(-(value >> 1) - 1)
|
|
else: #positive
|
|
samples.append(value >> 1)
|
|
else:
|
|
#unencoded residual
|
|
|
|
bits_per_sample = Con.Bits('escape_code',5).parse_stream(
|
|
self.bitstream)
|
|
|
|
sample = Con.Bits("sample",bits_per_sample,signed=True)
|
|
|
|
for x in xrange(total_samples):
|
|
samples.append(sample.parse_stream(self.bitstream))
|
|
|
|
return samples
|
|
|
|
|
|
###############################
|
|
#Checksum calculation functions
|
|
###############################
|
|
|
|
CRC8TABLE = (0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
|
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
|
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
|
|
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
|
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
|
|
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
|
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
|
|
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
|
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
|
|
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
|
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
|
|
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
|
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
|
|
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
|
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
|
|
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
|
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
|
|
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
|
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
|
|
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
|
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
|
|
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
|
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
|
|
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
|
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
|
|
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
|
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
|
|
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
|
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
|
|
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
|
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
|
|
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3)
|
|
|
|
def crc8(data, start=0):
|
|
value = start
|
|
|
|
for i in map(ord,data):
|
|
value = CRC8TABLE[value ^ i]
|
|
|
|
return value
|
|
|
|
CRC16TABLE = (0x0000,0x8005,0x800f,0x000a,0x801b,0x001e,0x0014,0x8011,
|
|
0x8033,0x0036,0x003c,0x8039,0x0028,0x802d,0x8027,0x0022,
|
|
0x8063,0x0066,0x006c,0x8069,0x0078,0x807d,0x8077,0x0072,
|
|
0x0050,0x8055,0x805f,0x005a,0x804b,0x004e,0x0044,0x8041,
|
|
0x80c3,0x00c6,0x00cc,0x80c9,0x00d8,0x80dd,0x80d7,0x00d2,
|
|
0x00f0,0x80f5,0x80ff,0x00fa,0x80eb,0x00ee,0x00e4,0x80e1,
|
|
0x00a0,0x80a5,0x80af,0x00aa,0x80bb,0x00be,0x00b4,0x80b1,
|
|
0x8093,0x0096,0x009c,0x8099,0x0088,0x808d,0x8087,0x0082,
|
|
0x8183,0x0186,0x018c,0x8189,0x0198,0x819d,0x8197,0x0192,
|
|
0x01b0,0x81b5,0x81bf,0x01ba,0x81ab,0x01ae,0x01a4,0x81a1,
|
|
0x01e0,0x81e5,0x81ef,0x01ea,0x81fb,0x01fe,0x01f4,0x81f1,
|
|
0x81d3,0x01d6,0x01dc,0x81d9,0x01c8,0x81cd,0x81c7,0x01c2,
|
|
0x0140,0x8145,0x814f,0x014a,0x815b,0x015e,0x0154,0x8151,
|
|
0x8173,0x0176,0x017c,0x8179,0x0168,0x816d,0x8167,0x0162,
|
|
0x8123,0x0126,0x012c,0x8129,0x0138,0x813d,0x8137,0x0132,
|
|
0x0110,0x8115,0x811f,0x011a,0x810b,0x010e,0x0104,0x8101,
|
|
0x8303,0x0306,0x030c,0x8309,0x0318,0x831d,0x8317,0x0312,
|
|
0x0330,0x8335,0x833f,0x033a,0x832b,0x032e,0x0324,0x8321,
|
|
0x0360,0x8365,0x836f,0x036a,0x837b,0x037e,0x0374,0x8371,
|
|
0x8353,0x0356,0x035c,0x8359,0x0348,0x834d,0x8347,0x0342,
|
|
0x03c0,0x83c5,0x83cf,0x03ca,0x83db,0x03de,0x03d4,0x83d1,
|
|
0x83f3,0x03f6,0x03fc,0x83f9,0x03e8,0x83ed,0x83e7,0x03e2,
|
|
0x83a3,0x03a6,0x03ac,0x83a9,0x03b8,0x83bd,0x83b7,0x03b2,
|
|
0x0390,0x8395,0x839f,0x039a,0x838b,0x038e,0x0384,0x8381,
|
|
0x0280,0x8285,0x828f,0x028a,0x829b,0x029e,0x0294,0x8291,
|
|
0x82b3,0x02b6,0x02bc,0x82b9,0x02a8,0x82ad,0x82a7,0x02a2,
|
|
0x82e3,0x02e6,0x02ec,0x82e9,0x02f8,0x82fd,0x82f7,0x02f2,
|
|
0x02d0,0x82d5,0x82df,0x02da,0x82cb,0x02ce,0x02c4,0x82c1,
|
|
0x8243,0x0246,0x024c,0x8249,0x0258,0x825d,0x8257,0x0252,
|
|
0x0270,0x8275,0x827f,0x027a,0x826b,0x026e,0x0264,0x8261,
|
|
0x0220,0x8225,0x822f,0x022a,0x823b,0x023e,0x0234,0x8231,
|
|
0x8213,0x0216,0x021c,0x8219,0x0208,0x820d,0x8207,0x0202)
|
|
|
|
def crc16(data, start=0):
|
|
value = start
|
|
|
|
for i in map(ord,data):
|
|
value = ((value << 8) ^ CRC16TABLE[(value >> 8) ^ i]) & 0xFFFF
|
|
|
|
return value
|
|
|
|
#BufferedStream stores the data that passes through read()
|
|
#so that checksums can be calculated from it.
|
|
#Be sure to reset the buffer as needed.
|
|
class BufferedStream:
|
|
def __init__(self, stream):
|
|
self.stream = stream
|
|
self.buffer = cStringIO.StringIO()
|
|
|
|
def read(self, count):
|
|
s = self.stream.read(count)
|
|
self.buffer.write(s)
|
|
return s
|
|
|
|
def seek(self, offset, whence=0):
|
|
self.stream.seek(offset,whence)
|
|
|
|
def tell(self):
|
|
return self.stream.tell()
|
|
|
|
def close(self):
|
|
self.stream.close()
|
|
|
|
def reset_buffer(self):
|
|
self.buffer.close()
|
|
self.buffer = cStringIO.StringIO()
|
|
|
|
def getvalue(self):
|
|
return self.buffer.getvalue()
|
|
|
|
|
|
class FlacPCMReader(audiotools.PCMReader):
|
|
#flac_file should be a file-like stream of FLAC data
|
|
def __init__(self, flac_file):
|
|
self.flacreader = FlacReader(flac_file)
|
|
self.sample_rate = self.flacreader.streaminfo.samplerate
|
|
self.channels = self.flacreader.streaminfo.channels + 1
|
|
self.bits_per_sample = self.flacreader.streaminfo.bits_per_sample + 1
|
|
self.process = None
|
|
|
|
self.buffer = []
|
|
|
|
#this won't return even close to the expected number of bytes
|
|
#(though that won't really break anything)
|
|
def read(self, bytes):
|
|
return self.flacreader.read_frame()
|
|
|
|
def close(self):
|
|
self.flacreader.close()
|
|
|