Skip to content

Commit e293581

Browse files
committed
PWM Support for MBED
1 parent c2ef919 commit e293581

File tree

12 files changed

+3314
-70
lines changed

12 files changed

+3314
-70
lines changed

docs/.DS_Store

0 Bytes
Binary file not shown.

examples/streams-memory_wav-pwm/alice.h

Lines changed: 3054 additions & 0 deletions
Large diffs are not rendered by default.

examples/streams-memory_wav-pwm/streams-memory_wav-pwm.ino

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,42 @@
88
* @copyright Copyright (c) 2021
99
*/
1010

11+
/**
12+
* @file stream-memory_wav-pwm.ino
13+
* @author Phil Schatzmann
14+
* @brief decode WAV stream and output to PWM pins
15+
* @version 0.1
16+
* @date 2021-01-24
17+
*
18+
* @copyright Copyright (c) 2021
19+
*/
20+
1121
#include "AudioTools.h"
1222
#include "CodecWAV.h"
13-
#include "knghtsng.h"
23+
//#include "knghtsng.h"
24+
#include "alice.h"
1425

1526
using namespace audio_tools;
1627

17-
// MemoryStream -> AudioOutputStream -> WAVDecoder -> PWMAudioStream
18-
MemoryStream wav(knghtsng_wav, knghtsng_wav_len);
28+
//Data Flow: MemoryStream -> AudioOutputStream -> WAVDecoder -> PWMAudioStream
29+
30+
//MemoryStream wav(knghtsng_wav, knghtsng_wav_len);
31+
MemoryStream wav(alice_wav, alice_wav_len);
1932
PWMAudioStream pwm; // PWM output
2033
WAVDecoder decoder(pwm); // decode wav to pcm and send it to printer
2134
AudioOutputStream out(decoder); // output to decoder
2235
StreamCopy copier(out, wav); // copy in to out
36+
PWMConfig config = pwm.defaultConfig();
2337

2438
void setup(){
2539
Serial.begin(115200);
2640
while(!Serial);
2741
AudioLogger::instance().begin(Serial, AudioLogger::Debug);
2842

2943
// setup pwm output
30-
PWMConfig config = pwm.defaultConfig();
3144
config.channels = 1;
32-
config.sample_rate = 11025;
45+
//config.sample_rate = 11025; // for knghtsng_wav
46+
config.sample_rate = 8000; // for alice_wav
3347
pwm.begin(config);
3448
}
3549

@@ -41,6 +55,12 @@ void loop(){
4155
auto info = decoder.audioInfo();
4256
LOGI("The audio rate from the wav file is %d", info.sample_rate);
4357
LOGI("The channels from the wav file is %d", info.channels);
44-
stop();
58+
59+
// restart from the beginning
60+
Serial.println("Restarting...");
61+
delay(5000);
62+
decoder.begin(); // indicate that we process the WAV header
63+
wav.begin(); // reset actual position to 0
64+
pwm.begin(); // reset counters
4565
}
4666
}

src/AudioConfig.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,27 @@
4040
#define A2DP_BUFFER_SIZE 4096
4141
#define A2DP_BUFFER_COUNT 8
4242
#define DEFAUT_ADC_PIN 34
43+
44+
45+
/**
46+
* -------------------------------------------------------------------------
47+
* @brief PWM
48+
*/
4349
#define PWM_BUFFER_SIZE 512
4450
#define PWM_BUFFERS 10
45-
51+
#define PWM_FREQUENCY 60000
52+
#if defined(ESP32)
53+
#define PWM_START_PIN 4
54+
#elif defined(__arm__)
55+
#define PWM_START_PIN 2
56+
#endif
4657
/**
4758
* -------------------------------------------------------------------------
48-
* @brief Optional Functionality - comment out if not wanted
59+
* @brief Optional Functionality - comment out if not wanted
4960
*/
50-
61+
#ifndef __arm__
5162
#define USE_URL_ARDUINO
52-
63+
#endif
5364

