arduino-audio-tools
AudioOutput.h
1 #pragma once
2 #include "AudioConfig.h"
3 #include "AudioTools/AudioTypes.h"
4 #include "AudioTools/BaseConverter.h"
5 #include "AudioTools/Buffers.h"
6 
7 namespace audio_tools {
8 
9 #if USE_PRINT_FLUSH
10 #define PRINT_FLUSH_OVERRIDE override
11 #else
12 #define PRINT_FLUSH_OVERRIDE
13 #endif
14 
20 class AudioOutput : public Print,
21  public AudioInfoSupport,
22  public AudioInfoSource {
23 public:
24  virtual ~AudioOutput() = default;
25 
26  virtual size_t write(const uint8_t *buffer, size_t size) 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  operator bool() { return is_active; }
81 
82 protected:
83  int tmpPos = 0;
84  AudioInfo cfg;
85  SingleBuffer<uint8_t> tmp{MAX_SINGLE_CHARS};
86  bool is_active = false;
87 };
88 
96 class ModifyingOutput : public AudioOutput {
97  public:
99  virtual void setOutput(Print& out) = 0;
100 };
101 
102 
112 template <typename T> class CsvOutput : public AudioOutput {
113 public:
114  CsvOutput(int buffer_size = DEFAULT_BUFFER_SIZE, bool active = true) {
115  this->is_active = active;
116  }
117 
119  CsvOutput(Print &out, int channels = 2, int buffer_size = DEFAULT_BUFFER_SIZE,
120  bool active = true) {
121  this->out_ptr = &out;
122  this->is_active = active;
123  cfg.channels = channels;
124  }
125 
127  void setDelimiter(const char *del) { delimiter_str = del; }
128 
130  const char *delimiter() { return delimiter_str; }
131 
132  AudioInfo defaultConfig(RxTxMode mode) { return defaultConfig(); }
133 
136  AudioInfo info;
137  info.channels = 2;
138  info.sample_rate = 44100;
139  info.bits_per_sample = sizeof(T) * 8;
140  return info;
141  }
142 
144  bool begin(AudioInfo info) override { return begin(info.channels); }
145 
147  bool begin(int channels) {
148  TRACED();
149  cfg.channels = channels;
150  return begin();
151  }
152 
154  bool begin() override {
155  this->is_active = true;
156  // if (out_ptr == &Serial){
157  // Serial.setTimeout(60000);
158  // }
159  return true;
160  }
161 
163  virtual void setAudioInfo(AudioInfo info) override {
164  TRACEI();
165  this->is_active = true;
166  info.logInfo();
167  cfg = info;
168  };
169 
171  virtual size_t write(const uint8_t *data, size_t len) override {
172  LOGD("CsvOutput::write: %d", (int)len);
173  if (!is_active) {
174  LOGE("is not active");
175  return 0;
176  }
177 
178  if (cfg.channels == 0) {
179  LOGW("Channels not defined: using 2");
180  cfg.channels = 2;
181  }
182  size_t lenChannels = len / (sizeof(T) * cfg.channels);
183  if (lenChannels > 0) {
184  writeFrames((T *)data, lenChannels);
185  } else if (len == sizeof(T)) {
186  // if the write contains less then a frame we buffer the data
187  T *data_value = (T *)data;
188  out_ptr->print(data_value[0]);
189  channel++;
190  if (channel == cfg.channels) {
191  out_ptr->println();
192  channel = 0;
193  } else {
194  out_ptr->print(delimiter_str);
195  }
196  } else {
197  LOGE("Unsupported size: %d for channels %d and bits: %d", (int)len,
198  cfg.channels, cfg.bits_per_sample);
199  }
200 #if USE_PRINT_FLUSH
201  out_ptr->flush();
202 #endif
203  return len;
204  }
205 
206  int availableForWrite() override { return 1024; }
207 
208 protected:
209  T *data_ptr;
210  Print *out_ptr = &Serial;
211  int channel = 0;
212  const char *delimiter_str = ",";
213 
214  void writeFrames(T *data_ptr, int frameCount) {
215  for (size_t j = 0; j < frameCount; j++) {
216  for (int ch = 0; ch < cfg.channels; ch++) {
217  if (out_ptr != nullptr && data_ptr != nullptr) {
218  int value = *data_ptr;
219  out_ptr->print(value);
220  }
221  data_ptr++;
222  if (ch < cfg.channels - 1)
223  this->out_ptr->print(delimiter_str);
224  }
225  this->out_ptr->println();
226  }
227  }
228 };
229 
230 // legacy name
231 template <typename T> using CsvStream = CsvOutput<T>;
232 
239 class HexDumpOutput : public AudioOutput {
240 public:
241  HexDumpOutput(int buffer_size = DEFAULT_BUFFER_SIZE, bool active = true) {
242  this->is_active = active;
243  }
244 
246  HexDumpOutput(Print &out, int buffer_size = DEFAULT_BUFFER_SIZE,
247  bool active = true) {
248  this->out_ptr = &out;
249  this->is_active = active;
250  }
251 
252  bool begin() override {
253  TRACED();
254  this->is_active = true;
255  pos = 0;
256  return is_active;
257  }
258 
259  void flush() override {
260  out_ptr->println();
261  pos = 0;
262  }
263 
264  virtual size_t write(const uint8_t *data, size_t len) override {
265  if (!is_active)
266  return 0;
267  TRACED();
268  for (size_t j = 0; j < len; j++) {
269  out_ptr->print(data[j], HEX);
270  out_ptr->print(" ");
271  pos++;
272  if (pos == 8) {
273  out_ptr->print(" - ");
274  }
275  if (pos == 16) {
276  out_ptr->println();
277  pos = 0;
278  }
279  }
280  return len;
281  }
282 
283  //
284  AudioInfo defaultConfig(RxTxMode mode = TX_MODE) {
285  AudioInfo info;
286  return info;
287  }
288 
289 protected:
290  Print *out_ptr = &Serial;
291  int pos = 0;
292 };
293 
294 // legacy name
295 using HexDumpStream = HexDumpOutput;
296 
304 template <typename T>
305 class OutputMixer : public Print {
306 public:
307  OutputMixer() = default;
308 
309  OutputMixer(Print &finalOutput, int outputStreamCount) {
310  setOutput(finalOutput);
311  setOutputCount(outputStreamCount);
312  }
313 
314  void setOutput(Print &finalOutput) { p_final_output = &finalOutput; }
315 
316  void setOutputCount(int count) {
317  output_count = count;
318  buffers.resize(count);
319  for (int i = 0; i < count; i++) {
320  buffers[i] = nullptr;
321  }
322  weights.resize(count);
323  for (int i = 0; i < count; i++) {
324  weights[i] = 1.0;
325  }
326 
327  update_total_weights();
328  }
329 
332  void setWeight(int channel, float weight) {
333  if (channel < size()) {
334  weights[channel] = weight;
335  } else {
336  LOGE("Invalid channel %d - max is %d", channel, size() - 1);
337  }
338  update_total_weights();
339  }
340 
342  bool begin(int copy_buffer_size_bytes = DEFAULT_BUFFER_SIZE,
343  MemoryType memoryType = PS_RAM) {
344  is_active = true;
345  size_bytes = copy_buffer_size_bytes;
346  stream_idx = 0;
347  memory_type = memoryType;
348  allocate_buffers(size_bytes);
349  return true;
350  }
351 
353  void end() {
354  total_weights = 0.0;
355  is_active = false;
356  // release memory for buffers
357  free_buffers();
358  }
359 
361  int size() { return output_count; }
362 
363  size_t write(uint8_t) override { return 0; }
364 
367  size_t write(const uint8_t *buffer_c, size_t bytes) override {
368  size_t result = write(stream_idx, buffer_c, bytes);
369  // after writing the last stream we flush
370  if (is_auto_index) stream_idx++;
371  if (stream_idx >= output_count) {
372  flushMixer();
373  }
374  return result;
375  }
376 
378  size_t write(int idx, const uint8_t *buffer_c, size_t bytes) {
379  LOGD("write idx %d: %d", idx, bytes);
380  size_t result = 0;
381  RingBuffer<T> *p_buffer = idx < output_count ? buffers[idx] : nullptr;
382  assert(p_buffer != nullptr);
383  size_t samples = bytes / sizeof(T);
384  if (p_buffer->availableForWrite() >= bytes) {
385  result = p_buffer->writeArray((T *)buffer_c, samples) * sizeof(T);
386  } else {
387  LOGW("Available Buffer %d too small %d: requested: %d -> increase the "
388  "buffer size", idx,
389  p_buffer->availableForWrite(), bytes);
390  }
391  return result;
392  }
393 
395  int availableForWrite() override {
396  return is_active ? availableForWrite(stream_idx) : 0;
397  }
398 
400  int availableForWrite(int idx) {
401  RingBuffer<T> *p_buffer = buffers[idx];
402  if (p_buffer == nullptr)
403  return 0;
404  return p_buffer->availableForWrite() * sizeof(T);
405  }
406 
408  int available(int idx){
409  RingBuffer<T> *p_buffer = buffers[idx];
410  if (p_buffer == nullptr)
411  return 0;
412  return p_buffer->available() * sizeof(T);
413  }
414 
416  void flushMixer() {
417  LOGD("flush");
418  bool result = false;
419 
420  // determine ringbuffer with mininum available data
421  size_t samples = size_bytes / sizeof(T);
422  for (int j = 0; j < output_count; j++) {
423  samples = MIN(samples, (size_t)buffers[j]->available());
424  }
425 
426  if (samples > 0) {
427  result = true;
428  // mix data from ringbuffers to output
429  output.resize(samples);
430  memset(output.data(), 0, samples * sizeof(T));
431  for (int j = 0; j < output_count; j++) {
432  float weight = weights[j];
433  // sum up input samples to result samples
434  for (int i = 0; i < samples; i++) {
435  output[i] += weight * buffers[j]->read() / total_weights;
436  }
437  }
438 
439  // write output
440  LOGD("write to final out: %d", samples * sizeof(T));
441  p_final_output->write((uint8_t *)output.data(), samples * sizeof(T));
442  }
443  stream_idx = 0;
444  return;
445  }
446 
448  void resize(int size) {
449  if (size != size_bytes) {
450  allocate_buffers(size);
451  }
452  size_bytes = size;
453  }
454 
455 
456  size_t writeSilence(size_t bytes) {
457  if (bytes == 0) return 0;
458  byte silence[bytes] = {0};
459  return write(stream_idx, silence, bytes);
460  }
461 
462  size_t writeSilence(int idx, size_t bytes){
463  if (bytes == 0) return 0;
464  byte silence[bytes] = {0};
465  return write(idx, silence, bytes);
466  }
467 
469  void setAutoIndex(bool flag){
470  is_auto_index = flag;
471  }
472 
474  void setIndex(int idx){
475  stream_idx = idx;
476  }
477 
479  void next() {
480  stream_idx++;
481  }
482 
483 protected:
484  Vector<RingBuffer<T> *> buffers{0};
485  Vector<T> output{0};
486  Vector<float> weights{0};
487  Print *p_final_output = nullptr;
488  float total_weights = 0.0;
489  bool is_active = false;
490  int stream_idx = 0;
491  int size_bytes;
492  int output_count;
493  MemoryType memory_type;
494  void *p_memory = nullptr;
495  bool is_auto_index = true;
496 
497  void update_total_weights() {
498  total_weights = 0.0;
499  for (int j = 0; j < weights.size(); j++) {
500  total_weights += weights[j];
501  }
502  }
503 
504  void allocate_buffers(int size) {
505  // allocate ringbuffers for each output
506  for (int j = 0; j < output_count; j++) {
507  if (buffers[j] != nullptr) {
508  delete buffers[j];
509  }
510 #if defined(ESP32) && defined(ARDUINO)
511  if (memory_type == PS_RAM && ESP.getFreePsram() >= size) {
512  p_memory = ps_malloc(size);
513  LOGI("Buffer %d allocated %d bytes in PS_RAM", j, size);
514  } else {
515  p_memory = malloc(size);
516  LOGI("Buffer %d allocated %d bytes in RAM", j, size);
517  }
518  if (p_memory != nullptr) {
519  buffers[j] = new (p_memory) RingBuffer<T>(size / sizeof(T));
520  } else {
521  LOGE("Not enough memory to allocate %d bytes", size);
522  }
523 #else
524  buffers[j] = new RingBuffer<T>(size / sizeof(T));
525 #endif
526  }
527  }
528 
529  void free_buffers() {
530  // allocate ringbuffers for each output
531  for (int j = 0; j < output_count; j++) {
532  if (buffers[j] != nullptr) {
533  delete buffers[j];
534 #ifdef ESP32
535  if (p_memory != nullptr) {
536  free(p_memory);
537  }
538 #endif
539  buffers[j] = nullptr;
540  }
541  }
542  }
543 };
544 
552 class VolumeOutput : public AudioOutput {
553 public:
554  void setAudioInfo(AudioInfo info) {
555  this->info = info;
556  if (info.channels > 0) {
557  volumes.resize(info.channels);
558  volumes_tmp.resize(info.channels);
559  }
560  }
561 
562  size_t write(const uint8_t *buffer, size_t size) {
563  clear();
564  switch (info.bits_per_sample) {
565  case 16:
566  updateVolumes<int16_t>(buffer, size);
567  break;
568  case 24:
569  updateVolumes<int24_t>(buffer, size);
570  break;
571  case 32:
572  updateVolumes<int32_t>(buffer, size);
573  break;
574  default:
575  LOGE("Unsupported bits_per_sample: %d", info.bits_per_sample);
576  break;
577  }
578  return size;
579  }
580 
583  float volume() { return f_volume; }
584 
587  float volume(int channel) {
588  if (volumes.size() == 0) {
589  LOGE("begin not called!");
590  return 0.0f;
591  }
592  if (channel >= volumes.size()) {
593  LOGE("invalid channel %d", channel);
594  return 0.0f;
595  }
596  return volumes[channel];
597  }
598 
600  void clear() {
601  f_volume_tmp = 0;
602  for (int j = 0; j < info.channels; j++) {
603  volumes_tmp[j] = 0;
604  }
605  }
606 
607 protected:
608  AudioInfo info;
609  float f_volume_tmp = 0;
610  float f_volume = 0;
611  Vector<float> volumes{0};
612  Vector<float> volumes_tmp{0};
613 
614  template <typename T> void updateVolumes(const uint8_t *buffer, size_t size) {
615  T *bufferT = (T *)buffer;
616  int samplesCount = size / sizeof(T);
617  for (int j = 0; j < samplesCount; j++) {
618  float tmp = abs(static_cast<float>(bufferT[j]));
619  updateVolume(tmp, j);
620  }
621  commit();
622  }
623 
624  void updateVolume(float tmp, int j) {
625  if (tmp > f_volume_tmp) {
626  f_volume_tmp = tmp;
627  }
628  if (volumes_tmp.size() > 0 && info.channels > 0) {
629  int ch = j % info.channels;
630  if (tmp > volumes_tmp[ch]) {
631  volumes_tmp[ch] = tmp;
632  }
633  }
634  }
635 
636  void commit() {
637  f_volume = f_volume_tmp;
638  for (int j = 0; j < info.channels; j++) {
639  volumes[j] = volumes_tmp[j];
640  }
641  }
642 };
643 
644 // legacy name
645 using VolumePrint = VolumeOutput;
646 
651 class MemoryOutput : public AudioOutput {
652 public:
653  MemoryOutput(uint8_t *start, int len) {
654  p_start = start;
655  p_next = start;
656  max_size = len;
657  is_active = true;
658  if (p_next == nullptr) {
659  LOGE("start must not be null");
660  }
661  }
662 
663  bool begin() {
664  is_active = true;
665  p_next = p_start;
666  pos = 0;
667  return true;
668  }
669 
670  size_t write(const uint8_t *buffer, size_t len) override {
671  if (p_next == nullptr)
672  return 0;
673  if (pos + len <= max_size) {
674  memcpy(p_next, buffer, len);
675  pos += len;
676  p_next += len;
677  return len;
678  } else {
679  LOGE("Buffer too small: pos:%d, size: %d ", pos, (int)max_size);
680  return 0;
681  }
682  }
683 
684  int availableForWrite() override { return max_size - pos; }
685 
686  int size() { return max_size; }
687 
688 protected:
689  int pos = 0;
690  uint8_t *p_start = nullptr;
691  uint8_t *p_next = nullptr;
692  size_t max_size;
693 };
694 
695 // legacy name
696 using MemoryPrint = MemoryOutput;
697 
707 public:
708  ChannelSplitOutput() = default;
709 
710  ChannelSplitOutput(Print &out, int channel) { addOutput(out, channel); }
711 
714  void addOutput(Print &out, int channel) {
716  def.channel = channel;
717  def.p_out = &out;
718  out_channels.push_back(def);
719  }
720 
721  size_t write(const uint8_t *buffer, size_t size) override {
722  switch(cfg.bits_per_sample){
723  case 16:
724  return writeT<int16_t>(buffer, size);
725  case 24:
726  return writeT<int24_t>(buffer, size);
727  case 32:
728  return writeT<int32_t>(buffer, size);
729  default:
730  return 0;
731  }
732  }
733 
734 protected:
736  Print *p_out = nullptr;
737  int channel;
738  };
740 
741  template <typename T>
742  size_t writeT(const uint8_t *buffer, size_t size) {
743  int sample_count = size / sizeof(T);
744  int result_size = sample_count / cfg.channels;
745  T *data = (T *)buffer;
746  T result[result_size];
747 
748  for (int ch = 0; ch < out_channels.size(); ch++) {
749  ChannelSelectionOutputDef &def = out_channels[ch];
750  // extract mono result
751  int i = 0;
752  for (int j = def.channel; j < sample_count; j += cfg.channels) {
753  result[i++] = data[j];
754  }
755  // write mono result
756  size_t written =
757  def.p_out->write((uint8_t *)result, result_size * sizeof(T));
758  if (written != result_size * sizeof(T)) {
759  LOGW("Could not write all samples");
760  }
761  }
762  return size;
763  }
764 
765 };
766 
767 
768 } // namespace audio_tools
Supports the subscription to audio change notifications.
Definition: AudioTypes.h:155
Supports changes to the sampling rate, bits and channels.
Definition: AudioTypes.h:136
virtual AudioInfo audioInfoOut()
provides the actual output AudioInfo: this is usually the same as audioInfo() unless we use a transfo...
Definition: AudioTypes.h:143
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
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:65
Simple functionality to extract mono streams from a multichannel (e.g. stereo) signal.
Definition: AudioOutput.h:706
void addOutput(Print &out, int channel)
Definition: AudioOutput.h:714
Stream Wrapper which can be used to print the values as readable ASCII to the screen to be analyzed i...
Definition: AudioOutput.h:112
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:171
AudioInfo defaultConfig()
Provides the default configuration.
Definition: AudioOutput.h:135
bool begin(int channels)
Starts the processing with the defined number of channels.
Definition: AudioOutput.h:147
CsvOutput(Print &out, int channels=2, int buffer_size=DEFAULT_BUFFER_SIZE, bool active=true)
Constructor.
Definition: AudioOutput.h:119
virtual void setAudioInfo(AudioInfo info) override
defines the number of channels
Definition: AudioOutput.h:163
const char * delimiter()
Provides the current column delimiter.
Definition: AudioOutput.h:130
bool begin(AudioInfo info) override
Starts the processing with the defined number of channels.
Definition: AudioOutput.h:144
bool begin() override
(Re)start (e.g. if channels is set in constructor)
Definition: AudioOutput.h:154
void setDelimiter(const char *del)
Defines an alternative (column) delimiter. The default is ,.
Definition: AudioOutput.h:127
Creates a Hex Dump.
Definition: AudioOutput.h:239
HexDumpOutput(Print &out, int buffer_size=DEFAULT_BUFFER_SIZE, bool active=true)
Constructor.
Definition: AudioOutput.h:246
Writes to a preallocated memory.
Definition: AudioOutput.h:651
Abstract class: Objects can be put into a pipleline.
Definition: AudioOutput.h:96
virtual void setOutput(Print &out)=0
Defines/Changes the output target.
Mixing of multiple outputs to one final output.
Definition: AudioOutput.h:305
void next()
Moves to the next mixing index.
Definition: AudioOutput.h:479
void setIndex(int idx)
Sets the Output Stream index.
Definition: AudioOutput.h:474
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:378
int available(int idx)
Provides the available bytes in the buffer.
Definition: AudioOutput.h:408
bool begin(int copy_buffer_size_bytes=DEFAULT_BUFFER_SIZE, MemoryType memoryType=PS_RAM)
Starts the processing.
Definition: AudioOutput.h:342
int availableForWrite() override
Provides the bytes available to write for the current stream buffer.
Definition: AudioOutput.h:395
size_t write(const uint8_t *buffer_c, size_t bytes) override
Definition: AudioOutput.h:367
void flushMixer()
Force output to final destination.
Definition: AudioOutput.h:416
void end()
Remove all input streams.
Definition: AudioOutput.h:353
void setWeight(int channel, float weight)
Definition: AudioOutput.h:332
int availableForWrite(int idx)
Provides the bytes available to write for the indicated stream index.
Definition: AudioOutput.h:400
void resize(int size)
Resizes the buffer to the indicated number of bytes.
Definition: AudioOutput.h:448
void setAutoIndex(bool flag)
Automatically increment mixing index after each write.
Definition: AudioOutput.h:469
int size()
Number of stremams to which are mixed together.
Definition: AudioOutput.h:361
Definition: NoArduino.h:58
Implements a typed Ringbuffer.
Definition: Buffers.h:298
virtual int availableForWrite()
provides the number of entries that are available to write
Definition: Buffers.h:365
virtual int available()
provides the number of entries that are available to read
Definition: Buffers.h:362
T * address() override
Provides address to beginning of the buffer.
Definition: Buffers.h:243
bool write(T sample) override
write add an entry to the buffer
Definition: Buffers.h:188
int available() override
provides the number of entries that are available to read
Definition: Buffers.h:213
bool isFull() override
checks if the buffer is full
Definition: Buffers.h:220
Vector implementation which provides the most important methods as defined by std::vector....
Definition: Vector.h:21
A simple class to determine the volume.
Definition: AudioOutput.h:552
float volume()
Definition: AudioOutput.h:583
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition: AudioOutput.h:554
void clear()
Resets the actual volume.
Definition: AudioOutput.h:600
float volume(int channel)
Definition: AudioOutput.h:587
MemoryType
Memory types.
Definition: AudioTypes.h:33
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
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