FastLED 3.10.8
Loading...
Searching...
No Matches
audio.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/math/math.h"
4#include "fl/stl/shared_ptr.h" // For FASTLED_SHARED_PTR macros
5#include "fl/stl/span.h"
6#include "fl/stl/vector.h"
7#include "fl/stl/int.h"
8#include "fl/stl/noexcept.h"
9
10namespace fl {
11namespace audio {
12namespace fft { class Bins; } // Forward declaration in correct namespace
13class Processor; // Forward declaration
14
15class SampleImpl;
16
18
19// Sample is a wrapper around SampleImpl, hiding the reference
20// counting so that the api object can be simple and have standard object
21// semantics.
22class Sample {
23 public:
27 Sample(const Sample &other) FL_NO_EXCEPT : mImpl(other.mImpl) {}
28 Sample(SampleImplPtr impl) FL_NO_EXCEPT : mImpl(impl) {}
30
31 // Constructor that takes raw audio data and handles pooling internally
32 Sample(fl::span<const fl::i16> span, fl::u32 timestamp = 0) FL_NO_EXCEPT;
33
34 Sample &operator=(const Sample &other) FL_NO_EXCEPT;
35 bool isValid() const FL_NO_EXCEPT { return mImpl != nullptr; }
36
37 fl::size size() const FL_NO_EXCEPT;
38 // Raw pcm levels.
39 const VectorPCM &pcm() const FL_NO_EXCEPT;
40 // Zero crossing factor between 0.0f -> 1.0f, detects "hiss"
41 // and sounds like cloths rubbing. Useful for sound analysis.
42 float zcf() const FL_NO_EXCEPT;
43 float rms() const FL_NO_EXCEPT;
44 fl::u32 timestamp() const FL_NO_EXCEPT; // Timestamp when sample became valid (millis)
45
46 void fft(fft::Bins *out) const FL_NO_EXCEPT;
47
48 const_iterator begin() const FL_NO_EXCEPT { return pcm().begin(); }
49 const_iterator end() const FL_NO_EXCEPT { return pcm().end(); }
50 const fl::i16 &at(fl::size i) const FL_NO_EXCEPT;
51 const fl::i16 &operator[](fl::size i) const FL_NO_EXCEPT;
52 operator bool() const FL_NO_EXCEPT { return isValid(); }
53 bool operator==(const Sample &other) const FL_NO_EXCEPT;
54 bool operator!=(const Sample &other) const FL_NO_EXCEPT;
55
58 void applyGain(float gain) FL_NO_EXCEPT;
59
60 private:
61 static const VectorPCM &empty() FL_NO_EXCEPT;
62 SampleImplPtr mImpl;
63};
64
65// Sound level meter is a persistant measuring class that will auto-tune the
66// microphone to real world SPL levels. It will adapt to the noise floor of the
67// environment. Note that the microphone only ever outputs DBFS (dB Full Scale)
68// values, which are collected over a stream of samples. The sound level meter
69// will convert this to SPL (Sound Pressure Level) values, which are the real
70// world values.
72 public:
76 SoundLevelMeter(double spl_floor = 33.0, double smoothing_alpha = 0.0) FL_NO_EXCEPT;
77
79 void processBlock(const fl::i16 *samples, fl::size count) FL_NO_EXCEPT;
81 processBlock(samples.data(), samples.size());
82 }
83
85 double getDBFS() const FL_NO_EXCEPT { return mCurrentDbfs; }
86
88 double getSPL() const FL_NO_EXCEPT { return mCurrentSpl; }
89
91 void setFloorSPL(double spl_floor) FL_NO_EXCEPT {
92 mSplFloor = spl_floor;
94 }
95
98 mDbfsFloorGlobal = FL_INFINITY_DOUBLE; // infinity<double>
99 mOffset = 0.0;
100 }
101
102 private:
103 double mSplFloor; // e.g. 33.0 dB SPL
104 double mSmoothingAlpha; // 0 = pure min, >0 = slow adapt
105 double mDbfsFloorGlobal; // lowest dBFS seen so far
106 double mOffset; // mSplFloor − mDbfsFloorGlobal
107 double mCurrentDbfs; // last block's dBFS
108 double mCurrentSpl; // last block's estimated SPL
109};
110
111// Implementation details.
113 public:
116 // template <typename It> void assign(It begin, It end) {
117 // assign(begin, end, 0); // Default timestamp to 0
118 // }
119 template <typename It> void assign(It begin, It end, fl::u32 timestamp) FL_NO_EXCEPT {
120 mSignedPcm.assign(begin, end);
122 // Pre-compute zero crossings for O(1) access
124 // RMS is computed lazily on first access to avoid blocking
125 mRmsComputed = false;
126 }
127 const VectorPCM &pcm() const FL_NO_EXCEPT { return mSignedPcm; }
129 void pcmDidChange() FL_NO_EXCEPT;
130 fl::u32 timestamp() const FL_NO_EXCEPT { return mTimestamp; }
131
132 // For object pool - reset internal state for reuse
134 mSignedPcm.clear();
135 mZeroCrossings = 0;
136 mRms = 0.0f;
137 mTimestamp = 0;
138 mRmsComputed = false;
139 }
140
141 // "Zero crossing factor". High values > .4 indicate hissing
142 // sounds. For example a microphone rubbing against a clothing.
143 // These types of signals indicate the audio should be ignored.
144 // Low zero crossing factors (with loud sound) indicate that there
145 // is organized sound like that coming from music. This is so cheap
146 // to calculate it's done automatically. It should be one of the first
147 // signals to reject or accept a sound signal.
148 //
149 // Returns: a value -> [0.0f, 1.0f)
150 // O(1) - pre-computed in constructor
151 float zcf() const FL_NO_EXCEPT {
152 const fl::size n = pcm().size();
153 if (n < 2) {
154 return 0.f;
155 }
156 return float(mZeroCrossings) / static_cast<float>(n - 1);
157 }
158
159 // Root mean square amplitude of the audio signal
160 // Returns: RMS value (computed lazily on first access)
161 float rms() const FL_NO_EXCEPT {
162 if (!mRmsComputed) {
163 const_cast<SampleImpl*>(this)->initRms();
164 const_cast<SampleImpl*>(this)->mRmsComputed = true;
165 }
166 return mRms;
167 }
168
169 private:
171 mZeroCrossings = 0;
172 if (mSignedPcm.size() > 1) {
173 for (fl::size i = 1; i < mSignedPcm.size(); ++i) {
174 const bool crossed =
175 (mSignedPcm[i - 1] < 0 && mSignedPcm[i] >= 0) ||
176 (mSignedPcm[i - 1] >= 0 && mSignedPcm[i] < 0);
177 if (crossed) {
179 }
180 }
181 }
182 }
183
185 if (mSignedPcm.empty()) {
186 mRms = 0.0f;
187 return;
188 }
189 fl::u64 sum_sq = 0;
190 const int N = mSignedPcm.size();
191 for (int i = 0; i < N; ++i) {
192 fl::i32 x32 = fl::i32(mSignedPcm[i]);
193 sum_sq += x32 * x32;
194 }
195 mRms = sqrtf(float(sum_sq) / N);
196 }
197
199 fl::u16 mZeroCrossings = 0;
200 float mRms = 0.0f; // Lazily computed RMS value
201 fl::u32 mTimestamp = 0;
202 mutable bool mRmsComputed = false; // Track if RMS has been computed
203};
204
205} // namespace audio
206} // namespace fl
float rms(fl::span< const int16_t > data)
Definition simple.h:104
bool operator==(const Sample &other) const FL_NO_EXCEPT
const VectorPCM & pcm() const FL_NO_EXCEPT
Definition audio.cpp.hpp:78
const fl::i16 & at(fl::size i) const FL_NO_EXCEPT
Definition audio.cpp.hpp:98
const_iterator end() const FL_NO_EXCEPT
Definition audio.h:49
const_iterator begin() const FL_NO_EXCEPT
Definition audio.h:48
fl::u32 timestamp() const FL_NO_EXCEPT
Sample(const Sample &other) FL_NO_EXCEPT
Definition audio.h:27
bool isValid() const FL_NO_EXCEPT
Definition audio.h:35
const fl::i16 & operator[](fl::size i) const FL_NO_EXCEPT
Sample(SampleImplPtr impl) FL_NO_EXCEPT
Definition audio.h:28
SampleImplPtr mImpl
Definition audio.h:62
fl::vector< fl::i16 > VectorPCM
Definition audio.h:24
float zcf() const FL_NO_EXCEPT
static const VectorPCM & empty() FL_NO_EXCEPT
bool operator!=(const Sample &other) const FL_NO_EXCEPT
Sample() FL_NO_EXCEPT
Definition audio.h:26
VectorPCM::const_iterator const_iterator
Definition audio.h:25
fl::size size() const FL_NO_EXCEPT
Definition audio.cpp.hpp:91
~Sample() FL_NO_EXCEPT
Definition audio.cpp.hpp:72
void applyGain(float gain) FL_NO_EXCEPT
Apply a digital gain multiplier to all PCM samples in-place.
~SampleImpl() FL_NO_EXCEPT
Definition audio.h:115
fl::vector< fl::i16 > VectorPCM
Definition audio.h:114
float rms() const FL_NO_EXCEPT
Definition audio.h:161
void reset() FL_NO_EXCEPT
Definition audio.h:133
void initZeroCrossings() FL_NO_EXCEPT
Definition audio.h:170
VectorPCM mSignedPcm
Definition audio.h:198
void initRms() FL_NO_EXCEPT
Definition audio.h:184
fl::u16 mZeroCrossings
Definition audio.h:199
fl::u32 timestamp() const FL_NO_EXCEPT
Definition audio.h:130
float zcf() const FL_NO_EXCEPT
Definition audio.h:151
void assign(It begin, It end, fl::u32 timestamp) FL_NO_EXCEPT
Definition audio.h:119
const VectorPCM & pcm() const FL_NO_EXCEPT
Definition audio.h:127
VectorPCM & pcm_mutable() FL_NO_EXCEPT
Definition audio.h:128
double getDBFS() const FL_NO_EXCEPT
Definition audio.h:85
SoundLevelMeter(double spl_floor=33.0, double smoothing_alpha=0.0) FL_NO_EXCEPT
void resetFloor() FL_NO_EXCEPT
reset so the next quiet block will re-initialize your floor
Definition audio.h:97
double getSPL() const FL_NO_EXCEPT
Definition audio.h:88
void processBlock(const fl::i16 *samples, fl::size count) FL_NO_EXCEPT
Process a block of int16 PCM samples.
void setFloorSPL(double spl_floor) FL_NO_EXCEPT
change your known noise-floor SPL at runtime
Definition audio.h:91
void processBlock(fl::span< const fl::i16 > samples) FL_NO_EXCEPT
Definition audio.h:80
const fl::i16 * const_iterator
Definition vector.h:453
iterator end() FL_NO_EXCEPT
Definition vector.h:661
iterator begin() FL_NO_EXCEPT
Definition vector.h:655
#define FL_INFINITY_DOUBLE
Definition math.h:50
constexpr T * end(T(&array)[N]) FL_NO_EXCEPT
constexpr T * begin(T(&array)[N]) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
float sqrtf(float value) FL_NO_EXCEPT
Definition math.h:461
Base definition for an LED controller.
Definition crgb.hpp:179
#define FASTLED_SHARED_PTR(type)
Definition shared_ptr.h:577