FastLED 3.10.8
Loading...
Searching...
No Matches
mp3.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/codec/common.h" // IWYU pragma: keep
5#include "fl/audio/audio.h" // IWYU pragma: keep
6#include "fl/stl/span.h"
7#include "fl/stl/vector.h"
8#include "fl/stl/stdint.h"
9#include "fl/stl/shared_ptr.h"
10#include "fl/stl/unique_ptr.h"
11#include "fl/stl/string.h" // IWYU pragma: keep
12#include "fl/stl/noexcept.h"
13
14
15namespace fl {
16
17// MP3 metadata information structure
18struct Mp3Info {
19 fl::u32 sampleRate = 0; // Sample rate in Hz
20 fl::u8 channels = 0; // Number of channels (1=mono, 2=stereo)
21 fl::u32 bitrate = 0; // Bitrate in bits per second
22 fl::u32 duration = 0; // Duration in milliseconds (may be 0 if unknown)
23 fl::u8 version = 0; // MPEG version (1, 2, or 2.5)
24 fl::u8 layer = 0; // MPEG layer (1, 2, or 3)
25 bool isValid = false; // True if metadata was successfully parsed
26
27 Mp3Info() FL_NO_EXCEPT = default;
28
29 // Constructor for easy initialization
30 Mp3Info(fl::u32 rate, fl::u8 ch, fl::u32 br)
31 FL_NO_EXCEPT : sampleRate(rate), channels(ch), bitrate(br), isValid(true) {}
32};
33
34namespace third_party {
35 // Forward declarations of internal implementation classes
36 class Mp3StreamDecoderImpl;
37 struct mp3dec_t;
38 union mp3dec_scratch_t;
39
40 // Mp3Frame represents a decoded MP3 audio frame
41 // This is exposed for testing purposes
42 struct Mp3Frame {
43 const fl::i16* pcm; // Interleaved PCM data (L/R)
44 int samples; // Samples per channel
45 int channels; // 1 (mono) or 2 (stereo)
46 int sample_rate; // Sample rate in Hz
47 int bitrate; // Bitrate in bits per second
48 int version; // MPEG version
49 int layer; // MPEG layer
50 };
51
52 // Frame metadata shared by the decoder wrappers.
62
63 // Mp3Minimp3Decoder wraps the vendored minimp3 decoder (fixed-point by
64 // default; see MINIMP3_FLOAT_POINT for the float opt-out).
65 // This is exposed for backend-parity testing.
67 public:
70
71 bool init() FL_NO_EXCEPT;
72 void reset() FL_NO_EXCEPT;
73
74 template <typename Fn>
75 int decode(const fl::u8* data, fl::size len,
76 Fn on_frame) FL_NO_EXCEPT {
77 if (!mDecoder) {
78 return 0;
79 }
80
81 const fl::u8* inptr = data;
82 fl::size bytes_left = len;
83 int frames_decoded = 0;
84
85 while (bytes_left > 0) {
86 int offset = findSyncWord(inptr, bytes_left);
87 if (offset < 0) {
88 break;
89 }
90
91 inptr += offset;
92 bytes_left -= offset;
93
94 int result = decodeFrame(&inptr, &bytes_left);
95 if (result == 0) {
96 Mp3Frame frame;
97 frame.pcm = mPcmBuffer.get();
98 frame.samples = mFrameInfo.outputSamps / mFrameInfo.nChans;
99 frame.channels = mFrameInfo.nChans;
100 frame.sample_rate = mFrameInfo.samprate;
101 frame.bitrate = mFrameInfo.bitrate;
102 frame.version = mFrameInfo.version;
103 frame.layer = mFrameInfo.layer;
104
105 on_frame(frame);
106 frames_decoded++;
107 } else if (bytes_left > 0) {
108 inptr++;
109 bytes_left--;
110 }
111 }
112
113 return frames_decoded;
114 }
115
117 decodeToAudioSamples(const fl::u8* data,
118 fl::size len) FL_NO_EXCEPT;
119
120 int findSyncWord(const fl::u8* buf, fl::size len) FL_NO_EXCEPT;
121 int decodeFrame(const fl::u8** inbuf,
122 fl::size* bytes_left) FL_NO_EXCEPT;
123
126
127 private:
131 };
132}
133
134// MP3 decoder with streaming byte interface
135// This decoder consumes MP3 data from a filebuf and decodes audio frames on demand
137public:
140
141 // Initialize the decoder with a byte stream
142 // Returns true on success, false on failure
143 bool begin(fl::filebuf_ptr stream) FL_NO_EXCEPT;
144
145 // Clean up decoder resources
146 void end() FL_NO_EXCEPT;
147
148 // Check if decoder is ready to use
149 bool isReady() const FL_NO_EXCEPT;
150
151 // Check for errors
152 bool hasError(fl::string* msg = nullptr) const FL_NO_EXCEPT;
153
154 // Decode the next audio frame from the stream
155 // Returns true if a frame was decoded, false if end of stream or error
156 bool decodeNextFrame(audio::Sample* out_sample) FL_NO_EXCEPT;
157
158 // Get current stream position in bytes
159 fl::size getPosition() const FL_NO_EXCEPT;
160
161 // Reset decoder state (but keep stream)
162 void reset() FL_NO_EXCEPT;
163
164 // Get MP3 stream information (only available after decoding first frame)
166
167private:
168 fl::unique_ptr<fl::third_party::Mp3StreamDecoderImpl> mImpl;
169};
170
172
173// MP3 factory for creating decoders and parsing metadata
174//
175// PERFORMANCE NOTE:
176// Benchmark testing (see REPORT.md) compared MP3 vs OGG Vorbis decoding performance
177// using FFmpeg on a host machine (not embedded platform). Results showed:
178// - MP3 decode time: 420ms (10 second audio sample)
179// - OGG decode time: 327ms (28.6% faster than MP3)
180// - MP3 file size: 157KB at 128kbps
181// - OGG file size: 108KB at 128kbps (31% smaller than MP3)
182// - Audio quality difference: 0.67% RMS error (negligible)
183//
184// IMPORTANT: These benchmarks used native FFmpeg decoders on a desktop host.
185// Performance on embedded platforms (ESP32, ARM, etc.) using the fixed-point
186// minimp3 decoder may differ significantly due to:
187// - Different decoder implementations (minimp3 vs FFmpeg)
188// - CPU architecture differences (ARM Cortex-M vs x86/x64)
189// - Clock speeds and available RAM
190// - Compiler optimizations and platform-specific code paths
191//
192// RECOMMENDATION: Always profile on your target platform before choosing a codec.
193// What works best on desktop FFmpeg may not match embedded performance characteristics.
194// Run your own benchmarks using your specific hardware, sample data, and use case.
195//
196class Mp3 {
197public:
198 // Create an MP3 decoder for streaming playback
199 static Mp3DecoderPtr createDecoder(fl::string* error_message = nullptr) FL_NO_EXCEPT;
200
201 // Check if MP3 decoding is supported on this platform
202 static bool isSupported() FL_NO_EXCEPT;
203
204 // Parse MP3 metadata from byte data without creating a decoder
205 // This is a fast, lightweight operation that only reads the first MP3 frame header
206 static Mp3Info parseMp3Info(fl::span<const fl::u8> data, fl::string* error_message = nullptr) FL_NO_EXCEPT;
207};
208
209} // 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
Definition mp3.h:196
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::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
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
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
fl::shared_ptr< filebuf > filebuf_ptr
Definition idecoder.h:15
fl::shared_ptr< Mp3Decoder > Mp3DecoderPtr
Definition fs.h:27
FASTLED_SHARED_PTR(Channel)
Base definition for an LED controller.
Definition crgb.hpp:179
fl::u8 layer
Definition mp3.h:24
Mp3Info() FL_NO_EXCEPT=default
fl::u8 channels
Definition mp3.h:20
fl::u8 version
Definition mp3.h:23
fl::u32 duration
Definition mp3.h:22
fl::u32 bitrate
Definition mp3.h:21
fl::u32 sampleRate
Definition mp3.h:19
bool isValid
Definition mp3.h:25