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()
Beispiel #2
0
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))
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #6
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
Beispiel #7
0
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)
Beispiel #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()
Beispiel #9
0
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()
Beispiel #10
0
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)
Beispiel #11
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
Beispiel #12
0
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__()
Beispiel #14
0
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__()