arduino-audio-tools
Loading...
Searching...
No Matches
CodecOpus.h
Go to the documentation of this file.
1#pragma once
2
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
15namespace audio_tools {
16
34
75 int application = OPUS_APPLICATION_AUDIO;
78 int bitrate = -1;
80 int force_channel = -1;
82 int vbr = -1;
86 int complexity = -1;
91 int max_bandwidth = -1;
93 int signal = -1;
95 int inband_fec = -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
122 public:
126 OpusAudioDecoder(bool releaseOnEnd = false) : release_on_end(releaseOnEnd) {}
127
133 OpusAudioDecoder(Print &out_stream) {
134 TRACED();
135 setOutput(out_stream);
136 }
137
138 const char *mime() override { return "audio/opus"; }
139
141 void setOutput(Print &out_stream) override { p_print = &out_stream; }
142
143 AudioInfo audioInfo() override { return cfg; }
144
146 OpusSettings &config() { return cfg; }
148
149 bool begin(OpusSettings settings) {
150 TRACED();
152 cfg = settings;
154 return begin();
155 }
156
157 bool begin() override {
158 TRACED();
160 LOGE("Sample rate not supported: %d", cfg.sample_rate);
161 return false;
162 }
164 assert(outbuf.data() != nullptr);
165
166 // allocate decoder
167 size_t size = opus_decoder_get_size(cfg.channels);
168 decbuf.resize(size);
169 assert(decbuf.data() != nullptr);
170 dec = (OpusDecoder *)decbuf.data();
171 int err = opus_decoder_init(dec, cfg.sample_rate, cfg.channels);
172
173 if (err != OPUS_OK) {
174 LOGE("opus_decoder_create: %s for sample_rate: %d, channels:%d",
175 opus_strerror(err), cfg.sample_rate, cfg.channels);
176 return false;
177 }
178 active = true;
179 return true;
180 }
181
182 void end() override {
183 TRACED();
184 dec = nullptr;
185 if (release_on_end) {
186 outbuf.resize(0);
187 decbuf.resize(0);
188 }
189 active = false;
190 }
191
192 void setAudioInfo(AudioInfo from) override {
194 info = from;
196 cfg.channels = from.channels;
198 }
199
201 size_t write(const uint8_t *data, size_t len) override {
202 if (!active || p_print == nullptr) return 0;
203 // decode data
204 LOGD("OpusAudioDecoder::write: %d", (int)len);
205 int in_band_forward_error_correction = 0;
206 int frame_count = cfg.max_buffer_size / cfg.channels / sizeof(opus_int16);
207 int out_samples =
208 opus_decode(dec, (uint8_t *)data, len, (opus_int16 *)outbuf.data(),
209 frame_count, in_band_forward_error_correction);
210 if (out_samples < 0) {
211 LOGW("opus-decode: %s", opus_strerror(out_samples));
212 } else if (out_samples > 0) {
213 // write data to final destination
214 int out_bytes = out_samples * cfg.channels * sizeof(int16_t);
215 LOGD("opus-decode: %d", out_bytes);
216 int open = out_bytes;
217 int processed = 0;
218 while (open > 0) {
219 int to_write = std::min(open, cfg.max_buffer_write_size);
220 int written = p_print->write(outbuf.data() + processed, to_write);
221 open -= written;
222 processed += written;
223 }
224 }
225 return len;
226 }
227
228 operator bool() override { return active; }
229
232 void setReleaseOnEnd(bool flag) { release_on_end = flag; }
233
234 protected:
235 Print *p_print = nullptr;
236 OpusDecoder *dec = nullptr;
238 bool active = false;
241 const uint32_t valid_rates[5] = {8000, 12000, 16000, 24000, 48000};
242 bool release_on_end = false;
243
244 bool isValidRate(int rate) {
245 for (auto &valid : valid_rates) {
246 if (valid == rate) return true;
247 }
248 return false;
249 }
250};
251
263 public:
264 // Empty Constructor - the output stream must be provided with begin()
265 OpusAudioEncoder() = default;
266
267 // Constructor providing the output stream
269
271 void setOutput(Print &out_stream) override { p_print = &out_stream; }
272
274 const char *mime() override { return "audio/opus"; }
275
277 void setAudioInfo(AudioInfo from) override {
280 cfg.channels = from.channels;
282 }
283
285 bool begin() override {
286 int err;
288 sizeof(int16_t);
289 frame.resize(size);
290 assert(frame.data() != nullptr);
291 enc = opus_encoder_create(cfg.sample_rate, cfg.channels, cfg.application,
292 &err);
293 if (err != OPUS_OK) {
294 LOGE("opus_encoder_create: %s for sample_rate: %d, channels:%d",
295 opus_strerror(err), cfg.sample_rate, cfg.channels);
296 return false;
297 }
298 is_open = settings();
299 return true;
300 }
301
304
306
308 cfg = settings;
309 return begin();
310 }
311
313 void end() override {
314 // Flush a partial frame only when data is pending; the remainder is
315 // padded with silence and must be trimmed by the container metadata.
316 if (frame_pos > 0) {
317 memset(frame.data() + frame_pos, 0, frame.size() - frame_pos);
318 encodeFrame();
319 frame_pos = 0;
320 }
321 // release memory
322 opus_encoder_destroy(enc);
323 enc = nullptr;
324 is_open = false;
325 }
326
328 size_t write(const uint8_t *data, size_t len) override {
329 if (!is_open || p_print == nullptr) return 0;
330 LOGD("OpusAudioEncoder::write: %d", (int)len);
331
332 // fill frame
333 for (int j = 0; j < len; j++) {
334 encodeByte(data[j]);
335 }
336 return len;
337 }
338
339 operator bool() override { return is_open; }
340
341 bool isOpen() { return is_open; }
342
344 if (enc == nullptr) return 0;
345 opus_int32 samples = 0;
346 if (opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&samples)) != OPUS_OK) {
347 return 0;
348 }
349 return samples > 0 ? samples : 0;
350 }
351
353
355 int bytesPerSample = cfg.channels * (int)sizeof(int16_t);
356 if (bytesPerSample <= 0) return 0;
357 return frame_pos / bytesPerSample;
358 }
359
360 uint16_t samplesPerFrame() { return frameSizeSamples();};
361
362
363 protected:
364 Print *p_print = nullptr;
365 OpusEncoder *enc = nullptr;
367 bool is_open = false;
369 int frame_pos = 0;
370
371 void encodeByte(uint8_t data) {
372 // add byte to frame
373 frame[frame_pos++] = data;
374
375 // if frame is complete -> encode
376 if (frame_pos >= frame.size()) {
377 encodeFrame();
378 frame_pos = 0;
379 }
380 }
381
382 void encodeFrame() {
383 if (frame.size() > 0) {
384 // allocate temp buffer on stack
385 int packet_len =
387 uint8_t packet[packet_len];
388
389 int frames = frame.size() / cfg.channels / sizeof(int16_t);
390 LOGD("opus_encode - frame_size: %d", frames);
391 int len = opus_encode(enc, (opus_int16 *)frame.data(), frames, packet,
392 packet_len);
393 if (len < 0) {
394 LOGE("opus_encode: %s", opus_strerror(len));
395 } else if (len > 0) {
396 LOGD("opus-encode: %d", len);
397 int eff = p_print->write(packet, len);
398 if (eff != len) {
399 LOGE("encodeFrame data lost: %d->%d", len, eff);
400 }
401 }
402 }
403 }
404
406 int getFrameSizeSamples(int sampling_rate) {
407 switch (cfg.frame_sizes_ms_x2) {
408 case OPUS_FRAMESIZE_2_5_MS:
409 return sampling_rate / 400;
410 case OPUS_FRAMESIZE_5_MS:
411 return sampling_rate / 200;
412 case OPUS_FRAMESIZE_10_MS:
413 return sampling_rate / 100;
414 case OPUS_FRAMESIZE_20_MS:
415 return sampling_rate / 50;
416 case OPUS_FRAMESIZE_40_MS:
417 return sampling_rate / 25;
418 case OPUS_FRAMESIZE_60_MS:
419 return 3 * sampling_rate / 50;
420 case OPUS_FRAMESIZE_80_MS:
421 return 4 * sampling_rate / 50;
422 case OPUS_FRAMESIZE_100_MS:
423 return 5 * sampling_rate / 50;
424 case OPUS_FRAMESIZE_120_MS:
425 return 6 * sampling_rate / 50;
426 }
427 return sampling_rate / 100;
428 }
429
430 bool settings() {
431 bool ok = true;
432 if (cfg.bitrate >= 0 &&
433 opus_encoder_ctl(enc, OPUS_SET_BITRATE(cfg.bitrate)) != OPUS_OK) {
434 LOGE("invalid bitrate: %d", cfg.bitrate);
435 ok = false;
436 }
437 if (cfg.force_channel >= 0 &&
438 opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(cfg.force_channel)) !=
439 OPUS_OK) {
440 LOGE("invalid force_channel: %d", cfg.force_channel);
441 ok = false;
442 };
443 if (cfg.vbr >= 0 &&
444 opus_encoder_ctl(enc, OPUS_SET_VBR(cfg.vbr)) != OPUS_OK) {
445 LOGE("invalid vbr: %d", cfg.vbr);
446 ok = false;
447 }
448 if (cfg.vbr_constraint >= 0 &&
449 opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cfg.vbr_constraint)) !=
450 OPUS_OK) {
451 LOGE("invalid vbr_constraint: %d", cfg.vbr_constraint);
452 ok = false;
453 }
454 if (cfg.complexity >= 0 &&
455 opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(cfg.complexity)) != OPUS_OK) {
456 LOGE("invalid complexity: %d", cfg.complexity);
457 ok = false;
458 }
459 if (cfg.max_bandwidth >= 0 &&
460 opus_encoder_ctl(enc, OPUS_SET_MAX_BANDWIDTH(cfg.max_bandwidth)) !=
461 OPUS_OK) {
462 LOGE("invalid max_bandwidth: %d", cfg.max_bandwidth);
463 ok = false;
464 }
465 if (cfg.signal >= 0 &&
466 opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.signal)) != OPUS_OK) {
467 LOGE("invalid signal: %d", cfg.signal);
468 ok = false;
469 }
470 if (cfg.inband_fec >= 0 &&
471 opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(cfg.inband_fec)) != OPUS_OK) {
472 LOGE("invalid inband_fec: %d", cfg.inband_fec);
473 ok = false;
474 }
475 if (cfg.packet_loss_perc >= 0 &&
476 opus_encoder_ctl(
477 enc, OPUS_SET_PACKET_LOSS_PERC(cfg.packet_loss_perc)) != OPUS_OK) {
478 LOGE("invalid pkt_loss: %d", cfg.packet_loss_perc);
479 ok = false;
480 }
481 if (cfg.lsb_depth >= 0 &&
482 opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(cfg.lsb_depth)) != OPUS_OK) {
483 LOGE("invalid lsb_depth: %d", cfg.lsb_depth);
484 ok = false;
485 }
486 if (cfg.prediction_disabled >= 0 &&
487 opus_encoder_ctl(enc, OPUS_SET_PREDICTION_DISABLED(
488 cfg.prediction_disabled)) != OPUS_OK) {
489 LOGE("invalid pred_disabled: %d", cfg.prediction_disabled);
490 ok = false;
491 }
492 if (cfg.use_dtx >= 0 &&
493 opus_encoder_ctl(enc, OPUS_SET_DTX(cfg.use_dtx)) != OPUS_OK) {
494 LOGE("invalid use_dtx: %d", cfg.use_dtx);
495 ok = false;
496 }
497 if (cfg.frame_sizes_ms_x2 > 0 &&
498 opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(
499 cfg.frame_sizes_ms_x2)) != OPUS_OK) {
500 LOGE("invalid frame_sizes_ms_x2: %d", cfg.frame_sizes_ms_x2);
501 ok = false;
502 }
503 return ok;
504 }
505};
506
507} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define OPUS_DEC_MAX_BUFFER_SIZE
Definition CodecOpus.h:12
#define OPUS_ENC_MAX_BUFFER_SIZE
Definition CodecOpus.h:8
#define assert(T)
Definition avr.h:10
Definition Arduino.h:56
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:19
AudioInfo info
Definition AudioCodecsBase.h:80
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition AudioCodecsBase.h:32
Encoding of PCM data.
Definition AudioCodecsBase.h:101
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:110
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:175
Decoder for the Opus audio format. Each Opus frame must be provided with one write() call....
Definition CodecOpus.h:121
OpusAudioDecoder(Print &out_stream)
Construct a new OpusDecoder object.
Definition CodecOpus.h:133
bool active
Definition CodecOpus.h:238
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecOpus.h:141
Vector< uint8_t > outbuf
Definition CodecOpus.h:239
bool begin(OpusSettings settings)
Definition CodecOpus.h:149
OpusAudioDecoder(bool releaseOnEnd=false)
Construct a new OpusDecoder object.
Definition CodecOpus.h:126
OpusDecoder * dec
Definition CodecOpus.h:236
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition CodecOpus.h:192
void end() override
Definition CodecOpus.h:182
Vector< uint8_t > decbuf
Definition CodecOpus.h:240
size_t write(const uint8_t *data, size_t len) override
write one full opus frame
Definition CodecOpus.h:201
OpusSettings cfg
Definition CodecOpus.h:237
const uint32_t valid_rates[5]
Definition CodecOpus.h:241
bool release_on_end
Definition CodecOpus.h:242
bool isValidRate(int rate)
Definition CodecOpus.h:244
void setReleaseOnEnd(bool flag)
Definition CodecOpus.h:232
const char * mime() override
Provides the mime type of the data that is expected by this decoder.
Definition CodecOpus.h:138
OpusSettings & config()
Provides access to the configuration.
Definition CodecOpus.h:146
bool begin() override
Definition CodecOpus.h:157
Print * p_print
Definition CodecOpus.h:235
OpusSettings & defaultConfig()
Definition CodecOpus.h:147
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition CodecOpus.h:143
Encode for Opus audio.
Definition CodecOpus.h:262
bool is_open
Definition CodecOpus.h:367
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecOpus.h:271
bool isOpen()
Definition CodecOpus.h:341
bool begin(OpusEncoderSettings settings)
Definition CodecOpus.h:307
uint16_t samplesPerFrame()
Optional rtsp function: provide samples per the frame.
Definition CodecOpus.h:360
int frameSizeSamples()
Definition CodecOpus.h:352
void setAudioInfo(AudioInfo from) override
We actually do nothing with this.
Definition CodecOpus.h:277
void end() override
stops the processing
Definition CodecOpus.h:313
OpusAudioEncoder(Print &out)
Definition CodecOpus.h:268
OpusEncoderSettings cfg
Definition CodecOpus.h:366
void encodeFrame()
Definition CodecOpus.h:382
int lookaheadSamples()
Definition CodecOpus.h:343
size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as Opus.
Definition CodecOpus.h:328
OpusEncoderSettings & config()
Provides access to the configuration.
Definition CodecOpus.h:303
int pendingSamples()
Definition CodecOpus.h:354
OpusEncoder * enc
Definition CodecOpus.h:365
bool settings()
Definition CodecOpus.h:430
const char * mime() override
Provides "audio/pcm".
Definition CodecOpus.h:274
int frame_pos
Definition CodecOpus.h:369
bool begin() override
starts the processing using the actual OpusAudioInfo
Definition CodecOpus.h:285
Print * p_print
Definition CodecOpus.h:364
Vector< uint8_t > frame
Definition CodecOpus.h:368
void encodeByte(uint8_t data)
Definition CodecOpus.h:371
int getFrameSizeSamples(int sampling_rate)
Returns the frame size in samples.
Definition CodecOpus.h:406
OpusEncoderSettings & defaultConfig()
Definition CodecOpus.h:305
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
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 signal
OPUS_AUTO, OPUS_SIGNAL_VOICE, OPUS_SIGNAL_MUSIC.
Definition CodecOpus.h:93
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
Setting for Opus Decoder.
Definition CodecOpus.h:22
OpusSettings()
Definition CodecOpus.h:23
int max_buffer_size
Definition CodecOpus.h:31
int max_buffer_write_size
Definition CodecOpus.h:32