Skip to content

Commit f806236

Browse files
committed
AAC support
1 parent 6e8fb7b commit f806236

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+71557
-15
lines changed

.vscode/launch.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "(lldb) Launch",
9+
"type": "cppdbg",
10+
"request": "launch",
11+
"program": "${workspaceFolder}/build/examples/output_aac/aac",
12+
"args": [],
13+
"stopAtEntry": true,
14+
"cwd": "${fileDirname}",
15+
"environment": [],
16+
"externalConsole": false,
17+
"MIMode": "lldb"
18+
}
19+
]
20+
}

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"files.associations": {
3+
"istream": "cpp",
4+
"tuple": "cpp",
5+
"*.tcc": "cpp"
6+
}
7+
}

CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ project(arduino_helix)
55
include(FetchContent)
66

77
# lots of warnings and all warnings as errors
8-
add_compile_options(-Wall -Wextra -pedantic )
8+
## add_compile_options(-Wall -Wextra )
99
set(CMAKE_CXX_STANDARD 17)
1010

1111
# Build with Linux Arduino Emulator
@@ -24,13 +24,13 @@ add_library (arduino_helix ${SRC_LIST_C})
2424

2525
# prevent compile errors
2626
target_compile_options(arduino_helix PRIVATE -DUSE_DEFAULT_STDLIB)
27-
target_compile_options(arduino_emulator PRIVATE -DUSE_DEFAULT_STDLIB -DEMULATOR )
2827

2928
# define location for header files
30-
target_include_directories(arduino_helix PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/libhelix-mp3 )
29+
target_include_directories(arduino_helix PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/libhelix-mp3 ${CMAKE_CURRENT_SOURCE_DIR}/src/libhelix-aac )
3130

3231
# specify libraries
3332
target_link_libraries(arduino_helix arduino_emulator)
3433

3534
# build examples
36-
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/examples/output_mp3/")
35+
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/examples/output_mp3")
36+
add_subdirectory( "${CMAKE_CURRENT_SOURCE_DIR}/examples/output_aac")

examples/output_aac/BabyElephantWalk60_aac.h

Lines changed: 57020 additions & 0 deletions
Large diffs are not rendered by default.

examples/output_aac/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
# set the project name
4+
project(aac)
5+
set (DCMAKE_CXX_FLAGS -Werror -Wall -Wextra)
6+
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
7+
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
8+
9+
# build sketch as executable
10+
add_executable (aac desktop_aac.cpp )
11+
target_compile_options(aac PRIVATE -DEMULATOR)
12+
13+
# specify libraries
14+
target_link_libraries(aac arduino_helix)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifdef EMULATOR
2+
#include "Arduino.h"
3+
#include "output_aac.ino"
4+
5+
int main(){
6+
setup();
7+
while(true){
8+
loop();
9+
}
10+
return -1;
11+
}
12+
13+
#endif

examples/output_aac/output_aac.ino

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
/**
3+
* @file output_aac.ino
4+
* @author Phil Schatzmann
5+
* @brief We just display the decoded audio data on the serial monitor
6+
* @version 0.1
7+
* @date 2021-07-18
8+
*
9+
* @copyright Copyright (c) 2021
10+
*
11+
*/
12+
#include "AACDecoderHelix.h"
13+
#include "BabyElephantWalk60_aac.h"
14+
15+
using namespace libhelix;
16+
17+
void dataCallback(AACFrameInfo &info, int16_t *pwm_buffer, size_t len) {
18+
for (size_t i=0; i<len; i+=info.nChans){
19+
for (int j=0;j<info.nChans;j++){
20+
Serial.print(pwm_buffer[i+j]);
21+
Serial.print(" ");
22+
}
23+
Serial.println();
24+
}
25+
}
26+
27+
AACDecoderHelix aac(dataCallback);
28+
29+
void setup() {
30+
Serial.begin(115200);
31+
aac.begin();
32+
}
33+
34+
void loop() {
35+
Serial.println("writing...");
36+
aac.write(BabyElephantWalk60_aac, BabyElephantWalk60_aac_len);
37+
38+
// restart from the beginning
39+
delay(2000);
40+
aac.begin();
41+
}

