class FlacDecoder: CHANNEL_COUNT = [1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2, None, None, None, None, None] (SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED, SUBFRAME_LPC) = range(4) def __init__(self, filename, channel_mask): self.reader = BitstreamReader(open(filename, "rb"), 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): 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] def metadata_blocks(self, reader): """yields a (block_id, block_size, block_reader) tuple per metadata block where block_reader is a BitstreamReader substream""" (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u") while (last_block == 0): yield (block_id, block_size, self.reader.substream(block_size)) (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u") else: yield (block_id, block_size, self.reader.substream(block_size)) def read(self, pcm_frames): #if the stream is exhausted, #verify its MD5 sum and return an empty pcm.FrameList object if (self.total_frames < 1): if (self.md5sum == self.current_md5sum.digest()): return from_list([], self.channels, self.bits_per_sample, True) else: raise ValueError("MD5 checksum mismatch") crc16 = CRC16() self.reader.add_callback(crc16.update) #fetch the decoding parameters from the frame header (block_size, channel_assignment, bits_per_sample) = self.read_frame_header() channel_count = self.CHANNEL_COUNT[channel_assignment] if (channel_count is None): raise ValueError("invalid channel assignment") #channel data will be a list of signed sample lists, one per channel #such as [[1, 2, 3, ...], [4, 5, 6, ...]] for a 2 channel stream channel_data = [] for channel_number in xrange(channel_count): if ((channel_assignment == 0x8) and (channel_number == 1)): #for left-difference assignment #the difference channel has 1 additional bit channel_data.append(self.read_subframe(block_size, bits_per_sample + 1)) elif ((channel_assignment == 0x9) and (channel_number == 0)): #for difference-right assignment #the difference channel has 1 additional bit channel_data.append(self.read_subframe(block_size, bits_per_sample + 1)) elif ((channel_assignment == 0xA) and (channel_number == 1)): #for mid-side assignment #the side channel has 1 additional bit channel_data.append(self.read_subframe(block_size, bits_per_sample + 1)) else: #otherwise, use the frame's bits-per-sample value channel_data.append(self.read_subframe(block_size, bits_per_sample)) #one all the subframes have been decoded, #reconstruct them depending on the channel assignment if (channel_assignment == 0x8): #left-difference samples = [] for (left, difference) in zip(*channel_data): samples.append(left) samples.append(left - difference) elif (channel_assignment == 0x9): #difference-right samples = [] for (difference, right) in zip(*channel_data): samples.append(difference + right) samples.append(right) elif (channel_assignment == 0xA): #mid-side samples = [] for (mid, side) in zip(*channel_data): samples.append((((mid * 2) + (side % 2)) + side) / 2) samples.append((((mid * 2) + (side % 2)) - side) / 2) else: #independent samples = [0] * block_size * channel_count for (i, channel) in enumerate(channel_data): samples[i::channel_count] = channel self.reader.byte_align() #read and verify the frame's trailing CRC-16 footer self.reader.read(16) self.reader.pop_callback() if (int(crc16) != 0): raise ValueError("CRC16 mismatch in frame footer") #deduct the amount of PCM frames from the remaining amount self.total_frames -= block_size #build a pcm.FrameList object from the combined samples framelist = from_list(samples, channel_count, bits_per_sample, True) #update the running MD5 sum calculation with the frame's data self.current_md5sum.update(framelist.to_bytes(0, 1)) #and finally return the frame data return framelist def read_frame_header(self): crc8 = CRC8() self.reader.add_callback(crc8.update) #read the 32-bit FLAC frame header sync_code = self.reader.read(14) if (sync_code != 0x3FFE): raise ValueError("invalid sync code") self.reader.skip(1) blocking_strategy = self.reader.read(1) block_size_bits = self.reader.read(4) sample_rate_bits = self.reader.read(4) channel_assignment = self.reader.read(4) bits_per_sample_bits = self.reader.read(3) self.reader.skip(1) #the frame number is a UTF-8 encoded value #which takes a variable number of whole bytes frame_number = self.read_utf8() #unpack the 4 bit block size field #which is the total PCM frames in the FLAC frame #and may require up to 16 more bits if the frame is usually-sized #(which typically happens at the end of the stream) if (block_size_bits == 0x6): block_size = self.reader.read(8) + 1 elif (block_size_bits == 0x7): block_size = self.reader.read(16) + 1 else: block_size = self.BLOCK_SIZE[block_size_bits] #unpack the 4 bit sample rate field #which is used for playback, but not needed for decoding #and may require up to 16 more bits #if the stream has a particularly unusual sample rate if (sample_rate_bits == 0xC): sample_rate = self.reader.read(8) * 1000 elif (sample_rate_bits == 0xD): sample_rate = self.reader.read(16) elif (sample_rate_bits == 0xE): sample_rate = self.reader.read(16) * 10 elif (sample_rate_bits == 0xF): raise ValueError("invalid sample rate") else: sample_rate = self.SAMPLE_RATE[sample_rate_bits] #unpack the 3 bit bits-per-sample field #this never requires additional bits if ((bits_per_sample_bits == 0x3) or (bits_per_sample_bits == 0x7)): raise ValueError("invalid bits per sample") else: bits_per_sample = self.BITS_PER_SAMPLE[bits_per_sample_bits] #read and verify frame's CRC-8 value self.reader.read(8) self.reader.pop_callback() if (int(crc8) != 0): raise ValueError("CRC8 mismatch in frame header") return (block_size, channel_assignment, bits_per_sample) def read_subframe_header(self): """returns a tuple of (subframe_type, subframe_order, wasted_bps)""" self.reader.skip(1) subframe_type = self.reader.read(6) if (self.reader.read(1) == 1): wasted_bps = self.reader.unary(1) + 1 else: wasted_bps = 0 #extract "order" value from 6 bit subframe type, if necessary if (subframe_type == 0): return (self.SUBFRAME_CONSTANT, None, wasted_bps) elif (subframe_type == 1): return (self.SUBFRAME_VERBATIM, None, wasted_bps) elif ((subframe_type & 0x38) == 0x08): return (self.SUBFRAME_FIXED, subframe_type & 0x07, wasted_bps) elif ((subframe_type & 0x20) == 0x20): return (self.SUBFRAME_LPC, (subframe_type & 0x1F) + 1, wasted_bps) else: raise ValueError("invalid subframe type") def read_subframe(self, block_size, bits_per_sample): (subframe_type, subframe_order, wasted_bps) = self.read_subframe_header() #read a list of signed sample values #depending on the subframe type, block size, #adjusted bits per sample and optional subframe order if (subframe_type == self.SUBFRAME_CONSTANT): subframe_samples = self.read_constant_subframe( block_size, bits_per_sample - wasted_bps) elif (subframe_type == self.SUBFRAME_VERBATIM): subframe_samples = self.read_verbatim_subframe( block_size, bits_per_sample - wasted_bps) elif (subframe_type == self.SUBFRAME_FIXED): subframe_samples = self.read_fixed_subframe( block_size, bits_per_sample - wasted_bps, subframe_order) else: subframe_samples = self.read_lpc_subframe( block_size, bits_per_sample - wasted_bps, subframe_order) #account for wasted bits-per-sample, if necessary if (wasted_bps): return [sample << wasted_bps for sample in subframe_samples] else: return subframe_samples def read_constant_subframe(self, block_size, bits_per_sample): sample = self.reader.read_signed(bits_per_sample) return [sample] * block_size def read_verbatim_subframe(self, block_size, bits_per_sample): return [self.reader.read_signed(bits_per_sample) for x in xrange(block_size)] def read_fixed_subframe(self, block_size, bits_per_sample, order): #"order" number of warm-up samples samples = [self.reader.read_signed(bits_per_sample) for i in xrange(order)] #"block_size" - "order" number of residual values residuals = self.read_residual(block_size, order) #which are applied to the warm-up samples #depending on the FIXED subframe order #and results in "block_size" number of total samples if (order == 0): return residuals elif (order == 1): for residual in residuals: samples.append( samples[-1] + residual) return samples elif (order == 2): for residual in residuals: samples.append( (2 * samples[-1]) - samples[-2] + residual) return samples elif (order == 3): for residual in residuals: samples.append( (3 * samples[-1]) - (3 * samples[-2]) + samples[-3] + residual) return samples elif (order == 4): for residual in residuals: samples.append( (4 * samples[-1]) - (6 * samples[-2]) + (4 * samples[-3]) - samples[-4] + residual) return samples else: raise ValueError("unsupported FIXED subframe order") def read_lpc_subframe(self, block_size, bits_per_sample, order): #"order" number of warm-up samples samples = [self.reader.read_signed(bits_per_sample) for i in xrange(order)] #the size of each QLP coefficient, in bits qlp_precision = self.reader.read(4) #the amount of right shift to apply #during LPC calculation #(though this is a signed value, negative shifts are noops # in the reference FLAC decoder) qlp_shift_needed = max(self.reader.read_signed(5), 0) #"order" number of signed QLP coefficients qlp_coeffs = [self.reader.read_signed(qlp_precision + 1) for i in xrange(order)] #QLP coefficients are applied in reverse order qlp_coeffs.reverse() #"block_size" - "order" number of residual values residuals = self.read_residual(block_size, order) #which are applied to the running LPC calculation for residual in residuals: samples.append((sum([coeff * sample for (coeff, sample) in zip(qlp_coeffs, samples[-order:])]) >> qlp_shift_needed) + residual) return samples def read_residual(self, block_size, order): residuals = [] coding_method = self.reader.read(2) partition_order = self.reader.read(4) #each parititon contains block_size / 2 ** partition_order #number of residuals for partition_number in xrange(2 ** partition_order): if (partition_number == 0): #except for the first partition #which contains "order" less than the rest residuals.extend( self.read_residual_partition( coding_method, (block_size / 2 ** partition_order) - order)) else: residuals.extend( self.read_residual_partition( coding_method, block_size / 2 ** partition_order)) return residuals def read_residual_partition(self, coding_method, residual_count): if (coding_method == 0): #the Rice parameters determines the number of #least-significant bits to read for each residual rice_parameter = self.reader.read(4) if (rice_parameter == 0xF): escape_code = self.reader.read(5) return [self.reader.read_signed(escape_code) for i in xrange(residual_count)] elif (coding_method == 1): #24 bps streams may use a 5-bit Rice parameter #for better compression rice_parameter = self.reader.read(5) if (rice_parameter == 0x1F): escape_code = self.reader.read(5) return [self.reader.read_signed(escape_code) for i in xrange(residual_count)] else: raise ValueError("invalid Rice coding parameter") #a list of signed residual values partition_residuals = [] for i in xrange(residual_count): msb = self.reader.unary(1) # most-significant bits lsb = self.reader.read(rice_parameter) # least-significant bits value = (msb << rice_parameter) | lsb # combined into a value if (value & 1): # whose least-significant bit is the sign value partition_residuals.append(-(value >> 1) - 1) else: partition_residuals.append(value >> 1) return partition_residuals def read_utf8(self): total_bytes = self.reader.unary(0) value = self.reader.read(7 - total_bytes) while (total_bytes > 1): value = ((value << 6) | self.reader.parse("2p 6u")[0]) total_bytes -= 1 return value def close(self): self.reader.close()
class TTADecoder: def __init__(self, filename): self.reader = BitstreamReader(open(filename, "rb"), True) crc = CRC32() self.reader.add_callback(crc.update) #read the header (signature, format_, self.channels, self.bits_per_sample, self.sample_rate, self.total_pcm_frames) = self.reader.parse( "4b 16u 16u 16u 32u 32u") self.reader.pop_callback() header_crc = self.reader.read(32) if (int(crc) != header_crc): raise ValueError( "CRC32 mismatch in header (0x%8.8X != 0x%8.8X)" % (header_crc, int(crc))) self.channel_mask = {1:0x4, 2:0x3}.get(self.channels, 0) total_tta_frames = div_ceil(self.total_pcm_frames * 245, self.sample_rate * 256) self.pcm_frames_per_tta_frame = (self.sample_rate * 256) / 245 #read the seektable crc = CRC32() self.reader.add_callback(crc.update) self.frame_sizes = [self.reader.read(32) for i in xrange(total_tta_frames)] self.reader.pop_callback() seektable_crc = self.reader.read(32) if (int(crc) != seektable_crc): raise ValueError( "CRC32 mismatch in seektable (0x%8.8X != 0x%8.8X)" % (header_crc, int(crc))) self.current_tta_frame = 0 def read(self, pcm_frames): if (self.total_pcm_frames == 0): return FrameList("", self.channels, self.bits_per_sample, True, True) pcm_frames = min(self.pcm_frames_per_tta_frame, self.total_pcm_frames) frame_reader = self.reader.substream( self.frame_sizes[self.current_tta_frame]) crc = CRC32() frame_reader.add_callback(crc.update) self.total_pcm_frames -= pcm_frames self.current_tta_frame += 1 #setup Rice parameters for each channel k0 = [10] * self.channels k1 = [10] * self.channels sum0 = [2 ** 14] * self.channels sum1 = [2 ** 14] * self.channels #list of unfiltered output for each channel unfiltered = [[] for i in xrange(self.channels)] for f in xrange(pcm_frames): correlated = [] for (c, ch_output) in enumerate(unfiltered): #read most-significant bits MSB = frame_reader.unary(0) if (MSB == 0): #read least-significant bits unsigned = frame_reader.read(k0[c]) else: #read least-significant bits LSB = frame_reader.read(k1[c]) unshifted = ((MSB - 1) << k1[c]) + LSB unsigned = unshifted + (1 << k0[c]) #adjust sum1 and k1 sum1[c] += (unshifted - (sum1[c] >> 4)) if (sum1[c] < (2 ** (k1[c] + 4))): k1[c] = max(k1[c] - 1, 0) elif (sum1[c] > (2 ** (k1[c] + 5))): k1[c] += 1 #adjust sum0 and k0 sum0[c] += (unsigned - (sum0[c] >> 4)) if (sum0[c] < (2 ** (k0[c] + 4))): k0[c] = max(k0[c] - 1, 0) elif (sum0[c] > (2 ** (k0[c] + 5))): k0[c] += 1 #apply sign bit if ((unsigned % 2) == 1): #positive ch_output.append((unsigned + 1) / 2) else: #negative ch_output.append(-(unsigned / 2)) #check frame's trailing CRC32 now that reading is finished frame_reader.byte_align() frame_reader.pop_callback() frame_crc = frame_reader.read(32) if (int(crc) != frame_crc): raise ValueError("CRC32 mismatch in frame (0x%8.8X != 0x%8.8X)" % (frame_crc, int(crc))) #run hybrid filter on each channel filtered = [] for unfiltered_ch in unfiltered: filtered.append( tta_filter(self.bits_per_sample, unfiltered_ch)) #run fixed order prediction on each channel predicted = [] for filtered_ch in filtered: predicted.append( fixed_predictor(self.bits_per_sample, filtered_ch)) if (self.channels == 1): #send channel as-is return from_list(predicted[0], 1, self.bits_per_sample, True) else: #decorrelate channels decorrelated = decorrelate(predicted) #return all channels as single FrameList return from_channels([from_list(decorrelated_ch, 1, self.bits_per_sample, True) for decorrelated_ch in decorrelated]) def close(self): #FIXME pass
class TTADecoder(object): def __init__(self, filename): self.reader = BitstreamReader(open(filename, "rb"), True) crc = CRC32() self.reader.add_callback(crc.update) # read the header (signature, format_, self.channels, self.bits_per_sample, self.sample_rate, self.total_pcm_frames) = self.reader.parse("4b 16u 16u 16u 32u 32u") self.reader.pop_callback() header_crc = self.reader.read(32) if (int(crc) != header_crc): raise ValueError("CRC32 mismatch in header (0x%8.8X != 0x%8.8X)" % (header_crc, int(crc))) self.channel_mask = {1: 0x4, 2: 0x3}.get(self.channels, 0) total_tta_frames = div_ceil(self.total_pcm_frames * 245, self.sample_rate * 256) self.pcm_frames_per_tta_frame = (self.sample_rate * 256) // 245 # read the seektable crc = CRC32() self.reader.add_callback(crc.update) self.frame_sizes = [ self.reader.read(32) for i in range(total_tta_frames) ] self.reader.pop_callback() seektable_crc = self.reader.read(32) if (int(crc) != seektable_crc): raise ValueError( "CRC32 mismatch in seektable (0x%8.8X != 0x%8.8X)" % (header_crc, int(crc))) self.current_tta_frame = 0 def read(self, pcm_frames): if (self.total_pcm_frames == 0): return FrameList("", self.channels, self.bits_per_sample, True, True) pcm_frames = min(self.pcm_frames_per_tta_frame, self.total_pcm_frames) frame_reader = self.reader.substream( self.frame_sizes[self.current_tta_frame]) crc = CRC32() frame_reader.add_callback(crc.update) self.total_pcm_frames -= pcm_frames self.current_tta_frame += 1 # setup Rice parameters for each channel k0 = [10] * self.channels k1 = [10] * self.channels sum0 = [2**14] * self.channels sum1 = [2**14] * self.channels # list of unfiltered output for each channel unfiltered = [[] for i in range(self.channels)] for f in range(pcm_frames): correlated = [] for (c, ch_output) in enumerate(unfiltered): # read most-significant bits MSB = frame_reader.unary(0) if (MSB == 0): # read least-significant bits unsigned = frame_reader.read(k0[c]) else: # read least-significant bits LSB = frame_reader.read(k1[c]) unshifted = ((MSB - 1) << k1[c]) + LSB unsigned = unshifted + (1 << k0[c]) # adjust sum1 and k1 sum1[c] += (unshifted - (sum1[c] >> 4)) if (sum1[c] < (2**(k1[c] + 4))): k1[c] = max(k1[c] - 1, 0) elif (sum1[c] > (2**(k1[c] + 5))): k1[c] += 1 # adjust sum0 and k0 sum0[c] += (unsigned - (sum0[c] >> 4)) if (sum0[c] < (2**(k0[c] + 4))): k0[c] = max(k0[c] - 1, 0) elif (sum0[c] > (2**(k0[c] + 5))): k0[c] += 1 # apply sign bit if ((unsigned % 2) == 1): # positive ch_output.append((unsigned + 1) // 2) else: # negative ch_output.append(-(unsigned // 2)) # check frame's trailing CRC32 now that reading is finished frame_reader.byte_align() frame_reader.pop_callback() frame_crc = frame_reader.read(32) if (int(crc) != frame_crc): raise ValueError("CRC32 mismatch in frame (0x%8.8X != 0x%8.8X)" % (frame_crc, int(crc))) # run hybrid filter on each channel filtered = [] for unfiltered_ch in unfiltered: filtered.append(tta_filter(self.bits_per_sample, unfiltered_ch)) # run fixed order prediction on each channel predicted = [] for filtered_ch in filtered: predicted.append(fixed_predictor(self.bits_per_sample, filtered_ch)) if (self.channels == 1): # send channel as-is return from_list(predicted[0], 1, self.bits_per_sample, True) else: # decorrelate channels decorrelated = decorrelate(predicted) # return all channels as single FrameList return from_channels([ from_list(decorrelated_ch, 1, self.bits_per_sample, True) for decorrelated_ch in decorrelated ]) def close(self): # FIXME pass
class FlacDecoder(object): CHANNEL_COUNT = [ 1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2, None, None, None, None, None ] (SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED, SUBFRAME_LPC) = range(4) def __init__(self, filename, channel_mask): self.reader = BitstreamReader(open(filename, "rb"), False) if (self.reader.read_bytes(4) != b'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): 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.read(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 ] def metadata_blocks(self, reader): """yields a (block_id, block_size, block_reader) tuple per metadata block where block_reader is a BitstreamReader substream""" (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u") while (last_block == 0): yield (block_id, block_size, self.reader.substream(block_size)) (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u") else: yield (block_id, block_size, self.reader.substream(block_size)) def read(self, pcm_frames): # if the stream is exhausted, # verify its MD5 sum and return an empty pcm.FrameList object if (self.total_frames < 1): if (self.md5sum == self.current_md5sum.digest()): return empty_framelist(self.channels, self.bits_per_sample) else: raise ValueError("MD5 checksum mismatch") crc16 = CRC16() self.reader.add_callback(crc16.update) # fetch the decoding parameters from the frame header (block_size, channel_assignment, bits_per_sample) = self.read_frame_header() channel_count = self.CHANNEL_COUNT[channel_assignment] if (channel_count is None): raise ValueError("invalid channel assignment") # channel data will be a list of signed sample lists, one per channel # such as [[1, 2, 3, ...], [4, 5, 6, ...]] for a 2 channel stream channel_data = [] for channel_number in range(channel_count): if ((channel_assignment == 0x8) and (channel_number == 1)): # for left-difference assignment # the difference channel has 1 additional bit channel_data.append( self.read_subframe(block_size, bits_per_sample + 1)) elif ((channel_assignment == 0x9) and (channel_number == 0)): # for difference-right assignment # the difference channel has 1 additional bit channel_data.append( self.read_subframe(block_size, bits_per_sample + 1)) elif ((channel_assignment == 0xA) and (channel_number == 1)): # for average-difference assignment # the difference channel has 1 additional bit channel_data.append( self.read_subframe(block_size, bits_per_sample + 1)) else: # otherwise, use the frame's bits-per-sample value channel_data.append( self.read_subframe(block_size, bits_per_sample)) # one all the subframes have been decoded, # reconstruct them depending on the channel assignment if (channel_assignment == 0x8): # left-difference samples = [] for (left, difference) in zip(*channel_data): samples.append(left) samples.append(left - difference) elif (channel_assignment == 0x9): # difference-right samples = [] for (difference, right) in zip(*channel_data): samples.append(difference + right) samples.append(right) elif (channel_assignment == 0xA): # mid-side samples = [] for (mid, side) in zip(*channel_data): samples.append((((mid * 2) + (side % 2)) + side) // 2) samples.append((((mid * 2) + (side % 2)) - side) // 2) else: # independent samples = [0] * block_size * channel_count for (i, channel) in enumerate(channel_data): samples[i::channel_count] = channel self.reader.byte_align() # read and verify the frame's trailing CRC-16 footer self.reader.read(16) self.reader.pop_callback() if (int(crc16) != 0): raise ValueError("CRC16 mismatch in frame footer") # deduct the amount of PCM frames from the remaining amount self.total_frames -= block_size # build a pcm.FrameList object from the combined samples framelist = from_list(samples, channel_count, bits_per_sample, True) # update the running MD5 sum calculation with the frame's data self.current_md5sum.update(framelist.to_bytes(0, 1)) # and finally return the frame data return framelist def read_frame_header(self): crc8 = CRC8() self.reader.add_callback(crc8.update) # read the 32-bit FLAC frame header sync_code = self.reader.read(14) if (sync_code != 0x3FFE): raise ValueError("invalid sync code") self.reader.skip(1) blocking_strategy = self.reader.read(1) block_size_bits = self.reader.read(4) sample_rate_bits = self.reader.read(4) channel_assignment = self.reader.read(4) bits_per_sample_bits = self.reader.read(3) self.reader.skip(1) # the frame number is a UTF-8 encoded value # which takes a variable number of whole bytes frame_number = self.read_utf8() # unpack the 4 bit block size field # which is the total PCM frames in the FLAC frame # and may require up to 16 more bits if the frame is usually-sized # (which typically happens at the end of the stream) if (block_size_bits == 0x6): block_size = self.reader.read(8) + 1 elif (block_size_bits == 0x7): block_size = self.reader.read(16) + 1 else: block_size = self.BLOCK_SIZE[block_size_bits] # unpack the 4 bit sample rate field # which is used for playback, but not needed for decoding # and may require up to 16 more bits # if the stream has a particularly unusual sample rate if (sample_rate_bits == 0xC): sample_rate = self.reader.read(8) * 1000 elif (sample_rate_bits == 0xD): sample_rate = self.reader.read(16) elif (sample_rate_bits == 0xE): sample_rate = self.reader.read(16) * 10 elif (sample_rate_bits == 0xF): raise ValueError("invalid sample rate") else: sample_rate = self.SAMPLE_RATE[sample_rate_bits] # unpack the 3 bit bits-per-sample field # this never requires additional bits if ((bits_per_sample_bits == 0x3) or (bits_per_sample_bits == 0x7)): raise ValueError("invalid bits per sample") else: bits_per_sample = self.BITS_PER_SAMPLE[bits_per_sample_bits] # read and verify frame's CRC-8 value self.reader.read(8) self.reader.pop_callback() if (int(crc8) != 0): raise ValueError("CRC8 mismatch in frame header") return (block_size, channel_assignment, bits_per_sample) def read_subframe_header(self): """returns a tuple of (subframe_type, subframe_order, wasted_bps)""" self.reader.skip(1) subframe_type = self.reader.read(6) if (self.reader.read(1) == 1): wasted_bps = self.reader.unary(1) + 1 else: wasted_bps = 0 # extract "order" value from 6 bit subframe type, if necessary if (subframe_type == 0): return (self.SUBFRAME_CONSTANT, None, wasted_bps) elif (subframe_type == 1): return (self.SUBFRAME_VERBATIM, None, wasted_bps) elif ((subframe_type & 0x38) == 0x08): return (self.SUBFRAME_FIXED, subframe_type & 0x07, wasted_bps) elif ((subframe_type & 0x20) == 0x20): return (self.SUBFRAME_LPC, (subframe_type & 0x1F) + 1, wasted_bps) else: raise ValueError("invalid subframe type") def read_subframe(self, block_size, bits_per_sample): (subframe_type, subframe_order, wasted_bps) = self.read_subframe_header() # read a list of signed sample values # depending on the subframe type, block size, # adjusted bits per sample and optional subframe order if (subframe_type == self.SUBFRAME_CONSTANT): subframe_samples = self.read_constant_subframe( block_size, bits_per_sample - wasted_bps) elif (subframe_type == self.SUBFRAME_VERBATIM): subframe_samples = self.read_verbatim_subframe( block_size, bits_per_sample - wasted_bps) elif (subframe_type == self.SUBFRAME_FIXED): subframe_samples = self.read_fixed_subframe( block_size, bits_per_sample - wasted_bps, subframe_order) else: subframe_samples = self.read_lpc_subframe( block_size, bits_per_sample - wasted_bps, subframe_order) # account for wasted bits-per-sample, if necessary if (wasted_bps): return [sample << wasted_bps for sample in subframe_samples] else: return subframe_samples def read_constant_subframe(self, block_size, bits_per_sample): sample = self.reader.read_signed(bits_per_sample) return [sample] * block_size def read_verbatim_subframe(self, block_size, bits_per_sample): return [ self.reader.read_signed(bits_per_sample) for x in range(block_size) ] def read_fixed_subframe(self, block_size, bits_per_sample, order): # "order" number of warm-up samples samples = [ self.reader.read_signed(bits_per_sample) for i in range(order) ] # "block_size" - "order" number of residual values residuals = self.read_residual(block_size, order) # which are applied to the warm-up samples # depending on the FIXED subframe order # and results in "block_size" number of total samples if (order == 0): return residuals elif (order == 1): for residual in residuals: samples.append(samples[-1] + residual) return samples elif (order == 2): for residual in residuals: samples.append((2 * samples[-1]) - samples[-2] + residual) return samples elif (order == 3): for residual in residuals: samples.append((3 * samples[-1]) - (3 * samples[-2]) + samples[-3] + residual) return samples elif (order == 4): for residual in residuals: samples.append((4 * samples[-1]) - (6 * samples[-2]) + (4 * samples[-3]) - samples[-4] + residual) return samples else: raise ValueError("unsupported FIXED subframe order") def read_lpc_subframe(self, block_size, bits_per_sample, order): # "order" number of warm-up samples samples = [ self.reader.read_signed(bits_per_sample) for i in range(order) ] # the size of each QLP coefficient, in bits qlp_precision = self.reader.read(4) # the amount of right shift to apply # during LPC calculation # (though this is a signed value, negative shifts are noops # in the reference FLAC decoder) qlp_shift_needed = max(self.reader.read_signed(5), 0) # "order" number of signed QLP coefficients qlp_coeffs = [ self.reader.read_signed(qlp_precision + 1) for i in range(order) ] # QLP coefficients are applied in reverse order qlp_coeffs.reverse() # "block_size" - "order" number of residual values residuals = self.read_residual(block_size, order) # which are applied to the running LPC calculation for residual in residuals: samples.append((sum([ coeff * sample for (coeff, sample) in zip(qlp_coeffs, samples[-order:]) ]) >> qlp_shift_needed) + residual) return samples def read_residual(self, block_size, order): residuals = [] coding_method = self.reader.read(2) partition_order = self.reader.read(4) # each parititon contains block_size / 2 ** partition_order # number of residuals for partition_number in range(2**partition_order): if (partition_number == 0): # except for the first partition # which contains "order" less than the rest residuals.extend( self.read_residual_partition( coding_method, (block_size // 2**partition_order) - order)) else: residuals.extend( self.read_residual_partition( coding_method, block_size // 2**partition_order)) return residuals def read_residual_partition(self, coding_method, residual_count): if (coding_method == 0): # the Rice parameters determines the number of # least-significant bits to read for each residual rice_parameter = self.reader.read(4) if (rice_parameter == 0xF): escape_code = self.reader.read(5) return [ self.reader.read_signed(escape_code) for i in range(residual_count) ] elif (coding_method == 1): # 24 bps streams may use a 5-bit Rice parameter # for better compression rice_parameter = self.reader.read(5) if (rice_parameter == 0x1F): escape_code = self.reader.read(5) return [ self.reader.read_signed(escape_code) for i in range(residual_count) ] else: raise ValueError("invalid Rice coding parameter") # a list of signed residual values partition_residuals = [] for i in range(residual_count): msb = self.reader.unary(1) # most-significant bits lsb = self.reader.read(rice_parameter) # least-significant bits value = (msb << rice_parameter) | lsb # combined into a value if (value & 1): # whose least-significant bit is the sign value partition_residuals.append(-(value >> 1) - 1) else: partition_residuals.append(value >> 1) return partition_residuals def read_utf8(self): total_bytes = self.reader.unary(0) value = self.reader.read(7 - total_bytes) while (total_bytes > 1): value = ((value << 6) | self.reader.parse("2p 6u")[0]) total_bytes -= 1 return value def close(self): self.reader.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close()