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.º 2
0
    def parse(cls, file_data):
        try:
            (magic_number,
             file_size,
             data_offset,
             header_size,
             width,
             height,
             color_planes,
             bits_per_pixel,
             compression_method,
             image_size,
             horizontal_resolution,
             vertical_resolution,
             colors_used,
             important_colors_used) = BitstreamReader(file_data, True).parse(
                "2b 32u 16p 16p 32u " +
                "32u 32u 32u 16u 16u 32u 32u 32u 32u 32u 32u")
        except IOError:
            from audiotools.text import ERR_IMAGE_IOERROR_BMP
            raise InvalidBMP(ERR_IMAGE_IOERROR_BMP)

        if (magic_number != b'BM'):
            from audiotools.text import ERR_IMAGE_INVALID_BMP
            raise InvalidBMP(ERR_IMAGE_INVALID_BMP)
        else:
            return cls(width=width,
                       height=height,
                       bits_per_pixel=bits_per_pixel,
                       color_count=colors_used)
Exemplo n.º 3
0
    def blocks(self, reader=None):
        """yields (length, reader) tuples of WavPack frames

        length is the total length of all the substreams
        reader is a BitstreamReader which can be parsed
        """

        def blocks_iter(reader):
            try:
                while (True):
                    (wvpk, block_size) = reader.parse("4b 32u 192p")
                    if (wvpk == b"wvpk"):
                        yield (block_size - 24,
                               reader.substream(block_size - 24))
                    else:
                        return
            except IOError:
                return

        if (reader is None):
            from audiotools.bitstream import BitstreamReader

            with BitstreamReader(open(self.filename, "rb"), True) as reader:
                for block in blocks_iter(reader):
                    yield block
        else:
            for block in blocks_iter(reader):
                yield block
