Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "StdAfx.h" | |
| 2 | |
| 3 #include "mp3_utils.h" | |
| 4 #include "bitreader_helper.h" | |
| 5 | |
| 6 using namespace bitreader_helper; | |
| 7 | |
| 8 static unsigned extract_header_bits(const t_uint8 p_header[4],unsigned p_base,unsigned p_bits) | |
| 9 { | |
| 10 PFC_ASSERT(p_base+p_bits<=32); | |
| 11 return (unsigned) extract_bits(p_header,p_base,p_bits); | |
| 12 } | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 class header_parser | |
| 17 { | |
| 18 public: | |
| 19 header_parser(const t_uint8 p_header[4]) : m_bitptr(0) | |
| 20 { | |
| 21 memcpy(m_header,p_header,4); | |
| 22 } | |
| 23 unsigned read(unsigned p_bits) | |
| 24 { | |
| 25 unsigned ret = extract_header_bits(m_header,m_bitptr,p_bits); | |
| 26 m_bitptr += p_bits; | |
| 27 return ret; | |
| 28 } | |
| 29 private: | |
| 30 t_uint8 m_header[4]; | |
| 31 unsigned m_bitptr; | |
| 32 }; | |
| 33 } | |
| 34 | |
| 35 typedef t_uint16 uint16; | |
| 36 | |
| 37 static const uint16 bitrate_table_l1v1[16] = { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448, 0}; | |
| 38 static const uint16 bitrate_table_l2v1[16] = { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384, 0}; | |
| 39 static const uint16 bitrate_table_l3v1[16] = { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320, 0}; | |
| 40 static const uint16 bitrate_table_l1v2[16] = { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256, 0}; | |
| 41 static const uint16 bitrate_table_l23v2[16] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160, 0}; | |
| 42 static const uint16 sample_rate_table[] = {11025,12000,8000}; | |
| 43 | |
| 44 unsigned mp3_utils::QueryMPEGFrameSize(const t_uint8 p_header[4]) | |
| 45 { | |
| 46 TMPEGFrameInfo info; | |
| 47 if (!ParseMPEGFrameHeader(info,p_header)) return 0; | |
| 48 return info.m_bytes; | |
| 49 } | |
| 50 | |
| 51 bool mp3_utils::ParseMPEGFrameHeader(TMPEGFrameInfo & p_info,const t_uint8 p_header[4]) | |
| 52 { | |
| 53 enum {MPEG_LAYER_1 = 3, MPEG_LAYER_2 = 2, MPEG_LAYER_3 = 1}; | |
| 54 enum {_MPEG_1 = 3, _MPEG_2 = 2, _MPEG_25 = 0}; | |
| 55 | |
| 56 header_parser parser(p_header); | |
| 57 if (parser.read(11) != 0x7FF) return false; | |
| 58 unsigned mpeg_version = parser.read(2); | |
| 59 unsigned layer = parser.read(2); | |
| 60 unsigned protection = parser.read(1); | |
| 61 unsigned bitrate_index = parser.read(4); | |
| 62 unsigned sample_rate_index = parser.read(2); | |
| 63 if (sample_rate_index == 3) return false;//reserved | |
| 64 unsigned paddingbit = parser.read(1); | |
| 65 int paddingdelta = 0; | |
| 66 parser.read(1);//private | |
| 67 unsigned channel_mode = parser.read(2); | |
| 68 unsigned channel_mode_ext = parser.read(2);//channel_mode_extension | |
| 69 parser.read(1);//copyright | |
| 70 parser.read(1);//original | |
| 71 parser.read(2);//emphasis | |
| 72 | |
| 73 unsigned bitrate = 0,sample_rate = 0; | |
| 74 | |
| 75 switch(layer) | |
| 76 { | |
| 77 default: | |
| 78 return false; | |
| 79 case MPEG_LAYER_3: | |
| 80 paddingdelta = paddingbit ? 1 : 0; | |
| 81 | |
| 82 p_info.m_layer = 3; | |
| 83 switch(mpeg_version) | |
| 84 { | |
| 85 case _MPEG_1: | |
| 86 p_info.m_duration = 1152; | |
| 87 bitrate = bitrate_table_l3v1[bitrate_index]; | |
| 88 break; | |
| 89 case _MPEG_2: | |
| 90 case _MPEG_25: | |
| 91 p_info.m_duration = 576; | |
| 92 bitrate = bitrate_table_l23v2[bitrate_index]; | |
| 93 break; | |
| 94 default: | |
| 95 return false; | |
| 96 } | |
| 97 | |
| 98 break; | |
| 99 case MPEG_LAYER_2: | |
| 100 paddingdelta = paddingbit ? 1 : 0; | |
| 101 p_info.m_duration = 1152; | |
| 102 p_info.m_layer = 2; | |
| 103 switch(mpeg_version) | |
| 104 { | |
| 105 case _MPEG_1: | |
| 106 bitrate = bitrate_table_l2v1[bitrate_index]; | |
| 107 break; | |
| 108 case _MPEG_2: | |
| 109 case _MPEG_25: | |
| 110 bitrate = bitrate_table_l23v2[bitrate_index]; | |
| 111 break; | |
| 112 default: | |
| 113 return false; | |
| 114 } | |
| 115 break; | |
| 116 case MPEG_LAYER_1: | |
| 117 paddingdelta = paddingbit ? 4 : 0; | |
| 118 p_info.m_duration = 384; | |
| 119 p_info.m_layer = 1; | |
| 120 switch(mpeg_version) | |
| 121 { | |
| 122 case _MPEG_1: | |
| 123 bitrate = bitrate_table_l1v1[bitrate_index]; | |
| 124 break; | |
| 125 case _MPEG_2: | |
| 126 case _MPEG_25: | |
| 127 bitrate = bitrate_table_l1v2[bitrate_index]; | |
| 128 break; | |
| 129 default: | |
| 130 return false; | |
| 131 } | |
| 132 break; | |
| 133 } | |
| 134 if (bitrate == 0) return false; | |
| 135 | |
| 136 sample_rate = sample_rate_table[sample_rate_index]; | |
| 137 if (sample_rate == 0) return false; | |
| 138 switch(mpeg_version) | |
| 139 { | |
| 140 case _MPEG_1: | |
| 141 sample_rate *= 4; | |
| 142 p_info.m_mpegversion = MPEG_1; | |
| 143 break; | |
| 144 case _MPEG_2: | |
| 145 sample_rate *= 2; | |
| 146 p_info.m_mpegversion = MPEG_2; | |
| 147 break; | |
| 148 case _MPEG_25: | |
| 149 p_info.m_mpegversion = MPEG_25; | |
| 150 break; | |
| 151 } | |
| 152 | |
| 153 switch(channel_mode) | |
| 154 { | |
| 155 case 0: | |
| 156 case 1: | |
| 157 case 2: | |
| 158 p_info.m_channels = 2; | |
| 159 break; | |
| 160 case 3: | |
| 161 p_info.m_channels = 1; | |
| 162 break; | |
| 163 } | |
| 164 | |
| 165 | |
| 166 p_info.m_channel_mode = channel_mode; | |
| 167 p_info.m_channel_mode_ext = channel_mode_ext; | |
| 168 | |
| 169 p_info.m_sample_rate = sample_rate; | |
| 170 p_info.m_sample_rate_idx = sample_rate_index; | |
| 171 | |
| 172 p_info.m_bitrate = bitrate; | |
| 173 p_info.m_bitrate_idx = bitrate_index; | |
| 174 | |
| 175 p_info.m_bytes = ( bitrate /*kbps*/ * (1000/8) /* kbps-to-bytes*/ * p_info.m_duration /*samples-per-frame*/ ) / sample_rate + paddingdelta; | |
| 176 | |
| 177 if (p_info.m_layer == 1) p_info.m_bytes &= ~3; | |
| 178 | |
| 179 p_info.m_crc = protection == 0; | |
| 180 | |
| 181 return true; | |
| 182 } | |
| 183 | |
| 184 unsigned mp3header::get_samples_per_frame() | |
| 185 { | |
| 186 mp3_utils::TMPEGFrameInfo fr; | |
| 187 if (!decode(fr)) return 0; | |
| 188 return fr.m_duration; | |
| 189 } | |
| 190 | |
| 191 bool mp3_utils::IsSameStream(TMPEGFrameInfo const & p_frame1,TMPEGFrameInfo const & p_frame2) { | |
| 192 return | |
| 193 // FFmpeg writes VBR headers with null channel mode... | |
| 194 /* p_frame1.m_channel_mode == p_frame2.m_channel_mode && */ | |
| 195 p_frame1.m_sample_rate == p_frame2.m_sample_rate && | |
| 196 p_frame1.m_layer == p_frame2.m_layer && | |
| 197 p_frame1.m_mpegversion == p_frame2.m_mpegversion; | |
| 198 } | |
| 199 | |
| 200 | |
| 201 | |
| 202 bool mp3_utils::ValidateFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { | |
| 203 if (frameSize < info.m_bytes) return false; //FAIL, incomplete data | |
| 204 if (!info.m_crc) return true; //nothing to check, frame appears valid | |
| 205 return ExtractFrameCRC(frameData, frameSize, info) == CalculateFrameCRC(frameData, frameSize, info); | |
| 206 } | |
| 207 | |
| 208 static t_uint32 CRC_update(unsigned value, t_uint32 crc) | |
| 209 { | |
| 210 enum { CRC16_POLYNOMIAL = 0x8005 }; | |
| 211 unsigned i; | |
| 212 value <<= 8; | |
| 213 for (i = 0; i < 8; i++) { | |
| 214 value <<= 1; | |
| 215 crc <<= 1; | |
| 216 if (((crc ^ value) & 0x10000)) crc ^= CRC16_POLYNOMIAL; | |
| 217 } | |
| 218 return crc; | |
| 219 } | |
| 220 | |
| 221 | |
| 222 void mp3_utils::RecalculateFrameCRC(t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { | |
| 223 PFC_ASSERT( frameSize >= info.m_bytes && info.m_crc ); | |
| 224 | |
| 225 const t_uint16 crc = CalculateFrameCRC(frameData, frameSize, info); | |
| 226 frameData[4] = (t_uint8)(crc >> 8); | |
| 227 frameData[5] = (t_uint8)(crc & 0xFF); | |
| 228 } | |
| 229 | |
| 230 static t_uint16 grabFrameCRC(const t_uint8 * frameData, t_size sideInfoLen) { | |
| 231 t_uint32 crc = 0xffff; | |
| 232 crc = CRC_update(frameData[2], crc); | |
| 233 crc = CRC_update(frameData[3], crc); | |
| 234 for (t_size i = 6; i < sideInfoLen; i++) { | |
| 235 crc = CRC_update(frameData[i], crc); | |
| 236 } | |
| 237 | |
| 238 return (t_uint32) (crc & 0xFFFF); | |
| 239 } | |
| 240 | |
| 241 t_uint16 mp3_utils::ExtractFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { | |
| 242 PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)info; (void)frameSize; | |
| 243 | |
| 244 return ((t_uint16)frameData[4] << 8) | (t_uint16)frameData[5]; | |
| 245 | |
| 246 } | |
| 247 t_uint16 mp3_utils::CalculateFrameCRC(const t_uint8 * frameData, t_size frameSize, TMPEGFrameInfo const & info) { | |
| 248 PFC_ASSERT(frameSize >= info.m_bytes && info.m_crc); (void)frameSize; | |
| 249 | |
| 250 t_size sideInfoLen = 0; | |
| 251 if (info.m_mpegversion == MPEG_1) | |
| 252 sideInfoLen = (info.m_channels == 1) ? 4 + 17 : 4 + 32; | |
| 253 else | |
| 254 sideInfoLen = (info.m_channels == 1) ? 4 + 9 : 4 + 17; | |
| 255 | |
| 256 //CRC | |
| 257 sideInfoLen += 2; | |
| 258 | |
| 259 PFC_ASSERT( sideInfoLen <= frameSize ); | |
| 260 | |
| 261 return grabFrameCRC(frameData, sideInfoLen); | |
| 262 } | |
| 263 | |
| 264 | |
| 265 bool mp3_utils::ValidateFrameCRC(const t_uint8 * frameData, t_size frameSize) { | |
| 266 if (frameSize < 4) return false; //FAIL, not a valid frame | |
| 267 TMPEGFrameInfo info; | |
| 268 if (!ParseMPEGFrameHeader(info, frameData)) return false; //FAIL, not a valid frame | |
| 269 return ValidateFrameCRC(frameData, frameSize, info); | |
| 270 } | |
| 271 | |
| 272 | |
| 273 bool mp3_utils::ParseMPEGFrameHeader(TMPEGFrameInfo & p_info, const void * bytes, size_t bytesAvail) { | |
| 274 if (bytesAvail < 4) return false; //FAIL, not a valid frame | |
| 275 return ParseMPEGFrameHeader(p_info, reinterpret_cast<const t_uint8*>(bytes)); | |
| 276 } | |
| 277 | |
| 278 bool mp3_utils::IsValidMPEGFrameHeader(const void* fourbytes) { | |
| 279 TMPEGFrameInfo info = {}; | |
| 280 return ParseMPEGFrameHeader(info, fourbytes, 4); | |
| 281 } |
