Exemplo n.º 1
0
        def tags(file, order):
            while True:
                reader = BitstreamReader(file, order)
                # read all the tags in an IFD
                tag_count = reader.read(16)
                sub_reader = reader.substream(tag_count * 12)
                next_ifd = reader.read(32)

                for i in range(tag_count):
                    (tag_code,
                     tag_datatype,
                     tag_value_count) = sub_reader.parse("16u 16u 32u")
                    if tag_datatype == 1:    # BYTE type
                        tag_struct = "8u" * tag_value_count
                    elif tag_datatype == 3:  # SHORT type
                        tag_struct = "16u" * tag_value_count
                    elif tag_datatype == 4:  # LONG type
                        tag_struct = "32u" * tag_value_count
                    else:                      # all other types
                        tag_struct = "4b"
                    if format_size(tag_struct) <= 32:
                        yield (tag_code, sub_reader.parse(tag_struct))
                        sub_reader.skip(32 - format_size(tag_struct))
                    else:
                        offset = sub_reader.read(32)
                        file.seek(offset, 0)
                        yield (tag_code,
                               BitstreamReader(file, order).parse(tag_struct))

                if next_ifd != 0:
                    file.seek(next_ifd, 0)
                else:
                    break
        def tags(file, order):
            while True:
                reader = BitstreamReader(file, order)
                # read all the tags in an IFD
                tag_count = reader.read(16)
                sub_reader = reader.substream(tag_count * 12)
                next_ifd = reader.read(32)

                for i in range(tag_count):
                    (tag_code, tag_datatype,
                     tag_value_count) = sub_reader.parse("16u 16u 32u")
                    if tag_datatype == 1:  # BYTE type
                        tag_struct = "8u" * tag_value_count
                    elif tag_datatype == 3:  # SHORT type
                        tag_struct = "16u" * tag_value_count
                    elif tag_datatype == 4:  # LONG type
                        tag_struct = "32u" * tag_value_count
                    else:  # all other types
                        tag_struct = "4b"
                    if format_size(tag_struct) <= 32:
                        yield (tag_code, sub_reader.parse(tag_struct))
                        sub_reader.skip(32 - format_size(tag_struct))
                    else:
                        offset = sub_reader.read(32)
                        file.seek(offset, 0)
                        yield (tag_code,
                               BitstreamReader(file, order).parse(tag_struct))

                if next_ifd != 0:
                    file.seek(next_ifd, 0)
                else:
                    break
Exemplo n.º 3
0
    def get_metadata(self):
        """returns a MetaData object, or None

        raises IOError if unable to read the file"""

        from cStringIO import StringIO
        from audiotools.bitstream import BitstreamReader
        from audiotools.ogg import PacketReader, PageReader
        from audiotools.vorbiscomment import VorbisComment

        reader = PacketReader(PageReader(open(self.filename, "rb")))

        identification = reader.read_packet()
        comment = BitstreamReader(StringIO(reader.read_packet()), True)

        (packet_type, packet_header) = comment.parse("8u 6b")
        if ((packet_type == 3) and (packet_header == 'vorbis')):
            vendor_string = \
                comment.read_bytes(comment.read(32)).decode('utf-8')
            comment_strings = [
                comment.read_bytes(comment.read(32)).decode('utf-8')
                for i in xrange(comment.read(32))]
            if (comment.read(1) == 1):   # framing bit
                return VorbisComment(comment_strings, vendor_string)
            else:
                return None
        else:
            return None
Exemplo n.º 4
0
    def get_replay_gain(self):
        """returns a ReplayGain object of our ReplayGain values

        returns None if we have no values

        may raise IOError if unable to read the file"""

        from audiotools import ReplayGain

        try:
            rg = BitstreamReader(self.get_block(b"RG"), False)
        except KeyError:
            return None

        version = rg.read(8)
        if version != 1:
            return None

        gain_title = rg.read(16)
        peak_title = rg.read(16)
        gain_album = rg.read(16)
        peak_album = rg.read(16)

        if ((gain_title == 0) and (peak_title == 0) and
            (gain_album == 0) and (peak_album == 0)):
            return None
        else:
            return ReplayGain(
                track_gain=64.82 - float(gain_title) / 256,
                track_peak=(10 ** (float(peak_title) / 256 / 20)) / 2 ** 15,
                album_gain=64.82 - float(gain_album) / 256,
                album_peak=(10 ** (float(peak_album) / 256 / 20)) / 2 ** 15)
Exemplo n.º 5
0
    def get_metadata(self):
        """returns a MetaData object, or None

        raises IOError if unable to read the file"""

        from io import BytesIO
        from audiotools.bitstream import BitstreamReader
        from audiotools.ogg import PacketReader, PageReader

        reader = PacketReader(PageReader(open(self.filename, "rb")))

        identification = reader.read_packet()
        comment = BitstreamReader(BytesIO(reader.read_packet()), True)

        if (comment.read_bytes(8) == "OpusTags"):
            vendor_string = \
                comment.read_bytes(comment.read(32)).decode('utf-8')
            comment_strings = [
                comment.read_bytes(comment.read(32)).decode('utf-8')
                for i in range(comment.read(32))
            ]

            return VorbisComment(comment_strings, vendor_string)
        else:
            return None
Exemplo n.º 6
0
    def get_replay_gain(self):
        """returns a ReplayGain object of our ReplayGain values

        returns None if we have no values

        may raise IOError if unable to read the file"""

        from audiotools import ReplayGain

        try:
            rg = BitstreamReader(self.get_block(b"RG"), False)
        except KeyError:
            return None

        version = rg.read(8)
        if version != 1:
            return None

        gain_title = rg.read(16)
        peak_title = rg.read(16)
        gain_album = rg.read(16)
        peak_album = rg.read(16)

        if ((gain_title == 0) and (peak_title == 0) and (gain_album == 0)
                and (peak_album == 0)):
            return None
        else:
            return ReplayGain(
                track_gain=64.82 - float(gain_title) / 256,
                track_peak=(10**(float(peak_title) / 256 / 20)) / 2**15,
                album_gain=64.82 - float(gain_album) / 256,
                album_peak=(10**(float(peak_album) / 256 / 20)) / 2**15)
Exemplo n.º 7
0
    def __read_identification__(self):
        from .bitstream import BitstreamReader

        f = open(self.filename, "rb")
        try:
            ogg_reader = BitstreamReader(f, 1)
            (magic_number,
             version,
             header_type,
             granule_position,
             self.__serial_number__,
             page_sequence_number,
             checksum,
             segment_count) = ogg_reader.parse("4b 8u 8u 64S 32u 32u 32u 8u")

            if (magic_number != 'OggS'):
                from .text import ERR_OGG_INVALID_MAGIC_NUMBER
                raise InvalidFLAC(ERR_OGG_INVALID_MAGIC_NUMBER)
            if (version != 0):
                from .text import ERR_OGG_INVALID_VERSION
                raise InvalidFLAC(ERR_OGG_INVALID_VERSION)

            segment_length = ogg_reader.read(8)

            (vorbis_type,
             header,
             version,
             self.__channels__,
             self.__sample_rate__,
             maximum_bitrate,
             nominal_bitrate,
             minimum_bitrate,
             blocksize0,
             blocksize1,
             framing) = ogg_reader.parse(
                "8u 6b 32u 8u 32u 32u 32u 32u 4u 4u 1u")

            if (vorbis_type != 1):
                from .text import ERR_VORBIS_INVALID_TYPE
                raise InvalidVorbis(ERR_VORBIS_INVALID_TYPE)
            if (header != 'vorbis'):
                from .text import ERR_VORBIS_INVALID_HEADER
                raise InvalidVorbis(ERR_VORBIS_INVALID_HEADER)
            if (version != 0):
                from .text import ERR_VORBIS_INVALID_VERSION
                raise InvalidVorbis(ERR_VORBIS_INVALID_VERSION)
            if (framing != 1):
                from .text import ERR_VORBIS_INVALID_FRAMING_BIT
                raise InvalidVorbis(ERR_VORBIS_INVALID_FRAMING_BIT)
        finally:
            f.close()
Exemplo n.º 8
0
    def get_metadata(self):
        """returns a MetaData object, or None

        raises IOError if unable to read the file"""

        from io import BytesIO
        from audiotools.bitstream import BitstreamReader
        from audiotools.ogg import PacketReader, PageReader
        from audiotools.vorbiscomment import VorbisComment

        with PacketReader(PageReader(open(self.filename, "rb"))) as reader:
            identification = reader.read_packet()

            comment = BitstreamReader(BytesIO(reader.read_packet()), True)

            vendor_string = \
                comment.read_bytes(comment.read(32)).decode('utf-8')

            comment_strings = [
                comment.read_bytes(comment.read(32)).decode('utf-8')
                for i in range(comment.read(32))]

            return VorbisComment(comment_strings, vendor_string)
Exemplo n.º 9
0
    def get_metadata(self):
        """returns a MetaData object, or None

        raises IOError if unable to read the file"""

        from audiotools.ogg import PacketReader,PageReader
        from cStringIO import StringIO
        from audiotools.bitstream import BitstreamReader

        packets = PacketReader(PageReader(open(self.filename, "rb")))

        identification = packets.read_packet()
        comment = BitstreamReader(StringIO(packets.read_packet()), True)

        if (comment.read_bytes(8) == "OpusTags"):
            vendor_string = \
                comment.read_bytes(comment.read(32)).decode('utf-8')
            comment_strings = [
                comment.read_bytes(comment.read(32)).decode('utf-8')
                for i in xrange(comment.read(32))]
            return VorbisComment(comment_strings, vendor_string)
        else:
            return None
Exemplo n.º 10
0
    def __init__(self, filename):
        """filename is a plain string"""

        AudioFile.__init__(self, filename)
        try:
            block = BitstreamReader(self.get_block(b"SH"), False)
            crc = block.read(32)
            if block.read(8) != 8:
                from audiotools.text import ERR_MPC_INVALID_VERSION
                raise InvalidMPC(ERR_MPC_INVALID_VERSION)
            self.__samples__ = int(MPC_Size.parse(block))
            beg_silence = int(MPC_Size.parse(block))
            self.__sample_rate__ = \
                [44100, 48000, 37800, 32000][block.read(3)]
            max_band = block.read(5) + 1
            self.__channels__ = block.read(4) + 1
            ms = block.read(1)
            block_pwr = block.read(3) * 2
        except IOError as err:
            raise InvalidMPC(str(err))
Exemplo n.º 11
0
    def __init__(self, filename):
        """filename is a plain string"""

        AudioFile.__init__(self, filename)
        try:
            block = BitstreamReader(self.get_block(b"SH"), False)
            crc = block.read(32)
            if block.read(8) != 8:
                from audiotools.text import ERR_MPC_INVALID_VERSION
                raise InvalidMPC(ERR_MPC_INVALID_VERSION)
            self.__samples__ = int(MPC_Size.parse(block))
            beg_silence = int(MPC_Size.parse(block))
            self.__sample_rate__ = \
                [44100, 48000, 37800, 32000][block.read(3)]
            max_band = block.read(5) + 1
            self.__channels__ = block.read(4) + 1
            ms = block.read(1)
            block_pwr = block.read(3) * 2
        except IOError as err:
            raise InvalidMPC(str(err))
