arduino-audio-tools
CodecCodec2.h
Go to the documentation of this file.
1 
18 #pragma once
19 
20 #include "AudioTools/AudioCodecs/AudioCodecsBase.h"
21 #include "codec2.h"
22 
23 
24 namespace audio_tools {
25 
27 int getCodec2Mode(int bits_per_second) {
28  switch (bits_per_second) {
29  case 3200:
30  return CODEC2_MODE_3200;
31  case 2400:
32  return CODEC2_MODE_2400;
33  case 1600:
34  return CODEC2_MODE_1600;
35  case 1400:
36  return CODEC2_MODE_1400;
37  case 1300:
38  return CODEC2_MODE_1300;
39  case 1200:
40  return CODEC2_MODE_1200;
41  case 700:
42  return CODEC2_MODE_700C;
43  case 450:
44  return CODEC2_MODE_450;
45  default:
46  LOGE(
47  "Unsupported sample rate: use 3200, 2400, 1600, 1400, 1300, 1200, "
48  "700 or 450");
49  return -1;
50  }
51 }
52 
61 class Codec2Decoder : public AudioDecoder {
62  public:
63  Codec2Decoder(int bps = 3200) {
64  info.sample_rate = 8000;
65  info.channels = 1;
66  info.bits_per_sample = 16;
67  setBitsPerSecond(bps);
68  }
71  virtual void setBitsPerSecond(int bps) { bits_per_second = bps; }
72 
73  int bitsPerSecond() { return bits_per_second; }
74 
75  virtual bool begin() {
76  TRACEI();
77 
78  int mode = getCodec2Mode(bits_per_second);
79  if (mode == -1) {
80  LOGE("invalid bits_per_second")
81  return false;
82  }
83  if (info.channels != 1) {
84  LOGE("Only 1 channel supported")
85  return false;
86  }
87  if (info.bits_per_sample != 16) {
88  LOGE("Only 16 bps are supported")
89  return false;
90  }
91  if (info.sample_rate != 8000) {
92  LOGW("Sample rate should be 8000: %d", info.sample_rate);
93  }
94 
95  p_codec2 = codec2_create(mode);
96  if (p_codec2 == nullptr) {
97  LOGE("codec2_create");
98  return false;
99  }
100 
101  result_buffer.resize(bytesCompressed());
102  input_buffer.resize(bytesCompressed() );
103 
104  assert(input_buffer.size()>0);
105  assert(result_buffer.size()>0);
106 
107  notifyAudioChange(info);
108  LOGI("bytesCompressed:%d", bytesCompressed());
109  LOGI("bytesUncompressed:%d", bytesUncompressed());
110  is_active = true;
111  return true;
112  }
113 
114  int bytesCompressed() {
115  return p_codec2 != nullptr ? codec2_bytes_per_frame(p_codec2) : 0;
116  }
117 
118  int bytesUncompressed() {
119  return p_codec2 != nullptr
120  ? codec2_samples_per_frame(p_codec2) * sizeof(int16_t)
121  : 0;
122  }
123 
124 
125  virtual void end() {
126  TRACEI();
127  codec2_destroy(p_codec2);
128  is_active = false;
129  }
130 
131  virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
132 
133  operator bool() { return is_active; }
134 
135  size_t write(const uint8_t *data, size_t len) override {
136  LOGD("write: %d", len);
137  if (!is_active) {
138  LOGE("inactive");
139  return 0;
140  }
141 
142  uint8_t *p_byte = (uint8_t *)data;
143  for (int j = 0; j < len; j++) {
144  processByte(p_byte[j]);
145  }
146 
147  return len;
148  }
149 
150  protected:
151  Print *p_print = nullptr;
152  struct CODEC2 *p_codec2;
153  bool is_active = false;
154  Vector<uint8_t> input_buffer;
155  Vector<uint8_t> result_buffer;
156  int input_pos = 0;
157  int bits_per_second = 0;
158 
160  void processByte(uint8_t byte) {
161  // add byte to buffer
162  input_buffer[input_pos++] = byte;
163 
164  // decode if buffer is full
165  if (input_pos >= input_buffer.size()) {
166  codec2_decode(p_codec2, (short*)result_buffer.data(), input_buffer.data());
167  int written = p_print->write((uint8_t *)result_buffer.data(), result_buffer.size());
168  if (written != result_buffer.size()){
169  LOGE("write: %d written: %d", result_buffer.size(), written);
170  } else {
171  LOGD("write: %d written: %d", result_buffer.size(), written);
172  }
173  delay(2);
174  input_pos = 0;
175  }
176  }
177 };
178 
187 class Codec2Encoder : public AudioEncoder {
188  public:
189  Codec2Encoder(int bps = 3200) {
190  info.sample_rate = 8000;
191  info.channels = 1;
192  info.bits_per_sample = 16;
193  setBitsPerSecond(bps);
194  }
195 
198  virtual void setBitsPerSecond(int bps) { bits_per_second = bps; }
199 
200  int bitsPerSecond() { return bits_per_second; }
201 
202  int bytesCompressed() {
203  return p_codec2 != nullptr ? codec2_bytes_per_frame(p_codec2) : 0;
204  }
205 
206  int bytesUncompressed() {
207  return p_codec2 != nullptr
208  ? codec2_samples_per_frame(p_codec2) * sizeof(int16_t)
209  : 0;
210  }
211 
212  bool begin() {
213  TRACEI();
214 
215  int mode = getCodec2Mode(bits_per_second);
216  if (mode == -1) {
217  LOGE("invalid bits_per_second")
218  return false;
219  }
220  if (info.channels != 1) {
221  LOGE("Only 1 channel supported")
222  return false;
223  }
224  if (info.bits_per_sample != 16) {
225  LOGE("Only 16 bps are supported")
226  return false;
227  }
228  if (info.sample_rate != 8000) {
229  LOGW("Sample rate should be 8000: %d", info.sample_rate);
230  }
231 
232  p_codec2 = codec2_create(mode);
233  if (p_codec2 == nullptr) {
234  LOGE("codec2_create");
235  return false;
236  }
237 
238  input_buffer.resize(bytesCompressed());
239  result_buffer.resize(bytesUncompressed());
240  assert(input_buffer.size()>0);
241  assert(result_buffer.size()>0);
242  LOGI("bytesCompressed:%d", bytesCompressed());
243  LOGI("bytesUncompressed:%d", bytesUncompressed());
244  is_active = true;
245  return true;
246  }
247 
248  virtual void end() {
249  TRACEI();
250  codec2_destroy(p_codec2);
251  is_active = false;
252  }
253 
254  virtual const char *mime() { return "audio/codec2"; }
255 
256  virtual void setOutput(Print &out_stream) { p_print = &out_stream; }
257 
258  operator bool() { return is_active; }
259 
260  size_t write(const uint8_t *in_ptr, size_t in_size) override {
261  LOGD("write: %d", in_size);
262  if (!is_active) {
263  LOGE("inactive");
264  return 0;
265  }
266  // encode bytes
267  uint8_t *p_byte = (uint8_t *)in_ptr;
268  for (int j = 0; j < in_size; j++) {
269  processByte(p_byte[j]);
270  }
271  return in_size;
272  }
273 
274  protected:
275  Print *p_print = nullptr;
276  struct CODEC2 *p_codec2 = nullptr;
277  bool is_active = false;
278  int buffer_pos = 0;
279  Vector<uint8_t> input_buffer;
280  Vector<uint8_t> result_buffer;
281  int bits_per_second = 0;
282 
283  // add byte to decoding buffer and decode if buffer is full
284  void processByte(uint8_t byte) {
285  input_buffer[buffer_pos++] = byte;
286  if (buffer_pos >= input_buffer.size()) {
287  // encode
288  codec2_encode(p_codec2, result_buffer.data(),
289  (short*)input_buffer.data());
290  int written = p_print->write(result_buffer.data(), result_buffer.size());
291  if(written!=result_buffer.size()){
292  LOGE("write: %d written: %d", result_buffer.size(), written);
293  } else {
294  LOGD("write: %d written: %d", result_buffer.size(), written);
295  }
296  buffer_pos = 0;
297  }
298  }
299 };
300 
301 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioCodecsBase.h:16
Encoding of PCM data.
Definition: AudioCodecsBase.h:84
Decoder for Codec2. Depends on https://github.com/pschatzmann/arduino-libcodec2.
Definition: CodecCodec2.h:61
virtual void setOutput(Print &out_stream)
Defines where the decoded result is written to.
Definition: CodecCodec2.h:131
virtual void setBitsPerSecond(int bps)
Definition: CodecCodec2.h:71
void processByte(uint8_t byte)
Build decoding buffer and decode when frame is full.
Definition: CodecCodec2.h:160
Encoder for Codec2 - Depends on https://github.com/pschatzmann/arduino-libcodec2.
Definition: CodecCodec2.h:187
virtual void setBitsPerSecond(int bps)
Definition: CodecCodec2.h:198
virtual const char * mime()
Provides the mime type of the encoded result.
Definition: CodecCodec2.h:254
Definition: NoArduino.h:58
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
int getCodec2Mode(int bits_per_second)
Convert bits per sample to Codec2 mode.
Definition: CodecCodec2.h:27
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