arduino-audio-tools
AudioIO.h
1 #pragma once
2 #include "AudioTools/AudioOutput.h"
3 #include "AudioTools/AudioStreams.h"
4 
5 namespace audio_tools {
13 template <class T>
15  public:
20  void begin(T *transform, Stream *source) {
21  TRACED();
22  active = true;
23  p_stream = source;
24  p_transform = transform;
25  if (transform == nullptr) {
26  LOGE("transform is NULL");
27  active = false;
28  }
29  if (p_stream == nullptr) {
30  LOGE("p_stream is NULL");
31  active = false;
32  }
33  }
34 
35  size_t readBytes(uint8_t *data, size_t byteCount) {
36  LOGD("TransformationReader::readBytes: %d", (int)byteCount);
37  if (!active) {
38  LOGE("inactive");
39  return 0;
40  }
41  if (p_stream == nullptr) {
42  LOGE("p_stream is NULL");
43  return 0;
44  }
45 
46  // we read half the necessary bytes
47  if (buffer.size() == 0) {
48  int size = (0.5 / p_transform->getByteFactor() * byteCount);
49  // process full samples/frames
50  size = size / 4 * 4;
51  LOGI("read size: %d", size);
52  buffer.resize(size);
53  }
54 
55  if (rb.size() == 0) {
56  // make sure that the ring buffer is big enough
57  int rb_size = byteCount * byte_count_factor;
58  LOGI("buffer size: %d", rb_size);
59  rb.resize(rb_size);
60  result_queue.begin();
61  }
62 
63  if (result_queue.available() < byteCount) {
64  Print *tmp = setupOutput();
65  while (result_queue.available() < byteCount) {
66  int read_eff = p_stream->readBytes(buffer.data(), buffer.size());
67  if (read_eff > 0) {
68  if(read_eff != buffer.size()) LOGD("readBytes %d -> %d", buffer.size(), read_eff);
69  int write_eff = p_transform->write(buffer.data(), read_eff);
70  if (write_eff != read_eff) LOGE("write %d -> %d", read_eff, write_eff);
71  } else {
72  delay(5);
73  }
74  }
75  restoreOutput(tmp);
76  }
77 
78  int result_len = min((int)byteCount, result_queue.available());
79  result_len = result_queue.readBytes(data, result_len);
80  LOGD("TransformationReader::readBytes: %d -> %d", (int)byteCount,
81  result_len);
82 
83  return result_len;
84  }
85 
86  void end() {
87  rb.resize(0);
88  buffer.resize(0);
89  }
90 
91  void setByteCountFactor(int f){
92  byte_count_factor = f;
93  }
94 
95 
96  protected:
97  RingBuffer<uint8_t> rb{0};
98  QueueStream<uint8_t> result_queue{rb}; //
99  Stream *p_stream = nullptr;
100  Vector<uint8_t> buffer{0}; // we allocate memory only when needed
101  T *p_transform = nullptr;
102  bool active = false;
103  int byte_count_factor = 3;
104 
109  Print *result = p_transform->getPrint();
110  p_transform->setOutput((Print&)result_queue);
111 
112  return result;
113  }
116  void restoreOutput(Print *out) {
117  if (out) p_transform->setOutput(*out);
118  }
119 };
120 
128  public:
129  virtual void setStream(Stream &stream) override {
130  TRACED();
131  p_stream = &stream;
132  p_print = &stream;
133  }
134 
135  virtual void setStream(AudioStream &stream) {
136  TRACED();
137  p_stream = &stream;
138  p_print = &stream;
139  addNotifyAudioChange(stream);
140  }
141 
142  virtual void setOutput(AudioOutput &print) {
143  TRACED();
144  p_print = &print;
145  addNotifyAudioChange(print);
146  }
147 
148  virtual void setOutput(Print &print) override {
149  TRACED();
150  p_print = &print;
151  }
152 
153  virtual Print *getPrint() { return p_print; }
154 
155  virtual Stream *getStream() { return p_stream; }
156 
157  size_t readBytes(uint8_t *data, size_t size) override {
158  LOGD("ReformatBaseStream::readBytes: %d", (int)size);
159  return reader.readBytes(data, size);
160  }
161 
162  int available() override {
163  return DEFAULT_BUFFER_SIZE; // reader.availableForWrite();
164  }
165 
166  int availableForWrite() override {
167  return DEFAULT_BUFFER_SIZE; // reader.availableForWrite();
168  }
169 
170  virtual float getByteFactor() = 0;
171 
172  void end() override {
173  TRACED();
174  AudioStream::end();
175  reader.end();
176  }
177 
178  protected:
179  TransformationReader<ReformatBaseStream> reader;
180  Stream *p_stream = nullptr;
181  Print *p_print = nullptr;
182 
183  void setupReader() {
184  if(getStream() != nullptr) {
185  reader.begin(this, getStream());
186  }
187  }
188 };
189 
194 class AudioOutputAdapter : public AudioOutput {};
195 
201 public:
202  AdapterPrintToAudioOutput(Print &print) { p_print = &print; }
203  void setAudioInfo(AudioInfo info) {}
204  size_t write(const uint8_t *buffer, size_t size) {
205  return p_print->write(buffer, size);
206  }
208  virtual bool isDeletable() { return true; }
209 
210 protected:
211  Print *p_print = nullptr;
212 };
213 
219 public:
221 
222  AdapterAudioStreamToAudioOutput(AudioStream &stream) { setStream(stream); }
223 
224  void setStream(AudioStream &stream) { p_stream = &stream; }
225 
226  void setAudioInfo(AudioInfo info) override { p_stream->setAudioInfo(info); }
227 
228  size_t write(const uint8_t *buffer, size_t size) override {
229  return p_stream->write(buffer, size);
230  }
231 
232  int availableForWrite() override {
233  return p_stream->availableForWrite();
234  }
235 
236  bool begin() override {
237  return p_stream->begin();
238  }
239 
240  void end() override {
241  p_stream->end();
242  }
243 
245  virtual bool isDeletable() { return true; }
246 
247 protected:
248  AudioStream *p_stream = nullptr;
249 };
250 
256 public:
258 
259  AdapterAudioOutputToAudioStream(AudioOutput &stream) { setOutput(stream); }
260 
261  void setOutput(AudioOutput &stream) { p_stream = &stream; }
262 
263  void setAudioInfo(AudioInfo info) { p_stream->setAudioInfo(info); }
264  size_t write(const uint8_t *buffer, size_t size) {
265  return p_stream->write(buffer, size);
266  }
267 
269  virtual bool isDeletable() { return true; }
270 
271 protected:
272  AudioOutput *p_stream = nullptr;
273 };
274 
281 class MultiOutput : public ModifyingOutput {
282 public:
284  MultiOutput() = default;
285 
286  MultiOutput(Print &out) { add(out); }
287 
289  MultiOutput(AudioOutput &out) { add(out); }
290 
291  MultiOutput(AudioStream &out) { add(out); }
292 
295  add(out1);
296  add(out2);
297  }
298 
301  add(out1);
302  add(out2);
303  }
304 
307  MultiOutput(Print &out1, Print &out2) {
308  add(out1);
309  add(out2);
310  }
311 
312  virtual ~MultiOutput() {
313  for (int j = 0; j < vector.size(); j++) {
314  if (vector[j]->isDeletable()) {
315  delete vector[j];
316  }
317  }
318  }
319 
321  void add(AudioOutput &out) { vector.push_back(&out); }
322 
324  void add(AudioStream &stream) {
327  vector.push_back(out);
328  }
329 
330  void add(Print &print) {
332  vector.push_back(out);
333  }
334 
335  void flush() {
336  for (int j = 0; j < vector.size(); j++) {
337  vector[j]->flush();
338  }
339  }
340 
341  void setAudioInfo(AudioInfo info) {
342  for (int j = 0; j < vector.size(); j++) {
343  vector[j]->setAudioInfo(info);
344  }
345  }
346 
347  size_t write(const uint8_t *buffer, size_t size) {
348  for (int j = 0; j < vector.size(); j++) {
349  int open = size;
350  int start = 0;
351  while (open > 0) {
352  int written = vector[j]->write(buffer + start, open);
353  open -= written;
354  start += written;
355  }
356  }
357  return size;
358  }
359 
360  size_t write(uint8_t ch) {
361  for (int j = 0; j < vector.size(); j++) {
362  int open = 1;
363  while (open > 0) {
364  open -= vector[j]->write(ch);
365  }
366  }
367  return 1;
368  }
369 
370 protected:
371  Vector<AudioOutput *> vector;
373  void setOutput(Print& out) {
374  add(out);
375  }
376 
377 };
378 
388 class TimedStream : public ModifyingStream {
389 public:
390  TimedStream() = default;
391 
392  TimedStream(AudioStream &io, long startSeconds = 0, long endSeconds = -1) {
393  p_stream = &io;
394  p_print = &io;
395  p_info = &io;
396  setStartSec(startSeconds);
397  setEndSec(endSeconds);
398  }
399 
400  TimedStream(AudioOutput &o, long startSeconds = 0, long endSeconds = -1) {
401  p_print = &o;
402  p_info = &o;
403  setStartSec(startSeconds);
404  setEndSec(endSeconds);
405  }
406 
409  void setStartSec(uint32_t startSeconds) { start_ms = startSeconds*1000; calculateByteLimits(); }
410 
412  void setStartMs(uint32_t ms) { start_ms = ms; calculateByteLimits(); }
413 
416  void setEndSec(uint32_t endSeconds) { end_ms = endSeconds*1000; calculateByteLimits(); }
417 
419  void setEndMs(uint32_t ms) { end_ms = ms; calculateByteLimits(); }
420 
422  bool isPlaying() {
423  if (current_bytes < start_bytes)
424  return false;
425  if (end_bytes > 0 && current_bytes > end_bytes)
426  return false;
427  return true;
428  }
429 
431  bool isActive() {
432  return (current_bytes < end_bytes && current_bytes > start_bytes);
433  }
434 
435  bool begin(AudioInfo info) {
436  setAudioInfo(info);
437  return begin();
438  }
439 
440  bool begin() override {
441  calculateByteLimits();
442  current_bytes = 0;
443  return true;
444  }
445 
446  operator bool() { return isActive(); }
447 
451  size_t readBytes(uint8_t *buffer, size_t length) override {
452  // if reading is not supported we stop
453  if (p_stream == nullptr)
454  return 0;
455  // if we are past the end we stop
456  if (!isActive())
457  return 0;
458  // read the data now
459  size_t result = 0;
460  do {
461  result = p_stream->readBytes(buffer, length);
462  current_bytes += length;
463  // ignore data before start time
464  } while (result > 0 && current_bytes < start_bytes);
465  return isPlaying() ? result : 0;
466  }
467 
469  size_t write(const uint8_t *buffer, size_t length) override {
470  current_bytes += length;
471  return isPlaying() ? p_print->write(buffer, length) : length;
472  }
473 
475  int available() override {
476  if (p_stream == nullptr)
477  return 0;
478  return isActive() ? p_stream->available() : 0;
479  }
480 
482  void setAudioInfo(AudioInfo info) override {
484  if (p_info) p_info->setAudioInfo(info);
485  calculateByteLimits();
486  }
487 
488  int availableForWrite() override { return p_print->availableForWrite(); }
489 
492  void setCompressionRatio(float ratio) { compression_ratio = ratio; }
493 
496  return info.sample_rate * info.channels * info.bits_per_sample / 8;
497  }
498 
499  void setOutput(Print &out){
500  p_print = &out;
501  }
502 
503  void setStream(Stream &stream){
504  p_print = &stream;
505  p_stream = &stream;
506  }
507 
508  void setOutput(AudioOutput &out){
509  p_print = &out;
510  p_info = &out;
511  }
512 
513  void setStream(AudioOutput &out){
514  p_print = &out;
515  p_info = &out;
516  }
517 
518  void setStream(AudioStream &stream){
519  p_print = &stream;
520  p_stream = &stream;
521  p_info = &stream;
522  }
523 
524 protected:
525  Stream *p_stream = nullptr;
526  Print *p_print = nullptr;
527  AudioInfoSupport *p_info = nullptr;
528  uint32_t start_ms = 0;
529  uint32_t end_ms = UINT32_MAX;
530  uint32_t start_bytes = 0;
531  uint32_t end_bytes = UINT32_MAX;
532  uint32_t current_bytes = 0;
533  float compression_ratio = 1.0;
534 
535  void calculateByteLimits() {
536  float bytes_per_second = bytesPerSecond();
537  if (bytes_per_second > 0) {
538  start_bytes = bytes_per_second * start_ms / compression_ratio / 1000;
539  end_bytes = bytes_per_second * end_ms / compression_ratio / 1000;
540  } else {
541  LOGE("AudioInfo not defined");
542  }
543  }
544 };
545 
556 public:
557  ChannelsSelectOutput() = default;
558 
559  bool begin(AudioInfo info) {
560  setAudioInfo(info);
561  return begin();
562  }
563 
564  bool begin() {
565  AudioOutput::begin();
566  // make sure that selected channels are valid
567  for (auto &out : out_channels) {
568  for (auto &ch : out.channels) {
569  if (ch > cfg.channels - 1) {
570  LOGE("Channel '%d' not valid for max %d channels", ch, cfg.channels);
571  return false;
572  }
573  }
574  }
575  return true;
576  }
577 
580  void addOutput(AudioOutput &out, uint16_t channel) {
581  Vector<uint16_t> channels;
582  channels.push_back(channel);
584  def.channels = channels;
585  def.p_out = &out;
586  def.p_audio_info = &out;
587  out_channels.push_back(def);
588  }
589 
592  void addOutput(AudioStream &out, uint16_t channel) {
593  Vector<uint16_t> channels;
594  channels.push_back(channel);
596  def.channels = channels;
597  def.p_out = &out;
598  def.p_audio_info = &out;
599  out_channels.push_back(def);
600  }
601 
604  void addOutput(Print &out, uint16_t channel) {
605  Vector<uint16_t> channels;
606  channels.push_back(channel);
608  def.channels = channels;
609  def.p_out = &out;
610  out_channels.push_back(def);
611  }
612 
615  void addOutput(Print &out, uint16_t left, uint16_t right) {
616  Vector<uint16_t> channels;
617  channels.push_back(left);
618  channels.push_back(right);
620  def.channels = channels;
621  def.p_out = &out;
622  out_channels.push_back(def);
623  }
624 
627  void addOutput(AudioOutput &out, uint16_t left, uint16_t right) {
628  Vector<uint16_t> channels;
629  channels.push_back(left);
630  channels.push_back(right);
632  def.channels = channels;
633  def.p_out = &out;
634  def.p_audio_info = &out;
635  out_channels.push_back(def);
636  }
637 
640  void addOutput(AudioStream &out, uint16_t left, uint16_t right) {
641  Vector<uint16_t> channels;
642  channels.push_back(left);
643  channels.push_back(right);
645  def.channels = channels;
646  def.p_out = &out;
647  def.p_audio_info = &out;
648  out_channels.push_back(def);
649  }
650 
651 
652  size_t write(const uint8_t *buffer, size_t size) override {
653  switch(cfg.bits_per_sample){
654  case 16:
655  return writeT<int16_t>(buffer, size);
656  case 24:
657  return writeT<int24_t>(buffer, size);
658  case 32:
659  return writeT<int32_t>(buffer, size);
660  default:
661  return 0;
662  }
663  }
664 
665  void setAudioInfo(AudioInfo ai) override {
666  notifyAudioChange(ai);
667  for (auto &info : out_channels){
668  auto p_notify = info.p_audio_info;
669  if (p_notify != nullptr){
670  AudioInfo result{ai};
671  result.channels = info.channels.size();
672  p_notify->setAudioInfo(result);
673  }
674  }
675  }
676 
677 
678 protected:
680  Print *p_out = nullptr;
681  AudioInfoSupport *p_audio_info = nullptr;
682  Vector<uint16_t> channels{0};
683  };
684  Vector<ChannelSelectionOutputDef> out_channels{0};
685 
686  template <typename T>
687  size_t writeT(const uint8_t *buffer, size_t size) {
688  if (!is_active)
689  return 0;
690  int sample_count = size / sizeof(T);
691  int result_size = sample_count / cfg.channels;
692  T *data = (T *)buffer;
693 
694  for (int i = 0; i < sample_count; i += cfg.channels) {
695  T *frame = data + i;
696  for (auto &out : out_channels) {
697  T out_frame[out.channels.size()];
698  int ch_out = 0;
699  for (auto &ch : out.channels) {
700  // make sure we have a valid channel
701  int channel = (ch < cfg.channels) ? ch : cfg.channels - 1;
702  out_frame[ch_out++] = frame[channel];
703  }
704  // write full frame
705  size_t written = out.p_out->write((uint8_t *)&out_frame, sizeof(out_frame));
706  if (written != sizeof(out_frame)) {
707  LOGW("Could not write all samples");
708  }
709  }
710  }
711  return size;
712  }
713 
715  int getChannels(Print*out, int defaultChannels){
716  for (auto& channels_select : out_channels){
717  if (channels_select.p_out == out) return channels_select.channels.size();
718  }
719  return defaultChannels;
720  }
721 
722 };
723 
724 
725 } // namespace audio_tools
Wrapper which converts a AudioStream to a AudioOutput.
Definition: AudioIO.h:255
virtual bool isDeletable()
If true we need to release the related memory in the destructor.
Definition: AudioIO.h:269
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition: AudioIO.h:263
Wrapper which converts a AudioStream to a AudioOutput.
Definition: AudioIO.h:218
virtual bool isDeletable()
If true we need to release the related memory in the destructor.
Definition: AudioIO.h:245
void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition: AudioIO.h:226
Wrapper which converts a Print to a AudioOutput.
Definition: AudioIO.h:200
virtual bool isDeletable()
If true we need to release the related memory in the destructor.
Definition: AudioIO.h:208
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition: AudioIO.h:203
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition: AudioTypes.h:158
Supports changes to the sampling rate, bits and channels.
Definition: AudioTypes.h:136
virtual void setAudioInfo(AudioInfo info)=0
Defines the input AudioInfo.
Base class for Output Adpapters.
Definition: AudioIO.h:194
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
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: AudioStreams.h:24
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: AudioStreams.h:32
Flexible functionality to extract one or more channels from a multichannel signal....
Definition: AudioIO.h:555
void addOutput(Print &out, uint16_t left, uint16_t right)
Definition: AudioIO.h:615
void setAudioInfo(AudioInfo ai) override
Defines the input AudioInfo.
Definition: AudioIO.h:665
void addOutput(Print &out, uint16_t channel)
Definition: AudioIO.h:604
void addOutput(AudioOutput &out, uint16_t channel)
Definition: AudioIO.h:580
int getChannels(Print *out, int defaultChannels)
Determine number of channels for destination.
Definition: AudioIO.h:715
void addOutput(AudioStream &out, uint16_t channel)
Definition: AudioIO.h:592
void addOutput(AudioStream &out, uint16_t left, uint16_t right)
Definition: AudioIO.h:640
void addOutput(AudioOutput &out, uint16_t left, uint16_t right)
Definition: AudioIO.h:627
Abstract class: Objects can be put into a pipleline.
Definition: AudioOutput.h:96
Abstract class: Objects can be put into a pipleline.
Definition: AudioStreams.h:135
Replicates the output to multiple destinations.
Definition: AudioIO.h:281
void add(AudioOutput &out)
Add an additional AudioOutput output.
Definition: AudioIO.h:321
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition: AudioIO.h:341
void add(AudioStream &stream)
Add an AudioStream to the output.
Definition: AudioIO.h:324
MultiOutput(AudioStream &out1, AudioStream &out2)
Defines a MultiOutput with 2 final outputs.
Definition: AudioIO.h:300
MultiOutput(AudioOutput &out1, AudioOutput &out2)
Defines a MultiOutput with 2 final outputs.
Definition: AudioIO.h:294
MultiOutput(AudioOutput &out)
Defines a MultiOutput with a single final outputs,.
Definition: AudioIO.h:289
void setOutput(Print &out)
support for Pipleline
Definition: AudioIO.h:373
MultiOutput(Print &out1, Print &out2)
Definition: AudioIO.h:307
MultiOutput()=default
Defines a MultiOutput with no final output: Define your outputs with add()
Definition: NoArduino.h:58
virtual bool begin()
Activates the output.
Definition: BaseStream.h:247
Base class for chained converting streams.
Definition: AudioIO.h:127
virtual void setStream(Stream &stream) override
Defines/Changes the input & output.
Definition: AudioIO.h:129
virtual void setOutput(Print &print) override
Defines/Changes the output target.
Definition: AudioIO.h:148
virtual size_t size()
Returns the maximum capacity of the buffer.
Definition: Buffers.h:379
Definition: NoArduino.h:125
AudioStream class that can define a start and (an optional) stop time Usually it is used to wrap an A...
Definition: AudioIO.h:388
void setEndSec(uint32_t endSeconds)
Definition: AudioIO.h:416
void setEndMs(uint32_t ms)
Defines the (optional) end time in milliseconds.
Definition: AudioIO.h:419
void setCompressionRatio(float ratio)
Definition: AudioIO.h:492
void setStream(Stream &stream)
Defines/Changes the input & output.
Definition: AudioIO.h:503
void setStartSec(uint32_t startSeconds)
Definition: AudioIO.h:409
size_t readBytes(uint8_t *buffer, size_t length) override
Definition: AudioIO.h:451
int available() override
Provides the available bytes until the end time has reached.
Definition: AudioIO.h:475
void setStartMs(uint32_t ms)
Defines the start time in milliseconds.
Definition: AudioIO.h:412
bool isPlaying()
Returns true if we are in a valid time range and are still playing sound.
Definition: AudioIO.h:422
bool isActive()
Returns true if we are not past the end time;.
Definition: AudioIO.h:431
void setAudioInfo(AudioInfo info) override
Updates the AudioInfo in the current object and in the source or target.
Definition: AudioIO.h:482
void setOutput(Print &out)
Defines/Changes the output target.
Definition: AudioIO.h:499
size_t write(const uint8_t *buffer, size_t length) override
Plays only data for the indiated start and end time.
Definition: AudioIO.h:469
int bytesPerSecond()
Calculates the bytes per second from the AudioInfo.
Definition: AudioIO.h:495
ConverterStream Helper class which implements the readBytes with the help of write.
Definition: AudioIO.h:14
void begin(T *transform, Stream *source)
setup of the TransformationReader class
Definition: AudioIO.h:20
void restoreOutput(Print *out)
restores the original output in the converter class
Definition: AudioIO.h:116
Print * setupOutput()
Definition: AudioIO.h:108
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
void delay(uint32_t ms)
Waits for the indicated milliseconds.
Definition: Millis.h:11
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