arduino-audio-tools
Loading...
Searching...
No Matches
AnalogDriverZephyr.h
Go to the documentation of this file.
1#pragma once
2
3#include "AudioToolsConfig.h"
4
5#if defined(USE_ANALOG) && (defined(IS_ZEPHYR))
6
7#include <vector>
8#include <algorithm>
9
10#include <zephyr/kernel.h>
11#include <zephyr/device.h>
12#include <zephyr/drivers/adc.h>
13#include <zephyr/drivers/dac.h>
14
18
19namespace audio_tools {
20
22public:
23 AnalogDriverZephyr() = default;
24 virtual ~AnalogDriverZephyr() { end(); }
25
26 // ============================================================
27 // BEGIN / END
28 // ============================================================
29
30 bool begin(AnalogConfig cfg) override {
31 TRACED();
32 end();
33
34 config = cfg;
35
36 if (config.channels == 0 || config.bits_per_sample == 0) {
37 LOGE("Invalid config");
38 return false;
39 }
40
41 frame_size = config.channels * (config.bits_per_sample / 8);
42 if (frame_size <= 0) {
43 LOGE("Invalid frame size");
44 return false;
45 }
46
47 if (config.rx_tx_mode == TX_MODE || config.rx_tx_mode == RXTX_MODE) {
48 if (!setupDac()) return false;
49 }
50
51 if (config.rx_tx_mode == RX_MODE || config.rx_tx_mode == RXTX_MODE) {
52 if (!setupAdc()) return false;
53 }
54
55 setupTimer();
56 active = true;
57 return true;
58 }
59
60 void end() override {
61 timer.end();
62 active = false;
63 rx_buffer.resize(0);
64 tx_buffer.resize(0);
65 }
66
67 // ============================================================
68 // TX (APP → DAC)
69 // ============================================================
70
71 size_t write(const uint8_t* src, size_t size_bytes) override {
72 if (!active || config.rx_tx_mode == RX_MODE) return 0;
73
74 size_t bytes = (size_bytes / frame_size) * frame_size;
75 if (bytes == 0) return 0;
76
77 return tx_buffer.writeArray(src, bytes);
78 }
79
80 // ============================================================
81 // RX (ADC → APP)
82 // ============================================================
83
84 size_t readBytes(uint8_t* dst, size_t size_bytes) override {
85 if (!active || config.rx_tx_mode == TX_MODE) return 0;
86
87 size_t bytes = (size_bytes / frame_size) * frame_size;
88 if (bytes == 0) return 0;
89
90 return rx_buffer.readArray(dst, bytes);
91 }
92
93 int available() override {
94 return rx_buffer.available();
95 }
96
97 int availableForWrite() override {
98 return tx_buffer.availableForWrite();
99 }
100
101private:
102
103 // ============================================================
104 // CONFIG + STATE
105 // ============================================================
106
107 AnalogConfig config;
108
109 bool active = false;
110 int frame_size = 0;
111 int configured_channels = 0;
112
113 // ============================================================
114 // BUFFERS (FULL DUPLEX)
115 // ============================================================
116
117 RingBuffer<uint8_t> rx_buffer{0}; // ADC → APP
118 RingBuffer<uint8_t> tx_buffer{0}; // APP → DAC
119
120 // ============================================================
121 // TIMING
122 // ============================================================
123
124 AudioTimer timer;
125
126 // ============================================================
127 // DEVICES
128 // ============================================================
129
130 const struct device* dac_dev = nullptr;
131 const struct device* adc_dev = nullptr;
132
133 // ============================================================
134 // SETUP BUFFERS
135 // ============================================================
136
137 bool setupBuffer(RingBuffer<uint8_t>& buffer) {
138 size_t size = config.buffer_count * config.buffer_size;
139
140 buffer.resize(size);
141
142 return buffer.address() != nullptr;
143 }
144
145 // ============================================================
146 // DAC SETUP
147 // ============================================================
148
149 bool setupDac() {
150#if IS_ENABLED(CONFIG_DAC)
151
152 if (config.dac.empty()) {
153 LOGE("No DAC configured");
154 return false;
155 }
156
157 if (!setupBuffer(tx_buffer)){
158 LOGE("TX Buffer");
159 return false;
160 }
161
162 configured_channels = std::min<int>(config.channels, config.dac.size());
163
164 for (int ch = 0; ch < configured_channels; ch++) {
165 if (!device_is_ready(config.dac[ch].dev)) {
166 LOGE("DAC not ready");
167 return false;
168 }
169
170 int rc = dac_channel_setup_dt(&config.dac[ch]);
171 if (rc != 0) {
172 LOGE("DAC setup failed ch=%d rc=%d", ch, rc);
173 return false;
174 }
175 }
176
177 return true;
178#else
179 return false;
180#endif
181 }
182
183 // ============================================================
184 // ADC SETUP
185 // ============================================================
186
187 bool setupAdc() {
188#if IS_ENABLED(CONFIG_ADC)
189
190 if (config.adc.empty()) {
191 LOGE("No ADC configured");
192 return false;
193 }
194
195 if (!setupBuffer(rx_buffer)){
196 LOGE("RX Buffer");
197 return false;
198 }
199
200 configured_channels = std::min<int>(config.channels, config.adc.size());
201
202 for (int ch = 0; ch < configured_channels; ch++) {
203 if (!device_is_ready(config.adc[ch].dev)) {
204 LOGE("ADC not ready");
205 return false;
206 }
207
208 int rc = adc_channel_setup_dt(&config.adc[ch]);
209 if (rc != 0) {
210 LOGE("ADC setup failed ch=%d rc=%d", ch, rc);
211 return false;
212 }
213 }
214
215 return true;
216#else
217 return false;
218#endif
219 }
220
221 // ============================================================
222 // TIMER
223 // ============================================================
224
225 void setupTimer() {
226 timer.setCallbackParameter(this);
227 timer.begin(timerCallback, config.sample_rate, HZ);
228 }
229
230 static void timerCallback(void* arg) {
231 auto* self = static_cast<AnalogDriverZephyr*>(arg);
232 if (!self || !self->active) return;
233
234 switch (self->config.rx_tx_mode) {
235 case TX_MODE:
236 self->processTx();
237 break;
238
239 case RX_MODE:
240 self->processRx();
241 break;
242
243 case RXTX_MODE:
244 self->processRx(); // sample first
245 self->processTx(); // output second
246 break;
247 default:
248 LOGE("Undefined rx_tx_mode: %d", (int)self->config.rx_tx_mode)
249 }
250 }
251
252 // ============================================================
253 // TX PIPELINE (APP → DAC)
254 // ============================================================
255
256 void processTx() {
257#if IS_ENABLED(CONFIG_DAC)
258
259 if (tx_buffer.available() < frame_size) {
260 writeSilence();
261 return;
262 }
263
264 uint8_t frame[64];
265
266 tx_buffer.readArray(frame, frame_size);
267
268 for (int ch = 0; ch < configured_channels; ch++) {
269 int32_t sample = extractSample(frame, ch);
270 uint32_t value = toDac(sample);
271
272 dac_write_value_dt(&config.dac[ch], value);
273 }
274
275#endif
276 }
277
278 void writeSilence() {
279#if IS_ENABLED(CONFIG_DAC)
280
281 uint32_t mid = (1u << dacBits()) / 2u;
282
283 for (int ch = 0; ch < configured_channels; ch++) {
284 dac_write_value_dt(&config.dac[ch], mid);
285 }
286
287#endif
288 }
289
290 // ============================================================
291 // RX PIPELINE (ADC → APP)
292 // ============================================================
293
294 void processRx() {
295#if IS_ENABLED(CONFIG_ADC)
296
297 if (rx_buffer.availableForWrite() < frame_size) return;
298
299 uint8_t frame[64];
300
301 for (int ch = 0; ch < configured_channels; ch++) {
302
303 int16_t io = 0;
304
305 struct adc_sequence seq = {
306 .buffer = &io,
307 .buffer_size = sizeof(io),
308 .resolution = config.adc[ch].resolution,
309 };
310 adc_read_dt(&config.adc[ch], &seq);
311 int32_t sample = adcToSigned(io);
312
313 writeSample(frame, ch, sample);
314 }
315
316 rx_buffer.writeArray(frame, frame_size);
317
318#endif
319 }
320
321 // ============================================================
322 // AUDIO HELPERS
323 // ============================================================
324
325 int32_t extractSample(const uint8_t* frame, int ch) {
326 int bytes = config.bits_per_sample / 8;
327 const uint8_t* p = frame + ch * bytes;
328
329 switch (config.bits_per_sample) {
330 case 8: return (int8_t)p[0];
331 case 16: return *(int16_t*)p;
332 case 24: return (p[0] | (p[1] << 8) | (p[2] << 16));
333 default: return *(int32_t*)p;
334 }
335 }
336
337 void writeSample(uint8_t* frame, int ch, int32_t sample) {
338 int bytes = config.bits_per_sample / 8;
339 uint8_t* p = frame + ch * bytes;
340
341 switch (config.bits_per_sample) {
342 case 8:
343 p[0] = (uint8_t)(sample >> 8);
344 break;
345 case 16:
346 *(int16_t*)p = (int16_t)sample;
347 break;
348 case 24:
349 p[0] = sample & 0xFF;
350 p[1] = (sample >> 8) & 0xFF;
351 p[2] = (sample >> 16) & 0xFF;
352 break;
353 default:
354 *(int32_t*)p = sample;
355 break;
356 }
357 }
358
359 uint32_t toDac(int32_t sample) {
360 int64_t min_in = -(1LL << 15);
361 int64_t max_in = (1LL << 15) - 1;
362
363 uint32_t max_out = (1u << dacBits()) - 1;
364
365 return (uint32_t)((sample - min_in) * max_out / (max_in - min_in));
366 }
367
368 int32_t adcToSigned(int16_t raw) {
369 return (int32_t)raw - 2048;
370 }
371
372 int dacBits() const {
373 return (config.bits_per_sample >= 12) ? 12 : 10;
374 }
375};
376
377using AnalogDriver = AnalogDriverZephyr;
378
379} // namespace audio_tools
380
381#endif
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGE(...)
Definition AudioLoggerIDF.h:30
ESP32 specific configuration for i2s input via adc. The default input pin is GPIO34....
Definition AnalogConfigESP32.h:22
int buffer_count
Definition AnalogConfigESP32.h:25
RxTxMode rx_tx_mode
Definition AnalogConfigESP32.h:27
int buffer_size
Definition AnalogConfigESP32.h:26
Definition AnalogDriverBase.h:9
Definition AnalogDriverZephyr.h:21
bool begin(AnalogConfig cfg) override
Definition AnalogDriverZephyr.h:30
size_t write(const uint8_t *src, size_t size_bytes) override
Definition AnalogDriverZephyr.h:71
void end() override
Definition AnalogDriverZephyr.h:60
int available() override
Definition AnalogDriverZephyr.h:93
int availableForWrite() override
Definition AnalogDriverZephyr.h:97
virtual ~AnalogDriverZephyr()
Definition AnalogDriverZephyr.h:24
size_t readBytes(uint8_t *dst, size_t size_bytes) override
Definition AnalogDriverZephyr.h:84
bool end()
Definition AudioTimer.h:52
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:34
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:56
Implements a typed Ringbuffer.
Definition Buffers.h:353
virtual int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:425
virtual bool resize(size_t len)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:430
virtual int available() override
provides the number of entries that are available to read
Definition Buffers.h:422
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
@ HZ
Definition AudioTypes.h:44
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
AnalogDriverArduino AnalogDriver
AnalogAudioStream.
Definition AnalogDriverArduino.h:48
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
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