arduino-audio-tools
AnalogAudioArduino.h
1 #pragma once
2 
3 #include "AudioConfig.h"
4 #if defined(USE_ANALOG_ARDUINO) || defined(DOXYGEN)
5 #include "AudioAnalog/AnalogAudioBase.h"
6 #include "AudioTimer/AudioTimer.h"
7 #include "AudioTools/AudioStreams.h"
8 #include "AudioTools/AudioTypes.h"
9 #include "AudioTools/Buffers.h"
10 #include <limits.h> // for INT_MIN and INT_MAX
11 
12 namespace audio_tools {
13 
23  public:
24  AnalogDriverArduino() = default;
25 
26  bool begin(AnalogConfig cfg) {
27  TRACED();
28  config = cfg;
29  if (config.rx_tx_mode == RXTX_MODE) {
30  LOGE("RXTX not supported");
31  return false;
32  }
33 
34  frame_size = config.channels*(config.bits_per_sample/8);
35  result_factor = 1;
36 
37  if (!setupTx()) return false;
38 
39  if (!setupBuffer()) return false;
40 
41  // (re)start timer
42  return setupTimer();
43  }
44 
45  void end() override { timer.end(); }
46 
47  int available() override {
48  if (config.rx_tx_mode == TX_MODE) return 0;
49  return buffer == nullptr ? 0 : buffer->available() * 2;
50  };
51 
53  size_t readBytes(uint8_t *values, size_t len) override {
54  if (config.rx_tx_mode == TX_MODE) return 0;
55  if (buffer == nullptr) return 0;
56  int bytes = len / frame_size * frame_size;
57  return buffer->readArray(values, bytes);
58  }
59 
60  int availableForWrite() override {
61  if (config.rx_tx_mode == RX_MODE) return 0;
62  if (buffer == nullptr) return 0;
63  return config.is_blocking_write ? config.buffer_size : buffer->availableForWrite();
64  }
65 
66  size_t write(const uint8_t *data, size_t len) override {
67  LOGD("write: %d", (int)len);
68  if (config.rx_tx_mode == RX_MODE) return 0;
69  // only process full frames
70  len = len / frame_size * frame_size;
71 
72  if (isCombinedChannel()){
73  ChannelReducer cr(1, 2, config.bits_per_sample);
74  len = cr.convert((uint8_t*)data, len);
75  LOGD("ChannelReducer len: %d", (int) len);
76  }
77 
78  if (isDecimateActive()){
79  Decimate dec(decim, 1, config.bits_per_sample);
80  len = dec.convert((uint8_t*)data, len);
81  LOGD("Decimate len: %d for factor %d", (int) len, decim);
82  }
83 
84  // blocking write ?
85  if (config.is_blocking_write) {
86  LOGD("Waiting for buffer to be available");
87  while (buffer->availableForWrite() < len) {
88  delay(10);
89  }
90  }
91 
92  // write data
93  size_t result = buffer->writeArray(data, len) * result_factor;
94  LOGD("write: -> %d / factor: %d", (int)result, result_factor);
95  return result;
96  }
97 
98  protected:
99  AnalogConfig config;
100  TimerAlarmRepeating timer;
101  BaseBuffer<uint8_t> *buffer = nullptr;
102  int avg_value, min, max, count;
103  bool is_combined_channels = false;
104  uint16_t frame_size = 0;
105  int result_factor = 1;
106  int decim = 1;
107 
108  bool setupTx(){
109  if (config.rx_tx_mode == TX_MODE) {
110  // check channels
111  if (config.channels>ANALOG_MAX_OUT_CHANNELS){
112  if (config.channels == 2){
113  is_combined_channels = true;
114  config.channels = 1;
115  } else {
116  LOGE("Unsupported channels");
117  return false;
118  }
119  }
120  if (isDecimateActive()){
121  LOGI("Using reduced sample rate: %d", effectiveOutputSampleRate());
122  decim = decimation();
123  result_factor = result_factor * decim;
124  }
125  if (isCombinedChannel()){
126  LOGI("Combining channels");
127  result_factor = result_factor * 2;
128  }
129  }
130  return true;
131  }
132 
133  bool setupBuffer(){
134  if (buffer == nullptr) {
135  // allocate buffer_count
136  buffer = new RingBuffer<uint8_t>(config.buffer_size * config.buffer_count);
137  if (buffer == nullptr) {
138  LOGE("Not enough memory for buffer");
139  return false;
140  }
141  // setup pins
142  setupPins();
143  }
144  return true;
145  }
146 
147  bool setupTimer(){
148  int sample_rate = config.rx_tx_mode == TX_MODE ? effectiveOutputSampleRate() : config.sample_rate;
149  LOGI("sample_rate: %d", sample_rate);
150  timer.setCallbackParameter(this);
151  return timer.begin(callback, sample_rate, TimeUnit::HZ);
152  }
153 
155  static void callback(void *arg) {
156  int16_t value = 0;
158  // prevent NPE
159  if (self->buffer == nullptr) return;
160 
161  // Logic for reading audio data
162  if (self->config.rx_tx_mode == RX_MODE) {
163  int channels = self->config.channels;
164  for (int j = 0; j < channels; j++) {
165  // provides value in range 0…4095
166  value = analogRead(self->config.start_pin + j);
167  if (self->config.is_auto_center_read){
168  self->updateMinMax(value);
169  }
170  value = (value - self->avg_value) * 16;
171  self->buffer->write(value);
172  }
173  // Logic for writing audio data
174  } else if (self->config.rx_tx_mode == TX_MODE) {
175  int channels = self->config.channels;
176  for (int j = 0; j < channels; j++) {
177  int16_t sample = self->buffer->read();
178  sample = map(sample, -32768, 32767, 0, 255);
179  int pin = self->config.start_pin + j;
180  analogWrite(pin, sample);
181  // LOGI("analogWrite(%d, %d)", pin, sample);
182  }
183  }
184  }
185 
187  void setupPins() {
188  TRACED();
189  if (config.rx_tx_mode == RX_MODE) {
190  LOGI("rx start_pin: %d", config.start_pin);
191  // setup pins for read
192  for (int j = 0; j < config.channels; j++) {
193  int pin = config.start_pin + j;
194  pinMode(pin, INPUT);
195  LOGD("pinMode(%d, INPUT)", pin);
196  }
197 
198  if (config.is_auto_center_read){
199  // calculate the avarage value to center the signal
200  for (int j = 0; j < 1024; j++) {
201  updateMinMax(analogRead(config.start_pin));
202  }
203  LOGI("Avg Signal was %d", avg_value);
204  }
205  } else if (config.rx_tx_mode == TX_MODE) {
206  LOGI("tx start_pin: %d", config.start_pin);
207  // setup pins for read
208  for (int j = 0; j < config.channels; j++) {
209  int pin = config.start_pin + j;
210  pinMode(pin, OUTPUT);
211  LOGD("pinMode(%d, OUTPUT)", pin);
212  }
213  }
214  }
215 
216  void updateMinMax(int value){
217  if (value<min) min = value;
218  if (value>max) max = value;
219  if (count++==1024) updateAvg();
220  }
221 
222  void updateAvg(){
223  avg_value = (max + min) / 2;
224  min = INT_MAX;
225  max = INT_MIN;
226  count = 0;
227  }
228 
231  return config.sample_rate >= ANALOG_MAX_SAMPLE_RATE;
232  }
233 
234  // combined stereo channel to mono
235  bool isCombinedChannel(){
236  return is_combined_channels;
237  }
238 
241  return config.sample_rate / decimation();
242  }
243 
244  int decimation() {
245  if (config.sample_rate <= ANALOG_MAX_SAMPLE_RATE) return 1;
246  for(int j=2;j<6;j+=2){
247  if (config.sample_rate/j <= ANALOG_MAX_SAMPLE_RATE) {
248  return j;
249  }
250  }
251  return 6;
252  }
253 
254 
255 
256 };
257 
259 
260 } // namespace audio_tools
261 
262 #endif
ESP32 specific configuration for i2s input via adc. The default input pin is GPIO34....
Definition: AnalogConfigESP32.h:21
Please use the AnalogAudioStream: Reading Analog Data using a timer and the Arduino analogRead() meth...
Definition: AnalogAudioArduino.h:22
static void callback(void *arg)
Sample data and write to buffer.
Definition: AnalogAudioArduino.h:155
bool isDecimateActive()
The requested sampling rate is too hight: we only process half of the samples so we can half the samp...
Definition: AnalogAudioArduino.h:230
void setupPins()
pinmode input for defined analog pins
Definition: AnalogAudioArduino.h:187
size_t readBytes(uint8_t *values, size_t len) override
Provides the sampled audio data.
Definition: AnalogAudioArduino.h:53
int effectiveOutputSampleRate()
Returns the effective output sample rate.
Definition: AnalogAudioArduino.h:240
Definition: AnalogAudioBase.h:13
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 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: AnalogAudio.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:161
void delay(uint32_t ms)
Waits for the indicated milliseconds.
Definition: Millis.h:11
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