examples/output_mp3/CMakeLists.txt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ cmake_minimum_required(VERSION 3.16)
33
# set the project name
44
project(mp3)
55
set (DCMAKE_CXX_FLAGS -Werror -Wall -Wextra)
6-
7-
# Collect all relevant source files
8-
file(GLOB_RECURSE SRC_LIST CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.c" )
6+
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
7+
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
98

109
# build sketch as executable
11-
add_executable (mp3 desktop_mp3.cpp ${SRC_LIST})
12-
target_compile_options(mp3 PRIVATE -DUSE_DEFAULT_STDLIB -DDEFINE_MAIN -DEMULATOR)
10+
add_executable (mp3 desktop_mp3.cpp )
11+
target_compile_options(mp3 PRIVATE -DEMULATOR)
1312

1413
# specify libraries
1514
target_link_libraries(mp3 arduino_helix)

src/AACDecoderHelix.h

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#pragma once
2+
3+
#include "Arduino.h"
4+
#include "CommonHelix.h"
5+
#include "libhelix-aac/aacdec.h"
6+
7+
#define AAC_MAX_OUTPUT_SIZE 2048
8+
#define AAC_MAX_FRAME_SIZE 1600
9+
10+
namespace libhelix {
11+
12+
typedef void (*AACInfoCallback)(_AACFrameInfo &info);
13+
typedef void (*AACDataCallback)(_AACFrameInfo &info,short *pwm_buffer, size_t len);
14+
15+
/**
16+
* @brief A simple Arduino API for the libhelix AAC decoder. The data us provided with the help of write() calls.
17+
* The decoded result is available either via a callback method or via an output stream.
18+
*/
19+
class AACDecoderHelix : public CommonHelix {
20+
public:
21+
AACDecoderHelix() {
22+
}
23+
24+
AACDecoderHelix(Print &output, AACInfoCallback infoCallback=nullptr){
25+
this->out = &output;
26+
this->infoCallback = infoCallback;
27+
}
28+
29+
AACDecoderHelix(AACDataCallback dataCallback){
30+
this->pwmCallback = dataCallback;
31+
}
32+
void setInfoCallback(AACInfoCallback cb){
33+
this->infoCallback = cb;
34+
}
35+
36+
void setDataCallback(AACDataCallback cb){
37+
this->pwmCallback = cb;
38+
}
39+
40+
void setOutput(Print &output){
41+
this->out = &output;
42+
}
43+
44+
/// Starts the processing
45+
void begin(){
46+
LOG(Debug, "begin");
47+
CommonHelix::begin();
48+
decoder = AACInitDecoder();
49+
if (decoder==nullptr){
50+
LOG(Error, "MP3InitDecoder has failed");
51+
active = false;
52+
return;
53+
}
54+
}
55+
56+
/// Releases the reserved memory
57+
void end(){
58+
LOG(Debug, "end");
59+
AACFreeDecoder(decoder);
60+
CommonHelix::end();
61+
}
62+
63+
/// Provides the last available _AACFrameInfo_t
64+
_AACFrameInfo audioInfo(){
65+
return aacFrameInfo;
66+
}
67+
68+
protected:
69+
HAACDecoder decoder = nullptr;
70+
AACDataCallback pwmCallback = nullptr;
71+
AACInfoCallback infoCallback = nullptr;
72+
_AACFrameInfo aacFrameInfo;
73+
74+
size_t maxFrameSize(){
75+
return max_frame_size == 0 ? AAC_MAX_FRAME_SIZE : max_frame_size;
76+
}
77+
78+
size_t maxPWMSize() {
79+
return max_pwm_size == 0 ? AAC_MAX_OUTPUT_SIZE : max_pwm_size;
80+
}
81+
82+
int findSynchWord(int offset=0) {
83+
int result = AACFindSyncWord(frame_buffer+offset, buffer_size)+offset;
84+
return result < 0 ? result : result + offset;
85+
}
86+
87+
/// decods the data and removes the decoded frame from the buffer
88+
void decode(Range r) {
89+
LOG(Debug, "decode %d", r.end);
90+
int len = r.end;
91+
int bytesLeft = r.end; //r.end; //r.end; // buffer_size
92+
uint8_t* ptr = frame_buffer + r.start;
93+
94+
int result = AACDecode(decoder, &ptr, &bytesLeft, pwm_buffer);
95+
int decoded = len - bytesLeft;
96+
assert(decoded == ptr-(frame_buffer + r.start));
97+
if (result==0){
98+
LOG(Debug, "-> bytesLeft %d -> %d = %d ", buffer_size, bytesLeft, decoded);
99+
LOG(Debug, "-> End of frame (%d) vs end of decoding (%d)", r.end, decoded)
100+
101+
// return the decoded result
102+
_AACFrameInfo info;
103+
AACGetLastFrameInfo(decoder, &info);
104+
provideResult(info);
105+
106+
// remove processed data from buffer
107+
buffer_size -= decoded;
108+
assert(buffer_size<=maxFrameSize());
109+
memmove(frame_buffer, frame_buffer+r.start+decoded, buffer_size);
110+
LOG(Debug, " -> decoded %d bytes - remaining buffer_size: %d", decoded, buffer_size);
111+
} else {
112+
// in the case of error we remove the frame
113+
LOG(Debug, " -> decode error: %d - removing frame!", result);
114+
int ignore = decoded;
115+
if (ignore <= 0) ignore = r.end;
116+
// We advance to the next synch world
117+
buffer_size -= ignore;
118+
assert(buffer_size<=maxFrameSize());
119+
memmove(frame_buffer, frame_buffer+ignore, buffer_size);
120+
}
121+
}
122+
123+
// return the result PWM data
124+
void provideResult(_AACFrameInfo &info){
125+
LOG(Debug, "provideResult: %d samples",info.outputSamps);
126+
if (info.outputSamps>0){
127+
// provide result
128+
if(pwmCallback!=nullptr){
129+
// output via callback
130+
pwmCallback(info, pwm_buffer,info.outputSamps);
131+
} else {
132+
// output to stream
133+
if (info.sampRateOut!=aacFrameInfo.sampRateOut && infoCallback!=nullptr){
134+
infoCallback(info);
135+
}
136+
out->write((uint8_t*)pwm_buffer, info.outputSamps);
137+
}
138+
aacFrameInfo = info;
139+
}
140+
}
141+
};
142+
}

src/CommonHelix.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class CommonHelix {
8181
active = false;
8282
return;
8383
}
84-
memset(frame_buffer,0, maxFrameSize()+1);
84+
memset(frame_buffer,0, maxFrameSize());
8585
memset(pwm_buffer,0, maxPWMSize());
8686
pwm_buffer[maxPWMSize()]=-1;
8787
active = true;
@@ -164,6 +164,8 @@ class CommonHelix {
164164
int process_size = min((int)(maxFrameSize() - buffer_size), in_size);
165165
memmove(frame_buffer+buffer_size, in_ptr, process_size);
166166
buffer_size += process_size;
167+
assert(buffer_size<=maxFrameSize());
168+
167169
LOG(Debug, "appendToBuffer %d + %d -> %u", buffer_size_old, process_size, buffer_size );
168170
return process_size;
169171
}
@@ -200,6 +202,8 @@ class CommonHelix {
200202
// make sure that buffer starts with a synch word
201203
LOG(Debug, "-> moving to new start %d",range.start);
202204
buffer_size -= range.start;
205+
assert(buffer_size<=maxFrameSize());
206+
203207
memmove(frame_buffer, frame_buffer + range.start, buffer_size);
204208
range.end -= range.start;
205209
range.start = 0;
@@ -225,6 +229,7 @@ class CommonHelix {
225229

226230
void advanceFrameBuffer(int offset){
227231
buffer_size -= offset;
232+
assert(buffer_size<=maxFrameSize());
228233
memmove(frame_buffer, frame_buffer+offset, buffer_size);
229234
}
230235

0 commit comments

Comments
 (0)