Skip to content

Commit 10ec1b9

Browse files
committed
DRAFT RTOS implementation
1 parent 56efd94 commit 10ec1b9

File tree

6 files changed

+258
-83
lines changed

6 files changed

+258
-83
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.vscode/
22
build/
3-
miniaudio.h
3+
miniaudio.h
4+
arduino-snapclient.code-workspace
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "AudioTools.h"
2+
#include "SnapClient.h"
3+
#include "api/SnapProcessorRTOS.h" // install https://github.com/pschatzmann/arduino-freertos-addons
4+
#include "AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver
5+
#include "AudioCodecs/CodecOpus.h" // https://github.com/pschatzmann/arduino-libopus
6+
//#include "AudioCodecs/CodecFLAC.h" // https://github.com/pschatzmann/arduino-libflac.git
7+
//#include "AudioCodecs/CodecVorbis.h" //https://github.com/pschatzmann/arduino-libvorbis-idec
8+
9+
10+
AudioBoardStream out(AudioKitEs8388V1); // or replace with e.g. I2SStream out;
11+
//FLACDecoder flac;
12+
//VorbisDecoder ogg;
13+
//WAVDecoder pcm;
14+
OpusAudioDecoder opus;
15+
WiFiClient wifi;
16+
SnapProcessorRTOS rtos(1024*8);
17+
SnapTimeSyncDynamic synch(172, 10); // optional configuratioin
18+
SnapClient client(wifi, out, opus);
19+
20+
void setup() {
21+
Serial.begin(115200);
22+
23+
// login to wifi -> Define values in SnapConfig.h or replace them here
24+
WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD);
25+
Serial.print("Connecting to WiFi ..");
26+
while (WiFi.status() != WL_CONNECTED) {
27+
Serial.print('.');
28+
delay(1000);
29+
}
30+
31+
// print ip address
32+
Serial.println();
33+
Serial.println(WiFi.localIP());
34+
35+
// use full volume of kit - volume control done by client
36+
out.setVolume(1.0);
37+
38+
// Use FreeRTOS
39+
client.setSnapProcessor(rtos);
40+
41+
// Define CONFIG_SNAPCAST_SERVER_HOST in SnapConfig.h or here
42+
client.setServerIP(IPAddress(192,168,1,33));
43+
44+
// start snap client
45+
client.begin(synch);
46+
}
47+
48+
49+
void loop() {
50+
client.doLoop();
51+
}

