arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
AudioServer.h
1#pragma once
2
3#include "AudioConfig.h"
4#if defined(USE_AUDIO_SERVER) && (defined(USE_ETHERNET) || defined(USE_WIFI))
5
6#ifdef USE_WIFI
7# ifdef ESP8266
8# include <ESP8266WiFi.h>
9# else
10# include <WiFi.h>
11# endif
12#endif
13
14#ifdef USE_ETHERNET
15# include <Ethernet.h>
16#endif
17
18#include "AudioTools/AudioCodecs/CodecWAV.h"
19#include "AudioTools.h"
20
21namespace audio_tools {
22
25
35template<class Client,class Server>
37 public:
42 AudioServerT(int port = 80) {
43 // the client returns 0 for avialableForWrite()
44 copier.setCheckAvailableForWrite(false);
45 setupServer(port);
46 }
47
54 AudioServerT(const char *network, const char *password, int port = 80) {
55 this->network = (char *)network;
56 this->password = (char *)password;
57 // the client returns 0 for avialableForWrite()
58 copier.setCheckAvailableForWrite(false);
59 setupServer(port);
60 }
61
69 bool begin(Stream &in, const char *contentType) {
70 TRACED();
71 this->in = &in;
72 this->content_type = contentType;
73
74#ifdef USE_WIFI
75 connectWiFi();
76#endif
77 // start server
78 server.begin();
79 return true;
80 }
81
88 bool begin(AudioServerDataCallback cb, const char *contentType) {
89 TRACED();
90 this->in = nullptr;
91 this->callback = cb;
92 this->content_type = contentType;
93
94#ifdef USE_WIFI
95 connectWiFi();
96#endif
97
98 // start server
99 server.begin();
100 return true;
101 }
102
111 bool copy() { return doLoop(); }
112
117 bool doLoop() {
118 // LOGD("doLoop");
119 bool active = true;
120 if (!client_obj.connected()) {
121#if USE_SERVER_ACCEPT
122 client_obj = server.accept(); // listen for incoming clients
123#else
124 client_obj = server.available(); // listen for incoming clients
125#endif
126 processClient();
127 } else {
128 // We are connected: copy input from source to wav output
129 if (client_obj) {
130 if (callback == nullptr) {
131 LOGD("copy data...");
132 if (converter_ptr == nullptr) {
133 copier.copy();
134 } else {
135 copier.copy(*converter_ptr);
136 }
137 // if we limit the size of the WAV the encoder gets automatically
138 // closed when all has been sent
139 if (!client_obj) {
140 LOGI("stop client...");
141 client_obj.stop();
142 active = false;
143 }
144 }
145 } else {
146 LOGI("client was not connected");
147 }
148 }
149 return active;
150 }
151
153 void setConverter(BaseConverter *c) { converter_ptr = c; }
154
156 Stream &out() { return client_obj; }
157
159 Client *out_ptr() { return &client_obj; }
160
162 bool isClientConnected() { return client_obj.connected(); }
163
165 void setCopyBufferSize(int size){
166 copier.resize(size);
167 }
168
169 protected:
170 // WIFI
171#ifdef ESP32
172 Server server;
173#else
174 Server server{80};
175#endif
176 Client client_obj;
177 char *password = nullptr;
178 char *network = nullptr;
179
180 // Content
181 const char *content_type = nullptr;
182 AudioServerDataCallback callback = nullptr;
183 Stream *in = nullptr;
184 StreamCopy copier;
185 BaseConverter *converter_ptr = nullptr;
186
187 void setupServer(int port) {
188 Server tmp(port);
189 server = tmp;
190 }
191
192#ifdef USE_WIFI
193 void connectWiFi() {
194 TRACED();
195 if (WiFi.status() != WL_CONNECTED && network != nullptr &&
196 password != nullptr) {
197 WiFi.begin(network, password);
198 while (WiFi.status() != WL_CONNECTED) {
199 Serial.print(".");
200 delay(500);
201 }
202#ifdef ESP32
203 WiFi.setSleep(false);
204#endif
205 Serial.println();
206 }
207 Serial.print("IP address: ");
208 Serial.println(WiFi.localIP());
209 }
210#endif
211
212 virtual void sendReplyHeader() {
213 TRACED();
214 // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
215 // and a content-type so the client knows what's coming, then a blank line:
216 client_obj.println("HTTP/1.1 200 OK");
217 LOGI("Reply: HTTP/1.1 200 OK");
218 if (content_type != nullptr) {
219 client_obj.print("Content-type:");
220 client_obj.println(content_type);
221 LOGI("Content-type: %s", content_type);
222 }
223 client_obj.println();
224 if (!client_obj.connected()){
225 LOGE("connection was closed");
226 }
227 }
228
229 virtual void sendReplyContent() {
230 TRACED();
231 if (callback != nullptr) {
232 // provide data via Callback
233 LOGI("sendReply - calling callback");
234 callback(&client_obj);
235 client_obj.stop();
236 } else if (in != nullptr) {
237 // provide data for stream
238 LOGI("sendReply - Returning audio stream...");
239 copier.begin(client_obj, *in);
240 if (!client_obj.connected()){
241 LOGE("connection was closed");
242 }
243 }
244 }
245
246 // Handle an new client connection and return the data
247 void processClient() {
248 // LOGD("processClient");
249 if (client_obj) { // if you get a client,
250 LOGI("New Client:"); // print a message out the serial port
251 String currentLine =
252 ""; // make a String to hold incoming data from the client
253 while (client_obj.connected()) { // loop while the client's connected
254 if (client_obj
255 .available()) { // if there's bytes to read from the client,
256 char c = client_obj.read(); // read a byte, then
257 if (c == '\n') { // if the byte is a newline character
258 LOGI("Request: %s", currentLine.c_str());
259 // if the current line is blank, you got two newline characters in a
260 // row. that's the end of the client HTTP request, so send a
261 // response:
262 if (currentLine.length() == 0) {
263 sendReplyHeader();
264 sendReplyContent();
265 // break out of the while loop:
266 break;
267 } else { // if you got a newline, then clear currentLine:
268 currentLine = "";
269 }
270 } else if (c != '\r') { // if you got anything else but a carriage
271 // return character,
272 currentLine += c; // add it to the end of the currentLine
273 }
274 }
275 }
276 }
277 }
278};
279
280#ifdef USE_WIFI
281using AudioServer = AudioServerT<WiFiClient, WiFiServer>;
282using AudioServerWiFi = AudioServerT<WiFiClient, WiFiServer>;
283#endif
284
285#ifdef USE_ETHERNET
286using AudioServer = AudioServerT<EthernetClient, EthernetServer>;
287using AudioServerEthernet = AudioServerT<EthernetClient, EthernetServer>;
288#endif
289
300 public:
305 AudioEncoderServer(AudioEncoder *encoder, int port = 80) : AudioServer(port) {
306 this->encoder = encoder;
307 }
308
315 AudioEncoderServer(AudioEncoder *encoder, const char *network,
316 const char *password, int port = 80)
317 : AudioServer(network, password, port) {
318 this->encoder = encoder;
319 }
320
325
334 bool begin(Stream &in, int sample_rate, int channels,
335 int bits_per_sample = 16, BaseConverter *converter = nullptr) {
336 TRACED();
337 this->in = &in;
338 setConverter(converter);
339 audio_info.sample_rate = sample_rate;
340 audio_info.channels = channels;
341 audio_info.bits_per_sample = bits_per_sample;
342 encoder->setAudioInfo(audio_info);
343 // encoded_stream.begin(&client_obj, encoder);
344 encoded_stream.setOutput(&client_obj);
345 encoded_stream.setEncoder(encoder);
346 encoded_stream.begin(audio_info);
347 return AudioServer::begin(in, encoder->mime());
348 }
349
358 bool begin(Stream &in, AudioInfo info, BaseConverter *converter = nullptr) {
359 TRACED();
360 this->in = &in;
361 this->audio_info = info;
362 setConverter(converter);
363 encoder->setAudioInfo(audio_info);
364 encoded_stream.setOutput(&client_obj);
365 encoded_stream.setEncoder(encoder);
366 if (!encoded_stream.begin(audio_info)){
367 LOGE("encoder begin failed");
368 stop();
369 }
370
371 return AudioServer::begin(in, encoder->mime());
372 }
373
381 bool begin(AudioStream &in, BaseConverter *converter = nullptr) {
382 TRACED();
383 this->in = &in;
384 this->audio_info = in.audioInfo();
385 setConverter(converter);
386 encoder->setAudioInfo(audio_info);
387 encoded_stream.setOutput(&client_obj);
388 encoded_stream.setEncoder(encoder);
389 encoded_stream.begin(audio_info);
390
391 return AudioServer::begin(in, encoder->mime());
392 }
393
401 bool begin(AudioServerDataCallback cb, int sample_rate, int channels,
402 int bits_per_sample = 16) {
403 TRACED();
404 audio_info.sample_rate = sample_rate;
405 audio_info.channels = channels;
406 audio_info.bits_per_sample = bits_per_sample;
407 encoder->setAudioInfo(audio_info);
408
409 return AudioServer::begin(cb, encoder->mime());
410 }
411
412 // provides a pointer to the encoder
413 AudioEncoder *audioEncoder() { return encoder; }
414
415 protected:
416 // Sound Generation - use EncodedAudioOutput with is more efficient then EncodedAudioStream
417 EncodedAudioOutput encoded_stream;
418 AudioInfo audio_info;
419 AudioEncoder *encoder = nullptr;
420
421 // moved to be part of reply content to avoid timeout issues in Chrome
422 void sendReplyHeader() override {}
423
424 void sendReplyContent() override {
425 TRACED();
426 // restart encoder
427 if (encoder) {
428 encoder->end();
429 encoder->begin();
430 }
431
432 if (callback != nullptr) {
433 // encoded_stream.begin(out_ptr(), encoder);
434 encoded_stream.setOutput(out_ptr());
435 encoded_stream.setEncoder(encoder);
436 encoded_stream.begin();
437
438 // provide data via Callback to encoded_stream
439 LOGI("sendReply - calling callback");
440 // Send delayed header
441 AudioServer::sendReplyHeader();
442 callback(&encoded_stream);
443 client_obj.stop();
444 } else if (in != nullptr) {
445 // provide data for stream: in -copy> encoded_stream -> out
446 LOGI("sendReply - Returning encoded stream...");
447 // encoded_stream.begin(out_ptr(), encoder);
448 encoded_stream.setOutput(out_ptr());
449 encoded_stream.setEncoder(encoder);
450 encoded_stream.begin();
451
452 copier.begin(encoded_stream, *in);
453 if (!client_obj.connected()){
454 LOGE("connection was closed");
455 }
456 // Send delayed header
457 AudioServer::sendReplyHeader();
458 }
459 }
460};
461
471 public:
476 AudioWAVServer(int port = 80) : AudioEncoderServer(new WAVEncoder(), port) {}
477
484 AudioWAVServer(const char *network, const char *password, int port = 80)
485 : AudioEncoderServer(new WAVEncoder(), network, password, port) {}
486
489 AudioEncoder *encoder = audioEncoder();
490 if (encoder != nullptr) {
491 delete encoder;
492 }
493 }
494
495 // provides a pointer to the encoder
496 WAVEncoder &wavEncoder() { return *static_cast<WAVEncoder *>(encoder); }
497};
498
499} // namespace audio_tools
500
501#endif
Encoding of PCM data.
Definition AudioCodecsBase.h:90
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:99
virtual const char * mime()=0
Provides the mime type of the encoded result.
A simple Arduino Webserver which streams the audio using the indicated encoder.. This class is based ...
Definition AudioServer.h:299
~AudioEncoderServer()
Destructor release the memory.
Definition AudioServer.h:324
AudioEncoderServer(AudioEncoder *encoder, const char *network, const char *password, int port=80)
Construct a new Audio Server object.
Definition AudioServer.h:315
bool begin(Stream &in, AudioInfo info, BaseConverter *converter=nullptr)
Start the server. You need to be connected to WiFI before calling this method.
Definition AudioServer.h:358
bool begin(AudioServerDataCallback cb, int sample_rate, int channels, int bits_per_sample=16)
Start the server. The data must be provided by a callback method.
Definition AudioServer.h:401
bool begin(Stream &in, int sample_rate, int channels, int bits_per_sample=16, BaseConverter *converter=nullptr)
Start the server. You need to be connected to WiFI before calling this method.
Definition AudioServer.h:334
bool begin(AudioStream &in, BaseConverter *converter=nullptr)
Start the server. You need to be connected to WiFI before calling this method.
Definition AudioServer.h:381
AudioEncoderServer(AudioEncoder *encoder, int port=80)
Construct a new Audio Server object that supports an AudioEncoder We assume that the WiFi is already ...
Definition AudioServer.h:305
A simple Arduino Webserver which streams the result This class is based on the WiFiServer class....
Definition AudioServer.h:36
void setConverter(BaseConverter *c)
defines a converter that will be used when the audio is rendered
Definition AudioServer.h:153
bool isClientConnected()
Checks if any clinent has connnected.
Definition AudioServer.h:162
bool begin(AudioServerDataCallback cb, const char *contentType)
Start the server. The data must be provided by a callback method.
Definition AudioServer.h:88
bool copy()
Add this method to your loop Returns true while the client is connected. (The same functionality like...
Definition AudioServer.h:111
Client * out_ptr()
Provides a pointer to the WiFiClient.
Definition AudioServer.h:159
AudioServerT(const char *network, const char *password, int port=80)
Construct a new Audio WAV Server object.
Definition AudioServer.h:54
AudioServerT(int port=80)
Construct a new Audio Server object We assume that the WiFi is already connected.
Definition AudioServer.h:42
void setCopyBufferSize(int size)
Changes the copy buffer size.
Definition AudioServer.h:165
bool doLoop()
Add this method to your loop Returns true while the client is connected.
Definition AudioServer.h:117
bool begin(Stream &in, const char *contentType)
Start the server. You need to be connected to WiFI before calling this method.
Definition AudioServer.h:69
Stream & out()
Provides the output stream.
Definition AudioServer.h:156
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:115
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition BaseStream.h:146
A simple Arduino Webserver which streams the audio as WAV data. This class is based on the AudioEncod...
Definition AudioServer.h:470
AudioWAVServer(const char *network, const char *password, int port=80)
Construct a new Audio WAV Server object.
Definition AudioServer.h:484
~AudioWAVServer()
Destructor: release the allocated encoder.
Definition AudioServer.h:488
AudioWAVServer(int port=80)
Construct a new Audio WAV Server object We assume that the WiFi is already connected.
Definition AudioServer.h:476
Abstract Base class for Converters A converter is processing the data in the indicated array.
Definition BaseConverter.h:24
Definition NoArduino.h:167
bool begin() override
Starts the processing - sets the status to active.
Definition AudioEncoded.h:137
void setOutput(Print &outputStream)
Defines/Changes the output target.
Definition AudioEncoded.h:97
Definition NoArduino.h:62
void resize(int len)
resizes the copy buffer
Definition StreamCopy.h:305
size_t copy()
copies the data from the source to the destination and returns the processed number of bytes
Definition StreamCopy.h:95
void setCheckAvailableForWrite(bool flag)
Activates the check that we copy only if available for write returns a value.
Definition StreamCopy.h:285
Definition NoArduino.h:142
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
A simple WAV file encoder. If no AudioEncoderExt is specified the WAV file contains PCM data,...
Definition CodecWAV.h:468
void stop()
Public generic methods.
Definition AudioRuntime.h:28
StreamCopyT< uint8_t > StreamCopy
We provide the typeless StreamCopy.
Definition StreamCopy.h:430
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioConfig.h:885
void(* AudioServerDataCallback)(Print *out)
Calback which writes the sound data to the stream.
Definition AudioServer.h:24
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:52
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
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