9
9
namespace audio_tools {
10
10
11
11
/* *
12
- * @brief Manage multiple decoders: the actual decoder is only opened when it
13
- * has been selected. The relevant decoder is determined dynamically at the
14
- * first write from the determined mime type. You can add your own custom mime
15
- * type determination logic.
12
+ * @brief Manage multiple AudioDecoders with automatic format detection
13
+ *
14
+ * This class automatically detects the audio format from incoming data and
15
+ * selects the appropriate decoder from a collection of registered decoders.
16
+ * The format detection is performed using the MimeDetector on the first chunk
17
+ * of data written to the decoder.
18
+ *
19
+ * Key features:
20
+ * - Automatic format detection using MimeDetector
21
+ * - Support for multiple decoder registration
22
+ * - Custom MIME type detection logic support
23
+ * - External MIME source integration (e.g., HTTP headers)
24
+ * - Lazy decoder initialization for memory efficiency
25
+ * - Seamless integration with existing AudioDecoder architecture
26
+ *
27
+ * The actual decoder is only opened when it has been selected, which allows
28
+ * for memory-efficient operation when dealing with multiple possible formats.
29
+ * The relevant decoder is determined dynamically at the first write() call
30
+ * based on the determined MIME type.
31
+ *
32
+ * @note This class uses a write-based interface, unlike StreamingDecoder
33
+ * which uses a pull-based approach. For streaming scenarios with direct
34
+ * access to input/output streams, consider using MultiStreamingDecoder.
35
+ *
16
36
* @ingroup codecs
17
37
* @ingroup decoder
18
38
* @author Phil Schatzmann
19
39
* @copyright GPLv3
20
40
*/
21
41
class MultiDecoder : public AudioDecoder {
22
42
public:
23
- // / Default constructor
43
+ /* *
44
+ * @brief Default constructor
45
+ */
24
46
MultiDecoder () = default ;
25
- // / Provides a URLStream to look up the mime type from the http reply header
47
+
48
+ /* *
49
+ * @brief Constructor with external MIME source
50
+ *
51
+ * Creates a MultiDecoder that uses an external source for MIME type
52
+ * determination, such as HTTP Content-Type headers. This can be more
53
+ * efficient than automatic detection as it avoids analyzing data content.
54
+ *
55
+ * @param mimeSource Reference to a MimeSource that provides MIME type information
56
+ */
26
57
MultiDecoder (MimeSource& mimeSource) { setMimeSource (mimeSource); }
27
58
28
- // / Enables the automatic mime type determination
59
+ /* *
60
+ * @brief Starts the processing and enables automatic MIME type determination
61
+ *
62
+ * Initializes the MIME detector and prepares the MultiDecoder for format
63
+ * detection. This method must be called before any write() operations.
64
+ *
65
+ * @return true if initialization was successful, false if no output is defined
66
+ */
29
67
bool begin () override {
30
68
mime_detector.begin ();
31
69
is_first = true ;
@@ -36,7 +74,13 @@ class MultiDecoder : public AudioDecoder {
36
74
return true ;
37
75
}
38
76
39
- // / closes the actual decoder
77
+ /* *
78
+ * @brief Releases resources and closes the active decoder
79
+ *
80
+ * Stops the currently active decoder and resets the MultiDecoder state
81
+ * for potential reuse. After calling end(), begin() must be called again
82
+ * before the decoder can process new data.
83
+ */
40
84
void end () override {
41
85
if (actual_decoder.decoder != nullptr && actual_decoder.is_open ) {
42
86
actual_decoder.decoder ->end ();
@@ -47,31 +91,79 @@ class MultiDecoder : public AudioDecoder {
47
91
is_first = true ;
48
92
}
49
93
50
- // / Adds a decoder that will be selected by it's mime type
94
+ /* *
95
+ * @brief Adds a decoder that will be selected by its MIME type
96
+ *
97
+ * Registers an AudioDecoder that will be automatically selected when
98
+ * the corresponding MIME type is detected in the input data.
99
+ *
100
+ * @param decoder The AudioDecoder to register
101
+ * @param mime The MIME type string to associate with this decoder
102
+ */
51
103
void addDecoder (AudioDecoder& decoder, const char * mime) {
52
104
DecoderInfo info{mime, &decoder};
53
105
decoder.addNotifyAudioChange (*this );
54
106
decoders.push_back (info);
55
107
}
56
108
57
- // / Adds a decoder that will be selected by it's mime type and defines the
58
- // / mime checking logic
109
+ /* *
110
+ * @brief Adds a decoder with custom MIME detection logic
111
+ *
112
+ * Registers an AudioDecoder with a specific MIME type and provides custom
113
+ * logic for detecting that MIME type from raw data. This allows for
114
+ * specialized format detection beyond the standard MimeDetector capabilities.
115
+ *
116
+ * @param decoder The AudioDecoder to register
117
+ * @param mime The MIME type string to associate with this decoder
118
+ * @param check Custom function that analyzes data to detect this MIME type.
119
+ * Should return true if the data matches this format.
120
+ */
59
121
void addDecoder (AudioDecoder& decoder, const char * mime,
60
122
bool (*check)(uint8_t * data, size_t len)) {
61
123
addDecoder (decoder, mime);
62
124
mime_detector.setCheck (mime, check);
63
125
}
64
126
127
+ /* *
128
+ * @brief Sets the output stream for decoded audio data
129
+ *
130
+ * Defines where the decoded PCM audio data will be written to.
131
+ * This output will be automatically configured for the selected decoder.
132
+ *
133
+ * @param out_stream The Print stream to write decoded audio data to
134
+ */
65
135
void setOutput (Print& out_stream) override {
66
136
p_print = &out_stream;
67
137
}
68
138
69
- // / Defines url stream from which we determine the mime type from the reply
70
- // / header
139
+ /* *
140
+ * @brief Sets an external MIME source for format detection
141
+ *
142
+ * Provides an alternative to automatic MIME detection by allowing an external
143
+ * source to provide the MIME type information. This is particularly useful
144
+ * when the MIME type is available from HTTP headers or other metadata sources.
145
+ *
146
+ * When a MIME source is set, it takes precedence over automatic detection,
147
+ * making the decoder selection process more efficient.
148
+ *
149
+ * @param mimeSource Reference to a MimeSource that provides MIME type information
150
+ *
151
+ * @note The MimeSource object must remain valid for the lifetime of this
152
+ * MultiDecoder instance, as only a reference is stored.
153
+ */
71
154
void setMimeSource (MimeSource& mimeSource) { p_mime_source = &mimeSource; }
72
155
73
- // / selects the actual decoder by mime type - this is usually called
74
- // / automatically from the determined mime type
156
+ /* *
157
+ * @brief Selects the actual decoder by MIME type
158
+ *
159
+ * Searches through registered decoders to find one that matches the
160
+ * specified MIME type, then initializes it for use. This method is
161
+ * usually called automatically from the determined MIME type during
162
+ * the first write() operation.
163
+ *
164
+ * @param mime The MIME type string to match against registered decoders
165
+ * @return true if a matching decoder was found and initialized, false otherwise
166
+ */
75
167
bool selectDecoder (const char * mime) {
76
168
bool result = false ;
77
169
if (mime == nullptr ) return false ;
@@ -108,8 +200,28 @@ class MultiDecoder : public AudioDecoder {
108
200
return result;
109
201
}
110
202
203
+ /* *
204
+ * @brief Returns the MIME type that was detected and selected
205
+ *
206
+ * @return The MIME type string that was detected and used to select
207
+ * the current decoder, or nullptr if no decoder has been selected
208
+ */
111
209
const char * selectedMime () { return selected_mime; }
112
210
211
+ /* *
212
+ * @brief Writes encoded audio data to be decoded
213
+ *
214
+ * On the first call, this method performs MIME type detection to select
215
+ * the appropriate decoder. Subsequent calls delegate to the selected
216
+ * decoder's write() method to process the audio data.
217
+ *
218
+ * The MIME detection process uses either an external MIME source (if set)
219
+ * or analyzes the provided data to determine the audio format.
220
+ *
221
+ * @param data Buffer containing encoded audio data
222
+ * @param len Number of bytes to write
223
+ * @return Number of bytes actually written to the selected decoder
224
+ */
113
225
size_t write (const uint8_t * data, size_t len) override {
114
226
if (is_first) {
115
227
const char * mime = nullptr ;
@@ -140,12 +252,27 @@ class MultiDecoder : public AudioDecoder {
140
252
return actual_decoder.decoder ->write (data, len);
141
253
}
142
254
255
+ /* *
256
+ * @brief Checks if the decoder is active and ready
257
+ *
258
+ * @return true if a decoder is selected and active, or if format detection
259
+ * hasn't been performed yet; false if no suitable decoder was found
260
+ */
143
261
virtual operator bool () override {
144
262
if (actual_decoder.decoder == &nop) return false ;
145
263
return is_first || actual_decoder.is_open ;
146
264
};
147
265
148
- // / Sets the config to the selected decoder
266
+ /* *
267
+ * @brief Sets codec-specific configuration data
268
+ *
269
+ * Forwards codec configuration data to the currently selected decoder.
270
+ * This method can only be called after a decoder has been selected.
271
+ *
272
+ * @param data Buffer containing codec configuration data
273
+ * @param len Length of the configuration data
274
+ * @return true if the configuration was successfully applied, false otherwise
275
+ */
149
276
bool setCodecConfig (const uint8_t * data, size_t len) override {
150
277
if (actual_decoder.decoder == nullptr ) {
151
278
LOGE (" No decoder defined, cannot set codec config" );
@@ -155,22 +282,37 @@ class MultiDecoder : public AudioDecoder {
155
282
}
156
283
157
284
protected:
285
+ /* *
286
+ * @brief Information about a registered decoder
287
+ */
158
288
struct DecoderInfo {
159
- const char * mime = nullptr ;
160
- AudioDecoder* decoder = nullptr ;
161
- bool is_open = false ;
289
+ const char * mime = nullptr ; // /< MIME type for this decoder
290
+ AudioDecoder* decoder = nullptr ; // /< Pointer to the decoder instance
291
+ bool is_open = false ; // /< Whether the decoder is currently active
292
+
293
+ /* *
294
+ * @brief Default constructor
295
+ */
162
296
DecoderInfo () = default ;
297
+
298
+ /* *
299
+ * @brief Constructor with parameters
300
+ *
301
+ * @param mime MIME type string
302
+ * @param decoder Pointer to AudioDecoder instance
303
+ */
163
304
DecoderInfo (const char * mime, AudioDecoder* decoder) {
164
305
this ->mime = mime;
165
306
this ->decoder = decoder;
166
307
}
167
- } actual_decoder;
168
- Vector<DecoderInfo> decoders{0 };
169
- MimeDetector mime_detector;
170
- CodecNOP nop;
171
- MimeSource* p_mime_source = nullptr ;
172
- bool is_first = true ;
173
- const char * selected_mime = nullptr ;
308
+ } actual_decoder; // /< Currently active decoder information
309
+
310
+ Vector<DecoderInfo> decoders{0 }; // /< Collection of registered decoders
311
+ MimeDetector mime_detector; // /< MIME type detection engine
312
+ CodecNOP nop; // /< No-operation codec for unsupported formats
313
+ MimeSource* p_mime_source = nullptr ; // /< Optional external MIME source
314
+ bool is_first = true ; // /< Flag for first write() call
315
+ const char * selected_mime = nullptr ; // /< MIME type that was selected
174
316
};
175
317
176
318
} // namespace audio_tools
0 commit comments