arduino-audio-tools
R2ROutput.h
1 #pragma once
2 
3 #include "AudioConfig.h"
4 #include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h"
5 #include "AudioTools/CoreAudio/AudioLogger.h"
6 #include "AudioTools/CoreAudio/AudioOutput.h"
7 #include "AudioTools/CoreAudio/Buffers.h"
8 #include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h"
9 namespace audio_tools {
10 
19  public:
20  virtual void setupPins(Vector<int> &channel1_pins,
21  Vector<int> &channel2_pins) = 0;
22 
23  virtual void writePins(int channels, int channel, unsigned uvalue) = 0;
24 };
25 
34 class R2RDriver : public R2RDriverBase {
35  public:
36  void setupPins(Vector<int> &channel1_pins,
37  Vector<int> &channel2_pins) override {
38  TRACED();
39  p_channel1_pins = &channel1_pins;
40  p_channel2_pins = &channel2_pins;
41 
42  for (auto pin : channel1_pins) {
43  LOGI("Setup channel1 pin %d", pin);
44  pinMode(pin, OUTPUT);
45  }
46  for (int pin : channel2_pins) {
47  LOGI("Setup channel2 pin %d", pin);
48  pinMode(pin, OUTPUT);
49  }
50  };
51 
52  void writePins(int channels, int channel, unsigned uvalue) override {
53  switch (channel) {
54  case 0:
55  for (int j = 0; j < (*p_channel1_pins).size(); j++) {
56  int pin = (*p_channel1_pins)[j];
57  if (pin >= 0) digitalWrite(pin, (uvalue >> j) & 1);
58  }
59  break;
60  case 1:
61  for (int j = 0; j < (*p_channel2_pins).size(); j++) {
62  int pin = (*p_channel2_pins)[j];
63  if (pin >= 0) digitalWrite(pin, (uvalue >> j) & 1);
64  }
65  break;
66  }
67  }
68 
69  protected:
70  Vector<int> *p_channel1_pins = nullptr;
71  Vector<int> *p_channel2_pins = nullptr;
72 } r2r_driver;
73 
80 class R2RConfig : public AudioInfo {
81  public:
82  Vector<int> channel1_pins;
83  Vector<int> channel2_pins;
84  uint16_t buffer_size = DEFAULT_BUFFER_SIZE;
85  uint16_t buffer_count = 2; // double buffer
86  R2RDriverBase *driver = &r2r_driver; // by default use Arduino driver
87  bool is_blocking = true;
88  int blocking_retry_delay_ms = 5;
89  int timer_id = 0;
90 };
91 
105 class R2ROutput : public AudioOutput {
106  public:
107  R2RConfig defaultConfig() {
108  R2RConfig r;
109  return r;
110  }
111 
112  bool begin(R2RConfig c) {
113  TRACED();
114  cfg = c;
115  rcfg = c;
116  return begin();
117  }
118 
119  bool begin() override {
120  TRACED();
121  if (cfg.channels == 0 || cfg.channels > 2) {
122  LOGE("channels is %d", cfg.channels);
123  return false;
124  }
125  if (rcfg.channel1_pins.size() == 0) {
126  LOGE("channel1_pins not defined");
127  return false;
128  }
129  if (cfg.channels == 2 &&
130  rcfg.channel2_pins.size() != rcfg.channel1_pins.size()) {
131  LOGE("channel2_pins not defined");
132  return false;
133  }
134  if (rcfg.buffer_size * rcfg.buffer_count == 0) {
135  LOGE("buffer_size or buffer_count is 0");
136  return false;
137  }
138  buffer.resize(rcfg.buffer_size, rcfg.buffer_count);
139  rcfg.driver->setupPins(rcfg.channel1_pins, rcfg.channel2_pins);
140 
141  // setup timer
142  timer.setCallbackParameter(this);
143  timer.setIsSave(true);
144  timer.setTimer(rcfg.timer_id);
145  return timer.begin(r2r_timer_callback, cfg.sample_rate, HZ);
146  }
147 
148  size_t write(const uint8_t *data, size_t len) override {
149  LOGD("write: %d", len);
150  size_t result = 0;
151  // if buffer has not been allocated (buffer_size==0)
152  if (len > rcfg.buffer_size) {
153  LOGE("buffer_size %d too small for write size: %d", rcfg.buffer_size,
154  len);
155  return len;
156  }
157 
158  if (rcfg.is_blocking){
159  // write of all bytes
160  int open = len;
161  while(open > 0){
162  int written = buffer.writeArray(data + result, open);
163  open -= written;
164  result += written;
165  if (open > 0){
166  delay(rcfg.blocking_retry_delay_ms);
167  }
168  }
169  } else {
170  // write as much as possible
171  result = buffer.writeArray(data, len);
172  }
173  // activate output when buffer is half full
174  if (!is_active && buffer.bufferCountFilled() >= rcfg.buffer_count / 2) {
175  LOGI("is_active = true");
176  is_active = true;
177  }
178 
179  return result;
180  }
181 
182  protected:
183  TimerAlarmRepeating timer;
184  // Double buffer
185  NBuffer<uint8_t> buffer{DEFAULT_BUFFER_SIZE, 0};
186  R2RConfig rcfg;
187 
188  void writeValue(int channel) {
189  switch (cfg.bits_per_sample) {
190  case 8:
191  return writeValueT<int8_t>(channel);
192  case 16:
193  return writeValueT<int16_t>(channel);
194  case 24:
195  return writeValueT<int24_t>(channel);
196  case 32:
197  return writeValueT<int32_t>(channel);
198  }
199  }
200 
201  template <typename T>
202  void writeValueT(int channel) {
203  // don't do anything if we do not have enough data
204  if (buffer.available() < sizeof(T)) return;
205 
206  // get next value from buffer
207  T value = 0;
208  buffer.readArray((uint8_t *)&value, sizeof(T));
209  // convert to unsigned
210  unsigned uvalue = (int)value + NumberConverter::maxValueT<T>() + 1;
211  // scale value
212  uvalue = uvalue >> ((sizeof(T) * 8) - rcfg.channel1_pins.size());
213  // Serial.println(uvalue);
214 
215  // output pins
216  rcfg.driver->writePins(cfg.channels, channel, uvalue);
217  }
218 
219  static void r2r_timer_callback(void *ptr) {
220  R2ROutput *self = (R2ROutput *)ptr;
221  if (self->is_active) {
222  // output channel 1
223  self->writeValue(0);
224  // output channel 2
225  if (self->cfg.channels == 2) self->writeValue(1);
226  };
227  }
228 };
229 
230 } // namespace audio_tools
Abstract Audio Ouptut class.
Definition: AudioOutput.h:22
virtual int readArray(T data[], int len)
reads multiple values
Definition: Buffers.h:41
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:65
int available()
provides the number of entries that are available to read
Definition: Buffers.h:621
R2R configuration.
Definition: R2ROutput.h:80
R2R driver base class.
Definition: R2ROutput.h:18
R2R driver which uses the Arduino API to setup and write to the digital pins.
Definition: R2ROutput.h:34
Output to R-2R DAC. You need to define the used digital pins in the configuration....
Definition: R2ROutput.h:105
Common Interface definition for TimerAlarmRepeating.
Definition: AudioTimer.h:25
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:821
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