arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
R2ROutput.h
1#pragma once
2
3#include "AudioToolsConfig.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"
9namespace 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
34class 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
80class 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
105class 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:
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:37
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
A lock free N buffer. If count=2 we create a DoubleBuffer, if count=3 a TripleBuffer etc.
Definition Buffers.h:617
int available()
provides the number of entries that are available to read
Definition Buffers.h:664
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
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 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