arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
PWMAudioBase.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 uint32_t max_sample_rate = PWM_MAX_SAMPLE_RATE;
54
55#ifndef __AVR__
57 uint16_t start_pin = PIN_PWM_START;
58
60 template <typename T, int N>
61 void setPins(T (&a)[N]) {
62 pins_data.clear();
63 pins_data.resize(N);
64 for (int i = 0; i < N; ++i) {
65 pins_data[i] = a[i]; // reset all elements
66 }
67 }
68
71 void setPins(Pins &pins) {
72 pins_data.clear();
73 for (int i = 0; i < pins.size(); i++) {
74 pins_data.push_back(pins[i]);
75 }
76 }
77
79 Pins &pins() {
80 if (pins_data.size() == 0) {
81 pins_data.resize(channels);
82 for (int j = 0; j < channels; j++) {
83 pins_data[j] = start_pin + j;
84 }
85 }
86 return pins_data;
87 }
88
89#endif
90
91 void logConfig() {
92 LOGI("sample_rate: %d", (int) sample_rate);
93 LOGI("channels: %d", channels);
94 LOGI("bits_per_sample: %u", bits_per_sample);
95 LOGI("buffer_size: %u", buffer_size);
96 LOGI("buffer_count: %u", buffers);
97 LOGI("pwm_frequency: %u", (unsigned)pwm_frequency);
98 LOGI("resolution: %d", resolution);
99 // LOGI("timer_id: %d", timer_id);
100 }
101
102 protected:
103 Pins pins_data;
104};
105
111 public:
112 DriverPWMBase() = default;
113 virtual ~DriverPWMBase() { end(); }
114
115 PWMConfig &audioInfo() { return audio_config; }
116
117 virtual PWMConfig defaultConfig() {
118 PWMConfig cfg;
119 return cfg;
120 }
121
122 // restart with prior definitions
123 bool begin(PWMConfig cfg) {
124 TRACEI();
125
126 decimation_factor = 0;
127 audio_config = cfg;
128 decimate.setChannels(cfg.channels);
129 decimate.setBits(cfg.bits_per_sample);
130 decimate.setFactor(decimation());
131 frame_size = audio_config.channels * (audio_config.bits_per_sample / 8);
132 if (audio_config.channels > maxChannels()) {
133 LOGE("Only max %d channels are supported!", maxChannels());
134 return false;
135 }
136
137 if (buffer == nullptr) {
138 LOGI("->Allocating new buffer %d * %d bytes", audio_config.buffers,
139 audio_config.buffer_size);
140 // buffer = new NBuffer<uint8_t>(audio_config.buffer_size,
141 // audio_config.buffers);
142 buffer = new RingBuffer<uint8_t>(audio_config.buffer_size *
143 audio_config.buffers);
144 }
145
146 // initialize if necessary
147 if (!isTimerStarted() || !cfg.equals(actual_info)) {
148 audio_config.logConfig();
149 actual_info = cfg;
150 setupPWM();
151 setupTimer();
152 }
153
154 // reset class variables
155 underflow_count = 0;
156 underflow_per_second = 0;
157 frame_count = 0;
158 frames_per_second = 0;
159
160 LOGI("->Buffer available: %d", buffer->available());
161 LOGI("->Buffer available for write: %d", buffer->availableForWrite());
162 LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false");
163 return true;
164 }
165
166 virtual int availableForWrite() {
167 // return is_blocking_write
168 // ? audio_config.buffer_size
169 // : buffer->availableForWrite() / frame_size * frame_size;
170 // we must not write anything bigger then the buffer size
171 return buffer->size() / frame_size * frame_size;
172 }
173
174 // blocking write for an array: we expect a singed value and convert it into a
175 // unsigned
176 virtual size_t write(const uint8_t *data, size_t len) {
177 size_t size = len;
178
179 // only allow full frame
180 size = (size / frame_size) * frame_size;
181 LOGD("adjusted size: %d", (int)size);
182
183 if (isDecimateActive()) {
184 size = decimate.convert((uint8_t *)data, size);
185 LOGD("decimated size: %d", (int)size);
186 }
187
188 if (is_blocking_write && buffer->availableForWrite() < size) {
189 LOGD("Waiting for buffer to be available");
190 while (buffer->availableForWrite() < size) delay(5);
191 } else {
192 size = min((size_t)availableForWrite(), size);
193 }
194
195 size_t result = buffer->writeArray(data, size);
196 if (result != size) {
197 LOGW("Could not write all data: %u -> %d", (unsigned)size, result);
198 }
199 // activate the timer now - if not already done
200 if (!is_timer_started) startTimer();
201
202 // adjust the result by the descimation
203 if (isDecimateActive()) {
204 result = result * decimation();
205 }
206 //LOGD("write %u -> %u", len, result);
207 return result;
208 }
209
210 // When the timer does not have enough data we increase the underflow_count;
211 uint32_t underflowsPerSecond() { return underflow_per_second; }
212 // provides the effectivly measured output frames per second
213 uint32_t framesPerSecond() { return frames_per_second; }
214
215 inline void updateStatistics() {
216 frame_count++;
217 if (millis() >= time_1_sec) {
218 time_1_sec = millis() + 1000;
219 frames_per_second = frame_count;
220 underflow_per_second = underflow_count;
221 underflow_count = 0;
222 frame_count = 0;
223 }
224 }
225
226 bool isTimerStarted() { return is_timer_started; }
227
228 virtual void setupPWM() = 0;
229 virtual void setupTimer() = 0;
230 virtual void startTimer() = 0;
231 virtual int maxChannels() = 0;
232 virtual int maxOutputValue() = 0;
233 virtual void end() {};
234
235 virtual void pwmWrite(int channel, int value) = 0;
236
239 void setBuffer(BaseBuffer<uint8_t> *buffer) { this->buffer = buffer; }
240
243 return audio_config.sample_rate / decimation();
244 }
245
246 protected:
247 PWMConfig audio_config;
248 AudioInfo actual_info;
249 BaseBuffer<uint8_t> *buffer = nullptr;
250 uint32_t underflow_count = 0;
251 uint32_t underflow_per_second = 0;
252 uint32_t frame_count = 0;
253 uint32_t frames_per_second = 0;
254 uint32_t frame_size = 0;
255 uint32_t time_1_sec;
256 bool is_timer_started = false;
257 bool is_blocking_write = true;
258 Decimate decimate;
259 int decimation_factor = 0;
260
261 void deleteBuffer() {
262 // delete buffer if necessary
263 if (buffer != nullptr) {
264 delete buffer;
265 buffer = nullptr;
266 }
267 }
268
271 if (isTimerStarted() && buffer != nullptr) {
272 // TRACED();
273 int required = (audio_config.bits_per_sample / 8) * audio_config.channels;
274 if (buffer->available() >= required) {
275 for (int j = 0; j < audio_config.channels; j++) {
276 int value = nextValue();
277 pwmWrite(j, value);
278 }
279 } else {
280 underflow_count++;
281 }
282 updateStatistics();
283 }
284 }
285
287 virtual int nextValue() {
288 int result = 0;
289 switch (audio_config.bits_per_sample) {
290 case 8: {
291 int8_t value;
292 if (buffer->readArray((uint8_t *)&value, 1) != 1) {
293 LOGE(READ_ERROR_MSG);
294 }
295 result = map(value, -NumberConverter::maxValue(8),
296 NumberConverter::maxValue(8), 0, maxOutputValue());
297 break;
298 }
299 case 16: {
300 int16_t value;
301 if (buffer->readArray((uint8_t *)&value, 2) != 2) {
302 LOGE(READ_ERROR_MSG);
303 }
304 result = map(value, -NumberConverter::maxValue(16),
305 NumberConverter::maxValue(16), 0, maxOutputValue());
306 break;
307 }
308 case 24: {
309 int24_t value;
310 if (buffer->readArray((uint8_t *)&value, 3) != 3) {
311 LOGE(READ_ERROR_MSG);
312 }
313 result = map((int32_t)value, -NumberConverter::maxValue(24),
314 NumberConverter::maxValue(24), 0, maxOutputValue());
315 break;
316 }
317 case 32: {
318 int32_t value;
319 if (buffer->readArray((uint8_t *)&value, 4) != 4) {
320 LOGE(READ_ERROR_MSG);
321 }
322 result = map(value, -NumberConverter::maxValue(32),
323 NumberConverter::maxValue(32), 0, maxOutputValue());
324 break;
325 }
326 }
327 return result;
328 }
329
331 virtual int maxSampleRate() { return audio_config.max_sample_rate; }
332
335 virtual bool isDecimateActive() {
336 return audio_config.sample_rate >= audio_config.max_sample_rate;
337 }
338
340 virtual int decimation() {
341 if (decimation_factor == 0){
342 for (int j = 1; j < 20; j++){
343 if (audio_config.sample_rate / j <= audio_config.max_sample_rate){
344 decimation_factor = j;
345 LOGI("Decimation factor: %d" ,j);
346 return j;
347 }
348 }
349 decimation_factor = 1;
350 LOGI("Decimation factor: %d", (int)decimation_factor);
351 }
352
353 return decimation_factor;
354 }
355};
356
357} // namespace audio_tool
358
359#endif
Shared functionality of all buffers.
Definition Buffers.h:26
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:37
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
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 PWMAudioBase.h:110
virtual int effectiveOutputSampleRate()
Provides the effective sample rate.
Definition PWMAudioBase.h:242
void playNextFrame()
writes the next frame to the output pins
Definition PWMAudioBase.h:270
void setBuffer(BaseBuffer< uint8_t > *buffer)
Definition PWMAudioBase.h:239
virtual int maxSampleRate()
Provides the max working sample rate.
Definition PWMAudioBase.h:331
virtual int nextValue()
determines the next scaled value
Definition PWMAudioBase.h:287
virtual int decimation()
Decimation factor to reduce the sample rate.
Definition PWMAudioBase.h:340
virtual bool isDecimateActive()
Definition PWMAudioBase.h:335
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:308
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 PWMAudioAVR.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:188
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 PWMAudioBase.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 PWMAudioBase.h:48
uint8_t timer_id
Timer used: Only used by ESP32 must be between 0 and 3
Definition PWMAudioBase.h:50
uint32_t pwm_frequency
additinal info which might not be used by all processors
Definition PWMAudioBase.h:46
uint32_t max_sample_rate
max sample sample rate that still produces good audio
Definition PWMAudioBase.h:53
uint16_t buffer_size
size of an inidividual buffer
Definition PWMAudioBase.h:41
uint8_t buffers
number of buffers
Definition PWMAudioBase.h:43