Exemplo n.º 4
0
    def read(cls, apefile):
        """returns an ApeTag object from an APEv2 tagged file object

        may return None if the file object has no tag"""

        from audiotools.bitstream import BitstreamReader, parse

        apefile.seek(-32, 2)
        tag_footer = apefile.read(32)

        if len(tag_footer) < 32:
            # not enough bytes for an ApeV2 tag
            return None

        (preamble,
         version,
         tag_size,
         item_count,
         read_only,
         item_encoding,
         is_header,
         no_footer,
         has_header) = parse(cls.HEADER_FORMAT, True, tag_footer)

        if (preamble != b"APETAGEX") or (version != 2000):
            return None

        apefile.seek(-tag_size, 2)
        reader = BitstreamReader(apefile, True)

        return cls([ApeTagItem.parse(reader) for i in range(item_count)],
                   contains_header=has_header,
                   contains_footer=True)
    def __read_identification__(self):
        from audiotools.bitstream import BitstreamReader

        with BitstreamReader(open(self.filename, "rb"), True) as ogg_reader:
            (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 != b'OggS':
                from audiotools.text import ERR_OGG_INVALID_MAGIC_NUMBER
                raise InvalidVorbis(ERR_OGG_INVALID_MAGIC_NUMBER)
            if version != 0:
                from audiotools.text import ERR_OGG_INVALID_VERSION
                raise InvalidVorbis(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 audiotools.text import ERR_VORBIS_INVALID_TYPE
                raise InvalidVorbis(ERR_VORBIS_INVALID_TYPE)
            if header != b'vorbis':
                from audiotools.text import ERR_VORBIS_INVALID_HEADER
                raise InvalidVorbis(ERR_VORBIS_INVALID_HEADER)
            if version != 0:
                from audiotools.text import ERR_VORBIS_INVALID_VERSION
                raise InvalidVorbis(ERR_VORBIS_INVALID_VERSION)
            if framing != 1:
                from audiotools.text import ERR_VORBIS_INVALID_FRAMING_BIT
                raise InvalidVorbis(ERR_VORBIS_INVALID_FRAMING_BIT)
Exemplo n.º 6
0
    def __init__(self, filename):
        """filename is a plain string"""

        from audiotools import ChannelMask

        AiffContainer.__init__(self, filename)

        self.__channels__ = 0
        self.__bits_per_sample__ = 0
        self.__sample_rate__ = 0
        self.__channel_mask__ = ChannelMask(0)
        self.__total_sample_frames__ = 0

        from audiotools.bitstream import BitstreamReader

        try:
            for chunk in self.chunks():
                if chunk.id == b"COMM":
                    try:
                        (self.__channels__, self.__total_sample_frames__,
                         self.__bits_per_sample__, self.__sample_rate__,
                         self.__channel_mask__) = parse_comm(
                             BitstreamReader(chunk.data(), False))
                        break
                    except IOError:
                        continue
        except IOError:
            raise InvalidAIFF("I/O error reading wave")
Exemplo n.º 7
0
    def __init__(self, filename):
        self.reader = BitstreamReader(open(filename, "rb"), False)

        stream_start = self.reader.getpos()

        # 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.setpos(stream_start)
        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.setpos(stream_start)
        (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")
Exemplo n.º 8
0
    def __init__(self, filename):
        from audiotools.id3 import skip_id3v2_comment

        AudioFile.__init__(self, filename)

        try:
            with open(filename, "rb") as f:
                skip_id3v2_comment(f)

                from audiotools.bitstream import BitstreamReader
                from audiotools.text import (ERR_TTA_INVALID_SIGNATURE,
                                             ERR_TTA_INVALID_FORMAT)

                reader = BitstreamReader(f, True)

                (signature, format_, self.__channels__,
                 self.__bits_per_sample__, self.__sample_rate__,
                 self.__total_pcm_frames__
                 ) = reader.parse("4b 16u 16u 16u 32u 32u 32p")

                if signature != b"TTA1":
                    raise InvalidTTA(ERR_TTA_INVALID_SIGNATURE)
                elif format_ != 1:
                    raise InvalidTTA(ERR_TTA_INVALID_FORMAT)

                self.__total_tta_frames__ = div_ceil(
                    self.__total_pcm_frames__ * 245,
                    self.__sample_rate__ * 256)
                self.__frame_lengths__ = list(
                    reader.parse(
                        "{:d}* 32u".format(self.__total_tta_frames__) + "32p"))
        except IOError as msg:
            raise InvalidTTA(str(msg))
    def parse(cls, file_data):
        def segments(reader):
            if reader.read(8) != 0xFF:
                from audiotools.text import ERR_IMAGE_INVALID_JPEG_MARKER
                raise InvalidJPEG(ERR_IMAGE_INVALID_JPEG_MARKER)
            segment_type = reader.read(8)

            while segment_type != 0xDA:
                if segment_type not in {0xD8, 0xD9}:
                    yield (segment_type, reader.substream(reader.read(16) - 2))
                else:
                    yield (segment_type, None)

                if reader.read(8) != 0xFF:
                    from audiotools.text import ERR_IMAGE_INVALID_JPEG_MARKER
                    raise InvalidJPEG(ERR_IMAGE_INVALID_JPEG_MARKER)
                segment_type = reader.read(8)

        try:
            for (segment_type,
                 segment_data) in segments(BitstreamReader(file_data, False)):
                if (segment_type in {
                        0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0XC5, 0xC6, 0xC7, 0xC9,
                        0xCA, 0xCB, 0xCD, 0xCE, 0xCF
                }):  # start of frame
                    (data_precision, image_height, image_width,
                     components) = segment_data.parse("8u 16u 16u 8u")
                    return __JPEG__(width=image_width,
                                    height=image_height,
                                    bits_per_pixel=data_precision * components)
        except IOError:
            from audiotools.text import ERR_IMAGE_IOERROR_JPEG
            raise InvalidJPEG(ERR_IMAGE_IOERROR_JPEG)
Exemplo n.º 10
0
    def __init__(self, filename):
        from audiotools.bitstream import BitstreamReader

        AudioFile.__init__(self, filename)

        try:
            with BitstreamReader(open(self.filename, "rb"),
                                 True) as ogg_reader:
                (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 != b'OggS':
                    from audiotools.text import ERR_OGG_INVALID_MAGIC_NUMBER
                    raise InvalidVorbis(ERR_OGG_INVALID_MAGIC_NUMBER)
                if version != 0:
                    from audiotools.text import ERR_OGG_INVALID_VERSION
                    raise InvalidVorbis(ERR_OGG_INVALID_VERSION)

                segment_lengths = [
                    ogg_reader.read(8) for i in range(segment_count)
                ]

                (speex_string, speex_version, speex_version_id, header_size,
                 self.__sampling_rate__, mode, mode_bitstream_version,
                 self.__channels__, bitrate, frame_size, vbr, frame_per_packet,
                 extra_headers, reserved1,
                 reserved2) = ogg_reader.parse("8b 20b 13*32u")

                if speex_string != b"Speex   ":
                    raise InvalidSpeex(ERR_SPEEX_INVALID_VERSION)
        except IOError as err:
            raise InvalidSpeex(str(err))
Exemplo n.º 11
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.º 12
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.º 13
0
    def __titlesets__(self):
        """return valid audio titleset integers from AUDIO_TS.IFO"""

        from audiotools.bitstream import BitstreamReader

        try:
            f = open(self.files['AUDIO_TS.IFO'], 'rb')
        except (KeyError, IOError):
            from audiotools.text import ERR_DVDA_IOERROR_AUDIO_TS
            raise InvalidDVDA(ERR_DVDA_IOERROR_AUDIO_TS)
        try:
            (identifier, AMG_start_sector, AMGI_end_sector, DVD_version,
             volume_count, volume_number, disc_side, autoplay, ts_to_sv,
             video_titlesets, audio_titlesets,
             provider_information) = BitstreamReader(f, 0).parse(
                 "12b 32u 12P 32u 16u 4P 16u 16u 8u 4P 8u 32u 10P 8u 8u 40b")

            if (identifier != 'DVDAUDIO-AMG'):
                from audiotools.text import ERR_DVDA_INVALID_AUDIO_TS
                raise InvalidDVDA(ERR_DVDA_INVALID_AUDIO_TS)

            for titleset in range(1, audio_titlesets + 1):
                # ensure there are IFO files and AOBs
                # for each valid titleset
                if (("ATS_%2.2d_0.IFO" % (titleset) in self.files.keys()) and
                    ("ATS_%2.2d_1.AOB" % (titleset) in self.files.keys())):
                    yield titleset
        finally:
            f.close()
Exemplo n.º 14
0
    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()
Exemplo n.º 15
0
    def __find_next_mp3_frame__(cls, mp3file):
        from audiotools.id3 import skip_id3v2_comment

        # if we're starting at an ID3v2 header, skip it to save a bunch of time
        bytes_skipped = skip_id3v2_comment(mp3file)

        # then find the next mp3 frame
        from audiotools.bitstream import BitstreamReader

        reader = BitstreamReader(mp3file, False)
        pos = reader.getpos()
        try:
            (sync, mpeg_id, layer_description) = reader.parse("11u 2u 2u 1p")
        except IOError as err:
            raise err

        while (not ((sync == 0x7FF) and (mpeg_id in (0, 2, 3)) and
                    (layer_description in (1, 2, 3)))):
            reader.setpos(pos)
            reader.skip(8)
            bytes_skipped += 1
            pos = reader.getpos()
            try:
                (sync, mpeg_id,
                 layer_description) = reader.parse("11u 2u 2u 1p")
            except IOError as err:
                raise err
        else:
            reader.setpos(pos)
            return bytes_skipped
Exemplo n.º 16
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:
            with BitstreamReader(open(filename, "rb"), True) as ogg_reader:
                (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 != b'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 != b"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__)
                    ]
        except IOError as msg:
            raise InvalidOpus(str(msg))
Exemplo n.º 17
0
    def __init__(self, filename):
        """filename is a plain string"""

        from audiotools.bitstream import BitstreamReader

        AudioFile.__init__(self, filename)

        # first, fetch the mdia atom
        # which is the parent of both the mp4a and mdhd atoms
        try:
            with BitstreamReader(open(filename, "rb"), False) as reader:
                mdia = get_m4a_atom(reader, b"moov", b"trak", b"mdia")[1]
                mdia_start = mdia.getpos()
        except IOError:
            from audiotools.text import ERR_M4A_IOERROR
            raise InvalidM4A(ERR_M4A_IOERROR)
        except KeyError:
            from audiotools.text import ERR_M4A_MISSING_MDIA
            raise InvalidM4A(ERR_M4A_MISSING_MDIA)

        try:
            stsd = get_m4a_atom(mdia, b"minf", b"stbl", b"stsd")[1]
        except KeyError:
            from audiotools.text import ERR_M4A_MISSING_STSD
            raise InvalidM4A(ERR_M4A_MISSING_STSD)

        # then, fetch the mp4a atom for bps, channels and sample rate
        try:
            (stsd_version, descriptions) = stsd.parse("8u 24p 32u")
            (mp4a, self.__channels__, self.__bits_per_sample__
             ) = stsd.parse("32p 4b 48p 16p 16p 16p 4P 16u 16u 16p 16p 32p")
        except IOError:
            from audiotools.text import ERR_M4A_INVALID_MP4A
            raise InvalidM4A(ERR_M4A_INVALID_MP4A)

        # finally, fetch the mdhd atom for total track length
        mdia.setpos(mdia_start)
        try:
            mdhd = get_m4a_atom(mdia, b"mdhd")[1]
        except KeyError:
            from audiotools.text import ERR_M4A_MISSING_MDHD
            raise InvalidM4A(ERR_M4A_MISSING_MDHD)
        try:
            (version, ) = mdhd.parse("8u 24p")
            if version == 0:
                (
                    self.__sample_rate__,
                    self.__length__,
                ) = mdhd.parse("32p 32p 32u 32u 2P 16p")
            elif version == 1:
                (
                    self.__sample_rate__,
                    self.__length__,
                ) = mdhd.parse("64p 64p 32u 64U 2P 16p")
            else:
                from audiotools.text import ERR_M4A_UNSUPPORTED_MDHD
                raise InvalidM4A(ERR_M4A_UNSUPPORTED_MDHD)
        except IOError:
            from audiotools.text import ERR_M4A_INVALID_MDHD
            raise InvalidM4A(ERR_M4A_INVALID_MDHD)
Exemplo n.º 18
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.º 19
0
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        if (metadata is None):
            return
        elif (not isinstance(metadata, ApeTag)):
            from audiotools.text import ERR_FOREIGN_METADATA
            raise ValueError(ERR_FOREIGN_METADATA)

        from audiotools.bitstream import BitstreamReader, BitstreamWriter
        from audiotools import transfer_data

        f = open(self.filename, "r+b")
        f.seek(-32, 2)

        (preamble, version, tag_size, item_count, read_only, item_encoding,
         is_header, no_footer,
         has_header) = BitstreamReader(f, 1).parse(ApeTag.HEADER_FORMAT)

        if ((preamble == 'APETAGEX') and (version == 2000)):
            if (has_header):
                old_tag_size = 32 + tag_size
            else:
                old_tag_size = tag_size

            if (metadata.total_size() >= old_tag_size):
                # metadata has grown
                # so append it to existing file
                f.seek(-old_tag_size, 2)
                metadata.build(BitstreamWriter(f, 1))
            else:
                # metadata has shrunk
                # so rewrite file with smaller metadata
                from audiotools import TemporaryFile
                from os.path import getsize

                # copy everything but the last "old_tag_size" bytes
                # from existing file to rewritten file
                new_apev2 = TemporaryFile(self.filename)
                old_apev2 = open(self.filename, "rb")

                limited_transfer_data(old_apev2.read, new_apev2.write,
                                      getsize(self.filename) - old_tag_size)

                # append new tag to rewritten file
                metadata.build(BitstreamWriter(new_apev2, 1))

                old_apev2.close()
                new_apev2.close()
        else:
            # no existing metadata, so simply append a fresh tag
            f = open(self.filename, "ab")
            metadata.build(BitstreamWriter(f, 1))
            f.close()
Exemplo n.º 20
0
    def delete_metadata(self):
        """deletes the track's MetaData

        raises IOError if unable to write the file"""

        if ((self.get_replay_gain() is not None) or
            (self.get_cuesheet() is not None)):
            # non-textual metadata is present and needs preserving
            self.set_metadata(MetaData())
        else:
            # no non-textual metadata, so wipe out the entire block
            from os import access, R_OK, W_OK
            from audiotools.bitstream import BitstreamReader
            from audiotools import transfer_data

            if not access(self.filename, R_OK | W_OK):
                raise IOError(self.filename)

            with open(self.filename, "rb") as f:
                f.seek(-32, 2)

                (preamble,
                 version,
                 tag_size,
                 item_count,
                 read_only,
                 item_encoding,
                 is_header,
                 no_footer,
                 has_header) = BitstreamReader(f, True).parse(
                    ApeTag.HEADER_FORMAT)

            if (preamble == b'APETAGEX') and (version == 2000):
                from audiotools import TemporaryFile
                from os.path import getsize

                # there's existing metadata to delete
                # so rewrite file without trailing metadata tag
                if has_header:
                    old_tag_size = 32 + tag_size
                else:
                    old_tag_size = tag_size

                # copy everything but the last "old_tag_size" bytes
                # from existing file to rewritten file
                new_apev2 = TemporaryFile(self.filename)
                old_apev2 = open(self.filename, "rb")

                limited_transfer_data(
                    old_apev2.read,
                    new_apev2.write,
                    getsize(self.filename) - old_tag_size)

                old_apev2.close()
                new_apev2.close()
Exemplo n.º 21
0
    def get_metadata(self):
        """returns a MetaData object, or None

        raises IOError if unable to read the file"""

        from audiotools.bitstream import BitstreamReader
        from audiotools.id3 import ID3v22Comment

        for chunk in self.chunks():
            if (chunk.id == 'ID3 '):
                return ID3v22Comment.parse(BitstreamReader(chunk.data(), 0))
        else:
            return None
Exemplo n.º 22
0
    def blocks(self):
        with BitstreamReader(open(self.filename, "rb"), False) as r:
            if r.read_bytes(4) != b"MPCK":
                from audiotools.text import ERR_MPC_INVALID_ID
                raise InvalidMPC(ERR_MPC_INVALID_ID)

            key = r.read_bytes(2)
            size = MPC_Size.parse(r)
            while key != b"SE":
                yield key, size, r.read_bytes(int(size) - len(size) - 2)
                key = r.read_bytes(2)
                size = MPC_Size.parse(r)
            yield key, size, r.read_bytes(int(size) - len(size) - 2)
Exemplo n.º 23
0
def perform_lookup(disc_id,
                   accuraterip_server="www.accuraterip.com",
                   accuraterip_port=80):
    """performs web-based lookup using the given DiscID object
    and returns a dict of
    {track_number:[(confidence, crc, crc2), ...], ...}
    where track_number starts from 1

    may return a dict of empty lists if no AccurateRip entry is found

    may raise urllib2.HTTPError if an error occurs querying the server
    """

    from audiotools.bitstream import BitstreamReader
    try:
        from urllib.request import urlopen, URLError
    except ImportError:
        from urllib2 import urlopen, URLError

    matches = {n: [] for n in disc_id.track_numbers()}

    url = "http://%s:%s/accuraterip/%s/%s/%s/%s" % (accuraterip_server,
                                                    accuraterip_port,
                                                    str(disc_id)[16],
                                                    str(disc_id)[15],
                                                    str(disc_id)[14],
                                                    disc_id)

    try:
        response = BitstreamReader(urlopen(url), True)
    except URLError:
        # no CD found matching given parameters
        return matches

    try:
        while (True):
            (track_count,
             id1,
             id2,
             freedb_disc_id) = response.parse("8u 32u 32u 32u")
            if (((id1 == disc_id.id1()) and
                 (id2 == disc_id.id2()) and
                 (freedb_disc_id == disc_id.freedb_disc_id()))):
                for track_number in range(1, track_count + 1):
                    if (track_number in matches):
                        matches[track_number].append(
                            tuple(response.parse("8u 32u 32u")))
    except IOError:
        # keep trying to parse values until the data runs out
        response.close()
        return matches
Exemplo n.º 24
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.º 25
0
    def parse(cls, file_data):
        try:
            (gif, version, width, height, color_table_size) = BitstreamReader(
                file_data, True).parse("3b 3b 16u 16u 3u 5p")
        except IOError:
            from audiotools.text import ERR_IMAGE_IOERROR_GIF
            raise InvalidGIF(ERR_IMAGE_IOERROR_GIF)

        if gif != b'GIF':
            from audiotools.text import ERR_IMAGE_INVALID_GIF
            raise InvalidGIF(ERR_IMAGE_INVALID_GIF)
        else:
            return cls(width=width,
                       height=height,
                       color_count=2**(color_table_size + 1))
Exemplo n.º 26
0
    def seekable(self):
        """returns True if the file is seekable"""

        from audiotools.bitstream import BitstreamReader

        with BitstreamReader(open(self.filename, "rb"), False) as reader:
            stream_start = reader.getpos()
            has_stts = has_m4a_atom(reader, b"moov", b"trak", b"mdia", b"minf",
                                    b"stbl", b"stts")
            reader.setpos(stream_start)
            has_stsc = has_m4a_atom(reader, b"moov", b"trak", b"mdia", b"minf",
                                    b"stbl", b"stsc")
            reader.setpos(stream_start)
            has_stco = has_m4a_atom(reader, b"moov", b"trak", b"mdia", b"minf",
                                    b"stbl", b"stco")
            return has_stts and has_stsc and has_stco
Exemplo n.º 27
0
def validate_footer(footer, ssnd_bytes_written):
    """given a footer string as returned by aiff_header_footer()
    and PCM stream parameters, returns True if the footer is valid

    raises ValueError is the footer is invalid"""

    from io import BytesIO
    from audiotools.bitstream import BitstreamReader

    total_size = len(footer)
    aiff_file = BitstreamReader(BytesIO(footer), False)
    try:
        # ensure footer is padded properly if necessary
        # based on size of data bytes written
        if ssnd_bytes_written % 2:
            aiff_file.skip_bytes(1)
            total_size -= 1

        while total_size > 0:
            (chunk_id, chunk_size) = aiff_file.parse("4b 32u")
            if frozenset(chunk_id).issubset(AiffAudio.PRINTABLE_ASCII):
                total_size -= 8
            else:
                from audiotools.text import ERR_AIFF_INVALID_CHUNK
                raise ValueError(ERR_AIFF_INVALID_CHUNK)

            if chunk_id == b"COMM":
                # ensure no COMM chunks are found
                from audiotools.text import ERR_AIFF_MULTIPLE_COMM_CHUNKS
                raise ValueError(ERR_AIFF_MULTIPLE_COMM_CHUNKS)
            elif chunk_id == b"SSND":
                # ensure no SSND chunks are found
                from audiotools.text import ERR_AIFF_MULTIPLE_SSND_CHUNKS
                raise ValueError(ERR_AIFF_MULTIPLE_SSND_CHUNKS)
            else:
                # skip the full contents of non-audio chunks
                if chunk_size % 2:
                    aiff_file.skip_bytes(chunk_size + 1)
                    total_size -= (chunk_size + 1)
                else:
                    aiff_file.skip_bytes(chunk_size)
                    total_size -= chunk_size
        else:
            return True
    except IOError:
        from audiotools.text import ERR_AIFF_FOOTER_IOERROR
        raise ValueError(ERR_AIFF_FOOTER_IOERROR)
Exemplo n.º 28
0
def validate_footer(footer, data_bytes_written):
    """given a footer string as returned by wave_header_footer()
    and PCM stream parameters, returns True if the footer is valid

    raises ValueError if the footer is invalid"""

    from io import BytesIO
    from audiotools.bitstream import BitstreamReader

    total_size = len(footer)
    wave_file = BitstreamReader(BytesIO(footer), True)
    try:
        # ensure footer is padded properly if necessary
        # based on size of data bytes written
        if data_bytes_written % 2:
            wave_file.skip_bytes(1)
            total_size -= 1

        while total_size > 0:
            (chunk_id, chunk_size) = wave_file.parse("4b 32u")
            if not frozenset(chunk_id).issubset(WaveAudio.PRINTABLE_ASCII):
                from audiotools.text import ERR_WAV_INVALID_CHUNK
                raise ValueError(ERR_WAV_INVALID_CHUNK)
            else:
                total_size -= 8

            if chunk_id == b"fmt ":
                # ensure no fmt chunks are found
                from audiotools.text import ERR_WAV_MULTIPLE_FMT
                raise ValueError(ERR_WAV_MULTIPLE_FMT)
            elif chunk_id == b"data":
                # ensure no data chunks are found
                from audiotools.text import ERR_WAV_MULTIPLE_DATA
                raise ValueError(ERR_WAV_MULTIPLE_DATA)
            else:
                # skip the full contents of non-audio chunks
                if chunk_size % 2:
                    wave_file.skip_bytes(chunk_size + 1)
                    total_size -= (chunk_size + 1)
                else:
                    wave_file.skip_bytes(chunk_size)
                    total_size -= chunk_size
        else:
            return True
    except IOError:
        from audiotools.text import ERR_WAV_FOOTER_IOERROR
        raise ValueError(ERR_WAV_FOOTER_IOERROR)
Exemplo n.º 29
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.º 30
0
    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