9
9
10
10
namespace audio_tools {
11
11
12
+ // forward declarations
13
+ // Callback for user
12
14
typedef bool (*PWMCallbackType)(uint8_t channels, int16_t * data);
15
+ // Callback used by system
13
16
void defaultPWMAudioOutputCallback ();
17
+ // Stream classes
18
+ class PWMAudioStreamESP32 ;
19
+ class PWMAudioStreamPico ;
20
+
14
21
/* *
15
22
* PWMConfigAVR
16
23
* @author Phil Schatzmann
@@ -27,12 +34,36 @@ struct PWMConfig {
27
34
LOGI (" sample_rate: %d" , sample_rate);
28
35
LOGI (" channels: %d" , channels);
29
36
LOGI (" bits_per_sample: %d" , bits_per_sample);
37
+ LOGI (" buffer_size: %d" , buffer_size);
30
38
}
31
39
32
40
#ifdef ESP32
33
- int resolution = 8 ; // must be between 8 and 11 -> drives pwm frequency
34
- int start_pin = 3 ;
35
- int *pins = nullptr ;
41
+ /* *
42
+ * @brief Configuration for PWM output
43
+ * RES | BITS | MAX_FREQ (kHz)
44
+ * ----|------|-------
45
+ * 8 | 256 | 312.5
46
+ * 9 | 512 | 156.25
47
+ * 10 | 1024 | 78.125
48
+ * 11 | 2048 | 39.0625
49
+ *
50
+ * The default resolution is 8. The value must be between 8 and 11 and also drives the PWM frequency.
51
+ */
52
+ int resolution = 8 ; // must be between 8 and 11 -> drives pwm frequency
53
+ int start_pin = 4 ;
54
+ uint8_t timer_id = 0 ; // must be between 0 and 3
55
+ #endif
56
+
57
+ #ifdef ARDUINO_ARCH_RP2040
58
+ int pwm_freq = 60000 ; // audable range is from 20 to 20,000Hz
59
+ int start_pin = 2 ; // channel 0 will be on gpio 2, channel 1 on 3 etc
60
+ #endif
61
+
62
+ // for architectures which support many flexible pins we support setPins
63
+ #if defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
64
+ friend class PWMAudioStreamESP32 ;
65
+ friend class PWMAudioStreamPico ;
66
+
36
67
37
68
// define all pins by passing an array and updates the channels by the number of pins
38
69
template <size_t N>
@@ -46,11 +77,11 @@ struct PWMConfig {
46
77
pins = array;
47
78
start_pin = -1 ; // mark start pin as invalid
48
79
}
49
- #endif
50
80
51
- #ifdef ARDUINO_ARCH_RP2040
52
- int pwm_freq = 60000 ; // audable range is from 20 to 20,000Hz
53
- int start_pin = 2 ; // channel 0 will be on gpio 2, channel 1 on 3 etc
81
+ protected:
82
+ int *pins = nullptr ;
83
+
84
+
54
85
#endif
55
86
56
87
} default_config;
@@ -71,8 +102,6 @@ class PWMAudioStreamBase : public Stream {
71
102
return audio_config;
72
103
}
73
104
74
- virtual int maxChannels () = 0;
75
-
76
105
// / Starts the PWMAudio using callbacks
77
106
bool begin (uint16_t sampleRate, uint8_t channels, PWMCallbackType cb) {
78
107
LOGD (__FUNCTION__);
@@ -88,7 +117,7 @@ class PWMAudioStreamBase : public Stream {
88
117
setupPWM ();
89
118
setupTimer ();
90
119
91
- data_write_started = true ;
120
+ is_timer_started = true ;
92
121
return true ;
93
122
}
94
123
@@ -103,6 +132,7 @@ class PWMAudioStreamBase : public Stream {
103
132
}
104
133
// allocate new buffer
105
134
if (buffer==nullptr ) {
135
+ LOGI (" Allocating new buffer %d * %d bytes" ,config.buffers , config.buffer_size );
106
136
buffer = new NBuffer<uint8_t >(config.buffer_size , config.buffers );
107
137
}
108
138
// check allocation
@@ -153,100 +183,117 @@ class PWMAudioStreamBase : public Stream {
153
183
size_t result = 0 ;
154
184
if (buffer->availableToWrite ()>1 ){
155
185
result = buffer->write (value);
156
- setWriteStarted ();
186
+ startTimer ();
157
187
}
158
188
return result;
159
189
}
160
190
161
191
// blocking write for an array: we expect a singed value and convert it into a unsigned
162
192
virtual size_t write (const uint8_t *wrt_buffer, size_t size){
163
- LOGI (" write: %lu bytes" , size)
193
+ LOGD (" write: %lu bytes" , size)
194
+ bool log_flag = true ;
164
195
while (availableForWrite ()<size){
165
- LOGI (" Buffer is full - waiting..." );
196
+ if (log_flag) LOGI (" Buffer is full - waiting..." );
197
+ log_flag = false ;
166
198
delay (10 );
167
199
}
168
200
size_t result = buffer->writeArray (wrt_buffer, size);
169
201
if (result!=size){
170
202
LOGW (" Could not write all data: %d -> %d" , size, result);
171
203
}
172
204
// activate the timer now - if not already done
173
- setWriteStarted ();
205
+ startTimer ();
174
206
return result;
175
207
}
176
208
177
209
// When the timer does not have enough data we increase the underflow_count;
178
- uint64_t underflowsPerSecond (){
179
- return underflow_count ;
210
+ uint32_t underflowsPerSecond (){
211
+ return underflow_per_second ;
180
212
}
181
-
182
- uint64_t frameCount (){
183
- return frame_count ;
213
+ // provides the effectivly measured output frames per second
214
+ uint32_t framesPerSecond (){
215
+ return frames_per_second ;
184
216
}
185
217
218
+
186
219
virtual void pwmWrite (int channel, int value) = 0;
187
220
188
221
protected:
189
- NBuffer<uint8_t > *buffer;
190
- bool data_write_started = false ;
191
- uint64_t underflow_count = 0 ;
192
- uint64_t frame_count = 0 ;
193
- PWMCallbackType user_callback = nullptr ;
194
222
PWMConfig audio_config;
223
+ NBuffer<uint8_t > *buffer = nullptr ;
224
+ PWMCallbackType user_callback = nullptr ;
225
+ uint32_t underflow_count = 0 ;
226
+ uint32_t underflow_per_second = 0 ;
227
+ uint32_t frame_count = 0 ;
228
+ uint32_t frames_per_second = 0 ;
229
+ uint64_t time_1_sec;
230
+ bool is_timer_started = false ;
195
231
196
232
virtual void logConfig () {
197
233
audio_config.logConfig ();
198
234
}
199
235
200
236
virtual void setupPWM () = 0;
201
-
202
237
virtual void setupTimer () = 0;
238
+ virtual int maxChannels () = 0;
239
+ virtual int maxOutputValue () = 0;
203
240
204
241
205
242
// / when we get the first write -> we activate the timer to start with the output of data
206
- virtual void setWriteStarted (){
207
- if (!data_write_started ){
208
- LOGI ( " timerAlarmEnable " );
209
- data_write_started = true ;
243
+ virtual void startTimer (){
244
+ if (!is_timer_started ){
245
+ LOGD (__FUNCTION__ );
246
+ is_timer_started = true ;
210
247
}
211
248
}
212
249
250
+ inline void updateStatistics (){
251
+ frame_count++;
252
+ if (millis ()>=time_1_sec){
253
+ time_1_sec = millis ()+1000 ;
254
+ frames_per_second = frame_count;
255
+ underflow_per_second = underflow_count;
256
+ underflow_count = 0 ;
257
+ frame_count = 0 ;
258
+ }
259
+ }
260
+
261
+
213
262
void playNextFrameCallback (){
263
+ // LOGD(__FUNCTION__);
214
264
uint8_t channels = audio_config.channels ;
215
265
int16_t data[channels];
216
266
if (user_callback (channels, data)){
217
- frame_count++;
218
267
for (uint8_t j=0 ;j<audio_config.channels ;j++){
219
268
int value = map (data[j], -maxValue (16 ), maxValue (16 ), 0 , 255 );
220
269
pwmWrite (j, value);
221
- }
270
+ }
271
+ updateStatistics ();
222
272
}
223
273
}
224
274
275
+
225
276
// / writes the next frame to the output pins
226
277
void playNextFrameStream (){
227
- static long underflow_time = millis ()+ 1000 ;
228
- if (data_write_started){
278
+ if (is_timer_started){
279
+ // LOGD(__FUNCTION__);
229
280
int required = (audio_config.bits_per_sample / 8 ) * audio_config.channels ;
230
281
if (buffer->available () >= required){
231
- underflow_count = 0 ;
232
- frame_count++;
233
282
for (int j=0 ;j<audio_config.channels ;j++){
234
283
int value = nextValue ();
235
- Serial.println (value);
236
284
pwmWrite (j, value);
237
285
}
238
286
} else {
239
287
underflow_count++;
240
288
}
241
-
242
- if (underflow_time>millis ()){
243
- underflow_time = millis ()+1000 ;
244
- underflow_count = 0 ;
245
- }
289
+ updateStatistics ();
290
+ } else {
291
+ // LOGE("is_timer_started is false");
246
292
}
247
293
}
248
294
249
295
void playNextFrame (){
296
+ // LOGD(__FUNCTION__);
250
297
if (user_callback!=nullptr ){
251
298
playNextFrameCallback ();
252
299
} else {
@@ -265,31 +312,31 @@ class PWMAudioStreamBase : public Stream {
265
312
LOGE (" Could not read full data" );
266
313
value = 0 ;
267
314
}
268
- result = map (value, -maxValue (8 ), maxValue (8 ), 0 , 255 );
315
+ result = map (value, -maxValue (8 ), maxValue (8 ), 0 , maxOutputValue () );
269
316
break ;
270
317
}
271
318
case 16 : {
272
319
int16_t value;
273
320
if (buffer->readArray ((uint8_t *)&value,2 )!=2 ){
274
321
LOGE (" Could not read full data" );
275
322
}
276
- result = map (value, -maxValue (16 ), maxValue (16 ), 0 , 255 );
323
+ result = map (value, -maxValue (16 ), maxValue (16 ), 0 , maxOutputValue () );
277
324
break ;
278
325
}
279
326
case 24 : {
280
327
int24_t value;
281
328
if (buffer->readArray ((uint8_t *)&value,3 )!=3 ){
282
329
LOGE (" Could not read full data" );
283
330
}
284
- result = map ((int32_t )value, -maxValue (24 ), maxValue (24 ), 0 , 255 );
331
+ result = map ((int32_t )value, -maxValue (24 ), maxValue (24 ), 0 , maxOutputValue () );
285
332
break ;
286
333
}
287
334
case 32 : {
288
335
int32_t value;
289
336
if (buffer->readArray ((uint8_t *)&value,4 )!=4 ){
290
337
LOGE (" Could not read full data" );
291
338
}
292
- result = map (value, -maxValue (32 ), maxValue (32 ), 0 , 255 );
339
+ result = map (value, -maxValue (32 ), maxValue (32 ), 0 , maxOutputValue () );
293
340
break ;
294
341
}
295
342
}
0 commit comments