src/SnapClient.h

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,53 +32,52 @@
3232
class SnapClient {
3333

3434
public:
35-
SnapClient(Client &client,AudioStream &stream, AudioDecoder &decoder) {
35+
SnapClient(Client &client, AudioStream &stream, AudioDecoder &decoder) {
36+
static AdapterAudioStreamToAudioOutput output_adapter;
3637
output_adapter.setStream(stream);
37-
p_snapprocessor->setOutput(output_adapter);
38-
p_snapprocessor->setDecoder(decoder);
39-
setClient(client);
38+
p_decoder = &decoder;
39+
p_output = &output_adapter;
40+
p_client = &client;
4041
}
4142

4243
SnapClient(Client &client, AudioStream &stream, StreamingDecoder &decoder,
4344
int bufferSize = CONFIG_STREAMIN_DECODER_BUFFER) {
44-
p_decoder_adapter = new DecoderFromStreaming(decoder, bufferSize);
45+
static DecoderFromStreaming decoder_adapter(decoder, bufferSize);
46+
static AdapterAudioStreamToAudioOutput output_adapter;
47+
p_decoder = &decoder_adapter;
4548
output_adapter.setStream(stream);
46-
p_snapprocessor->setOutput(output_adapter);
47-
p_snapprocessor->setDecoder(*p_decoder_adapter);
48-
setClient(client);
49+
p_output = &output_adapter;
50+
p_client = &client;
4951
}
5052

5153
SnapClient(Client &client, AudioOutput &output, AudioDecoder &decoder) {
52-
p_snapprocessor->setOutput(output);
53-
p_snapprocessor->setDecoder(decoder);
54-
setClient(client);
54+
p_decoder = &decoder;
55+
p_output = &output;
56+
p_client = &client;
5557
}
5658

5759
SnapClient(Client &client, AudioOutput &output, StreamingDecoder &decoder,
5860
int bufferSize = CONFIG_STREAMIN_DECODER_BUFFER) {
59-
p_decoder_adapter = new DecoderFromStreaming(decoder, bufferSize);
60-
p_snapprocessor->setOutput(output);
61-
p_snapprocessor->setDecoder(*p_decoder_adapter);
62-
setClient(client);
61+
p_decoder = new DecoderFromStreaming(decoder, bufferSize);
62+
p_output = &output;
63+
p_client = &client;
6364
}
6465

6566
/// Destructor
6667
~SnapClient() {
67-
if (p_decoder_adapter != nullptr)
68-
delete p_decoder_adapter;
6968
end();
7069
}
7170

7271
/// Defines an alternative commnuication client (default is WiFiClient)
73-
void setClient(Client &client) { p_snapprocessor->setClient(client); }
72+
void setClient(Client &client) { p_client = &client; }
7473

7574
/// @brief Defines the Snapcast Server IP address
7675
/// @param address
77-
void setServerIP(IPAddress address) { p_snapprocessor->setServerIP(address); }
76+
void setServerIP(IPAddress ipAddress) { this->address = ipAddress; }
7877

7978
/// Defines the time synchronization logic
8079
void setSnapTimeSync(SnapTimeSync &timeSync){
81-
p_snapprocessor->snapOutput().setSnapTimeSync(timeSync);
80+
p_time_sync = &timeSync;
8281
}
8382

8483
/// Starts the processing
@@ -112,6 +111,12 @@ class SnapClient {
112111
SnapTime::instance().setupSNTPTime();
113112
#endif
114113

114+
p_snapprocessor->setServerIP(address);
115+
p_snapprocessor->setOutput(*p_output);
116+
p_snapprocessor->snapOutput().setSnapTimeSync(*p_time_sync);
117+
p_snapprocessor->setDecoder(*p_decoder);
118+
p_snapprocessor->setClient(*p_client);
119+
115120
// start tasks
116121
return p_snapprocessor->begin();
117122
}
@@ -136,18 +141,24 @@ class SnapClient {
136141
p_snapprocessor = &processor;
137142
}
138143

144+
/// Defines the Snap output implementation to be used
145+
void setSnapOutput(SnapOutput &out){
146+
p_snapprocessor->setSnapOutput(out);
147+
}
148+
139149
/// Call from Arduino Loop (when no tasks are used)
140150
void doLoop() { p_snapprocessor->doLoop(); }
141151

142152
protected:
143153
const char *TAG = "SnapClient";
144-
AdapterAudioStreamToAudioOutput output_adapter;
145-
DecoderFromStreaming *p_decoder_adapter = nullptr;
146154
SnapTime &snap_time = SnapTime::instance();
147-
// default setup
148-
SnapOutput snap_output;
149-
SnapProcessor default_processor{snap_output};
155+
SnapProcessor default_processor;
150156
SnapProcessor *p_snapprocessor = &default_processor;
157+
AudioOutput *p_output = nullptr;
158+
AudioDecoder *p_decoder = nullptr;
159+
Client *p_client = nullptr;
160+
SnapTimeSync *p_time_sync = nullptr;
161+
IPAddress address;
151162

152163
void setupMDNS() {
153164
#if CONFIG_SNAPCLIENT_USE_MDNS && defined(ESP32)

src/api/SnapOutput.h

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
#pragma once
22

3-
#include "Arduino.h" // for ESP.getPsramSize()
3+
#include <stdint.h>
4+
#include <sys/time.h>
5+
6+
#include "Arduino.h" // for ESP.getPsramSize()
47
#include "AudioTools.h"
58
#include "SnapCommon.h"
69
#include "SnapConfig.h"
710
#include "SnapLogger.h"
811
#include "SnapTime.h"
912
#include "SnapTimeSync.h"
10-
#include <stdint.h>
11-
#include <sys/time.h>
1213

1314
class SnapProcessor;
15+
class SnapProcessorRTOS;
16+
1417
/**
1518
* @brief Simple Output Class which uses the AudioTools to build an output chain
1619
* with volume control and a resampler
@@ -21,18 +24,16 @@ class SnapProcessor;
2124
**/
2225

2326
class SnapOutput : public AudioInfoSupport {
24-
friend SnapProcessor;
25-
public:
26-
// singleton: only access via instance class method
27-
SnapOutput() {
28-
// audio_info.sample_rate = 48000;
29-
// audio_info.channels = 2;
30-
// audio_info.bits_per_sample = 16;
31-
}
27+
friend SnapProcessor;
28+
friend SnapProcessorRTOS;
29+
30+
public:
31+
SnapOutput() = default;
3232

3333
/// Starts the processing which is also starting the the dsp_i2s_task_handler
3434
/// task
3535
virtual bool begin() {
36+
ESP_LOGI(TAG, "begin");
3637
is_sync_started = false;
3738
return audioBegin();
3839
}
@@ -41,7 +42,8 @@ friend SnapProcessor;
4142
virtual size_t write(const uint8_t *data, size_t size) {
4243
ESP_LOGD(TAG, "%zu", size);
4344
// only start to proces data after we received codec header
44-
if (!is_audio_begin_called){
45+
if (!is_audio_begin_called) {
46+
ESP_LOGI(TAG, "not started");
4547
return 0;
4648
}
4749

@@ -86,54 +88,51 @@ friend SnapProcessor;
8688

8789
/// Defines the audio output chain to the final output
8890
void setOutput(AudioOutput &output) {
89-
this->out = &output; // final output
91+
this->out = &output; // final output
9092
resample.setStream(output);
91-
vol_stream.setStream(resample); // adjust volume
93+
vol_stream.setStream(resample); // adjust volume
9294
timed_stream.setStream(vol_stream);
93-
decoder_stream.setStream(&timed_stream); // decode to pcm
95+
decoder_stream.setStream(&timed_stream); // decode to pcm
9496
}
9597

98+
AudioOutput &getOutput() { return *out; }
99+
96100
/// Defines the decoder class
97101
void setDecoder(AudioDecoder &dec) { decoder_stream.setDecoder(&dec); }
98102

103+
AudioDecoder &getDecoder() { return decoder_stream.decoder(); }
104+
99105
/// setup of all audio objects
100106
void setAudioInfo(AudioInfo info) {
101107
ESP_LOGI(TAG, "sample_rate: %d, channels: %d, bits: %d", info.sample_rate,
102108
info.channels, info.bits_per_sample);
103109
audio_info = info;
104-
if (is_audio_begin_called){
110+
if (is_audio_begin_called) {
105111
vol_stream.setAudioInfo(info);
106112
out->setAudioInfo(info);
107113
timed_stream.setAudioInfo(info);
108114
}
109115
}
110116

111-
AudioInfo audioInfo(){
112-
return audio_info;
113-
}
117+
AudioInfo audioInfo() { return audio_info; }
114118

115119
/// Defines the time synchronization logic
116-
void setSnapTimeSync(SnapTimeSync &timeSync){
117-
p_snap_time_sync = &timeSync;
118-
}
120+
void setSnapTimeSync(SnapTimeSync &timeSync) { p_snap_time_sync = &timeSync; }
119121

120-
// do nothing
121-
virtual void doLoop() {}
122+
SnapTimeSync &snapTimeSync() { return *p_snap_time_sync; }
122123

123-
SnapTimeSync& snapTimeSync() {
124-
return *p_snap_time_sync;
125-
}
124+
bool isStarted() { return is_audio_begin_called; }
126125

127-
protected:
126+
protected:
128127
const char *TAG = "SnapOutput";
129128
AudioOutput *out = nullptr;
130129
AudioInfo audio_info;
131130
EncodedAudioStream decoder_stream;
132131
VolumeStream vol_stream;
133132
ResampleStream resample;
134133
TimedStream timed_stream;
135-
float vol = 1.0; // volume in the range 0.0 - 1.0
136-
float vol_factor = 1.0; //
134+
float vol = 1.0; // volume in the range 0.0 - 1.0
135+
float vol_factor = 1.0; //
137136
bool is_mute = false;
138137
SnapAudioHeader header;
139138
SnapTime &snap_time = SnapTime::instance();
@@ -144,9 +143,10 @@ friend SnapProcessor;
144143

145144
/// setup of all audio objects
146145
bool audioBegin() {
147-
148-
if (out == nullptr)
146+
if (out == nullptr) {
147+
ESP_LOGI(TAG, "out is null");
149148
return false;
149+
}
150150

151151
// open volume control: allow amplification
152152
auto vol_cfg = vol_stream.defaultConfig();
@@ -178,7 +178,6 @@ friend SnapProcessor;
178178
return true;
179179
}
180180

181-
182181
/// to speed up or slow down playback
183182
void setPlaybackFactor(float fact) { resample.setStepSize(fact); }
184183

@@ -193,8 +192,7 @@ friend SnapProcessor;
193192

194193
void audioEnd() {
195194
ESP_LOGD(TAG, "start");
196-
if (out == nullptr)
197-
return;
195+
if (out == nullptr) return;
198196
out->end();
199197
}
200198

@@ -210,19 +208,18 @@ friend SnapProcessor;
210208
/// ignored - update playback speed
211209
bool synchronizePlayback() {
212210
bool result = true;
213-
SnapTimeSync& ts = *p_snap_time_sync;
211+
SnapTimeSync &ts = *p_snap_time_sync;
214212

215213
// calculate how long we need to wait to playback the audio
216214
auto delay_ms = getDelayMs();
217215

218216
if (!is_sync_started) {
219-
220217
ts.begin(audio_info.sample_rate);
221218

222219
// start audio when first package in the future becomes valid
223220
result = synchronizeOnStart(delay_ms);
224221
} else {
225-
// provide the actual delay to the synch
222+
// provide the actual delay to the synch
226223
ts.updateActualDelay(delay_ms);
227224

228225
if (ts.isSync()) {

0 commit comments

Comments
 (0)