FastLED 3.9.15
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_NOEXCEPT : mImpl(other.mImpl) {}
28 Sample(SampleImplPtr impl) FL_NOEXCEPT : 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_NOEXCEPT;
33
34 Sample &operator=(const Sample &other) FL_NOEXCEPT;
35 bool isValid() const FL_NOEXCEPT { return mImpl != nullptr; }
36
37 fl::size size() const FL_NOEXCEPT;
38 // Raw pcm levels.
39 const VectorPCM &pcm() const FL_NOEXCEPT;
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_NOEXCEPT;
43 float rms() const FL_NOEXCEPT;
44 fl::u32 timestamp() const FL_NOEXCEPT; // Timestamp when sample became valid (millis)
45
46 void fft(fft::Bins *out) const FL_NOEXCEPT;
47
48 const_iterator begin() const FL_NOEXCEPT { return pcm().begin(); }
49 const_iterator end() const FL_NOEXCEPT { return pcm().end(); }
50 const fl::i16 &at(fl::size i) const FL_NOEXCEPT;
51 const fl::i16 &operator[](fl::size i) const FL_NOEXCEPT;
52 operator bool() const FL_NOEXCEPT { return isValid(); }
53 bool operator==(const Sample &other) const FL_NOEXCEPT;
54 bool operator!=(const Sample &other) const FL_NOEXCEPT;
55
58 void applyGain(float gain) FL_NOEXCEPT;
59
60 private:
61 static const VectorPCM &empty() FL_NOEXCEPT;
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_NOEXCEPT;
77
79 void processBlock(const fl::i16 *samples, fl::size count) FL_NOEXCEPT;
81 processBlock(samples.data(), samples.size());
82 }
83
85 double getDBFS() const FL_NOEXCEPT { return mCurrentDbfs; }
86
88 double getSPL() const FL_NOEXCEPT { return mCurrentSpl; }
89
91 void setFloorSPL(double spl_floor) FL_NOEXCEPT {
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_NOEXCEPT {
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_NOEXCEPT { return mSignedPcm; }
129 fl::u32 timestamp() const FL_NOEXCEPT { return mTimestamp; }
130
131 // For object pool - reset internal state for reuse
133 mSignedPcm.clear();
134 mZeroCrossings = 0;
135 mRms = 0.0f;
136 mTimestamp = 0;
137 mRmsComputed = false;
138 }
139
140 // "Zero crossing factor". High values > .4 indicate hissing
141 // sounds. For example a microphone rubbing against a clothing.
142 // These types of signals indicate the audio should be ignored.
143 // Low zero crossing factors (with loud sound) indicate that there
144 // is organized sound like that coming from music. This is so cheap
145 // to calculate it's done automatically. It should be one of the first
146 // signals to reject or accept a sound signal.
147 //
148 // Returns: a value -> [0.0f, 1.0f)
149 // O(1) - pre-computed in constructor
150 float zcf() const FL_NOEXCEPT {
151 const fl::size n = pcm().size();
152 if (n < 2) {
153 return 0.f;
154 }
155 return float(mZeroCrossings) / static_cast<float>(n - 1);
156 }
157
158 // Root mean square amplitude of the audio signal
159 // Returns: RMS value (computed lazily on first access)
160 float rms() const FL_NOEXCEPT {
161 if (!mRmsComputed) {
162 const_cast<SampleImpl*>(this)->initRms();
163 const_cast<SampleImpl*>(this)->mRmsComputed = true;
164 }
165 return mRms;
166 }
167
168 private:
170 mZeroCrossings = 0;
171 if (mSignedPcm.size() > 1) {
172 for (fl::size i = 1; i < mSignedPcm.size(); ++i) {
173 const bool crossed =
174 (mSignedPcm[i - 1] < 0 && mSignedPcm[i] >= 0) ||
175 (mSignedPcm[i - 1] >= 0 && mSignedPcm[i] < 0);
176 if (crossed) {
178 }
179 }
180 }
181 }
182
184 if (mSignedPcm.empty()) {
185 mRms = 0.0f;
186 return;
187 }
188 fl::u64 sum_sq = 0;
189 const int N = mSignedPcm.size();
190 for (int i = 0; i < N; ++i) {
191 fl::i32 x32 = fl::i32(mSignedPcm[i]);
192 sum_sq += x32 * x32;
193 }
194 mRms = sqrtf(float(sum_sq) / N);
195 }
196
198 fl::u16 mZeroCrossings = 0;
199 float mRms = 0.0f; // Lazily computed RMS value
200 fl::u32 mTimestamp = 0;
201 mutable bool mRmsComputed = false; // Track if RMS has been computed
202};
203
204} // namespace audio
205} // namespace fl
float rms(fl::span< const int16_t > data)
Definition simple.h:104
Sample(const Sample &other) FL_NOEXCEPT
Definition audio.h:27
Sample() FL_NOEXCEPT
Definition audio.h:26
static const VectorPCM & empty() FL_NOEXCEPT
bool isValid() const FL_NOEXCEPT
Definition audio.h:35
void applyGain(float gain) FL_NOEXCEPT
Apply a digital gain multiplier to all PCM samples in-place.
fl::u32 timestamp() const FL_NOEXCEPT
float zcf() const FL_NOEXCEPT
const fl::i16 & operator[](fl::size i) const FL_NOEXCEPT
SampleImplPtr mImpl
Definition audio.h:62
fl::vector< fl::i16 > VectorPCM
Definition audio.h:24
~Sample() FL_NOEXCEPT
Definition audio.cpp.hpp:67
const_iterator end() const FL_NOEXCEPT
Definition audio.h:49
fl::size size() const FL_NOEXCEPT
Definition audio.cpp.hpp:86
VectorPCM::const_iterator const_iterator
Definition audio.h:25
bool operator==(const Sample &other) const FL_NOEXCEPT
Sample(SampleImplPtr impl) FL_NOEXCEPT
Definition audio.h:28
bool operator!=(const Sample &other) const FL_NOEXCEPT
const_iterator begin() const FL_NOEXCEPT
Definition audio.h:48
const fl::i16 & at(fl::size i) const FL_NOEXCEPT
Definition audio.cpp.hpp:93
const VectorPCM & pcm() const FL_NOEXCEPT
Definition audio.cpp.hpp:73
VectorPCM & pcm_mutable() FL_NOEXCEPT
Definition audio.h:128
fl::vector< fl::i16 > VectorPCM
Definition audio.h:114
float zcf() const FL_NOEXCEPT
Definition audio.h:150
void assign(It begin, It end, fl::u32 timestamp) FL_NOEXCEPT
Definition audio.h:119
void reset() FL_NOEXCEPT
Definition audio.h:132
~SampleImpl() FL_NOEXCEPT
Definition audio.h:115
void initZeroCrossings() FL_NOEXCEPT
Definition audio.h:169
VectorPCM mSignedPcm
Definition audio.h:197
const VectorPCM & pcm() const FL_NOEXCEPT
Definition audio.h:127
fl::u32 timestamp() const FL_NOEXCEPT
Definition audio.h:129
void initRms() FL_NOEXCEPT
Definition audio.h:183
fl::u16 mZeroCrossings
Definition audio.h:198
float rms() const FL_NOEXCEPT
Definition audio.h:160
void setFloorSPL(double spl_floor) FL_NOEXCEPT
change your known noise-floor SPL at runtime
Definition audio.h:91
void processBlock(const fl::i16 *samples, fl::size count) FL_NOEXCEPT
Process a block of int16 PCM samples.
double getDBFS() const FL_NOEXCEPT
Definition audio.h:85
void processBlock(fl::span< const fl::i16 > samples) FL_NOEXCEPT
Definition audio.h:80
void resetFloor() FL_NOEXCEPT
reset so the next quiet block will re-initialize your floor
Definition audio.h:97
SoundLevelMeter(double spl_floor=33.0, double smoothing_alpha=0.0) FL_NOEXCEPT
double getSPL() const FL_NOEXCEPT
Definition audio.h:88
iterator begin() FL_NOEXCEPT
Definition vector.h:655
const fl::i16 * const_iterator
Definition vector.h:453
iterator end() FL_NOEXCEPT
Definition vector.h:661
#define FL_INFINITY_DOUBLE
Definition math.h:50
float sqrtf(float value) FL_NOEXCEPT
Definition math.h:453
constexpr T * begin(T(&array)[N]) FL_NOEXCEPT
constexpr T * end(T(&array)[N]) FL_NOEXCEPT
fl::u64 u64
Definition s16x16x4.h:221
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
#define FASTLED_SHARED_PTR(type)
Definition shared_ptr.h:535