arduino-audio-tools
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/AudioTypes.h"
10 #include "AudioTools/CoreAudio/Buffers.h"
11 
12 namespace audio_tools {
13 
23  public:
24  AnalogAudioArduino() = default;
25 
28  AnalogConfigStd def;
29  return def;
30  }
31 
32  void setAudioInfo(AudioInfo info) override {
33  TRACEI();
34  if (config.sample_rate != info.sample_rate ||
35  config.channels != info.channels ||
36  config.bits_per_sample != info.bits_per_sample) {
37  config.sample_rate = info.sample_rate;
38  config.bits_per_sample = info.bits_per_sample;
39  config.channels = info.channels;
40  config.logInfo();
41  setupTimer();
42  }
43  }
44 
46  bool begin() override { return begin(config); }
47 
48  bool begin(AnalogConfigStd cfg) {
49  TRACED();
50 
51  config = cfg;
52  if (config.rx_tx_mode == RXTX_MODE) {
53  LOGE("RXTX not supported");
54  return false;
55  }
56 
57  frame_size = config.channels * (config.bits_per_sample / 8);
58  result_factor = 1;
59 
60  if (!setupPins()) return false;
61 
62  if (!setupTx()) return false;
63 
64  if (!setupBuffer()) return false;
65 
66  // (re)start timer
67  return setupTimer();
68  }
69 
70  void end() override { timer.end(); }
71 
72  int available() override {
73  if (config.rx_tx_mode == TX_MODE) return 0;
74  return buffer == nullptr ? 0 : buffer->available() * 2;
75  };
76 
78  size_t readBytes(uint8_t *data, size_t len) override {
79  if (config.rx_tx_mode == TX_MODE) return 0;
80  if (buffer == nullptr) return 0;
81  int bytes = len / frame_size * frame_size;
82  return buffer->readArray(data, bytes);
83  }
84 
85  int availableForWrite() override {
86  if (config.rx_tx_mode == RX_MODE) return 0;
87  if (buffer == nullptr) return 0;
88  return config.is_blocking_write ? config.buffer_size
89  : buffer->availableForWrite();
90  }
91 
92  size_t write(const uint8_t *data, size_t len) override {
93  LOGD("write: %d", (int)len);
94  if (config.rx_tx_mode == RX_MODE) return 0;
95  // only process full frames
96  len = len / frame_size * frame_size;
97 
98  if (isCombinedChannel()) {
99  ChannelReducer cr(1, 2, config.bits_per_sample);
100  len = cr.convert((uint8_t *)data, len);
101  LOGD("ChannelReducer len: %d", (int)len);
102  }
103 
104  if (isDecimateActive()) {
105  Decimate dec(decim, 1, config.bits_per_sample);
106  len = dec.convert((uint8_t *)data, len);
107  LOGD("Decimate len: %d for factor %d", (int)len, decim);
108  }
109 
110  // blocking write ?
111  if (config.is_blocking_write) {
112  LOGD("Waiting for buffer to be available");
113  while (buffer->availableForWrite() < len) {
114  delay(10);
115  }
116  }
117 
118  size_t result = 0;;
119  switch(config.bits_per_sample){
120  case 8: {
121  result = buffer->writeArray(data, len);
122  } break;
123  case 16: {
124  size_t samples = len / 2;
125  int16_t *p16 = (int16_t*)data;
126  for (int j=0;j<samples;j++){
127  uint8_t sample = map(p16[j],-32768, 32767,0,255);
128  if (buffer->write(sample)){
129  result += 2;
130  } else {
131  break;
132  }
133  }
134  } break;
135  case 24: {
136  size_t samples = len / 3;
137  int24_t *p24 = (int24_t*)data;
138  for (int j=0;j<samples;j++){
139  uint8_t sample = map(p24[j],-8388608, 8388607,0,255);
140  if (buffer->write(sample)){
141  result += 3;
142  } else {
143  break;
144  }
145  }
146 
147  } break;
148  case 32: {
149  size_t samples = len / 4;
150  int32_t *p32 = (int32_t*)data;
151  for (int j=0;j<samples;j++){
152  uint8_t sample = map(p32[j],-2147483648, 2147483647,0,255);
153  if (buffer->write(sample)){
154  result += 4;
155  } else {
156  break;
157  }
158  }
159 
160  } break;
161  }
162 
163  // write data
164  return result * result_factor;
165  }
166 
167  protected:
168  AnalogConfigStd config;
169  TimerAlarmRepeating timer;
170  BaseBuffer<uint8_t> *buffer = nullptr;
171  int avg_value, min, max, count;
172  bool is_combined_channels = false;
173  uint16_t frame_size = 0;
174  int result_factor = 1;
175  int decim = 1;
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;
226  AnalogAudioArduino *self = (AnalogAudioArduino *)arg;
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  for (int j = 0; j < channels; j++) {
246  int16_t sample = self->buffer->read();
247  int pin = self->config.pins_data[j];
248  analogWrite(pin, sample);
249  //LOGW("analogWrite(%d, %d)", pin, sample);
250  }
251  }
252  }
253 
255  bool setupPins() {
256  TRACED();
257 
258  Pins& pins = config.pins();
259  if (pins.size()<config.channels){
260  LOGE("Only pins %d of %d defined", pins.size(), config.channels);
261  return false;
262  }
263 
264 
265  if (config.rx_tx_mode == RX_MODE) {
266  LOGI("rx start_pin: %d", config.start_pin);
267  // setup pins for read
268  for (int j = 0; j < config.channels; j++) {
269  int pin = config.pins_data [j];
270  pinMode(pin, INPUT);
271  LOGD("pinMode(%d, INPUT)", pin);
272  }
273 
274  if (config.is_auto_center_read) {
275  // calculate the avarage value to center the signal
276  for (int j = 0; j < 1024; j++) {
277  updateMinMax(analogRead(config.pins_data[0]));
278  }
279  LOGI("Avg Signal was %d", avg_value);
280  }
281  } else if (config.rx_tx_mode == TX_MODE) {
282  // setup pins for read
283  for (int j = 0; j < config.channels; j++) {
284  int pin = config.pins_data[j];
285  LOGI("tx pin %d: %d", j, pin);
286  pinMode(pin, OUTPUT);
287  LOGD("pinMode(%d, OUTPUT)", pin);
288  }
289  }
290  return true;
291  }
292 
293  void updateMinMax(int value) {
294  if (value < min) min = value;
295  if (value > max) max = value;
296  if (count++ == 1024) updateAvg();
297  }
298 
299  void updateAvg() {
300  avg_value = (max + min) / 2;
301  min = INT_MAX;
302  max = INT_MIN;
303  count = 0;
304  }
305 
309  return config.sample_rate >= config.max_sample_rate;
310  }
311 
312  // combined stereo channel to mono
313  bool isCombinedChannel() { return is_combined_channels; }
314 
316  int effectiveOutputSampleRate() { return config.sample_rate / decimation(); }
317 
318  int decimation() {
319  if (config.sample_rate <= config.max_sample_rate) return 1;
320  for (int j = 2; j < 6; j += 2) {
321  if (config.sample_rate / j <= config.max_sample_rate) {
322  return j;
323  }
324  }
325  return 6;
326  }
327 };
328 
329 } // namespace audio_tools
Analog Data IO using a timer and the Arduino analogRead() method and writing using analogWrite();.
Definition: AnalogAudioArduino.h:22
static void callback(void *arg)
Sample data and write to buffer.
Definition: AnalogAudioArduino.h:224
bool isDecimateActive()
Definition: AnalogAudioArduino.h:308
int effectiveOutputSampleRate()
Returns the effective output sample rate.
Definition: AnalogAudioArduino.h:316
size_t readBytes(uint8_t *data, size_t len) override
Provides the sampled audio data.
Definition: AnalogAudioArduino.h:78
bool setupPins()
pinmode input for defined analog pins
Definition: AnalogAudioArduino.h:255
AnalogConfigStd defaultConfig()
provides the default configuration
Definition: AnalogAudioArduino.h:27
bool begin() override
Reopen with last config.
Definition: AnalogAudioArduino.h:46
void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition: AnalogAudioArduino.h:32
Generic ADC and DAC configuration.
Definition: AnalogConfigStd.h:29
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
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
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: AudioConfig.h:823
long map(long x, long in_min, long in_max, long out_min, long out_max)
Maps input to output values.
Definition: NoArduino.h:162
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
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