Example #1
0
    def parse(cls, data):
        stream_info_block_data = bitstruct.unpack(
            'u16 u16 u24 u24 u20 u3 u5 u36 r128',
            data.read(34),
        )

        min_block_size = stream_info_block_data[0]
        max_block_size = stream_info_block_data[1]
        min_frame_size = stream_info_block_data[2]
        max_frame_size = stream_info_block_data[3]
        sample_rate = stream_info_block_data[4]
        channels = stream_info_block_data[5] + 1
        bit_depth = stream_info_block_data[6] + 1
        total_samples = stream_info_block_data[7]
        md5sum = binascii.hexlify(stream_info_block_data[8]).decode(
            'ascii', 'replace')
        duration = total_samples / sample_rate

        return cls(
            start=None,
            size=None,
            min_block_size=min_block_size,
            max_block_size=max_block_size,
            min_frame_size=min_frame_size,
            max_frame_size=max_frame_size,
            bit_depth=bit_depth,
            bitrate=None,
            channels=channels,
            duration=duration,
            md5=md5sum,
            sample_rate=sample_rate,
        )
Example #2
0
	def find_mpeg_frames(data):
		frames = []
		cached_frames = None
		buffer_size = 128
		buffer = data.peek(buffer_size)

		while len(buffer) >= buffer_size:
			sync_start = buffer.find(b'\xFF')

			if sync_start >= 0:
				data.seek(sync_start, os.SEEK_CUR)

				if bitstruct.unpack('u11', data.peek(2))[0] == 2047:
					for _ in range(4):
						try:
							frame = MPEGFrameHeader.parse(data)
							frames.append(frame)
							if frame._xing and frame._xing.num_frames:
								break
							data.seek(frame._start + frame._size, os.SEEK_SET)
						except (FormatError, *bitstruct.Error):
							data.seek(1, os.SEEK_CUR)
							break
				else:
					data.seek(sync_start + 1, os.SEEK_CUR)

				if frames and (len(frames) >= 4 or frames[0]._xing):
					break

				if len(frames) >= 2 and cached_frames is None:
					cached_frames = frames.copy()

				del frames[:]
			else:
				data.seek(buffer_size, os.SEEK_CUR)

			buffer = data.peek(buffer_size)

		# I actually found a PNG file that had multiple consecutive MPEG frames parsed.
		# The all_equal check combats this false positive by
		# making sure certain attributes don't change between frames.
		if not frames:
			if (
				cached_frames
				and more_itertools.all_equal(
					[
						frame.channel_mode,
						frame.channels,
						frame.layer,
						frame.sample_rate,
						frame.version,
					]
					for frame in cached_frames
				)
			):
				frames = cached_frames
			else:
				raise FormatError("No XING header and insufficient MPEG frames.")

		return frames
Example #3
0
    def parse(cls, data):
        catalog_number = data.read(128).rstrip(b'\0').decode(
            'ascii', 'replace')
        lead_in_samples = struct.unpack(
            '>Q',
            data.read(8),
        )[0]
        compact_disc = bitstruct.unpack(
            'b1',
            data.read(1),
        )[0]

        data.read(258)
        num_tracks = struct.unpack(
            'B',
            data.read(1),
        )[0]

        tracks = []
        for _ in range(num_tracks):
            tracks.append(FLACCueSheetTrack.parse(data))

        return cls(
            tracks,
            catalog_number=catalog_number,
            lead_in_samples=lead_in_samples,
            compact_disc=compact_disc,
        )
Example #4
0
    def parse(cls, data):
        offset = struct.unpack(
            '>Q',
            data.read(8),
        )[0]
        track_number = struct.unpack(
            '>B',
            data.read(1),
        )[0]
        isrc = data.read(12).rstrip(b'\x00').decode('ascii', 'replace')

        type_, pre_emphasis = bitstruct.unpack(
            'u1 b1',
            data.read(1),
        )

        data.read(13)
        num_indexes = struct.unpack(
            '>B',
            data.read(1),
        )[0]

        indexes = []
        for _ in range(num_indexes):
            indexes.append(FLACCueSheetIndex.parse(data))

        return cls(
            track_number=track_number,
            offset=offset,
            isrc=isrc,
            type=type_,
            pre_emphasis=pre_emphasis,
            indexes=indexes,
        )
