arduino-audio-tools
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 
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 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 
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 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 
288 protected:
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:16
Encoding of PCM data.
Definition: AudioCodecsBase.h:84
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: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
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
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:309
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: AudioConfig.h:868
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