FastLED 3.9.15
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
4#include "fl/audio/audio.h" // IWYU pragma: keep
5#include "fl/stl/span.h"
6#include "fl/stl/vector.h"
7#include "fl/stl/stdint.h"
8#include "fl/stl/shared_ptr.h"
9#include "fl/stl/unique_ptr.h"
10#include "fl/stl/string.h" // IWYU pragma: keep
11#include "fl/stl/noexcept.h"
12
13namespace fl {
14
15// MP3 metadata information structure
16struct Mp3Info {
17 fl::u32 sampleRate = 0; // Sample rate in Hz
18 fl::u8 channels = 0; // Number of channels (1=mono, 2=stereo)
19 fl::u32 bitrate = 0; // Bitrate in kbps
20 fl::u32 duration = 0; // Duration in milliseconds (may be 0 if unknown)
21 fl::u8 version = 0; // MPEG version (1, 2, or 2.5)
22 fl::u8 layer = 0; // MPEG layer (1, 2, or 3)
23 bool isValid = false; // True if metadata was successfully parsed
24
25 Mp3Info() FL_NOEXCEPT = default;
26
27 // Constructor for easy initialization
28 Mp3Info(fl::u32 rate, fl::u8 ch, fl::u32 br)
29 : sampleRate(rate), channels(ch), bitrate(br), isValid(true) {}
30};
31
32namespace third_party {
33 // Forward declarations of internal implementation classes
34 class Mp3StreamDecoderImpl;
35
36 // Mp3Frame represents a decoded MP3 audio frame
37 // This is exposed for testing purposes
38 struct Mp3Frame {
39 const fl::i16* pcm; // Interleaved PCM data (L/R)
40 int samples; // Samples per channel
41 int channels; // 1 (mono) or 2 (stereo)
42 int sample_rate; // Sample rate in Hz
43 int bitrate; // Bitrate in kbps
44 int version; // MPEG version
45 int layer; // MPEG layer
46 };
47
48 // Mp3HelixDecoder wraps the Helix MP3 fixed-point decoder
49 // This is exposed for testing purposes
51 public:
54
55 // Initialize the decoder. Returns true on success.
56 bool init();
57
58 // Reset decoder state
59 void reset();
60
61 // Decode MP3 data from the input buffer. Calls the provided callback
62 // for each decoded frame. Returns the number of frames decoded.
63 template <typename Fn>
64 int decode(const fl::u8* data, fl::size len, Fn on_frame) {
65 if (!mDecoder) {
66 return 0;
67 }
68
69 const fl::u8* inptr = data;
70 fl::size bytes_left = len;
71 int frames_decoded = 0;
72
73 while (bytes_left > 0) {
74 // Find sync word
75 int offset = findSyncWord(inptr, bytes_left);
76 if (offset < 0) {
77 break; // No sync word found
78 }
79
80 inptr += offset;
81 bytes_left -= offset;
82
83 // Decode one frame
84 int result = decodeFrame(&inptr, &bytes_left);
85 if (result == 0) {
86 // Successfully decoded a frame
87 Mp3Frame frame;
88 frame.pcm = mPcmBuffer.get();
89 frame.samples = mFrameInfo.outputSamps / mFrameInfo.nChans;
90 frame.channels = mFrameInfo.nChans;
91 frame.sample_rate = mFrameInfo.samprate;
92 frame.bitrate = mFrameInfo.bitrate;
93 frame.version = mFrameInfo.version;
94 frame.layer = mFrameInfo.layer;
95
96 on_frame(frame);
97 frames_decoded++;
98 } else if (result < 0) {
99 // Decode error - skip a bit and try again
100 if (bytes_left > 0) {
101 inptr++;
102 bytes_left--;
103 }
104 }
105 }
106
107 return frames_decoded;
108 }
109
110 // Decode MP3 data and convert to audio::Sample objects
111 fl::vector<audio::Sample> decodeToAudioSamples(const fl::u8* data, fl::size len);
112
113 // Public members/methods used by internal decoder implementation
114 int findSyncWord(const fl::u8* buf, fl::size len);
115 int decodeFrame(const fl::u8** inbuf, fl::size* bytes_left);
116
128
129 private:
130 void* mDecoder; // HMP3Decoder handle
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);
144
145 // Clean up decoder resources
146 void end();
147
148 // Check if decoder is ready to use
149 bool isReady() const;
150
151 // Check for errors
152 bool hasError(fl::string* msg = nullptr) const;
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);
157
158 // Get current stream position in bytes
159 fl::size getPosition() const;
160
161 // Reset decoder state (but keep stream)
162 void reset();
163
164 // Get MP3 stream information (only available after decoding first frame)
165 Mp3Info getInfo() const;
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 Helix MP3
186// fixed-point decoder may differ significantly due to:
187// - Different decoder implementations (Helix 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);
200
201 // Check if MP3 decoding is supported on this platform
202 static bool isSupported();
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);
207};
208
209} // namespace fl
static Mp3Info parseMp3Info(fl::span< const fl::u8 > data, fl::string *error_message=nullptr)
Definition mp3.cpp.hpp:410
static bool isSupported()
Definition mp3.cpp.hpp:405
static Mp3DecoderPtr createDecoder(fl::string *error_message=nullptr)
Definition mp3.cpp.hpp:400
Mp3Decoder() FL_NOEXCEPT
Definition mp3.cpp.hpp:363
bool begin(fl::filebuf_ptr stream)
Definition mp3.cpp.hpp:367
bool isReady() const
Definition mp3.cpp.hpp:375
bool hasError(fl::string *msg=nullptr) const
Definition mp3.cpp.hpp:379
~Mp3Decoder() FL_NOEXCEPT
bool decodeNextFrame(audio::Sample *out_sample)
Definition mp3.cpp.hpp:383
Mp3Info getInfo() const
Definition mp3.cpp.hpp:395
fl::size getPosition() const
Definition mp3.cpp.hpp:387
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)
Definition mp3.cpp.hpp:65
int findSyncWord(const fl::u8 *buf, fl::size len)
Definition mp3.cpp.hpp:60
fl::unique_ptr< fl::i16[]> mPcmBuffer
Definition mp3.h:117
int decode(const fl::u8 *data, fl::size len, Fn on_frame)
Definition mp3.h:64
fl::vector< audio::Sample > decodeToAudioSamples(const fl::u8 *data, fl::size len)
Definition mp3.cpp.hpp:96
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
unsigned char u8
Definition s16x16x4.h:132
unsigned char u8
Definition coder.h:132
const fl::i16 * pcm
Definition mp3.h:39
unsigned char u8
Definition stdint.h:131
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 file_system.h:19
FASTLED_SHARED_PTR(Channel)
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
fl::u8 layer
Definition mp3.h:22
Mp3Info() FL_NOEXCEPT=default
fl::u8 channels
Definition mp3.h:18
fl::u8 version
Definition mp3.h:21
fl::u32 duration
Definition mp3.h:20
fl::u32 bitrate
Definition mp3.h:19
fl::u32 sampleRate
Definition mp3.h:17
bool isValid
Definition mp3.h:23