36#if defined(FASTLED_TESTING)
37struct Mp3MemoryHookState {
38 Mp3MemoryHook* hook =
nullptr;
50 return "decoder-state";
54 return "stream-buffer";
63#if defined(FASTLED_TESTING)
66bool Mp3MemoryHook::allowAllocate(
fl::size,
72 mp3MemoryHook() = hook;
76 mp3MemoryHook() =
nullptr;
82#if defined(FASTLED_TESTING)
83 if (mp3MemoryHook() && !mp3MemoryHook()->allowAllocate(bytes, tag)) {
88#if defined(FASTLED_TESTING)
89 if (ptr && mp3MemoryHook()) {
90 mp3MemoryHook()->onAllocate(ptr, bytes, tag);
103#if defined(FASTLED_TESTING)
104 if (mp3MemoryHook()) {
105 mp3MemoryHook()->onFree(ptr, bytes, tag);
120 const int version_bits = (header[1] >> 3) & 0x03;
121 if (version_bits == 0x03) {
124 if (version_bits == 0x02) {
179 if (!buf || len < 4) {
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);
200 !*inbuf || *bytes_left == 0) {
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);
213 static_cast<fl::size
>(info.
frame_bytes) > *bytes_left) {
219 *bytes_left -=
static_cast<fl::size
>(info.
frame_bytes);
222 if (frame_bytes > 0 && (header[2] & 0x02) != 0) {
226 if (
mFrameInfo.bitrate == 0 && samples > 0 && frame_bytes > 0) {
228 (frame_bytes * info.
hz * 8) / 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));
339 mErrorMsg =
"Failed to initialize MP3 decoder";
348 mErrorMsg =
"Failed to allocate MP3 stream buffer";
384 mErrorMsg =
"Failed to reinitialize MP3 decoder";
403 for (fl::size i = 0; i < remaining; i++) {
419 return bytesRead > 0;
434 if (bytes_left == 0) {
451 const fl::u8* decode_ptr = inptr;
452 fl::size decode_bytes = bytes_left;
471 fl::size consumed = (decode_ptr - inptr);
504 mInfo.isValid =
true;
515 first =
static_cast<int>(drop);
521 const int emitted = frame.
samples - first;
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));
560 const fl::size previous_position =
mBufferPos;
596 return mImpl->begin(stream);
604 return mImpl->isReady();
608 return mImpl->hasError(msg);
612 return mImpl->decodeNextFrame(out_sample);
616 return mImpl->getPosition();
624 return mImpl->getInfo();
644 *error_message =
"Empty MP3 data";
650 if (data.size() < 128) {
652 *error_message =
"MP3 data too small";
658 bool foundSync =
false;
659 fl::size syncOffset = 0;
661 for (fl::size i = 0; i <= data.size() - 4; i++) {
662 if (data[i] == 0xFF && (data[i + 1] & 0xE0) == 0xE0) {
671 *error_message =
"Invalid MP3 stream - no sync word found";
678 if (!decoder.
init()) {
680 *error_message =
"Failed to initialize MP3 decoder";
685 const fl::u8* inptr = data.data() + syncOffset;
686 fl::size bytes_left = data.size() - syncOffset;
703 }
else if (error_message) {
704 *error_message =
"Failed to decode MP3 frame header";
706 }
else if (error_message) {
707 *error_message =
"Failed to find MP3 sync word";
static bool isSupported() FL_NO_EXCEPT
static Mp3Info parseMp3Info(fl::span< const fl::u8 > data, fl::string *error_message=nullptr) FL_NO_EXCEPT
static Mp3DecoderPtr createDecoder(fl::string *error_message=nullptr) FL_NO_EXCEPT
~Mp3Decoder() FL_NO_EXCEPT
void reset() FL_NO_EXCEPT
Mp3Decoder() FL_NO_EXCEPT
bool decodeNextFrame(audio::Sample *out_sample) FL_NO_EXCEPT
bool isReady() const FL_NO_EXCEPT
fl::size getPosition() const FL_NO_EXCEPT
bool hasError(fl::string *msg=nullptr) const FL_NO_EXCEPT
Mp3Info getInfo() const FL_NO_EXCEPT
bool begin(fl::filebuf_ptr stream) FL_NO_EXCEPT
fl::unique_ptr< fl::third_party::Mp3StreamDecoderImpl > mImpl
static T & instance() FL_NO_EXCEPT
int decodeFrame(const fl::u8 **inbuf, fl::size *bytes_left) FL_NO_EXCEPT
int decode(const fl::u8 *data, fl::size len, Fn on_frame) FL_NO_EXCEPT
void reset() FL_NO_EXCEPT
fl::unique_ptr< mp3dec_t, Mp3MemoryDeleter< mp3dec_t > > mDecoder
fl::vector< audio::Sample > decodeToAudioSamples(const fl::u8 *data, fl::size len) FL_NO_EXCEPT
Mp3Minimp3Decoder() FL_NO_EXCEPT
int findSyncWord(const fl::u8 *buf, fl::size len) FL_NO_EXCEPT
fl::unique_ptr< mp3dec_scratch_t, Mp3MemoryDeleter< mp3dec_scratch_t > > mScratch
fl::unique_ptr< fl::i16[], Mp3MemoryDeleter< fl::i16 > > mPcmBuffer
~Mp3Minimp3Decoder() FL_NO_EXCEPT
fl::unique_ptr< fl::u8[], Mp3MemoryDeleter< fl::u8 > > mBuffer
bool isReady() const FL_NO_EXCEPT
Mp3Info getInfo() const FL_NO_EXCEPT
bool mHasDecodedFirstFrame
bool decodeNextFrame(audio::Sample *out_sample) FL_NO_EXCEPT
static constexpr fl::size BUFFER_SIZE
fl::size getPosition() const FL_NO_EXCEPT
bool mInspectedFirstFrame
fl::unique_ptr< Mp3SelectedDecoder > mDecoder
bool begin(fl::filebuf_ptr stream) FL_NO_EXCEPT
bool findAndDecodeFrame(audio::Sample *out_sample) FL_NO_EXCEPT
bool fillBuffer() FL_NO_EXCEPT
void reset() FL_NO_EXCEPT
Mp3StreamDecoderImpl() FL_NO_EXCEPT
~Mp3StreamDecoderImpl() FL_NO_EXCEPT
bool hasError(fl::string *msg=nullptr) const FL_NO_EXCEPT
void resetVbrState() FL_NO_EXCEPT
void reserve(fl::size n) FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
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
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
int minimp3Version(const fl::u8 *header) FL_NO_EXCEPT
bool Mp3ParseVbrTag(fl::span< const fl::u8 > bytes, Mp3VbrTag *out) FL_NO_EXCEPT
const char * Mp3MemoryTagName(Mp3MemoryTag tag) FL_NO_EXCEPT
T * Mp3MemoryAllocateArray(fl::size count, Mp3MemoryTag tag) FL_NO_EXCEPT
Mp3Minimp3Decoder Mp3SelectedDecoder
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.
constexpr fl::size MP3_MINIMP3_STREAM_BUFFER_SIZE
FL_NO_INLINE void * Mp3MemoryAllocate(fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT
void Mp3MemoryFree(void *ptr, fl::size bytes, Mp3MemoryTag tag) FL_NO_EXCEPT
constexpr fl::size MAX_PCM_SAMPLES
bool present
The frame is metadata and must not be emitted as audio.
fl::u32 encoderDelay
Samples per channel of encoder priming to drop from the start.
u8 u8 fract8 blur_amount FL_NO_EXCEPT
void Free(void *ptr) FL_NO_EXCEPT
FASTLED_FORCE_INLINE void * memset(void *s, int c, size_t n) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
fl::shared_ptr< filebuf > filebuf_ptr
int available() FL_NO_EXCEPT
fl::shared_ptr< Mp3Decoder > Mp3DecoderPtr
void * Malloc(fl::size size) FL_NO_EXCEPT
Base definition for an LED controller.
void operator()(T *ptr) const FL_NO_EXCEPT
Mp3MemoryDeleter() FL_NO_EXCEPT