Skip to content

Commit aa2badc

Browse files
committed
add support for MPEG1 Layer I in MP3FindSyncWord()
use this in mp3_correctResumeFilePo
1 parent d57906f commit aa2badc

File tree

3 files changed

+128
-95
lines changed

3 files changed

+128
-95
lines changed

src/Audio.cpp

Lines changed: 10 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
audio.cpp
44
55
Created on: Oct 28.2018 */char audioI2SVers[] ="\
6-
Version 3.3.2k ";
7-
/* Updated on: Jul 08.2025
6+
Version 3.3.2l ";
7+
/* Updated on: Jul 09.2025
88
99
Author: Wolle (schreibfaul1)
1010
Audio library for ESP32, ESP32-S3 or ESP32-P4
@@ -4655,7 +4655,7 @@ int Audio::sendBytes(uint8_t* data, size_t len) {
46554655

46564656
switch(m_codec) {
46574657
case CODEC_WAV: m_decodeError = 0; m_sbyt.bytesLeft = 0; break;
4658-
case CODEC_MP3: m_decodeError = MP3Decode( data, &m_sbyt.bytesLeft, m_outBuff.get(), 0); break;
4658+
case CODEC_MP3: m_decodeError = MP3Decode( data, &m_sbyt.bytesLeft, m_outBuff.get()); break;
46594659
case CODEC_AAC: m_decodeError = AACDecode( data, &m_sbyt.bytesLeft, m_outBuff.get()); break;
46604660
case CODEC_M4A: m_decodeError = AACDecode( data, &m_sbyt.bytesLeft, m_outBuff.get()); break;
46614661
case CODEC_FLAC: m_decodeError = FLACDecode( data, &m_sbyt.bytesLeft, m_outBuff.get()); break;
@@ -6222,77 +6222,22 @@ int32_t Audio::flac_correctResumeFilePos() {
62226222
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
62236223
int32_t Audio::mp3_correctResumeFilePos() {
62246224

6225-
// The SncronWord sequence 0xFF 0xF? can be part of valid audio data. Therefore, it cannot be ensured that the next 0xFFF is really the beginning
6226-
// of a new MP3 frame. Therefore, the following byte is parsed. If the bitrate and sample rate match the one currently being played,
6227-
// the beginning of a new MP3 frame is likely.
6228-
6229-
const int16_t bitrateTab[3][3][15] PROGMEM = { {
6230-
/* MPEG-1 */
6231-
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, /* Layer 1 */
6232-
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, /* Layer 2 */
6233-
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }, /* Layer 3 */
6234-
}, {
6235-
/* MPEG-2 */
6236-
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, /* Layer 1 */
6237-
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 2 */
6238-
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 3 */
6239-
}, {
6240-
/* MPEG-2.5 */
6241-
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, /* Layer 1 */
6242-
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 2 */
6243-
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, /* Layer 3 */
6244-
}, };
6245-
6246-
auto find_sync_word = [&](uint8_t* pos, uint32_t av) -> int {
6247-
int steps = 0;
6248-
while(av--) {
6249-
char c = pos[steps];
6250-
steps++;
6251-
if(c == 0xFF) { // First byte of sync word found
6252-
char nextByte = pos[steps];
6253-
steps++;
6254-
if((nextByte & 0xF0) == 0xF0) { // Check for the second part of sync word
6255-
return steps - 2; // Sync word found, return the number of steps
6256-
}
6257-
}
6258-
}
6259-
return -1; // Return -1 if sync word is not found
6260-
};
6261-
6262-
uint8_t syncH, syncL, frame0;
62636225
int32_t steps = 0;
6264-
const uint8_t* pos = InBuff.getReadPtr();
6265-
uint8_t* readPtr = InBuff.getReadPtr();
6226+
int32_t sumSteps = 0;
6227+
uint8_t* pos = InBuff.getReadPtr();
62666228
size_t av = InBuff.getMaxAvailableBytes();
62676229

6268-
62696230
if(av < InBuff.getMaxBlockSize()) return -1; // guard
62706231

