arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
CodecSBC.h
Go to the documentation of this file.
1
9#pragma once
10
11#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
12#include "sbc.h"
13#include "sbc/formats.h"
14
15
16namespace audio_tools {
17
27class SBCDecoder : public AudioDecoder {
28public:
29 SBCDecoder(int bufferSize = 8192) {
30 result_buffer = new uint8_t[bufferSize];
31 result_buffer_size = bufferSize;
32 }
33
34 ~SBCDecoder() {
35 if (result_buffer != nullptr)
36 delete[] result_buffer;
37 if (input_buffer != nullptr)
38 delete[] input_buffer;
39 }
40
41 virtual bool begin() {
42 TRACEI();
43 is_first = true;
44 is_active = true;
45 sbc_init(&sbc, 0L);
46 return true;
47 }
48
49 virtual void end() {
50 TRACEI();
51 sbc_finish(&sbc);
52 is_active = false;
53 }
54
55 virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
56
57 operator bool() { return is_active; }
58
59 virtual size_t write(const uint8_t *data, size_t len) {
60 LOGD("write: %d", len);
61 if (!is_active) {
62 LOGE("inactive");
63 return 0;
64 }
65
66 uint8_t *start = (uint8_t *)data;
67 int count = len;
68 if (is_first) {
69 framelen = firstWrite(data, len);
70 LOGI("framelen: %d", framelen);
71 // check if we have a valid frame length
72 if (isValidFrameLen(framelen)) {
73 start = start + framelen;
74 count = len - framelen;
75 is_first = false;
76 }
77 }
78
79 if (!is_first) {
80 for (int j = 0; j < count; j++) {
81 processByte(start[j]);
82 }
83 }
84
85 return len;
86 }
87
88
89 // Provides the uncompressed length (of the PCM data) in bytes
90 int bytesUncompressed() {
91 return codeSize();
92 }
95 return frameLength();
96 }
97
98protected:
99 Print *p_print = nullptr;
100 sbc_t sbc;
101 bool is_first = true;
102 bool is_active = false;
103 uint8_t *result_buffer = nullptr;
104 int result_buffer_size;
105 int framelen;
106 uint8_t *input_buffer = nullptr;
107 int input_pos = 0;
108
110 int frameLength() { return sbc_get_frame_length(&sbc); }
111
112 // Provides the uncompressed length (of the PCM data) in bytes
113 int codeSize() { return sbc_get_codesize(&sbc); }
114
117 info.bits_per_sample = 16;
118 info.channels = sbc.mode == SBC_MODE_MONO ? 1 : 2;
119 LOGI("channels: %d", info.channels);
120 switch (sbc.frequency) {
121 case SBC_FREQ_16000:
122 info.sample_rate = 16000;
123 break;
124 case SBC_FREQ_32000:
125 info.sample_rate = 32000;
126 break;
127 case SBC_FREQ_44100:
128 info.sample_rate = 44100;
129 break;
130 case SBC_FREQ_48000:
131 info.sample_rate = 48000;
132 break;
133 default:
134 LOGE("Unsupported sample rate");
135 info.sample_rate = 0;
136 break;
137 }
138 LOGI("sample_rate: %d", info.sample_rate);
139 notifyAudioChange(info);
140 }
141
142 bool isValidFrameLen(int len) { return len > 0 && len < 256; }
143
145 int firstWrite(const void *data, size_t length) {
146 size_t result_len = 0;
147 int frame_len = sbc_parse(&sbc, data, length);
148 if (isValidFrameLen(frame_len)) {
149
150 // setup audio info
152
153 // setup input buffer for subsequent decoding stpes
154 setupInputBuffer(frame_len);
155 }
156
157 return frame_len;
158 }
159
160 void setupInputBuffer(int len) {
161 LOGI("input_buffer: %d", len);
162 if (input_buffer != nullptr)
163 delete[] input_buffer;
164 input_buffer = new uint8_t[len];
165 }
166
168 void processByte(uint8_t byte) {
169 // add byte to buffer
170 input_buffer[input_pos++] = byte;
171
172 // decode if buffer is full
173 if (input_pos >= framelen) {
174 size_t result_len = 0;
175 sbc_decode(&sbc, input_buffer, framelen, result_buffer,
176 result_buffer_size, &result_len);
177 if (result_len > 0) {
178 p_print->write(result_buffer, result_len);
179 }
180 input_pos = 0;
181 }
182 }
183};
184
194class SBCEncoder : public AudioEncoder {
195public:
196 SBCEncoder(int subbands = 8, int blocks = 16, int bitpool = 32,
197 int allocation_method = SBC_AM_LOUDNESS) {
198 setSubbands(subbands);
199 setBlocks(blocks);
200 setBitpool(bitpool);
201 setAllocationMethod(allocation_method);
202 }
203
205 void setSubbands(int subbands) {
206 if (subbands == 8 || subbands == 4) {
207 this->subbands = subbands;
208 } else {
209 LOGE("Invalid subbands: %d - using 8", subbands);
210 this->subbands = 8;
211 }
212 }
213
215 void setBlocks(int blocks) {
216 if (blocks == 16 || blocks == 12 || blocks == 8 || blocks == 4) {
217 this->blocks = blocks;
218 } else {
219 LOGE("Invalid blocks: %d - using 16", blocks);
220 this->blocks = 16;
221 }
222 }
223
225 void setBitpool(int bitpool) { this->bitpool = bitpool; }
226
228 void setAllocationMethod(int allocation_method) {
229 if (allocation_method == SBC_AM_LOUDNESS || allocation_method == SBC_AM_SNR) {
230 this->allocation_method = allocation_method;
231 } else {
232 LOGE("Invalid allocation Method: %d - using SBC_AM_LOUDNESS", allocation_method);
233 this->allocation_method = SBC_AM_LOUDNESS;
234 }
235 }
236
238 bool begin() {
239 TRACEI();
240 is_first = true;
241 is_active = setup();
242 current_codesize = codeSize();
243 buffer.resize(current_codesize);
244 result_buffer.resize(frameLength());
245 return true;
246 }
247
249 virtual void end() {
250 TRACEI();
251 sbc_finish(&sbc);
252 is_active = false;
253 }
254
255 virtual const char *mime() { return "audio/sbc"; }
256
257 virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
258
259 operator bool() { return is_active; }
260
261 virtual size_t write(const uint8_t *data, size_t len) {
262 LOGD("write: %d", len);
263 if (!is_active) {
264 LOGE("inactive");
265 return 0;
266 }
267 if (p_print==nullptr){
268 LOGE("output not defined");
269 return 0;
270 }
271
272 // encode bytes
273 for (int j = 0; j < len; j++) {
274 processByte(data[j]);
275 }
276
277 return len;
278 }
279
280
281 int bytesUncompressed() {
282 return codeSize();
283 }
284 int bytesCompressed() {
285 return frameLength();
286 }
287
288protected:
289 Print *p_print = nullptr;
290 sbc_t sbc;
291 bool is_first = true;
292 bool is_active = false;
293 int current_codesize = 0;
294 int buffer_pos = 0;
295 Vector<uint8_t> buffer{0};
296 Vector<uint8_t> result_buffer{0};
297 int subbands = 4;
298 int blocks = 4;
299 int bitpool = 32;
300 int allocation_method;
301
303 int frameLength() { return sbc_get_frame_length(&sbc); }
304
306 int codeSize() { return sbc_get_codesize(&sbc); }
307
309 bool setup() {
310 sbc_init(&sbc, 0L);
311
312 if (info.bits_per_sample!=16){
313 LOGE("Invalid bits_per_sample: %d", info.bits_per_sample);
314 return false;
315 }
316
317 switch (info.sample_rate) {
318 case 16000:
319 sbc.frequency = SBC_FREQ_16000;
320 break;
321 case 32000:
322 sbc.frequency = SBC_FREQ_32000;
323 break;
324 case 44100:
325 sbc.frequency = SBC_FREQ_44100;
326 break;
327 case 48000:
328 sbc.frequency = SBC_FREQ_48000;
329 break;
330 default:
331 LOGE("Invalid sample_rate: %d", info.sample_rate);
332 return false;
333 }
334
335 switch (info.channels) {
336 case 1:
337 sbc.mode = SBC_MODE_MONO;
338 break;
339 case 2:
340 sbc.mode = SBC_MODE_STEREO;
341 break;
342 default:
343 LOGE("Invalid channels: %d", info.channels);
344 return false;
345 }
346
347 switch (subbands) {
348 case 4:
349 sbc.subbands = SBC_SB_4;
350 break;
351 case 8:
352 sbc.subbands = SBC_SB_8;
353 break;
354 default:
355 LOGE("Invalid subbands: %d", subbands);
356 return false;
357 }
358
359 switch (blocks) {
360 case 4:
361 sbc.blocks = SBC_BLK_4;
362 break;
363 case 8:
364 sbc.blocks = SBC_BLK_8;
365 break;
366 case 12:
367 sbc.blocks = SBC_BLK_12;
368 break;
369 case 16:
370 sbc.blocks = SBC_BLK_16;
371 break;
372 default:
373 LOGE("Invalid blocks: %d", blocks);
374 return false;
375 }
376
377 sbc.bitpool = bitpool;
378 sbc.allocation = allocation_method;
379 return true;
380 }
381
382 // add byte to decoding buffer and decode if buffer is full
383 void processByte(uint8_t byte) {
384 buffer[buffer_pos++] = byte;
385 if (buffer_pos >= current_codesize) {
386 ssize_t written;
387 // Encodes ONE input block into ONE output block */
388 // ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
389 // void *output, size_t output_len, ssize_t *written);
390 sbc_encode(&sbc, &buffer[0], current_codesize, &result_buffer[0],
391 result_buffer.size(), &written);
392 LOGD("sbc_encode: %d -> %d (buffer: %d))", current_codesize, written,
393 result_buffer.size());
394 p_print->write(&result_buffer[0], written);
395 buffer_pos = 0;
396 }
397 }
398};
399
400} // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Encoding of PCM data.
Definition AudioCodecsBase.h:90
Definition NoArduino.h:62
Decoder for SBC. Depends on https://github.com/pschatzmann/arduino-libsbc. Inspired by sbcdec....
Definition CodecSBC.h:27
virtual void setOutput(Print &out_stream)
Defines where the decoded result is written to.
Definition CodecSBC.h:55
int bytesCompressed()
Provides the compressed length in bytes (after encoding)
Definition CodecSBC.h:94
void setupAudioInfo()
Process audio info.
Definition CodecSBC.h:116
int frameLength()
Provides the compressed length in bytes (after encoding)
Definition CodecSBC.h:110
void processByte(uint8_t byte)
Build decoding buffer and decode when frame is full.
Definition CodecSBC.h:168
int firstWrite(const void *data, size_t length)
Determines the framelen.
Definition CodecSBC.h:145
Encoder for SBC - Depends on https://github.com/pschatzmann/arduino-libsbc. Inspired by sbcenc....
Definition CodecSBC.h:194
int codeSize()
Provides the uncompressed length (of the PCM data) in bytes.
Definition CodecSBC.h:306
void setSubbands(int subbands)
Defines the subbands: Use 4 or 8.
Definition CodecSBC.h:205
void setBlocks(int blocks)
Defines the number of blocks: valid values (4,8,12,16)
Definition CodecSBC.h:215
bool begin()
Restarts the processing.
Definition CodecSBC.h:238
virtual const char * mime()
Provides the mime type of the encoded result.
Definition CodecSBC.h:255
int frameLength()
Provides the compressed length in bytes (after encoding)
Definition CodecSBC.h:303
void setBitpool(int bitpool)
Defines the bitpool (2-86?)
Definition CodecSBC.h:225
virtual void end()
Ends the processing.
Definition CodecSBC.h:249
bool setup()
Determines audio information and calls sbc_init;.
Definition CodecSBC.h:309
void setAllocationMethod(int allocation_method)
Defines the allocation method: Use SBC_AM_LOUDNESS, SBC_AM_SNR.
Definition CodecSBC.h:228
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioConfig.h:885
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59