arduino-audio-tools
PWMAudioBase.h
1 #pragma once
2 
3 #include "AudioConfig.h"
4 #ifdef USE_PWM
5 
6 #include "AudioBasic/Collections.h"
7 #include "AudioTools.h"
8 #include "AudioTools/AudioLogger.h"
9 #include "AudioTools/AudioOutput.h"
10 #include "AudioTools/AudioTypes.h"
11 
12 #define READ_ERROR_MSG "Could not read full data"
13 
14 namespace audio_tools {
15 
16 // forward declarations
17 // Callback for user
18 typedef bool (*PWMCallbackType)(uint8_t channels, int16_t *data);
19 // Callback used by system
20 static void defaultPWMAudioOutputCallback();
21 // Driver classes
22 class PWMDriverESP32;
23 class PWMDriverRP2040;
24 class PWMDriverMBED;
25 class PWMDriverSTM32;
26 
32 struct PWMConfig : public AudioInfo {
33  PWMConfig() {
34  // default basic information
35  sample_rate = 8000u; // sample rate in Hz
36  channels = 1;
37  bits_per_sample = 16;
38  }
39 
40  // basic pwm information
41  uint16_t buffer_size = PWM_BUFFER_SIZE;
42  uint8_t buffers = PWM_BUFFER_COUNT;
43 
44  // additinal info which might not be used by all processors
45  uint32_t pwm_frequency = PWM_AUDIO_FREQUENCY; // audable range is from 20 to
46  // 20,000Hz (not used by ESP32)
47  uint8_t resolution = 8; // Only used by ESP32: must be between 8 and 11 ->
48  // drives pwm frequency
49  uint8_t timer_id = 0; // Only used by ESP32 must be between 0 and 3
50 
51 #ifndef __AVR__
52  uint16_t start_pin = PIN_PWM_START;
53 
55  template <typename T, int N>
56  void setPins(T (&a)[N]) {
57  pins_data.clear();
58  pins_data.resize(N);
59  for (int i = 0; i < N; ++i) {
60  pins_data[i] = a[i]; // reset all elements
61  }
62  }
63 
66  void setPins(Pins &pins) {
67  pins_data.clear();
68  for (int i = 0; i < pins.size(); i++) {
69  pins_data.push_back(pins[i]);
70  }
71  }
72 
74  Pins &pins() {
75  if (pins_data.size() == 0) {
76  pins_data.resize(channels);
77  for (int j = 0; j < channels; j++) {
78  pins_data[j] = start_pin + j;
79  }
80  }
81  return pins_data;
82  }
83 
84 #endif
85 
86  void logConfig() {
87  LOGI("sample_rate: %d", (int) sample_rate);
88  LOGI("channels: %d", channels);
89  LOGI("bits_per_sample: %u", bits_per_sample);
90  LOGI("buffer_size: %u", buffer_size);
91  LOGI("buffer_count: %u", buffers);
92  LOGI("pwm_frequency: %u", (unsigned)pwm_frequency);
93  LOGI("resolution: %d", resolution);
94  // LOGI("timer_id: %d", timer_id);
95  }
96 
97  protected:
98  Pins pins_data;
99 };
100 
106  public:
107  DriverPWMBase() = default;
108  virtual ~DriverPWMBase() { end(); }
109 
110  PWMConfig &audioInfo() { return audio_config; }
111 
112  virtual PWMConfig defaultConfig() {
113  PWMConfig cfg;
114  return cfg;
115  }
116 
117  // restart with prior definitions
118  bool begin(PWMConfig cfg) {
119  TRACED();
120  audio_config = cfg;
121  decimate.setChannels(cfg.channels);
122  decimate.setBits(cfg.bits_per_sample);
123  decimate.setFactor(decimation());
124  LOGI("decimation: %d", decimation());
125  frame_size = audio_config.channels * (audio_config.bits_per_sample / 8);
126  if (audio_config.channels > maxChannels()) {
127  LOGE("Only max %d channels are supported!", maxChannels());
128  return false;
129  }
130 
131  if (buffer == nullptr) {
132  LOGI("->Allocating new buffer %d * %d bytes", audio_config.buffers,
133  audio_config.buffer_size);
134  // buffer = new NBuffer<uint8_t>(audio_config.buffer_size,
135  // audio_config.buffers);
136  buffer = new RingBuffer<uint8_t>(audio_config.buffer_size *
137  audio_config.buffers);
138  }
139 
140  // initialize if necessary
141  if (!isTimerStarted()) {
142  audio_config.logConfig();
143  setupPWM();
144  setupTimer();
145  }
146 
147  // reset class variables
148  underflow_count = 0;
149  underflow_per_second = 0;
150  frame_count = 0;
151  frames_per_second = 0;
152 
153  LOGI("->Buffer available: %d", buffer->available());
154  LOGI("->Buffer available for write: %d", buffer->availableForWrite());
155  LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false");
156  return true;
157  }
158 
159  virtual int availableForWrite() {
160  return is_blocking_write
161  ? audio_config.buffer_size
162  : buffer->availableForWrite() / frame_size * frame_size;
163  }
164 
165  // blocking write for an array: we expect a singed value and convert it into a
166  // unsigned
167  virtual size_t write(const uint8_t *wrt_buffer, size_t bytes) {
168  size_t size = bytes;
169 
170  // only allow full frame
171  size = (size / frame_size) * frame_size;
172  LOGD("adjusted size: %d", (int)size);
173 
174  if (isDecimateActive()) {
175  size = decimate.convert((uint8_t *)wrt_buffer, size);
176  LOGD("decimated size: %d", (int)size);
177  }
178 
179  if (is_blocking_write && buffer->availableForWrite() < size) {
180  LOGD("Waiting for buffer to be available");
181  while (buffer->availableForWrite() < size) delay(5);
182  } else {
183  size = min((size_t)availableForWrite(), size);
184  }
185 
186  size_t result = buffer->writeArray(wrt_buffer, size);
187  if (result != size) {
188  LOGW("Could not write all data: %u -> %d", (unsigned int)size, result);
189  }
190  // activate the timer now - if not already done
191  startTimer();
192  return result * decimation();
193  }
194 
195  // When the timer does not have enough data we increase the underflow_count;
196  uint32_t underflowsPerSecond() { return underflow_per_second; }
197  // provides the effectivly measured output frames per second
198  uint32_t framesPerSecond() { return frames_per_second; }
199 
200  inline void updateStatistics() {
201  frame_count++;
202  if (millis() >= time_1_sec) {
203  time_1_sec = millis() + 1000;
204  frames_per_second = frame_count;
205  underflow_per_second = underflow_count;
206  underflow_count = 0;
207  frame_count = 0;
208  }
209  }
210 
211  bool isTimerStarted() { return is_timer_started; }
212 
213  virtual void setupPWM() = 0;
214  virtual void setupTimer() = 0;
215  virtual void startTimer() = 0;
216  virtual int maxChannels() = 0;
217  virtual int maxOutputValue() = 0;
218  virtual void end() {};
219 
220  virtual void pwmWrite(int channel, int value) = 0;
221 
224  void setBuffer(BaseBuffer<uint8_t> *buffer) { this->buffer = buffer; }
225 
226  protected:
227  PWMConfig audio_config;
228  BaseBuffer<uint8_t> *buffer = nullptr;
229  uint32_t underflow_count = 0;
230  uint32_t underflow_per_second = 0;
231  uint32_t frame_count = 0;
232  uint32_t frames_per_second = 0;
233  uint32_t frame_size = 0;
234  uint32_t time_1_sec;
235  bool is_timer_started = false;
236  bool is_blocking_write = true;
237  Decimate decimate;
238 
239  void deleteBuffer() {
240  // delete buffer if necessary
241  if (buffer != nullptr) {
242  delete buffer;
243  buffer = nullptr;
244  }
245  }
246 
248  void playNextFrame() {
249  if (isTimerStarted() && buffer != nullptr) {
250  // TRACED();
251  int required = (audio_config.bits_per_sample / 8) * audio_config.channels;
252  if (buffer->available() >= required) {
253  for (int j = 0; j < audio_config.channels; j++) {
254  int value = nextValue();
255  pwmWrite(j, value);
256  }
257  } else {
258  underflow_count++;
259  }
260  updateStatistics();
261  }
262  }
263 
265  virtual int nextValue() {
266  int result = 0;
267  switch (audio_config.bits_per_sample) {
268  case 8: {
269  int16_t value = buffer->read();
270  if (value < 0) {
271  LOGE(READ_ERROR_MSG);
272  value = 0;
273  }
274  result = map(value, -NumberConverter::maxValue(8),
275  NumberConverter::maxValue(8), 0, maxOutputValue());
276  break;
277  }
278  case 16: {
279  int16_t value;
280  if (buffer->readArray((uint8_t *)&value, 2) != 2) {
281  LOGE(READ_ERROR_MSG);
282  }
283  result = map(value, -NumberConverter::maxValue(16),
284  NumberConverter::maxValue(16), 0, maxOutputValue());
285  break;
286  }
287  case 24: {
288  int24_t value;
289  if (buffer->readArray((uint8_t *)&value, 3) != 3) {
290  LOGE(READ_ERROR_MSG);
291  }
292  result = map((int32_t)value, -NumberConverter::maxValue(24),
293  NumberConverter::maxValue(24), 0, maxOutputValue());
294  break;
295  }
296  case 32: {
297  int32_t value;
298  if (buffer->readArray((uint8_t *)&value, 4) != 4) {
299  LOGE(READ_ERROR_MSG);
300  }
301  result = map(value, -NumberConverter::maxValue(32),
302  NumberConverter::maxValue(32), 0, maxOutputValue());
303  break;
304  }
305  }
306  return result;
307  }
308 
310  virtual int maxSampleRate() { return 48000; }
311 
314  virtual bool isDecimateActive() {
315  return audio_config.sample_rate >= ANALOG_MAX_SAMPLE_RATE;
316  }
317 
320  return audio_config.sample_rate / decimation();
321  }
322 
324  virtual int decimation() { return 1; }
325 };
326 
327 } // namespace audio_tools
328 
329 #endif
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 T read()=0
reads a single value
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
Provides a reduced sampling rate by ignoring a defined rate of samples.
Definition: BaseConverter.h:531
void setFactor(int factor)
Sets the factor: e.g. with 4 we keep every forth sample.
Definition: BaseConverter.h:543
void setChannels(int channels)
Defines the number of channels.
Definition: BaseConverter.h:540
Base Class for all PWM drivers.
Definition: PWMAudioBase.h:105
virtual int effectiveOutputSampleRate()
Provides the effective sample rate.
Definition: PWMAudioBase.h:319
void playNextFrame()
writes the next frame to the output pins
Definition: PWMAudioBase.h:248
void setBuffer(BaseBuffer< uint8_t > *buffer)
Definition: PWMAudioBase.h:224
virtual int maxSampleRate()
Provides the max working sample rate.
Definition: PWMAudioBase.h:310
virtual int nextValue()
determines the next scaled value
Definition: PWMAudioBase.h:265
virtual int decimation()
Decimation factor to reduce the sample rate.
Definition: PWMAudioBase.h:324
virtual bool isDecimateActive()
Definition: PWMAudioBase.h:314
static int64_t maxValue(int value_bits_per_sample)
provides the biggest number for the indicated number of bits
Definition: AudioTypes.h:330
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition: Int24_4bytes_t.h:16
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 defaultPWMAudioOutputCallback()
Definition: PWMAudioAVR.h:131
void delay(uint32_t ms)
Waits for the indicated milliseconds.
Definition: Millis.h:11
uint32_t millis()
Returns the milliseconds since the start.
Definition: Millis.h:18
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
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
Configuration data for PWM audio output.
Definition: PWMAudioBase.h:32