62716232
while(true) {
6272-
steps = find_sync_word(readPtr, av);
6233+
steps = MP3FindSyncWord(pos, av);
6234+
if(steps == 0)break;
62736235
if(steps == -1) return -1;
6274-
readPtr += steps;
6275-
syncH = *(readPtr ); (void)syncH; // readPtr[0];
6276-
syncL = *(readPtr + 1);
6277-
frame0 = *(readPtr + 2);
6278-
if((frame0 & 0b11110000) == 0b11110000){readPtr++; /* log_w("wrong bitrate index"); */ continue;}
6279-
if((frame0 & 0b00001100) == 0b00001100){readPtr++; /* log_w("wrong sampling rate frequency index"); */ continue;}
6280-
int32_t verIdx = (syncL >> 3) & 0x03;
6281-
uint8_t mpegVers = (verIdx == 0 ? MPEG25 : ((verIdx & 0x01) ? MPEG1 : MPEG2));
6282-
uint8_t brIdx = (frame0 >> 4) & 0x0f;
6283-
uint8_t srIdx = (frame0 >> 2) & 0x03;
6284-
uint8_t layer = 4 - ((syncL >> 1) & 0x03);
6285-
if(srIdx == 3 || layer == 4 || brIdx == 15) {readPtr++; continue;}
6286-
if(brIdx){
6287-
uint32_t bitrate = ((int32_t) bitrateTab[mpegVers][layer - 1][brIdx]) * 1000;
6288-
uint32_t samplerate = samplerateTab[mpegVers][srIdx];
6289-
// log_w("syncH 0x%02X, syncL 0x%02X bitrate %i, samplerate %i", syncH, syncL, bitrate, samplerate);
6290-
if(MP3GetBitrate() == bitrate && getSampleRate() == samplerate) break;
6291-
}
6292-
readPtr++;
6236+
pos += steps;
6237+
sumSteps += steps;
62936238
}
62946239
// log_w("found sync word at %i sync1 = 0x%02X, sync2 = 0x%02X", readPtr - pos, *readPtr, *(readPtr + 1));
6295-
return readPtr - pos; // return the position of the first byte of the frame
6240+
return sumSteps; // return the position of the first byte of the frame
62966241
}
62976242
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
62986243
uint8_t Audio::determineOggCodec(uint8_t* data, uint16_t len) {

src/mp3_decoder/mp3_decoder.cpp

Lines changed: 117 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* libhelix_HMP3DECODER
44
*
55
* Created on: 26.10.2018
6-
* Updated on: 07.07.2025
6+
* Updated on: 09.07.2025
77
*/
88
#include "mp3_decoder.h"
99
/* clip to range [-2^n, 2^n - 1] */
@@ -1185,7 +1185,43 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
11851185
// auto extract_bits = [&](uint8_t byte, uint8_t start_bit, uint8_t num_bits) {
11861186
// return (byte >> (8 - start_bit - num_bits)) & ((1 << num_bits) - 1);
11871187
// };
1188+
const uint8_t SYNCWORDH = 0xff;
1189+
const uint8_t SYNCWORDL = 0xe0;
1190+
1191+
typedef struct {
1192+
uint8_t mpeg_version; // 0=MPEG2.5, 1=reserved, 2=MPEG2, 3=MPEG1
1193+
uint8_t layer; // 0=reserved, 1=Layer III, 2=Layer II, 3=Layer I
1194+
bool crc_protected;
1195+
uint8_t bitrate_idx;
1196+
uint8_t sample_rate_idx;
1197+
bool padding;
1198+
uint8_t channel_mode;
1199+
uint32_t frame_length; // In Bytes
1200+
uint16_t sample_rate_hz; // Die tatsächliche Abtastrate in Hz
1201+
uint16_t bitrate_kbps; // Die tatsächliche Bitrate in kbps
1202+
uint16_t samples_per_frame;
1203+
} Mp3FrameHeader;
1204+
1205+
// SamplingFrequenz-Lookup tables(Beispiel für MPEG1, MPEG2, MPEG2.5)
1206+
const uint16_t sampling_rates[3][4] = {
1207+
{44100, 48000, 32000, 0}, // MPEG1
1208+
{22050, 24000, 16000, 0}, // MPEG2
1209+
{11025, 12000, 8000, 0} // MPEG2.5
1210+
};
1211+
1212+
typedef enum { /* map to 0,1,2 to make table indexing easier */
1213+
MPEG1 = 0,
1214+
MPEG2 = 1,
1215+
MPEG25 = 2
1216+
} MPEGVersion_t;
1217+
1218+
const uint16_t mpeg1_layer1_bitrates[16] = {
1219+
0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0
1220+
};
11881221

1222+
const uint16_t mpeg1_layer3_bitrates[16] = { // Bitraten-Lookup tables (example for MPEG1 Layer III)
1223+
0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 // Attention: These tables must be complete and correct!
1224+
};
11891225
// Define bitrate tables for MPEG1 Layer II and MPEG2/2.5 Layer II
11901226
// These tables are examples and need to be complete based on the MPEG standard
11911227
const uint16_t mpeg1_layer2_bitrates[] = {
@@ -1228,9 +1264,9 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
12281264
return false;
12291265
}
12301266
// Modified part: Support for Layer II and Layer III
1231-
if (header_info->layer != 1 && header_info->layer != 2) { // Allow Layer II (2) and Layer III (1)
1267+
if (header_info->layer != 1 && header_info->layer != 2 && header_info->layer != 3 ) { // Allow Layer I (3) Layer II (2) and Layer III (1)
12321268
printf("\n"); for(int i = 0; i<10; i++) printf("0x%02x ", header_data[i]); printf("\n"); // Use header_data instead of buf
1233-
log_d("Not Layer II or III\n");
1269+
log_d("Not Layer I or II or III\n");
12341270
return false;
12351271
}
12361272

@@ -1261,13 +1297,17 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
12611297
bitrate_kbps = mpeg1_layer3_bitrates[header_info->bitrate_idx];
12621298
} else if (header_info->layer == 2) { // Layer II
12631299
bitrate_kbps = mpeg1_layer2_bitrates[header_info->bitrate_idx];
1300+
} else if (header_info->layer == 3) { // Layer I
1301+
bitrate_kbps = mpeg1_layer1_bitrates[header_info->bitrate_idx];
12641302
}
12651303
} else if (header_info->mpeg_version == 2 || header_info->mpeg_version == 0) { // MPEG 2 or MPEG 2.5
12661304
if (header_info->layer == 1) { // Layer III
1267-
bitrate_kbps = mpeg2_layer3_bitrates[header_info->bitrate_idx]; // Assuming mpeg2_layer3_bitrates exists
1305+
bitrate_kbps = mpeg2_layer3_bitrates[header_info->bitrate_idx];
12681306
} else if (header_info->layer == 2) { // Layer II
12691307
bitrate_kbps = mpeg2_layer2_bitrates[header_info->bitrate_idx];
12701308
}
1309+
// If you also want to support MPEG 2/2.5 Layer I, you'd add another else if here
1310+
// e.g., else if (header_info->layer == 3) { bitrate_kbps = mpeg2_layer1_bitrates[header_info->bitrate_idx]; }
12711311
}
12721312

