arduino-audio-tools
Loading...
Searching...
No Matches
MetaDataICY.h
1#pragma once
2#include "AudioToolsConfig.h"
3#include "AbstractMetaData.h"
4#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
5#include "AudioTools/Communication/HTTP/AbstractURLStream.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
141 protected:
142 Status nextStatus = ProcessData;
143 Status currentStatus = ProcessData;
144 void (*callback)(MetaDataType info, const char* str, int len) = nullptr;
145 Vector<char> metaData{0};
146 int totalData = 0;
147 int mp3_blocksize = 0;
148 int metaDataMaxLen = 0;
149 int metaDataLen = 0;
150 int metaDataPos = 0;
151 bool is_data; // indicates if the current byte is a data byte
152 // data
153 uint8_t* dataBuffer = nullptr;
154 void (*dataCallback)(const uint8_t* str, int len) = nullptr;
155 int dataLen = 0;
156 int dataPos = 0;
157
158 virtual void clear() {
159 nextStatus = ProcessData;
160 totalData = 0;
161 metaData.resize(0);
162 metaDataLen = 0;
163 metaDataPos = 0;
164 dataLen = 0;
165 dataPos = 0;
166 }
167
169 virtual int metaSize(uint8_t metaSize) { return metaSize * 16; }
170
172 virtual bool isPrintable(const char* str, int l) {
173 int remain = 0;
174 for (int j = 0; j < l; j++) {
175 uint8_t ch = str[j];
176 if (remain) {
177 if (ch < 0x80 || ch > 0xbf) return false;
178 remain--;
179 } else {
180 if (ch < 0x80) { // ASCII
181 if (ch != '\n' && ch != '\r' && ch != '\t' && ch < 32 || ch == 127)
182 return false; // control chars
183 }
184#if !AUDIOTOOLS_METADATA_ICY_ASCII_ONLY
185 else if (ch >= 0xc2 && ch <= 0xdf) remain = 1;
186 else if (ch >= 0xe0 && ch <= 0xef) remain = 2;
187 else if (ch >= 0xf0 && ch <= 0xf4) remain = 3;
188#endif
189 else return false;
190 }
191 }
192 return remain == 0;
193 }
194
196 virtual void setupMetaData(int meta_size) {
197 TRACED();
198 metaData.resize(meta_size + 1);
199 metaDataMaxLen = meta_size;
200 memset(metaData.data(), 0, meta_size + 1);
201 }
202
205 virtual void processMetaData(char* metaData, int len) {
206 // CHECK_MEMORY();
207 TRACED();
208 metaData[len] = 0;
209 if (isPrintable(metaData, len)) {
210 LOGI("%s", metaData);
211 StrView meta(metaData, len + 1, len);
212 int start = meta.indexOf("StreamTitle=");
213 if (start >= 0) {
214 start += 12;
215 }
216 int end = meta.indexOf("';");
217 if (start >= 0 && end > start) {
218 metaData[end] = 0;
219 if (callback != nullptr) {
220 callback(Title, (const char*)metaData + start + 1, end - start);
221 }
222 }
223 // CHECK_MEMORY();
224 } else {
225 // CHECK_MEMORY();
226 // Don't print corrupted binary data - could contain terminal control codes
227 LOGW("Unexpected Data: corrupted metadata block rejected (len=%d)", len);
228 // Signal corruption to application so it can disconnect/reconnect
229 if (callback != nullptr) {
230 callback(Corrupted, nullptr, len);
231 }
232 }
233 }
234
237 virtual void processData(char ch) {
238 if (dataBuffer != nullptr) {
239 dataBuffer[dataPos++] = ch;
240 // output data via callback
241 if (dataPos >= dataLen) {
242 dataCallback(dataBuffer, dataLen);
243 dataPos = 0;
244 }
245 }
246 }
247};
248
257 public:
261 TRACED();
262 p_url = &url;
263 const char* iceMetaintStr = url.getReplyHeader("icy-metaint");
264 if (iceMetaintStr) {
265 LOGI("icy-metaint: %s", iceMetaintStr);
266 } else {
267 LOGE("icy-metaint not defined");
268 }
269 StrView value(iceMetaintStr);
270 int iceMetaint = value.toInt();
271 return iceMetaint;
272 }
273
276 void executeCallback(void (*callback)(MetaDataType info, const char* str,
277 int len)) {
278 TRACEI();
279 if (callback == nullptr) {
280 LOGW("callback not defined")
281 }
282 if (p_url == nullptr) {
283 LOGW("http not defined")
284 }
285 // Callbacks filled from url reply for icy
286 if (callback != nullptr && p_url != nullptr) {
287 // handle icy parameters
288 StrView genre(p_url->getReplyHeader("icy-genre"));
289 if (!genre.isEmpty()) {
290 callback(Genre, genre.c_str(), genre.length());
291 }
292
293 StrView descr(p_url->getReplyHeader("icy-description"));
294 if (!descr.isEmpty()) {
295 callback(Description, descr.c_str(), descr.length());
296 }
297
298 StrView name(p_url->getReplyHeader("icy-name"));
299 if (!name.isEmpty()) {
300 callback(Name, name.c_str(), name.length());
301 }
302 }
303 }
304
305 protected:
306 AbstractURLStream* p_url = nullptr;
307};
308
309} // 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:256
int setup(AbstractURLStream &url)
Definition MetaDataICY.h:260
void executeCallback(void(*callback)(MetaDataType info, const char *str, int len))
Definition MetaDataICY.h:276
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:196
virtual bool isPrintable(const char *str, int l)
Make sure that the result is a printable string.
Definition MetaDataICY.h:172
virtual int metaSize(uint8_t metaSize)
determines the meta data size from the size byte
Definition MetaDataICY.h:169
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:205
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:237
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