Esempio n. 1
0
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 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
Esempio n. 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)
    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))
Esempio n. 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, 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)
Esempio n. 5
0
    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
Esempio n. 6
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)
Esempio n. 7
0
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new MP2Audio object"""

        from audiotools import (PCMConverter, BufferedPCMReader, ChannelMask,
                                __default_quality__, EncodingError)
        from audiotools.encoders import encode_mp2
        import bisect

        if (((compression is None)
             or (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        if (pcmreader.sample_rate in (32000, 48000, 44100)):
            sample_rate = pcmreader.sample_rate
        else:
            sample_rate = [32000, 32000, 44100,
                           48000][bisect.bisect([32000, 44100, 48000],
                                                pcmreader.sample_rate)]

        if (total_pcm_frames is not None):
            from audiotools import CounterPCMReader
            pcmreader = CounterPCMReader(pcmreader)

        try:
            encode_mp2(
                filename,
                BufferedPCMReader(
                    PCMConverter(pcmreader,
                                 sample_rate=sample_rate,
                                 channels=min(pcmreader.channels, 2),
                                 channel_mask=ChannelMask.from_channels(
                                     min(pcmreader.channels, 2)),
                                 bits_per_sample=16)), int(compression))

            if ((total_pcm_frames is not None)
                    and (total_pcm_frames != pcmreader.frames_written)):
                from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                cls.__unlink__(filename)
                raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

            return MP2Audio(filename)
        except (ValueError, IOError) as err:
            cls.__unlink__(filename)
            raise EncodingError(str(err))
        finally:
            pcmreader.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, 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)
Esempio n. 9
0
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AudioFile-compatible object

        may raise EncodingError if some problem occurs when
        encoding the input file.  This includes an error
        in the input stream, a problem writing the output file,
        or even an EncodingError subclass such as
        "UnsupportedBitsPerSample" if the input stream
        is formatted in a way this class is unable to support
        """

        from audiotools import (BufferedPCMReader, PCMConverter,
                                __default_quality__, EncodingError)
        from audiotools.encoders import encode_opus

        if (((compression is None)
             or (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        if ((pcmreader.channels > 2) and (pcmreader.channels <= 8)):
            channel_mask = int(pcmreader.channel_mask)
            if ((channel_mask != 0) and (channel_mask not in (
                    0x7,  # FR, FC, FL
                    0x33,  # FR, FL, BR, BL
                    0x37,  # FR, FC, FL, BL, BR
                    0x3f,  # FR, FC, FL, BL, BR, LFE
                    0x70f,  # FL, FC, FR, SL, SR, BC, LFE
                    0x63f))):  # FL, FC, FR, SL, SR, BL, BR, LFE
                raise UnsupportedChannelMask(filename, channel_mask)

        try:
            encode_opus(filename,
                        BufferedPCMReader(
                            PCMConverter(pcmreader,
                                         sample_rate=48000,
                                         channels=pcmreader.channels,
                                         channel_mask=pcmreader.channel_mask,
                                         bits_per_sample=16)),
                        quality=int(compression),
                        original_sample_rate=pcmreader.sample_rate)

            return cls(filename)
        except (ValueError, IOError), err:
            cls.__unlink__(filename)
            raise EncodingError(err)
Esempio n. 10
0
    def from_pcm(cls, filename, pcmreader,
                 compression=None, total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new VorbisAudio object"""

        from audiotools import (BufferedPCMReader,
                                __default_quality__,
                                EncodingError)
        from audiotools.encoders import encode_vorbis

        if (((compression is None) or
             (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        if ((pcmreader.channels > 2) and (pcmreader.channels <= 8)):
            channel_mask = int(pcmreader.channel_mask)
            if ((channel_mask != 0) and
                (channel_mask not in
                 (0x7,       # FR, FC, FL
                  0x33,      # FR, FL, BR, BL
                  0x37,      # FR, FC, FL, BL, BR
                  0x3f,      # FR, FC, FL, BL, BR, LFE
                  0x70f,     # FL, FC, FR, SL, SR, BC, LFE
                  0x63f))):  # FL, FC, FR, SL, SR, BL, BR, LFE
                from audiotools import UnsupportedChannelMask
                raise UnsupportedChannelMask(filename, channel_mask)

        if (total_pcm_frames is not None):
            from audiotools import CounterPCMReader
            pcmreader = CounterPCMReader(pcmreader)
        try:

            encode_vorbis(filename,
                          BufferedPCMReader(pcmreader),
                          float(compression) / 10)

            if ((total_pcm_frames is not None) and
                (total_pcm_frames != pcmreader.frames_written)):
                from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                cls.__unlink__(filename)
                raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

            return VorbisAudio(filename)
        except (ValueError, IOError) as err:
            cls.__unlink__(filename)
            raise EncodingError(str(err))
        finally:
            pcmreader.close()
Esempio n. 11
0
    def from_pcm(cls, filename, pcmreader,
                 compression=None,
                 total_pcm_frames=None,
                 encoding_function=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new WavPackAudio object"""

        from audiotools.encoders import encode_wavpack
        from audiotools import BufferedPCMReader
        from audiotools import CounterPCMReader
        from audiotools import EncodingError
        from audiotools import __default_quality__

        if (((compression is None) or
             (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        counter = CounterPCMReader(pcmreader)

        try:
            (encode_wavpack if encoding_function is None
             else encoding_function)(
                filename,
                BufferedPCMReader(counter),
                total_pcm_frames=(total_pcm_frames if
                                  total_pcm_frames is not None else 0),
                **cls.__options__[compression])
            counter.close()
        except (ValueError, IOError) as msg:
            counter.close()
            cls.__unlink__(filename)
            raise EncodingError(str(msg))
        except Exception:
            counter.close()
            cls.__unlink__(filename)
            raise

        # ensure actual total PCM frames matches argument, if any
        if (((total_pcm_frames is not None) and
             (counter.frames_written != total_pcm_frames))):
            cls.__unlink__(filename)
            from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
            raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

        return cls(filename)
    def play(self):
        """if track has been selected,
        changes current state of player to PLAYER_PLAYING"""

        from audiotools import (BufferedPCMReader,
                                ThreadedPCMReader,
                                PCMReaderHead)

        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.__track_number__ is not None)):
            # go from stopped to playing
            # if a track number has been selected

            # seek to specified track number
            self.__cddareader__.seek(self.__offsets__[self.__track_number__])
            track = PCMReaderHead(self.__cddareader__,
                                  self.__lengths__[self.__track_number__],
                                  False)

            # decode PCMReader in thread
            # and place in buffer so one can process small chunks of data
            self.__pcmreader__ = BufferedPCMReader(ThreadedPCMReader(track))

            # calculate quarter second buffer size
            self.__buffer_size__ = int(round(0.25 * 44100))

            # set output to be compatible with PCMReader
            self.__audio_output__.set_format(
                sample_rate=44100,
                channels=2,
                channel_mask=0x3,
                bits_per_sample=16)

            # reset progress
            self.__current_frames__ = 0
            self.__total_frames__ = self.__lengths__[self.__track_number__]

            # update state so audio begins playing
            self.__state__ = PLAYER_PLAYING
Esempio n. 13
0
    def from_pcm(cls, filename, pcmreader, compression=None):
        """Encodes a new file from PCM data.

        Takes a filename string, PCMReader object
        and optional compression level string.
        Encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new WavPackAudio object."""

        from . import encoders

        if ((compression is None)
                or (compression not in cls.COMPRESSION_MODES)):
            compression = __default_quality__(cls.NAME)

        try:
            encoders.encode_wavpack(filename, BufferedPCMReader(pcmreader),
                                    **cls.__options__[compression])

            return cls(filename)
        except (ValueError, IOError), msg:
            cls.__unlink__(filename)
            raise EncodingError(str(msg))
Esempio n. 14
0
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new MP3Audio object"""

        from audiotools import (PCMConverter, BufferedPCMReader, ChannelMask,
                                __default_quality__, EncodingError)
        from audiotools.encoders import encode_mp3

        if (((compression is None)
             or (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        try:
            encode_mp3(
                filename,
                BufferedPCMReader(
                    PCMConverter(pcmreader,
                                 sample_rate=pcmreader.sample_rate,
                                 channels=min(pcmreader.channels, 2),
                                 channel_mask=ChannelMask.from_channels(
                                     min(pcmreader.channels, 2)),
                                 bits_per_sample=16)), compression)

            return MP3Audio(filename)
        except (ValueError, IOError), err:
            cls.__unlink__(filename)
            raise EncodingError(str(err))
Esempio n. 15
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
Esempio n. 16
0
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None,
                 encoding_function=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AudioFile-compatible object

        may raise EncodingError if some problem occurs when
        encoding the input file.  This includes an error
        in the input stream, a problem writing the output file,
        or even an EncodingError subclass such as
        "UnsupportedBitsPerSample" if the input stream
        is formatted in a way this class is unable to support
        """

        from audiotools import (BufferedPCMReader, CounterPCMReader,
                                transfer_data, EncodingError)
        # from audiotools.py_encoders import encode_tta
        from audiotools.encoders import encode_tta
        from audiotools.bitstream import BitstreamWriter

        # open output file right away
        # so we can fail as soon as possible
        try:
            file = open(filename, "wb")
        except IOError as err:
            pcmreader.close()
            raise EncodingError(str(err))

        writer = BitstreamWriter(file, True)
        counter = CounterPCMReader(pcmreader)
        try:
            if (total_pcm_frames is not None):
                # write header to disk
                write_header(writer, pcmreader.channels,
                             pcmreader.bits_per_sample, pcmreader.sample_rate,
                             total_pcm_frames)

                block_size = (pcmreader.sample_rate * 256) // 245
                total_tta_frames = div_ceil(total_pcm_frames, block_size)

                # write temporary seektable to disk
                writer.mark()
                write_seektable(writer, [0] * total_tta_frames)
                writer.flush()

                # write frames to disk
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(file,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # ensure written number of PCM frames
                # matches total_pcm_frames
                if (counter.frames_written != total_pcm_frames):
                    from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                    cls.__unlink__(filename)
                    raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

                assert (len(frame_sizes) == total_tta_frames)

                # go back and rewrite seektable with completed one
                writer.rewind()
                write_seektable(writer, frame_sizes)
                writer.unmark()
            else:
                import tempfile

                frames = tempfile.TemporaryFile()

                # encode TTA frames to temporary file
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(frames,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    frames.close()
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # write header to disk
                write_header(writer, pcmreader.channels,
                             pcmreader.bits_per_sample, pcmreader.sample_rate,
                             counter.frames_written)

                # write seektable to disk
                write_seektable(writer, frame_sizes)

                # transfer TTA frames from temporary space to disk
                frames.seek(0, 0)
                transfer_data(frames.read, writer.write_bytes)
                frames.close()
        finally:
            counter.close()
            if (writer.has_mark()):
                writer.unmark()
            writer.close()

        return cls(filename)
Esempio n. 17
0
    def from_wave(cls, filename, header, pcmreader, footer, compression=None,
                  encoding_function=None):
        """encodes a new file from wave data

        takes a filename string, header string,
        PCMReader object, footer string
        and optional compression level string
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new WaveAudio object

        header + pcm data + footer should always result
        in the original wave file being restored
        without need for any padding bytes

        may raise EncodingError if some problem occurs when
        encoding the input file"""

        from audiotools.encoders import encode_wavpack
        from audiotools import BufferedPCMReader
        from audiotools import CounterPCMReader
        from audiotools.wav import (validate_header, validate_footer)
        from audiotools import EncodingError
        from audiotools import __default_quality__

        if (((compression is None) or
             (compression not in cls.COMPRESSION_MODES))):
            compression = __default_quality__(cls.NAME)

        # ensure header is valid
        try:
            (total_size, data_size) = validate_header(header)
        except ValueError as err:
            raise EncodingError(str(err))

        counter = CounterPCMReader(pcmreader)

        try:
            (encode_wavpack if encoding_function is None
             else encoding_function)(filename,
                                     BufferedPCMReader(counter),
                                     wave_header=header,
                                     wave_footer=footer,
                                     **cls.__options__[compression])

            counter.close()

            data_bytes_written = counter.bytes_written()

            # ensure output data size matches the "data" chunk's size
            if (data_size != data_bytes_written):
                from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK
                raise EncodingError(ERR_WAV_TRUNCATED_DATA_CHUNK)

            # ensure footer validates correctly
            try:
                validate_footer(footer, data_bytes_written)
            except ValueError as err:
                raise EncodingError(str(err))

            # ensure total size is correct
            if ((len(header) + data_size + len(footer)) != total_size):
                from audiotools.text import ERR_WAV_INVALID_SIZE
                raise EncodingError(ERR_WAV_INVALID_SIZE)

            return cls(filename)
        except (ValueError, IOError) as msg:
            counter.close()
            cls.__unlink__(filename)
            raise EncodingError(str(msg))
        except Exception as err:
            counter.close()
            cls.__unlink__(filename)
            raise err
Esempio n. 18
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__()
Esempio n. 19
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)
Esempio n. 20
0
    def from_aiff(cls,
                  filename,
                  header,
                  pcmreader,
                  footer,
                  compression=None,
                  block_size=256,
                  encoding_function=None):
        """encodes a new file from AIFF data

        takes a filename string, header string,
        PCMReader object, footer string
        and optional compression level string
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AiffAudio object

        header + pcm data + footer should always result
        in the original AIFF file being restored
        without need for any padding bytes

        may raise EncodingError if some problem occurs when
        encoding the input file"""

        from audiotools import (CounterPCMReader, BufferedPCMReader,
                                UnsupportedBitsPerSample, EncodingError)
        from audiotools.aiff import (validate_header, validate_footer)

        if (encoding_function is None):
            from audiotools.encoders import encode_shn
        else:
            encode_shn = encoding_function

        if (pcmreader.bits_per_sample not in (8, 16)):
            raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample)

        # ensure header is valid
        try:
            (total_size, ssnd_size) = validate_header(header)
        except ValueError as err:
            raise EncodingError(str(err))

        counter = CounterPCMReader(pcmreader)

        try:
            if (len(footer) == 0):
                encode_shn(filename=filename,
                           pcmreader=BufferedPCMReader(counter),
                           is_big_endian=True,
                           signed_samples=True,
                           header_data=header,
                           block_size=block_size)
            else:
                encode_shn(filename=filename,
                           pcmreader=BufferedPCMReader(counter),
                           is_big_endian=True,
                           signed_samples=True,
                           header_data=header,
                           footer_data=footer,
                           block_size=block_size)

            ssnd_bytes_written = counter.bytes_written()

            # ensure output data size matches the "SSND" chunk's size
            if (ssnd_size != ssnd_bytes_written):
                from audiotools.text import ERR_AIFF_TRUNCATED_SSND_CHUNK
                raise EncodingError(ERR_AIFF_TRUNCATED_SSND_CHUNK)

            # ensure footer validates correctly
            try:
                validate_footer(footer, ssnd_bytes_written)
            except ValueError as err:
                raise EncodingError(str(err))

            # ensure total size is correct
            if ((len(header) + ssnd_size + len(footer)) != total_size):
                from audiotools.text import ERR_AIFF_INVALID_SIZE
                raise EncodingError(ERR_AIFF_INVALID_SIZE)

            return cls(filename)
        except IOError as err:
            cls.__unlink__(filename)
            raise EncodingError(str(err))
        except Exception as err:
            cls.__unlink__(filename)
            raise err
Esempio n. 21
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()
Esempio n. 22
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()
Esempio n. 23
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)
Esempio n. 24
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
Esempio n. 25
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()
Esempio n. 26
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__()