12731313
if (bitrate_kbps == 0 || sample_rate_hz == 0) {
@@ -1292,9 +1332,18 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
12921332
} else if (header_info->mpeg_version == 2 || header_info->mpeg_version == 0) { // MPEG 2/2.5 Layer II
12931333
header_info->frame_length = (144 * bitrate_kbps * 1000) / sample_rate_hz;
12941334
}
1335+
} else if (header_info->layer == 3) { // Layer I
1336+
// For Layer I, the formula is (Bitrate * 12 / SampleRate) + Padding (in Bytes)
1337+
// Note: Bitrate is in kbps, so multiply by 1000 to get bps
1338+
if (header_info->mpeg_version == 3) { // MPEG 1 Layer I
1339+
header_info->frame_length = (bitrate_kbps * 1000 / 8 * 12) / sample_rate_hz; // Correct
1340+
} else if (header_info->mpeg_version == 2 || header_info->mpeg_version == 0) { // MPEG 2/2.5 Layer I (if supported)
1341+
// You'd add the specific calculation for MPEG2/2.5 Layer I here
1342+
// For MPEG 2/2.5 Layer I, samples per frame is 576, so the constant is 6
1343+
header_info->frame_length = (bitrate_kbps * 1000 / 8 * 6) / sample_rate_hz; // Hypothetical, verify constant
1344+
}
12951345
}
12961346