Exemplo n.º 12
0
class FlacDecoder:
    CHANNEL_COUNT = [1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2,
                     None, None, None, None, None]

    (SUBFRAME_CONSTANT,
     SUBFRAME_VERBATIM,
     SUBFRAME_FIXED,
     SUBFRAME_LPC) = range(4)

    def __init__(self, filename, channel_mask):
        self.reader = BitstreamReader(open(filename, "rb"), 0)

        if (self.reader.read_bytes(4) != 'fLaC'):
            raise ValueError("invalid FLAC file")

        self.current_md5sum = md5()

        #locate the STREAMINFO,
        #which is sometimes needed to handle non-subset streams
        for (block_id,
             block_size,
             block_reader) in self.metadata_blocks(self.reader):
            if (block_id == 0):
                #read STREAMINFO
                self.minimum_block_size = block_reader.read(16)
                self.maximum_block_size = block_reader.read(16)
                self.minimum_frame_size = block_reader.read(24)
                self.maximum_frame_size = block_reader.read(24)
                self.sample_rate = block_reader.read(20)
                self.channels = block_reader.read(3) + 1
                self.channel_mask = channel_mask
                self.bits_per_sample = block_reader.read(5) + 1
                self.total_frames = block_reader.read64(36)
                self.md5sum = block_reader.read_bytes(16)

                #these are frame header lookup tables
                #which vary slightly depending on STREAMINFO's values
                self.BLOCK_SIZE = [self.maximum_block_size,
                                   192,  576,  1152,
                                   2304, 4608,  None,  None,
                                   256,  512,  1024,  2048,
                                   4096, 8192, 16384, 32768]
                self.SAMPLE_RATE = [self.sample_rate,
                                    88200, 176400, 192000,
                                    8000,  16000,  22050, 24000,
                                    32000,  44100,  48000, 96000,
                                    None,   None,   None,  None]
                self.BITS_PER_SAMPLE = [self.bits_per_sample,
                                        8, 12, None, 16, 20, 24, None]

    def metadata_blocks(self, reader):
        """yields a (block_id, block_size, block_reader) tuple
        per metadata block where block_reader is a BitstreamReader substream"""

        (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u")
        while (last_block == 0):
            yield (block_id, block_size, self.reader.substream(block_size))
            (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u")
        else:
            yield (block_id, block_size, self.reader.substream(block_size))

    def read(self, pcm_frames):
        #if the stream is exhausted,
        #verify its MD5 sum and return an empty pcm.FrameList object
        if (self.total_frames < 1):
            if (self.md5sum == self.current_md5sum.digest()):
                return from_list([], self.channels, self.bits_per_sample, True)
            else:
                raise ValueError("MD5 checksum mismatch")

        crc16 = CRC16()
        self.reader.add_callback(crc16.update)

        #fetch the decoding parameters from the frame header
        (block_size,
         channel_assignment,
         bits_per_sample) = self.read_frame_header()
        channel_count = self.CHANNEL_COUNT[channel_assignment]
        if (channel_count is None):
            raise ValueError("invalid channel assignment")

        #channel data will be a list of signed sample lists, one per channel
        #such as  [[1, 2, 3, ...], [4, 5, 6, ...]]  for a 2 channel stream
        channel_data = []

        for channel_number in xrange(channel_count):
            if ((channel_assignment == 0x8) and (channel_number == 1)):
                #for left-difference assignment
                #the difference channel has 1 additional bit
                channel_data.append(self.read_subframe(block_size,
                                                       bits_per_sample + 1))
            elif ((channel_assignment == 0x9) and (channel_number == 0)):
                #for difference-right assignment
                #the difference channel has 1 additional bit
                channel_data.append(self.read_subframe(block_size,
                                                       bits_per_sample + 1))
            elif ((channel_assignment == 0xA) and (channel_number == 1)):
                #for mid-side assignment
                #the side channel has 1 additional bit
                channel_data.append(self.read_subframe(block_size,
                                                       bits_per_sample + 1))
            else:
                #otherwise, use the frame's bits-per-sample value
                channel_data.append(self.read_subframe(block_size,
                                                       bits_per_sample))

        #one all the subframes have been decoded,
        #reconstruct them depending on the channel assignment
        if (channel_assignment == 0x8):
            #left-difference
            samples = []
            for (left, difference) in zip(*channel_data):
                samples.append(left)
                samples.append(left - difference)
        elif (channel_assignment == 0x9):
            #difference-right
            samples = []
            for (difference, right) in zip(*channel_data):
                samples.append(difference + right)
                samples.append(right)
        elif (channel_assignment == 0xA):
            #mid-side
            samples = []
            for (mid, side) in zip(*channel_data):
                samples.append((((mid * 2) + (side % 2)) + side) / 2)
                samples.append((((mid * 2) + (side % 2)) - side) / 2)
        else:
            #independent
            samples = [0] * block_size * channel_count
            for (i, channel) in enumerate(channel_data):
                samples[i::channel_count] = channel

        self.reader.byte_align()

        #read and verify the frame's trailing CRC-16 footer
        self.reader.read(16)
        self.reader.pop_callback()
        if (int(crc16) != 0):
            raise ValueError("CRC16 mismatch in frame footer")

        #deduct the amount of PCM frames from the remaining amount
        self.total_frames -= block_size

        #build a pcm.FrameList object from the combined samples
        framelist = from_list(samples, channel_count, bits_per_sample, True)

        #update the running MD5 sum calculation with the frame's data
        self.current_md5sum.update(framelist.to_bytes(0, 1))

        #and finally return the frame data
        return framelist

    def read_frame_header(self):
        crc8 = CRC8()
        self.reader.add_callback(crc8.update)

        #read the 32-bit FLAC frame header
        sync_code = self.reader.read(14)
        if (sync_code != 0x3FFE):
            raise ValueError("invalid sync code")

        self.reader.skip(1)
        blocking_strategy = self.reader.read(1)
        block_size_bits = self.reader.read(4)
        sample_rate_bits = self.reader.read(4)
        channel_assignment = self.reader.read(4)
        bits_per_sample_bits = self.reader.read(3)
        self.reader.skip(1)

        #the frame number is a UTF-8 encoded value
        #which takes a variable number of whole bytes
        frame_number = self.read_utf8()

        #unpack the 4 bit block size field
        #which is the total PCM frames in the FLAC frame
        #and may require up to 16 more bits if the frame is usually-sized
        #(which typically happens at the end of the stream)
        if (block_size_bits == 0x6):
            block_size = self.reader.read(8) + 1
        elif (block_size_bits == 0x7):
            block_size = self.reader.read(16) + 1
        else:
            block_size = self.BLOCK_SIZE[block_size_bits]

        #unpack the 4 bit sample rate field
        #which is used for playback, but not needed for decoding
        #and may require up to 16 more bits
        #if the stream has a particularly unusual sample rate
        if (sample_rate_bits == 0xC):
            sample_rate = self.reader.read(8) * 1000
        elif (sample_rate_bits == 0xD):
            sample_rate = self.reader.read(16)
        elif (sample_rate_bits == 0xE):
            sample_rate = self.reader.read(16) * 10
        elif (sample_rate_bits == 0xF):
            raise ValueError("invalid sample rate")
        else:
            sample_rate = self.SAMPLE_RATE[sample_rate_bits]

        #unpack the 3 bit bits-per-sample field
        #this never requires additional bits
        if ((bits_per_sample_bits == 0x3) or (bits_per_sample_bits == 0x7)):
            raise ValueError("invalid bits per sample")
        else:
            bits_per_sample = self.BITS_PER_SAMPLE[bits_per_sample_bits]

        #read and verify frame's CRC-8 value
        self.reader.read(8)
        self.reader.pop_callback()
        if (int(crc8) != 0):
            raise ValueError("CRC8 mismatch in frame header")

        return (block_size, channel_assignment, bits_per_sample)

    def read_subframe_header(self):
        """returns a tuple of (subframe_type, subframe_order, wasted_bps)"""

        self.reader.skip(1)
        subframe_type = self.reader.read(6)
        if (self.reader.read(1) == 1):
            wasted_bps = self.reader.unary(1) + 1
        else:
            wasted_bps = 0

        #extract "order" value from 6 bit subframe type, if necessary
        if (subframe_type == 0):
            return (self.SUBFRAME_CONSTANT, None, wasted_bps)
        elif (subframe_type == 1):
            return (self.SUBFRAME_VERBATIM, None, wasted_bps)
        elif ((subframe_type & 0x38) == 0x08):
            return (self.SUBFRAME_FIXED, subframe_type & 0x07, wasted_bps)
        elif ((subframe_type & 0x20) == 0x20):
            return (self.SUBFRAME_LPC, (subframe_type & 0x1F) + 1, wasted_bps)
        else:
            raise ValueError("invalid subframe type")

    def read_subframe(self, block_size, bits_per_sample):
        (subframe_type,
         subframe_order,
         wasted_bps) = self.read_subframe_header()

        #read a list of signed sample values
        #depending on the subframe type, block size,
        #adjusted bits per sample and optional subframe order
        if (subframe_type == self.SUBFRAME_CONSTANT):
            subframe_samples = self.read_constant_subframe(
                block_size, bits_per_sample - wasted_bps)
        elif (subframe_type == self.SUBFRAME_VERBATIM):
            subframe_samples = self.read_verbatim_subframe(
                block_size, bits_per_sample - wasted_bps)
        elif (subframe_type == self.SUBFRAME_FIXED):
            subframe_samples = self.read_fixed_subframe(
                block_size, bits_per_sample - wasted_bps, subframe_order)
        else:
            subframe_samples = self.read_lpc_subframe(
                block_size, bits_per_sample - wasted_bps, subframe_order)

        #account for wasted bits-per-sample, if necessary
        if (wasted_bps):
            return [sample << wasted_bps for sample in subframe_samples]
        else:
            return subframe_samples

    def read_constant_subframe(self, block_size, bits_per_sample):
        sample = self.reader.read_signed(bits_per_sample)
        return [sample] * block_size

    def read_verbatim_subframe(self, block_size, bits_per_sample):
        return [self.reader.read_signed(bits_per_sample)
                for x in xrange(block_size)]

    def read_fixed_subframe(self, block_size, bits_per_sample, order):
        #"order" number of warm-up samples
        samples = [self.reader.read_signed(bits_per_sample)
                   for i in xrange(order)]

        #"block_size" - "order" number of residual values
        residuals = self.read_residual(block_size, order)

        #which are applied to the warm-up samples
        #depending on the FIXED subframe order
        #and results in "block_size" number of total samples
        if (order == 0):
            return residuals
        elif (order == 1):
            for residual in residuals:
                samples.append(
                    samples[-1] +
                    residual)
            return samples
        elif (order == 2):
            for residual in residuals:
                samples.append(
                    (2 * samples[-1]) -
                    samples[-2] +
                    residual)
            return samples
        elif (order == 3):
            for residual in residuals:
                samples.append(
                    (3 * samples[-1]) -
                    (3 * samples[-2]) +
                    samples[-3] +
                    residual)
            return samples
        elif (order == 4):
            for residual in residuals:
                samples.append(
                    (4 * samples[-1]) -
                    (6 * samples[-2]) +
                    (4 * samples[-3]) -
                    samples[-4] +
                    residual)
            return samples
        else:
            raise ValueError("unsupported FIXED subframe order")

    def read_lpc_subframe(self, block_size, bits_per_sample, order):
        #"order" number of warm-up samples
        samples = [self.reader.read_signed(bits_per_sample)
                   for i in xrange(order)]

        #the size of each QLP coefficient, in bits
        qlp_precision = self.reader.read(4)

        #the amount of right shift to apply
        #during LPC calculation
        #(though this is a signed value, negative shifts are noops
        # in the reference FLAC decoder)
        qlp_shift_needed = max(self.reader.read_signed(5), 0)

        #"order" number of signed QLP coefficients
        qlp_coeffs = [self.reader.read_signed(qlp_precision + 1)
                      for i in xrange(order)]
        #QLP coefficients are applied in reverse order
        qlp_coeffs.reverse()

        #"block_size" - "order" number of residual values
        residuals = self.read_residual(block_size, order)

        #which are applied to the running LPC calculation
        for residual in residuals:
            samples.append((sum([coeff * sample for (coeff, sample) in
                                 zip(qlp_coeffs, samples[-order:])]) >>
                            qlp_shift_needed) + residual)

        return samples

    def read_residual(self, block_size, order):
        residuals = []

        coding_method = self.reader.read(2)
        partition_order = self.reader.read(4)

        #each parititon contains  block_size / 2 ** partition_order
        #number of residuals
        for partition_number in xrange(2 ** partition_order):
            if (partition_number == 0):
                #except for the first partition
                #which contains "order" less than the rest
                residuals.extend(
                    self.read_residual_partition(
                        coding_method,
                        (block_size / 2 ** partition_order) - order))
            else:
                residuals.extend(
                    self.read_residual_partition(
                        coding_method,
                        block_size / 2 ** partition_order))

        return residuals

    def read_residual_partition(self, coding_method, residual_count):
        if (coding_method == 0):
            #the Rice parameters determines the number of
            #least-significant bits to read for each residual
            rice_parameter = self.reader.read(4)
            if (rice_parameter == 0xF):
                escape_code = self.reader.read(5)
                return [self.reader.read_signed(escape_code)
                        for i in xrange(residual_count)]
        elif (coding_method == 1):
            #24 bps streams may use a 5-bit Rice parameter
            #for better compression
            rice_parameter = self.reader.read(5)
            if (rice_parameter == 0x1F):
                escape_code = self.reader.read(5)
                return [self.reader.read_signed(escape_code)
                        for i in xrange(residual_count)]
        else:
            raise ValueError("invalid Rice coding parameter")

        #a list of signed residual values
        partition_residuals = []

        for i in xrange(residual_count):
            msb = self.reader.unary(1)              # most-significant bits
            lsb = self.reader.read(rice_parameter)  # least-significant bits
            value = (msb << rice_parameter) | lsb   # combined into a value
            if (value & 1):   # whose least-significant bit is the sign value
                partition_residuals.append(-(value >> 1) - 1)
            else:
                partition_residuals.append(value >> 1)

        return partition_residuals

    def read_utf8(self):
        total_bytes = self.reader.unary(0)
        value = self.reader.read(7 - total_bytes)
        while (total_bytes > 1):
            value = ((value << 6) | self.reader.parse("2p 6u")[0])
            total_bytes -= 1
        return value

    def close(self):
        self.reader.close()
Exemplo n.º 13
0
class ALACDecoder(object):
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), False)

        self.reader.mark()
        try:
            # locate the "alac" atom
            # which is full of required decoding parameters
            try:
                stsd = self.find_sub_atom(b"moov", b"trak", b"mdia", b"minf",
                                          b"stbl", b"stsd")
            except KeyError:
                raise ValueError("required stsd atom not found")

            (stsd_version, descriptions) = stsd.parse("8u 24p 32u")
            (alac1, alac2, self.samples_per_frame, self.bits_per_sample,
             self.history_multiplier, self.initial_history, self.maximum_k,
             self.channels, self.sample_rate) = stsd.parse(
                 # ignore much of the stuff in the "high" ALAC atom
                 "32p 4b 6P 16p 16p 16p 4P 16p 16p 16p 16p 4P" +
                 # and use the attributes in the "low" ALAC atom instead
                 "32p 4b 4P 32u 8p 8u 8u 8u 8u 8u 16p 32p 32p 32u")

            self.channel_mask = {
                1: 0x0004,
                2: 0x0003,
                3: 0x0007,
                4: 0x0107,
                5: 0x0037,
                6: 0x003F,
                7: 0x013F,
                8: 0x00FF
            }.get(self.channels, 0)

            if ((alac1 != b'alac') or (alac2 != b'alac')):
                raise ValueError("Invalid alac atom")

            # also locate the "mdhd" atom
            # which contains the stream's length in PCM frames
            self.reader.rewind()
            mdhd = self.find_sub_atom(b"moov", b"trak", b"mdia", b"mdhd")
            (version, ) = mdhd.parse("8u 24p")
            if (version == 0):
                (self.total_pcm_frames,
                 ) = mdhd.parse("32p 32p 32p 32u 2P 16p")
            elif (version == 1):
                (self.total_pcm_frames,
                 ) = mdhd.parse("64p 64p 32p 64U 2P 16p")
            else:
                raise ValueError("invalid mdhd version")

            # finally, set our stream to the "mdat" atom
            self.reader.rewind()
            (atom_size, atom_name) = self.reader.parse("32u 4b")
            while (atom_name != b"mdat"):
                self.reader.skip_bytes(atom_size - 8)
                (atom_size, atom_name) = self.reader.parse("32u 4b")

        finally:
            self.reader.unmark()

    def find_sub_atom(self, *atom_names):
        reader = self.reader

        for (last, next_atom) in iter_last(iter(atom_names)):
            try:
                (length, stream_atom) = reader.parse("32u 4b")
                while (stream_atom != next_atom):
                    reader.skip_bytes(length - 8)
                    (length, stream_atom) = reader.parse("32u 4b")
                if (last):
                    return reader.substream(length - 8)
                else:
                    reader = reader.substream(length - 8)
            except IOError:
                raise KeyError(next_atom)

    def read(self, pcm_frames):
        # if the stream is exhausted, return an empty pcm.FrameList object
        if (self.total_pcm_frames == 0):
            return empty_framelist(self.channels, self.bits_per_sample)

        # otherwise, read one ALAC frameset's worth of frame data
        frameset_data = []
        frame_channels = self.reader.read(3) + 1
        while (frame_channels != 0x8):
            frameset_data.extend(self.read_frame(frame_channels))
            frame_channels = self.reader.read(3) + 1
        self.reader.byte_align()

        # reorder the frameset to Wave order, depending on channel count
        if ((self.channels == 1) or (self.channels == 2)):
            pass
        elif (self.channels == 3):
            frameset_data = [
                frameset_data[1], frameset_data[2], frameset_data[0]
            ]
        elif (self.channels == 4):
            frameset_data = [
                frameset_data[1], frameset_data[2], frameset_data[0],
                frameset_data[3]
            ]
        elif (self.channels == 5):
            frameset_data = [
                frameset_data[1], frameset_data[2], frameset_data[0],
                frameset_data[3], frameset_data[4]
            ]
        elif (self.channels == 6):
            frameset_data = [
                frameset_data[1], frameset_data[2], frameset_data[0],
                frameset_data[5], frameset_data[3], frameset_data[4]
            ]
        elif (self.channels == 7):
            frameset_data = [
                frameset_data[1], frameset_data[2], frameset_data[0],
                frameset_data[6], frameset_data[3], frameset_data[4],
                frameset_data[5]
            ]
        elif (self.channels == 8):
            frameset_data = [
                frameset_data[3], frameset_data[4], frameset_data[0],
                frameset_data[7], frameset_data[5], frameset_data[6],
                frameset_data[1], frameset_data[2]
            ]
        else:
            raise ValueError("unsupported channel count")

        framelist = from_channels([
            from_list(channel, 1, self.bits_per_sample, True)
            for channel in frameset_data
        ])

        # deduct PCM frames from remainder
        self.total_pcm_frames -= framelist.frames

        # return samples as a pcm.FrameList object
        return framelist

    def read_frame(self, channel_count):
        """returns a list of PCM sample lists, one per channel"""

        # read the ALAC frame header
        self.reader.skip(16)
        has_sample_count = self.reader.read(1)
        uncompressed_lsb_size = self.reader.read(2)
        uncompressed = self.reader.read(1)
        if (has_sample_count):
            sample_count = self.reader.read(32)
        else:
            sample_count = self.samples_per_frame

        if (uncompressed == 1):
            # if the frame is uncompressed,
            # read the raw, interlaced samples
            samples = [
                self.reader.read_signed(self.bits_per_sample)
                for i in range(sample_count * channel_count)
            ]
            return [samples[i::channel_count] for i in range(channel_count)]
        else:
            # if the frame is compressed,
            # read the interlacing parameters
            interlacing_shift = self.reader.read(8)
            interlacing_leftweight = self.reader.read(8)

            # subframe headers
            subframe_headers = [
                self.read_subframe_header() for i in range(channel_count)
            ]

            # optional uncompressed LSB values
            if (uncompressed_lsb_size > 0):
                uncompressed_lsbs = [
                    self.reader.read(uncompressed_lsb_size * 8)
                    for i in range(sample_count * channel_count)
                ]
            else:
                uncompressed_lsbs = []

            sample_size = (self.bits_per_sample - (uncompressed_lsb_size * 8) +
                           channel_count - 1)

            # and residual blocks
            residual_blocks = [
                self.read_residuals(sample_size, sample_count)
                for i in range(channel_count)
            ]

            # calculate subframe samples based on
            # subframe header's QLP coefficients and QLP shift-needed
            decoded_subframes = [
                self.decode_subframe(header[0], header[1], sample_size,
                                     residuals)
                for (header,
                     residuals) in zip(subframe_headers, residual_blocks)
            ]

            # decorrelate channels according interlacing shift and leftweight
            decorrelated_channels = self.decorrelate_channels(
                decoded_subframes, interlacing_shift, interlacing_leftweight)

            # if uncompressed LSB values are present,
            # prepend them to each sample of each channel
            if (uncompressed_lsb_size > 0):
                channels = []
                for (i, channel) in enumerate(decorrelated_channels):
                    assert (len(channel) == len(
                        uncompressed_lsbs[i::channel_count]))
                    channels.append([
                        s << (uncompressed_lsb_size * 8) | l for (s, l) in zip(
                            channel, uncompressed_lsbs[i::channel_count])
                    ])
                return channels
            else:
                return decorrelated_channels

    def read_subframe_header(self):
        prediction_type = self.reader.read(4)
        qlp_shift_needed = self.reader.read(4)
        rice_modifier = self.reader.read(3)
        qlp_coefficients = [
            self.reader.read_signed(16) for i in range(self.reader.read(5))
        ]

        return (qlp_shift_needed, qlp_coefficients)

    def read_residuals(self, sample_size, sample_count):
        residuals = []
        history = self.initial_history
        sign_modifier = 0
        i = 0

        while (i < sample_count):
            # get an unsigned residual based on "history"
            # and on "sample_size" as a lst resort
            k = min(log2(history // (2**9) + 3), self.maximum_k)

            unsigned = self.read_residual(k, sample_size) + sign_modifier

            # clear out old sign modifier, if any
            sign_modifier = 0

            # change unsigned residual to signed residual
            if (unsigned & 1):
                residuals.append(-((unsigned + 1) // 2))
            else:
                residuals.append(unsigned // 2)

            # update history based on unsigned residual
            if (unsigned <= 0xFFFF):
                history += ((unsigned * self.history_multiplier) -
                            ((history * self.history_multiplier) >> 9))
            else:
                history = 0xFFFF

            # if history gets too small, we may have a block of 0 samples
            # which can be compressed more efficiently
            if ((history < 128) and ((i + 1) < sample_count)):
                zeroes_k = min(7 - log2(history) + ((history + 16) // 64),
                               self.maximum_k)
                zero_residuals = self.read_residual(zeroes_k, 16)
                if (zero_residuals > 0):
                    residuals.extend([0] * zero_residuals)
                    i += zero_residuals

                history = 0

                if (zero_residuals <= 0xFFFF):
                    sign_modifier = 1

            i += 1

        return residuals

    def read_residual(self, k, sample_size):
        msb = self.reader.read_huffman_code(RESIDUAL)
        if (msb == -1):
            return self.reader.read(sample_size)
        elif (k == 0):
            return msb
        else:
            lsb = self.reader.read(k)
            if (lsb > 1):
                return msb * ((1 << k) - 1) + (lsb - 1)
            elif (lsb == 1):
                self.reader.unread(1)
                return msb * ((1 << k) - 1)
            else:
                self.reader.unread(0)
                return msb * ((1 << k) - 1)

    def decode_subframe(self, qlp_shift_needed, qlp_coefficients, sample_size,
                        residuals):
        # first sample is always copied verbatim
        samples = [residuals.pop(0)]

        if (len(qlp_coefficients) < 31):
            # the next "coefficient count" samples
            # are applied as differences to the previous
            for i in range(len(qlp_coefficients)):
                samples.append(
                    truncate_bits(samples[-1] + residuals.pop(0), sample_size))

            # remaining samples are processed much like LPC
            for residual in residuals:
                base_sample = samples[-len(qlp_coefficients) - 1]
                lpc_sum = sum([(s - base_sample) * c
                               for (s,
                                    c) in zip(samples[-len(qlp_coefficients):],
                                              reversed(qlp_coefficients))])
                outval = (1 << (qlp_shift_needed - 1)) + lpc_sum
                outval >>= qlp_shift_needed
                samples.append(
                    truncate_bits(outval + residual + base_sample,
                                  sample_size))

                buf = samples[-len(qlp_coefficients) - 2:-1]

                # error value then adjusts the coefficients table
                if (residual > 0):
                    predictor_num = len(qlp_coefficients) - 1

                    while ((predictor_num >= 0) and residual > 0):
                        val = (buf[0] -
                               buf[len(qlp_coefficients) - predictor_num])

                        sign = sign_only(val)

                        qlp_coefficients[predictor_num] -= sign

                        val *= sign

                        residual -= ((val >> qlp_shift_needed) *
                                     (len(qlp_coefficients) - predictor_num))

                        predictor_num -= 1

                elif (residual < 0):
                    # the same as above, but we break if residual goes positive
                    predictor_num = len(qlp_coefficients) - 1

                    while ((predictor_num >= 0) and residual < 0):
                        val = (buf[0] -
                               buf[len(qlp_coefficients) - predictor_num])

                        sign = -sign_only(val)

                        qlp_coefficients[predictor_num] -= sign

                        val *= sign

                        residual -= ((val >> qlp_shift_needed) *
                                     (len(qlp_coefficients) - predictor_num))

                        predictor_num -= 1
        else:
            # residuals are encoded as simple difference values
            for residual in residuals:
                samples.append(
                    truncate_bits(samples[-1] + residual, sample_size))

        return samples

    def decorrelate_channels(self, channel_data, interlacing_shift,
                             interlacing_leftweight):
        if (len(channel_data) != 2):
            return channel_data
        elif (interlacing_leftweight == 0):
            return channel_data
        else:
            left = []
            right = []
            for (ch1, ch2) in zip(*channel_data):
                right.append(ch1 - ((ch2 * interlacing_leftweight) //
                                    (2**interlacing_shift)))
                left.append(ch2 + right[-1])
            return [left, right]

    def close(self):
        self.reader.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()
Exemplo n.º 14
0
    def __parse_info__(self):
        """generates a cache of sample_rate, bits-per-sample, etc"""

        import re
        import os.path
        from audiotools.bitstream import BitstreamReader

        if (len(self.tracks) == 0):
            return

        # Why is this post-init processing necessary?
        # DVDATrack references DVDATitle
        # so a DVDATitle must exist when DVDATrack is initialized.
        # But because reading this info requires knowing the sector
        # of the first track, we wind up with a circular dependency.
        # Doing a "post-process" pass fixes that problem.

        # find the AOB file of the title's first track
        track_sector = self[0].first_sector
        titleset = re.compile("ATS_%2.2d_\\d\\.AOB" % (self.titleset))
        for aob_path in sorted([self.dvdaudio.files[key] for key in
                                self.dvdaudio.files.keys()
                                if (titleset.match(key))]):
            aob_sectors = os.path.getsize(aob_path) // DVDAudio.SECTOR_SIZE
            if (track_sector > aob_sectors):
                track_sector -= aob_sectors
            else:
                break
        else:
            from audiotools.text import ERR_DVDA_NO_TRACK_SECTOR
            raise ValueError(ERR_DVDA_NO_TRACK_SECTOR)

        # open that AOB file and seek to that track's first sector
        aob_file = open(aob_path, 'rb')
        try:
            aob_file.seek(track_sector * DVDAudio.SECTOR_SIZE)
            aob_reader = BitstreamReader(aob_file, 0)

            # read and validate the pack header
            # (there's one pack header per sector, at the sector's start)
            (sync_bytes,
             marker1,
             current_pts_high,
             marker2,
             current_pts_mid,
             marker3,
             current_pts_low,
             marker4,
             scr_extension,
             marker5,
             bit_rate,
             marker6,
             stuffing_length) = aob_reader.parse(
                "32u 2u 3u 1u 15u 1u 15u 1u 9u 1u 22u 2u 5p 3u")
            aob_reader.skip_bytes(stuffing_length)
            if (sync_bytes != 0x1BA):
                from audiotools.text import ERR_DVDA_INVALID_AOB_SYNC
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_SYNC)
            if (((marker1 != 1) or (marker2 != 1) or (marker3 != 1) or
                 (marker4 != 1) or (marker5 != 1) or (marker6 != 3))):
                from audiotools.text import ERR_DVDA_INVALID_AOB_MARKER
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_MARKER)
            packet_pts = ((current_pts_high << 30) |
                          (current_pts_mid << 15) |
                          current_pts_low)

            # skip packets until one with a stream ID of 0xBD is found
            (start_code,
             stream_id,
             packet_length) = aob_reader.parse("24u 8u 16u")
            if (start_code != 1):
                from audiotools.text import ERR_DVDA_INVALID_AOB_START
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_START)
            while (stream_id != 0xBD):
                aob_reader.skip_bytes(packet_length)
                (start_code,
                 stream_id,
                 packet_length) = aob_reader.parse("24u 8u 16u")
                if (start_code != 1):
                    from audiotools.text import ERR_DVDA_INVALID_AOB_START
                    raise InvalidDVDA(ERR_DVDA_INVALID_AOB_START)

            # parse the PCM/MLP header in the packet data
            (pad1_size,) = aob_reader.parse("16p 8u")
            aob_reader.skip_bytes(pad1_size)
            (stream_id, crc) = aob_reader.parse("8u 8u 8p")
            if (stream_id == 0xA0):  # PCM
                # read a PCM reader
                (pad2_size,
                 first_audio_frame,
                 padding2,
                 group1_bps,
                 group2_bps,
                 group1_sample_rate,
                 group2_sample_rate,
                 padding3,
                 channel_assignment) = aob_reader.parse(
                    "8u 16u 8u 4u 4u 4u 4u 8u 8u")
            else:                    # MLP
                aob_reader.skip_bytes(aob_reader.read(8))  # skip pad2
                # read a total frame size + MLP major sync header
                (total_frame_size,
                 sync_words,
                 stream_type,
                 group1_bps,
                 group2_bps,
                 group1_sample_rate,
                 group2_sample_rate,
                 unknown1,
                 channel_assignment,
                 unknown2) = aob_reader.parse(
                    "4p 12u 16p 24u 8u 4u 4u 4u 4u 11u 5u 48u")

            # return the values indicated by the header
            self.sample_rate = DVDATrack.SAMPLE_RATE[group1_sample_rate]
            self.channels = DVDATrack.CHANNELS[channel_assignment]
            self.channel_mask = DVDATrack.CHANNEL_MASK[channel_assignment]
            self.bits_per_sample = DVDATrack.BITS_PER_SAMPLE[group1_bps]
            self.stream_id = stream_id

        finally:
            aob_file.close()
Exemplo n.º 15
0
class ALACDecoder:
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), 0)

        self.reader.mark()
        try:
            #locate the "alac" atom
            #which is full of required decoding parameters
            try:
                stsd = self.find_sub_atom("moov", "trak", "mdia",
                                          "minf", "stbl", "stsd")
            except KeyError:
                raise ValueError("required stsd atom not found")

            (stsd_version, descriptions) = stsd.parse("8u 24p 32u")
            (alac1,
             alac2,
             self.samples_per_frame,
             self.bits_per_sample,
             self.history_multiplier,
             self.initial_history,
             self.maximum_k,
             self.channels,
             self.sample_rate) = stsd.parse(
                 #ignore much of the stuff in the "high" ALAC atom
                 "32p 4b 6P 16p 16p 16p 4P 16p 16p 16p 16p 4P" +
                 #and use the attributes in the "low" ALAC atom instead
                 "32p 4b 4P 32u 8p 8u 8u 8u 8u 8u 16p 32p 32p 32u")

            self.channel_mask = {1: 0x0004,
                                 2: 0x0003,
                                 3: 0x0007,
                                 4: 0x0107,
                                 5: 0x0037,
                                 6: 0x003F,
                                 7: 0x013F,
                                 8: 0x00FF}.get(self.channels, 0)

            if ((alac1 != 'alac') or (alac2 != 'alac')):
                raise ValueError("Invalid alac atom")

            #also locate the "mdhd" atom
            #which contains the stream's length in PCM frames
            self.reader.rewind()
            mdhd = self.find_sub_atom("moov", "trak", "mdia", "mdhd")
            (version, ) = mdhd.parse("8u 24p")
            if (version == 0):
                (self.total_pcm_frames,) = mdhd.parse(
                    "32p 32p 32p 32u 2P 16p")
            elif (version == 1):
                (self.total_pcm_frames,) = mdhd.parse(
                    "64p 64p 32p 64U 2P 16p")
            else:
                raise ValueError("invalid mdhd version")

            #finally, set our stream to the "mdat" atom
            self.reader.rewind()
            (atom_size, atom_name) = self.reader.parse("32u 4b")
            while (atom_name != "mdat"):
                self.reader.skip_bytes(atom_size - 8)
                (atom_size, atom_name) = self.reader.parse("32u 4b")

        finally:
            self.reader.unmark()

    def find_sub_atom(self, *atom_names):
        reader = self.reader

        for (last, next_atom) in iter_last(iter(atom_names)):
            try:
                (length, stream_atom) = reader.parse("32u 4b")
                while (stream_atom != next_atom):
                    reader.skip_bytes(length - 8)
                    (length, stream_atom) = reader.parse("32u 4b")
                if (last):
                    return reader.substream(length - 8)
                else:
                    reader = reader.substream(length - 8)
            except IOError:
                raise KeyError(next_atom)

    def read(self, pcm_frames):
        #if the stream is exhausted, return an empty pcm.FrameList object
        if (self.total_pcm_frames == 0):
            return from_list([], self.channels, self.bits_per_sample, True)

        #otherwise, read one ALAC frameset's worth of frame data
        frameset_data = []
        frame_channels = self.reader.read(3) + 1
        while (frame_channels != 0x8):
            frameset_data.extend(self.read_frame(frame_channels))
            frame_channels = self.reader.read(3) + 1
        self.reader.byte_align()

        #reorder the frameset to Wave order, depending on channel count
        if ((self.channels == 1) or (self.channels == 2)):
            pass
        elif (self.channels == 3):
            frameset_data = [frameset_data[1],
                             frameset_data[2],
                             frameset_data[0]]
        elif (self.channels == 4):
            frameset_data = [frameset_data[1],
                             frameset_data[2],
                             frameset_data[0],
                             frameset_data[3]]
        elif (self.channels == 5):
            frameset_data = [frameset_data[1],
                             frameset_data[2],
                             frameset_data[0],
                             frameset_data[3],
                             frameset_data[4]]
        elif (self.channels == 6):
            frameset_data = [frameset_data[1],
                             frameset_data[2],
                             frameset_data[0],
                             frameset_data[5],
                             frameset_data[3],
                             frameset_data[4]]
        elif (self.channels == 7):
            frameset_data = [frameset_data[1],
                             frameset_data[2],
                             frameset_data[0],
                             frameset_data[6],
                             frameset_data[3],
                             frameset_data[4],
                             frameset_data[5]]
        elif (self.channels == 8):
            frameset_data = [frameset_data[3],
                             frameset_data[4],
                             frameset_data[0],
                             frameset_data[7],
                             frameset_data[5],
                             frameset_data[6],
                             frameset_data[1],
                             frameset_data[2]]
        else:
            raise ValueError("unsupported channel count")

        framelist = from_channels([from_list(channel,
                                             1,
                                             self.bits_per_sample,
                                             True)
                                   for channel in frameset_data])

        #deduct PCM frames from remainder
        self.total_pcm_frames -= framelist.frames

        #return samples as a pcm.FrameList object
        return framelist

    def read_frame(self, channel_count):
        """returns a list of PCM sample lists, one per channel"""

        #read the ALAC frame header
        self.reader.skip(16)
        has_sample_count = self.reader.read(1)
        uncompressed_lsb_size = self.reader.read(2)
        uncompressed = self.reader.read(1)
        if (has_sample_count):
            sample_count = self.reader.read(32)
        else:
            sample_count = self.samples_per_frame

        if (uncompressed == 1):
            #if the frame is uncompressed,
            #read the raw, interlaced samples
            samples = [self.reader.read_signed(self.bits_per_sample)
                       for i in xrange(sample_count * channel_count)]
            return [samples[i::channel_count] for i in xrange(channel_count)]
        else:
            #if the frame is compressed,
            #read the interlacing parameters
            interlacing_shift = self.reader.read(8)
            interlacing_leftweight = self.reader.read(8)

            #subframe headers
            subframe_headers = [self.read_subframe_header()
                                for i in xrange(channel_count)]

            #optional uncompressed LSB values
            if (uncompressed_lsb_size > 0):
                uncompressed_lsbs = [
                    self.reader.read(uncompressed_lsb_size * 8)
                    for i in xrange(sample_count * channel_count)]
            else:
                uncompressed_lsbs = []

            sample_size = (self.bits_per_sample -
                           (uncompressed_lsb_size * 8) +
                           channel_count - 1)

            #and residual blocks
            residual_blocks = [self.read_residuals(sample_size,
                                                   sample_count)
                               for i in xrange(channel_count)]

            #calculate subframe samples based on
            #subframe header's QLP coefficients and QLP shift-needed
            decoded_subframes = [self.decode_subframe(header[0],
                                                      header[1],
                                                      sample_size,
                                                      residuals)
                                 for (header, residuals) in
                                 zip(subframe_headers,
                                     residual_blocks)]

            #decorrelate channels according interlacing shift and leftweight
            decorrelated_channels = self.decorrelate_channels(
                decoded_subframes,
                interlacing_shift,
                interlacing_leftweight)

            #if uncompressed LSB values are present,
            #prepend them to each sample of each channel
            if (uncompressed_lsb_size > 0):
                channels = []
                for (i, channel) in enumerate(decorrelated_channels):
                    assert(len(channel) ==
                           len(uncompressed_lsbs[i::channel_count]))
                    channels.append([s << (uncompressed_lsb_size * 8) | l
                                     for (s, l) in
                                     zip(channel,
                                         uncompressed_lsbs[i::channel_count])])
                return channels
            else:
                return decorrelated_channels

    def read_subframe_header(self):
        prediction_type = self.reader.read(4)
        qlp_shift_needed = self.reader.read(4)
        rice_modifier = self.reader.read(3)
        qlp_coefficients = [self.reader.read_signed(16)
                            for i in xrange(self.reader.read(5))]

        return (qlp_shift_needed, qlp_coefficients)

    def read_residuals(self, sample_size, sample_count):
        residuals = []
        history = self.initial_history
        sign_modifier = 0
        i = 0

        while (i < sample_count):
            #get an unsigned residual based on "history"
            #and on "sample_size" as a lst resort
            k = min(log2(history / (2 ** 9) + 3), self.maximum_k)

            unsigned = self.read_residual(k, sample_size) + sign_modifier

            #clear out old sign modifier, if any
            sign_modifier = 0

            #change unsigned residual to signed residual
            if (unsigned & 1):
                residuals.append(-((unsigned + 1) / 2))
            else:
                residuals.append(unsigned / 2)

            #update history based on unsigned residual
            if (unsigned <= 0xFFFF):
                history += ((unsigned * self.history_multiplier) -
                            ((history * self.history_multiplier) >> 9))
            else:
                history = 0xFFFF

            #if history gets too small, we may have a block of 0 samples
            #which can be compressed more efficiently
            if ((history < 128) and ((i + 1) < sample_count)):
                zeroes_k = min(7 -
                               log2(history) +
                               ((history + 16) / 64),
                               self.maximum_k)
                zero_residuals = self.read_residual(zeroes_k, 16)
                if (zero_residuals > 0):
                    residuals.extend([0] * zero_residuals)
                    i += zero_residuals

                history = 0

                if (zero_residuals <= 0xFFFF):
                    sign_modifier = 1

            i += 1

        return residuals

    def read_residual(self, k, sample_size):
        msb = self.reader.limited_unary(0, 9)
        if (msb is None):
            return self.reader.read(sample_size)
        elif (k == 0):
            return msb
        else:
            lsb = self.reader.read(k)
            if (lsb > 1):
                return msb * ((1 << k) - 1) + (lsb - 1)
            elif (lsb == 1):
                self.reader.unread(1)
                return msb * ((1 << k) - 1)
            else:
                self.reader.unread(0)
                return msb * ((1 << k) - 1)

    def decode_subframe(self, qlp_shift_needed, qlp_coefficients,
                        sample_size, residuals):
        #first sample is always copied verbatim
        samples = [residuals.pop(0)]

        if (len(qlp_coefficients) < 31):
            #the next "coefficient count" samples
            #are applied as differences to the previous
            for i in xrange(len(qlp_coefficients)):
                samples.append(truncate_bits(samples[-1] + residuals.pop(0),
                                             sample_size))

            #remaining samples are processed much like LPC
            for residual in residuals:
                base_sample = samples[-len(qlp_coefficients) - 1]
                lpc_sum = sum([(s - base_sample) * c for (s, c) in
                               zip(samples[-len(qlp_coefficients):],
                                   reversed(qlp_coefficients))])
                outval = (1 << (qlp_shift_needed - 1)) + lpc_sum
                outval >>= qlp_shift_needed
                samples.append(truncate_bits(outval + residual + base_sample,
                                             sample_size))

                buf = samples[-len(qlp_coefficients) - 2:-1]

                #error value then adjusts the coefficients table
                if (residual > 0):
                    predictor_num = len(qlp_coefficients) - 1

                    while ((predictor_num >= 0) and residual > 0):
                        val = (buf[0] -
                               buf[len(qlp_coefficients) - predictor_num])

                        sign = sign_only(val)

                        qlp_coefficients[predictor_num] -= sign

                        val *= sign

                        residual -= ((val >> qlp_shift_needed) *
                                     (len(qlp_coefficients) - predictor_num))

                        predictor_num -= 1

                elif (residual < 0):
                    #the same as above, but we break if residual goes positive
                    predictor_num = len(qlp_coefficients) - 1

                    while ((predictor_num >= 0) and residual < 0):
                        val = (buf[0] -
                               buf[len(qlp_coefficients) - predictor_num])

                        sign = -sign_only(val)

                        qlp_coefficients[predictor_num] -= sign

                        val *= sign

                        residual -= ((val >> qlp_shift_needed) *
                                     (len(qlp_coefficients) - predictor_num))

                        predictor_num -= 1
        else:
            #residuals are encoded as simple difference values
            for residual in residuals:
                samples.append(truncate_bits(samples[-1] + residual,
                                             sample_size))

        return samples

    def decorrelate_channels(self, channel_data,
                             interlacing_shift, interlacing_leftweight):
        if (len(channel_data) != 2):
            return channel_data
        elif (interlacing_leftweight == 0):
            return channel_data
        else:
            left = []
            right = []
            for (ch1, ch2) in zip(*channel_data):
                right.append(ch1 - ((ch2 * interlacing_leftweight) /
                                    (2 ** interlacing_shift)))
                left.append(ch2 + right[-1])
            return [left, right]

    def close(self):
        pass
Exemplo n.º 16
0
class TTADecoder(object):
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), True)
        crc = CRC32()
        self.reader.add_callback(crc.update)

        # read the header
        (signature, format_, self.channels, self.bits_per_sample,
         self.sample_rate,
         self.total_pcm_frames) = self.reader.parse("4b 16u 16u 16u 32u 32u")

        self.reader.pop_callback()
        header_crc = self.reader.read(32)
        if (int(crc) != header_crc):
            raise ValueError("CRC32 mismatch in header (0x%8.8X != 0x%8.8X)" %
                             (header_crc, int(crc)))

        self.channel_mask = {1: 0x4, 2: 0x3}.get(self.channels, 0)

        total_tta_frames = div_ceil(self.total_pcm_frames * 245,
                                    self.sample_rate * 256)

        self.pcm_frames_per_tta_frame = (self.sample_rate * 256) // 245

        # read the seektable
        crc = CRC32()
        self.reader.add_callback(crc.update)
        self.frame_sizes = [
            self.reader.read(32) for i in range(total_tta_frames)
        ]
        self.reader.pop_callback()
        seektable_crc = self.reader.read(32)
        if (int(crc) != seektable_crc):
            raise ValueError(
                "CRC32 mismatch in seektable (0x%8.8X != 0x%8.8X)" %
                (header_crc, int(crc)))

        self.current_tta_frame = 0

    def read(self, pcm_frames):
        if (self.total_pcm_frames == 0):
            return FrameList("", self.channels, self.bits_per_sample, True,
                             True)

        pcm_frames = min(self.pcm_frames_per_tta_frame, self.total_pcm_frames)

        frame_reader = self.reader.substream(
            self.frame_sizes[self.current_tta_frame])
        crc = CRC32()
        frame_reader.add_callback(crc.update)

        self.total_pcm_frames -= pcm_frames
        self.current_tta_frame += 1

        # setup Rice parameters for each channel
        k0 = [10] * self.channels
        k1 = [10] * self.channels
        sum0 = [2**14] * self.channels
        sum1 = [2**14] * self.channels

        # list of unfiltered output for each channel
        unfiltered = [[] for i in range(self.channels)]

        for f in range(pcm_frames):
            correlated = []

            for (c, ch_output) in enumerate(unfiltered):
                # read most-significant bits
                MSB = frame_reader.unary(0)
                if (MSB == 0):
                    # read least-significant bits
                    unsigned = frame_reader.read(k0[c])
                else:
                    # read least-significant bits
                    LSB = frame_reader.read(k1[c])
                    unshifted = ((MSB - 1) << k1[c]) + LSB
                    unsigned = unshifted + (1 << k0[c])

                    # adjust sum1 and k1
                    sum1[c] += (unshifted - (sum1[c] >> 4))
                    if (sum1[c] < (2**(k1[c] + 4))):
                        k1[c] = max(k1[c] - 1, 0)
                    elif (sum1[c] > (2**(k1[c] + 5))):
                        k1[c] += 1

                # adjust sum0 and k0
                sum0[c] += (unsigned - (sum0[c] >> 4))
                if (sum0[c] < (2**(k0[c] + 4))):
                    k0[c] = max(k0[c] - 1, 0)
                elif (sum0[c] > (2**(k0[c] + 5))):
                    k0[c] += 1

                # apply sign bit
                if ((unsigned % 2) == 1):
                    # positive
                    ch_output.append((unsigned + 1) // 2)
                else:
                    # negative
                    ch_output.append(-(unsigned // 2))

        # check frame's trailing CRC32 now that reading is finished
        frame_reader.byte_align()
        frame_reader.pop_callback()
        frame_crc = frame_reader.read(32)
        if (int(crc) != frame_crc):
            raise ValueError("CRC32 mismatch in frame (0x%8.8X != 0x%8.8X)" %
                             (frame_crc, int(crc)))

        # run hybrid filter on each channel
        filtered = []
        for unfiltered_ch in unfiltered:
            filtered.append(tta_filter(self.bits_per_sample, unfiltered_ch))

        # run fixed order prediction on each channel
        predicted = []
        for filtered_ch in filtered:
            predicted.append(fixed_predictor(self.bits_per_sample,
                                             filtered_ch))

        if (self.channels == 1):
            # send channel as-is
            return from_list(predicted[0], 1, self.bits_per_sample, True)
        else:
            # decorrelate channels
            decorrelated = decorrelate(predicted)

            # return all channels as single FrameList
            return from_channels([
                from_list(decorrelated_ch, 1, self.bits_per_sample, True)
                for decorrelated_ch in decorrelated
            ])

    def close(self):
        # FIXME
        pass
Exemplo n.º 17
0
    def parse(cls, file_data):
        from io import BytesIO

        def tags(file, order):
            while True:
                reader = BitstreamReader(file, order)
                # read all the tags in an IFD
                tag_count = reader.read(16)
                sub_reader = reader.substream(tag_count * 12)
                next_ifd = reader.read(32)

                for i in range(tag_count):
                    (tag_code,
                     tag_datatype,
                     tag_value_count) = sub_reader.parse("16u 16u 32u")
                    if tag_datatype == 1:    # BYTE type
                        tag_struct = "8u" * tag_value_count
                    elif tag_datatype == 3:  # SHORT type
                        tag_struct = "16u" * tag_value_count
                    elif tag_datatype == 4:  # LONG type
                        tag_struct = "32u" * tag_value_count
                    else:                      # all other types
                        tag_struct = "4b"
                    if format_size(tag_struct) <= 32:
                        yield (tag_code, sub_reader.parse(tag_struct))
                        sub_reader.skip(32 - format_size(tag_struct))
                    else:
                        offset = sub_reader.read(32)
                        file.seek(offset, 0)
                        yield (tag_code,
                               BitstreamReader(file, order).parse(tag_struct))

                if next_ifd != 0:
                    file.seek(next_ifd, 0)
                else:
                    break

        file = BytesIO(file_data)
        try:
            byte_order = file.read(2)
            if byte_order == b'II':
                order = 1
            elif byte_order == b'MM':
                order = 0
            else:
                from audiotools.text import ERR_IMAGE_INVALID_TIFF
                raise InvalidTIFF(ERR_IMAGE_INVALID_TIFF)
            reader = BitstreamReader(file, order)
            if reader.read(16) != 42:
                from audiotools.text import ERR_IMAGE_INVALID_TIFF
                raise InvalidTIFF(ERR_IMAGE_INVALID_TIFF)

            initial_ifd = reader.read(32)
            file.seek(initial_ifd, 0)

            width = 0
            height = 0
            bits_per_pixel = 0
            color_count = 0
            for (tag_id, tag_values) in tags(file, order):
                if tag_id == 0x0100:
                    width = tag_values[0]
                elif tag_id == 0x0101:
                    height = tag_values[0]
                elif tag_id == 0x0102:
                    bits_per_pixel = sum(tag_values)
                elif tag_id == 0x0140:
                    color_count = len(tag_values) // 3
        except IOError:
            from audiotools.text import ERR_IMAGE_IOERROR_TIFF
            raise InvalidTIFF(ERR_IMAGE_IOERROR_TIFF)

        return cls(width=width,
                   height=height,
                   bits_per_pixel=bits_per_pixel,
                   color_count=color_count)
Exemplo n.º 18
0
    def parse(cls, file_data):
        from io import BytesIO

        def tags(file, order):
            while True:
                reader = BitstreamReader(file, order)
                # read all the tags in an IFD
                tag_count = reader.read(16)
                sub_reader = reader.substream(tag_count * 12)
                next_ifd = reader.read(32)

                for i in range(tag_count):
                    (tag_code, tag_datatype,
                     tag_value_count) = sub_reader.parse("16u 16u 32u")
                    if tag_datatype == 1:  # BYTE type
                        tag_struct = "8u" * tag_value_count
                    elif tag_datatype == 3:  # SHORT type
                        tag_struct = "16u" * tag_value_count
                    elif tag_datatype == 4:  # LONG type
                        tag_struct = "32u" * tag_value_count
                    else:  # all other types
                        tag_struct = "4b"
                    if format_size(tag_struct) <= 32:
                        yield (tag_code, sub_reader.parse(tag_struct))
                        sub_reader.skip(32 - format_size(tag_struct))
                    else:
                        offset = sub_reader.read(32)
                        file.seek(offset, 0)
                        yield (tag_code,
                               BitstreamReader(file, order).parse(tag_struct))

                if next_ifd != 0:
                    file.seek(next_ifd, 0)
                else:
                    break

        file = BytesIO(file_data)
        try:
            byte_order = file.read(2)
            if byte_order == b'II':
                order = 1
            elif byte_order == b'MM':
                order = 0
            else:
                from audiotools.text import ERR_IMAGE_INVALID_TIFF
                raise InvalidTIFF(ERR_IMAGE_INVALID_TIFF)
            reader = BitstreamReader(file, order)
            if reader.read(16) != 42:
                from audiotools.text import ERR_IMAGE_INVALID_TIFF
                raise InvalidTIFF(ERR_IMAGE_INVALID_TIFF)

            initial_ifd = reader.read(32)
            file.seek(initial_ifd, 0)

            width = 0
            height = 0
            bits_per_pixel = 0
            color_count = 0
            for (tag_id, tag_values) in tags(file, order):
                if tag_id == 0x0100:
                    width = tag_values[0]
                elif tag_id == 0x0101:
                    height = tag_values[0]
                elif tag_id == 0x0102:
                    bits_per_pixel = sum(tag_values)
                elif tag_id == 0x0140:
                    color_count = len(tag_values) // 3
        except IOError:
            from audiotools.text import ERR_IMAGE_IOERROR_TIFF
            raise InvalidTIFF(ERR_IMAGE_IOERROR_TIFF)

        return cls(width=width,
                   height=height,
                   bits_per_pixel=bits_per_pixel,
                   color_count=color_count)
Exemplo n.º 19
0
class TTADecoder:
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), True)
        crc = CRC32()
        self.reader.add_callback(crc.update)

        #read the header
        (signature,
         format_,
         self.channels,
         self.bits_per_sample,
         self.sample_rate,
         self.total_pcm_frames) = self.reader.parse(
            "4b 16u 16u 16u 32u 32u")

        self.reader.pop_callback()
        header_crc = self.reader.read(32)
        if (int(crc) != header_crc):
            raise ValueError(
                "CRC32 mismatch in header (0x%8.8X != 0x%8.8X)" %
                (header_crc, int(crc)))

        self.channel_mask = {1:0x4, 2:0x3}.get(self.channels, 0)

        total_tta_frames = div_ceil(self.total_pcm_frames * 245,
                                    self.sample_rate * 256)

        self.pcm_frames_per_tta_frame = (self.sample_rate * 256) / 245

        #read the seektable
        crc = CRC32()
        self.reader.add_callback(crc.update)
        self.frame_sizes = [self.reader.read(32) for i in
                            xrange(total_tta_frames)]
        self.reader.pop_callback()
        seektable_crc = self.reader.read(32)
        if (int(crc) != seektable_crc):
            raise ValueError(
                "CRC32 mismatch in seektable (0x%8.8X != 0x%8.8X)" %
                (header_crc, int(crc)))

        self.current_tta_frame = 0

    def read(self, pcm_frames):
        if (self.total_pcm_frames == 0):
            return FrameList("",
                             self.channels,
                             self.bits_per_sample,
                             True,
                             True)

        pcm_frames = min(self.pcm_frames_per_tta_frame, self.total_pcm_frames)

        frame_reader = self.reader.substream(
            self.frame_sizes[self.current_tta_frame])
        crc = CRC32()
        frame_reader.add_callback(crc.update)

        self.total_pcm_frames -= pcm_frames
        self.current_tta_frame += 1

        #setup Rice parameters for each channel
        k0 = [10] * self.channels
        k1 = [10] * self.channels
        sum0 = [2 ** 14] * self.channels
        sum1 = [2 ** 14] * self.channels

        #list of unfiltered output for each channel
        unfiltered = [[] for i in xrange(self.channels)]

        for f in xrange(pcm_frames):
            correlated = []

            for (c, ch_output) in enumerate(unfiltered):
                #read most-significant bits
                MSB = frame_reader.unary(0)
                if (MSB == 0):
                    #read least-significant bits
                    unsigned = frame_reader.read(k0[c])
                else:
                    #read least-significant bits
                    LSB = frame_reader.read(k1[c])
                    unshifted = ((MSB - 1) << k1[c]) + LSB
                    unsigned = unshifted + (1 << k0[c])

                    #adjust sum1 and k1
                    sum1[c] += (unshifted - (sum1[c] >> 4))
                    if (sum1[c] < (2 ** (k1[c] + 4))):
                        k1[c] = max(k1[c] - 1, 0)
                    elif (sum1[c] > (2 ** (k1[c] + 5))):
                        k1[c] += 1

                #adjust sum0 and k0
                sum0[c] += (unsigned - (sum0[c] >> 4))
                if (sum0[c] < (2 ** (k0[c] + 4))):
                    k0[c] = max(k0[c] - 1, 0)
                elif (sum0[c] > (2 ** (k0[c] + 5))):
                    k0[c] += 1

                #apply sign bit
                if ((unsigned % 2) == 1):
                    #positive
                    ch_output.append((unsigned + 1) / 2)
                else:
                    #negative
                    ch_output.append(-(unsigned / 2))

        #check frame's trailing CRC32 now that reading is finished
        frame_reader.byte_align()
        frame_reader.pop_callback()
        frame_crc = frame_reader.read(32)
        if (int(crc) != frame_crc):
            raise ValueError("CRC32 mismatch in frame (0x%8.8X != 0x%8.8X)" %
                             (frame_crc, int(crc)))

        #run hybrid filter on each channel
        filtered = []
        for unfiltered_ch in unfiltered:
            filtered.append(
                tta_filter(self.bits_per_sample, unfiltered_ch))

        #run fixed order prediction on each channel
        predicted = []
        for filtered_ch in filtered:
            predicted.append(
                fixed_predictor(self.bits_per_sample, filtered_ch))

        if (self.channels == 1):
            #send channel as-is
            return from_list(predicted[0],
                             1,
                             self.bits_per_sample,
                             True)
        else:
            #decorrelate channels
            decorrelated = decorrelate(predicted)

            #return all channels as single FrameList
            return from_channels([from_list(decorrelated_ch,
                                            1,
                                            self.bits_per_sample,
                                            True)
                                  for decorrelated_ch in decorrelated])

    def close(self):
        #FIXME
        pass
Exemplo n.º 20
0
    def __parse_info__(self):
        """generates a cache of sample_rate, bits-per-sample, etc"""

        import re
        import os.path
        from audiotools.bitstream import BitstreamReader

        if (len(self.tracks) == 0):
            return

        # Why is this post-init processing necessary?
        # DVDATrack references DVDATitle
        # so a DVDATitle must exist when DVDATrack is initialized.
        # But because reading this info requires knowing the sector
        # of the first track, we wind up with a circular dependency.
        # Doing a "post-process" pass fixes that problem.

        # find the AOB file of the title's first track
        track_sector = self[0].first_sector
        titleset = re.compile("ATS_%2.2d_\\d\\.AOB" % (self.titleset))
        for aob_path in sorted([
                self.dvdaudio.files[key] for key in self.dvdaudio.files.keys()
                if (titleset.match(key))
        ]):
            aob_sectors = os.path.getsize(aob_path) // DVDAudio.SECTOR_SIZE
            if (track_sector > aob_sectors):
                track_sector -= aob_sectors
            else:
                break
        else:
            from audiotools.text import ERR_DVDA_NO_TRACK_SECTOR
            raise ValueError(ERR_DVDA_NO_TRACK_SECTOR)

        # open that AOB file and seek to that track's first sector
        aob_file = open(aob_path, 'rb')
        try:
            aob_file.seek(track_sector * DVDAudio.SECTOR_SIZE)
            aob_reader = BitstreamReader(aob_file, 0)

            # read and validate the pack header
            # (there's one pack header per sector, at the sector's start)
            (sync_bytes, marker1, current_pts_high, marker2, current_pts_mid,
             marker3, current_pts_low, marker4, scr_extension, marker5,
             bit_rate, marker6, stuffing_length) = aob_reader.parse(
                 "32u 2u 3u 1u 15u 1u 15u 1u 9u 1u 22u 2u 5p 3u")
            aob_reader.skip_bytes(stuffing_length)
            if (sync_bytes != 0x1BA):
                from audiotools.text import ERR_DVDA_INVALID_AOB_SYNC
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_SYNC)
            if (((marker1 != 1) or (marker2 != 1) or (marker3 != 1)
                 or (marker4 != 1) or (marker5 != 1) or (marker6 != 3))):
                from audiotools.text import ERR_DVDA_INVALID_AOB_MARKER
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_MARKER)
            packet_pts = ((current_pts_high << 30) | (current_pts_mid << 15)
                          | current_pts_low)

            # skip packets until one with a stream ID of 0xBD is found
            (start_code, stream_id,
             packet_length) = aob_reader.parse("24u 8u 16u")
            if (start_code != 1):
                from audiotools.text import ERR_DVDA_INVALID_AOB_START
                raise InvalidDVDA(ERR_DVDA_INVALID_AOB_START)
            while (stream_id != 0xBD):
                aob_reader.skip_bytes(packet_length)
                (start_code, stream_id,
                 packet_length) = aob_reader.parse("24u 8u 16u")
                if (start_code != 1):
                    from audiotools.text import ERR_DVDA_INVALID_AOB_START
                    raise InvalidDVDA(ERR_DVDA_INVALID_AOB_START)

            # parse the PCM/MLP header in the packet data
            (pad1_size, ) = aob_reader.parse("16p 8u")
            aob_reader.skip_bytes(pad1_size)
            (stream_id, crc) = aob_reader.parse("8u 8u 8p")
            if (stream_id == 0xA0):  # PCM
                # read a PCM reader
                (pad2_size, first_audio_frame, padding2, group1_bps,
                 group2_bps, group1_sample_rate, group2_sample_rate, padding3,
                 channel_assignment
                 ) = aob_reader.parse("8u 16u 8u 4u 4u 4u 4u 8u 8u")
            else:  # MLP
                aob_reader.skip_bytes(aob_reader.read(8))  # skip pad2
                # read a total frame size + MLP major sync header
                (total_frame_size, sync_words, stream_type, group1_bps,
                 group2_bps, group1_sample_rate, group2_sample_rate, unknown1,
                 channel_assignment, unknown2) = aob_reader.parse(
                     "4p 12u 16p 24u 8u 4u 4u 4u 4u 11u 5u 48u")

            # return the values indicated by the header
            self.sample_rate = DVDATrack.SAMPLE_RATE[group1_sample_rate]
            self.channels = DVDATrack.CHANNELS[channel_assignment]
            self.channel_mask = DVDATrack.CHANNEL_MASK[channel_assignment]
            self.bits_per_sample = DVDATrack.BITS_PER_SAMPLE[group1_bps]
            self.stream_id = stream_id

        finally:
            aob_file.close()
