FastLED 3.9.15
Loading...
Searching...
No Matches
audio.cpp.hpp
Go to the documentation of this file.
1
2#include "fl/audio/audio.h"
3#include "fl/audio/fft/fft.h"
4#include "fl/math/math.h"
5#include "fl/stl/move.h"
6#include "fl/stl/mutex.h"
7#include "fl/stl/shared_ptr.h"
8#include "fl/stl/singleton.h"
9#include "fl/stl/span.h"
10#include "fl/stl/vector.h"
11#include "fl/stl/int.h"
12#include "fl/stl/noexcept.h"
13
14namespace fl {
15namespace audio {
16
17namespace {
18
19struct GuardedFFT {
21 const fft::Args &args) {
22 fl::unique_lock<fl::mutex> lock(mtx);
23 fft.run(sample, out, args);
24 }
25
26 private:
29};
30
31// Object pool implementation
32
34 void put(SampleImplPtr&& impl) {
35 if (impl.unique()) {
36 // There is no more shared_ptr to this object, so we can recycle it.
37 fl::unique_lock<fl::mutex> lock(mutex);
38 if (impl && pool.size() < MAX_POOL_SIZE) {
39 // Reset the impl for reuse (clear internal state)
40 impl->reset();
41 pool.push_back(impl);
42 return;
43 }
44 }
45 // Pool is full, discard the impl
46 impl.reset();
47 }
48 SampleImplPtr getOrCreate() {
49 {
50 fl::unique_lock<fl::mutex> lock(mutex);
51 if (!pool.empty()) {
52 SampleImplPtr impl = pool.back();
53 pool.pop_back();
54 return impl;
55 }
56 }
58 }
59
61 static constexpr fl::size MAX_POOL_SIZE = 8;
63};
64
65} // namespace
66
72
74 if (isValid()) {
75 return mImpl->pcm();
76 }
77 static VectorPCM empty;
78 return empty;
79}
80
82 mImpl = other.mImpl;
83 return *this;
84}
85
86fl::size Sample::size() const {
87 if (isValid()) {
88 return mImpl->pcm().size();
89 }
90 return 0;
91}
92
93const fl::i16 &Sample::at(fl::size i) const {
94 if (i < size()) {
95 return pcm()[i];
96 }
97 return empty()[0];
98}
99
100const fl::i16 &Sample::operator[](fl::size i) const { return at(i); }
101
102bool Sample::operator==(const Sample &other) const {
103 if (mImpl == other.mImpl) {
104 return true;
105 }
106 if (mImpl == nullptr || other.mImpl == nullptr) {
107 return false;
108 }
109 if (mImpl->pcm().size() != other.mImpl->pcm().size()) {
110 return false;
111 }
112 for (fl::size i = 0; i < mImpl->pcm().size(); ++i) {
113 if (mImpl->pcm()[i] != other.mImpl->pcm()[i]) {
114 return false;
115 }
116 }
117 return true;
118}
119
120bool Sample::operator!=(const Sample &other) const {
121 return !(*this == other);
122}
123
125 static fl::i16 empty_data[1] = {0}; // okay static in header
126 static VectorPCM empty(empty_data); // okay static in header
127 return empty;
128}
129
130float Sample::zcf() const { return mImpl->zcf(); }
131
132fl::u32 Sample::timestamp() const {
133 if (isValid()) {
134 return mImpl->timestamp();
135 }
136 return 0;
137}
138
139// O(1) - returns pre-computed cached value
140float Sample::rms() const {
141 if (!isValid()) {
142 return 0.0f;
143 }
144 return mImpl->rms();
145}
146
147SoundLevelMeter::SoundLevelMeter(double spl_floor, double smoothing_alpha)
148 : mSplFloor(spl_floor), mSmoothingAlpha(smoothing_alpha),
150 mCurrentSpl(spl_floor) {}
151
152void SoundLevelMeter::processBlock(const fl::i16 *samples, fl::size count) {
153 // 1) compute block power → dBFS
154 double sum_sq = 0.0;
155 for (fl::size i = 0; i < count; ++i) {
156 double s = samples[i] / 32768.0; // normalize to ±1
157 sum_sq += s * s;
158 }
159 double p = sum_sq / count; // mean power
160 double dbfs = 10.0 * log10(p + 1e-12);
161 mCurrentDbfs = dbfs;
162
163 // 2) update global floor (with optional smoothing)
164 if (dbfs < mDbfsFloorGlobal) {
165 if (mSmoothingAlpha <= 0.0) {
166 mDbfsFloorGlobal = dbfs;
167 } else {
170 }
172 }
173
174 // 3) estimate SPL
175 mCurrentSpl = dbfs + mOffset;
176}
177
178void Sample::fft(fft::Bins *out) const {
181 args.samples = sample.size();
182 args.bands = out->bands();
185 args.sample_rate =
186 fft::Args::DefaultSampleRate(); // TODO: get sample rate from Sample
188}
189
190
191void Sample::applyGain(float gain) {
192 if (!isValid() || gain == 1.0f) return;
193 auto& samples = mImpl->pcm_mutable();
194 for (fl::size i = 0; i < samples.size(); ++i) {
195 fl::i32 val = static_cast<fl::i32>(static_cast<float>(samples[i]) * gain);
196 if (val > 32767) val = 32767;
197 if (val < -32768) val = -32768;
198 samples[i] = static_cast<fl::i16>(val);
199 }
200}
201
204 auto begin = span.data();
205 auto end = begin + span.size();
206 mImpl->assign(begin, end, timestamp);
207}
208
209
210} // namespace audio
211} // namespace fl
static T & instance() FL_NOEXCEPT
Definition singleton.h:41
Sample & operator=(const Sample &other) FL_NOEXCEPT
Definition audio.cpp.hpp:81
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.
float rms() const FL_NOEXCEPT
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
void fft(fft::Bins *out) const FL_NOEXCEPT
~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
bool operator==(const Sample &other) const FL_NOEXCEPT
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
void processBlock(const fl::i16 *samples, fl::size count) FL_NOEXCEPT
Process a block of int16 PCM samples.
SoundLevelMeter(double spl_floor=33.0, double smoothing_alpha=0.0) FL_NOEXCEPT
fl::size bands() const FL_NOEXCEPT
Definition fft.cpp.hpp:61
const T * data() const FL_NOEXCEPT
Definition span.h:461
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
#define FL_INFINITY_DOUBLE
Definition math.h:50
Platform-independent mutex interface.
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition s16x16x4.h:28
CRGB sample(const CRGB *grid, const XYMap &xyMap, float x, float y, SampleMode mode)
Sample a pixel from a 2D CRGB grid at floating-point coordinates.
Definition sample.cpp.hpp:9
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
double log10(double value) FL_NOEXCEPT
Definition math.h:425
fl::platforms::mutex mutex
Definition mutex.h:20
Base definition for an LED controller.
Definition crgb.hpp:179
corkscrew_args args
Definition old.h:149
#define FL_NOEXCEPT
void run(fl::span< const fl::i16 > sample, fft::Bins *out, const fft::Args &args)
Definition audio.cpp.hpp:20
static float DefaultMaxFrequency() FL_NOEXCEPT
Definition fft.h:128
static int DefaultSampleRate() FL_NOEXCEPT
Definition fft.h:129
static float DefaultMinFrequency() FL_NOEXCEPT
Definition fft.h:127