1297-
12981347
if (header_info->padding) {
12991348
header_info->frame_length += 1; // Füge 1 Byte für Padding hinzu
13001349
}
@@ -1303,7 +1352,34 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
13031352
log_d("Calculated frame length is zero\n");
13041353
return false;
13051354
}
1306-
1355+
header_info->sample_rate_hz = sample_rate_hz;
1356+
header_info->bitrate_kbps = bitrate_kbps;
1357+
1358+
// Determine samples_per_frame based on the version and layer
1359+
if (header_info->mpeg_version == 3) { // MPEG-1
1360+
if (header_info->layer == 1 || header_info->layer == 2) { // Layer III oder Layer II
1361+
header_info->samples_per_frame = 1152;
1362+
} else if (header_info->layer == 3) { // Layer I
1363+
header_info->samples_per_frame = 384;
1364+
} else {
1365+
header_info->samples_per_frame = 0; // Should be caught by previous checks
1366+
return false;
1367+
}
1368+
} else if (header_info->mpeg_version == 2 || header_info->mpeg_version == 0) { // MPEG-2 oder MPEG-2.5
1369+
if (header_info->layer == 1) { // Layer III
1370+
header_info->samples_per_frame = 576;
1371+
} else if (header_info->layer == 2) { // Layer II
1372+
header_info->samples_per_frame = 1152;
1373+
} else if (header_info->layer == 3) { // Layer I
1374+
header_info->samples_per_frame = 576; // Correct for MPEG-2/2.5 Layer I
1375+
} else {
1376+
header_info->samples_per_frame = 0; // Should be caught by previous checks
1377+
return false;
1378+
}
1379+
} else { // header_info->mpeg_version == 1 (Reserved)
1380+
header_info->samples_per_frame = 0;
1381+
return false;
1382+
}
13071383
return true; // Header ist gültig
13081384
};
13091385

