Skip to content

Commit 8fb643f

Browse files
committed
KARadioProtocolServer provide abstract classes
1 parent 1a22173 commit 8fb643f

File tree

6 files changed

+233
-66
lines changed

6 files changed

+233
-66
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
/**
3+
* @file receive-mp3.ino
4+
* @brief Example of receiving an mp3 stream over serial and playing it over I2S
5+
* using the AudioTools library.
6+
* The processing must be synchronized with RTS/CTS flow control to prevent a
7+
* buffer overflow and lost data.
8+
*/
9+
#include "AudioTools.h"
10+
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
11+
#include "AudioTools/AudioLibs/AudioBoardStream.h"
12+
13+
14+
AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream
15+
EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream
16+
HardwareSerial MP3Serial(1); // define a Serial for UART1
17+
StreamCopy copier(dec, MP3Serial); // copy url to decoder
18+
// pins
19+
const int MySerialTX = -1;
20+
const int MySerialRX = 18;
21+
const int MySerialRTS = -1;
22+
const int MySerialCTS = 20;
23+
24+
void setup() {
25+
Serial.begin(115200);
26+
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
27+
28+
// setup serial data source with flow control
29+
MP3Serial.begin(115200, SERIAL_8N1);
30+
MP3Serial.setPins(MySerialRX, MySerialTX, MySerialCTS, MySerialRTS);
31+
MP3Serial.setHwFlowCtrlMode(HW_FLOWCTRL_CTS_RTS);
32+
33+
// setup i2s
34+
auto config = i2s.defaultConfig(TX_MODE);
35+
i2s.begin(config);
36+
37+
// setup I2S based on sampling rate provided by decoder
38+
dec.begin();
39+
40+
}
41+
42+
void loop() { copier.copy(); }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
/**
3+
* @file send-mp3.ino
4+
* @brief Example of sending an mp3 stream over Serial the AudioTools library.
5+
* The processing must be synchronized with RTS/CTS flow control to prevent a
6+
* buffer overflow and lost data.
7+
* We get the mp3 from an URLStream.
8+
*/
9+
#include <HardwareSerial.h>
10+
11+
#include "AudioTools.h"
12+
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
13+
#include "AudioTools/AudioLibs/AudioBoardStream.h"
14+
15+
URLStream url("ssid", "password"); // or replace with ICYStream to get metadata
16+
AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream
17+
HardwareSerial MP3Serial(1); // define a Serial for UART1
18+
StreamCopy copier(MP3Serial, url); // copy url to decoder
19+
// pins
20+
const int MySerialTX = 17;
21+
const int MySerialRX = -1;
22+
const int MySerialRTS = 19;
23+
const int MySerialCTS = -1;
24+
25+
void setup() {
26+
Serial.begin(115200);
27+
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
28+
29+
// setup serial data sink with flow control
30+
MP3Serial.begin(115200, SERIAL_8N1);
31+
MP3Serial.setPins(MySerialRX, MySerialTX, MySerialCTS, MySerialRTS);
32+
MP3Serial.setHwFlowCtrlMode(HW_FLOWCTRL_CTS_RTS);
33+
34+
// mp3 radio
35+
url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128", "audio/mp3");
36+
}
37+
38+
void loop() { copier.copy(); }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#pragma once
2+
#include "AudioTools/CoreAudio/AudioPlayer.h"
3+
4+
namespace audio_tools {
5+
6+
/***
7+
* @brief Abstract class for protocol to control the audio player.
8+
* @ingroup player
9+
* @author Phil Schatzmann
10+
*/
11+
class AudioPlayerProtocol {
12+
public:
13+
/// processes the commands and returns the result output via the Print
14+
/// object
15+
virtual bool processCommand(const char* input, Print& result) = 0;
16+
17+
/// Proceess commands passed by Stream (e.g. Serial)
18+
virtual bool processCommand(Stream& input, Print& result) {
19+
char buffer[max_input_buffer_size];
20+
int len = readLine(input, buffer, max_input_buffer_size);
21+
if (len == 0) return false;
22+
return processCommand(buffer, result);
23+
}
24+
/// Defines the player
25+
virtual void setPlayer(AudioPlayer& player) { p_player = &player; }
26+
27+
/// Defines the input buffer size used by the readLine function (default 256)
28+
void setMaxInputBufferSize(int size) { max_input_buffer_size = size; }
29+
30+
protected:
31+
AudioPlayer* p_player = nullptr;
32+
int max_input_buffer_size = 256;
33+
34+
/// Reads a line delimited by '\n' from the stream
35+
int readLine(Stream& in, char* str, int max) {
36+
int index = 0;
37+
if (in.available() > 0) {
38+
index = in.readBytesUntil('\n', str, max);
39+
str[index] = '\0'; // null termination character
40+
}
41+
return index;
42+
}
43+
};
44+
45+
} // namespace audio_tools
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#pragma once
2+
#include "AudioPlayerProtocol.h"
3+
#include "AudioTools/CoreAudio/BaseStream.h"
4+
#include "AudioTools/CoreAudio/Buffers.h"
5+
#include "HttpServer.h"
6+
7+
namespace audio_tools {
8+
9+
/***
10+
* @brief Audio Player Protocol Server: We can use the indicated protocol over
11+
* http to control the audio player provided by the audiotools.
12+
* @author Phil Schatzmann
13+
*/
14+
class AudioPlayerProtocolServer {
15+
public:
16+
/// Default constructor
17+
AudioPlayerProtocolServer(AudioPlayerProtocol& protocol,
18+
AudioPlayer& player, int port = 80,
19+
const char* ssid = nullptr,
20+
const char* pwd = nullptr) {
21+
setProtocol(protocol);
22+
setPlayer(player);
23+
setPort(port);
24+
setSSID(ssid);
25+
setPassword(pwd);
26+
}
27+
28+
/// Empty constructor: call setPlayer to define the player
29+
AudioPlayerProtocolServer() = default;
30+
31+
/// Defines the player
32+
void setPlayer(AudioPlayer& player) { p_protocol->setPlayer(player); }
33+
34+
void setPort(int port) { this->port = port; }
35+
void setSSID(const char* ssid) { this->ssid = ssid; }
36+
void setPassword(const char* password) { this->password = password; }
37+
void setSSID(const char* ssid, const char* password) {
38+
this->ssid = ssid;
39+
this->password = password;
40+
}
41+
42+
bool begin() {
43+
context[0] = this;
44+
server.on("/", T_GET, parse, context.data(), context.size());
45+
46+
// connect to WIFI
47+
if (ssid != nullptr && password != nullptr) {
48+
return server.begin(port, ssid, password);
49+
}
50+
return server.begin(port);
51+
}
52+
53+
void loop() { server.copy(); }
54+
void copy() { server.copy(); }
55+
56+
/// Defines the buffer size that is made available for the http reply
57+
void setBufferSize(int size) { buffer_size = size; }
58+
59+
void setProtocol(AudioPlayerProtocol& protocol) {
60+
this->p_protocol = &protocol;
61+
}
62+
63+
protected:
64+
WiFiServer wifi;
65+
HttpServer server{wifi};
66+
AudioPlayerProtocol* p_protocol;
67+
RingBuffer<uint8_t> ringBuffer{0};
68+
QueueStream<uint8_t> queueStream{ringBuffer};
69+
Vector<void*> context{1};
70+
int port = 80;
71+
const char* ssid = nullptr;
72+
const char* password = nullptr;
73+
int buffer_size = 512;
74+
75+
static void parse(HttpServer* server, const char* requestPath,
76+
HttpRequestHandlerLine* hl) {
77+
LOGI("parse: %s", requestPath);
78+
AudioPlayerProtocolServer* self = (AudioPlayerProtocolServer*)hl->context[0];
79+
self->ringBuffer.resize(self->buffer_size);
80+
QueueStream<uint8_t>& queueStream = self->queueStream;
81+
queueStream.begin();
82+
bool ok = self->p_protocol->processCommand(requestPath, queueStream);
83+
LOGI("available: %d", queueStream.available());
84+
server->reply("text/plain", queueStream, queueStream.available(),
85+
ok ? 200 : 400, ok ? SUCCESS : "Error");
86+
self->ringBuffer.resize(0);
87+
}
88+
};
89+
90+
} // namespace audio_tools

