arduino-audio-tools
Loading...
Searching...
No Matches
MultiDecoder.h
Go to the documentation of this file.
1
2#pragma once
3
4#include <cstring>
10
11namespace audio_tools {
12
51class MultiDecoder : public AudioDecoder {
52 public:
56 MultiDecoder() = default;
57
67 MultiDecoder(MimeSource& mimeSource) { setMimeSource(mimeSource); }
68
69 #ifdef USE_EXPERIMENTAL
76 // Clean up any adapters we created
77 for (auto* adapter : adapters) {
78 delete adapter;
79 }
80 adapters.clear();
81 }
82#endif
83
89 const char* mime() override { return actual_decoder.mime; }
90
99 bool begin() override {
101 is_first = true;
102 if (p_print == nullptr) {
103 LOGE("No output defined");
104 return false;
105 }
106 return true;
107 }
108
116 void end() override {
117 if (actual_decoder.decoder != nullptr && actual_decoder.is_open) {
119 }
120 actual_decoder.is_open = false;
121 actual_decoder.decoder = nullptr;
122 actual_decoder.mime = nullptr;
123 is_first = true;
124 }
125
135 void addDecoder(AudioDecoder& decoder, const char* mime = nullptr) {
136 const char* existing_mime = mime != nullptr ? mime : mime_detector.mime();
137 LOGI("Adding decoder for %s", existing_mime);
138 DecoderInfo info{existing_mime, &decoder};
139 decoder.addNotifyAudioChange(*this);
140 decoders.push_back(info);
141 }
142
151 void setOutput(Print& out_stream) override {
152 p_print = &out_stream;
153 }
154
170 void setMimeSource(MimeSource& mimeSource) { p_mime_source = &mimeSource; }
171
188 static void baseMimeType(const char* mime, char* out, size_t outSize) {
189 size_t len = 0;
190 while (mime[len] != '\0' && mime[len] != ';' && len < outSize - 1) len++;
191 while (len > 0 && (mime[len - 1] == ' ' || mime[len - 1] == '\t')) len--;
192 memcpy(out, mime, len);
193 out[len] = '\0';
194 }
195
196 bool selectDecoder(const char* mime) {
197 bool result = false;
198 if (mime == nullptr) return false;
199 // Same mime as before (e.g. back-to-back tracks of the same format):
200 // still restart the decoder so state (bit reservoir, frame buffers,
201 // etc.) from the previous track does not leak into the next one -
202 // just keep the already resolved DecoderInfo instead of re-searching
203 // the decoders list.
204 if (StrView(mime).equalsIgnoreCase(actual_decoder.mime)) {
205 is_first = false;
206 if (actual_decoder.decoder != nullptr) {
209 actual_decoder.is_open = true;
210 }
211 return true;
212 }
213 // close actual decoder
214 if (actual_decoder.decoder != this) end();
215
216 // A source may report a MIME type with an RFC 6381-style "codecs"
217 // (or other) parameter, e.g. "audio/mpeg; codecs=\"mpeg1-layer2\"" -
218 // a decoder registered for that exact parameterized type (a more
219 // specific match) must win over one only registered for the plain
220 // base type ("audio/mpeg"). Two passes, not one combined check: a
221 // single pass stops at the first entry that matches *either* way, so
222 // a generic base-type decoder registered before the specific one
223 // would otherwise shadow it purely by list order.
224 char base[64];
225 baseMimeType(mime, base, sizeof(base));
226 int exact_j = -1, base_j = -1;
227 for (int j = 0; j < decoders.size(); j++) {
228 if (StrView(decoders[j].mime).equalsIgnoreCase(mime)) { exact_j = j; break; }
229 if (base_j < 0 && StrView(decoders[j].mime).equalsIgnoreCase(base)) base_j = j;
230 }
231 int match_j = exact_j >= 0 ? exact_j : base_j;
232
233 // find the corresponding decoder
234 selected_mime = nullptr;
235 if (match_j >= 0) {
236 DecoderInfo info = decoders[match_j];
237 LOGI("Using decoder for %s (%s)", info.mime, mime);
239 // define output if it has not been defined
240 if (p_print != nullptr && actual_decoder.decoder != this
241 && actual_decoder.decoder->getOutput() == nullptr) {
243 }
244 if (!*actual_decoder.decoder) {
246 LOGI("Decoder %s started", actual_decoder.mime);
247 }
248 // mark as open so that a later end()/format switch actually calls
249 // decoder->end() instead of silently skipping it (is_open defaults
250 // to false and was previously only ever set for the 'nop' fallback)
251 actual_decoder.is_open = true;
252 result = true;
254 }
255 is_first = false;
256 return result;
257 }
258
265 const char* selectedMime() { return selected_mime; }
266
281 size_t write(const uint8_t* data, size_t len) override {
282 if (is_first) {
283 const char* mime = nullptr;
284 if (p_mime_source != nullptr) {
285 // get content type from http header
287 if (mime) LOGI("mime from http request: %s", mime);
288 }
289 if (mime == nullptr) {
290 // use the mime detector
291 mime_detector.write((uint8_t*)data, len);
293 if (mime) LOGI("mime from mime_detector: %s", mime);
294 }
295 if (mime != nullptr) {
296 // select the decoder based on the detemined mime type
297 if (!selectDecoder(mime)) {
298 LOGE("The decoder could not be found for %s", mime);
300 actual_decoder.is_open = true;
301 }
302 }
303 is_first = false;
304 }
305 // check if we have a decoder
306 if (actual_decoder.decoder == nullptr) return 0;
307 // decode the data
308 return actual_decoder.decoder->write(data, len);
309 }
310
317 virtual operator bool() override {
318 if (actual_decoder.decoder == &nop) return false;
320 };
321
332 bool setCodecConfig(const uint8_t* data, size_t len) override {
333 if (actual_decoder.decoder == nullptr) {
334 LOGE("No decoder defined, cannot set codec config");
335 return false;
336 }
337 return actual_decoder.decoder->setCodecConfig(data, len);
338 }
339
350
351#ifdef USE_EXPERIMENTAL
352
365 void addDecoder(StreamingDecoder& decoder, const char* mime,
366 int bufferSize = 1024) {
367 if (mime != nullptr) {
368 // Create a DecoderAdapter to wrap the StreamingDecoder
369 decoder.addNotifyAudioChange(*this);
370 auto adapter = new DecoderAdapter(decoder, bufferSize);
371 adapters.push_back(adapter); // Store for cleanup
372
373 // Add the adapter as a regular AudioDecoder
374 addDecoder(*adapter, mime);
375 } else {
376 LOGE("MIME type is nullptr - cannot add StreamingDecoder");
377 }
378 }
379#endif
380
381 protected:
385 struct DecoderInfo {
386 const char* mime = nullptr;
388 bool is_open = false;
389
393 DecoderInfo() = default;
394
402 this->mime = mime;
403 this->decoder = decoder;
404 }
406
408#ifdef USE_EXPERIMENTAL
409 Vector<DecoderAdapter*> adapters{0};
410#endif
414 bool is_first = true;
415 const char* selected_mime = nullptr;
416};
417
418} // namespace audio_tools
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Definition Arduino.h:56
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:19
AudioInfo info
Definition AudioCodecsBase.h:80
Print * getOutput()
Definition AudioCodecsBase.h:68
virtual bool begin(AudioInfo info) override
Definition AudioCodecsBase.h:58
void end() override
Definition AudioCodecsBase.h:63
virtual void setOutput(AudioStream &out_stream)
Defines where the decoded result is written to.
Definition AudioCodecsBase.h:40
virtual bool setCodecConfig(const uint8_t *data, size_t len)
Some decoders need e.g. a magic cookie to provide the relevant info for decoding.
Definition AudioCodecsBase.h:73
Print * p_print
Definition AudioCodecsBase.h:79
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:150
virtual size_t write(const uint8_t *data, size_t len)=0
Dummy no implmentation Codec. This is used so that we can initialize some pointers to decoders and en...
Definition AudioCodecsBase.h:150
Adapter class which allows the AudioDecoder API on a StreamingDecoder.
Definition StreamingDecoder.h:857
Logic to detemine the mime type from the content. By default the following mime types are supported (...
Definition MimeDetector.h:26
bool begin()
Sets is_first to true.
Definition MimeDetector.h:49
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:61
const char * mime()
Definition MimeDetector.h:93
Abstract interface for classes that can provide MIME type information.
Definition AudioTypes.h:547
virtual const char * mime()=0
Get the MIME type string.
Manage multiple AudioDecoders with automatic format detection.
Definition MultiDecoder.h:51
void setOutput(Print &out_stream) override
Sets the output stream for decoded audio data.
Definition MultiDecoder.h:151
struct audio_tools::MultiDecoder::DecoderInfo actual_decoder
Currently active decoder information.
MultiDecoder()=default
Default constructor.
bool setCodecConfig(const uint8_t *data, size_t len) override
Sets codec-specific configuration data.
Definition MultiDecoder.h:332
void setMimeSource(MimeSource &mimeSource)
Sets an external MIME source for format detection.
Definition MultiDecoder.h:170
const char * selectedMime()
Returns the MIME type that was detected and selected.
Definition MultiDecoder.h:265
bool selectDecoder(const char *mime)
Definition MultiDecoder.h:196
CodecNOP nop
No-operation codec for unsupported formats.
Definition MultiDecoder.h:412
bool is_first
Flag for first write() call.
Definition MultiDecoder.h:414
MimeDetector mime_detector
MIME type detection engine.
Definition MultiDecoder.h:411
void end() override
Releases resources and closes the active decoder.
Definition MultiDecoder.h:116
MultiDecoder(MimeSource &mimeSource)
Constructor with external MIME source.
Definition MultiDecoder.h:67
size_t write(const uint8_t *data, size_t len) override
Writes encoded audio data to be decoded.
Definition MultiDecoder.h:281
static void baseMimeType(const char *mime, char *out, size_t outSize)
Selects the actual decoder by MIME type.
Definition MultiDecoder.h:188
Vector< DecoderInfo > decoders
Collection of registered decoders.
Definition MultiDecoder.h:407
const char * selected_mime
MIME type that was selected.
Definition MultiDecoder.h:415
const char * mime() override
Provides the MIME type of the currently selected decoder.
Definition MultiDecoder.h:89
MimeSource * p_mime_source
Optional external MIME source.
Definition MultiDecoder.h:413
bool begin() override
Starts the processing and enables automatic MIME type determination.
Definition MultiDecoder.h:99
MimeDetector & mimeDetector()
Provides access to the internal MIME detector.
Definition MultiDecoder.h:349
void addDecoder(AudioDecoder &decoder, const char *mime=nullptr)
Adds a decoder that will be selected by its MIME type.
Definition MultiDecoder.h:135
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:29
A Streaming Decoder where we provide both the input and output as streams.
Definition StreamingDecoder.h:29
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Information about a registered decoder.
Definition MultiDecoder.h:385
bool is_open
Whether the decoder is currently active.
Definition MultiDecoder.h:388
DecoderInfo(const char *mime, AudioDecoder *decoder)
Constructor with parameters.
Definition MultiDecoder.h:401
AudioDecoder * decoder
Pointer to the decoder instance.
Definition MultiDecoder.h:387
DecoderInfo()=default
Default constructor.
const char * mime
MIME type for this decoder.
Definition MultiDecoder.h:386