def __init__(self, fileobj):
        """Parses the Xing header or raises XingHeaderError.

        The file position after this returns is undefined.
        """

        data = fileobj.read(8)
        if len(data) != 8 or data[:4] not in (b"Xing", b"Info"):
            raise XingHeaderError("Not a Xing header")

        self.is_info = (data[:4] == b"Info")

        flags = cdata.uint32_be_from(data, 4)[0]

        if flags & XingHeaderFlags.FRAMES:
            data = fileobj.read(4)
            if len(data) != 4:
                raise XingHeaderError("Xing header truncated")
            self.frames = cdata.uint32_be(data)

        if flags & XingHeaderFlags.BYTES:
            data = fileobj.read(4)
            if len(data) != 4:
                raise XingHeaderError("Xing header truncated")
            self.bytes = cdata.uint32_be(data)

        if flags & XingHeaderFlags.TOC:
            data = fileobj.read(100)
            if len(data) != 100:
                raise XingHeaderError("Xing header truncated")
            self.toc = list(bytearray(data))

        if flags & XingHeaderFlags.VBR_SCALE:
            data = fileobj.read(4)
            if len(data) != 4:
                raise XingHeaderError("Xing header truncated")
            self.vbr_scale = cdata.uint32_be(data)

        try:
            self.lame_version, self.lame_version_desc, has_header = \
                LAMEHeader.parse_version(fileobj)
            if has_header:
                self.lame_header = LAMEHeader(self, fileobj)
        except LAMEError:
            pass
    def __init__(self, xing, fileobj):
        """Raises LAMEError if parsing fails"""

        payload = fileobj.read(27)
        if len(payload) != 27:
            raise LAMEError("Not enough data")

        # extended lame header
        r = BitReader(cBytesIO(payload))
        revision = r.bits(4)
        if revision != 0:
            raise LAMEError("unsupported header revision %d" % revision)

        self.vbr_method = r.bits(4)
        self.lowpass_filter = r.bits(8) * 100

        # these have a different meaning for lame; expose them again here
        self.quality = (100 - xing.vbr_scale) % 10
        self.vbr_quality = (100 - xing.vbr_scale) // 10

        track_peak_data = r.bytes(4)
        if track_peak_data == b"\x00\x00\x00\x00":
            self.track_peak = None
        else:
            # see PutLameVBR() in LAME's VbrTag.c
            self.track_peak = (
                cdata.uint32_be(track_peak_data) - 0.5) / 2 ** 23
        track_gain_type = r.bits(3)
        self.track_gain_origin = r.bits(3)
        sign = r.bits(1)
        gain_adj = r.bits(9) / 10.0
        if sign:
            gain_adj *= -1
        if track_gain_type == 1:
            self.track_gain_adjustment = gain_adj
        else:
            self.track_gain_adjustment = None
        assert r.is_aligned()

        album_gain_type = r.bits(3)
        self.album_gain_origin = r.bits(3)
        sign = r.bits(1)
        album_gain_adj = r.bits(9) / 10.0
        if album_gain_type == 2:
            self.album_gain_adjustment = album_gain_adj
        else:
            self.album_gain_adjustment = None

        self.encoding_flags = r.bits(4)
        self.ath_type = r.bits(4)

        self.bitrate = r.bits(8)

        self.encoder_delay_start = r.bits(12)
        self.encoder_padding_end = r.bits(12)

        self.source_sample_frequency_enum = r.bits(2)
        self.unwise_setting_used = r.bits(1)
        self.stereo_mode = r.bits(3)
        self.noise_shaping = r.bits(2)

        sign = r.bits(1)
        mp3_gain = r.bits(7)
        if sign:
            mp3_gain *= -1
        self.mp3_gain = mp3_gain

        r.skip(2)
        self.surround_info = r.bits(3)
        self.preset_used = r.bits(11)
        self.music_length = r.bits(32)
        self.music_crc = r.bits(16)

        self.header_crc = r.bits(16)
        assert r.is_aligned()