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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy