Skip to content

Commit a54c9f9

Browse files
committed
Added HeaderParserAAC
1 parent a6378a8 commit a54c9f9

File tree

4 files changed

+110
-5
lines changed

4 files changed

+110
-5
lines changed

examples/tests/codecs/test-mp3_parser/test-mp3_parser.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "AudioTools/AudioLibs/AudioBoardStream.h" // for SD pins
55

66
AudioSourceSD source("/", "", PIN_AUDIO_KIT_SD_CARD_CS);
7-
MP3HeaderParser mp3;
7+
HeaderParserMP3 mp3;
88

99
void setup() {
1010
Serial.begin(115200);
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#pragma once
2+
#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
3+
4+
namespace audio_tools {
5+
6+
/**
7+
* @brief AAC header parser to check if the data is a valid ADTS aac which
8+
* can extract some relevant audio information.
9+
* @ingroup codecs
10+
* @ingroup decoder
11+
* @author Phil Schatzmann
12+
* @copyright GPLv3
13+
*/
14+
15+
class HeaderParserAAC {
16+
class AACHeader {
17+
public:
18+
uint8_t id;
19+
uint8_t layer;
20+
bool protection_absent;
21+
uint8_t profile;
22+
uint8_t sampling_freq_idx;
23+
bool private_bit;
24+
uint8_t channel_cfg;
25+
bool original_copy;
26+
// uint8_t home; // not used
27+
uint8_t copyright_id_bit;
28+
uint8_t copyright_id_start;
29+
int frame_length;
30+
uint8_t adts_buf_fullness; // Used for bit reservoir, 0x7FF = VBR
31+
int num_rawdata_blocks; // Usually 00 (1 AAC frame per ADTS frame)
32+
};
33+
34+
public:
35+
/// parses the header string and returns true if this is a valid mp3 file
36+
bool isValid(const uint8_t* data, int len) {
37+
if (findSyncWord(data, len) == -1) {
38+
LOGE("Could not find FrameSync");
39+
return false;
40+
}
41+
42+
header.id = (data[1] >> 3) & 0b1;
43+
header.layer = (data[1] >> 1) & 0b11;
44+
header.protection_absent = (data[1]) & 0b1;
45+
header.profile = (data[2] >> 6) & 0b11;
46+
header.sampling_freq_idx = (data[2] >> 2) & 0b1111;
47+
header.private_bit = (data[2] >> 1) & 0b1;
48+
header.channel_cfg = ((data[2] & 0b1) << 2) | (data[3] >> 6);
49+
header.original_copy = (data[3] >> 5) & 0b1;
50+
// header.home = (data[3] >> 4) & 0b1; // unused
51+
// parse adts_variable_header()
52+
header.copyright_id_bit = (data[3] >> 3) & 0b1;
53+
header.copyright_id_start = (data[3] >> 2) & 0b1;
54+
header.frame_length =
55+
((data[3] & 0b11) << 11) | (data[4] << 3) | (data[5] >> 5);
56+
header.adts_buf_fullness = ((data[5] & 0b11111) << 6) | (data[6] >> 2);
57+
header.num_rawdata_blocks =
58+
(data[6]) & 0b11; // Usually 00 (1 AAC frame per ADTS frame)
59+
60+
bool is_valid = true;
61+
62+
is_valid = is_valid && header.sampling_freq_idx < 12;
63+
is_valid = is_valid && header.channel_cfg < 8 && header.channel_cfg > 0;
64+
is_valid = is_valid && header.profile < 3; // 0: Main, 1: LC, 2: SSR
65+
is_valid = is_valid && header.layer == 0; // 0: ADTS
66+
is_valid = is_valid && header.id <= 1; // 0: MPEG-4, 1: MPEG-2
67+
68+
if (len >= header.frame_length + 11) {
69+
is_valid = is_valid && findSyncWord(data + header.frame_length,
70+
len - header.frame_length) != 0;
71+
}
72+
return is_valid;
73+
}
74+
75+
uint16_t getSampleRate() const {
76+
return sample_rates[header.sampling_freq_idx];
77+
}
78+
79+
uint8_t getChannels() const { return header.channel_cfg; }
80+
81+
/// Determines the frame length
82+
int getFrameLength() { return header.frame_length; }
83+
84+
/// Finds the mp3/aac sync word
85+
int findSyncWord(const uint8_t* buf, size_t nBytes, uint8_t synch = 0xFF,
86+
uint8_t syncl = 0xF0) {
87+
for (int i = 0; i < nBytes - 1; i++) {
88+
if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl)
89+
return i;
90+
}
91+
return -1;
92+
}
93+
int getRawDataBlocks() { return header.num_rawdata_blocks; }
94+
95+
AACHeader getHeader() { return header; }
96+
97+
protected:
98+
const int sample_rates[13] = {96000, 88200, 64000, 48000, 44100, 32000, 24000,
99+
22050, 16000, 12000, 11025, 8000, 7350};
100+
101+
AACHeader header{0};
102+
};
103+
104+
} // namespace audio_tools

src/AudioTools/AudioCodecs/MP3HeaderParser.h renamed to src/AudioTools/AudioCodecs/HeaderParserMP3.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace audio_tools {
1414
* @copyright GPLv3
1515
*/
1616

17-
class MP3HeaderParser {
17+
class HeaderParserMP3 {
1818
// MPEG audio frame header
1919
// variables are declared in their serialized order
2020
// includes crc value

src/AudioTools/CoreAudio/AudioMetaData/MimeDetector.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

3-
#include "AudioTools/AudioCodecs/MP3HeaderParser.h"
3+
#include "AudioTools/AudioCodecs/HeaderParserMP3.h"
4+
#include "AudioTools/AudioCodecs/HeaderParserAAC.h"
45
#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
56

67
namespace audio_tools {
@@ -74,7 +75,7 @@ class MimeDetector {
7475
if (!(start[0] == 0xFF &&
7576
(start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9)))
7677
return false;
77-
MP3HeaderParser mp3;
78+
HeaderParserMP3 mp3;
7879
// it should start with a synch word
7980
if (mp3.findSyncWord((const uint8_t*)start, len) != 0) {
8081
return false;
@@ -92,7 +93,7 @@ class MimeDetector {
9293
}
9394

9495
static bool checkMP3Ext(uint8_t* start, size_t len) {
95-
MP3HeaderParser mp3;
96+
HeaderParserMP3 mp3;
9697
return mp3.isValid(start, len);
9798
}
9899

0 commit comments

Comments
 (0)