Exemplo n.º 21
0
class FlacDecoder(object):
    CHANNEL_COUNT = [
        1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2, None, None, None, None, None
    ]

    (SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED,
     SUBFRAME_LPC) = range(4)

    def __init__(self, filename, channel_mask):
        self.reader = BitstreamReader(open(filename, "rb"), False)

        if (self.reader.read_bytes(4) != b'fLaC'):
            raise ValueError("invalid FLAC file")

        self.current_md5sum = md5()

        # locate the STREAMINFO,
        # which is sometimes needed to handle non-subset streams
        for (block_id, block_size,
             block_reader) in self.metadata_blocks(self.reader):
            if (block_id == 0):
                # read STREAMINFO
                self.minimum_block_size = block_reader.read(16)
                self.maximum_block_size = block_reader.read(16)
                self.minimum_frame_size = block_reader.read(24)
                self.maximum_frame_size = block_reader.read(24)
                self.sample_rate = block_reader.read(20)
                self.channels = block_reader.read(3) + 1
                self.channel_mask = channel_mask
                self.bits_per_sample = block_reader.read(5) + 1
                self.total_frames = block_reader.read(36)
                self.md5sum = block_reader.read_bytes(16)

                # these are frame header lookup tables
                # which vary slightly depending on STREAMINFO's values
                self.BLOCK_SIZE = [
                    self.maximum_block_size, 192, 576, 1152, 2304, 4608, None,
                    None, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
                ]
                self.SAMPLE_RATE = [
                    self.sample_rate, 88200, 176400, 192000, 8000, 16000,
                    22050, 24000, 32000, 44100, 48000, 96000, None, None, None,
                    None
                ]
                self.BITS_PER_SAMPLE = [
                    self.bits_per_sample, 8, 12, None, 16, 20, 24, None
                ]

    def metadata_blocks(self, reader):
        """yields a (block_id, block_size, block_reader) tuple
        per metadata block where block_reader is a BitstreamReader substream"""

        (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u")
        while (last_block == 0):
            yield (block_id, block_size, self.reader.substream(block_size))
            (last_block, block_id, block_size) = self.reader.parse("1u 7u 24u")
        else:
            yield (block_id, block_size, self.reader.substream(block_size))

    def read(self, pcm_frames):
        # if the stream is exhausted,
        # verify its MD5 sum and return an empty pcm.FrameList object
        if (self.total_frames < 1):
            if (self.md5sum == self.current_md5sum.digest()):
                return empty_framelist(self.channels, self.bits_per_sample)
            else:
                raise ValueError("MD5 checksum mismatch")

        crc16 = CRC16()
        self.reader.add_callback(crc16.update)

        # fetch the decoding parameters from the frame header
        (block_size, channel_assignment,
         bits_per_sample) = self.read_frame_header()
        channel_count = self.CHANNEL_COUNT[channel_assignment]
        if (channel_count is None):
            raise ValueError("invalid channel assignment")

        # channel data will be a list of signed sample lists, one per channel
        # such as  [[1, 2, 3, ...], [4, 5, 6, ...]]  for a 2 channel stream
        channel_data = []

        for channel_number in range(channel_count):
            if ((channel_assignment == 0x8) and (channel_number == 1)):
                # for left-difference assignment
                # the difference channel has 1 additional bit
                channel_data.append(
                    self.read_subframe(block_size, bits_per_sample + 1))
            elif ((channel_assignment == 0x9) and (channel_number == 0)):
                # for difference-right assignment
                # the difference channel has 1 additional bit
                channel_data.append(
                    self.read_subframe(block_size, bits_per_sample + 1))
            elif ((channel_assignment == 0xA) and (channel_number == 1)):
                # for average-difference assignment
                # the difference channel has 1 additional bit
                channel_data.append(
                    self.read_subframe(block_size, bits_per_sample + 1))
            else:
                # otherwise, use the frame's bits-per-sample value
                channel_data.append(
                    self.read_subframe(block_size, bits_per_sample))

        # one all the subframes have been decoded,
        # reconstruct them depending on the channel assignment
        if (channel_assignment == 0x8):
            # left-difference
            samples = []
            for (left, difference) in zip(*channel_data):
                samples.append(left)
                samples.append(left - difference)
        elif (channel_assignment == 0x9):
            # difference-right
            samples = []
            for (difference, right) in zip(*channel_data):
                samples.append(difference + right)
                samples.append(right)
        elif (channel_assignment == 0xA):
            # mid-side
            samples = []
            for (mid, side) in zip(*channel_data):
                samples.append((((mid * 2) + (side % 2)) + side) // 2)
                samples.append((((mid * 2) + (side % 2)) - side) // 2)
        else:
            # independent
            samples = [0] * block_size * channel_count
            for (i, channel) in enumerate(channel_data):
                samples[i::channel_count] = channel

        self.reader.byte_align()

        # read and verify the frame's trailing CRC-16 footer
        self.reader.read(16)
        self.reader.pop_callback()
        if (int(crc16) != 0):
            raise ValueError("CRC16 mismatch in frame footer")

        # deduct the amount of PCM frames from the remaining amount
        self.total_frames -= block_size

        # build a pcm.FrameList object from the combined samples
        framelist = from_list(samples, channel_count, bits_per_sample, True)

        # update the running MD5 sum calculation with the frame's data
        self.current_md5sum.update(framelist.to_bytes(0, 1))

        # and finally return the frame data
        return framelist

    def read_frame_header(self):
        crc8 = CRC8()
        self.reader.add_callback(crc8.update)

        # read the 32-bit FLAC frame header
        sync_code = self.reader.read(14)
        if (sync_code != 0x3FFE):
            raise ValueError("invalid sync code")

        self.reader.skip(1)
        blocking_strategy = self.reader.read(1)
        block_size_bits = self.reader.read(4)
        sample_rate_bits = self.reader.read(4)
        channel_assignment = self.reader.read(4)
        bits_per_sample_bits = self.reader.read(3)
        self.reader.skip(1)

        # the frame number is a UTF-8 encoded value
        # which takes a variable number of whole bytes
        frame_number = self.read_utf8()

        # unpack the 4 bit block size field
        # which is the total PCM frames in the FLAC frame
        # and may require up to 16 more bits if the frame is usually-sized
        # (which typically happens at the end of the stream)
        if (block_size_bits == 0x6):
            block_size = self.reader.read(8) + 1
        elif (block_size_bits == 0x7):
            block_size = self.reader.read(16) + 1
        else:
            block_size = self.BLOCK_SIZE[block_size_bits]

        # unpack the 4 bit sample rate field
        # which is used for playback, but not needed for decoding
        # and may require up to 16 more bits
        # if the stream has a particularly unusual sample rate
        if (sample_rate_bits == 0xC):
            sample_rate = self.reader.read(8) * 1000
        elif (sample_rate_bits == 0xD):
            sample_rate = self.reader.read(16)
        elif (sample_rate_bits == 0xE):
            sample_rate = self.reader.read(16) * 10
        elif (sample_rate_bits == 0xF):
            raise ValueError("invalid sample rate")
        else:
            sample_rate = self.SAMPLE_RATE[sample_rate_bits]

        # unpack the 3 bit bits-per-sample field
        # this never requires additional bits
        if ((bits_per_sample_bits == 0x3) or (bits_per_sample_bits == 0x7)):
            raise ValueError("invalid bits per sample")
        else:
            bits_per_sample = self.BITS_PER_SAMPLE[bits_per_sample_bits]

        # read and verify frame's CRC-8 value
        self.reader.read(8)
        self.reader.pop_callback()
        if (int(crc8) != 0):
            raise ValueError("CRC8 mismatch in frame header")

        return (block_size, channel_assignment, bits_per_sample)

    def read_subframe_header(self):
        """returns a tuple of (subframe_type, subframe_order, wasted_bps)"""

        self.reader.skip(1)
        subframe_type = self.reader.read(6)
        if (self.reader.read(1) == 1):
            wasted_bps = self.reader.unary(1) + 1
        else:
            wasted_bps = 0

        # extract "order" value from 6 bit subframe type, if necessary
        if (subframe_type == 0):
            return (self.SUBFRAME_CONSTANT, None, wasted_bps)
        elif (subframe_type == 1):
            return (self.SUBFRAME_VERBATIM, None, wasted_bps)
        elif ((subframe_type & 0x38) == 0x08):
            return (self.SUBFRAME_FIXED, subframe_type & 0x07, wasted_bps)
        elif ((subframe_type & 0x20) == 0x20):
            return (self.SUBFRAME_LPC, (subframe_type & 0x1F) + 1, wasted_bps)
        else:
            raise ValueError("invalid subframe type")

    def read_subframe(self, block_size, bits_per_sample):
        (subframe_type, subframe_order,
         wasted_bps) = self.read_subframe_header()

        # read a list of signed sample values
        # depending on the subframe type, block size,
        # adjusted bits per sample and optional subframe order
        if (subframe_type == self.SUBFRAME_CONSTANT):
            subframe_samples = self.read_constant_subframe(
                block_size, bits_per_sample - wasted_bps)
        elif (subframe_type == self.SUBFRAME_VERBATIM):
            subframe_samples = self.read_verbatim_subframe(
                block_size, bits_per_sample - wasted_bps)
        elif (subframe_type == self.SUBFRAME_FIXED):
            subframe_samples = self.read_fixed_subframe(
                block_size, bits_per_sample - wasted_bps, subframe_order)
        else:
            subframe_samples = self.read_lpc_subframe(
                block_size, bits_per_sample - wasted_bps, subframe_order)

        # account for wasted bits-per-sample, if necessary
        if (wasted_bps):
            return [sample << wasted_bps for sample in subframe_samples]
        else:
            return subframe_samples

    def read_constant_subframe(self, block_size, bits_per_sample):
        sample = self.reader.read_signed(bits_per_sample)
        return [sample] * block_size

    def read_verbatim_subframe(self, block_size, bits_per_sample):
        return [
            self.reader.read_signed(bits_per_sample) for x in range(block_size)
        ]

    def read_fixed_subframe(self, block_size, bits_per_sample, order):
        # "order" number of warm-up samples
        samples = [
            self.reader.read_signed(bits_per_sample) for i in range(order)
        ]

        # "block_size" - "order" number of residual values
        residuals = self.read_residual(block_size, order)

        # which are applied to the warm-up samples
        # depending on the FIXED subframe order
        # and results in "block_size" number of total samples
        if (order == 0):
            return residuals
        elif (order == 1):
            for residual in residuals:
                samples.append(samples[-1] + residual)
            return samples
        elif (order == 2):
            for residual in residuals:
                samples.append((2 * samples[-1]) - samples[-2] + residual)
            return samples
        elif (order == 3):
            for residual in residuals:
                samples.append((3 * samples[-1]) - (3 * samples[-2]) +
                               samples[-3] + residual)
            return samples
        elif (order == 4):
            for residual in residuals:
                samples.append((4 * samples[-1]) - (6 * samples[-2]) +
                               (4 * samples[-3]) - samples[-4] + residual)
            return samples
        else:
            raise ValueError("unsupported FIXED subframe order")

    def read_lpc_subframe(self, block_size, bits_per_sample, order):
        # "order" number of warm-up samples
        samples = [
            self.reader.read_signed(bits_per_sample) for i in range(order)
        ]

        # the size of each QLP coefficient, in bits
        qlp_precision = self.reader.read(4)

        # the amount of right shift to apply
        # during LPC calculation
        # (though this is a signed value, negative shifts are noops
        # in the reference FLAC decoder)
        qlp_shift_needed = max(self.reader.read_signed(5), 0)

        # "order" number of signed QLP coefficients
        qlp_coeffs = [
            self.reader.read_signed(qlp_precision + 1) for i in range(order)
        ]
        # QLP coefficients are applied in reverse order
        qlp_coeffs.reverse()

        # "block_size" - "order" number of residual values
        residuals = self.read_residual(block_size, order)

        # which are applied to the running LPC calculation
        for residual in residuals:
            samples.append((sum([
                coeff * sample
                for (coeff, sample) in zip(qlp_coeffs, samples[-order:])
            ]) >> qlp_shift_needed) + residual)

        return samples

    def read_residual(self, block_size, order):
        residuals = []

        coding_method = self.reader.read(2)
        partition_order = self.reader.read(4)

        # each parititon contains  block_size / 2 ** partition_order
        # number of residuals
        for partition_number in range(2**partition_order):
            if (partition_number == 0):
                # except for the first partition
                # which contains "order" less than the rest
                residuals.extend(
                    self.read_residual_partition(
                        coding_method,
                        (block_size // 2**partition_order) - order))
            else:
                residuals.extend(
                    self.read_residual_partition(
                        coding_method, block_size // 2**partition_order))

        return residuals

    def read_residual_partition(self, coding_method, residual_count):
        if (coding_method == 0):
            # the Rice parameters determines the number of
            # least-significant bits to read for each residual
            rice_parameter = self.reader.read(4)
            if (rice_parameter == 0xF):
                escape_code = self.reader.read(5)
                return [
                    self.reader.read_signed(escape_code)
                    for i in range(residual_count)
                ]
        elif (coding_method == 1):
            # 24 bps streams may use a 5-bit Rice parameter
            # for better compression
            rice_parameter = self.reader.read(5)
            if (rice_parameter == 0x1F):
                escape_code = self.reader.read(5)
                return [
                    self.reader.read_signed(escape_code)
                    for i in range(residual_count)
                ]
        else:
            raise ValueError("invalid Rice coding parameter")

        # a list of signed residual values
        partition_residuals = []

        for i in range(residual_count):
            msb = self.reader.unary(1)  # most-significant bits
            lsb = self.reader.read(rice_parameter)  # least-significant bits
            value = (msb << rice_parameter) | lsb  # combined into a value
            if (value & 1):  # whose least-significant bit is the sign value
                partition_residuals.append(-(value >> 1) - 1)
            else:
                partition_residuals.append(value >> 1)

        return partition_residuals

    def read_utf8(self):
        total_bytes = self.reader.unary(0)
        value = self.reader.read(7 - total_bytes)
        while (total_bytes > 1):
            value = ((value << 6) | self.reader.parse("2p 6u")[0])
            total_bytes -= 1
        return value

    def close(self):
        self.reader.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()
Exemplo n.º 22
0
    def __init__(self, filename):
        """filename is a plain string"""

        AudioFile.__init__(self, filename)
        self.__channels__ = 0
        self.__channel_mask__ = 0

        #get channel count and channel mask from first packet
        from .bitstream import BitstreamReader
        try:
            f = open(filename, "rb")
            try:
                ogg_reader = BitstreamReader(f, 1)
                (magic_number,
                 version,
                 header_type,
                 granule_position,
                 self.__serial_number__,
                 page_sequence_number,
                 checksum,
                 segment_count) = ogg_reader.parse(
                     "4b 8u 8u 64S 32u 32u 32u 8u")

                if (magic_number != 'OggS'):
                    from .text import ERR_OGG_INVALID_MAGIC_NUMBER
                    raise InvalidFLAC(ERR_OGG_INVALID_MAGIC_NUMBER)
                if (version != 0):
                    from .text import ERR_OGG_INVALID_VERSION
                    raise InvalidFLAC(ERR_OGG_INVALID_VERSION)

                segment_length = ogg_reader.read(8)

                (opushead,
                 version,
                 self.__channels__,
                 pre_skip,
                 input_sample_rate,
                 output_gain,
                 mapping_family) = ogg_reader.parse(
                     "8b 8u 8u 16u 32u 16s 8u")

                if (opushead != "OpusHead"):
                    from .text import ERR_OPUS_INVALID_TYPE
                    raise InvalidOpus(ERR_OPUS_INVALID_TYPE)
                if (version != 1):
                    from .text import ERR_OPUS_INVALID_VERSION
                    raise InvalidOpus(ERR_OPUS_INVALID_VERSION)
                if (self.__channels__ == 0):
                    from .text import ERR_OPUS_INVALID_CHANNELS
                    raise InvalidOpus(ERR_OPUS_INVALID_CHANNELS)

                #FIXME - assign channel mask from mapping family
                if (mapping_family == 0):
                    if (self.__channels__ == 1):
                        self.__channel_mask__ = VorbisChannelMask(0x4)
                    elif (self.__channels__ == 2):
                        self.__channel_mask__ = VorbisChannelMask(0x3)
                    else:
                        self.__channel_mask__ = VorbisChannelMask(0)
                else:
                    (stream_count,
                     coupled_stream_count) = ogg_reader.parse("8u 8u")
                    if (self.__channels__ !=
                        ((coupled_stream_count * 2) +
                         (stream_count - coupled_stream_count))):
                        from .text import ERR_OPUS_INVALID_CHANNELS
                        raise InvalidOpus(ERR_OPUS_INVALID_CHANNELS)
                    channel_mapping = [ogg_reader.read(8)
                                       for i in xrange(self.__channels__)]
            finally:
                f.close()
        except IOError, msg:
            raise InvalidOpus(str(msg))
Exemplo n.º 23
0
class SHNDecoder(object):
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), False)

        (self.file_type,
         self.channels,
         self.block_length,
         self.max_LPC,
         self.number_of_means) = self.read_header()

        if (1 <= self.file_type) and (self.file_type <= 2):
            self.bits_per_sample = 8
            self.signed_samples = (self.file_type == 1)
        elif (3 <= self.file_type) and (self.file_type <= 6):
            self.bits_per_sample = 16
            self.signed_samples = (self.file_type in (3, 5))
        else:
            raise ValueError("unsupported Shorten file type")

        self.wrapped_samples = [[0] * 3 for c in range(self.channels)]
        self.means = [[0] * self.number_of_means
                      for c in range(self.channels)]
        self.left_shift = 0
        self.stream_finished = False

        # try to read the first command for a wave/aiff header
        data_start = self.reader.getpos()
        self.read_metadata()
        self.reader.setpos(data_start)

    def read_metadata(self):
        from io import BytesIO

        command = self.unsigned(2)
        if command == 9:
            # got verbatim, so read data
            verbatim_bytes = ints_to_bytes([self.unsigned(8) & 0xFF
                                            for i in range(self.unsigned(5))])

            try:
                wave = BitstreamReader(BytesIO(verbatim_bytes), True)
                header = wave.read_bytes(12)
                if header.startswith(b"RIFF") and header.endswith(b"WAVE"):
                    # got RIFF/WAVE header, so parse wave blocks as needed
                    total_size = len(verbatim_bytes) - 12
                    while total_size > 0:
                        (chunk_id, chunk_size) = wave.parse("4b 32u")
                        total_size -= 8
                        if chunk_id == b'fmt ':
                            (channels,
                             self.sample_rate,
                             bits_per_sample,
                             channel_mask) = parse_fmt(
                                wave.substream(chunk_size))
                            self.channel_mask = int(channel_mask)
                            return
                        else:
                            if chunk_size % 2:
                                wave.read_bytes(chunk_size + 1)
                                total_size -= (chunk_size + 1)
                            else:
                                wave.read_bytes(chunk_size)
                                total_size -= chunk_size
                    else:
                        # no fmt chunk, so use default metadata
                        pass
            except (IOError, ValueError):
                pass

            try:
                aiff = BitstreamReader(BytesIO(verbatim_bytes), False)
                header = aiff.read_bytes(12)
                if header.startswith(b"FORM") and header.endswith(b"AIFF"):
                    # got FORM/AIFF header, so parse aiff blocks as needed
                    total_size = len(verbatim_bytes) - 12
                    while total_size > 0:
                        (chunk_id, chunk_size) = aiff.parse("4b 32u")
                        total_size -= 8
                        if chunk_id == b'COMM':
                            (channels,
                             total_sample_frames,
                             bits_per_sample,
                             self.sample_rate,
                             channel_mask) = parse_comm(
                                aiff.substream(chunk_size))
                            self.channel_mask = int(channel_mask)
                            return
                        else:
                            if chunk_size % 2:
                                aiff.read_bytes(chunk_size + 1)
                                total_size -= (chunk_size + 1)
                            else:
                                aiff.read_bytes(chunk_size)
                                total_size -= chunk_size
                    else:
                        # no COMM chunk, so use default metadata
                        pass
            except IOError:
                pass

        # got something else, so invent some PCM parameters
        self.sample_rate = 44100
        if self.channels == 1:
            self.channel_mask = 0x4
        elif self.channels == 2:
            self.channel_mask = 0x3
        else:
            self.channel_mask = 0

    def unsigned(self, c):
        MSB = self.reader.unary(1)
        LSB = self.reader.read(c)
        return MSB * 2 ** c + LSB

    def signed(self, c):
        u = self.unsigned(c + 1)
        if (u % 2) == 0:
            return u // 2
        else:
            return -(u // 2) - 1

    def long(self):
        return self.unsigned(self.unsigned(2))

    def skip_unsigned(self, c):
        self.reader.skip_unary(1)
        self.reader.skip(c)

    def read_header(self):
        magic = self.reader.read_bytes(4)
        if magic != b"ajkg":
            raise ValueError("invalid magic number")
        version = self.reader.read(8)
        if version != 2:
            raise ValueError("unsupported version")

        file_type = self.long()
        channels = self.long()
        block_length = self.long()
        max_LPC = self.long()
        number_of_means = self.long()
        bytes_to_skip = self.long()
        self.reader.read_bytes(bytes_to_skip)

        return (file_type, channels, block_length, max_LPC, number_of_means)

    def read(self, pcm_frames):
        if self.stream_finished:
            return from_channels([empty_framelist(1, self.bits_per_sample)
                                  for channel in range(self.channels)])

        c = 0
        samples = []
        unshifted = []
        while True:
            command = self.unsigned(2)
            if (((0 <= command) and (command <= 3) or
                 (7 <= command) and (command <= 8))):
                # audio data commands
                if command == 0:    # DIFF0
                    samples.append(self.read_diff0(self.block_length,
                                                   self.means[c]))
                elif command == 1:  # DIFF1
                    samples.append(self.read_diff1(self.block_length,
                                                   self.wrapped_samples[c]))
                elif command == 2:  # DIFF2
                    samples.append(self.read_diff2(self.block_length,
                                                   self.wrapped_samples[c]))
                elif command == 3:  # DIFF3
                    samples.append(self.read_diff3(self.block_length,
                                                   self.wrapped_samples[c]))
                elif command == 7:  # QLPC
                    samples.append(self.read_qlpc(self.block_length,
                                                  self.means[c],
                                                  self.wrapped_samples[c]))
                elif command == 8:  # ZERO
                    samples.append([0] * self.block_length)

                # update means for channel
                self.means[c].append(shnmean(samples[c]))
                self.means[c] = self.means[c][1:]

                # wrap samples for next command in channel
                self.wrapped_samples[c] = samples[c][-(max(3, self.max_LPC)):]

                # apply left shift to samples
                if self.left_shift > 0:
                    unshifted.append([s << self.left_shift
                                      for s in samples[c]])
                else:
                    unshifted.append(samples[c])

                c += 1
                if c == self.channels:
                    # return a FrameList from shifted data
                    return from_channels([from_list(channel, 1,
                                                    self.bits_per_sample,
                                                    self.signed_samples)
                                          for channel in unshifted])
            else:
                # non audio commands
                if command == 4:  # QUIT
                    self.stream_finished = True
                    return from_channels(
                        [empty_framelist(1, self.bits_per_sample)
                         for channel in range(self.channels)])
                elif command == 5:  # BLOCKSIZE
                    self.block_length = self.long()
                elif command == 6:  # BITSHIFT
                    self.left_shift = self.unsigned(2)
                elif command == 9:  # VERBATIM
                    # skip this command during reading
                    size = self.unsigned(5)
                    for i in range(size):
                        self.skip_unsigned(8)
                else:
                    raise ValueError("unsupported Shorten command")

    def read_diff0(self, block_length, means):
        offset = shnmean(means)
        energy = self.unsigned(3)
        samples = []
        for i in range(block_length):
            residual = self.signed(energy)
            samples.append(residual + offset)
        return samples

    def read_diff1(self, block_length, previous_samples):
        samples = previous_samples[-1:]
        energy = self.unsigned(3)
        for i in range(1, block_length + 1):
            residual = self.signed(energy)
            samples.append(samples[i - 1] + residual)
        return samples[1:]

    def read_diff2(self, block_length, previous_samples):
        samples = previous_samples[-2:]
        energy = self.unsigned(3)
        for i in range(2, block_length + 2):
            residual = self.signed(energy)
            samples.append((2 * samples[i - 1]) - samples[i - 2] + residual)
        return samples[2:]

    def read_diff3(self, block_length, previous_samples):
        samples = previous_samples[-3:]
        energy = self.unsigned(3)
        for i in range(3, block_length + 3):
            residual = self.signed(energy)
            samples.append((3 * (samples[i - 1] - samples[i - 2])) +
                           samples[i - 3] + residual)
        return samples[3:]

    def read_qlpc(self, block_length, means, previous_samples):
        offset = shnmean(means)
        energy = self.unsigned(3)
        LPC_count = self.unsigned(2)
        LPC_coeff = [self.signed(5) for i in range(LPC_count)]
        unoffset = []
        samples = previous_samples[-LPC_count:]
        for i in range(block_length):
            residual = self.signed(energy)
            LPC_sum = 2 ** 5
            for j in range(LPC_count):
                if (i - j - 1) < 0:
                    # apply offset to warm-up samples
                    LPC_sum += (LPC_coeff[j] *
                                (samples[LPC_count + (i - j - 1)] - offset))
                else:
                    LPC_sum += LPC_coeff[j] * unoffset[i - j - 1]
            unoffset.append(LPC_sum // 2 ** 5 + residual)
        return [u + offset for u in unoffset]

    def close(self):
        self.reader.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()
Exemplo n.º 24
0
    def __init__(self, filename):
        """filename is a plain string"""

        AudioFile.__init__(self, filename)
        self.__channels__ = 0
        self.__channel_mask__ = 0

        # get channel count and channel mask from first packet
        from audiotools.bitstream import BitstreamReader
        try:
            f = open(filename, "rb")
            try:
                ogg_reader = BitstreamReader(f, 1)
                (magic_number, version, header_type, granule_position,
                 self.__serial_number__, page_sequence_number, checksum,
                 segment_count
                 ) = ogg_reader.parse("4b 8u 8u 64S 32u 32u 32u 8u")

                if (magic_number != 'OggS'):
                    from audiotools.text import ERR_OGG_INVALID_MAGIC_NUMBER
                    raise InvalidOpus(ERR_OGG_INVALID_MAGIC_NUMBER)
                if (version != 0):
                    from audiotools.text import ERR_OGG_INVALID_VERSION
                    raise InvalidOpus(ERR_OGG_INVALID_VERSION)

                segment_length = ogg_reader.read(8)

                (opushead, version, self.__channels__, pre_skip,
                 input_sample_rate, output_gain,
                 mapping_family) = ogg_reader.parse("8b 8u 8u 16u 32u 16s 8u")

                if (opushead != "OpusHead"):
                    from audiotools.text import ERR_OPUS_INVALID_TYPE
                    raise InvalidOpus(ERR_OPUS_INVALID_TYPE)
                if (version != 1):
                    from audiotools.text import ERR_OPUS_INVALID_VERSION
                    raise InvalidOpus(ERR_OPUS_INVALID_VERSION)
                if (self.__channels__ == 0):
                    from audiotools.text import ERR_OPUS_INVALID_CHANNELS
                    raise InvalidOpus(ERR_OPUS_INVALID_CHANNELS)

                # FIXME - assign channel mask from mapping family
                if (mapping_family == 0):
                    if (self.__channels__ == 1):
                        self.__channel_mask__ = VorbisChannelMask(0x4)
                    elif (self.__channels__ == 2):
                        self.__channel_mask__ = VorbisChannelMask(0x3)
                    else:
                        self.__channel_mask__ = VorbisChannelMask(0)
                else:
                    (stream_count,
                     coupled_stream_count) = ogg_reader.parse("8u 8u")
                    if (self.__channels__ !=
                        ((coupled_stream_count * 2) +
                         (stream_count - coupled_stream_count))):
                        from audiotools.text import ERR_OPUS_INVALID_CHANNELS
                        raise InvalidOpus(ERR_OPUS_INVALID_CHANNELS)
                    channel_mapping = [
                        ogg_reader.read(8) for i in range(self.__channels__)
                    ]
            finally:
                f.close()
        except IOError as msg:
            raise InvalidOpus(str(msg))
Exemplo n.º 25
0
class SHNDecoder(object):
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), False)

        (self.file_type, self.channels, self.block_length, self.max_LPC,
         self.number_of_means) = self.read_header()

        if ((1 <= self.file_type) and (self.file_type <= 2)):
            self.bits_per_sample = 8
            self.signed_samples = (self.file_type == 1)
        elif ((3 <= self.file_type) and (self.file_type <= 6)):
            self.bits_per_sample = 16
            self.signed_samples = (self.file_type in (3, 5))
        else:
            raise ValueError("unsupported Shorten file type")

        self.wrapped_samples = [[0] * 3 for c in range(self.channels)]
        self.means = [[0] * self.number_of_means for c in range(self.channels)]
        self.left_shift = 0
        self.stream_finished = False

        # try to read the first command for a wave/aiff header
        self.reader.mark()
        self.read_metadata()
        self.reader.rewind()
        self.reader.unmark()

    def read_metadata(self):
        from io import BytesIO

        command = self.unsigned(2)
        if (command == 9):
            # got verbatim, so read data
            verbatim_bytes = ints_to_bytes(
                [self.unsigned(8) & 0xFF for i in range(self.unsigned(5))])

            try:
                wave = BitstreamReader(BytesIO(verbatim_bytes), True)
                header = wave.read_bytes(12)
                if (header.startswith(b"RIFF") and header.endswith(b"WAVE")):
                    # got RIFF/WAVE header, so parse wave blocks as needed
                    total_size = len(verbatim_bytes) - 12
                    while (total_size > 0):
                        (chunk_id, chunk_size) = wave.parse("4b 32u")
                        total_size -= 8
                        if (chunk_id == b'fmt '):
                            (channels, self.sample_rate, bits_per_sample,
                             channel_mask) = parse_fmt(
                                 wave.substream(chunk_size))
                            self.channel_mask = int(channel_mask)
                            return
                        else:
                            if (chunk_size % 2):
                                wave.read_bytes(chunk_size + 1)
                                total_size -= (chunk_size + 1)
                            else:
                                wave.read_bytes(chunk_size)
                                total_size -= chunk_size
                    else:
                        # no fmt chunk, so use default metadata
                        pass
            except (IOError, ValueError):
                pass

            try:
                aiff = BitstreamReader(BytesIO(verbatim_bytes), False)
                header = aiff.read_bytes(12)
                if (header.startswith(b"FORM") and header.endswith(b"AIFF")):
                    # got FORM/AIFF header, so parse aiff blocks as needed
                    total_size = len(verbatim_bytes) - 12
                    while (total_size > 0):
                        (chunk_id, chunk_size) = aiff.parse("4b 32u")
                        total_size -= 8
                        if (chunk_id == b'COMM'):
                            (channels, total_sample_frames, bits_per_sample,
                             self.sample_rate, channel_mask) = parse_comm(
                                 aiff.substream(chunk_size))
                            self.channel_mask = int(channel_mask)
                            return
                        else:
                            if (chunk_size % 2):
                                aiff.read_bytes(chunk_size + 1)
                                total_size -= (chunk_size + 1)
                            else:
                                aiff.read_bytes(chunk_size)
                                total_size -= chunk_size
                    else:
                        # no COMM chunk, so use default metadata
                        pass
            except IOError:
                pass

        # got something else, so invent some PCM parameters
        self.sample_rate = 44100
        if (self.channels == 1):
            self.channel_mask = 0x4
        elif (self.channels == 2):
            self.channel_mask = 0x3
        else:
            self.channel_mask = 0

    def unsigned(self, c):
        MSB = self.reader.unary(1)
        LSB = self.reader.read(c)
        return MSB * 2**c + LSB

    def signed(self, c):
        u = self.unsigned(c + 1)
        if ((u % 2) == 0):
            return u // 2
        else:
            return -(u // 2) - 1

    def long(self):
        return self.unsigned(self.unsigned(2))

    def skip_unsigned(self, c):
        self.reader.skip_unary(1)
        self.reader.skip(c)

    def read_header(self):
        magic = self.reader.read_bytes(4)
        if (magic != b"ajkg"):
            raise ValueError("invalid magic number")
        version = self.reader.read(8)
        if (version != 2):
            raise ValueError("unsupported version")

        file_type = self.long()
        channels = self.long()
        block_length = self.long()
        max_LPC = self.long()
        number_of_means = self.long()
        bytes_to_skip = self.long()
        self.reader.read_bytes(bytes_to_skip)

        return (file_type, channels, block_length, max_LPC, number_of_means)

    def read(self, pcm_frames):
        if (self.stream_finished):
            return from_channels([
                empty_framelist(1, self.bits_per_sample)
                for channel in range(self.channels)
            ])

        c = 0
        samples = []
        unshifted = []
        while (True):
            command = self.unsigned(2)
            if (((0 <= command) and (command <= 3)
                 or (7 <= command) and (command <= 8))):
                # audio data commands
                if (command == 0):  # DIFF0
                    samples.append(
                        self.read_diff0(self.block_length, self.means[c]))
                elif (command == 1):  # DIFF1
                    samples.append(
                        self.read_diff1(self.block_length,
                                        self.wrapped_samples[c]))
                elif (command == 2):  # DIFF2
                    samples.append(
                        self.read_diff2(self.block_length,
                                        self.wrapped_samples[c]))
                elif (command == 3):  # DIFF3
                    samples.append(
                        self.read_diff3(self.block_length,
                                        self.wrapped_samples[c]))
                elif (command == 7):  # QLPC
                    samples.append(
                        self.read_qlpc(self.block_length, self.means[c],
                                       self.wrapped_samples[c]))
                elif (command == 8):  # ZERO
                    samples.append([0] * self.block_length)

                # update means for channel
                self.means[c].append(shnmean(samples[c]))
                self.means[c] = self.means[c][1:]

                # wrap samples for next command in channel
                self.wrapped_samples[c] = samples[c][-(max(3, self.max_LPC)):]

                # apply left shift to samples
                if (self.left_shift > 0):
                    unshifted.append(
                        [s << self.left_shift for s in samples[c]])
                else:
                    unshifted.append(samples[c])

                c += 1
                if (c == self.channels):
                    # return a FrameList from shifted data
                    return from_channels([
                        from_list(channel, 1, self.bits_per_sample,
                                  self.signed_samples) for channel in unshifted
                    ])
            else:
                # non audio commands
                if (command == 4):  # QUIT
                    self.stream_finished = True
                    return from_channels([
                        empty_framelist(1, self.bits_per_sample)
                        for channel in range(self.channels)
                    ])
                elif (command == 5):  # BLOCKSIZE
                    self.block_length = self.long()
                elif (command == 6):  # BITSHIFT
                    self.left_shift = self.unsigned(2)
                elif (command == 9):  # VERBATIM
                    # skip this command during reading
                    size = self.unsigned(5)
                    for i in range(size):
                        self.skip_unsigned(8)
                else:
                    raise ValueError("unsupported Shorten command")

    def read_diff0(self, block_length, means):
        offset = shnmean(means)
        energy = self.unsigned(3)
        samples = []
        for i in range(block_length):
            residual = self.signed(energy)
            samples.append(residual + offset)
        return samples

    def read_diff1(self, block_length, previous_samples):
        samples = previous_samples[-1:]
        energy = self.unsigned(3)
        for i in range(1, block_length + 1):
            residual = self.signed(energy)
            samples.append(samples[i - 1] + residual)
        return samples[1:]

    def read_diff2(self, block_length, previous_samples):
        samples = previous_samples[-2:]
        energy = self.unsigned(3)
        for i in range(2, block_length + 2):
            residual = self.signed(energy)
            samples.append((2 * samples[i - 1]) - samples[i - 2] + residual)
        return samples[2:]

    def read_diff3(self, block_length, previous_samples):
        samples = previous_samples[-3:]
        energy = self.unsigned(3)
        for i in range(3, block_length + 3):
            residual = self.signed(energy)
            samples.append((3 * (samples[i - 1] - samples[i - 2])) +
                           samples[i - 3] + residual)
        return samples[3:]

    def read_qlpc(self, block_length, means, previous_samples):
        offset = shnmean(means)
        energy = self.unsigned(3)
        LPC_count = self.unsigned(2)
        LPC_coeff = [self.signed(5) for i in range(LPC_count)]
        unoffset = []
        samples = previous_samples[-LPC_count:]
        for i in range(block_length):
            residual = self.signed(energy)
            LPC_sum = 2**5
            for j in range(LPC_count):
                if ((i - j - 1) < 0):
                    # apply offset to warm-up samples
                    LPC_sum += (LPC_coeff[j] * (samples[LPC_count +
                                                        (i - j - 1)] - offset))
                else:
                    LPC_sum += LPC_coeff[j] * unoffset[i - j - 1]
            unoffset.append(LPC_sum // 2**5 + residual)
        return [u + offset for u in unoffset]

    def close(self):
        self.reader.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()