src/AudioTools/AudioLibs/KARadioProtocol.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,25 @@
55
#include "AudioTools/CoreAudio/AudioBasic/Str.h"
66
#include "AudioTools/CoreAudio/AudioPlayer.h"
77
#include "AudioTools/Disk/AudioSource.h"
8+
#include "AudioPlayerProtocol.h"
89

910
#define KA_VERSION "Release: 2.4, Revision: R0"
1011

1112
namespace audio_tools {
1213

1314
/***
1415
* @brief KA-Radio Protocol: We can use the KA-Radio protocol to control the
15-
* audio player provided by the audiotools.
16+
* audio player provided by the audiotools.
1617
* Supported commands: play, instant, volume, volume+, volume-, pause,
1718
* resume, stop, start, next, prev, mute, infos, version.
1819
* Example: volume=50&play=128&infos
1920
* See https://github.com/karawin/Ka-Radio32/blob/master/Interface.md
20-
*
21+
*
2122
* @ingroup player
2223
* @author Phil Schatzmann
2324
*/
2425

25-
class KARadioProtocol {
26+
class KARadioProtocol : public AudioPlayerProtocol {
2627
public:
2728
/// Empty constructor: call setPlayer to define the player
2829
KARadioProtocol() {
@@ -141,14 +142,18 @@ class KARadioProtocol {
141142
}
142143

143144
/// Defines the player
144-
void setPlayer(AudioPlayer& player) {
145-
p_player = &player;
145+
void setPlayer(AudioPlayer& player) override {
146+
AudioPlayerProtocol::setPlayer(player);
146147
volume = player.volume() * 254.0f;
147148
}
148149

150+
bool processCommand(Stream& input, Print& result) override {
151+
return AudioPlayerProtocol::processCommand(input, result);
152+
}
153+
149154
/// processes the commands and returns the result output via the Print
150155
/// object
151-
bool processCommand(const char* input, Print& result) {
156+
bool processCommand(const char* input, Print& result) override {
152157
if (p_player == nullptr) {
153158
LOGE("player not set");
154159
return false;
@@ -219,7 +224,6 @@ class KARadioProtocol {
219224
}
220225

221226
protected:
222-
AudioPlayer* p_player = nullptr;
223227
int volume = 0;
224228
Str title_str = "n/a";
225229
struct Action {
Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
#pragma once
2-
#include "AudioTools/CoreAudio/BaseStream.h"
3-
#include "AudioTools/CoreAudio/Buffers.h"
4-
#include "HttpServer.h"
2+
#include "AudioPlayerProtocolServer.h"
53
#include "KARadioProtocol.h"
64

75
namespace audio_tools {
86

97
/***
10-
* @brief KA-Radio Protocol: We can use the KA-Radio protocol over http to
11-
* control the audio player provided by the audiotools.
8+
* @brief KA-Radio Protocol Server which provides the KA-Radio protocol over
9+
* http to control the audio player provided by the audiotools.
10+
* @ingroup player
1211
* @author Phil Schatzmann
1312
*/
14-
class KARadioProtocolServer {
13+
14+
class KARadioProtocolServer : public AudioPlayerProtocolServer {
1515
public:
1616
/// Default constructor
1717
KARadioProtocolServer(AudioPlayer& player, int port = 80,
1818
const char* ssid = nullptr, const char* pwd = nullptr) {
19+
setProtocol(protocol);
1920
setPlayer(player);
2021
setPort(port);
2122
setSSID(ssid);
@@ -25,60 +26,7 @@ class KARadioProtocolServer {
2526
/// Empty constructor: call setPlayer to define the player
2627
KARadioProtocolServer() = default;
2728

28-
/// Defines the player
29-
void setPlayer(AudioPlayer& player) { protocol.setPlayer(player); }
30-
31-
void setPort(int port) { this->port = port; }
32-
void setSSID(const char* ssid) { this->ssid = ssid; }
33-
void setPassword(const char* password) { this->password = password; }
34-
void setSSID(const char* ssid, const char* password) {
35-
this->ssid = ssid;
36-
this->password = password;
37-
}
38-
39-
bool begin() {
40-
context[0] = this;
41-
server.on("/", T_GET, parse, context.data(), context.size());
42-
43-
// connect to WIFI
44-
if (ssid != nullptr && password != nullptr) {
45-
return server.begin(port, ssid, password);
46-
}
47-
return server.begin(port);
48-
}
49-
50-
void loop() { server.copy(); }
51-
void copy() { server.copy(); }
52-
53-
/// Defines the buffer size that is made available for the http reply
54-
void setBufferSize(int size){
55-
buffer_size = size;
56-
}
57-
5829
protected:
59-
WiFiServer wifi;
60-
HttpServer server{wifi};
6130
KARadioProtocol protocol;
62-
RingBuffer<uint8_t> ringBuffer{0};
63-
QueueStream<uint8_t> queueStream{ringBuffer};
64-
Vector<void*> context{1};
65-
int port = 80;
66-
const char* ssid = nullptr;
67-
const char* password = nullptr;
68-
int buffer_size = 512;
69-
70-
static void parse(HttpServer* server, const char* requestPath,
71-
HttpRequestHandlerLine* hl) {
72-
LOGI("parse: %s", requestPath);
73-
KARadioProtocolServer* self = (KARadioProtocolServer*)hl->context[0];
74-
self->ringBuffer.resize(self->buffer_size);
75-
QueueStream<uint8_t>& queueStream = self->queueStream;
76-
queueStream.begin();
77-
bool ok = self->protocol.processCommand(requestPath, queueStream);
78-
LOGI("available: %d", queueStream.available());
79-
server->reply("text/plain", queueStream, queueStream.available(),
80-
ok ? 200 : 400, ok ? SUCCESS : "Error");
81-
self->ringBuffer.resize(0);
82-
}
8331
};
8432
} // namespace audio_tools

0 commit comments

Comments
 (0)