5465
// Activate ESP32 Audio - for ESP32, ESP8266 and Raspberry Pico
5566
#define USE_ESP8266_AUDIO

src/AudioPWM/PWMAudioStreamBase.h

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,53 +17,30 @@ void defaultPWMAudioOutputCallback();
1717
// Stream classes
1818
class PWMAudioStreamESP32;
1919
class PWMAudioStreamPico;
20+
class PWMAudioStreamMBED;
2021

2122
/**
2223
* PWMConfigAVR
2324
* @author Phil Schatzmann
2425
* @copyright GPLv3
2526
*/
2627
struct PWMConfig {
28+
friend class PWMAudioStreamESP32;
29+
friend class PWMAudioStreamPico;
30+
friend class PWMAudioStreamMBED;
31+
32+
// basic information
2733
uint16_t sample_rate = 10000; // sample rate in Hz
2834
uint8_t channels = 2;
2935
uint8_t bits_per_sample = 16;
3036
uint16_t buffer_size = PWM_BUFFER_SIZE;
3137
uint8_t buffers = PWM_BUFFERS;
3238

33-
void logConfig(){
34-
LOGI("sample_rate: %d", sample_rate);
35-
LOGI("channels: %d", channels);
36-
LOGI("bits_per_sample: %d", bits_per_sample);
37-
LOGI("buffer_size: %d", buffer_size);
38-
}
39-
40-
#ifdef ESP32
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-
39+
// additinal info
40+
uint16_t start_pin = PWM_START_PIN;
41+
uint32_t pwm_frequency = PWM_FREQUENCY; // audable range is from 20 to 20,000Hz (not used by ESP32)
42+
uint8_t resolution = 8; // Only used by ESP32: must be between 8 and 11 -> drives pwm frequency
43+
uint8_t timer_id = 0; // Only used by ESP32 must be between 0 and 3
6744

6845
// define all pins by passing an array and updates the channels by the number of pins
6946
template<size_t N>
@@ -78,12 +55,21 @@ struct PWMConfig {
7855
start_pin = -1; // mark start pin as invalid
7956
}
8057

58+
void logConfig(){
59+
LOGI("sample_rate: %d", sample_rate);
60+
LOGI("channels: %d", channels);
61+
LOGI("bits_per_sample: %d", bits_per_sample);
62+
LOGI("buffer_size: %d", buffer_size);
63+
LOGI("start_pin: %d", start_pin);
64+
LOGI("pwm_frequency: %d", pwm_frequency);
65+
//LOGI("resolution: %d", resolution);
66+
//LOGI("timer_id: %d", timer_id);
67+
}
68+
8169
protected:
8270
int *pins = nullptr;
8371

8472

85-
#endif
86-
8773
} default_config;
8874

8975

