99
1010namespace audio_tools {
1111
12+ // forward declarations
13+ // Callback for user
1214typedef bool (*PWMCallbackType)(uint8_t channels, int16_t * data);
15+ // Callback used by system
1316void defaultPWMAudioOutputCallback ();
17+ // Stream classes
18+ class PWMAudioStreamESP32 ;
19+ class PWMAudioStreamPico ;
20+
1421/* *
1522 * PWMConfigAVR
1623 * @author Phil Schatzmann
@@ -27,12 +34,36 @@ struct PWMConfig {
2734 LOGI (" sample_rate: %d" , sample_rate);
2835 LOGI (" channels: %d" , channels);
2936 LOGI (" bits_per_sample: %d" , bits_per_sample);
37+ LOGI (" buffer_size: %d" , buffer_size);
3038 }
3139
3240#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+
3667
3768 // define all pins by passing an array and updates the channels by the number of pins
3869 template <size_t N>
@@ -46,11 +77,11 @@ struct PWMConfig {
4677 pins = array;
4778 start_pin = -1 ; // mark start pin as invalid
4879 }
49- #endif
5080
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+
5485#endif
5586
5687} default_config;
@@ -71,8 +102,6 @@ class PWMAudioStreamBase : public Stream {
71102 return audio_config;
72103 }
73104
74- virtual int maxChannels () = 0;
75-
76105 // / Starts the PWMAudio using callbacks
77106 bool begin (uint16_t sampleRate, uint8_t channels, PWMCallbackType cb) {
78107 LOGD (__FUNCTION__);
@@ -88,7 +117,7 @@ class PWMAudioStreamBase : public Stream {
88117 setupPWM ();
89118 setupTimer ();
90119
91- data_write_started = true ;
120+ is_timer_started = true ;
92121 return true ;
93122 }
94123
@@ -103,6 +132,7 @@ class PWMAudioStreamBase : public Stream {
103132 }
104133 // allocate new buffer
105134 if (buffer==nullptr ) {
135+ LOGI (" Allocating new buffer %d * %d bytes" ,config.buffers , config.buffer_size );
106136 buffer = new NBuffer<uint8_t >(config.buffer_size , config.buffers );
107137 }
108138 // check allocation
@@ -153,100 +183,117 @@ class PWMAudioStreamBase : public Stream {
153183 size_t result = 0 ;
154184 if (buffer->availableToWrite ()>1 ){
155185 result = buffer->write (value);
156- setWriteStarted ();
186+ startTimer ();
157187 }
158188 return result;
159189 }
160190
161191 // blocking write for an array: we expect a singed value and convert it into a unsigned
162192 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 ;
164195 while (availableForWrite ()<size){
165- LOGI (" Buffer is full - waiting..." );
196+ if (log_flag) LOGI (" Buffer is full - waiting..." );
197+ log_flag = false ;
166198 delay (10 );
167199 }
168200 size_t result = buffer->writeArray (wrt_buffer, size);
169201 if (result!=size){
170202 LOGW (" Could not write all data: %d -> %d" , size, result);
171203 }
172204 // activate the timer now - if not already done
173- setWriteStarted ();
205+ startTimer ();
174206 return result;
175207 }
176208
177209 // 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 ;
180212 }
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 ;
184216 }
185217
218+
186219 virtual void pwmWrite (int channel, int value) = 0;
187220
188221 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 ;
194222 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 ;
195231
196232 virtual void logConfig () {
197233 audio_config.logConfig ();
198234 }
199235
200236 virtual void setupPWM () = 0;
201-
202237 virtual void setupTimer () = 0;
238+ virtual int maxChannels () = 0;
239+ virtual int maxOutputValue () = 0;
203240
204241
205242 // / 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 ;
210247 }
211248 }
212249
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+
213262 void playNextFrameCallback (){
263+ // LOGD(__FUNCTION__);
214264 uint8_t channels = audio_config.channels ;
215265 int16_t data[channels];
216266 if (user_callback (channels, data)){
217- frame_count++;
218267 for (uint8_t j=0 ;j<audio_config.channels ;j++){
219268 int value = map (data[j], -maxValue (16 ), maxValue (16 ), 0 , 255 );
220269 pwmWrite (j, value);
221- }
270+ }
271+ updateStatistics ();
222272 }
223273 }
224274
275+
225276 // / writes the next frame to the output pins
226277 void playNextFrameStream (){
227- static long underflow_time = millis ()+ 1000 ;
228- if (data_write_started){
278+ if (is_timer_started){
279+ // LOGD(__FUNCTION__);
229280 int required = (audio_config.bits_per_sample / 8 ) * audio_config.channels ;
230281 if (buffer->available () >= required){
231- underflow_count = 0 ;
232- frame_count++;
233282 for (int j=0 ;j<audio_config.channels ;j++){
234283 int value = nextValue ();
235- Serial.println (value);
236284 pwmWrite (j, value);
237285 }
238286 } else {
239287 underflow_count++;
240288 }
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");
246292 }
247293 }
248294
249295 void playNextFrame (){
296+ // LOGD(__FUNCTION__);
250297 if (user_callback!=nullptr ){
251298 playNextFrameCallback ();
252299 } else {
@@ -265,31 +312,31 @@ class PWMAudioStreamBase : public Stream {
265312 LOGE (" Could not read full data" );
266313 value = 0 ;
267314 }
268- result = map (value, -maxValue (8 ), maxValue (8 ), 0 , 255 );
315+ result = map (value, -maxValue (8 ), maxValue (8 ), 0 , maxOutputValue () );
269316 break ;
270317 }
271318 case 16 : {
272319 int16_t value;
273320 if (buffer->readArray ((uint8_t *)&value,2 )!=2 ){
274321 LOGE (" Could not read full data" );
275322 }
276- result = map (value, -maxValue (16 ), maxValue (16 ), 0 , 255 );
323+ result = map (value, -maxValue (16 ), maxValue (16 ), 0 , maxOutputValue () );
277324 break ;
278325 }
279326 case 24 : {
280327 int24_t value;
281328 if (buffer->readArray ((uint8_t *)&value,3 )!=3 ){
282329 LOGE (" Could not read full data" );
283330 }
284- result = map ((int32_t )value, -maxValue (24 ), maxValue (24 ), 0 , 255 );
331+ result = map ((int32_t )value, -maxValue (24 ), maxValue (24 ), 0 , maxOutputValue () );
285332 break ;
286333 }
287334 case 32 : {
288335 int32_t value;
289336 if (buffer->readArray ((uint8_t *)&value,4 )!=4 ){
290337 LOGE (" Could not read full data" );
291338 }
292- result = map (value, -maxValue (32 ), maxValue (32 ), 0 , 255 );
339+ result = map (value, -maxValue (32 ), maxValue (32 ), 0 , maxOutputValue () );
293340 break ;
294341 }
295342 }
0 commit comments