Example #5
0
	def parse(cls, data):
		peak_data = struct.unpack('>I', data.read(4))[0]

		if peak_data == 0:
			gain_peak = None
		else:
			gain_peak = peak_data / 2 ** 23

		track_gain_type_, track_gain_origin_, track_gain_sign, track_gain_adjustment_ = bitstruct.unpack(
			'u3 u3 b1 u9',
			data.read(2),
		)

		track_gain_type = LAMEReplayGainType(track_gain_type_)
		track_gain_origin = LAMEReplayGainOrigin(track_gain_origin_)
		track_gain_adjustment = track_gain_adjustment_ / 10.0

		if track_gain_sign:
			track_gain_adjustment *= -1

		album_gain_type_, album_gain_origin_, album_gain_sign, album_gain_adjustment_ = bitstruct.unpack(
			'u3 u3 b1 u9',
			data.read(2),
		)

		album_gain_type = LAMEReplayGainType(album_gain_type_)
		album_gain_origin = LAMEReplayGainOrigin(album_gain_origin_)
		album_gain_adjustment = album_gain_adjustment_ / 10.0

		if album_gain_sign:
			album_gain_adjustment *= -1

		return cls(
			peak=gain_peak,
			track_type=track_gain_type,
			track_origin=track_gain_origin,
			track_adjustment=track_gain_adjustment,
			album_type=album_gain_type,
			album_origin=album_gain_origin,
			album_adjustment=album_gain_adjustment,
		)
Example #6
0
    def _parse_metadata_block(data):
        is_last_block, block_type, block_size = bitstruct.unpack(
            'b1 u7 u24',
            data.read(4),
        )

        if block_size == 0:
            raise FormatError(
                "FLAC metadata block size must be greater than 0.")

        # There are examples of tools writing incorrect block sizes.
        # The FLAC reference implementation unintentionally (I hope?) parses them.
        # I've chosen not to add special handling for these invalid files.
        # If needed, mutagen (https://github.com/quodlibet/mutagen) may support them.
        metadata_block_data = data.read(block_size)

        if block_type == FLACMetadataBlockType.STREAMINFO:
            metadata_block = FLACStreamInfo.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.PADDING:
            metadata_block = FLACPadding.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.APPLICATION:
            metadata_block = FLACApplication.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.SEEKTABLE:
            metadata_block = FLACSeekTable.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.VORBIS_COMMENT:
            metadata_block = FLACVorbisComments.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.CUESHEET:
            metadata_block = FLACCueSheet.parse(metadata_block_data)
        elif block_type == FLACMetadataBlockType.PICTURE:
            metadata_block = FLACPicture.parse(metadata_block_data)
        elif block_type >= 127:
            raise FormatError(
                f"{block_type} is not a valid FLAC metadata block type.")
        else:
            metadata_block = FLACMetadataBlock(
                type=block_type,
                data=metadata_block_data,
            )

        return metadata_block, is_last_block
