arduino-audio-tools
MetaDataICY.h
1 #pragma once
2 #include "AudioConfig.h"
3 #ifdef USE_URL_ARDUINO
4 
5 #include "AbstractMetaData.h"
6 #include "AudioTools/CoreAudio/AudioBasic/StrView.h"
7 #include "AudioTools/CoreAudio/AudioHttp/HttpRequest.h"
8 
9 namespace audio_tools {
10 
26 class MetaDataICY : public AbstractMetaData {
27 
28  enum Status {ProcessData, ProcessMetaData, SetupSize};
29 
30  public:
31  MetaDataICY() = default;
32 
34  MetaDataICY(int metaint){
35  setIcyMetaInt(metaint);
36  }
37 
38  ~MetaDataICY(){
39  if (metaData!=nullptr) delete[]metaData;
40  }
41 
43  virtual void setIcyMetaInt(int value) override {
44  this->mp3_blocksize = value;
45  }
46 
48  virtual void setCallback(void (*fn)(MetaDataType info, const char* str, int len)) override {
49  callback = fn;
50  }
51 
53  virtual void setAudioDataCallback(void (*fn)(const uint8_t* str, int len), int bufferLen=1024) {
54  dataBuffer = new uint8_t[bufferLen];
55  dataCallback = fn;
56  dataLen = 0;
57  dataPos = 0;
58  }
59 
61  virtual void begin() override {
62  clear();
63  LOGI("mp3_blocksize: %d", mp3_blocksize);
64  }
65 
67  virtual void end() override {
68  clear();
69  }
70 
72  virtual size_t write(const uint8_t *data, size_t len) override {
73  if (callback!=nullptr){
74  for (size_t j=0;j<len;j++){
75  processChar((char)data[j]);
76  }
77  }
78  return len;
79  }
80 
82  virtual Status status() {
83  return currentStatus;
84  }
85 
87  virtual bool isData(){
88  return currentStatus==ProcessData;
89  }
90 
92  virtual bool hasMetaData() {
93  return this->mp3_blocksize>0;
94  }
95 
97  virtual int metaInt() {
98  return mp3_blocksize;
99  }
100 
102  virtual void processChar(char ch){
103  switch(nextStatus){
104  case ProcessData:
105  currentStatus = ProcessData;
106  processData(ch);
107 
108  // increment data counter and determine next status
109  ++totalData;
110  if (totalData>=mp3_blocksize){
111  LOGI("Data ended")
112  totalData = 0;
113  nextStatus = SetupSize;
114  }
115  break;
116 
117  case SetupSize:
118  currentStatus = SetupSize;
119  totalData = 0;
120  metaDataPos = 0;
121  metaDataLen = metaSize(ch);
122  LOGI("metaDataLen: %d", metaDataLen);
123  if (metaDataLen>0){
124  if (metaDataLen>200){
125  LOGI("Unexpected metaDataLen -> processed as data");
126  nextStatus = ProcessData;
127  } else {
128  LOGI("Metadata found");
129  setupMetaData(metaDataLen);
130  nextStatus = ProcessMetaData;
131  }
132  } else {
133  LOGI("Data found");
134  nextStatus = ProcessData;
135  }
136  break;
137 
138  case ProcessMetaData:
139  currentStatus = ProcessMetaData;
140  metaData[metaDataPos++]=ch;
141  if (metaDataPos>=metaDataLen){
142  processMetaData(metaData, metaDataLen);
143  LOGI("Metadata ended")
144  nextStatus = ProcessData;
145  }
146  break;
147  }
148  }
149 
150 
151  protected:
152  Status nextStatus = ProcessData;
153  Status currentStatus = ProcessData;
154  void (*callback)(MetaDataType info, const char* str, int len) = nullptr;
155  char* metaData=nullptr;
156  int totalData = 0;
157  int mp3_blocksize = 0;
158  int metaDataMaxLen = 0;
159  int metaDataLen = 0;
160  int metaDataPos = 0;
161  bool is_data; // indicates if the current byte is a data byte
162  // data
163  uint8_t *dataBuffer=nullptr;
164  void (*dataCallback)(const uint8_t* str, int len) = nullptr;
165  int dataLen = 0;
166  int dataPos = 0;
167 
168  virtual void clear() {
169  nextStatus = ProcessData;
170  totalData = 0;
171  metaDataLen = 0;
172  metaDataPos = 0;
173  dataLen = 0;
174  dataPos = 0;
175  }
176 
178  virtual int metaSize(uint8_t metaSize){
179  return metaSize*16;
180  }
181 
183  virtual bool isAscii(char* result, int l){
184  // check on first 10 characters
185  int m = l < 5 ? l : 10;
186  for (int j=0; j<m; j++){
187  if (!isascii(result[j])) return false;
188  }
189  return true;
190  }
191 
193  virtual void setupMetaData(int meta_size) {
194  TRACED();
195  if (meta_size>0){
196  if (metaData==nullptr){
197  metaData = new char[meta_size+1];
198  metaDataMaxLen = meta_size;
199  LOGD("metaDataMaxLen: %d", metaDataMaxLen);
200  } else {
201  if (meta_size>metaDataMaxLen){
202  delete [] metaData;
203  metaData = new char[meta_size+1];
204  metaDataMaxLen = meta_size;
205  LOGD("metaDataMaxLen: %d", metaDataMaxLen);
206  }
207  }
208  memset(metaData, 0, meta_size);
209  }
210  }
211 
213  virtual void processMetaData( char* metaData, int len) {
214  //CHECK_MEMORY();
215  TRACED();
216  metaData[len]=0;
217  if (isAscii(metaData, 12)){
218  LOGI("%s", metaData);
219  StrView meta(metaData,len+1, len);
220  int start = meta.indexOf("StreamTitle=");
221  if (start>=0){
222  start+=12;
223  }
224  int end = meta.indexOf("';");
225  if (start>=0 && end>start){
226  metaData[end]=0;
227  if (callback!=nullptr){
228  callback(Title, (const char*)metaData+start+1, end-start);
229  }
230  }
231  // CHECK_MEMORY();
232  } else {
233  // CHECK_MEMORY();
234  LOGW("Unexpected Data: %s", metaData);
235  }
236  }
237 
239  virtual void processData(char ch){
240  if (dataBuffer!=nullptr){
241  dataBuffer[dataPos++] = ch;
242  // output data via callback
243  if (dataPos>=dataLen){
244  dataCallback(dataBuffer, dataLen);
245  dataPos = 0;
246  }
247  }
248  }
249 };
250 
251 
259 class ICYUrlSetup {
260  public:
262  int setup(HttpRequest &http ) {
263  TRACED();
264  p_http = &http;
265  const char* iceMetaintStr = http.reply().get("icy-metaint");
266  if (iceMetaintStr){
267  LOGI("icy-metaint: %s", iceMetaintStr);
268  } else {
269  LOGE("icy-metaint not defined");
270  }
271  StrView value(iceMetaintStr);
272  int iceMetaint = value.toInt();
273  return iceMetaint;
274  }
275 
277  void executeCallback(void (*callback)(MetaDataType info, const char* str, int len)) {
278  TRACEI();
279  if (callback==nullptr){
280  LOGW("callback not defined")
281  }
282  if (p_http==nullptr){
283  LOGW("http not defined")
284  }
285  // Callbacks filled from url reply for icy
286  if (callback!=nullptr && p_http!=nullptr) {
287  // handle icy parameters
288  StrView genre(p_http->reply().get("icy-genre"));
289  if (!genre.isEmpty()){
290  callback(Genre, genre.c_str(), genre.length());
291  }
292 
293  StrView descr(p_http->reply().get("icy-description"));
294  if (!descr.isEmpty()){
295  callback(Description, descr.c_str(), descr.length());
296  }
297 
298  StrView name(p_http->reply().get("icy-name"));
299  if (!name.isEmpty()){
300  callback(Name, name.c_str(), name.length());
301  }
302  }
303  }
304 
305  protected:
306  HttpRequest *p_http = nullptr;
307 
308 };
309 
310 }
311 
312 #endif
Common Metadata methods.
Definition: AbstractMetaData.h:34
Simple API to process get, put, post, del http requests I tried to use Arduino HttpClient,...
Definition: HttpRequest.h:26
Resolve icy-metaint from HttpRequest and execute metadata callbacks.
Definition: MetaDataICY.h:259
int setup(HttpRequest &http)
Fills the metaint from the Http Request and executes metadata callbacks on http reply parameters.
Definition: MetaDataICY.h:262
void executeCallback(void(*callback)(MetaDataType info, const char *str, int len))
Executes the metadata callbacks with data provided from the http request result parameter.
Definition: MetaDataICY.h:277
Icecast/Shoutcast Metadata Handling. Metadata class which splits the data into audio and metadata....
Definition: MetaDataICY.h:26
virtual void setCallback(void(*fn)(MetaDataType info, const char *str, int len)) override
Defines the metadata callback function.
Definition: MetaDataICY.h:48
virtual Status status()
Returns the actual status of the state engine for the current byte.
Definition: MetaDataICY.h:82
virtual size_t write(const uint8_t *data, size_t len) override
Writes the data in order to retrieve the metadata and perform the corresponding callbacks.
Definition: MetaDataICY.h:72
MetaDataICY(int metaint)
We just process the Metadata and ignore the audio info.
Definition: MetaDataICY.h:34
virtual bool isAscii(char *result, int l)
Make sure that the result is a valid ASCII string.
Definition: MetaDataICY.h:183
virtual void setupMetaData(int meta_size)
allocates the memory to store the metadata / we support changing sizes
Definition: MetaDataICY.h:193
virtual int metaSize(uint8_t metaSize)
determines the meta data size from the size byte
Definition: MetaDataICY.h:178
virtual void begin() override
Resets all counters and restarts the prcessing.
Definition: MetaDataICY.h:61
virtual bool hasMetaData()
Returns true if the ICY stream contains metadata.
Definition: MetaDataICY.h:92
virtual void setAudioDataCallback(void(*fn)(const uint8_t *str, int len), int bufferLen=1024)
Defines the audio callback function.
Definition: MetaDataICY.h:53
virtual int metaInt()
provides the metaint
Definition: MetaDataICY.h:97
virtual void processMetaData(char *metaData, int len)
e.g. StreamTitle=' House Bulldogs - But your love (Radio Edit)';StreamUrl='';
Definition: MetaDataICY.h:213
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:43
virtual bool isData()
returns true if the actual bytes is an audio data byte (e.g.mp3)
Definition: MetaDataICY.h:87
virtual void processData(char ch)
Collects the data in a buffer and executes the callback when the buffer is full.
Definition: MetaDataICY.h:239
virtual void processChar(char ch)
character based state engine
Definition: MetaDataICY.h:102
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
MetaDataType
Type of meta info.
Definition: AbstractMetaData.h:11
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868