arduino-audio-tools
CodecSBC.h
Go to the documentation of this file.
1 
9 #pragma once
10 
11 #include "AudioCodecs/AudioEncoded.h"
12 #include "sbc.h"
13 #include "sbc/formats.h"
14 
15 
16 namespace audio_tools {
17 
27 class SBCDecoder : public AudioDecoder {
28 public:
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 void *data, size_t length) {
60  LOGD("write: %d", length);
61  if (!is_active) {
62  LOGE("inactive");
63  return 0;
64  }
65 
66  uint8_t *start = (uint8_t *)data;
67  int count = length;
68  if (is_first) {
69  framelen = firstWrite(data, length);
70  LOGI("framelen: %d", framelen);
71  // check if we have a valid frame length
72  if (isValidFrameLen(framelen)) {
73  start = start + framelen;
74  count = length - 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 length;
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 
98 protected:
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 
116  void setupAudioInfo() {
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
151  setupAudioInfo();
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 
194 class SBCEncoder : public AudioEncoder {
195 public:
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 void *in_ptr, size_t in_size) {
262  LOGD("write: %d", in_size);
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  const uint8_t *start = (const uint8_t *)in_ptr;
273  // encode bytes
274  for (int j = 0; j < in_size; j++) {
275  processByte(start[j]);
276  }
277 
278  return in_size;
279  }
280 
281 
282  int bytesUncompressed() {
283  return codeSize();
284  }
285  int bytesCompressed() {
286  return frameLength();
287  }
288 
289 protected:
290  Print *p_print = nullptr;
291  sbc_t sbc;
292  bool is_first = true;
293  bool is_active = false;
294  int current_codesize = 0;
295  int buffer_pos = 0;
296  Vector<uint8_t> buffer{0};
297  Vector<uint8_t> result_buffer{0};
298  int subbands = 4;
299  int blocks = 4;
300  int bitpool = 32;
301  int allocation_method;
302 
304  int frameLength() { return sbc_get_frame_length(&sbc); }
305 
307  int codeSize() { return sbc_get_codesize(&sbc); }
308 
310  bool setup() {
311  sbc_init(&sbc, 0L);
312 
313  if (info.bits_per_sample!=16){
314  LOGE("Invalid bits_per_sample: %d", info.bits_per_sample);
315  return false;
316  }
317 
318  switch (info.sample_rate) {
319  case 16000:
320  sbc.frequency = SBC_FREQ_16000;
321  break;
322  case 32000:
323  sbc.frequency = SBC_FREQ_32000;
324  break;
325  case 44100:
326  sbc.frequency = SBC_FREQ_44100;
327  break;
328  case 48000:
329  sbc.frequency = SBC_FREQ_48000;
330  break;
331  default:
332  LOGE("Invalid sample_rate: %d", info.sample_rate);
333  return false;
334  }
335 
336  switch (info.channels) {
337  case 1:
338  sbc.mode = SBC_MODE_MONO;
339  break;
340  case 2:
341  sbc.mode = SBC_MODE_STEREO;
342  break;
343  default:
344  LOGE("Invalid channels: %d", info.channels);
345  return false;
346  }
347 
348  switch (subbands) {
349  case 4:
350  sbc.subbands = SBC_SB_4;
351  break;
352  case 8:
353  sbc.subbands = SBC_SB_8;
354  break;
355  default:
356  LOGE("Invalid subbands: %d", subbands);
357  return false;
358  }
359 
360  switch (blocks) {
361  case 4:
362  sbc.blocks = SBC_BLK_4;
363  break;
364  case 8:
365  sbc.blocks = SBC_BLK_8;
366  break;
367  case 12:
368  sbc.blocks = SBC_BLK_12;
369  break;
370  case 16:
371  sbc.blocks = SBC_BLK_16;
372  break;
373  default:
374  LOGE("Invalid blocks: %d", blocks);
375  return false;
376  }
377 
378  sbc.bitpool = bitpool;
379  sbc.allocation = allocation_method;
380  return true;
381  }
382 
383  // add byte to decoding buffer and decode if buffer is full
384  void processByte(uint8_t byte) {
385  buffer[buffer_pos++] = byte;
386  if (buffer_pos >= current_codesize) {
387  ssize_t written;
388  // Encodes ONE input block into ONE output block */
389  // ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
390  // void *output, size_t output_len, ssize_t *written);
391  sbc_encode(&sbc, &buffer[0], current_codesize, &result_buffer[0],
392  result_buffer.size(), &written);
393  LOGD("sbc_encode: %d -> %d (buffer: %d))", current_codesize, written,
394  result_buffer.size());
395  p_print->write(&result_buffer[0], written);
396  buffer_pos = 0;
397  }
398  }
399 };
400 
401 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioEncoded.h:18
Encoding of PCM data.
Definition: AudioEncoded.h:88
Definition: NoArduino.h:58
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:307
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
int frameLength()
Provides the compressed length in bytes (after encoding)
Definition: CodecSBC.h:304
void setBitpool(int bitpool)
Defines the bitpool (2-86?)
Definition: CodecSBC.h:225
virtual void end()
Ends the processing.
Definition: CodecSBC.h:249
virtual const char * mime()
Provides the mime type of the encoded result.
Definition: CodecSBC.h:255
bool setup()
Determines audio information and calls sbc_init;.
Definition: CodecSBC.h:310
void setAllocationMethod(int allocation_method)
Defines the allocation method: Use SBC_AM_LOUDNESS, SBC_AM_SNR.
Definition: CodecSBC.h:228
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:57