arduino-audio-tools
CodecOpus.h
1 #pragma once
2 
3 #include "AudioCodecs/AudioEncoded.h"
4 #include "Print.h"
5 #include "opus.h"
6 
7 #ifndef OPUS_ENC_MAX_BUFFER_SIZE
8 #define OPUS_ENC_MAX_BUFFER_SIZE 2048
9 #endif
10 
11 #ifndef OPUS_DEC_MAX_BUFFER_SIZE
12 #define OPUS_DEC_MAX_BUFFER_SIZE 4*1024
13 #endif
14 
15 
16 namespace audio_tools {
17 
23 struct OpusSettings : public AudioInfo {
26  sample_rate = 48000;
28  channels = 2;
30  bits_per_sample = 16;
31  }
32  int max_buffer_size = OPUS_DEC_MAX_BUFFER_SIZE;
33  int max_buffer_write_size = 512;
34 
35 };
36 
71  max_buffer_size = OPUS_ENC_MAX_BUFFER_SIZE;
72  }
75  int application = OPUS_APPLICATION_AUDIO;
78  int bitrate = -1;
80  int force_channel = -1;
82  int vbr = -1;
84  int vbr_constraint = -1;
86  int complexity = -1;
91  int max_bandwidth = -1;
93  int singal = -1;
95  int inband_fec = -1;
97  int packet_loss_perc = -1;
99  int lsb_depth = -1;
103  int use_dtx = -1;
105  int frame_sizes_ms_x2 = -1; /* x2 to avoid 2.5 ms */
106 };
107 
116  public:
120  OpusAudioDecoder() = default;
121 
127  OpusAudioDecoder(Print &out_stream) {
128  TRACED();
129  setOutput(out_stream);
130  }
131 
133  void setOutput(Print &out_stream) override { p_print = &out_stream; }
134 
135  AudioInfo audioInfo() override { return cfg; }
136 
138  OpusSettings &config() { return cfg; }
139  OpusSettings &defaultConfig() { return cfg; }
140 
141  bool begin(OpusSettings settings) {
142  TRACED();
143  AudioDecoder::setAudioInfo(settings);
144  cfg = settings;
145  notifyAudioChange(cfg);
146  return begin();
147  }
148 
149  bool begin() override {
150  TRACED();
151  if (!isValidRate(cfg.sample_rate)){
152  LOGE("Sample rate not supported: %d", cfg.sample_rate);
153  return false;
154  }
155  outbuf.resize(cfg.max_buffer_size);
156  assert(outbuf.data() != nullptr);
157 
158  // int err;
159  // dec = opus_decoder_create(cfg.sample_rate, cfg.channels, &err);
160 
161  size_t size = opus_decoder_get_size(cfg.channels);
162  decbuf.resize(size);
163  assert(decbuf.data() != nullptr);
164  dec = (OpusDecoder*)decbuf.data();
165  int err = opus_decoder_init(dec, cfg.sample_rate, cfg.channels);
166 
167 
168  if (err != OPUS_OK) {
169  LOGE("opus_decoder_create: %s for sample_rate: %d, channels:%d",
170  opus_strerror(err), cfg.sample_rate, cfg.channels);
171  return false;
172  }
173  active = true;
174  return true;
175  }
176 
177  void end() override {
178  TRACED();
179  if (dec) {
180  opus_decoder_destroy(dec);
181  dec = nullptr;
182  }
183  outbuf.resize(0);
184  decbuf.resize(0);
185  active = false;
186  }
187 
188  void setAudioInfo(AudioInfo from) override {
190  info = from;
191  cfg.sample_rate = from.sample_rate;
192  cfg.channels = from.channels;
193  cfg.bits_per_sample = from.bits_per_sample;
194  }
195 
196  size_t write(const void *in_ptr, size_t in_size) override {
197  if (!active || p_print == nullptr) return 0;
198  // decode data
199  LOGD("OpusAudioDecoder::write: %d", (int)in_size);
200  int in_band_forward_error_correction = 0;
201  int frame_count = cfg.max_buffer_size / cfg.channels / sizeof(opus_int16);
202  int out_samples = opus_decode(
203  dec, (uint8_t *)in_ptr, in_size, (opus_int16 *)outbuf.data(),
204  frame_count, in_band_forward_error_correction);
205  if (out_samples < 0) {
206  LOGW("opus-decode: %s", opus_strerror(out_samples));
207  } else if (out_samples > 0) {
208  // write data to final destination
209  int out_bytes = out_samples * cfg.channels * sizeof(int16_t);
210  LOGD("opus-decode: %d", out_bytes);
211  int open = out_bytes;
212  int processed = 0;
213  while(open>0){
214  int to_write = std::min(open, cfg.max_buffer_write_size);
215  int written = p_print->write(outbuf.data()+processed, to_write);
216  open -= written;
217  processed += written;
218  }
219  }
220  return in_size;
221  }
222 
223  operator bool() override { return active; }
224 
225  protected:
226  Print *p_print = nullptr;
227  OpusSettings cfg;
228  OpusDecoder *dec;
229  bool active = false;
230  Vector<uint8_t> outbuf{0};
231  Vector<uint8_t> decbuf{0};
232  const uint32_t valid_rates[5] = {8000, 12000, 16000, 24000, 48000};
233 
234  bool isValidRate(int rate){
235  for (auto &valid : valid_rates){
236  if (valid==rate) return true;
237  }
238  return false;
239  }
240 };
241 
250  public:
251  // Empty Constructor - the output stream must be provided with begin()
252  OpusAudioEncoder() = default;
253 
254  // Constructor providing the output stream
255  OpusAudioEncoder(Print &out) { setOutput(out); }
256 
258  void setOutput(Print &out_stream) override { p_print = &out_stream; }
259 
261  const char *mime() override { return "audio/opus"; }
262 
264  void setAudioInfo(AudioInfo from) override {
266  cfg.sample_rate = from.sample_rate;
267  cfg.channels = from.channels;
268  cfg.bits_per_sample = from.bits_per_sample;
269  }
270 
272  bool begin() override {
273  int err;
274  int size = getFrameSizeSamples(cfg.sample_rate) * 2;
275  frame.resize(size);
276  assert(frame.data() != nullptr);
277  enc = opus_encoder_create(cfg.sample_rate, cfg.channels, cfg.application, &err);
278  if (err != OPUS_OK) {
279  LOGE("opus_encoder_create: %s for sample_rate: %d, channels:%d",
280  opus_strerror(err), cfg.sample_rate, cfg.channels);
281  return false;
282  }
283  is_open = settings();
284  return true;
285  }
286 
288  OpusEncoderSettings &config() { return cfg; }
289 
290  OpusEncoderSettings &defaultConfig() { return cfg; }
291 
292  bool begin(OpusEncoderSettings settings) {
293  cfg = settings;
294  return begin();
295  }
296 
298  void end() override {
299  // flush buffered data
300  encodeFrame();
301  // release memory
302  opus_encoder_destroy(enc);
303  is_open = false;
304  }
305 
307  size_t write(const void *in_ptr, size_t in_size) override {
308  if (!is_open || p_print == nullptr) return 0;
309  LOGD("OpusAudioEncoder::write: %d", (int)in_size);
310 
311  // fill frame
312  uint8_t *p_byte = (uint8_t *)in_ptr;
313  for (int j = 0; j < in_size; j++) {
314  encodeByte(p_byte[j]);
315  }
316  return in_size;
317  }
318 
319  operator bool() override { return is_open; }
320 
321  bool isOpen() { return is_open; }
322 
323  protected:
324  Print *p_print = nullptr;
325  OpusEncoder *enc = nullptr;
326  OpusEncoderSettings cfg;
327  bool is_open = false;
328  Vector<uint8_t> frame{0};
329  int frame_pos = 0;
330 
331  void encodeByte(uint8_t data) {
332  // add byte to frame
333  frame[frame_pos++] = data;
334 
335  // if frame is complete -> encode
336  if (frame_pos >= frame.size()) {
337  encodeFrame();
338  frame_pos = 0;
339  }
340  }
341 
342  void encodeFrame() {
343  if (frame.size() > 0) {
344  // allocate temp buffer on stack
345  int packet_len = OPUS_ENC_MAX_BUFFER_SIZE > 0 ? OPUS_ENC_MAX_BUFFER_SIZE : 512;
346  uint8_t packet[packet_len];
347 
348  int frames = frame.size() / cfg.channels / sizeof(int16_t);
349  LOGD("opus_encode - frame_size: %d", frames);
350  int len = opus_encode(enc, (opus_int16 *)frame.data(), frames,
351  packet, packet_len);
352  if (len < 0) {
353  LOGE("opus_encode: %s", opus_strerror(len));
354  } else if (len > 0) {
355  LOGD("opus-encode: %d", len);
356  int eff = p_print->write(packet, len);
357  if (eff!=len){
358  LOGE("encodeFrame data lost: %d->%d", len, eff);
359  }
360  }
361  }
362  }
363 
365  int getFrameSizeSamples(int sampling_rate) {
366  switch (cfg.frame_sizes_ms_x2) {
367  case OPUS_FRAMESIZE_2_5_MS:
368  return sampling_rate / 400;
369  case OPUS_FRAMESIZE_5_MS:
370  return sampling_rate / 200;
371  case OPUS_FRAMESIZE_10_MS:
372  return sampling_rate / 100;
373  case OPUS_FRAMESIZE_20_MS:
374  return sampling_rate / 50;
375  case OPUS_FRAMESIZE_40_MS:
376  return sampling_rate / 25;
377  case OPUS_FRAMESIZE_60_MS:
378  return 3 * sampling_rate / 50;
379  case OPUS_FRAMESIZE_80_MS:
380  return 4 * sampling_rate / 50;
381  case OPUS_FRAMESIZE_100_MS:
382  return 5 * sampling_rate / 50;
383  case OPUS_FRAMESIZE_120_MS:
384  return 6 * sampling_rate / 50;
385  }
386  return sampling_rate / 100;
387  }
388 
389  bool settings() {
390  bool ok = true;
391  if (cfg.bitrate >= 0 &&
392  opus_encoder_ctl(enc, OPUS_SET_BITRATE(cfg.bitrate)) != OPUS_OK) {
393  LOGE("invalid bitrate: %d", cfg.bitrate);
394  ok = false;
395  }
396  if (cfg.force_channel >= 0 &&
397  opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(cfg.force_channel)) !=
398  OPUS_OK) {
399  LOGE("invalid force_channel: %d", cfg.force_channel);
400  ok = false;
401  };
402  if (cfg.vbr >= 0 &&
403  opus_encoder_ctl(enc, OPUS_SET_VBR(cfg.vbr)) != OPUS_OK) {
404  LOGE("invalid vbr: %d", cfg.vbr);
405  ok = false;
406  }
407  if (cfg.vbr_constraint >= 0 &&
408  opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cfg.vbr_constraint)) !=
409  OPUS_OK) {
410  LOGE("invalid vbr_constraint: %d", cfg.vbr_constraint);
411  ok = false;
412  }
413  if (cfg.complexity >= 0 &&
414  opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(cfg.complexity)) != OPUS_OK) {
415  LOGE("invalid complexity: %d", cfg.complexity);
416  ok = false;
417  }
418  if (cfg.max_bandwidth >= 0 &&
419  opus_encoder_ctl(enc, OPUS_SET_MAX_BANDWIDTH(cfg.max_bandwidth)) !=
420  OPUS_OK) {
421  LOGE("invalid max_bandwidth: %d", cfg.max_bandwidth);
422  ok = false;
423  }
424  if (cfg.singal >= 0 &&
425  opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.singal)) != OPUS_OK) {
426  LOGE("invalid singal: %d", cfg.singal);
427  ok = false;
428  }
429  if (cfg.inband_fec >= 0 &&
430  opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(cfg.inband_fec)) != OPUS_OK) {
431  LOGE("invalid inband_fec: %d", cfg.inband_fec);
432  ok = false;
433  }
434  if (cfg.packet_loss_perc >= 0 &&
435  opus_encoder_ctl(
436  enc, OPUS_SET_PACKET_LOSS_PERC(cfg.packet_loss_perc)) != OPUS_OK) {
437  LOGE("invalid pkt_loss: %d", cfg.packet_loss_perc);
438  ok = false;
439  }
440  if (cfg.lsb_depth >= 0 &&
441  opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(cfg.lsb_depth)) != OPUS_OK) {
442  LOGE("invalid lsb_depth: %d", cfg.lsb_depth);
443  ok = false;
444  }
445  if (cfg.prediction_disabled >= 0 &&
446  opus_encoder_ctl(enc, OPUS_SET_PREDICTION_DISABLED(
447  cfg.prediction_disabled)) != OPUS_OK) {
448  LOGE("invalid pred_disabled: %d", cfg.prediction_disabled);
449  ok = false;
450  }
451  if (cfg.use_dtx >= 0 &&
452  opus_encoder_ctl(enc, OPUS_SET_DTX(cfg.use_dtx)) != OPUS_OK) {
453  LOGE("invalid use_dtx: %d", cfg.use_dtx);
454  ok = false;
455  }
456  if (cfg.frame_sizes_ms_x2 > 0 &&
457  opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(
458  cfg.frame_sizes_ms_x2)) != OPUS_OK) {
459  LOGE("invalid frame_sizes_ms_x2: %d", cfg.frame_sizes_ms_x2);
460  ok = false;
461  }
462  return ok;
463  }
464 };
465 
466 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioEncoded.h:18
virtual void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition: AudioEncoded.h:28
Encoding of PCM data.
Definition: AudioEncoded.h:88
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition: AudioEncoded.h:97
OpusAudioDecoder: Depends on https://github.com/pschatzmann/arduino-libopus.git.
Definition: CodecOpus.h:115
OpusAudioDecoder(Print &out_stream)
Construct a new OpusDecoder object.
Definition: CodecOpus.h:127
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition: CodecOpus.h:133
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition: CodecOpus.h:188
OpusSettings & config()
Provides access to the configuration.
Definition: CodecOpus.h:138
OpusAudioDecoder()=default
Construct a new OpusDecoder object.
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: CodecOpus.h:135
OpusAudioEncoder: Dependens on https://github.com/pschatzmann/arduino-libopus.git.
Definition: CodecOpus.h:249
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition: CodecOpus.h:258
OpusEncoderSettings & config()
Provides access to the configuration.
Definition: CodecOpus.h:288
void setAudioInfo(AudioInfo from) override
We actually do nothing with this.
Definition: CodecOpus.h:264
const char * mime() override
Provides "audio/pcm".
Definition: CodecOpus.h:261
void end() override
stops the processing
Definition: CodecOpus.h:298
size_t write(const void *in_ptr, size_t in_size) override
Writes PCM data to be encoded as Opus.
Definition: CodecOpus.h:307
bool begin() override
starts the processing using the actual OpusAudioInfo
Definition: CodecOpus.h:272
int getFrameSizeSamples(int sampling_rate)
Returns the frame size in samples.
Definition: CodecOpus.h:365
Definition: NoArduino.h:58
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
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
Setting for Opus Encoder where the following values are valid: -1 indicates that the default value sh...
Definition: CodecOpus.h:68
int vbr
0, 1
Definition: CodecOpus.h:82
int vbr_constraint
0, 1
Definition: CodecOpus.h:84
int frame_sizes_ms_x2
OPUS_FRAMESIZE_2_5_MS,OPUS_FRAMESIZE_5_MS,OPUS_FRAMESIZE_10_MS,OPUS_FRAMESIZE_20_MS,...
Definition: CodecOpus.h:105
int inband_fec
0, 1
Definition: CodecOpus.h:95
OpusEncoderSettings()
Definition: CodecOpus.h:69
int use_dtx
0, 1
Definition: CodecOpus.h:103
int packet_loss_perc
0, 1, 2, 5
Definition: CodecOpus.h:97
int complexity
0 to 10
Definition: CodecOpus.h:86
int force_channel
OPUS_AUTO, OPUS_AUTO, 1, 2.
Definition: CodecOpus.h:80
int bitrate
Definition: CodecOpus.h:78
int prediction_disabled
0, 1
Definition: CodecOpus.h:101
int max_bandwidth
Definition: CodecOpus.h:91
int lsb_depth
8, 24
Definition: CodecOpus.h:99
int application
Definition: CodecOpus.h:75
int singal
OPUS_AUTO, OPUS_SIGNAL_VOICE, OPUS_SIGNAL_MUSIC.
Definition: CodecOpus.h:93
Setting for Opus Decoder.
Definition: CodecOpus.h:23
OpusSettings()
Definition: CodecOpus.h:24