arduino-audio-tools
Loading...
Searching...
No Matches
AudioOutput.h
1#pragma once
2#include "AudioToolsConfig.h"
3#include "AudioTools/CoreAudio/AudioTypes.h"
4#include "AudioTools/CoreAudio/BaseConverter.h"
5#include "AudioTools/CoreAudio/Buffers.h"
6
7namespace audio_tools {
8
9#if USE_PRINT_FLUSH
10#define PRINT_FLUSH_OVERRIDE override
11#else
12#define PRINT_FLUSH_OVERRIDE
13#endif
14
20class AudioOutput : public Print,
21 public AudioInfoSupport,
22 public AudioInfoSource {
23public:
24 virtual ~AudioOutput() = default;
25
26 virtual size_t write(const uint8_t *data, size_t len) override = 0;
27
28 virtual size_t write(uint8_t ch) override {
29 if (tmp.isFull()) {
30 flush();
31 }
32 return tmp.write(ch);
33 }
34
35 virtual int availableForWrite() override { return DEFAULT_BUFFER_SIZE; }
36
37 // removed override because some old implementation did not define this method
38 // as virtual
39 virtual void flush() PRINT_FLUSH_OVERRIDE {
40 if (tmp.available() > 0) {
41 write((const uint8_t *)tmp.address(), tmp.available());
42 }
43 }
44
45 // overwrite to do something useful
46 virtual void setAudioInfo(AudioInfo newInfo) override {
47 TRACED();
48 if (cfg != newInfo){
49 cfg = newInfo;
50 cfg.logInfo();
51 }
52 AudioInfo out = audioInfoOut();
53 if (out) notifyAudioChange(out);
54 }
55
57 virtual bool isDeletable() { return false; }
58
59 virtual AudioInfo audioInfo() override { return cfg; }
60
63 virtual void writeSilence(size_t len) {
64 int16_t zero = 0;
65 for (int j = 0; j < len / 2; j++) {
66 write((uint8_t *)&zero, 2);
67 }
68 }
69
70 virtual bool begin(AudioInfo info) {
71 setAudioInfo(info);
72 return begin();
73 }
74
75 virtual bool begin() {
76 is_active = true;
77 return true;
78 }
79 virtual void end() { is_active = false; }
80
81 virtual operator bool() { return is_active; }
82
83protected:
84 int tmpPos = 0;
85 AudioInfo cfg;
86 SingleBuffer<uint8_t> tmp{MAX_SINGLE_CHARS};
87 bool is_active = false;
88};
89
98 public:
100 virtual void setOutput(Print& out) = 0;
101};
102
103
113template <typename T> class CsvOutput : public AudioOutput {
114public:
115 CsvOutput(int buffer_size = DEFAULT_BUFFER_SIZE, bool active = true) {
116 this->is_active = active;
117 }
118
120 CsvOutput(Print &out, int channels = 2, int buffer_size = DEFAULT_BUFFER_SIZE,
121 bool active = true) {
122 this->out_ptr = &out;
123 this->is_active = active;
124 cfg.channels = channels;
125 }
126
128 void setDelimiter(const char *del) { delimiter_str = del; }
129
131 const char *delimiter() { return delimiter_str; }
132
134
137 AudioInfo info;
138 info.channels = 2;
139 info.sample_rate = 44100;
140 info.bits_per_sample = sizeof(T) * 8;
141 return info;
142 }
143
145 bool begin(AudioInfo info) override { return begin(info.channels); }
146
148 bool begin(int channels) {
149 TRACED();
150 cfg.channels = channels;
151 return begin();
152 }
153
155 bool begin() override {
156 this->is_active = true;
157 // if (out_ptr == &Serial){
158 // Serial.setTimeout(60000);
159 // }
160 return true;
161 }
162
164 virtual void setAudioInfo(AudioInfo info) override {
165 TRACEI();
166 this->is_active = true;
167 info.logInfo();
168 cfg = info;
169 };
170
172 virtual size_t write(const uint8_t *data, size_t len) override {
173 LOGD("CsvOutput::write: %d", (int)len);
174 if (!is_active) {
175 LOGE("is not active");
176 return 0;
177 }
178
179 if (len==0){
180 return 0;
181 }
182
183 if (cfg.channels == 0) {
184 LOGW("Channels not defined: using 2");
185 cfg.channels = 2;
186 }
187 size_t lenChannels = len / (sizeof(T) * cfg.channels);
188 if (lenChannels > 0) {
189 writeFrames((T *)data, lenChannels);
190 } else if (len == sizeof(T)) {
191 // if the write contains less then a frame we buffer the data
192 T *data_value = (T *)data;
193 out_ptr->print(data_value[0]);
194 channel++;
195 if (channel == cfg.channels) {
196 out_ptr->println();
197 channel = 0;
198 } else {
199 out_ptr->print(delimiter_str);
200 }
201 } else {
202 LOGE("Unsupported size: %d for channels %d and bits: %d", (int)len,
203 cfg.channels, cfg.bits_per_sample);
204 }
205#if USE_PRINT_FLUSH
206 out_ptr->flush();
207#endif
208 return len;
209 }
210
211 int availableForWrite() override { return 1024; }
212
213protected:
214 T *data_ptr;
215 Print *out_ptr = &Serial;
216 int channel = 0;
217 const char *delimiter_str = ",";
218
219 void writeFrames(T *data_ptr, int frameCount) {
220 for (size_t j = 0; j < frameCount; j++) {
221 for (int ch = 0; ch < cfg.channels; ch++) {
222 if (out_ptr != nullptr && data_ptr != nullptr) {
223 T value = *data_ptr;
224 out_ptr->print(value);
225 }
226 data_ptr++;
227 if (ch < cfg.channels - 1)
228 this->out_ptr->print(delimiter_str);
229 }
230 this->out_ptr->println();
231 }
232 }
233};
234
235
243public:
244 HexDumpOutput(int buffer_size = DEFAULT_BUFFER_SIZE, bool active = true) {
245 this->is_active = active;
246 }
247
249 HexDumpOutput(Print &out, int buffer_size = DEFAULT_BUFFER_SIZE,
250 bool active = true) {
251 this->out_ptr = &out;
252 this->is_active = active;
253 }
254
255 bool begin() override {
256 TRACED();
257 this->is_active = true;
258 pos = 0;
259 return is_active;
260 }
261
262 void flush() override {
263 out_ptr->println();
264 pos = 0;
265 }
266
267 virtual size_t write(const uint8_t *data, size_t len) override {
268 if (!is_active)
269 return 0;
270 TRACED();
271 for (size_t j = 0; j < len; j++) {
272 out_ptr->print(data[j], HEX);
273 out_ptr->print(" ");
274 pos++;
275 if (pos == 8) {
276 out_ptr->print(" - ");
277 }
278 if (pos == 16) {
279 out_ptr->println();
280 pos = 0;
281 }
282 }
283 return len;
284 }
285
286 //
287 AudioInfo defaultConfig(RxTxMode mode = TX_MODE) {
288 AudioInfo info;
289 return info;
290 }
291
292protected:
293 Print *out_ptr = &Serial;
294 int pos = 0;
295};
296
297
365template <typename T>
366class OutputMixer : public Print {
367public:
368 OutputMixer() = default;
369
370 OutputMixer(Print &finalOutput, int outputStreamCount) {
371 setOutput(finalOutput);
372 setOutputCount(outputStreamCount);
373 }
374
376 void setOutput(Print &finalOutput) { p_final_output = &finalOutput; }
377
379 void setOutputCount(int count) {
380 output_count = count;
381 buffers.resize(count);
382 for (int i = 0; i < count; i++) {
383 buffers[i] = nullptr;
384 }
385 weights.resize(count);
386 for (int i = 0; i < count; i++) {
387 weights[i] = 1.0;
388 }
389
391 }
392
395 void setWeight(int channel, float weight) {
396 if (channel < size()) {
397 weights[channel] = weight;
398 } else {
399 LOGE("Invalid channel %d - max is %d", channel, size() - 1);
400 }
402 }
403
405 bool begin(int copy_buffer_size_bytes = DEFAULT_BUFFER_SIZE) {
406 is_active = true;
407 size_bytes = copy_buffer_size_bytes;
408 stream_idx = 0;
409 allocate_buffers(size_bytes);
410 return true;
411 }
412
414 void end() {
415 total_weights = 0.0;
416 is_active = false;
417 // release memory for buffers
418 free_buffers();
419 }
420
422 int size() { return output_count; }
423
425 size_t write(uint8_t) override { return 0; }
426
429 size_t write(const uint8_t *data, size_t len) override {
430 size_t result = write(stream_idx, data, len);
431 // after writing the last stream we flush
432 if (is_auto_index) {
433 stream_idx++;
434 if (stream_idx >= output_count) {
435 flushMixer();
436 }
437 }
438 return result;
439 }
440
442 size_t write(int idx, const uint8_t *buffer_c, size_t bytes) {
443 LOGD("write idx %d: %d", idx, (int)bytes);
444 size_t result = 0;
445 BaseBuffer<T> *p_buffer = idx < output_count ? buffers[idx] : nullptr;
446 assert(p_buffer != nullptr);
447 size_t samples = bytes / sizeof(T);
448 if (p_buffer->availableForWrite() >= samples) {
449 result = p_buffer->writeArray((T *)buffer_c, samples) * sizeof(T);
450 } else {
451 LOGW("Available Buffer %d too small %d: requested: %d -> increase the "
452 "buffer size", (int) idx,
453 static_cast<int>(p_buffer->availableForWrite()*sizeof(T)), (int)bytes);
454 }
455 return result;
456 }
457
459 int availableForWrite() override {
460 return is_active ? availableForWrite(stream_idx) : 0;
461 }
462
464 int availableForWrite(int idx) {
465 BaseBuffer<T> *p_buffer = buffers[idx];
466 if (p_buffer == nullptr)
467 return 0;
468 return p_buffer->availableForWrite() * sizeof(T);
469 }
470
472 int available(int idx){
473 BaseBuffer<T> *p_buffer = buffers[idx];
474 if (p_buffer == nullptr)
475 return 0;
476 return p_buffer->available() * sizeof(T);
477 }
478
480 int availablePercent(int idx){
481 return 100.0 * available(idx) / size_bytes;
482 }
483
485 void flushMixer() {
486 LOGD("flush");
487 bool result = false;
488
489 // determine ringbuffer with mininum available data
490 size_t samples = availableSamples();
491 // sum up samples
492 if (samples > 0) {
493 result = true;
494 // mix data from ringbuffers to output
495 output.resize(samples);
496 memset(output.data(), 0, samples * sizeof(T));
497 for (int j = 0; j < output_count; j++) {
498 float weight = weights[j];
499 // sum up input samples to result samples
500 for (int i = 0; i < samples; i++) {
501 T sample = 0;;
502 buffers[j]->read(sample);
503 output[i] += weight * sample / total_weights;
504 }
505 }
506
507 // write output
508 LOGD("write to final out: %d", static_cast<int>(samples * sizeof(T)));
509 p_final_output->write((uint8_t *)output.data(), samples * sizeof(T));
510 }
511 stream_idx = 0;
512 return;
513 }
514
517 size_t samples = 0;
518 for (int j = 0; j < output_count; j++) {
519 int available_samples = buffers[j]->available();
520 if (available_samples > 0){
521 samples = MIN(size_bytes / sizeof(T), (size_t)available_samples);
522 }
523 }
524 return samples;
525 }
526
528 void resize(int size) {
529 if (size != size_bytes) {
531 }
532 size_bytes = size;
533 }
534
536 size_t writeSilence(size_t bytes) {
537 if (bytes == 0) return 0;
538 uint8_t silence[bytes];
539 memset(silence, 0, bytes);
540 return write(stream_idx, silence, bytes);
541 }
542
544 size_t writeSilence(int idx, size_t bytes){
545 if (bytes == 0) return 0;
546 uint8_t silence[bytes];
547 memset(silence, 0, bytes);
548 return write(idx, silence, bytes);
549 }
550
552 void setAutoIndex(bool flag){
553 is_auto_index = flag;
554 }
555
557 void setIndex(int idx){
558 stream_idx = idx;
559 }
560
562 void next() {
563 stream_idx++;
564 }
565
568 create_buffer_cb = cb;
569 }
570
573 return idx < output_count ? buffers[idx] : nullptr;
574 }
575
576protected:
577 Vector<BaseBuffer<T> *> buffers{0};
578 Vector<T> output{0};
579 Vector<float> weights{0};
580 Print *p_final_output = nullptr;
581 float total_weights = 0.0;
582 bool is_active = false;
583 int stream_idx = 0;
584 int size_bytes = 0;
585 int output_count = 0;
586 void *p_memory = nullptr;
587 bool is_auto_index = true;
588 BaseBuffer<T>* (*create_buffer_cb)(int size) = create_buffer;
589
592 return new RingBuffer<T>(size / sizeof(T));
593 }
594
597 total_weights = 0.0;
598 for (int j = 0; j < weights.size(); j++) {
599 total_weights += weights[j];
600 }
601 }
602
605 // allocate ringbuffers for each output
606 for (int j = 0; j < output_count; j++) {
607 if (buffers[j] != nullptr) {
608 delete buffers[j];
609 }
610 buffers[j] = create_buffer(size);
611 }
612 }
613
616 // allocate ringbuffers for each output
617 for (int j = 0; j < output_count; j++) {
618 if (buffers[j] != nullptr) {
619 delete buffers[j];
620 buffers[j] = nullptr;
621 }
622 }
623 }
624};
625
626
631class MemoryOutput : public AudioOutput {
632public:
633 MemoryOutput(uint8_t *start, int len) {
634 p_start = start;
635 p_next = start;
636 max_size = len;
637 is_active = true;
638 if (p_next == nullptr) {
639 LOGE("start must not be null");
640 }
641 }
642
643 bool begin() override {
644 is_active = true;
645 p_next = p_start;
646 pos = 0;
647 return true;
648 }
649
650 size_t write(const uint8_t *data, size_t len) override {
651 if (p_next == nullptr)
652 return 0;
653 if (pos + len <= max_size) {
654 memcpy(p_next, data, len);
655 pos += len;
656 p_next += len;
657 return len;
658 } else {
659 LOGE("Buffer too small: pos:%d, size: %d ", pos, (int)max_size);
660 return 0;
661 }
662 }
663
664 int availableForWrite() override { return max_size - pos; }
665
666 int size() { return max_size; }
667
668protected:
669 int pos = 0;
670 uint8_t *p_start = nullptr;
671 uint8_t *p_next = nullptr;
672 size_t max_size;
673};
674
675
685public:
686 ChannelSplitOutput() = default;
687
688 ChannelSplitOutput(Print &out, int channel) { addOutput(out, channel); }
689
692 void addOutput(Print &out, int channel) {
694 def.channel = channel;
695 def.p_out = &out;
696 out_channels.push_back(def);
697 }
698
699 size_t write(const uint8_t *data, size_t len) override {
700 switch(cfg.bits_per_sample){
701 case 16:
702 return writeT<int16_t>(data, len);
703 case 24:
704 return writeT<int24_t>(data, len);
705 case 32:
706 return writeT<int32_t>(data, len);
707 default:
708 return 0;
709 }
710 }
711
712protected:
714 Print *p_out = nullptr;
715 int channel;
716 };
718
719 template <typename T>
720 size_t writeT(const uint8_t *buffer, size_t size) {
721 int sample_count = size / sizeof(T);
722 int result_size = sample_count / cfg.channels;
723 T *data = (T *)buffer;
724 T result[result_size];
725
726 for (int ch = 0; ch < out_channels.size(); ch++) {
727 ChannelSelectionOutputDef &def = out_channels[ch];
728 // extract mono result
729 int i = 0;
730 for (int j = def.channel; j < sample_count; j += cfg.channels) {
731 result[i++] = data[j];
732 }
733 // write mono result
734 size_t written =
735 def.p_out->write((uint8_t *)result, result_size * sizeof(T));
736 if (written != result_size * sizeof(T)) {
737 LOGW("Could not write all samples");
738 }
739 }
740 return size;
741 }
742
743};
744
745
746} // namespace audio_tools
Supports the subscription to audio change notifications.
Definition AudioTypes.h:148
Supports changes to the sampling rate, bits and channels.
Definition AudioTypes.h:133
virtual AudioInfo audioInfoOut()
Definition AudioTypes.h:141
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
virtual bool isDeletable()
If true we need to release the related memory in the destructor.
Definition AudioOutput.h:57
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioOutput.h:46
virtual void writeSilence(size_t len)
Definition AudioOutput.h:63
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition AudioOutput.h:59
Shared functionality of all buffers.
Definition Buffers.h:22
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
Simple functionality to extract mono streams from a multichannel (e.g. stereo) signal.
Definition AudioOutput.h:684
void addOutput(Print &out, int channel)
Definition AudioOutput.h:692
Stream Wrapper which can be used to print the values as readable ASCII to the screen to be analyzed i...
Definition AudioOutput.h:113
virtual size_t write(const uint8_t *data, size_t len) override
Writes the data - formatted as CSV - to the output stream.
Definition AudioOutput.h:172
AudioInfo defaultConfig()
Provides the default configuration.
Definition AudioOutput.h:136
bool begin(int channels)
Starts the processing with the defined number of channels.
Definition AudioOutput.h:148
CsvOutput(Print &out, int channels=2, int buffer_size=DEFAULT_BUFFER_SIZE, bool active=true)
Constructor.
Definition AudioOutput.h:120
virtual void setAudioInfo(AudioInfo info) override
defines the number of channels
Definition AudioOutput.h:164
bool begin(AudioInfo info) override
Starts the processing with the defined number of channels.
Definition AudioOutput.h:145
bool begin() override
(Re)start (e.g. if channels is set in constructor)
Definition AudioOutput.h:155
void setDelimiter(const char *del)
Defines an alternative (column) delimiter. The default is ,.
Definition AudioOutput.h:128
const char * delimiter()
Provides the current column delimiter.
Definition AudioOutput.h:131
Creates a Hex Dump.
Definition AudioOutput.h:242
HexDumpOutput(Print &out, int buffer_size=DEFAULT_BUFFER_SIZE, bool active=true)
Constructor.
Definition AudioOutput.h:249
Writes to a preallocated memory.
Definition AudioOutput.h:631
Abstract class: Objects can be put into a pipleline.
Definition AudioOutput.h:97
virtual void setOutput(Print &out)=0
Defines/Changes the output target.
Mixing of multiple audio input streams into a single output stream.
Definition AudioOutput.h:366
void next()
Moves to the next mixing index.
Definition AudioOutput.h:562
size_t writeSilence(size_t bytes)
Writes silence to the current stream buffer.
Definition AudioOutput.h:536
void setIndex(int idx)
Sets the Output Stream index.
Definition AudioOutput.h:557
int availableSamples()
Returns the minimum number of samples available across all buffers.
Definition AudioOutput.h:516
size_t writeSilence(int idx, size_t bytes)
Writes silence to the specified stream buffer.
Definition AudioOutput.h:544
size_t write(int idx, const uint8_t *buffer_c, size_t bytes)
Write the data for an individual stream idx which will be mixed together.
Definition AudioOutput.h:442
size_t write(const uint8_t *data, size_t len) override
Definition AudioOutput.h:429
int available(int idx)
Provides the available bytes in the buffer.
Definition AudioOutput.h:472
BaseBuffer< T > * getBuffer(int idx)
Provides the write buffer for the indicated index.
Definition AudioOutput.h:572
void setCreateBufferCallback(BaseBuffer< T > *(*cb)(int size))
Define callback to allocate custum buffer types.
Definition AudioOutput.h:567
int availableForWrite() override
Provides the bytes available to write for the current stream buffer.
Definition AudioOutput.h:459
int availablePercent(int idx)
Provides the % fill level of the buffer for the indicated index.
Definition AudioOutput.h:480
void allocate_buffers(int size)
Allocates ring buffers for all input streams.
Definition AudioOutput.h:604
void update_total_weights()
Recalculates the total weights for normalization.
Definition AudioOutput.h:596
void free_buffers()
Releases memory for all ring buffers.
Definition AudioOutput.h:615
void flushMixer()
Force output to final destination.
Definition AudioOutput.h:485
void end()
Remove all input streams.
Definition AudioOutput.h:414
void setOutput(Print &finalOutput)
Sets the final output destination for mixed audio.
Definition AudioOutput.h:376
static BaseBuffer< T > * create_buffer(int size)
Creates a default ring buffer of the specified size.
Definition AudioOutput.h:591
void setWeight(int channel, float weight)
Definition AudioOutput.h:395
int availableForWrite(int idx)
Provides the bytes available to write for the indicated stream index.
Definition AudioOutput.h:464
void resize(int size)
Resizes the buffer to the indicated number of bytes.
Definition AudioOutput.h:528
void setAutoIndex(bool flag)
Automatically increment mixing index after each write.
Definition AudioOutput.h:552
bool begin(int copy_buffer_size_bytes=DEFAULT_BUFFER_SIZE)
Starts the processing.
Definition AudioOutput.h:405
int size()
Number of stremams to which are mixed together.
Definition AudioOutput.h:422
void setOutputCount(int count)
Sets the number of input streams to mix.
Definition AudioOutput.h:379
size_t write(uint8_t) override
Single byte write - not supported, returns 0.
Definition AudioOutput.h:425
Definition NoArduino.h:62
Implements a typed Ringbuffer.
Definition Buffers.h:336
bool write(T sample) override
write add an entry to the buffer
Definition Buffers.h:202
int available() override
provides the number of entries that are available to read
Definition Buffers.h:229
T * address() override
Provides address to beginning of the buffer.
Definition Buffers.h:277
bool isFull() override
checks if the buffer is full
Definition Buffers.h:236
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28
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