initial commit

This commit is contained in:
2026-02-12 00:45:31 -08:00
commit 5f168f370b
3024 changed files with 804889 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
#pragma once
#include "fl/fft.h"
#include "fl/math.h"
#include "fl/memory.h"
#include "fl/span.h"
#include "fl/vector.h"
#include "fl/int.h"
#include <math.h>
#include "fl/stdint.h"
#include "fl/span.h"
namespace fl {
class AudioSampleImpl;
FASTLED_SMART_PTR(AudioSampleImpl);
// AudioSample is a wrapper around AudioSampleImpl, hiding the reference
// counting so that the api object can be simple and have standard object
// semantics.
class AudioSample {
public:
using VectorPCM = fl::vector<fl::i16>;
using const_iterator = VectorPCM::const_iterator;
AudioSample() {}
AudioSample(const AudioSample &other) : mImpl(other.mImpl) {}
AudioSample(AudioSampleImplPtr impl) : mImpl(impl) {}
~AudioSample();
// Constructor that takes raw audio data and handles pooling internally
AudioSample(fl::span<const fl::i16> span, fl::u32 timestamp = 0);
AudioSample &operator=(const AudioSample &other);
bool isValid() const { return mImpl != nullptr; }
fl::size size() const;
// Raw pcm levels.
const VectorPCM &pcm() const;
// Zero crossing factor between 0.0f -> 1.0f, detects "hiss"
// and sounds like cloths rubbing. Useful for sound analysis.
float zcf() const;
float rms() const;
fl::u32 timestamp() const; // Timestamp when sample became valid (millis)
void fft(FFTBins *out) const;
const_iterator begin() const { return pcm().begin(); }
const_iterator end() const { return pcm().end(); }
const fl::i16 &at(fl::size i) const;
const fl::i16 &operator[](fl::size i) const;
operator bool() const { return isValid(); }
bool operator==(const AudioSample &other) const;
bool operator!=(const AudioSample &other) const;
private:
static const VectorPCM &empty();
AudioSampleImplPtr mImpl;
};
// Sound level meter is a persistant measuring class that will auto-tune the
// microphone to real world SPL levels. It will adapt to the noise floor of the
// environment. Note that the microphone only ever outputs DBFS (dB Full Scale)
// values, which are collected over a stream of samples. The sound level meter
// will convert this to SPL (Sound Pressure Level) values, which are the real
// world values.
class SoundLevelMeter {
public:
/// @param spl_floor The SPL (dB SPL) that corresponds to your true
/// noise-floor.
/// @param smoothing_alpha [0…1] how quickly to adapt floor; 0=instant min.
SoundLevelMeter(double spl_floor = 33.0, double smoothing_alpha = 0.0);
/// Process a block of int16 PCM samples.
void processBlock(const fl::i16 *samples, fl::size count);
void processBlock(fl::span<const fl::i16> samples) {
processBlock(samples.data(), samples.size());
}
/// @returns most recent blocks level in dBFS (≤0)
double getDBFS() const { return current_dbfs_; }
/// @returns calibrated estimate in dB SPL
double getSPL() const { return current_spl_; }
/// change your known noise-floor SPL at runtime
void setFloorSPL(double spl_floor) {
spl_floor_ = spl_floor;
offset_ = spl_floor_ - dbfs_floor_global_;
}
/// reset so the next quiet block will re-initialize your floor
void resetFloor() {
dbfs_floor_global_ = INFINITY_DOUBLE; // infinity<double>
offset_ = 0.0;
}
private:
double spl_floor_; // e.g. 33.0 dB SPL
double smoothing_alpha_; // 0 = pure min, >0 = slow adapt
double dbfs_floor_global_; // lowest dBFS seen so far
double offset_; // spl_floor_ dbfs_floor_global_
double current_dbfs_; // last blocks dBFS
double current_spl_; // last blocks estimated SPL
};
// Implementation details.
class AudioSampleImpl {
public:
using VectorPCM = fl::vector<fl::i16>;
~AudioSampleImpl() {}
// template <typename It> void assign(It begin, It end) {
// assign(begin, end, 0); // Default timestamp to 0
// }
template <typename It> void assign(It begin, It end, fl::u32 timestamp) {
mSignedPcm.assign(begin, end);
mTimestamp = timestamp;
// calculate zero crossings
initZeroCrossings();
}
const VectorPCM &pcm() const { return mSignedPcm; }
fl::u32 timestamp() const { return mTimestamp; }
// For object pool - reset internal state for reuse
void reset() {
mSignedPcm.clear();
mZeroCrossings = 0;
mTimestamp = 0;
}
// "Zero crossing factor". High values > .4 indicate hissing
// sounds. For example a microphone rubbing against a clothing.
// These types of signals indicate the audio should be ignored.
// Low zero crossing factors (with loud sound) indicate that there
// is organized sound like that coming from music. This is so cheap
// to calculate it's done automatically. It should be one of the first
// signals to reject or accept a sound signal.
//
// Returns: a value -> [0.0f, 1.0f)
float zcf() const {
const fl::size n = pcm().size();
if (n < 2) {
return 0.f;
}
return float(mZeroCrossings) / static_cast<float>(n - 1);
}
private:
void initZeroCrossings() {
mZeroCrossings = 0;
if (mSignedPcm.size() > 1) {
for (fl::size i = 1; i < mSignedPcm.size(); ++i) {
const bool crossed =
(mSignedPcm[i - 1] < 0 && mSignedPcm[i] >= 0) ||
(mSignedPcm[i - 1] >= 0 && mSignedPcm[i] < 0);
if (crossed) {
++mZeroCrossings;
}
}
}
}
VectorPCM mSignedPcm;
fl::i16 mZeroCrossings = 0;
fl::u32 mTimestamp = 0;
};
} // namespace fl