1+ #pragma once
2+
3+ #include < optional>
4+
5+ #include " AudioTools/CoreAudio/AudioBasic/Str.h"
6+ #include " AudioTools/CoreAudio/AudioPlayer.h"
7+ #include " AudioTools/Disk/AudioSource.h"
8+
9+ #define KA_VERSION " Release: 2.4, Revision: R0"
10+
11+ namespace audio_tools {
12+
13+ /* **
14+ * @brief KA-Radio Protocol: We can use the KA-Radio protocol to control the
15+ * audio player provided by the audiotools. Example: volume=50&play=128&infos
16+ * See https://github.com/karawin/Ka-Radio32/blob/master/Interface.md
17+ * @author Phil Schatzmann
18+ */
19+
20+ class KARadioProtocol {
21+ public:
22+ // / Default constructor
23+ KARadioProtocol (AudioPlayer& player) { setPlayer (player); }
24+
25+ // / Empty constructor: call setPlayer to define the player
26+ KARadioProtocol () = default ;
27+
28+ // / Defines the player
29+ void setPlayer (AudioPlayer& player) {
30+ p_player = &player;
31+ volume = player.volume () * 254 .0f ;
32+ }
33+
34+ // / processes the commands and returns the result output via the Print object
35+ bool processCommand (const char * input, Print& result) {
36+ if (p_player == nullptr ) {
37+ LOGE (" player not set" );
38+ return false ;
39+ }
40+
41+ Str name;
42+ Str arg;
43+ StrView line = input;
44+
45+ int start = line.indexOf (' ?' );
46+ if (start < 0 ) start = 0 ;
47+ int endPos = line.length ();
48+ bool rc = true ;
49+ while (start >= 0 && start < endPos) {
50+ int eqPos = line.indexOf (' =' , start);
51+ int toPos = getEndPos (line, start + 1 );
52+ if (eqPos >= 0 ) {
53+ name.substring (line, start + 1 , eqPos);
54+ arg.substring (line, eqPos + 1 , toPos);
55+ } else {
56+ name.substring (line, start + 1 , toPos);
57+ arg = " " ;
58+ }
59+ LOGD (" start=%d, eqPos=%d, toPos=%d" , start, eqPos, toPos);
60+ // remove leading and trailing spaces
61+ name.trim ();
62+ arg.trim ();
63+ // execute the command
64+ rc = processCommand (name, arg, result);
65+ // calculate new positions
66+ start = toPos;
67+ toPos = getEndPos (line, start + 1 );
68+ }
69+ return rc;
70+ }
71+
72+ // / Processes a single command
73+ bool processCommand (Str& name, Str& arg, Print& result) {
74+ LOGI (" command: %s (%s)" , name.c_str (), arg.c_str ());
75+ if (p_player == nullptr ) {
76+ LOGE (" No player set" );
77+ return false ;
78+ }
79+ if (name == " play" ) {
80+ if (!arg.isEmpty ()) {
81+ int idx = arg.toInt ();
82+ p_player->setIndex (idx);
83+ }
84+ } else if (name == " instant" ) {
85+ p_player->setPath (arg.c_str ());
86+ } else if (name == " volume" ) {
87+ if (!arg.isEmpty ()) {
88+ volume = arg.toInt ();
89+ p_player->setVolume (static_cast <float >(volume) / 254 .0f );
90+ }
91+ } else if (name == " volume+" ) {
92+ volume += 5 ;
93+ if (volume > 245 ) {
94+ volume = 254 ;
95+ }
96+ p_player->setVolume (static_cast <float >(volume) / 254 .0f );
97+ } else if (name == " volume-" ) {
98+ volume -= 5 ;
99+ if (volume < 0 ) {
100+ volume = 0 ;
101+ }
102+ p_player->setVolume (static_cast <float >(volume) / 254 .0f );
103+ } else if (name == " pause" ) {
104+ p_player->setActive (false );
105+ } else if (name == " resume" ) {
106+ p_player->setActive (true );
107+ } else if (name == " stop" ) {
108+ p_player->setActive (false );
109+ } else if (name == " start" ) {
110+ p_player->setActive (true );
111+ } else if (name == " next" ) {
112+ p_player->next ();
113+ } else if (name == " prev" ) {
114+ p_player->previous ();
115+ } else if (name == " version" ) {
116+ result.print (" version: " );
117+ result.println (KA_VERSION);
118+ } else if (name == " mute" ) {
119+ if (!arg.isEmpty ()) {
120+ p_player->setActive (!(arg.toInt () == 1 ));
121+ }
122+ } else if (name == " infos" ) {
123+ result.print (" vol: " );
124+ result.println (volume);
125+ result.print (" num: " );
126+ result.println (index ());
127+ result.print (" stn: " ); // station
128+ result.println (stationName ());
129+ result.print (" tit: " ); // title
130+ result.println (title ());
131+ result.print (" sts: " ); // status
132+ result.println (p_player->isActive ());
133+ } else if (name == " list" ) {
134+ // arg: 0 to 254
135+ if (!arg.isEmpty ()) {
136+ p_player->setIndex (arg.toInt ());
137+ }
138+ result.println (stationName ());
139+ } else {
140+ LOGE (" Invalid command:" , name.c_str ());
141+ return false ;
142+ }
143+ result.flush ();
144+ return true ;
145+ }
146+
147+ // / Provides the actual index
148+ int index () { return p_player->audioSource ().index (); }
149+
150+ // / Provides the actual title
151+ const char * title () { return title_str.c_str (); }
152+
153+ protected:
154+ AudioPlayer* p_player = nullptr ;
155+ int volume = 0 ;
156+ Str title_str = " n/a" ;
157+
158+ int getEndPos (StrView& line, int start) {
159+ int endPos = line.indexOf (' &' , start);
160+ if (endPos < 0 ) {
161+ endPos = line.length ();
162+ }
163+ return endPos;
164+ }
165+
166+ const char * stationName () {
167+ if (p_player != nullptr ) {
168+ return p_player->audioSource ().toStr ();
169+ }
170+ return " " ;
171+ }
172+ };
173+ } // namespace audio_tools
0 commit comments