@@ -1313,8 +1389,8 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
13131389
auto findSync = [&](uint8_t* search_buf, uint16_t offset, uint16_t len) {
13141390
for (int32_t i = 0; i < len - 1; i++) {
13151391
// Prüfe auf die 11 oder 12 Sync-Bits
1316-
if ((search_buf[i + offset] == m_SYNCWORDH) &&
1317-
((search_buf[i + offset + 1] & m_SYNCWORDL) == m_SYNCWORDL)) {
1392+
if ((search_buf[i + offset] == SYNCWORDH) &&
1393+
((search_buf[i + offset + 1] & SYNCWORDL) == SYNCWORDL)) {
13181394
return i;
13191395
}
13201396
}
@@ -1345,11 +1421,23 @@ int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes) {
13451421
if (current_pos + header.frame_length + mp3FHsize <= current_pos + nBytes) {
13461422
// Check whether there is a syncword at the expected next frame start and a valid header is (optional but very robust)
13471423
Mp3FrameHeader next_header;
1348-
if (((buf[current_pos + header.frame_length] == m_SYNCWORDH) &&
1349-
((buf[current_pos + header.frame_length + 1] & m_SYNCWORDL) == m_SYNCWORDL)) &&
1350-
parseMp3Header(&buf[current_pos + header.frame_length], &next_header)
1351-
) {
1424+
if (((buf[current_pos + header.frame_length] == SYNCWORDH) && ((buf[current_pos + header.frame_length + 1] & SYNCWORDL) == SYNCWORDL)) &&
1425+
parseMp3Header(&buf[current_pos + header.frame_length], &next_header)) {
13521426
log_d("Found reliable MP3 frame at pos: %d, length: %lu\n", current_pos, header.frame_length);
1427+
1428+
// s_samplerate = header.sample_rate_hz; // (angenommen in der Struktur vorhanden)
1429+
// s_bitRate = header.bitrate_kbps; // (angenommen in der Struktur vorhanden)
1430+
// s_mpeg_version = header.mpeg_version;
1431+
// s_layer = header.layer;
1432+
// s_channel_mode = header.channel_mode;
1433+
// s_samples_per_frame = header.samples_per_frame;
1434+
1435+
// // Für s_channels (1 für Mono, 2 für Stereo)
1436+
// if (header.channel_mode == 0b11) { // 0b11 ist Mono
1437+
// s_channels = 1;
1438+
// } else { // Alle anderen Modi (Stereo, Joint Stereo, Dual Channel) sind 2 Kanäle
1439+
// s_channels = 2;
1440+
// }
13531441
return current_pos;
13541442
} else {
13551443
log_d("Header valid, but next frame does not validate. False positive. Moving on.\n");
@@ -1523,7 +1611,7 @@ void MP3ClearBadFrame(int16_t *outbuf) {
15231611
* Notes: switching useSize on and off between frames in the same stream
15241612
* is not supported (bit reservoir is not maintained if useSize on)
15251613
**********************************************************************************************************************/
1526-
int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf, int32_t useSize){
1614+
int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf){
15271615

15281616
// int pos = MP3FindSyncWord(inbuf, *bytesLeft);
15291617
// if(pos > 0){*bytesLeft -= pos; MP3_INFO("skip %i bytes", pos); return MP3_NONE; }
@@ -1576,21 +1664,21 @@ int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf, int32_t
15761664
* - calling function should set mainDataBegin to 0, and tell us exactly how large this
15771665
* frame is (in bytesLeft)
15781666
*/
1579-
if (useSize) {
1580-
m_MP3DecInfo->nSlots = *bytesLeft;
1581-
if (m_MP3DecInfo->mainDataBegin != 0 || m_MP3DecInfo->nSlots <= 0) {
1582-
/* error - non self-contained frame, or missing frame (size <= 0), could do loss concealment here */
1583-
MP3ClearBadFrame(outbuf);
1584-
MP3_ERROR("MP3, invalid frameheader");
1585-
return MP3_ERR;
1586-
}
1587-
1588-
/* can operate in-place on reformatted frames */
1589-
m_MP3DecInfo->mainDataBytes = m_MP3DecInfo->nSlots;
1590-
mainPtr = inbuf;
1591-
inbuf += m_MP3DecInfo->nSlots;
1592-
*bytesLeft -= (m_MP3DecInfo->nSlots);
1593-
} else {
1667+
// if (useSize) {
1668+
// m_MP3DecInfo->nSlots = *bytesLeft;
1669+
// if (m_MP3DecInfo->mainDataBegin != 0 || m_MP3DecInfo->nSlots <= 0) {
1670+
// /* error - non self-contained frame, or missing frame (size <= 0), could do loss concealment here */
1671+
// MP3ClearBadFrame(outbuf);
1672+
// MP3_ERROR("MP3, invalid frameheader");
1673+
// return MP3_ERR;
1674+
// }
1675+
1676+
// /* can operate in-place on reformatted frames */
1677+
// m_MP3DecInfo->mainDataBytes = m_MP3DecInfo->nSlots;
1678+
// mainPtr = inbuf;
1679+
// inbuf += m_MP3DecInfo->nSlots;
1680+
// *bytesLeft -= (m_MP3DecInfo->nSlots);
1681+
// } else {
15941682
/* out of data - assume last or truncated frame */
15951683
if (m_MP3DecInfo->nSlots > *bytesLeft) {
15961684
MP3ClearBadFrame(outbuf);
@@ -1625,7 +1713,7 @@ int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf, int32_t
16251713
MP3_ERROR("MP3, maindata underflow");
16261714
return MP3_ERR;
16271715
}
1628-
}
1716+
// }
16291717
bitOffset = 0;
16301718
mainBits = m_MP3DecInfo->mainDataBytes * 8;
16311719

src/mp3_decoder/mp3_decoder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ const uint32_t csa[8][2] PROGMEM = {
483483
bool MP3Decoder_AllocateBuffers(void);
484484
bool MP3Decoder_IsInit();
485485
void MP3Decoder_FreeBuffers();
486-
int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf, int32_t useSize);
486+
int32_t MP3Decode( uint8_t *inbuf, int32_t *bytesLeft, int16_t *outbuf);
487487
void MP3GetLastFrameInfo();
488488
int32_t MP3GetNextFrameInfo(uint8_t *buf);
489489
int32_t MP3FindSyncWord(uint8_t *buf, int32_t nBytes);

0 commit comments

Comments
 (0)