arduino-audio-tools
Loading...
Searching...
No Matches
MetaDataICY.h
1#pragma once
2#include "AbstractMetaData.h"
3#include "AudioTools/Communication/HTTP/AbstractURLStream.h"
4#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
5#include "AudioToolsConfig.h"
6
7#ifndef AUDIOTOOLS_METADATA_ICY_ASCII_ONLY
8#define AUDIOTOOLS_METADATA_ICY_ASCII_ONLY true
9#endif
10
11namespace audio_tools {
12
29 enum Status { ProcessData, ProcessMetaData, SetupSize };
30
31 public:
32 MetaDataICY() = default;
33
35 MetaDataICY(int metaint) { setIcyMetaInt(metaint); }
36
37 virtual ~MetaDataICY() {}
38
40 virtual void setIcyMetaInt(int value) override {
41 this->mp3_blocksize = value;
42 }
43
45 virtual void setCallback(void (*fn)(MetaDataType info, const char* str,
46 int len)) override {
47 callback = fn;
48 }
49
51 virtual void setAudioDataCallback(void (*fn)(const uint8_t* str, int len),
52 int bufferLen = 1024) {
53 dataBuffer = new uint8_t[bufferLen];
54 dataCallback = fn;
55 dataLen = 0;
56 dataPos = 0;
57 }
58
60 virtual bool begin() override {
61 clear();
62 LOGI("mp3_blocksize: %d", mp3_blocksize);
63 return true;
64 }
65
67 virtual void end() override { clear(); }
68
71 virtual size_t write(const uint8_t* data, size_t len) override {
72 if (callback != nullptr) {
73 for (size_t j = 0; j < len; j++) {
74 processChar((char)data[j]);
75 }
76 }
77 return len;
78 }
79
81 virtual Status status() { return currentStatus; }
82
84 virtual bool isData() { return currentStatus == ProcessData; }
85
87 virtual bool hasMetaData() { return this->mp3_blocksize > 0; }
88
90 virtual int metaInt() { return mp3_blocksize; }
91
93 virtual void processChar(char ch) {
94 switch (nextStatus) {
95 case ProcessData:
96 currentStatus = ProcessData;
97 processData(ch);
98
99 // increment data counter and determine next status
100 ++totalData;
101 if (totalData >= mp3_blocksize) {
102 LOGI("Data ended")
103 totalData = 0;
104 nextStatus = SetupSize;
105 }
106 break;
107
108 case SetupSize:
109 currentStatus = SetupSize;
110 totalData = 0;
111 metaDataPos = 0;
112 metaDataLen = metaSize(ch);
113 LOGI("metaDataLen: %d", metaDataLen);
114 if (metaDataLen > 0) {
115 if (metaDataLen > 200) {
116 LOGI("Unexpected metaDataLen -> processed as data");
117 nextStatus = ProcessData;
118 } else {
119 LOGI("Metadata found");
120 setupMetaData(metaDataLen);
121 nextStatus = ProcessMetaData;
122 }
123 } else {
124 LOGI("Data found");
125 nextStatus = ProcessData;
126 }
127 break;
128
129 case ProcessMetaData:
130 currentStatus = ProcessMetaData;
131 metaData[metaDataPos++] = ch;
132 if (metaDataPos >= metaDataLen) {
133 processMetaData(metaData.data(), metaDataLen);
134 LOGI("Metadata ended")
135 nextStatus = ProcessData;
136 }
137 break;
138 }
139 }
140
142 void setAsciiOnly(bool value) { is_ascii = value; }
143
144 protected:
145 Status nextStatus = ProcessData;
146 Status currentStatus = ProcessData;
147 void (*callback)(MetaDataType info, const char* str, int len) = nullptr;
148 Vector<char> metaData{0};
149 int totalData = 0;
150 int mp3_blocksize = 0;
151 int metaDataMaxLen = 0;
152 int metaDataLen = 0;
153 int metaDataPos = 0;
154 bool is_data; // indicates if the current byte is a data byte
155 // data
156 uint8_t* dataBuffer = nullptr;
157 void (*dataCallback)(const uint8_t* str, int len) = nullptr;
158 int dataLen = 0;
159 int dataPos = 0;
160 bool is_ascii = AUDIOTOOLS_METADATA_ICY_ASCII_ONLY;
161
162 virtual void clear() {
163 nextStatus = ProcessData;
164 totalData = 0;
165 metaData.resize(0);
166 metaDataLen = 0;
167 metaDataPos = 0;
168 dataLen = 0;
169 dataPos = 0;
170 }
171
173 virtual int metaSize(uint8_t metaSize) { return metaSize * 16; }
174
176 virtual bool isPrintable(const char* str, int l) {
177 int remain = 0;
178 for (int j = 0; j < l; j++) {
179 uint8_t ch = str[j];
180 if (remain) {
181 if (ch < 0x80 || ch > 0xbf) {
182 LOGD("Invalid UTF-8 continuation byte: 0x%02X at pos %d", ch, j);
183 return false;
184 }
185 remain--;
186 } else {
187 if (ch < 0x80) { // ASCII
188 // Allow '\0' as printable (do not treat as control char)
189 if (ch != '\n' && ch != '\r' && ch != '\t' && ch != 0 && (ch < 32 || ch == 127)) {
190 LOGD("Non-printable ASCII character: 0x%02X at pos %d", ch, j);
191 return false; // control chars except allowed ones
192 }
193 } else if (!is_ascii) {
194 if (ch >= 0xc2 && ch <= 0xdf)
195 remain = 1;
196 else if (ch >= 0xe0 && ch <= 0xef)
197 remain = 2;
198 else if (ch >= 0xf0 && ch <= 0xf4)
199 remain = 3;
200 else {
201 LOGD("Invalid UTF-8 lead byte: 0x%02X at pos %d", ch, j);
202 return false;
203 }
204 } else {
205 LOGD("Non-ASCII character not allowed: 0x%02X at pos %d", ch, j);
206 return false;
207 }
208 }
209 }
210 if (remain != 0) {
211 LOGD("Incomplete UTF-8 sequence at end of string");
212 }
213 return remain == 0;
214 }
215
217 virtual void setupMetaData(int meta_size) {
218 TRACED();
219 metaData.resize(meta_size + 1);
220 metaDataMaxLen = meta_size;
221 memset(metaData.data(), 0, meta_size + 1);
222 }
223
226 virtual void processMetaData(char* metaData, int len) {
227 // CHECK_MEMORY();
228 TRACED();
229 metaData[len] = 0;
230 if (isPrintable(metaData, len)) {
231 LOGI("%s", metaData);
232 StrView meta(metaData, len + 1, len);
233 int start = meta.indexOf("StreamTitle=");
234 if (start >= 0) {
235 start += 12;
236 }
237 int end = meta.indexOf("';");
238 if (start >= 0 && end > start) {
239 metaData[end] = 0;
240 if (callback != nullptr) {
241 callback(Title, (const char*)metaData + start + 1, end - start);
242 }
243 }
244 // CHECK_MEMORY();
245 } else {
246 // CHECK_MEMORY();
247 // Don't print corrupted binary data - could contain terminal control
248 // codes
249 LOGW("Unexpected Data: corrupted metadata block rejected (len=%d)", len);
250 // Signal corruption to application so it can disconnect/reconnect
251 if (callback != nullptr) {
252 callback(Corrupted, nullptr, len);
253 }
254 }
255 }
256
259 virtual void processData(char ch) {
260 if (dataBuffer != nullptr) {
261 dataBuffer[dataPos++] = ch;
262 // output data via callback
263 if (dataPos >= dataLen) {
264 dataCallback(dataBuffer, dataLen);
265 dataPos = 0;
266 }
267 }
268 }
269};
270
279 public:
283 TRACED();
284 p_url = &url;
285 const char* iceMetaintStr = url.getReplyHeader("icy-metaint");
286 if (iceMetaintStr) {
287 LOGI("icy-metaint: %s", iceMetaintStr);
288 } else {
289 LOGE("icy-metaint not defined");
290 }
291 StrView value(iceMetaintStr);
292 int iceMetaint = value.toInt();
293 return iceMetaint;
294 }
295
298 void executeCallback(void (*callback)(MetaDataType info, const char* str,
299 int len)) {
300 TRACEI();
301 if (callback == nullptr) {
302 LOGW("callback not defined")
303 }
304 if (p_url == nullptr) {
305 LOGW("http not defined")
306 }
307 // Callbacks filled from url reply for icy
308 if (callback != nullptr && p_url != nullptr) {
309 // handle icy parameters
310 StrView genre(p_url->getReplyHeader("icy-genre"));
311 if (!genre.isEmpty()) {
312 callback(Genre, genre.c_str(), genre.length());
313 }
314
315 StrView descr(p_url->getReplyHeader("icy-description"));
316 if (!descr.isEmpty()) {
317 callback(Description, descr.c_str(), descr.length());
318 }
319
320 StrView name(p_url->getReplyHeader("icy-name"));
321 if (!name.isEmpty()) {
322 callback(Name, name.c_str(), name.length());
323 }
324 }
325 }
326
327
328 protected:
329 AbstractURLStream* p_url = nullptr;
330};
331
332} // namespace audio_tools
Common Metadata methods.
Definition AbstractMetaData.h:34
Abstract Base class for all URLStream implementations.
Definition AbstractURLStream.h:17
virtual const char * getReplyHeader(const char *header)=0
Provides reply header information.
Resolve icy-metaint from HttpRequest and execute metadata callbacks.
Definition MetaDataICY.h:278
int setup(AbstractURLStream &url)
Definition MetaDataICY.h:282
void executeCallback(void(*callback)(MetaDataType info, const char *str, int len))
Definition MetaDataICY.h:298
Icecast/Shoutcast Metadata Handling. Metadata class which splits the data into audio and metadata....
Definition MetaDataICY.h:28
virtual void setCallback(void(*fn)(MetaDataType info, const char *str, int len)) override
Defines the metadata callback function.
Definition MetaDataICY.h:45
virtual Status status()
Returns the actual status of the state engine for the current byte.
Definition MetaDataICY.h:81
virtual size_t write(const uint8_t *data, size_t len) override
Definition MetaDataICY.h:71
MetaDataICY(int metaint)
We just process the Metadata and ignore the audio info.
Definition MetaDataICY.h:35
virtual bool begin() override
Resets all counters and restarts the prcessing.
Definition MetaDataICY.h:60
virtual void setupMetaData(int meta_size)
allocates the memory to store the metadata / we support changing sizes
Definition MetaDataICY.h:217
virtual int metaSize(uint8_t metaSize)
determines the meta data size from the size byte
Definition MetaDataICY.h:173
void setAsciiOnly(bool value)
Sets whether to only accept ASCII characters in metadata (default is true)
Definition MetaDataICY.h:142
virtual bool hasMetaData()
Returns true if the ICY stream contains metadata.
Definition MetaDataICY.h:87
virtual void setAudioDataCallback(void(*fn)(const uint8_t *str, int len), int bufferLen=1024)
Defines the audio callback function.
Definition MetaDataICY.h:51
virtual int metaInt()
provides the metaint
Definition MetaDataICY.h:90
virtual void processMetaData(char *metaData, int len)
Definition MetaDataICY.h:226
virtual void end() override
Resets all counters and restarts the prcessing.
Definition MetaDataICY.h:67
virtual void setIcyMetaInt(int value) override
Defines the ICE metaint value which is provided by the web call!
Definition MetaDataICY.h:40
virtual bool isData()
returns true if the actual bytes is an audio data byte (e.g.mp3)
Definition MetaDataICY.h:84
virtual void processData(char ch)
Definition MetaDataICY.h:259
virtual void processChar(char ch)
character based state engine
Definition MetaDataICY.h:93
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual int length()
Definition StrView.h:383
virtual bool isEmpty()
checks if the string is empty
Definition StrView.h:386
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
int toInt()
Converts the string to an int.
Definition StrView.h:575
virtual int indexOf(const char c, int start=0)
Definition StrView.h:260
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
MetaDataType
Type of meta info.
Definition AbstractMetaData.h:11
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10