arduino-audio-tools
Loading...
Searching...
No Matches
CodecOpus.h
1#pragma once
2
3#include "AudioTools/AudioCodecs/AudioCodecsBase.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
15namespace audio_tools {
16
22struct OpusSettings : public AudioInfo {
25 sample_rate = 48000;
27 channels = 2;
29 bits_per_sample = 16;
30 }
31 int max_buffer_size = OPUS_DEC_MAX_BUFFER_SIZE;
32 int max_buffer_write_size = 512;
33};
34
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;
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() = default;
127
133 OpusAudioDecoder(Print &out_stream) {
134 TRACED();
135 setOutput(out_stream);
136 }
137
139 void setOutput(Print &out_stream) override { p_print = &out_stream; }
140
141 AudioInfo audioInfo() override { return cfg; }
142
144 OpusSettings &config() { return cfg; }
145 OpusSettings &defaultConfig() { return cfg; }
146
147 bool begin(OpusSettings settings) {
148 TRACED();
150 cfg = settings;
151 notifyAudioChange(cfg);
152 return begin();
153 }
154
155 bool begin() override {
156 TRACED();
157 if (!isValidRate(cfg.sample_rate)) {
158 LOGE("Sample rate not supported: %d", cfg.sample_rate);
159 return false;
160 }
161 outbuf.resize(cfg.max_buffer_size);
162 assert(outbuf.data() != nullptr);
163
164 // int err;
165 // dec = opus_decoder_create(cfg.sample_rate, cfg.channels, &err);
166
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 outbuf.resize(0);
186 decbuf.resize(0);
187 active = false;
188 }
189
190 void setAudioInfo(AudioInfo from) override {
192 info = from;
193 cfg.sample_rate = from.sample_rate;
194 cfg.channels = from.channels;
196 }
197
199 size_t write(const uint8_t *data, size_t len) override {
200 if (!active || p_print == nullptr) return 0;
201 // decode data
202 LOGD("OpusAudioDecoder::write: %d", (int)len);
203 int in_band_forward_error_correction = 0;
204 int frame_count = cfg.max_buffer_size / cfg.channels / sizeof(opus_int16);
205 int out_samples =
206 opus_decode(dec, (uint8_t *)data, len, (opus_int16 *)outbuf.data(),
207 frame_count, in_band_forward_error_correction);
208 if (out_samples < 0) {
209 LOGW("opus-decode: %s", opus_strerror(out_samples));
210 } else if (out_samples > 0) {
211 // write data to final destination
212 int out_bytes = out_samples * cfg.channels * sizeof(int16_t);
213 LOGD("opus-decode: %d", out_bytes);
214 int open = out_bytes;
215 int processed = 0;
216 while (open > 0) {
217 int to_write = std::min(open, cfg.max_buffer_write_size);
218 int written = p_print->write(outbuf.data() + processed, to_write);
219 open -= written;
220 processed += written;
221 }
222 }
223 return len;
224 }
225
226 operator bool() override { return active; }
227
228 protected:
229 Print *p_print = nullptr;
230 OpusDecoder *dec = nullptr;
231 OpusSettings cfg;
232 bool active = false;
233 Vector<uint8_t> outbuf{0};
234 Vector<uint8_t> decbuf{0};
235 const uint32_t valid_rates[5] = {8000, 12000, 16000, 24000, 48000};
236
237 bool isValidRate(int rate) {
238 for (auto &valid : valid_rates) {
239 if (valid == rate) return true;
240 }
241 return false;
242 }
243};
244
256 public:
257 // Empty Constructor - the output stream must be provided with begin()
258 OpusAudioEncoder() = default;
259
260 // Constructor providing the output stream
261 OpusAudioEncoder(Print &out) { setOutput(out); }
262
264 void setOutput(Print &out_stream) override { p_print = &out_stream; }
265
267 const char *mime() override { return "audio/opus"; }
268
270 void setAudioInfo(AudioInfo from) override {
272 cfg.sample_rate = from.sample_rate;
273 cfg.channels = from.channels;
274 cfg.bits_per_sample = from.bits_per_sample;
275 }
276
278 bool begin() override {
279 int err;
280 int size = getFrameSizeSamples(cfg.sample_rate) * 2;
281 frame.resize(size);
282 assert(frame.data() != nullptr);
283 enc = opus_encoder_create(cfg.sample_rate, cfg.channels, cfg.application,
284 &err);
285 if (err != OPUS_OK) {
286 LOGE("opus_encoder_create: %s for sample_rate: %d, channels:%d",
287 opus_strerror(err), cfg.sample_rate, cfg.channels);
288 return false;
289 }
290 is_open = settings();
291 return true;
292 }
293
295 OpusEncoderSettings &config() { return cfg; }
296
297 OpusEncoderSettings &defaultConfig() { return cfg; }
298
299 bool begin(OpusEncoderSettings settings) {
300 cfg = settings;
301 return begin();
302 }
303
305 void end() override {
306 // flush buffered data
307 encodeFrame();
308 // release memory
309 opus_encoder_destroy(enc);
310 is_open = false;
311 }
312
314 size_t write(const uint8_t *data, size_t len) override {
315 if (!is_open || p_print == nullptr) return 0;
316 LOGD("OpusAudioEncoder::write: %d", (int)len);
317
318 // fill frame
319 for (int j = 0; j < len; j++) {
320 encodeByte(data[j]);
321 }
322 return len;
323 }
324
325 operator bool() override { return is_open; }
326
327 bool isOpen() { return is_open; }
328
329 protected:
330 Print *p_print = nullptr;
331 OpusEncoder *enc = nullptr;
332 OpusEncoderSettings cfg;
333 bool is_open = false;
334 Vector<uint8_t> frame{0};
335 int frame_pos = 0;
336
337 void encodeByte(uint8_t data) {
338 // add byte to frame
339 frame[frame_pos++] = data;
340
341 // if frame is complete -> encode
342 if (frame_pos >= frame.size()) {
343 encodeFrame();
344 frame_pos = 0;
345 }
346 }
347
348 void encodeFrame() {
349 if (frame.size() > 0) {
350 // allocate temp buffer on stack
351 int packet_len =
352 OPUS_ENC_MAX_BUFFER_SIZE > 0 ? OPUS_ENC_MAX_BUFFER_SIZE : 512;
353 uint8_t packet[packet_len];
354
355 int frames = frame.size() / cfg.channels / sizeof(int16_t);
356 LOGD("opus_encode - frame_size: %d", frames);
357 int len = opus_encode(enc, (opus_int16 *)frame.data(), frames, packet,
358 packet_len);
359 if (len < 0) {
360 LOGE("opus_encode: %s", opus_strerror(len));
361 } else if (len > 0) {
362 LOGD("opus-encode: %d", len);
363 int eff = p_print->write(packet, len);
364 if (eff != len) {
365 LOGE("encodeFrame data lost: %d->%d", len, eff);
366 }
367 }
368 }
369 }
370
372 int getFrameSizeSamples(int sampling_rate) {
373 switch (cfg.frame_sizes_ms_x2) {
374 case OPUS_FRAMESIZE_2_5_MS:
375 return sampling_rate / 400;
376 case OPUS_FRAMESIZE_5_MS:
377 return sampling_rate / 200;
378 case OPUS_FRAMESIZE_10_MS:
379 return sampling_rate / 100;
380 case OPUS_FRAMESIZE_20_MS:
381 return sampling_rate / 50;
382 case OPUS_FRAMESIZE_40_MS:
383 return sampling_rate / 25;
384 case OPUS_FRAMESIZE_60_MS:
385 return 3 * sampling_rate / 50;
386 case OPUS_FRAMESIZE_80_MS:
387 return 4 * sampling_rate / 50;
388 case OPUS_FRAMESIZE_100_MS:
389 return 5 * sampling_rate / 50;
390 case OPUS_FRAMESIZE_120_MS:
391 return 6 * sampling_rate / 50;
392 }
393 return sampling_rate / 100;
394 }
395
396 bool settings() {
397 bool ok = true;
398 if (cfg.bitrate >= 0 &&
399 opus_encoder_ctl(enc, OPUS_SET_BITRATE(cfg.bitrate)) != OPUS_OK) {
400 LOGE("invalid bitrate: %d", cfg.bitrate);
401 ok = false;
402 }
403 if (cfg.force_channel >= 0 &&
404 opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(cfg.force_channel)) !=
405 OPUS_OK) {
406 LOGE("invalid force_channel: %d", cfg.force_channel);
407 ok = false;
408 };
409 if (cfg.vbr >= 0 &&
410 opus_encoder_ctl(enc, OPUS_SET_VBR(cfg.vbr)) != OPUS_OK) {
411 LOGE("invalid vbr: %d", cfg.vbr);
412 ok = false;
413 }
414 if (cfg.vbr_constraint >= 0 &&
415 opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cfg.vbr_constraint)) !=
416 OPUS_OK) {
417 LOGE("invalid vbr_constraint: %d", cfg.vbr_constraint);
418 ok = false;
419 }
420 if (cfg.complexity >= 0 &&
421 opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(cfg.complexity)) != OPUS_OK) {
422 LOGE("invalid complexity: %d", cfg.complexity);
423 ok = false;
424 }
425 if (cfg.max_bandwidth >= 0 &&
426 opus_encoder_ctl(enc, OPUS_SET_MAX_BANDWIDTH(cfg.max_bandwidth)) !=
427 OPUS_OK) {
428 LOGE("invalid max_bandwidth: %d", cfg.max_bandwidth);
429 ok = false;
430 }
431 if (cfg.signal >= 0 &&
432 opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.signal)) != OPUS_OK) {
433 LOGE("invalid signal: %d", cfg.signal);
434 ok = false;
435 }
436 if (cfg.inband_fec >= 0 &&
437 opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(cfg.inband_fec)) != OPUS_OK) {
438 LOGE("invalid inband_fec: %d", cfg.inband_fec);
439 ok = false;
440 }
441 if (cfg.packet_loss_perc >= 0 &&
442 opus_encoder_ctl(
443 enc, OPUS_SET_PACKET_LOSS_PERC(cfg.packet_loss_perc)) != OPUS_OK) {
444 LOGE("invalid pkt_loss: %d", cfg.packet_loss_perc);
445 ok = false;
446 }
447 if (cfg.lsb_depth >= 0 &&
448 opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(cfg.lsb_depth)) != OPUS_OK) {
449 LOGE("invalid lsb_depth: %d", cfg.lsb_depth);
450 ok = false;
451 }
452 if (cfg.prediction_disabled >= 0 &&
453 opus_encoder_ctl(enc, OPUS_SET_PREDICTION_DISABLED(
454 cfg.prediction_disabled)) != OPUS_OK) {
455 LOGE("invalid pred_disabled: %d", cfg.prediction_disabled);
456 ok = false;
457 }
458 if (cfg.use_dtx >= 0 &&
459 opus_encoder_ctl(enc, OPUS_SET_DTX(cfg.use_dtx)) != OPUS_OK) {
460 LOGE("invalid use_dtx: %d", cfg.use_dtx);
461 ok = false;
462 }
463 if (cfg.frame_sizes_ms_x2 > 0 &&
464 opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(
465 cfg.frame_sizes_ms_x2)) != OPUS_OK) {
466 LOGE("invalid frame_sizes_ms_x2: %d", cfg.frame_sizes_ms_x2);
467 ok = false;
468 }
469 return ok;
470 }
471};
472
473} // namespace audio_tools
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition AudioCodecsBase.h:28
Encoding of PCM data.
Definition AudioCodecsBase.h:96
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:105
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
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecOpus.h:139
void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition CodecOpus.h:190
size_t write(const uint8_t *data, size_t len) override
write one full opus frame
Definition CodecOpus.h:199
OpusSettings & config()
Provides access to the configuration.
Definition CodecOpus.h:144
OpusAudioDecoder()=default
Construct a new OpusDecoder object.
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition CodecOpus.h:141
Encode for Opus audio.
Definition CodecOpus.h:255
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecOpus.h:264
void setAudioInfo(AudioInfo from) override
We actually do nothing with this.
Definition CodecOpus.h:270
void end() override
stops the processing
Definition CodecOpus.h:305
size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as Opus.
Definition CodecOpus.h:314
OpusEncoderSettings & config()
Provides access to the configuration.
Definition CodecOpus.h:295
const char * mime() override
Provides "audio/pcm".
Definition CodecOpus.h:267
bool begin() override
starts the processing using the actual OpusAudioInfo
Definition CodecOpus.h:278
int getFrameSizeSamples(int sampling_rate)
Returns the frame size in samples.
Definition CodecOpus.h:372
Definition NoArduino.h:62
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
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
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