arduino-audio-tools
Loading...
Searching...
No Matches
MetaDataICY.h
Go to the documentation of this file.
1#pragma once
2#include "AbstractMetaData.h"
5#include "AudioToolsConfig.h"
6
7namespace audio_tools {
8
25 enum Status { ProcessData, ProcessMetaData, SetupSize };
26
27 public:
28 MetaDataICY() = default;
29
32
33 virtual ~MetaDataICY() {}
34
36 virtual void setIcyMetaInt(int value) override {
37 this->mp3_blocksize = value;
38 }
39
41 virtual void setCallback(void (*fn)(MetaDataType info, const char* str,
42 int len)) override {
43 callback = fn;
44 }
45
47 virtual void setAudioDataCallback(void (*fn)(const uint8_t* str, int len),
48 int bufferLen = 1024) {
49 dataBuffer = new uint8_t[bufferLen];
51 dataLen = 0;
52 dataPos = 0;
53 }
54
56 virtual bool begin() override {
57 clear();
58 LOGI("mp3_blocksize: %d", mp3_blocksize);
59 return true;
60 }
61
63 virtual void end() override { clear(); }
64
67 virtual size_t write(const uint8_t* data, size_t len) override {
68 if (callback != nullptr) {
69 for (size_t j = 0; j < len; j++) {
70 processChar((char)data[j]);
71 }
72 }
73 return len;
74 }
75
77 virtual Status status() { return currentStatus; }
78
80 virtual bool isData() { return currentStatus == ProcessData; }
81
83 virtual bool hasMetaData() { return this->mp3_blocksize > 0; }
84
86 virtual int metaInt() { return mp3_blocksize; }
87
89 virtual void processChar(char ch) {
90 switch (nextStatus) {
91 case ProcessData:
92 currentStatus = ProcessData;
94
95 // increment data counter and determine next status
96 ++totalData;
97 if (totalData >= mp3_blocksize) {
98 LOGI("Data ended")
99 totalData = 0;
100 nextStatus = SetupSize;
101 }
102 break;
103
104 case SetupSize:
105 currentStatus = SetupSize;
106 totalData = 0;
107 metaDataPos = 0;
109 LOGI("metaDataLen: %d", metaDataLen);
110 if (metaDataLen > 0) {
111 if (metaDataLen > maxLimit) {
112 LOGI("Unexpected metaDataLen -> processed as data");
113 nextStatus = ProcessData;
114 } else {
115 LOGI("Metadata found");
117 nextStatus = ProcessMetaData;
118 }
119 } else {
120 LOGI("Data found");
121 nextStatus = ProcessData;
122 }
123 break;
124
125 case ProcessMetaData:
126 currentStatus = ProcessMetaData;
128 if (metaDataPos >= metaDataLen) {
130 LOGI("Metadata ended")
131 nextStatus = ProcessData;
132 }
133 break;
134 }
135 }
136
138 void setAsciiOnly(bool value) { is_ascii = value; }
139
141 void setMaxMetaDataLimit(int limit) { maxLimit = limit; }
142
143 protected:
144 Status nextStatus = ProcessData;
145 Status currentStatus = ProcessData;
146 void (*callback)(MetaDataType info, const char* str, int len) = nullptr;
148 int totalData = 0;
151 int metaDataLen = 0;
152 int metaDataPos = 0;
153 bool is_data; // indicates if the current byte is a data byte
154 // data
155 uint8_t* dataBuffer = nullptr;
156 void (*dataCallback)(const uint8_t* str, int len) = nullptr;
157 int dataLen = 0;
158 int dataPos = 0;
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();
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) {
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:
330};
331
332} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACEI()
Definition AudioLoggerIDF.h:32
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define AUDIOTOOLS_METADATA_ICY_LIMIT
Definition AudioToolsConfig.h:177
#define AUDIOTOOLS_METADATA_ICY_ASCII_ONLY
Definition AudioToolsConfig.h:173
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
AbstractURLStream * p_url
Definition MetaDataICY.h:329
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:24
virtual void setCallback(void(*fn)(MetaDataType info, const char *str, int len)) override
Defines the metadata callback function.
Definition MetaDataICY.h:41
virtual Status status()
Returns the actual status of the state engine for the current byte.
Definition MetaDataICY.h:77
virtual size_t write(const uint8_t *data, size_t len) override
Definition MetaDataICY.h:67
int metaDataMaxLen
Definition MetaDataICY.h:150
int maxLimit
Definition MetaDataICY.h:160
virtual ~MetaDataICY()
Definition MetaDataICY.h:33
int mp3_blocksize
Definition MetaDataICY.h:149
MetaDataICY(int metaint)
We just process the Metadata and ignore the audio info.
Definition MetaDataICY.h:31
int metaDataLen
Definition MetaDataICY.h:151
Status currentStatus
Definition MetaDataICY.h:145
virtual bool begin() override
Resets all counters and restarts the prcessing.
Definition MetaDataICY.h:56
uint8_t * dataBuffer
Definition MetaDataICY.h:155
virtual void setupMetaData(int meta_size)
allocates the memory to store the metadata / we support changing sizes
Definition MetaDataICY.h:217
bool is_ascii
Definition MetaDataICY.h:159
int dataLen
Definition MetaDataICY.h:157
Status nextStatus
Definition MetaDataICY.h:144
virtual bool isPrintable(const char *str, int l)
Definition MetaDataICY.h:176
int dataPos
Definition MetaDataICY.h:158
virtual int metaSize(uint8_t metaSize)
determines the meta data size from the size byte
Definition MetaDataICY.h:173
void(* callback)(MetaDataType info, const char *str, int len)
Definition MetaDataICY.h:146
void setAsciiOnly(bool value)
Sets whether to only accept ASCII characters in metadata (default is true)
Definition MetaDataICY.h:138
void setMaxMetaDataLimit(int limit)
Sets the maximum allowed metadata length (default is 400)
Definition MetaDataICY.h:141
virtual void clear()
Definition MetaDataICY.h:162
int totalData
Definition MetaDataICY.h:148
virtual bool hasMetaData()
Returns true if the ICY stream contains metadata.
Definition MetaDataICY.h:83
virtual void setAudioDataCallback(void(*fn)(const uint8_t *str, int len), int bufferLen=1024)
Defines the audio callback function.
Definition MetaDataICY.h:47
Vector< char > metaData
Definition MetaDataICY.h:147
virtual int metaInt()
provides the metaint
Definition MetaDataICY.h:86
virtual void processMetaData(char *metaData, int len)
Definition MetaDataICY.h:226
void(* dataCallback)(const uint8_t *str, int len)
Definition MetaDataICY.h:156
virtual void end() override
Resets all counters and restarts the prcessing.
Definition MetaDataICY.h:63
virtual void setIcyMetaInt(int value) override
Defines the ICE metaint value which is provided by the web call!
Definition MetaDataICY.h:36
bool is_data
Definition MetaDataICY.h:153
virtual bool isData()
returns true if the actual bytes is an audio data byte (e.g.mp3)
Definition MetaDataICY.h:80
int metaDataPos
Definition MetaDataICY.h:152
virtual void processData(char ch)
Definition MetaDataICY.h:259
virtual void processChar(char ch)
character based state engine
Definition MetaDataICY.h:89
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:576
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
bool resize(int newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
MetaDataType
Type of meta info.
Definition AbstractMetaData.h:11
@ Genre
Definition AbstractMetaData.h:11
@ Description
Definition AbstractMetaData.h:11
@ Name
Definition AbstractMetaData.h:11
@ Title
Definition AbstractMetaData.h:11
@ Corrupted
Definition AbstractMetaData.h:11
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:512