Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/helpers/mp3_utils.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/foobar2000/helpers/mp3_utils.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,281 @@ +#include "StdAfx.h" + +#include "mp3_utils.h" +#include "bitreader_helper.h" + +using namespace bitreader_helper; + +static unsigned extract_header_bits(const t_uint8 p_header[4],unsigned p_base,unsigned p_bits) +{ + PFC_ASSERT(p_base+p_bits<=32); + return (unsigned) extract_bits(p_header,p_base,p_bits); +} + +namespace { + + class header_parser + { + public: + header_parser(const t_uint8 p_header[4]) : m_bitptr(0) + { + memcpy(m_header,p_header,4); + } + unsigned read(unsigned p_bits) + { + unsigned ret = extract_header_bits(m_header,m_bitptr,p_bits); + m_bitptr += p_bits; + return ret; + } + private: + t_uint8 m_header[4]; + unsigned m_bitptr; + }; +} + +typedef t_uint16 uint16; + +static const uint16 bitrate_table_l1v1[16] = { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448, 0}; +static const uint16 bitrate_table_l2v1[16] = { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384, 0}; +static const uint16 bitrate_table_l3v1[16] = { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320, 0}; +static const uint16 bitrate_table_l1v2[16] = { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256, 0}; +static const uint16 bitrate_table_l23v2[16] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160, 0}; +static const uint16 sample_rate_table[] = {11025,12000,8000}; + +unsigned mp3_utils::QueryMPEGFrameSize(const t_uint8 p_header[4]) +{ + TMPEGFrameInfo info; + if (!ParseMPEGFrameHeader(info,p_header)) return 0; + return info.m_bytes; +} + +bool mp3_utils::ParseMPEGFrameHeader(TMPEGFrameInfo & p_info,const t_uint8 p_header[4]) +{ + enum {MPEG_LAYER_1 = 3, MPEG_LAYER_2 = 2, MPEG_LAYER_3 = 1}; + enum {_MPEG_1 = 3, _MPEG_2 = 2, _MPEG_25 = 0}; + + header_parser parser(p_header); + if (parser.read(11) != 0x7FF) return false; + unsigned mpeg_version = parser.read(2); + unsigned layer = parser.read(2); + unsigned protection = parser.read(1); + unsigned bitrate_index = parser.read(4); + unsigned sample_rate_index = parser.read(2); + if (sample_rate_index == 3) return false;//reserved + unsigned paddingbit = parser.read(1); + int paddingdelta = 0; + parser.read(1);//private + unsigned channel_mode = parser.read(2); + unsigned channel_mode_ext = parser.read(2);//channel_mode_extension + parser.read(1);//copyright + parser.read(1);//original + parser.read(2);//emphasis + + unsigned bitrate = 0,sample_rate = 0; + + switch(layer) + { + default: + return false; + case MPEG_LAYER_3: + paddingdelta = paddingbit ? 1 : 0; + + p_info.m_layer = 3; + switch(mpeg_version) + { + case _MPEG_1: + p_info.m_duration = 1152; + bitrate = bitrate_table_l3v1[bitrate_index]; + break; + case _MPEG_2: + case _MPEG_25: + p_info.m_duration = 576; + bitrate = bitrate_table_l23v2[bitrate_index]; + break; + default: + return false; + } + + break; + case MPEG_LAYER_2: + paddingdelta = paddingbit ? 1 : 0; + p_info.m_duration = 1152; + p_info.m_layer = 2; + switch(mpeg_version) + { + case _MPEG_1: + bitrate = bitrate_table_l2v1[bitrate_index]; + break; + case _MPEG_2: + case _MPEG_25: + bitrate = bitrate_table_l23v2[bitrate_index]; + break; + default: + return false; + } + break; + case MPEG_LAYER_1: + paddingdelta = paddingbit ? 4 : 0; + p_info.m_duration = 384; + p_info.m_layer = 1; + switch(mpeg_version) + { + case _MPEG_1: + bitrate = bitrate_table_l1v1[bitrate_index]; + break; + case _MPEG_2: + case _MPEG_25: + bitrate = bitrate_table_l1v2[bitrate_index]; + break; + default: + return false; + } + break; + } + if (bitrate == 0) return false; + + sample_rate = sample_rate_table[sample_rate_index]; + if (sample_rate == 0) return false; + switch(mpeg_version) + { + case _MPEG_1: + sample_rate *= 4; + p_info.m_mpegversion = MPEG_1; + break; + case _MPEG_2: + sample_rate *= 2; + p_info.m_mpegversion = MPEG_2; + break; + case _MPEG_25: + p_info.m_mpegversion = MPEG_25; + break; + } + + switch(channel_mode) + { + case 0: + case 1: + case 2: + p_info.m_channels = 2; + break; + case 3: + p_info.m_channels = 1; + break; + } + + + p_info.m_channel_mode = channel_mode; + p_info.m_channel_mode_ext = channel_mode_ext; + + p_info.m_sample_rate = sample_rate; + p_info.m_sample_rate_idx = sample_rate_index; + + p_info.m_bitrate = bitrate; + p_info.m_bitrate_idx = bitrate_index; + + p_info.m_bytes = ( bitrate /*kbps*/ * (1000/8) /* kbps-to-bytes*/ * p_info.m_duration /*samples-per-frame*/ ) / sample_rate + paddingdelta; + + if (p_info.m_layer == 1) p_info.m_bytes &= ~3; + + p_info.m_crc = protection == 0; + + return true; +} + +unsigned mp3header::get_samples_per_frame() +{ + mp3_utils::TMPEGFrameInfo fr; + if (!decode(fr)) return 0; + return fr.m_duration; +} + +bool mp3_utils::IsSameStream(TMPEGFrameInfo const & p_frame1,TMPEGFrameInfo const & p_frame2) { + return + // FFmpeg writes VBR headers with null channel mode... + /* p_frame1.m_channel_mode == p_frame2.m_channel_mode && */ + p_frame1.m_sample_rate == p_frame2.m_sample_rate && + p_frame1.m_layer == p_frame2.m_layer && + p_frame1.m_mpegversion == p_frame2.m_mpegversion; +} + + + +bool mp3_utils::ValidateFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { + if (frameSize < info.m_bytes) return false; //FAIL, incomplete data + if (!info.m_crc) return true; //nothing to check, frame appears valid + return ExtractFrameCRC(frameData, frameSize, info) == CalculateFrameCRC(frameData, frameSize, info); +} + +static t_uint32 CRC_update(unsigned value, t_uint32 crc) +{ + enum { CRC16_POLYNOMIAL = 0x8005 }; + unsigned i; + value <<= 8; + for (i = 0; i < 8; i++) { + value <<= 1; + crc <<= 1; + if (((crc ^ value) & 0x10000)) crc ^= CRC16_POLYNOMIAL; + } + return crc; +} + + +void mp3_utils::RecalculateFrameCRC(t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { + PFC_ASSERT( frameSize >= info.m_bytes && info.m_crc ); + + const t_uint16 crc = CalculateFrameCRC(frameData, frameSize, info); + frameData[4] = (t_uint8)(crc >> 8); + frameData[5] = (t_uint8)(crc & 0xFF); +} + +static t_uint16 grabFrameCRC(const t_uint8 * frameData, t_size sideInfoLen) { + t_uint32 crc = 0xffff; + crc = CRC_update(frameData[2], crc); + crc = CRC_update(frameData[3], crc); + for (t_size i = 6; i < sideInfoLen; i++) { + crc = CRC_update(frameData[i], crc); + } + + return (t_uint32) (crc & 0xFFFF); +} + +t_uint16 mp3_utils::ExtractFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { + PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)info; (void)frameSize; + + return ((t_uint16)frameData[4] << 8) | (t_uint16)frameData[5]; + +} +t_uint16 mp3_utils::CalculateFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { + PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)frameSize; + + t_size sideInfoLen = 0; + if (info.m_mpegversion == MPEG_1) + sideInfoLen = (info.m_channels == 1) ? 4 + 17 : 4 + 32; + else + sideInfoLen = (info.m_channels == 1) ? 4 + 9 : 4 + 17; + + //CRC + sideInfoLen += 2; + + PFC_ASSERT( sideInfoLen <= frameSize ); + + return grabFrameCRC(frameData, sideInfoLen); +} + + +bool mp3_utils::ValidateFrameCRC(const t_uint8 * frameData, t_size frameSize) { + if (frameSize < 4) return false; //FAIL, not a valid frame + TMPEGFrameInfo info; + if (!ParseMPEGFrameHeader(info, frameData)) return false; //FAIL, not a valid frame + return ValidateFrameCRC(frameData, frameSize, info); +} + + +bool mp3_utils::ParseMPEGFrameHeader(TMPEGFrameInfo & p_info, const void * bytes, size_t bytesAvail) { + if (bytesAvail < 4) return false; //FAIL, not a valid frame + return ParseMPEGFrameHeader(p_info, reinterpret_cast<const t_uint8*>(bytes)); +} + +bool mp3_utils::IsValidMPEGFrameHeader(const void* fourbytes) { + TMPEGFrameInfo info = {}; + return ParseMPEGFrameHeader(info, fourbytes, 4); +}
