arduino-audio-tools
PWMAudioBase.h
1 #pragma once
2 
3 #include "AudioConfig.h"
4 #ifdef USE_PWM
5 
6 #include "AudioTools/CoreAudio/AudioBasic/Collections.h"
7 #include "AudioTools.h"
8 #include "AudioTools/CoreAudio/AudioLogger.h"
9 #include "AudioTools/CoreAudio/AudioOutput.h"
10 #include "AudioTools/CoreAudio/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() || !cfg.equals(actual_info)) {
142  audio_config.logConfig();
143  setupPWM();
144  setupTimer();
145  actual_info = cfg;
146  }
147 
148  // reset class variables
149  underflow_count = 0;
150  underflow_per_second = 0;
151  frame_count = 0;
152  frames_per_second = 0;
153 
154  LOGI("->Buffer available: %d", buffer->available());
155  LOGI("->Buffer available for write: %d", buffer->availableForWrite());
156  LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false");
157  return true;
158  }
159 
160  virtual int availableForWrite() {
161  return is_blocking_write
162  ? audio_config.buffer_size
163  : buffer->availableForWrite() / frame_size * frame_size;
164  }
165 
166  // blocking write for an array: we expect a singed value and convert it into a
167  // unsigned
168  virtual size_t write(const uint8_t *data, size_t len) {
169  size_t size = len;
170 
171  // only allow full frame
172  size = (size / frame_size) * frame_size;
173  LOGD("adjusted size: %d", (int)size);
174 
175  if (isDecimateActive()) {
176  size = decimate.convert((uint8_t *)data, size);
177  LOGD("decimated size: %d", (int)size);
178  }
179 
180  if (is_blocking_write && buffer->availableForWrite() < size) {
181  LOGD("Waiting for buffer to be available");
182  while (buffer->availableForWrite() < size) delay(5);
183  } else {
184  size = min((size_t)availableForWrite(), size);
185  }
186 
187  size_t result = buffer->writeArray(data, size);
188  if (result != size) {
189  LOGW("Could not write all data: %u -> %d", (unsigned int)size, result);
190  }
191  // activate the timer now - if not already done
192  if (!is_timer_started) startTimer();
193  return result * decimation();
194  }
195 
196  // When the timer does not have enough data we increase the underflow_count;
197  uint32_t underflowsPerSecond() { return underflow_per_second; }
198  // provides the effectivly measured output frames per second
199  uint32_t framesPerSecond() { return frames_per_second; }
200 
201  inline void updateStatistics() {
202  frame_count++;
203  if (millis() >= time_1_sec) {
204  time_1_sec = millis() + 1000;
205  frames_per_second = frame_count;
206  underflow_per_second = underflow_count;
207  underflow_count = 0;
208  frame_count = 0;
209  }
210  }
211 
212  bool isTimerStarted() { return is_timer_started; }
213 
214  virtual void setupPWM() = 0;
215  virtual void setupTimer() = 0;
216  virtual void startTimer() = 0;
217  virtual int maxChannels() = 0;
218  virtual int maxOutputValue() = 0;
219  virtual void end() {};
220 
221  virtual void pwmWrite(int channel, int value) = 0;
222 
225  void setBuffer(BaseBuffer<uint8_t> *buffer) { this->buffer = buffer; }
226 
227  protected:
228  PWMConfig audio_config;
229  AudioInfo actual_info;
230  BaseBuffer<uint8_t> *buffer = nullptr;
231  uint32_t underflow_count = 0;
232  uint32_t underflow_per_second = 0;
233  uint32_t frame_count = 0;
234  uint32_t frames_per_second = 0;
235  uint32_t frame_size = 0;
236  uint32_t time_1_sec;
237  bool is_timer_started = false;
238  bool is_blocking_write = true;
239  Decimate decimate;
240 
241  void deleteBuffer() {
242  // delete buffer if necessary
243  if (buffer != nullptr) {
244  delete buffer;
245  buffer = nullptr;
246  }
247  }
248 
250  void playNextFrame() {
251  if (isTimerStarted() && buffer != nullptr) {
252  // TRACED();
253  int required = (audio_config.bits_per_sample / 8) * audio_config.channels;
254  if (buffer->available() >= required) {
255  for (int j = 0; j < audio_config.channels; j++) {
256  int value = nextValue();
257  pwmWrite(j, value);
258  }
259  } else {
260  underflow_count++;
261  }
262  updateStatistics();
263  }
264  }
265 
267  virtual int nextValue() {
268  int result = 0;
269  switch (audio_config.bits_per_sample) {
270  case 8: {
271  int16_t value = buffer->read();
272  if (value < 0) {
273  LOGE(READ_ERROR_MSG);
274  value = 0;
275  }
276  result = map(value, -NumberConverter::maxValue(8),
277  NumberConverter::maxValue(8), 0, maxOutputValue());
278  break;
279  }
280  case 16: {
281  int16_t value;
282  if (buffer->readArray((uint8_t *)&value, 2) != 2) {
283  LOGE(READ_ERROR_MSG);
284  }
285  result = map(value, -NumberConverter::maxValue(16),
286  NumberConverter::maxValue(16), 0, maxOutputValue());
287  break;
288  }
289  case 24: {
290  int24_t value;
291  if (buffer->readArray((uint8_t *)&value, 3) != 3) {
292  LOGE(READ_ERROR_MSG);
293  }
294  result = map((int32_t)value, -NumberConverter::maxValue(24),
295  NumberConverter::maxValue(24), 0, maxOutputValue());
296  break;
297  }
298  case 32: {
299  int32_t value;
300  if (buffer->readArray((uint8_t *)&value, 4) != 4) {
301  LOGE(READ_ERROR_MSG);
302  }
303  result = map(value, -NumberConverter::maxValue(32),
304  NumberConverter::maxValue(32), 0, maxOutputValue());
305  break;
306  }
307  }
308  return result;
309  }
310 
312  virtual int maxSampleRate() { return 48000; }
313 
316  virtual bool isDecimateActive() {
317  return audio_config.sample_rate >= ANALOG_MAX_SAMPLE_RATE;
318  }
319 
322  return audio_config.sample_rate / decimation();
323  }
324 
326  virtual int decimation() { return 1; }
327 };
328 
329 } // namespace audio_tools
330 
331 #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 taking a sample at every factor location (ingoring factor-1 sampl...
Definition: BaseConverter.h:552
void setFactor(int factor)
Sets the factor: e.g. with 4 we keep every forth sample.
Definition: BaseConverter.h:564
void setChannels(int channels)
Defines the number of channels.
Definition: BaseConverter.h:561
Base Class for all PWM drivers.
Definition: PWMAudioBase.h:105
virtual int effectiveOutputSampleRate()
Provides the effective sample rate.
Definition: PWMAudioBase.h:321
void playNextFrame()
writes the next frame to the output pins
Definition: PWMAudioBase.h:250
void setBuffer(BaseBuffer< uint8_t > *buffer)
Definition: PWMAudioBase.h:225
virtual int maxSampleRate()
Provides the max working sample rate.
Definition: PWMAudioBase.h:312
virtual int nextValue()
determines the next scaled value
Definition: PWMAudioBase.h:267
virtual int decimation()
Decimation factor to reduce the sample rate.
Definition: PWMAudioBase.h:326
virtual bool isDecimateActive()
Definition: PWMAudioBase.h:316
static int64_t maxValue(int value_bits_per_sample)
provides the biggest number for the indicated number of bits
Definition: AudioTypes.h:317
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: AudioConfig.h:868
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
void defaultPWMAudioOutputCallback()
Definition: PWMAudioAVR.h:131
uint32_t millis()
Returns the milliseconds since the start.
Definition: Time.h:12
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
bool equals(AudioInfo alt)
Returns true if alt values are the same like the current values.
Definition: AudioTypes.h:85
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
Configuration data for PWM audio output.
Definition: PWMAudioBase.h:32