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 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()
class SHNDecoder(object): def __init__(self, filename): self.reader = BitstreamReader(open(filename, "rb"), False) (self.file_type, self.channels, self.block_length, self.max_LPC, self.number_of_means) = self.read_header() if (1 <= self.file_type) and (self.file_type <= 2): self.bits_per_sample = 8 self.signed_samples = (self.file_type == 1) elif (3 <= self.file_type) and (self.file_type <= 6): self.bits_per_sample = 16 self.signed_samples = (self.file_type in (3, 5)) else: raise ValueError("unsupported Shorten file type") self.wrapped_samples = [[0] * 3 for c in range(self.channels)] self.means = [[0] * self.number_of_means for c in range(self.channels)] self.left_shift = 0 self.stream_finished = False # try to read the first command for a wave/aiff header data_start = self.reader.getpos() self.read_metadata() self.reader.setpos(data_start) def read_metadata(self): from io import BytesIO command = self.unsigned(2) if command == 9: # got verbatim, so read data verbatim_bytes = ints_to_bytes([self.unsigned(8) & 0xFF for i in range(self.unsigned(5))]) try: wave = BitstreamReader(BytesIO(verbatim_bytes), True) header = wave.read_bytes(12) if header.startswith(b"RIFF") and header.endswith(b"WAVE"): # got RIFF/WAVE header, so parse wave blocks as needed total_size = len(verbatim_bytes) - 12 while total_size > 0: (chunk_id, chunk_size) = wave.parse("4b 32u") total_size -= 8 if chunk_id == b'fmt ': (channels, self.sample_rate, bits_per_sample, channel_mask) = parse_fmt( wave.substream(chunk_size)) self.channel_mask = int(channel_mask) return else: if chunk_size % 2: wave.read_bytes(chunk_size + 1) total_size -= (chunk_size + 1) else: wave.read_bytes(chunk_size) total_size -= chunk_size else: # no fmt chunk, so use default metadata pass except (IOError, ValueError): pass try: aiff = BitstreamReader(BytesIO(verbatim_bytes), False) header = aiff.read_bytes(12) if header.startswith(b"FORM") and header.endswith(b"AIFF"): # got FORM/AIFF header, so parse aiff blocks as needed total_size = len(verbatim_bytes) - 12 while total_size > 0: (chunk_id, chunk_size) = aiff.parse("4b 32u") total_size -= 8 if chunk_id == b'COMM': (channels, total_sample_frames, bits_per_sample, self.sample_rate, channel_mask) = parse_comm( aiff.substream(chunk_size)) self.channel_mask = int(channel_mask) return else: if chunk_size % 2: aiff.read_bytes(chunk_size + 1) total_size -= (chunk_size + 1) else: aiff.read_bytes(chunk_size) total_size -= chunk_size else: # no COMM chunk, so use default metadata pass except IOError: pass # got something else, so invent some PCM parameters self.sample_rate = 44100 if self.channels == 1: self.channel_mask = 0x4 elif self.channels == 2: self.channel_mask = 0x3 else: self.channel_mask = 0 def unsigned(self, c): MSB = self.reader.unary(1) LSB = self.reader.read(c) return MSB * 2 ** c + LSB def signed(self, c): u = self.unsigned(c + 1) if (u % 2) == 0: return u // 2 else: return -(u // 2) - 1 def long(self): return self.unsigned(self.unsigned(2)) def skip_unsigned(self, c): self.reader.skip_unary(1) self.reader.skip(c) def read_header(self): magic = self.reader.read_bytes(4) if magic != b"ajkg": raise ValueError("invalid magic number") version = self.reader.read(8) if version != 2: raise ValueError("unsupported version") file_type = self.long() channels = self.long() block_length = self.long() max_LPC = self.long() number_of_means = self.long() bytes_to_skip = self.long() self.reader.read_bytes(bytes_to_skip) return (file_type, channels, block_length, max_LPC, number_of_means) def read(self, pcm_frames): if self.stream_finished: return from_channels([empty_framelist(1, self.bits_per_sample) for channel in range(self.channels)]) c = 0 samples = [] unshifted = [] while True: command = self.unsigned(2) if (((0 <= command) and (command <= 3) or (7 <= command) and (command <= 8))): # audio data commands if command == 0: # DIFF0 samples.append(self.read_diff0(self.block_length, self.means[c])) elif command == 1: # DIFF1 samples.append(self.read_diff1(self.block_length, self.wrapped_samples[c])) elif command == 2: # DIFF2 samples.append(self.read_diff2(self.block_length, self.wrapped_samples[c])) elif command == 3: # DIFF3 samples.append(self.read_diff3(self.block_length, self.wrapped_samples[c])) elif command == 7: # QLPC samples.append(self.read_qlpc(self.block_length, self.means[c], self.wrapped_samples[c])) elif command == 8: # ZERO samples.append([0] * self.block_length) # update means for channel self.means[c].append(shnmean(samples[c])) self.means[c] = self.means[c][1:] # wrap samples for next command in channel self.wrapped_samples[c] = samples[c][-(max(3, self.max_LPC)):] # apply left shift to samples if self.left_shift > 0: unshifted.append([s << self.left_shift for s in samples[c]]) else: unshifted.append(samples[c]) c += 1 if c == self.channels: # return a FrameList from shifted data return from_channels([from_list(channel, 1, self.bits_per_sample, self.signed_samples) for channel in unshifted]) else: # non audio commands if command == 4: # QUIT self.stream_finished = True return from_channels( [empty_framelist(1, self.bits_per_sample) for channel in range(self.channels)]) elif command == 5: # BLOCKSIZE self.block_length = self.long() elif command == 6: # BITSHIFT self.left_shift = self.unsigned(2) elif command == 9: # VERBATIM # skip this command during reading size = self.unsigned(5) for i in range(size): self.skip_unsigned(8) else: raise ValueError("unsupported Shorten command") def read_diff0(self, block_length, means): offset = shnmean(means) energy = self.unsigned(3) samples = [] for i in range(block_length): residual = self.signed(energy) samples.append(residual + offset) return samples def read_diff1(self, block_length, previous_samples): samples = previous_samples[-1:] energy = self.unsigned(3) for i in range(1, block_length + 1): residual = self.signed(energy) samples.append(samples[i - 1] + residual) return samples[1:] def read_diff2(self, block_length, previous_samples): samples = previous_samples[-2:] energy = self.unsigned(3) for i in range(2, block_length + 2): residual = self.signed(energy) samples.append((2 * samples[i - 1]) - samples[i - 2] + residual) return samples[2:] def read_diff3(self, block_length, previous_samples): samples = previous_samples[-3:] energy = self.unsigned(3) for i in range(3, block_length + 3): residual = self.signed(energy) samples.append((3 * (samples[i - 1] - samples[i - 2])) + samples[i - 3] + residual) return samples[3:] def read_qlpc(self, block_length, means, previous_samples): offset = shnmean(means) energy = self.unsigned(3) LPC_count = self.unsigned(2) LPC_coeff = [self.signed(5) for i in range(LPC_count)] unoffset = [] samples = previous_samples[-LPC_count:] for i in range(block_length): residual = self.signed(energy) LPC_sum = 2 ** 5 for j in range(LPC_count): if (i - j - 1) < 0: # apply offset to warm-up samples LPC_sum += (LPC_coeff[j] * (samples[LPC_count + (i - j - 1)] - offset)) else: LPC_sum += LPC_coeff[j] * unoffset[i - j - 1] unoffset.append(LPC_sum // 2 ** 5 + residual) return [u + offset for u in unoffset] def close(self): self.reader.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close()
class SHNDecoder(object): def __init__(self, filename): self.reader = BitstreamReader(open(filename, "rb"), False) (self.file_type, self.channels, self.block_length, self.max_LPC, self.number_of_means) = self.read_header() if ((1 <= self.file_type) and (self.file_type <= 2)): self.bits_per_sample = 8 self.signed_samples = (self.file_type == 1) elif ((3 <= self.file_type) and (self.file_type <= 6)): self.bits_per_sample = 16 self.signed_samples = (self.file_type in (3, 5)) else: raise ValueError("unsupported Shorten file type") self.wrapped_samples = [[0] * 3 for c in range(self.channels)] self.means = [[0] * self.number_of_means for c in range(self.channels)] self.left_shift = 0 self.stream_finished = False # try to read the first command for a wave/aiff header self.reader.mark() self.read_metadata() self.reader.rewind() self.reader.unmark() def read_metadata(self): from io import BytesIO command = self.unsigned(2) if (command == 9): # got verbatim, so read data verbatim_bytes = ints_to_bytes( [self.unsigned(8) & 0xFF for i in range(self.unsigned(5))]) try: wave = BitstreamReader(BytesIO(verbatim_bytes), True) header = wave.read_bytes(12) if (header.startswith(b"RIFF") and header.endswith(b"WAVE")): # got RIFF/WAVE header, so parse wave blocks as needed total_size = len(verbatim_bytes) - 12 while (total_size > 0): (chunk_id, chunk_size) = wave.parse("4b 32u") total_size -= 8 if (chunk_id == b'fmt '): (channels, self.sample_rate, bits_per_sample, channel_mask) = parse_fmt( wave.substream(chunk_size)) self.channel_mask = int(channel_mask) return else: if (chunk_size % 2): wave.read_bytes(chunk_size + 1) total_size -= (chunk_size + 1) else: wave.read_bytes(chunk_size) total_size -= chunk_size else: # no fmt chunk, so use default metadata pass except (IOError, ValueError): pass try: aiff = BitstreamReader(BytesIO(verbatim_bytes), False) header = aiff.read_bytes(12) if (header.startswith(b"FORM") and header.endswith(b"AIFF")): # got FORM/AIFF header, so parse aiff blocks as needed total_size = len(verbatim_bytes) - 12 while (total_size > 0): (chunk_id, chunk_size) = aiff.parse("4b 32u") total_size -= 8 if (chunk_id == b'COMM'): (channels, total_sample_frames, bits_per_sample, self.sample_rate, channel_mask) = parse_comm( aiff.substream(chunk_size)) self.channel_mask = int(channel_mask) return else: if (chunk_size % 2): aiff.read_bytes(chunk_size + 1) total_size -= (chunk_size + 1) else: aiff.read_bytes(chunk_size) total_size -= chunk_size else: # no COMM chunk, so use default metadata pass except IOError: pass # got something else, so invent some PCM parameters self.sample_rate = 44100 if (self.channels == 1): self.channel_mask = 0x4 elif (self.channels == 2): self.channel_mask = 0x3 else: self.channel_mask = 0 def unsigned(self, c): MSB = self.reader.unary(1) LSB = self.reader.read(c) return MSB * 2**c + LSB def signed(self, c): u = self.unsigned(c + 1) if ((u % 2) == 0): return u // 2 else: return -(u // 2) - 1 def long(self): return self.unsigned(self.unsigned(2)) def skip_unsigned(self, c): self.reader.skip_unary(1) self.reader.skip(c) def read_header(self): magic = self.reader.read_bytes(4) if (magic != b"ajkg"): raise ValueError("invalid magic number") version = self.reader.read(8) if (version != 2): raise ValueError("unsupported version") file_type = self.long() channels = self.long() block_length = self.long() max_LPC = self.long() number_of_means = self.long() bytes_to_skip = self.long() self.reader.read_bytes(bytes_to_skip) return (file_type, channels, block_length, max_LPC, number_of_means) def read(self, pcm_frames): if (self.stream_finished): return from_channels([ empty_framelist(1, self.bits_per_sample) for channel in range(self.channels) ]) c = 0 samples = [] unshifted = [] while (True): command = self.unsigned(2) if (((0 <= command) and (command <= 3) or (7 <= command) and (command <= 8))): # audio data commands if (command == 0): # DIFF0 samples.append( self.read_diff0(self.block_length, self.means[c])) elif (command == 1): # DIFF1 samples.append( self.read_diff1(self.block_length, self.wrapped_samples[c])) elif (command == 2): # DIFF2 samples.append( self.read_diff2(self.block_length, self.wrapped_samples[c])) elif (command == 3): # DIFF3 samples.append( self.read_diff3(self.block_length, self.wrapped_samples[c])) elif (command == 7): # QLPC samples.append( self.read_qlpc(self.block_length, self.means[c], self.wrapped_samples[c])) elif (command == 8): # ZERO samples.append([0] * self.block_length) # update means for channel self.means[c].append(shnmean(samples[c])) self.means[c] = self.means[c][1:] # wrap samples for next command in channel self.wrapped_samples[c] = samples[c][-(max(3, self.max_LPC)):] # apply left shift to samples if (self.left_shift > 0): unshifted.append( [s << self.left_shift for s in samples[c]]) else: unshifted.append(samples[c]) c += 1 if (c == self.channels): # return a FrameList from shifted data return from_channels([ from_list(channel, 1, self.bits_per_sample, self.signed_samples) for channel in unshifted ]) else: # non audio commands if (command == 4): # QUIT self.stream_finished = True return from_channels([ empty_framelist(1, self.bits_per_sample) for channel in range(self.channels) ]) elif (command == 5): # BLOCKSIZE self.block_length = self.long() elif (command == 6): # BITSHIFT self.left_shift = self.unsigned(2) elif (command == 9): # VERBATIM # skip this command during reading size = self.unsigned(5) for i in range(size): self.skip_unsigned(8) else: raise ValueError("unsupported Shorten command") def read_diff0(self, block_length, means): offset = shnmean(means) energy = self.unsigned(3) samples = [] for i in range(block_length): residual = self.signed(energy) samples.append(residual + offset) return samples def read_diff1(self, block_length, previous_samples): samples = previous_samples[-1:] energy = self.unsigned(3) for i in range(1, block_length + 1): residual = self.signed(energy) samples.append(samples[i - 1] + residual) return samples[1:] def read_diff2(self, block_length, previous_samples): samples = previous_samples[-2:] energy = self.unsigned(3) for i in range(2, block_length + 2): residual = self.signed(energy) samples.append((2 * samples[i - 1]) - samples[i - 2] + residual) return samples[2:] def read_diff3(self, block_length, previous_samples): samples = previous_samples[-3:] energy = self.unsigned(3) for i in range(3, block_length + 3): residual = self.signed(energy) samples.append((3 * (samples[i - 1] - samples[i - 2])) + samples[i - 3] + residual) return samples[3:] def read_qlpc(self, block_length, means, previous_samples): offset = shnmean(means) energy = self.unsigned(3) LPC_count = self.unsigned(2) LPC_coeff = [self.signed(5) for i in range(LPC_count)] unoffset = [] samples = previous_samples[-LPC_count:] for i in range(block_length): residual = self.signed(energy) LPC_sum = 2**5 for j in range(LPC_count): if ((i - j - 1) < 0): # apply offset to warm-up samples LPC_sum += (LPC_coeff[j] * (samples[LPC_count + (i - j - 1)] - offset)) else: LPC_sum += LPC_coeff[j] * unoffset[i - j - 1] unoffset.append(LPC_sum // 2**5 + residual) return [u + offset for u in unoffset] def close(self): self.reader.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close()