arduino-audio-tools
Loading...
Searching...
No Matches
AnalogAudioArduino.h
1#pragma once
2
3
4#include "AnalogConfigStd.h"
5#if defined(USE_ANALOG)
6
7#include "AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h"
8#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h"
9#include "AudioTools/CoreAudio/AudioStreams.h"
10#include "AudioTools/CoreAudio/Buffers.h"
11#include <limits.h> // for INT_MIN and INT_MAX
12
13namespace audio_tools {
14
24 public:
25 AnalogAudioArduino() = default;
26
30 return def;
31 }
32
33 void setAudioInfo(AudioInfo info) override {
34 TRACEI();
35 if (config.sample_rate != info.sample_rate ||
36 config.channels != info.channels ||
37 config.bits_per_sample != info.bits_per_sample) {
38 config.sample_rate = info.sample_rate;
39 config.bits_per_sample = info.bits_per_sample;
40 config.channels = info.channels;
41 config.logInfo();
42 setupTimer();
43 }
44 }
45
47 bool begin() override { return begin(config); }
48
49 bool begin(AnalogConfigStd cfg) {
50 TRACED();
51
52 config = cfg;
53 if (config.rx_tx_mode == RXTX_MODE) {
54 LOGE("RXTX not supported");
55 return false;
56 }
57
58 frame_size = config.channels * (config.bits_per_sample / 8);
59 result_factor = 1;
60
61 if (!setupPins()) return false;
62
63 if (!setupTx()) return false;
64
65 if (!setupBuffer()) return false;
66
67 // (re)start timer
68 return setupTimer();
69 }
70
71 void end() override { timer.end(); }
72
73 int available() override {
74 if (config.rx_tx_mode == TX_MODE) return 0;
75 return buffer == nullptr ? 0 : buffer->available() * 2;
76 };
77
79 size_t readBytes(uint8_t *data, size_t len) override {
80 if (config.rx_tx_mode == TX_MODE) return 0;
81 if (buffer == nullptr) return 0;
82 int bytes = len / frame_size * frame_size;
83 return buffer->readArray(data, bytes);
84 }
85
86 int availableForWrite() override {
87 if (config.rx_tx_mode == RX_MODE) return 0;
88 if (buffer == nullptr) return 0;
89 return config.is_blocking_write ? config.buffer_size
90 : buffer->availableForWrite();
91 }
92
93 size_t write(const uint8_t *data, size_t len) override {
94 LOGD("write: %d", (int)len);
95 if (config.rx_tx_mode == RX_MODE) return 0;
96 // only process full frames
97 len = len / frame_size * frame_size;
98
99 if (isCombinedChannel()) {
100 ChannelReducer cr(1, 2, config.bits_per_sample);
101 len = cr.convert((uint8_t *)data, len);
102 LOGD("ChannelReducer len: %d", (int)len);
103 }
104
105 if (isDecimateActive()) {
106 Decimate dec(decim, 1, config.bits_per_sample);
107 len = dec.convert((uint8_t *)data, len);
108 LOGD("Decimate len: %d for factor %d", (int)len, decim);
109 }
110
111 // blocking write ?
112 if (config.is_blocking_write) {
113 LOGD("Waiting for buffer to be available");
114 while (buffer->availableForWrite() < len) {
115 delay(10);
116 }
117 }
118
119 size_t result = 0;;
120 switch(config.bits_per_sample){
121 case 8: {
122 result = buffer->writeArray(data, len);
123 } break;
124 case 16: {
125 size_t samples = len / 2;
126 int16_t *p16 = (int16_t*)data;
127 for (int j=0;j<samples;j++){
128 uint8_t sample = map(p16[j],-32768, 32767,0,255);
129 if (buffer->write(sample)){
130 result += 2;
131 } else {
132 break;
133 }
134 }
135 } break;
136 case 24: {
137 size_t samples = len / 3;
138 int24_t *p24 = (int24_t*)data;
139 for (int j=0;j<samples;j++){
140 uint8_t sample = map(p24[j],-8388608, 8388607,0,255);
141 if (buffer->write(sample)){
142 result += 3;
143 } else {
144 break;
145 }
146 }
147
148 } break;
149 case 32: {
150 size_t samples = len / 4;
151 int32_t *p32 = (int32_t*)data;
152 for (int j=0;j<samples;j++){
153 uint8_t sample = map(p32[j],-2147483648, 2147483647,0,255);
154 if (buffer->write(sample)){
155 result += 4;
156 } else {
157 break;
158 }
159 }
160
161 } break;
162 }
163
164 // write data
165 return result * result_factor;
166 }
167
168 protected:
169 AnalogConfigStd config;
170 TimerAlarmRepeating timer;
171 BaseBuffer<uint8_t> *buffer = nullptr;
172 int avg_value, min, max, count;
173 bool is_combined_channels = false;
174 uint16_t frame_size = 0;
175 int result_factor = 1;
176 int decim = 1;
177 bool is_active = false;
178
179
180 bool setupTx() {
181 if (config.rx_tx_mode == TX_MODE) {
182 // check channels
183 if (config.channels > ANALOG_MAX_OUT_CHANNELS) {
184 if (config.channels == 2) {
185 is_combined_channels = true;
186 config.channels = 1;
187 } else {
188 LOGE("Unsupported channels");
189 return false;
190 }
191 }
192 if (isDecimateActive()) {
193 LOGI("Using reduced sample rate: %d", effectiveOutputSampleRate());
194 decim = decimation();
195 result_factor = result_factor * decim;
196 }
197 if (isCombinedChannel()) {
198 LOGI("Combining channels");
199 result_factor = result_factor * 2;
200 }
201 }
202 return true;
203 }
204
205 bool setupBuffer() {
206 if (buffer == nullptr) {
207 // allocate buffer_count
208 buffer = new RingBuffer<uint8_t>(config.buffer_size * config.buffer_count);
209 if (buffer == nullptr) {
210 LOGE("Not enough memory for buffer");
211 return false;
212 }
213 }
214 return true;
215 }
216
217 bool setupTimer() {
218 int sample_rate = config.rx_tx_mode == TX_MODE ? effectiveOutputSampleRate()
219 : config.sample_rate;
220 LOGI("sample_rate: %d", sample_rate);
221 timer.setCallbackParameter(this);
222 return timer.begin(callback, sample_rate, TimeUnit::HZ);
223 }
224
226 static void callback(void *arg) {
227 int16_t value = 0;
229 // prevent NPE
230 if (self->buffer == nullptr) return;
231
232 // Logic for reading audio data
233 if (self->config.rx_tx_mode == RX_MODE) {
234 int channels = self->config.channels;
235 for (int j = 0; j < channels; j++) {
236 // provides value in range 0…4095
237 value = analogRead(self->config.pins_data[j]);
238 if (self->config.is_auto_center_read) {
239 self->updateMinMax(value);
240 }
241 value = (value - self->avg_value) * 16;
242 self->buffer->write(value);
243 }
244 // Logic for writing audio data
245 } else if (self->config.rx_tx_mode == TX_MODE) {
246 int channels = self->config.channels;
247 uint8_t sample = 0;
248 for (int j = 0; j < channels; j++) {
249 self->buffer->read(sample);
250 int pin = self->config.pins_data[j];
251 analogWrite(pin, sample);
252 //LOGW("analogWrite(%d, %d)", pin, sample);
253 }
254 }
255 }
256
258 bool setupPins() {
259 TRACED();
260
261 Pins& pins = config.pins();
262 if (pins.size()<config.channels){
263 LOGE("Only pins %d of %d defined", pins.size(), config.channels);
264 return false;
265 }
266
267
268 if (config.rx_tx_mode == RX_MODE) {
269 LOGI("rx start_pin: %d", config.start_pin);
270 // setup pins for read
271 for (int j = 0; j < config.channels; j++) {
272 int pin = config.pins_data [j];
273 pinMode(pin, INPUT);
274 LOGD("pinMode(%d, INPUT)", pin);
275 }
276
277 if (config.is_auto_center_read) {
278 // calculate the avarage value to center the signal
279 for (int j = 0; j < 1024; j++) {
280 updateMinMax(analogRead(config.pins_data[0]));
281 }
282 LOGI("Avg Signal was %d", avg_value);
283 }
284 } else if (config.rx_tx_mode == TX_MODE) {
285 // setup pins for read
286 for (int j = 0; j < config.channels; j++) {
287 int pin = config.pins_data[j];
288 LOGI("tx pin %d: %d", j, pin);
289 pinMode(pin, OUTPUT);
290 LOGD("pinMode(%d, OUTPUT)", pin);
291 }
292 }
293 return true;
294 }
295
296 void updateMinMax(int value) {
297 if (value < min) min = value;
298 if (value > max) max = value;
299 if (count++ == 1024) updateAvg();
300 }
301
302 void updateAvg() {
303 avg_value = (max + min) / 2;
304 min = INT_MAX;
305 max = INT_MIN;
306 count = 0;
307 }
308
312 return config.sample_rate >= config.max_sample_rate;
313 }
314
315 // combined stereo channel to mono
316 bool isCombinedChannel() { return is_combined_channels; }
317
319 int effectiveOutputSampleRate() { return config.sample_rate / decimation(); }
320
321 int decimation() {
322 if (config.sample_rate <= config.max_sample_rate) return 1;
323 for (int j = 2; j < 6; j += 2) {
324 if (config.sample_rate / j <= config.max_sample_rate) {
325 return j;
326 }
327 }
328 return 6;
329 }
330};
331
332} // namespace audio_tools
333
334#endif
Analog Data IO using a timer and the Arduino analogRead() method and writing using analogWrite();.
Definition AnalogAudioArduino.h:23
static void callback(void *arg)
Sample data and write to buffer.
Definition AnalogAudioArduino.h:226
bool isDecimateActive()
Definition AnalogAudioArduino.h:311
int effectiveOutputSampleRate()
Returns the effective output sample rate.
Definition AnalogAudioArduino.h:319
size_t readBytes(uint8_t *data, size_t len) override
Provides the sampled audio data.
Definition AnalogAudioArduino.h:79
bool setupPins()
pinmode input for defined analog pins
Definition AnalogAudioArduino.h:258
AnalogConfigStd defaultConfig()
provides the default configuration
Definition AnalogAudioArduino.h:28
bool begin() override
Reopen with last config.
Definition AnalogAudioArduino.h:47
void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition AnalogAudioArduino.h:33
Generic ADC and DAC configuration.
Definition AnalogConfigStd.h:31
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:122
virtual bool read(T &result)=0
reads a single value
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:33
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 bool write(T data)=0
write add an entry to the buffer
virtual int available()=0
provides the number of entries that are available to read
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
long map(long x, long in_min, long in_max, long out_min, long out_max)
Maps input to output values.
Definition NoArduino.h:189
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61