Example #7
0
	def parse(cls, data):
		frame_start = data.tell()

		sync, version_id, layer_index, protection = bitstruct.unpack(
			'u11 u2 u2 b1',
			data.read(2),
		)

		if sync != 2047:
			raise FormatError("Invalid MPEG frame sync.")

		version = [2.5, None, 2, 1][version_id]

		layer = 4 - layer_index

		protected = not protection

		bitrate_index, sample_rate_index, padded = bitstruct.unpack(
			'u4 u2 b1',
			data.read(1),
		)

		if (
			version_id == 1
			or layer_index == 0
			or bitrate_index == 0
			or bitrate_index == 15
			or sample_rate_index == 3
		):
			raise FormatError("Invalid MPEG audio frame.")

		channel_mode = MP3ChannelMode(bitstruct.unpack('u2', data.read(1))[0])
		channels = 1 if channel_mode == 3 else 2

		bitrate = MP3Bitrates[(version, layer)][bitrate_index] * 1000
		sample_rate = MP3SampleRates[version][sample_rate_index]

		samples_per_frame, slot_size = MP3SamplesPerFrame[(version, layer)]

		frame_size = (((samples_per_frame // 8 * bitrate) // sample_rate) + padded) * slot_size

		vbri_header = None
		xing_header = None
		if layer == 3:  # pragma: nobranch
			if version == 1:
				if channel_mode != 3:
					xing_header_start = 36
				else:
					xing_header_start = 21
			elif channel_mode != 3:
				xing_header_start = 21
			else:
				xing_header_start = 13

			data.seek(frame_start + xing_header_start, os.SEEK_SET)

			if data.peek(4) in [b'Xing', b'Info']:
				xing_header = XingHeader.parse(data.read(frame_size))

			data.seek(frame_start + 36, os.SEEK_SET)

			if data.peek(4) == b'VBRI':
				vbri_header = VBRIHeader.parse(data)

		return cls(
			start=frame_start,
			size=frame_size,
			vbri=vbri_header,
			xing=xing_header,
			version=version,
			layer=layer,
			protected=protected,
			padded=padded,
			bitrate=bitrate,
			channel_mode=channel_mode,
			channels=channels,
			sample_rate=sample_rate,
		)
Example #8
0
	def parse(cls, data, xing_quality):
		encoder = data.read(9)
		if not encoder.startswith(b'LAME'):
			raise FormatError("Valid LAME header not found.")

		version = None
		version_match = re.search(rb'LAME(\d+)\.(\d+)', encoder)
		if version_match:  # pragma: nobranch
			version = tuple(int(part) for part in version_match.groups())

		revision, bitrate_mode_ = bitstruct.unpack(
			'u4 u4',
			data.read(1),
		)
		bitrate_mode = LAMEBitrateMode(bitrate_mode_)

		# TODO: Decide what, if anything, to do with the different meanings in LAME.
		# quality = (100 - xing_quality) % 10
		# vbr_quality = (100 - xing_quality) // 10

		lowpass_filter = struct.unpack(
			'B',
			data.read(1),
		)[0] * 100

		replay_gain = LAMEReplayGain.parse(data)

		flags_ath = bitstruct.unpack_dict(
			'b1 b1 b1 b1 u4',
			[
				'nogap_continuation',
				'nogap_continued',
				'nssafejoint',
				'nspsytune',
				'ath_type',
			],
			data.read(1),
		)

		ath_type = flags_ath.pop('ath_type')
		encoding_flags = LAMEEncodingFlags(**flags_ath)

		# TODO: Different representation for VBR minimum bitrate vs CBR/ABR specified bitrate?
		# Can only go up to 255.
		bitrate = struct.unpack(
			'B',
			data.read(1),
		)[0] * 1000

		delay, padding = bitstruct.unpack(
			'u12 u12',
			data.read(3),
		)

		source_sample_rate, unwise_settings_used, channel_mode_, noise_shaping = bitstruct.unpack(
			'u2 b1 u3 u2',
			data.read(1),
		)
		channel_mode = LAMEChannelMode(channel_mode_)

		mp3_gain = bitstruct.unpack(
			's8',
			data.read(1),
		)[0]

		surround_info_, preset_used_ = bitstruct.unpack(
			'p2 u3 u11',
			data.read(2),
		)
		surround_info = LAMESurroundInfo(surround_info_)

		preset = LAMEPreset(preset_used_)

		audio_size, audio_crc, lame_crc = struct.unpack(
			'>I2s2s',
			data.read(8),
		)

		return cls(
			crc=lame_crc,
			version=version,
			revision=revision,
			ath_type=ath_type,
			audio_crc=audio_crc,
			audio_size=audio_size,
			bitrate=bitrate,
			bitrate_mode=bitrate_mode,
			channel_mode=channel_mode,
			delay=delay,
			encoding_flags=encoding_flags,
			lowpass_filter=lowpass_filter,
			mp3_gain=mp3_gain,
			noise_shaping=noise_shaping,
			padding=padding,
			preset=preset,
			replay_gain=replay_gain,
			source_sample_rate=source_sample_rate,
			surround_info=surround_info,
			unwise_settings_used=unwise_settings_used,
		)