ladybird/Userland/Libraries/LibAudio/MP3Types.h
Zaggy1024 e087d35cd8 LibAudio: Accurately skip MP3 frames using the actual header size
Prevously, the header size was used to calculate the `slot_count` field
of `MP3::Header`, but `build_seek_table()` just used the maximum size
of the header instead, causing it not to seek far enough, and in cases
where a possible sync code occurred two bytes before the next frame, it
would read that possible sync code as if it was a real frame. It would
then either reject it due to bad field values, or could possibly skip
over the next real frame due to a larger calculated frame size in the
bogus frame.

By fixing this issue, we now properly calculate the duration of MP3
files where these fake sync codes occur. In the case of the raw file
for this podcast:

https://changelog.com/podcast/554

the duration goes from 1:21:57 to 1:22:47, which is the real duration
according to the player user interface.
2023-08-26 18:43:23 -04:00

108 lines
2.4 KiB
C++

/*
* Copyright (c) 2021, Arne Elster <arne@elster.li>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/EnumBits.h>
#include <AK/FixedArray.h>
namespace Audio::MP3 {
constexpr size_t const frame_size = 1152;
// 576 samples.
constexpr size_t const granule_size = frame_size / 2;
enum class Mode {
Stereo = 0,
JointStereo = 1,
DualChannel = 2,
SingleChannel = 3,
};
enum class ModeExtension {
Stereo = 0,
IntensityStereo = 1,
MsStereo = 2,
};
AK_ENUM_BITWISE_OPERATORS(ModeExtension)
enum class Emphasis {
None = 0,
Microseconds_50_15 = 1,
Reserved = 2,
CCITT_J17 = 3,
};
enum class BlockType {
Normal = 0,
Start = 1,
Short = 2,
End = 3,
};
struct Header {
i32 id { 0 };
i32 layer { 0 };
bool protection_bit { false };
i32 bitrate { 0 };
i32 samplerate { 0 };
bool padding_bit { false };
bool private_bit { false };
Mode mode { Mode::Stereo };
ModeExtension mode_extension { ModeExtension::Stereo };
bool copyright_bit { false };
bool original_bit { false };
Emphasis emphasis { Emphasis::None };
u16 crc16 { 0 };
size_t header_size { 0 };
size_t frame_size { 0 };
size_t slot_count { 0 };
size_t channel_count() const { return mode == Mode::SingleChannel ? 1 : 2; }
};
struct Granule {
Array<float, MP3::granule_size> samples;
Array<Array<float, 18>, 32> filter_bank_input;
Array<Array<float, 32>, 18> pcm;
u32 part_2_3_length { 0 };
u32 big_values { 0 };
u32 global_gain { 0 };
u32 scalefac_compress { 0 };
bool window_switching_flag { false };
BlockType block_type { BlockType::Normal };
bool mixed_block_flag { false };
Array<int, 3> table_select;
Array<int, 3> sub_block_gain;
u32 region0_count { 0 };
u32 region1_count { 0 };
bool preflag { false };
bool scalefac_scale { false };
bool count1table_select { false };
};
struct Channel {
Array<Granule, 2> granules;
Array<int, 39> scale_factors;
Array<int, 4> scale_factor_selection_info;
};
struct MP3Frame {
Header header;
FixedArray<Channel> channels;
off_t main_data_begin { 0 };
u32 private_bits { 0 };
MP3Frame(Header header)
: header(header)
, channels(FixedArray<Channel>::must_create_but_fixme_should_propagate_errors(header.channel_count()))
{
}
};
}