FastLED 3.10.8
Loading...
Searching...
No Matches
mp3.cpp.hpp
Go to the documentation of this file.
1#include "fl/codec/mp3.h"
2#include "fl/stl/bit_cast.h"
3#include "fl/stl/cstring.h"
4#include "fl/stl/singleton.h"
5
6// IWYU pragma: begin_keep
9#include "fl/stl/noexcept.h"
10// IWYU pragma: end_keep
11
12namespace fl {
13namespace third_party {
14
15template <typename T>
16T* Mp3MemoryAllocateArray(fl::size count,
18 return static_cast<T*>(Mp3MemoryAllocate(sizeof(T) * count, tag));
19}
20
21template <typename T>
24
25template <typename T>
27 fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT
28 : mBytes(bytes), mTag(tag) {}
29
30template <typename T>
34
35namespace {
36#if defined(FASTLED_TESTING)
37struct Mp3MemoryHookState {
38 Mp3MemoryHook* hook = nullptr;
39};
40
41Mp3MemoryHook*& mp3MemoryHook() FL_NO_EXCEPT {
43}
44#endif
45} // anonymous namespace
46
48 switch (tag) {
50 return "decoder-state";
52 return "scratch";
54 return "stream-buffer";
56 return "pcm-output";
58 break;
59 }
60 return "unknown";
61}
62
63#if defined(FASTLED_TESTING)
64Mp3MemoryHook::~Mp3MemoryHook() FL_NO_EXCEPT = default;
65
66bool Mp3MemoryHook::allowAllocate(fl::size,
68 return true;
69}
70
71void SetMp3MemoryHook(Mp3MemoryHook* hook) FL_NO_EXCEPT {
72 mp3MemoryHook() = hook;
73}
74
75void ClearMp3MemoryHook() FL_NO_EXCEPT {
76 mp3MemoryHook() = nullptr;
77}
78#endif
79
81 fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT {
82#if defined(FASTLED_TESTING)
83 if (mp3MemoryHook() && !mp3MemoryHook()->allowAllocate(bytes, tag)) {
84 return nullptr;
85 }
86#endif
87 void* ptr = fl::Malloc(bytes);
88#if defined(FASTLED_TESTING)
89 if (ptr && mp3MemoryHook()) {
90 mp3MemoryHook()->onAllocate(ptr, bytes, tag);
91 }
92#else
93 FL_UNUSED(tag);
94#endif
95 return ptr;
96}
97
98void Mp3MemoryFree(void* ptr, fl::size bytes,
100 if (!ptr) {
101 return;
102 }
103#if defined(FASTLED_TESTING)
104 if (mp3MemoryHook()) {
105 mp3MemoryHook()->onFree(ptr, bytes, tag);
106 }
107#else
108 FL_UNUSED(bytes);
109 FL_UNUSED(tag);
110#endif
111 fl::Free(ptr);
112}
113
114// Maximum PCM output: 1152 samples/channel * 2 channels = 2304 samples
115constexpr fl::size MAX_PCM_SAMPLES = 2304;
116
117namespace {
118
120 const int version_bits = (header[1] >> 3) & 0x03;
121 if (version_bits == 0x03) {
122 return 0; // MPEG-1.
123 }
124 if (version_bits == 0x02) {
125 return 1; // MPEG-2.
126 }
127 return 2; // MPEG-2.5.
128}
129
130} // anonymous namespace
131
143
147
149 if (mDecoder) {
150 return true;
151 }
152
159 if (!mDecoder || !mScratch || !mPcmBuffer) {
160 mDecoder.reset();
161 mScratch.reset();
162 mPcmBuffer.reset();
163 return false;
164 }
165
166 mp3dec_init(mDecoder.get());
167 return true;
168}
169
171 mDecoder.reset();
172 mScratch.reset();
173 mPcmBuffer.reset();
174 fl::memset(&mFrameInfo, 0, sizeof(mFrameInfo));
175}
176
178 fl::size len) FL_NO_EXCEPT {
179 if (!buf || len < 4) {
180 return -1;
181 }
182
183 for (fl::size i = 0; i <= len - 4; ++i) {
184 const fl::u8 version = (buf[i + 1] >> 3) & 0x03;
185 const fl::u8 layer = (buf[i + 1] >> 1) & 0x03;
186 const fl::u8 bitrate = (buf[i + 2] >> 4) & 0x0f;
187 const fl::u8 sample_rate = (buf[i + 2] >> 2) & 0x03;
188 if (buf[i] == 0xff && (buf[i + 1] & 0xe0) == 0xe0 &&
189 version != 0x01 && layer != 0x00 && bitrate != 0x0f &&
190 sample_rate != 0x03) {
191 return static_cast<int>(i);
192 }
193 }
194 return -1;
195}
196
198 fl::size* bytes_left) FL_NO_EXCEPT {
199 if (!mDecoder || !mScratch || !mPcmBuffer || !inbuf || !bytes_left ||
200 !*inbuf || *bytes_left == 0) {
201 return -1;
202 }
203
204 const fl::u8* input = *inbuf;
205 const fl::size max_input = 0x7fffffff;
206 const int input_bytes = static_cast<int>(
207 *bytes_left > max_input ? max_input : *bytes_left);
208 mp3dec_frame_info_t info = {};
209 const int samples = mp3dec_decode_frame_r(
210 mDecoder.get(), mScratch.get(), input, input_bytes, mPcmBuffer.get(),
211 &info);
212 if (samples <= 0 || info.frame_bytes <= 0 ||
213 static_cast<fl::size>(info.frame_bytes) > *bytes_left) {
214 return -1;
215 }
216
217 const fl::u8* header = input + info.frame_offset;
218 *inbuf += info.frame_bytes;
219 *bytes_left -= static_cast<fl::size>(info.frame_bytes);
220
221 int frame_bytes = info.frame_bytes - info.frame_offset;
222 if (frame_bytes > 0 && (header[2] & 0x02) != 0) {
223 --frame_bytes;
224 }
225 mFrameInfo.bitrate = info.bitrate_kbps * 1000;
226 if (mFrameInfo.bitrate == 0 && samples > 0 && frame_bytes > 0) {
227 mFrameInfo.bitrate =
228 (frame_bytes * info.hz * 8) / samples;
229 }
230 mFrameInfo.nChans = info.channels;
231 mFrameInfo.samprate = info.hz;
232 mFrameInfo.bitsPerSample = 16;
233 mFrameInfo.outputSamps = samples * info.channels;
234 mFrameInfo.layer = info.layer;
235 mFrameInfo.version = minimp3Version(header);
236 return 0;
237}
238
241 fl::size len) FL_NO_EXCEPT {
243
244 decode(data, len, [&](const Mp3Frame& frame) {
245 if (frame.channels == 2) {
246 fl::vector<fl::i16> mono_pcm;
247 mono_pcm.reserve(frame.samples);
248 for (int i = 0; i < frame.samples; ++i) {
249 const fl::i32 left = frame.pcm[i * 2];
250 const fl::i32 right = frame.pcm[i * 2 + 1];
251 mono_pcm.push_back(static_cast<fl::i16>((left + right) / 2));
252 }
253 samples.push_back(audio::Sample(mono_pcm));
254 } else {
255 samples.push_back(
257 frame.samples)));
258 }
259 });
260
261 return samples;
262}
263
265
266// Mp3StreamDecoderImpl - internal implementation of streaming MP3 decoder
268 public:
271
273 void end() FL_NO_EXCEPT;
274 bool isReady() const FL_NO_EXCEPT { return mStream != nullptr && mDecoder != nullptr; }
275 bool hasError(fl::string* msg = nullptr) const FL_NO_EXCEPT;
276 bool decodeNextFrame(audio::Sample* out_sample) FL_NO_EXCEPT;
277 fl::size getPosition() const FL_NO_EXCEPT { return mBytesProcessed; }
278 void reset() FL_NO_EXCEPT;
279 Mp3Info getInfo() const FL_NO_EXCEPT { return mInfo; }
280
281 private:
282 static constexpr fl::size BUFFER_SIZE = MP3_MINIMP3_STREAM_BUFFER_SIZE;
283
285 bool findAndDecodeFrame(audio::Sample* out_sample) FL_NO_EXCEPT;
286
287 /* VBR-tag state (FastLED#4129). mSkipTagFrame suppresses the metadata
288 frame itself; mSkipSamples counts the encoder priming after it.
289
290 These are per-stream, so both begin() and reset() must clear them. A
291 decoder reused for a second stream otherwise carries the first one's
292 verdict across: having already inspected an untagged file it never
293 inspects the tagged one, and emits its metadata frame as audio. */
295 mInspectedFirstFrame = false;
296 mSkipTagFrame = false;
297 mSkipSamples = 0;
298 }
299
301 bool mSkipTagFrame = false;
302 fl::u32 mSkipSamples = 0;
303
307 fl::size mBufferPos;
315};
316
324
328
330 if (!stream) {
331 mErrorMsg = "Invalid stream provided";
332 mHasError = true;
333 return false;
334 }
335
336 mStream = stream;
338 if (!mDecoder->init()) {
339 mErrorMsg = "Failed to initialize MP3 decoder";
340 mHasError = true;
341 mDecoder.reset();
342 return false;
343 }
344
347 if (!mBuffer) {
348 mErrorMsg = "Failed to allocate MP3 stream buffer";
349 mHasError = true;
350 mDecoder.reset();
351 return false;
352 }
353 mBufferPos = 0;
354 mBufferFilled = 0;
355 mBytesProcessed = 0;
356 mHasError = false;
357 mEndOfStream = false;
358 mHasDecodedFirstFrame = false;
360
361 return true;
362}
363
365 mDecoder.reset();
366 if (mStream) {
367 mStream->close();
368 mStream = nullptr;
369 }
370 mBuffer.reset();
371}
372
374 if (msg && mHasError) {
375 *msg = mErrorMsg;
376 }
377 return mHasError;
378}
379
381 if (mDecoder) {
382 mDecoder->reset();
383 if (!mDecoder->init()) {
384 mErrorMsg = "Failed to reinitialize MP3 decoder";
385 mHasError = true;
386 mDecoder.reset();
387 return;
388 }
389 }
390 mBufferPos = 0;
391 mBufferFilled = 0;
392 mBytesProcessed = 0;
393 mHasError = false;
394 mEndOfStream = false;
395 mHasDecodedFirstFrame = false;
397}
398
400 // Shift remaining data to beginning of buffer
401 if (mBufferPos > 0 && mBufferFilled > mBufferPos) {
402 fl::size remaining = mBufferFilled - mBufferPos;
403 for (fl::size i = 0; i < remaining; i++) {
404 mBuffer[i] = mBuffer[mBufferPos + i];
405 }
406 mBufferFilled = remaining;
407 mBufferPos = 0;
408 } else if (mBufferPos >= mBufferFilled) {
409 mBufferPos = 0;
410 mBufferFilled = 0;
411 }
412
413 // Fill the rest of the buffer from stream
414 fl::size spaceAvailable = BUFFER_SIZE - mBufferFilled;
415 if (spaceAvailable > 0 && mStream && mStream->available(1)) {
416 fl::size bytesRead =
417 mStream->read(mBuffer.get() + mBufferFilled, spaceAvailable);
418 mBufferFilled += bytesRead;
419 return bytesRead > 0;
420 }
421
422 return false;
423}
424
426 if (!mDecoder) {
427 return false;
428 }
429
430 // Try to decode from current buffer
431 const fl::u8* inptr = mBuffer.get() + mBufferPos;
432 fl::size bytes_left = mBufferFilled - mBufferPos;
433
434 if (bytes_left == 0) {
435 return false;
436 }
437
438 // Find sync word
439 int offset = mDecoder->findSyncWord(inptr, bytes_left);
440 if (offset < 0) {
441 // No sync word found, consume buffer and try again
443 return false;
444 }
445
446 inptr += offset;
447 bytes_left -= offset;
449
450 // Try to decode one frame
451 const fl::u8* decode_ptr = inptr;
452 fl::size decode_bytes = bytes_left;
453
454 /* The first frame may be a Xing/Info/VBRI header rather than audio. It is
455 a complete, valid MPEG frame, so decoding it emits noise before the
456 music; LAME also records how much encoder priming follows it. Inspect
457 before decoding so the frame can be dropped whole (FastLED#4129). */
460 Mp3VbrTag tag;
461 if (Mp3ParseVbrTag(fl::span<const fl::u8>(inptr, bytes_left), &tag) &&
462 tag.present) {
463 mSkipTagFrame = true;
465 }
466 }
467
468 int result = mDecoder->decodeFrame(&decode_ptr, &decode_bytes);
469
470 // Update buffer position based on how many bytes were consumed
471 fl::size consumed = (decode_ptr - inptr);
472 if (result != 0 && consumed == 0 && bytes_left == BUFFER_SIZE) {
473 // A full buffer cannot grow to complete this frame. Drop one byte so
474 // corrupt input cannot pin the streaming decoder on the same sync.
475 consumed = 1;
476 }
477 mBufferPos += consumed;
478 mBytesProcessed += consumed;
479
480 if (result == 0 && mSkipTagFrame) {
481 // The metadata frame itself: consumed above, never emitted.
482 mSkipTagFrame = false;
483 return false;
484 }
485
486 if (result == 0) {
487 // Successfully decoded a frame
488 Mp3Frame frame;
489 frame.pcm = mDecoder->mPcmBuffer.get();
490 frame.samples = mDecoder->mFrameInfo.outputSamps / mDecoder->mFrameInfo.nChans;
491 frame.channels = mDecoder->mFrameInfo.nChans;
492 frame.sample_rate = mDecoder->mFrameInfo.samprate;
493 frame.bitrate = mDecoder->mFrameInfo.bitrate;
494 frame.version = mDecoder->mFrameInfo.version;
495 frame.layer = mDecoder->mFrameInfo.layer;
496
497 // Update stream info on first successful decode
499 mInfo.sampleRate = frame.sample_rate;
500 mInfo.channels = static_cast<fl::u8>(frame.channels);
501 mInfo.bitrate = frame.bitrate;
502 mInfo.version = static_cast<fl::u8>(frame.version);
503 mInfo.layer = static_cast<fl::u8>(frame.layer);
504 mInfo.isValid = true;
506 }
507
508 /* Encoder priming plus the filterbank's own latency: not signal, and
509 audible as a burst at the start of playback if emitted. */
510 int first = 0;
511 if (mSkipSamples > 0) {
512 const fl::u32 available = static_cast<fl::u32>(frame.samples);
513 const fl::u32 drop =
515 first = static_cast<int>(drop);
516 mSkipSamples -= drop;
517 if (first >= frame.samples) {
518 return false; // whole frame was priming
519 }
520 }
521 const int emitted = frame.samples - first;
522
523 // Convert to audio::Sample (convert stereo to mono if needed)
524 if (frame.channels == 2) {
525 fl::vector<fl::i16> mono_pcm;
526 mono_pcm.reserve(emitted);
527
528 for (int i = first; i < frame.samples; i++) {
529 fl::i32 left = frame.pcm[i * 2];
530 fl::i32 right = frame.pcm[i * 2 + 1];
531 fl::i32 avg = (left + right) / 2;
532 mono_pcm.push_back(static_cast<fl::i16>(avg));
533 }
534
535 *out_sample = audio::Sample(mono_pcm);
536 } else {
537 // Mono audio - use directly
538 *out_sample = audio::Sample(
539 fl::span<const fl::i16>(frame.pcm + first, emitted));
540 }
541
542 return true;
543 }
544
545 return false;
546}
547
549 if (!isReady()) {
550 mErrorMsg = "Decoder not ready";
551 mHasError = true;
552 return false;
553 }
554
555 if (mEndOfStream) {
556 return false;
557 }
558
559 while (true) {
560 const fl::size previous_position = mBufferPos;
561 if (findAndDecodeFrame(out_sample)) {
562 return true;
563 }
564
565 if (mBufferPos > previous_position) {
566 // The decoder rejected or skipped bytes. Retry the remainder before
567 // refilling so a valid frame after corruption is not discarded.
568 continue;
569 }
570
571 if (fillBuffer()) {
572 continue;
573 }
574
576 // No more bytes are available to complete the current candidate.
577 // Advance through the tail so a later sync can still be recovered.
578 ++mBufferPos;
580 continue;
581 }
582
583 mEndOfStream = true;
584 return false;
585 }
586}
587
588} // namespace third_party
589
590// Mp3Decoder implementation
592
594
596 return mImpl->begin(stream);
597}
598
600 mImpl->end();
601}
602
604 return mImpl->isReady();
605}
606
608 return mImpl->hasError(msg);
609}
610
612 return mImpl->decodeNextFrame(out_sample);
613}
614
616 return mImpl->getPosition();
617}
618
620 mImpl->reset();
621}
622
624 return mImpl->getInfo();
625}
626
627// Mp3 factory implementation
629 FL_UNUSED(error_message);
631}
632
634 // MP3 decoder is available on all platforms
635 return true;
636}
637
639 Mp3Info info;
640
641 // Validate input data
642 if (data.empty()) {
643 if (error_message) {
644 *error_message = "Empty MP3 data";
645 }
646 return info; // returns invalid info
647 }
648
649 // Minimum size check - need at least an MP3 frame header (4 bytes) + some data
650 if (data.size() < 128) {
651 if (error_message) {
652 *error_message = "MP3 data too small";
653 }
654 return info;
655 }
656
657 // Look for MP3 sync word (11 bits set: 0xFFE or 0xFFF)
658 bool foundSync = false;
659 fl::size syncOffset = 0;
660
661 for (fl::size i = 0; i <= data.size() - 4; i++) {
662 if (data[i] == 0xFF && (data[i + 1] & 0xE0) == 0xE0) {
663 foundSync = true;
664 syncOffset = i;
665 break;
666 }
667 }
668
669 if (!foundSync) {
670 if (error_message) {
671 *error_message = "Invalid MP3 stream - no sync word found";
672 }
673 return info;
674 }
675
676 // Use the decoder to parse the first frame
678 if (!decoder.init()) {
679 if (error_message) {
680 *error_message = "Failed to initialize MP3 decoder";
681 }
682 return info;
683 }
684
685 const fl::u8* inptr = data.data() + syncOffset;
686 fl::size bytes_left = data.size() - syncOffset;
687
688 // Try to decode first frame to get metadata
689 int offset = decoder.findSyncWord(inptr, bytes_left);
690 if (offset >= 0) {
691 inptr += offset;
692 bytes_left -= offset;
693
694 int result = decoder.decodeFrame(&inptr, &bytes_left);
695 if (result == 0) {
696 // Successfully decoded - extract metadata
697 info.sampleRate = decoder.mFrameInfo.samprate;
698 info.channels = static_cast<fl::u8>(decoder.mFrameInfo.nChans);
699 info.bitrate = decoder.mFrameInfo.bitrate;
700 info.version = static_cast<fl::u8>(decoder.mFrameInfo.version);
701 info.layer = static_cast<fl::u8>(decoder.mFrameInfo.layer);
702 info.isValid = true;
703 } else if (error_message) {
704 *error_message = "Failed to decode MP3 frame header";
705 }
706 } else if (error_message) {
707 *error_message = "Failed to find MP3 sync word";
708 }
709
710 return info;
711}
712
713} // namespace fl
static bool isSupported() FL_NO_EXCEPT
Definition mp3.cpp.hpp:633
static Mp3Info parseMp3Info(fl::span< const fl::u8 > data, fl::string *error_message=nullptr) FL_NO_EXCEPT
Definition mp3.cpp.hpp:638
static Mp3DecoderPtr createDecoder(fl::string *error_message=nullptr) FL_NO_EXCEPT
Definition mp3.cpp.hpp:628
~Mp3Decoder() FL_NO_EXCEPT
void reset() FL_NO_EXCEPT
Definition mp3.cpp.hpp:619
void end() FL_NO_EXCEPT
Definition mp3.cpp.hpp:599
Mp3Decoder() FL_NO_EXCEPT
Definition mp3.cpp.hpp:591
bool decodeNextFrame(audio::Sample *out_sample) FL_NO_EXCEPT
Definition mp3.cpp.hpp:611
bool isReady() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:603
fl::size getPosition() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:615
bool hasError(fl::string *msg=nullptr) const FL_NO_EXCEPT
Definition mp3.cpp.hpp:607
Mp3Info getInfo() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:623
bool begin(fl::filebuf_ptr stream) FL_NO_EXCEPT
Definition mp3.cpp.hpp:595
fl::unique_ptr< fl::third_party::Mp3StreamDecoderImpl > mImpl
Definition mp3.h:168
static T & instance() FL_NO_EXCEPT
Definition singleton.h:41
int decodeFrame(const fl::u8 **inbuf, fl::size *bytes_left) FL_NO_EXCEPT
Definition mp3.cpp.hpp:197
int decode(const fl::u8 *data, fl::size len, Fn on_frame) FL_NO_EXCEPT
Definition mp3.h:75
fl::unique_ptr< mp3dec_t, Mp3MemoryDeleter< mp3dec_t > > mDecoder
Definition mp3.h:128
fl::vector< audio::Sample > decodeToAudioSamples(const fl::u8 *data, fl::size len) FL_NO_EXCEPT
Definition mp3.cpp.hpp:240
int findSyncWord(const fl::u8 *buf, fl::size len) FL_NO_EXCEPT
Definition mp3.cpp.hpp:177
fl::unique_ptr< mp3dec_scratch_t, Mp3MemoryDeleter< mp3dec_scratch_t > > mScratch
Definition mp3.h:130
fl::unique_ptr< fl::i16[], Mp3MemoryDeleter< fl::i16 > > mPcmBuffer
Definition mp3.h:124
fl::unique_ptr< fl::u8[], Mp3MemoryDeleter< fl::u8 > > mBuffer
Definition mp3.cpp.hpp:306
bool isReady() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:274
Mp3Info getInfo() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:279
bool decodeNextFrame(audio::Sample *out_sample) FL_NO_EXCEPT
Definition mp3.cpp.hpp:548
static constexpr fl::size BUFFER_SIZE
Definition mp3.cpp.hpp:282
fl::size getPosition() const FL_NO_EXCEPT
Definition mp3.cpp.hpp:277
fl::unique_ptr< Mp3SelectedDecoder > mDecoder
Definition mp3.cpp.hpp:305
bool begin(fl::filebuf_ptr stream) FL_NO_EXCEPT
Definition mp3.cpp.hpp:329
bool findAndDecodeFrame(audio::Sample *out_sample) FL_NO_EXCEPT
Definition mp3.cpp.hpp:425
bool hasError(fl::string *msg=nullptr) const FL_NO_EXCEPT
Definition mp3.cpp.hpp:373
void reserve(fl::size n) FL_NO_EXCEPT
Definition vector.h:591
void push_back(const T &value) FL_NO_EXCEPT
Definition vector.h:624
#define FL_UNUSED(x)
#define FL_NO_INLINE
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
void mp3dec_init(mp3dec_t *dec) FL_NO_EXCEPT
int mp3dec_decode_frame_r(mp3dec_t *dec, mp3dec_scratch_t *scratch, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info) FL_NO_EXCEPT
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
Definition unique_ptr.h:263
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
Definition shared_ptr.h:423
int minimp3Version(const fl::u8 *header) FL_NO_EXCEPT
Definition mp3.cpp.hpp:119
bool Mp3ParseVbrTag(fl::span< const fl::u8 > bytes, Mp3VbrTag *out) FL_NO_EXCEPT
Definition mp3_vbr_tag.h:75
const char * Mp3MemoryTagName(Mp3MemoryTag tag) FL_NO_EXCEPT
Definition mp3.cpp.hpp:47
T * Mp3MemoryAllocateArray(fl::size count, Mp3MemoryTag tag) FL_NO_EXCEPT
Definition mp3.cpp.hpp:16
Mp3Minimp3Decoder Mp3SelectedDecoder
Definition mp3.cpp.hpp:264
static const fl::u32 MP3D_DECODER_DELAY
Metadata carried by an MP3's first frame when an encoder wrote a Xing, Info, or VBRI header there.
Definition mp3_vbr_tag.h:35
constexpr fl::size MP3_MINIMP3_STREAM_BUFFER_SIZE
Definition mp3_memory.h:14
FL_NO_INLINE void * Mp3MemoryAllocate(fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT
Definition mp3.cpp.hpp:80
void Mp3MemoryFree(void *ptr, fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT
Definition mp3.cpp.hpp:98
constexpr fl::size MAX_PCM_SAMPLES
Definition mp3.cpp.hpp:115
bool present
The frame is metadata and must not be emitted as audio.
Definition mp3_vbr_tag.h:39
fl::u32 encoderDelay
Samples per channel of encoder priming to drop from the start.
Definition mp3_vbr_tag.h:41
const fl::i16 * pcm
Definition mp3.h:43
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
void Free(void *ptr) FL_NO_EXCEPT
FASTLED_FORCE_INLINE void * memset(void *s, int c, size_t n) FL_NO_EXCEPT
Definition cstring.h:148
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
fl::shared_ptr< filebuf > filebuf_ptr
Definition idecoder.h:15
int available() FL_NO_EXCEPT
fl::shared_ptr< Mp3Decoder > Mp3DecoderPtr
Definition fs.h:27
void * Malloc(fl::size size) FL_NO_EXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NO_EXCEPT
fl::u8 layer
Definition mp3.h:24
fl::u8 channels
Definition mp3.h:20
fl::u8 version
Definition mp3.h:23
fl::u32 bitrate
Definition mp3.h:21
fl::u32 sampleRate
Definition mp3.h:19
bool isValid
Definition mp3.h:25
void operator()(T *ptr) const FL_NO_EXCEPT
Definition mp3.cpp.hpp:31