arduino-audio-tools
Loading...
Searching...
No Matches
PWMDriverBase.h
1#pragma once
2
3#include "AudioToolsConfig.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
14namespace audio_tools {
15
16// forward declarations
17// Callback for user
18typedef bool (*PWMCallbackType)(uint8_t channels, int16_t *data);
19// Callback used by system
21// Driver classes
22class PWMDriverESP32;
23class PWMDriverRP2040;
24class PWMDriverMBED;
25class PWMDriverSTM32;
26
32struct 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
41 uint16_t buffer_size = PWM_BUFFER_SIZE;
43 uint8_t buffers = PWM_BUFFER_COUNT;
44
46 uint32_t pwm_frequency = 0; // audable range is from 20 to
48 uint8_t resolution = 8;
50 uint8_t timer_id = 0;
51
53 uint16_t dead_time_us = 0;
54
56 uint32_t max_sample_rate = PWM_MAX_SAMPLE_RATE;
57
58#ifndef __AVR__
60 uint16_t start_pin = PIN_PWM_START;
61
63 template <typename T, int N>
64 void setPins(T (&a)[N]) {
65 pins_data.clear();
66 pins_data.resize(N);
67 for (int i = 0; i < N; ++i) {
68 pins_data[i] = a[i]; // reset all elements
69 }
70 }
71
74 void setPins(Pins &pins) {
75 pins_data.clear();
76 for (int i = 0; i < pins.size(); i++) {
77 pins_data.push_back(pins[i]);
78 }
79 }
80
82 Pins &pins() {
83 if (pins_data.size() == 0) {
84 pins_data.resize(channels);
85 for (int j = 0; j < channels; j++) {
86 pins_data[j] = start_pin + j;
87 }
88 }
89 return pins_data;
90 }
91
92#endif
93
94 void logConfig() {
95 LOGI("sample_rate: %d", (int) sample_rate);
96 LOGI("channels: %d", channels);
97 LOGI("bits_per_sample: %u", bits_per_sample);
98 LOGI("buffer_size: %u", buffer_size);
99 LOGI("buffer_count: %u", buffers);
100 LOGI("pwm_frequency: %u", (unsigned)pwm_frequency);
101 LOGI("resolution: %d", resolution);
102 LOGI("dead_time_us: %u", dead_time_us);
103 // LOGI("timer_id: %d", timer_id);
104 }
105
106 protected:
107 Pins pins_data;
108};
109
115 public:
116 DriverPWMBase() = default;
117 virtual ~DriverPWMBase() { end(); }
118
119 PWMConfig &audioInfo() { return audio_config; }
120
121 virtual PWMConfig defaultConfig() {
122 PWMConfig cfg;
123 return cfg;
124 }
125
126 // restart with prior definitions
127 bool begin(PWMConfig cfg) {
128 TRACEI();
129
130 decimation_factor = 0;
131 audio_config = cfg;
132 decimate.setChannels(cfg.channels);
133 decimate.setBits(cfg.bits_per_sample);
134 decimate.setFactor(decimation());
135 frame_size = audio_config.channels * (audio_config.bits_per_sample / 8);
136 if (audio_config.channels > maxChannels()) {
137 LOGE("Only max %d channels are supported!", maxChannels());
138 return false;
139 }
140
141 if (buffer == nullptr) {
142 LOGI("->Allocating new buffer %d * %d bytes", audio_config.buffers,
143 audio_config.buffer_size);
144 // buffer = new NBuffer<uint8_t>(audio_config.buffer_size,
145 // audio_config.buffers);
146 buffer = new RingBuffer<uint8_t>(audio_config.buffer_size *
147 audio_config.buffers);
148 }
149
150 // initialize if necessary
151 if (!isTimerStarted() || !cfg.equals(actual_info)) {
152 audio_config.logConfig();
153 actual_info = cfg;
154 setupPWM();
155 setupTimer();
156 }
157
158 // reset class variables
159 underflow_count = 0;
160 underflow_per_second = 0;
161 frame_count = 0;
162 frames_per_second = 0;
163
164 LOGI("->Buffer available: %d", buffer->available());
165 LOGI("->Buffer available for write: %d", buffer->availableForWrite());
166 LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false");
167 return true;
168 }
169
170 virtual int availableForWrite() {
171 // return is_blocking_write
172 // ? audio_config.buffer_size
173 // : buffer->availableForWrite() / frame_size * frame_size;
174 // we must not write anything bigger then the buffer size
175 return buffer->size() / frame_size * frame_size;
176 }
177
178 // blocking write for an array: we expect a singed value and convert it into a
179 // unsigned
180 virtual size_t write(const uint8_t *data, size_t len) {
181 size_t size = len;
182
183 // only allow full frame
184 size = (size / frame_size) * frame_size;
185 LOGD("adjusted size: %d", (int)size);
186
187 if (isDecimateActive()) {
188 size = decimate.convert((uint8_t *)data, size);
189 LOGD("decimated size: %d", (int)size);
190 }
191
192 if (is_blocking_write && buffer->availableForWrite() < size) {
193 LOGD("Waiting for buffer to be available");
194 while (buffer->availableForWrite() < size) delay(5);
195 } else {
196 size = min((size_t)availableForWrite(), size);
197 }
198
199 size_t result = buffer->writeArray(data, size);
200 if (result != size) {
201 LOGW("Could not write all data: %u -> %d", (unsigned)size, result);
202 }
203 // activate the timer now - if not already done
204 if (!is_timer_started) startTimer();
205
206 // adjust the result by the descimation
207 if (isDecimateActive()) {
208 result = result * decimation();
209 }
210 //LOGD("write %u -> %u", len, result);
211 return result;
212 }
213
214 // When the timer does not have enough data we increase the underflow_count;
215 uint32_t underflowsPerSecond() { return underflow_per_second; }
216 // provides the effectivly measured output frames per second
217 uint32_t framesPerSecond() { return frames_per_second; }
218
219 inline void updateStatistics() {
220 frame_count++;
221 if (millis() >= time_1_sec) {
222 time_1_sec = millis() + 1000;
223 frames_per_second = frame_count;
224 underflow_per_second = underflow_count;
225 underflow_count = 0;
226 frame_count = 0;
227 }
228 }
229
230 bool isTimerStarted() { return is_timer_started; }
231
232 virtual void setupPWM() = 0;
233 virtual void setupTimer() = 0;
234 virtual void startTimer() = 0;
235 virtual int maxChannels() = 0;
236 virtual int maxOutputValue() = 0;
237 virtual void end() {};
238
239 virtual void pwmWrite(int channel, int value) = 0;
240
243 void setBuffer(BaseBuffer<uint8_t> *buffer) { this->buffer = buffer; }
244
247 return audio_config.sample_rate / decimation();
248 }
249
250 protected:
251 PWMConfig audio_config;
252 AudioInfo actual_info;
253 BaseBuffer<uint8_t> *buffer = nullptr;
254 uint32_t underflow_count = 0;
255 uint32_t underflow_per_second = 0;
256 uint32_t frame_count = 0;
257 uint32_t frames_per_second = 0;
258 uint32_t frame_size = 0;
259 uint32_t time_1_sec;
260 bool is_timer_started = false;
261 bool is_blocking_write = true;
262 Decimate decimate;
263 int decimation_factor = 0;
264
265 void deleteBuffer() {
266 // delete buffer if necessary
267 if (buffer != nullptr) {
268 delete buffer;
269 buffer = nullptr;
270 }
271 }
272
275 if (isTimerStarted() && buffer != nullptr) {
276 // TRACED();
277 int required = (audio_config.bits_per_sample / 8) * audio_config.channels;
278 if (buffer->available() >= required) {
279 for (int j = 0; j < audio_config.channels; j++) {
280 int value = nextValue();
281 pwmWrite(j, value);
282 }
283 } else {
284 underflow_count++;
285 }
286 updateStatistics();
287 }
288 }
289
291 virtual int nextValue() {
292 int result = 0;
293 switch (audio_config.bits_per_sample) {
294 case 8: {
295 int8_t value;
296 if (buffer->readArray((uint8_t *)&value, 1) != 1) {
297 LOGE(READ_ERROR_MSG);
298 }
299 result = map(value, -NumberConverter::maxValue(8),
300 NumberConverter::maxValue(8), 0, maxOutputValue());
301 break;
302 }
303 case 16: {
304 int16_t value;
305 if (buffer->readArray((uint8_t *)&value, 2) != 2) {
306 LOGE(READ_ERROR_MSG);
307 }
308 result = map(value, -NumberConverter::maxValue(16),
309 NumberConverter::maxValue(16), 0, maxOutputValue());
310 break;
311 }
312 case 24: {
313 int24_t value;
314 if (buffer->readArray((uint8_t *)&value, 3) != 3) {
315 LOGE(READ_ERROR_MSG);
316 }
317 result = map((int32_t)value, -NumberConverter::maxValue(24),
318 NumberConverter::maxValue(24), 0, maxOutputValue());
319 break;
320 }
321 case 32: {
322 int32_t value;
323 if (buffer->readArray((uint8_t *)&value, 4) != 4) {
324 LOGE(READ_ERROR_MSG);
325 }
326 result = map(value, -NumberConverter::maxValue(32),
327 NumberConverter::maxValue(32), 0, maxOutputValue());
328 break;
329 }
330 }
331 return result;
332 }
333
335 virtual int maxSampleRate() { return audio_config.max_sample_rate; }
336
339 virtual bool isDecimateActive() {
340 return audio_config.sample_rate >= audio_config.max_sample_rate;
341 }
342
344 virtual int decimation() {
345 if (decimation_factor == 0){
346 for (int j = 1; j < 20; j++){
347 if (audio_config.sample_rate / j <= audio_config.max_sample_rate){
348 decimation_factor = j;
349 LOGI("Decimation factor: %d" ,j);
350 return j;
351 }
352 }
353 decimation_factor = 1;
354 LOGI("Decimation factor: %d", (int)decimation_factor);
355 }
356
357 return decimation_factor;
358 }
359};
360
361} // namespace audio_tool
362
363#endif
Shared functionality of all buffers.
Definition Buffers.h:22
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:33
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
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:565
void setFactor(int factor)
Sets the factor: e.g. with 4 we keep every forth sample.
Definition BaseConverter.h:577
void setChannels(int channels)
Defines the number of channels.
Definition BaseConverter.h:574
Base Class for all PWM drivers.
Definition PWMDriverBase.h:114
virtual int effectiveOutputSampleRate()
Provides the effective sample rate.
Definition PWMDriverBase.h:246
void playNextFrame()
writes the next frame to the output pins
Definition PWMDriverBase.h:274
void setBuffer(BaseBuffer< uint8_t > *buffer)
Definition PWMDriverBase.h:243
virtual int maxSampleRate()
Provides the max working sample rate.
Definition PWMDriverBase.h:335
virtual int nextValue()
determines the next scaled value
Definition PWMDriverBase.h:291
virtual int decimation()
Decimation factor to reduce the sample rate.
Definition PWMDriverBase.h:344
virtual bool isDecimateActive()
Definition PWMDriverBase.h:339
static int64_t maxValue(int value_bits_per_sample)
provides the biggest number for the indicated number of bits
Definition AudioTypes.h:299
Implements a typed Ringbuffer.
Definition Buffers.h:336
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition Int24_4bytes_t.h:16
Vector< int > Pins
Pins.
Definition AudioTypes.h:524
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
void defaultPWMAudioOutputCallback()
Definition PWMDriverAVR.h:132
long map(long x, long in_min, long in_max, long out_min, long out_max)
Maps input to output values.
Definition NoArduino.h:189
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
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 PWMDriverBase.h:32
uint8_t resolution
Only used by ESP32: must be between 8 and 11 -> drives pwm frequency // 20,000Hz (not used by ESP32)
Definition PWMDriverBase.h:48
uint8_t timer_id
Timer used: Only used by ESP32 must be between 0 and 3
Definition PWMDriverBase.h:50
uint32_t pwm_frequency
additinal info which might not be used by all processors
Definition PWMDriverBase.h:46
uint16_t dead_time_us
Dead time in microseconds for symmetric PWM (ESP32 only)
Definition PWMDriverBase.h:53
uint32_t max_sample_rate
max sample sample rate that still produces good audio
Definition PWMDriverBase.h:56
uint16_t buffer_size
size of an inidividual buffer
Definition PWMDriverBase.h:41
uint8_t buffers
number of buffers
Definition PWMDriverBase.h:43