def encode_flac(filename, pcmreader, block_size=4096, max_lpc_order=8, adaptive_mid_side=False, mid_side=True, exhaustive_model_search=False, max_residual_partition_order=5): options = Encoding_Options(block_size, max_lpc_order, adaptive_mid_side, mid_side, exhaustive_model_search, max_residual_partition_order, 14 if pcmreader.bits_per_sample <= 16 else 30) streaminfo = STREAMINFO(block_size, block_size, 2 ** 32, 0, pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, 0, md5()) pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, 0) #write placeholder metadata blocks writer.write_bytes("fLaC") writer.build("1u 7u 24u", [1, 0, 34]) streaminfo.write(writer) #walk through PCM reader's FrameLists frame_number = 0 frame = pcmreader.read(block_size * (pcmreader.bits_per_sample / 8) * pcmreader.channels) flac_frame = BitstreamRecorder(0) while (len(frame) > 0): streaminfo.input_update(frame) flac_frame.reset() encode_flac_frame(flac_frame, pcmreader, options, frame_number, frame) streaminfo.output_update(flac_frame) flac_frame.copy(writer) frame_number += 1 frame = pcmreader.read(block_size * (pcmreader.bits_per_sample / 8) * pcmreader.channels) #return to beginning of file and rewrite STREAMINFO block output_file.seek(8, 0) streaminfo.write(writer) writer.close()
def encode_mdat(file, pcmreader, block_size=4096, initial_history=10, history_multiplier=40, maximum_K=14, interlacing_shift=2, min_interlacing_leftweight=0, max_interlacing_leftweight=4): options = Encoding_Options(block_size, initial_history, history_multiplier, maximum_K, interlacing_shift, min_interlacing_leftweight, max_interlacing_leftweight) pcmreader = BufferedPCMReader(pcmreader) mdat = BitstreamWriter(file, 0) mdat_length = ByteCounter() mdat.add_callback(mdat_length.update) frame_sample_sizes = [] frame_byte_sizes = [] frame_file_offsets = [] #write placeholder mdat header mdat.write(32, 0) mdat.write_bytes("mdat") #read FrameList objects until stream is empty frame = pcmreader.read(block_size * pcmreader.channels * (pcmreader.bits_per_sample / 8)) while (len(frame) > 0): frame_sample_sizes.append(frame.frames) frame_file_offsets.append(int(mdat_length)) encode_frameset(mdat, pcmreader, options, frame) frame_byte_sizes.append(int(mdat_length) - frame_file_offsets[-1]) frame = pcmreader.read(block_size * pcmreader.channels * (pcmreader.bits_per_sample / 8)) #finally, return to start of mdat and write actual length mdat.byte_align() mdat.pop_callback() file.seek(0, 0) mdat.write(32, int(mdat_length)) return (frame_sample_sizes, frame_byte_sizes, frame_file_offsets, int(mdat_length))
def encode_mdat( file, pcmreader, block_size=4096, initial_history=10, history_multiplier=40, maximum_k=14, interlacing_shift=2, min_interlacing_leftweight=0, max_interlacing_leftweight=4, ): options = Encoding_Options( block_size, initial_history, history_multiplier, maximum_k, interlacing_shift, min_interlacing_leftweight, max_interlacing_leftweight, ) pcmreader = BufferedPCMReader(pcmreader) mdat = BitstreamWriter(file, 0) total_pcm_frames = 0 frame_byte_sizes = [] # write placeholder mdat header mdat_start = file.tell() mdat.write(32, 0) mdat.write_bytes("mdat") # read FrameList objects until stream is empty frame = pcmreader.read(block_size) while len(frame) > 0: total_pcm_frames += frame.frames frame_start = file.tell() encode_frameset(mdat, pcmreader, options, frame) mdat.flush() frame_byte_sizes.append(file.tell() - frame_start) frame = pcmreader.read(block_size) # finally, return to start of mdat and write actual length file.seek(mdat_start) mdat.write(32, sum(frame_byte_sizes) + 8) return (frame_byte_sizes, total_pcm_frames)
def encode_mdat(file, pcmreader, block_size=4096, initial_history=10, history_multiplier=40, maximum_k=14, interlacing_shift=2, min_interlacing_leftweight=0, max_interlacing_leftweight=4): options = Encoding_Options(block_size, initial_history, history_multiplier, maximum_k, interlacing_shift, min_interlacing_leftweight, max_interlacing_leftweight) pcmreader = BufferedPCMReader(pcmreader) mdat = BitstreamWriter(file, False) total_pcm_frames = 0 frame_byte_sizes = [] # write placeholder mdat header mdat.mark() mdat.write(32, 0) mdat.write_bytes(b"mdat") # read FrameList objects until stream is empty frame = pcmreader.read(block_size) while (len(frame) > 0): total_pcm_frames += frame.frames frame_byte_size = Counter() mdat.add_callback(frame_byte_size.add) encode_frameset(mdat, pcmreader, options, frame) mdat.pop_callback() frame_byte_sizes.append(int(frame_byte_size)) frame = pcmreader.read(block_size) # finally, return to start of mdat and write actual length mdat.rewind() mdat.write(32, sum(frame_byte_sizes) + 8) mdat.unmark() return (frame_byte_sizes, total_pcm_frames)
def encode_mdat(file, pcmreader, block_size=4096, initial_history=10, history_multiplier=40, maximum_k=14, interlacing_shift=2, min_interlacing_leftweight=0, max_interlacing_leftweight=4): options = Encoding_Options(block_size, initial_history, history_multiplier, maximum_k, interlacing_shift, min_interlacing_leftweight, max_interlacing_leftweight) pcmreader = BufferedPCMReader(pcmreader) mdat = BitstreamWriter(file, False) total_pcm_frames = 0 frame_byte_sizes = [] # write placeholder mdat header mdat_start = mdat.getpos() mdat.write(32, 0) mdat.write_bytes(b"mdat") # read FrameList objects until stream is empty frame = pcmreader.read(block_size) while len(frame) > 0: total_pcm_frames += frame.frames frame_byte_size = Counter() mdat.add_callback(frame_byte_size.add) encode_frameset(mdat, pcmreader, options, frame) mdat.pop_callback() frame_byte_sizes.append(int(frame_byte_size)) frame = pcmreader.read(block_size) # finally, return to start of mdat and write actual length mdat.setpos(mdat_start) mdat.write(32, sum(frame_byte_sizes) + 8) return (frame_byte_sizes, total_pcm_frames)
def encode_flac(filename, pcmreader, block_size=4096, max_lpc_order=8, min_residual_partition_order=0, max_residual_partition_order=5, mid_side=True, adaptive_mid_side=False, exhaustive_model_search=False, disable_verbatim_subframes=False, disable_constant_subframes=False, disable_fixed_subframes=False, disable_lpc_subframes=False, padding_size=4096): frame_sizes = [] options = Encoding_Options(block_size, max_lpc_order, adaptive_mid_side, mid_side, exhaustive_model_search, max_residual_partition_order, 14 if pcmreader.bits_per_sample <= 16 else 30) streaminfo = STREAMINFO(block_size, block_size, (2**24) - 1, 0, pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, 0, md5()) pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, False) # write placeholder metadata blocks such as STREAMINFO and PADDING writer.write_bytes("fLaC") writer.build("1u 7u 24u", [0, 0, 34]) streaminfo_start = writer.getpos() streaminfo.write(writer) writer.build("1u 7u 24u", [1, 1, padding_size]) writer.write_bytes(b"\x00" * padding_size) # walk through PCM reader's FrameLists frame_number = 0 frame = pcmreader.read(block_size) flac_frame = BitstreamRecorder(0) while len(frame) > 0: streaminfo.input_update(frame) flac_frame.reset() encode_flac_frame(flac_frame, pcmreader, options, frame_number, frame) frame_sizes.append((flac_frame.bytes(), frame.frames)) streaminfo.output_update(flac_frame) flac_frame.copy(writer) frame_number += 1 frame = pcmreader.read(block_size) # return to beginning of file and rewrite STREAMINFO block writer.setpos(streaminfo_start) streaminfo.write(writer) writer.flush() writer.close() return frame_sizes
def encode_shn(filename, pcmreader, is_big_endian, signed_samples, header_data, footer_data="", block_size=256): """filename is a string to the output file's path pcmreader is a PCMReader object header_data and footer_data are binary strings block_size is the default size of each Shorten audio command """ pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, 0) left_shift = 0 wrapped_channels = [[] for c in xrange(pcmreader.channels)] # write magic number and version writer.build("4b 8u", ["ajkg", 2]) bytes_written = __Counter__() writer.add_callback(bytes_written.byte) # write header from PCMReader info and encoding options if pcmreader.bits_per_sample == 8: if signed_samples: write_long(writer, 1) # signed, 8-bit sign_adjustment = 0 else: write_long(writer, 2) # unsigned, 8-bit sign_adjustment = 1 << (pcmreader.bits_per_sample - 1) # 8-bit samples have no endianness elif pcmreader.bits_per_sample == 16: if signed_samples: if is_big_endian: write_long(writer, 3) # signed, 16-bit, big-endian else: write_long(writer, 5) # signed, 16-bit, little-endian sign_adjustment = 0 else: if is_big_endian: write_long(writer, 4) # unsigned, 16-bit, big-endian else: write_long(writer, 6) # unsigned, 16-bit, little-endian sign_adjustment = 1 << (pcmreader.bits_per_sample - 1) else: raise ValueError("unsupported bits_per_sample") write_long(writer, pcmreader.channels) write_long(writer, block_size) write_long(writer, 0) # max LPC write_long(writer, 0) # mean count write_long(writer, 0) # bytes to skip # write header as a VERBATIM block write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM) write_unsigned(writer, VERBATIM_SIZE, len(header_data)) for b in header_data: write_unsigned(writer, VERBATIM_BYTE_SIZE, ord(b)) # split PCMReader into block_size chunks # and continue until the number of PCM frames is 0 frame = pcmreader.read(block_size) while len(frame) > 0: # if the chunk isn't block_size frames long, # issue a command to change it if frame.frames != block_size: block_size = frame.frames write_unsigned(writer, COMMAND_SIZE, FN_BLOCKSIZE) write_long(writer, block_size) # split chunk into individual channels for c in xrange(pcmreader.channels): # convert PCM data to unsigned, if necessary if signed_samples: channel = list(frame.channel(c)) else: channel = [s + sign_adjustment for s in frame.channel(c)] # if all samples are 0, issue a ZERO command if all_zeroes(channel): write_unsigned(writer, COMMAND_SIZE, FN_ZERO) # wrap zeroes around for next set of channels wrapped_channels[c] = channel else: # if channel's shifted bits have changed # from the previous channel's shift # issue a new BITSHIFT command wasted_bits = wasted_bps(channel) if wasted_bits != left_shift: write_unsigned(writer, COMMAND_SIZE, FN_BITSHIFT) write_unsigned(writer, BITSHIFT_SIZE, wasted_bits) left_shift = wasted_bits # and shift the channel's bits if the amount is still > 0 if left_shift > 0: shifted = [s >> left_shift for s in channel] else: shifted = channel # determine the best DIFF command and residuals # to issue for shifted channel data (diff, residuals) = best_diff(wrapped_channels[c], shifted) # determine the best energy size for DIFF's residuals energy = best_energy(residuals) # write DIFF command, energy size and residuals write_unsigned(writer, COMMAND_SIZE, {1: FN_DIFF1, 2: FN_DIFF2, 3: FN_DIFF3}[diff]) write_unsigned(writer, ENERGY_SIZE, energy) for residual in residuals: write_signed(writer, energy, residual) # wrap shifted channels around for next set of channels wrapped_channels[c] = shifted # and get another set of channels to encode frame = pcmreader.read(block_size) # once all PCM data has been sent # if there's any footer data, write it as another VERBATIM block if len(footer_data) > 0: write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM) write_unsigned(writer, VERBATIM_SIZE, len(footer_data)) for b in footer_data: write_unsigned(writer, VERBATIM_BYTE_SIZE, ord(b)) # issue a QUIT command write_unsigned(writer, COMMAND_SIZE, FN_QUIT) # finally, due to Shorten's silly way of using bit buffers, # output (not counting the 5 bytes of magic + version) # must be padded to a multiple of 4 bytes # or its reference decoder explodes writer.byte_align() while (int(bytes_written) % 4) != 0: writer.write(8, 0)
def encode_wavpack(filename, pcmreader, block_size, correlation_passes=0, wave_header=None, wave_footer=None): pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, 1) context = EncoderContext( pcmreader, block_parameters(pcmreader.channels, pcmreader.channel_mask, correlation_passes), wave_header, wave_footer, ) block_index = 0 # walk through PCM reader's FrameLists frame = pcmreader.read(block_size * (pcmreader.bits_per_sample / 8) * pcmreader.channels) while len(frame) > 0: context.total_frames += frame.frames context.md5sum.update(frame.to_bytes(False, pcmreader.bits_per_sample >= 16)) c = 0 for parameters in context.block_parameters: if parameters.channel_count == 1: channel_data = [list(frame.channel(c))] else: channel_data = [list(frame.channel(c)), list(frame.channel(c + 1))] first_block = parameters is context.block_parameters[0] last_block = parameters is context.block_parameters[-1] context.block_offsets.append(output_file.tell()) write_block(writer, context, channel_data, block_index, first_block, last_block, parameters) c += parameters.channel_count block_index += frame.frames frame = pcmreader.read(block_size * (pcmreader.bits_per_sample / 8) * pcmreader.channels) # write MD5 sum and optional Wave footer in final block sub_blocks = BitstreamRecorder(1) sub_block = BitstreamRecorder(1) sub_block.reset() sub_block.write_bytes(context.md5sum.digest()) write_sub_block(sub_blocks, WV_MD5, 1, sub_block) # write Wave footer in final block, if present if context.wave_footer is not None: sub_block.reset() sub_block.write_bytes(context.wave_footer) write_sub_block(sub_blocks, WV_WAVE_FOOTER, 1, sub_block) write_block_header( writer, sub_blocks.bytes(), 0xFFFFFFFF, 0, pcmreader.bits_per_sample, 1, 0, 0, 0, 1, 1, 0, pcmreader.sample_rate, 0, 0xFFFFFFFF, ) sub_blocks.copy(writer) # update Wave header's "data" chunk size, if generated if context.wave_header is None: output_file.seek(32 + 2) if context.wave_footer is None: write_wave_header(writer, context.pcmreader, context.total_frames, 0) else: write_wave_header(writer, context.pcmreader, context.total_frames, len(context.wave_footer)) # go back and populate block headers with total samples for block_offset in context.block_offsets: output_file.seek(block_offset + 12, 0) writer.write(32, block_index) writer.close()
class AudioPlayer(object): def __init__(self, audio_output, next_track_callback=lambda: None, replay_gain=RG_NO_REPLAYGAIN): """audio_output is an AudioOutput object to play audio to next_track_callback is an optional function which is called with no arguments when the current track is finished""" self.__state__ = PLAYER_STOPPED self.__audio_output__ = audio_output self.__next_track_callback__ = next_track_callback self.__audiofile__ = None self.__pcmreader__ = None self.__buffer_size__ = 1 self.__replay_gain__ = replay_gain self.__current_frames__ = 0 self.__total_frames__ = 1 def set_audiofile(self, audiofile): """sets audiofile to play""" self.__audiofile__ = audiofile def state(self): """returns current state of player which is one of: PLAYER_STOPPED, PLAYER_PAUSED, PLAYER_PLAYING""" return self.__state__ def progress(self): """returns current progress as a (current frames, total frames) tuple""" return (self.__current_frames__, self.__total_frames__) def stop(self): """changes current state of player to PLAYER_STOPPED""" if self.__state__ == PLAYER_STOPPED: # already stopped, so nothing to do return else: if self.__state__ == PLAYER_PAUSED: self.__audio_output__.resume() self.__state__ = PLAYER_STOPPED self.__pcmreader__ = None self.__current_frames__ = 0 self.__total_frames__ = 1 def pause(self): """if playing, changes current state of player to PLAYER_PAUSED""" # do nothing if player is stopped or already paused if self.__state__ == PLAYER_PLAYING: self.__audio_output__.pause() self.__state__ = PLAYER_PAUSED def play(self): """if audiofile has been opened, changes current state of player to PLAYER_PLAYING""" from audiotools import BufferedPCMReader if self.__state__ == PLAYER_PLAYING: # already playing, so nothing to do return elif self.__state__ == PLAYER_PAUSED: # go from unpaused to playing self.__audio_output__.resume() self.__state__ = PLAYER_PLAYING elif (self.__state__ == PLAYER_STOPPED) and (self.__audiofile__ is not None): # go from stopped to playing # if an audiofile has been opened # get PCMReader from selected audiofile pcmreader = self.__audiofile__.to_pcm() # apply ReplayGain if requested if self.__replay_gain__ in (RG_TRACK_GAIN, RG_ALBUM_GAIN): gain = self.__audiofile__.get_replay_gain() if gain is not None: from audiotools.replaygain import ReplayGainReader if self.__replay_gain__ == RG_TRACK_GAIN: pcmreader = ReplayGainReader(pcmreader, gain.track_gain, gain.track_peak) else: pcmreader = ReplayGainReader(pcmreader, gain.album_gain, gain.album_peak) # buffer PCMReader so that one can process small chunks of data self.__pcmreader__ = BufferedPCMReader(pcmreader) # calculate quarter second buffer size # (or at least 256 samples) self.__buffer_size__ = max(int(round(0.25 * pcmreader.sample_rate)), 256) # set output to be compatible with PCMReader self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample, ) # reset progress self.__current_frames__ = 0 self.__total_frames__ = self.__audiofile__.total_frames() # update state so audio begins playing self.__state__ = PLAYER_PLAYING def output_audio(self): """if player is playing, output the next chunk of audio if possible if audio is exhausted, stop playing and call the next_track callback""" if self.__state__ == PLAYER_PLAYING: try: frame = self.__pcmreader__.read(self.__buffer_size__) except (IOError, ValueError) as err: # some sort of read error occurred # so cease playing file and move on to next self.stop() if callable(self.__next_track_callback__): self.__next_track_callback__() return if len(frame) > 0: self.__current_frames__ += frame.frames self.__audio_output__.play(frame) else: # audio has been exhausted self.stop() if callable(self.__next_track_callback__): self.__next_track_callback__() def run(self, commands, responses): """runs the audio playing thread while accepting commands from the given Queue""" try: from queue import Empty except ImportError: from Queue import Empty while True: try: (command, args) = commands.get(self.__state__ != PLAYER_PLAYING) # got a command to process if command == "open": # stop whatever's playing and prepare new track for playing self.stop() self.set_audiofile(args[0]) elif command == "play": self.play() elif command == "set_replay_gain": self.__replay_gain__ = args[0] elif command == "set_output": # resume (if necessary) and close existing output if self.__state__ == PLAYER_PAUSED: self.__audio_output__.resume() self.__audio_output__.close() # set new output and set format (if necessary) self.__audio_output__ = args[0] if self.__pcmreader__ is not None: self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample, ) # if paused, reset audio output to paused if self.__state__ == PLAYER_PAUSED: self.__audio_output__.pause() elif command == "pause": self.pause() elif command == "toggle_play_pause": # switch from paused to playing or playing to paused if self.__state__ == PLAYER_PAUSED: self.play() elif self.__state__ == PLAYER_PLAYING: self.pause() elif command == "stop": self.stop() self.__audio_output__.close() elif command == "close": self.stop() self.__audio_output__.close() return except Empty: # no commands to process # so output audio if playing self.output_audio()
def encode_shn(filename, pcmreader, is_big_endian, signed_samples, header_data, footer_data="", block_size=256): """filename is a string to the output file's path pcmreader is a PCMReader object header_data and footer_data are binary strings block_size is the default size of each Shorten audio command """ pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, 0) left_shift = 0 wrapped_channels = [[] for c in xrange(pcmreader.channels)] #write magic number and version writer.build("4b 8u", ["ajkg", 2]) bytes_written = __Counter__() writer.add_callback(bytes_written.byte) #write header from PCMReader info and encoding options if (pcmreader.bits_per_sample == 8): if (signed_samples): write_long(writer, 1) # signed, 8-bit sign_adjustment = 0 else: write_long(writer, 2) # unsigned, 8-bit sign_adjustment = 1 << (pcmreader.bits_per_sample - 1) #8-bit samples have no endianness elif (pcmreader.bits_per_sample == 16): if (signed_samples): if (is_big_endian): write_long(writer, 3) # signed, 16-bit, big-endian else: write_long(writer, 5) # signed, 16-bit, little-endian sign_adjustment = 0 else: if (is_big_endian): write_long(writer, 4) # unsigned, 16-bit, big-endian else: write_long(writer, 6) # unsigned, 16-bit, little-endian sign_adjustment = 1 << (pcmreader.bits_per_sample - 1) else: raise ValueError("unsupported bits_per_sample") write_long(writer, pcmreader.channels) write_long(writer, block_size) write_long(writer, 0) # max LPC write_long(writer, 0) # mean count write_long(writer, 0) # bytes to skip #write header as a VERBATIM block write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM) write_unsigned(writer, VERBATIM_SIZE, len(header_data)) for b in header_data: write_unsigned(writer, VERBATIM_BYTE_SIZE, ord(b)) #split PCMReader into block_size chunks #and continue until the number of PCM frames is 0 frame = pcmreader.read(block_size) while (len(frame) > 0): #if the chunk isn't block_size frames long, #issue a command to change it if (frame.frames != block_size): block_size = frame.frames write_unsigned(writer, COMMAND_SIZE, FN_BLOCKSIZE) write_long(writer, block_size) #split chunk into individual channels for c in xrange(pcmreader.channels): #convert PCM data to unsigned, if necessary if (signed_samples): channel = list(frame.channel(c)) else: channel = [s + sign_adjustment for s in frame.channel(c)] #if all samples are 0, issue a ZERO command if (all_zeroes(channel)): write_unsigned(writer, COMMAND_SIZE, FN_ZERO) #wrap zeroes around for next set of channels wrapped_channels[c] = channel else: #if channel's shifted bits have changed #from the previous channel's shift #issue a new BITSHIFT command wasted_bits = wasted_bps(channel) if (wasted_bits != left_shift): write_unsigned(writer, COMMAND_SIZE, FN_BITSHIFT) write_unsigned(writer, BITSHIFT_SIZE, wasted_bits) left_shift = wasted_bits #and shift the channel's bits if the amount is still > 0 if (left_shift > 0): shifted = [s >> left_shift for s in channel] else: shifted = channel #determine the best DIFF command and residuals #to issue for shifted channel data (diff, residuals) = best_diff(wrapped_channels[c], shifted) #determine the best energy size for DIFF's residuals energy = best_energy(residuals) #write DIFF command, energy size and residuals write_unsigned(writer, COMMAND_SIZE, { 1: FN_DIFF1, 2: FN_DIFF2, 3: FN_DIFF3 }[diff]) write_unsigned(writer, ENERGY_SIZE, energy) for residual in residuals: write_signed(writer, energy, residual) #wrap shifted channels around for next set of channels wrapped_channels[c] = shifted #and get another set of channels to encode frame = pcmreader.read(block_size) #once all PCM data has been sent #if there's any footer data, write it as another VERBATIM block if (len(footer_data) > 0): write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM) write_unsigned(writer, VERBATIM_SIZE, len(footer_data)) for b in footer_data: write_unsigned(writer, VERBATIM_BYTE_SIZE, ord(b)) #issue a QUIT command write_unsigned(writer, COMMAND_SIZE, FN_QUIT) #finally, due to Shorten's silly way of using bit buffers, #output (not counting the 5 bytes of magic + version) #must be padded to a multiple of 4 bytes #or its reference decoder explodes writer.byte_align() while ((int(bytes_written) % 4) != 0): writer.write(8, 0)
def encode_flac(filename, pcmreader, block_size=4096, max_lpc_order=8, min_residual_partition_order=0, max_residual_partition_order=5, mid_side=True, adaptive_mid_side=False, exhaustive_model_search=False, disable_verbatim_subframes=False, disable_constant_subframes=False, disable_fixed_subframes=False, disable_lpc_subframes=False, padding_size=4096): frame_sizes = [] options = Encoding_Options(block_size, max_lpc_order, adaptive_mid_side, mid_side, exhaustive_model_search, max_residual_partition_order, 14 if pcmreader.bits_per_sample <= 16 else 30) streaminfo = STREAMINFO(block_size, block_size, (2 ** 24) - 1, 0, pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, 0, md5()) pcmreader = BufferedPCMReader(pcmreader) output_file = open(filename, "wb") writer = BitstreamWriter(output_file, False) # write placeholder metadata blocks such as STREAMINFO and PADDING writer.write_bytes("fLaC") writer.build("1u 7u 24u", [0, 0, 34]) streaminfo_start = writer.getpos() streaminfo.write(writer) writer.build("1u 7u 24u", [1, 1, padding_size]) writer.write_bytes(b"\x00" * padding_size) # walk through PCM reader's FrameLists frame_number = 0 frame = pcmreader.read(block_size) flac_frame = BitstreamRecorder(0) while len(frame) > 0: streaminfo.input_update(frame) flac_frame.reset() encode_flac_frame(flac_frame, pcmreader, options, frame_number, frame) frame_sizes.append((flac_frame.bytes(), frame.frames)) streaminfo.output_update(flac_frame) flac_frame.copy(writer) frame_number += 1 frame = pcmreader.read(block_size) # return to beginning of file and rewrite STREAMINFO block writer.setpos(streaminfo_start) streaminfo.write(writer) writer.flush() writer.close() return frame_sizes
class AudioPlayer(object): def __init__(self, audio_output, next_track_callback=lambda: None, replay_gain=RG_NO_REPLAYGAIN): """audio_output is an AudioOutput object to play audio to next_track_callback is an optional function which is called with no arguments when the current track is finished""" self.__state__ = PLAYER_STOPPED self.__audio_output__ = audio_output self.__next_track_callback__ = next_track_callback self.__audiofile__ = None self.__pcmreader__ = None self.__buffer_size__ = 1 self.__replay_gain__ = replay_gain self.__current_frames__ = 0 self.__total_frames__ = 1 def set_audiofile(self, audiofile): """sets audiofile to play""" self.__audiofile__ = audiofile def state(self): """returns current state of player which is one of: PLAYER_STOPPED, PLAYER_PAUSED, PLAYER_PLAYING""" return self.__state__ def progress(self): """returns current progress as a (current frames, total frames) tuple""" return (self.__current_frames__, self.__total_frames__) def stop(self): """changes current state of player to PLAYER_STOPPED""" if (self.__state__ == PLAYER_STOPPED): # already stopped, so nothing to do return else: if (self.__state__ == PLAYER_PAUSED): self.__audio_output__.resume() self.__state__ = PLAYER_STOPPED self.__pcmreader__ = None self.__current_frames__ = 0 self.__total_frames__ = 1 def pause(self): """if playing, changes current state of player to PLAYER_PAUSED""" # do nothing if player is stopped or already paused if (self.__state__ == PLAYER_PLAYING): self.__audio_output__.pause() self.__state__ = PLAYER_PAUSED def play(self): """if audiofile has been opened, changes current state of player to PLAYER_PLAYING""" from audiotools import BufferedPCMReader if (self.__state__ == PLAYER_PLAYING): # already playing, so nothing to do return elif (self.__state__ == PLAYER_PAUSED): # go from unpaused to playing self.__audio_output__.resume() self.__state__ = PLAYER_PLAYING elif ((self.__state__ == PLAYER_STOPPED) and (self.__audiofile__ is not None)): # go from stopped to playing # if an audiofile has been opened # get PCMReader from selected audiofile pcmreader = self.__audiofile__.to_pcm() # apply ReplayGain if requested if (self.__replay_gain__ in (RG_TRACK_GAIN, RG_ALBUM_GAIN)): gain = self.__audiofile__.get_replay_gain() if (gain is not None): from audiotools.replaygain import ReplayGainReader if (self.__replay_gain__ == RG_TRACK_GAIN): pcmreader = ReplayGainReader(pcmreader, gain.track_gain, gain.track_peak) else: pcmreader = ReplayGainReader(pcmreader, gain.album_gain, gain.album_peak) # buffer PCMReader so that one can process small chunks of data self.__pcmreader__ = BufferedPCMReader(pcmreader) # calculate quarter second buffer size # (or at least 256 samples) self.__buffer_size__ = max( int(round(0.25 * pcmreader.sample_rate)), 256) # set output to be compatible with PCMReader self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample) # reset progress self.__current_frames__ = 0 self.__total_frames__ = self.__audiofile__.total_frames() # update state so audio begins playing self.__state__ = PLAYER_PLAYING def output_audio(self): """if player is playing, output the next chunk of audio if possible if audio is exhausted, stop playing and call the next_track callback""" if (self.__state__ == PLAYER_PLAYING): try: frame = self.__pcmreader__.read(self.__buffer_size__) except (IOError, ValueError) as err: # some sort of read error occurred # so cease playing file and move on to next self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__() return if (len(frame) > 0): self.__current_frames__ += frame.frames self.__audio_output__.play(frame) else: # audio has been exhausted self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__() def run(self, commands, responses): """runs the audio playing thread while accepting commands from the given Queue""" try: from queue import Empty except ImportError: from Queue import Empty while (True): try: (command, args) = commands.get(self.__state__ != PLAYER_PLAYING) # got a command to process if (command == "open"): # stop whatever's playing and prepare new track for playing self.stop() self.set_audiofile(args[0]) elif (command == "play"): self.play() elif (command == "set_replay_gain"): self.__replay_gain__ = args[0] elif (command == "set_output"): # resume (if necessary) and close existing output if (self.__state__ == PLAYER_PAUSED): self.__audio_output__.resume() self.__audio_output__.close() # set new output and set format (if necessary) self.__audio_output__ = args[0] if (self.__pcmreader__ is not None): self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample) # if paused, reset audio output to paused if (self.__state__ == PLAYER_PAUSED): self.__audio_output__.pause() elif (command == "pause"): self.pause() elif (command == "toggle_play_pause"): # switch from paused to playing or playing to paused if (self.__state__ == PLAYER_PAUSED): self.play() elif (self.__state__ == PLAYER_PLAYING): self.pause() elif (command == "stop"): self.stop() self.__audio_output__.close() elif (command == "close"): self.stop() self.__audio_output__.close() return except Empty: # no commands to process # so output audio if playing self.output_audio()
class AudioPlayer: def __init__(self, audio_output, next_track_callback=lambda: None): """audio_output is an AudioOutput object to play audio to next_track_callback is an optional function which is called with no arguments when the current track is finished""" self.__state__ = PLAYER_STOPPED self.__audio_output__ = audio_output self.__next_track_callback__ = next_track_callback self.__audiofile__ = None self.__pcmreader__ = None self.__buffer_size__ = 1 self.__replay_gain__ = RG_NO_REPLAYGAIN self.__current_frames__ = 0 self.__total_frames__ = 1 def set_audiofile(self, audiofile): """sets audiofile to play""" self.__audiofile__ = audiofile def state(self): """returns current state of player which is one of: PLAYER_STOPPED, PLAYER_PAUSED, PLAYER_PLAYING""" return self.__state__ def progress(self): """returns current progress as a (current frames, total frames) tuple""" return (self.__current_frames__, self.__total_frames__) def stop(self): """changes current state of player to PLAYER_STOPPED""" if (self.__state__ == PLAYER_STOPPED): #already stopped, so nothing to do return else: if (self.__state__ == PLAYER_PAUSED): self.__audio_output__.resume() self.__state__ = PLAYER_STOPPED self.__pcmreader__.close() self.__pcmreader__ = None self.__current_frames__ = 0 self.__total_frames__ = 1 def pause(self): """if playing, changes current state of player to PLAYER_PAUSED""" #do nothing if player is stopped or already paused if (self.__state__ == PLAYER_PLAYING): self.__audio_output__.pause() self.__state__ = PLAYER_PAUSED def play(self): """if audiofile has been opened, changes current state of player to PLAYER_PLAYING""" from audiotools import BufferedPCMReader if (self.__state__ == PLAYER_PLAYING): #already playing, so nothing to do return elif (self.__state__ == PLAYER_PAUSED): #go from unpaused to playing self.__audio_output__.resume() self.__state__ = PLAYER_PLAYING elif ((self.__state__ == PLAYER_STOPPED) and (self.__audiofile__ is not None)): #go from stopped to playing #if an audiofile has been opened #get PCMReader from selected audiofile pcmreader = self.__audiofile__.to_pcm() #apply ReplayGain if requested if (self.__replay_gain__ in (RG_TRACK_GAIN, RG_ALBUM_GAIN)): gain = self.__audiofile__.replay_gain() if (gain is not None): from audiotools.replaygain import ReplayGainReader if (replay_gain == RG_TRACK_GAIN): pcmreader = ReplayGainReader(pcmreader, gain.track_gain, gain.track_peak) else: pcmreader = ReplayGainReader(pcmreader, gain.album_gain, gain.album_peak) #buffer PCMReader so that one can process small chunks of data self.__pcmreader__ = BufferedPCMReader(pcmreader) #calculate quarter second buffer size #(or at least 256 samples) self.__buffer_size__ = max(int(round(0.25 * pcmreader.sample_rate)), 256) #set output to be compatible with PCMReader self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample) #reset progress self.__current_frames__ = 0 self.__total_frames__ = self.__audiofile__.total_frames() #update state so audio begins playing self.__state__ = PLAYER_PLAYING def output_audio(self): """if player is playing, output the next chunk of audio if possible if audio is exhausted, stop playing and call the next_track callback""" if (self.__state__ == PLAYER_PLAYING): try: frame = self.__pcmreader__.read(self.__buffer_size__) except (IOError, ValueError), err: #some sort of read error occurred #so cease playing file and move on to next self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__() return if (len(frame) > 0): self.__current_frames__ += frame.frames self.__audio_output__.play(frame) else: #audio has been exhausted self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__()
class AudioPlayer: def __init__(self, audio_output, next_track_callback=lambda: None): """audio_output is an AudioOutput object to play audio to next_track_callback is an optional function which is called with no arguments when the current track is finished""" self.__state__ = PLAYER_STOPPED self.__audio_output__ = audio_output self.__next_track_callback__ = next_track_callback self.__audiofile__ = None self.__pcmreader__ = None self.__buffer_size__ = 1 self.__replay_gain__ = RG_NO_REPLAYGAIN self.__current_frames__ = 0 self.__total_frames__ = 1 def set_audiofile(self, audiofile): """sets audiofile to play""" self.__audiofile__ = audiofile def state(self): """returns current state of player which is one of: PLAYER_STOPPED, PLAYER_PAUSED, PLAYER_PLAYING""" return self.__state__ def progress(self): """returns current progress as a (current frames, total frames) tuple""" return (self.__current_frames__, self.__total_frames__) def stop(self): """changes current state of player to PLAYER_STOPPED""" if (self.__state__ == PLAYER_STOPPED): #already stopped, so nothing to do return else: if (self.__state__ == PLAYER_PAUSED): self.__audio_output__.resume() self.__state__ = PLAYER_STOPPED self.__pcmreader__.close() self.__pcmreader__ = None self.__current_frames__ = 0 self.__total_frames__ = 1 def pause(self): """if playing, changes current state of player to PLAYER_PAUSED""" #do nothing if player is stopped or already paused if (self.__state__ == PLAYER_PLAYING): self.__audio_output__.pause() self.__state__ = PLAYER_PAUSED def play(self): """if audiofile has been opened, changes current state of player to PLAYER_PLAYING""" from audiotools import BufferedPCMReader if (self.__state__ == PLAYER_PLAYING): #already playing, so nothing to do return elif (self.__state__ == PLAYER_PAUSED): #go from unpaused to playing self.__audio_output__.resume() self.__state__ = PLAYER_PLAYING elif ((self.__state__ == PLAYER_STOPPED) and (self.__audiofile__ is not None)): #go from stopped to playing #if an audiofile has been opened #get PCMReader from selected audiofile pcmreader = self.__audiofile__.to_pcm() #apply ReplayGain if requested if (self.__replay_gain__ in (RG_TRACK_GAIN, RG_ALBUM_GAIN)): gain = self.__audiofile__.replay_gain() if (gain is not None): from audiotools.replaygain import ReplayGainReader if (replay_gain == RG_TRACK_GAIN): pcmreader = ReplayGainReader(pcmreader, gain.track_gain, gain.track_peak) else: pcmreader = ReplayGainReader(pcmreader, gain.album_gain, gain.album_peak) #buffer PCMReader so that one can process small chunks of data self.__pcmreader__ = BufferedPCMReader(pcmreader) #calculate quarter second buffer size #(or at least 256 samples) self.__buffer_size__ = max( int(round(0.25 * pcmreader.sample_rate)), 256) #set output to be compatible with PCMReader self.__audio_output__.set_format( sample_rate=self.__pcmreader__.sample_rate, channels=self.__pcmreader__.channels, channel_mask=self.__pcmreader__.channel_mask, bits_per_sample=self.__pcmreader__.bits_per_sample) #reset progress self.__current_frames__ = 0 self.__total_frames__ = self.__audiofile__.total_frames() #update state so audio begins playing self.__state__ = PLAYER_PLAYING def output_audio(self): """if player is playing, output the next chunk of audio if possible if audio is exhausted, stop playing and call the next_track callback""" if (self.__state__ == PLAYER_PLAYING): try: frame = self.__pcmreader__.read(self.__buffer_size__) except (IOError, ValueError), err: #some sort of read error occurred #so cease playing file and move on to next self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__() return if (len(frame) > 0): self.__current_frames__ += frame.frames self.__audio_output__.play(frame) else: #audio has been exhausted self.stop() if (callable(self.__next_track_callback__)): self.__next_track_callback__()