arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
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
15
16namespace audio_tools {
17
23struct 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 }
78 int bitrate = -1;
80 int force_channel = -1;
82 int vbr = -1;
86 int complexity = -1;
91 int max_bandwidth = -1;
93 int singal = -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
116 public:
120 OpusAudioDecoder() = default;
121
128 TRACED();
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();
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;
194 }
195
196 size_t write(const uint8_t *data, size_t len) override {
197 if (!active || p_print == nullptr) return 0;
198 // decode data
199 LOGD("OpusAudioDecoder::write: %d", (int)len);
201 int frame_count = cfg.max_buffer_size / cfg.channels / sizeof(opus_int16);
203 dec, (uint8_t *)data, len, (opus_int16 *)outbuf.data(),
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 len;
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
303 is_open = false;
304 }
305
307 size_t write(const uint8_t *data, size_t len) override {
308 if (!is_open || p_print == nullptr) return 0;
309 LOGD("OpusAudioEncoder::write: %d", (int)len);
310
311 // fill frame
312 for (int j = 0; j < len; j++) {
313 encodeByte(data[j]);
314 }
315 return len;
316 }
317
318 operator bool() override { return is_open; }
319
320 bool isOpen() { return is_open; }
321
322 protected:
323 Print *p_print = nullptr;
324 OpusEncoder *enc = nullptr;
325 OpusEncoderSettings cfg;
326 bool is_open = false;
327 Vector<uint8_t> frame{0};
328 int frame_pos = 0;
329
330 void encodeByte(uint8_t data) {
331 // add byte to frame
332 frame[frame_pos++] = data;
333
334 // if frame is complete -> encode
335 if (frame_pos >= frame.size()) {
336 encodeFrame();
337 frame_pos = 0;
338 }
339 }
340
341 void encodeFrame() {
342 if (frame.size() > 0) {
343 // allocate temp buffer on stack
344 int packet_len = OPUS_ENC_MAX_BUFFER_SIZE > 0 ? OPUS_ENC_MAX_BUFFER_SIZE : 512;
345 uint8_t packet[packet_len];
346
347 int frames = frame.size() / cfg.channels / sizeof(int16_t);
348 LOGD("opus_encode - frame_size: %d", frames);
349 int len = opus_encode(enc, (opus_int16 *)frame.data(), frames,
350 packet, packet_len);
351 if (len < 0) {
352 LOGE("opus_encode: %s", opus_strerror(len));
353 } else if (len > 0) {
354 LOGD("opus-encode: %d", len);
355 int eff = p_print->write(packet, len);
356 if (eff!=len){
357 LOGE("encodeFrame data lost: %d->%d", len, eff);
358 }
359 }
360 }
361 }
362
365 switch (cfg.frame_sizes_ms_x2) {
367 return sampling_rate / 400;
369 return sampling_rate / 200;
371 return sampling_rate / 100;
373 return sampling_rate / 50;
375 return sampling_rate / 25;
377 return 3 * sampling_rate / 50;
379 return 4 * sampling_rate / 50;
381 return 5 * sampling_rate / 50;
383 return 6 * sampling_rate / 50;
384 }
385 return sampling_rate / 100;
386 }
387
388 bool settings() {
389 bool ok = true;
390 if (cfg.bitrate >= 0 &&
391 opus_encoder_ctl(enc, OPUS_SET_BITRATE(cfg.bitrate)) != OPUS_OK) {
392 LOGE("invalid bitrate: %d", cfg.bitrate);
393 ok = false;
394 }
395 if (cfg.force_channel >= 0 &&
396 opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(cfg.force_channel)) !=
397 OPUS_OK) {
398 LOGE("invalid force_channel: %d", cfg.force_channel);
399 ok = false;
400 };
401 if (cfg.vbr >= 0 &&
402 opus_encoder_ctl(enc, OPUS_SET_VBR(cfg.vbr)) != OPUS_OK) {
403 LOGE("invalid vbr: %d", cfg.vbr);
404 ok = false;
405 }
406 if (cfg.vbr_constraint >= 0 &&
407 opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cfg.vbr_constraint)) !=
408 OPUS_OK) {
409 LOGE("invalid vbr_constraint: %d", cfg.vbr_constraint);
410 ok = false;
411 }
412 if (cfg.complexity >= 0 &&
413 opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(cfg.complexity)) != OPUS_OK) {
414 LOGE("invalid complexity: %d", cfg.complexity);
415 ok = false;
416 }
417 if (cfg.max_bandwidth >= 0 &&
418 opus_encoder_ctl(enc, OPUS_SET_MAX_BANDWIDTH(cfg.max_bandwidth)) !=
419 OPUS_OK) {
420 LOGE("invalid max_bandwidth: %d", cfg.max_bandwidth);
421 ok = false;
422 }
423 if (cfg.singal >= 0 &&
424 opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.singal)) != OPUS_OK) {
425 LOGE("invalid singal: %d", cfg.singal);
426 ok = false;
427 }
428 if (cfg.inband_fec >= 0 &&
429 opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(cfg.inband_fec)) != OPUS_OK) {
430 LOGE("invalid inband_fec: %d", cfg.inband_fec);
431 ok = false;
432 }
433 if (cfg.packet_loss_perc >= 0 &&
434 opus_encoder_ctl(
435 enc, OPUS_SET_PACKET_LOSS_PERC(cfg.packet_loss_perc)) != OPUS_OK) {
436 LOGE("invalid pkt_loss: %d", cfg.packet_loss_perc);
437 ok = false;
438 }
439 if (cfg.lsb_depth >= 0 &&
440 opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(cfg.lsb_depth)) != OPUS_OK) {
441 LOGE("invalid lsb_depth: %d", cfg.lsb_depth);
442 ok = false;
443 }
444 if (cfg.prediction_disabled >= 0 &&
445 opus_encoder_ctl(enc, OPUS_SET_PREDICTION_DISABLED(
446 cfg.prediction_disabled)) != OPUS_OK) {
447 LOGE("invalid pred_disabled: %d", cfg.prediction_disabled);
448 ok = false;
449 }
450 if (cfg.use_dtx >= 0 &&
451 opus_encoder_ctl(enc, OPUS_SET_DTX(cfg.use_dtx)) != OPUS_OK) {
452 LOGE("invalid use_dtx: %d", cfg.use_dtx);
453 ok = false;
454 }
455 if (cfg.frame_sizes_ms_x2 > 0 &&
456 opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(
457 cfg.frame_sizes_ms_x2)) != OPUS_OK) {
458 LOGE("invalid frame_sizes_ms_x2: %d", cfg.frame_sizes_ms_x2);
459 ok = false;
460 }
461 return ok;
462 }
463};
464
465} // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
virtual void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition AudioCodecsBase.h:28
Encoding of PCM data.
Definition AudioCodecsBase.h:90
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:99
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
void setAudioInfo(AudioInfo from) override
We actually do nothing with this.
Definition CodecOpus.h:264
void end() override
stops the processing
Definition CodecOpus.h:298
size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as Opus.
Definition CodecOpus.h:307
OpusEncoderSettings & config()
Provides access to the configuration.
Definition CodecOpus.h:288
const char * mime() override
Provides "audio/pcm".
Definition CodecOpus.h:261
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:364
Definition NoArduino.h:62
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioConfig.h:885
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:52
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 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