@@ -93,6 +79,11 @@ struct PWMConfig {
9379
*/
9480
class PWMAudioStreamBase : public Stream {
9581
public:
82+
~PWMAudioStreamBase(){
83+
if (is_timer_started){
84+
end();
85+
}
86+
}
9687

9788
PWMConfig defaultConfig() {
9889
return default_config;
@@ -134,6 +125,8 @@ class PWMAudioStreamBase : public Stream {
134125
if (buffer==nullptr) {
135126
LOGI("Allocating new buffer %d * %d bytes",config.buffers, config.buffer_size);
136127
buffer = new NBuffer<uint8_t>(config.buffer_size, config.buffers);
128+
} else {
129+
buffer->reset();
137130
}
138131
// check allocation
139132
if (buffer==nullptr){
@@ -146,7 +139,45 @@ class PWMAudioStreamBase : public Stream {
146139
setupTimer();
147140

148141
return true;
149-
}
142+
}
143+
144+
// restart with prior definitions
145+
bool begin(){
146+
LOGD(__FUNCTION__);
147+
// allocate buffer if necessary
148+
if (user_callback==nullptr) {
149+
if (buffer==nullptr) {
150+
LOGI("->Allocating new buffer %d * %d bytes",audio_config.buffers, audio_config.buffer_size);
151+
buffer = new NBuffer<uint8_t>(audio_config.buffer_size, audio_config.buffers);
152+
} else {
153+
buffer->reset();
154+
}
155+
}
156+
// initialize if necessary
157+
if (!is_timer_started){
158+
logConfig();
159+
setupPWM();
160+
setupTimer();
161+
}
162+
163+
// reset class variables
164+
is_timer_started = true;
165+
underflow_count = 0;
166+
underflow_per_second = 0;
167+
frame_count = 0;
168+
frames_per_second = 0;
169+
170+
LOGI("->Buffer available: %d", buffer->available());
171+
LOGI("->Buffer available for write: %d", buffer->availableToWrite());
172+
LOGI("->is_timer_started: %s ", is_timer_started ? "true" : "false");
173+
}
174+
175+
virtual void end(){
176+
LOGD(__FUNCTION__);
177+
is_timer_started = false;
178+
}
179+
180+
150181
// not supported
151182
virtual int available() {
152183
LOGE("not supported");
@@ -190,15 +221,10 @@ class PWMAudioStreamBase : public Stream {
190221

191222
// blocking write for an array: we expect a singed value and convert it into a unsigned
192223
virtual size_t write(const uint8_t *wrt_buffer, size_t size){
193-
LOGD("write: %lu bytes", size)
194-
bool log_flag = true;
195-
while(availableForWrite()<size){
196-
if (log_flag) LOGI("Buffer is full - waiting...");
197-
log_flag = false;
198-
delay(10);
199-
}
200-
size_t result = buffer->writeArray(wrt_buffer, size);
201-
if (result!=size){
224+
size_t available = min((size_t)availableForWrite(),size);
225+
LOGD("write: %lu bytes -> %lu", size, available);
226+
size_t result = buffer->writeArray(wrt_buffer, available);
227+
if (result!=available){
202228
LOGW("Could not write all data: %d -> %d", size, result);
203229
}
204230
// activate the timer now - if not already done
@@ -229,10 +255,6 @@ class PWMAudioStreamBase : public Stream {
229255
uint64_t time_1_sec;
230256
bool is_timer_started = false;
231257

232-
virtual void logConfig() {
233-
audio_config.logConfig();
234-
}
235-
236258
virtual void setupPWM() = 0;
237259
virtual void setupTimer() = 0;
238260
virtual int maxChannels() = 0;
@@ -258,6 +280,9 @@ class PWMAudioStreamBase : public Stream {
258280
}
259281
}
260282

283+
void logConfig() {
284+
audio_config.logConfig();
285+
}
261286

262287
void playNextFrameCallback(){
263288
//LOGD(__FUNCTION__);

src/AudioPWM/PWMAudioStreamESP32.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class PWMAudioStreamESP32 : public PWMAudioStreamBase {
6868
/// Setup LED PWM
6969
virtual void setupPWM(){
7070
LOGD(__FUNCTION__);
71+
// frequency is driven by selected resolution
72+
uint64_t freq = frequency(audio_config.resolution)*1000;
73+
audio_config.pwm_frequency = freq;
7174

7275
pins.resize(audio_config.channels);
7376
for (int j=0;j<audio_config.channels;j++){
@@ -83,7 +86,6 @@ class PWMAudioStreamESP32 : public PWMAudioStreamBase {
8386
LOGD("-> defining pin %d",audio_config.pins[j]);
8487
pins[j].gpio = audio_config.pins[j];
8588
}
86-
uint64_t freq = frequency(audio_config.resolution)*1000;
8789
LOGD("-> ledcSetup: frequency=%d / resolution=%d", freq, audio_config.resolution);
8890
ledcSetup(pwmChannel, freq, audio_config.resolution);
8991
LOGD("-> ledcAttachPin: %d", pins[j].gpio);

0 commit comments

Comments
 (0)