mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-15 15:50:59 +02:00
change spectrum db to dbFS
This commit is contained in:
@@ -212,6 +212,14 @@ Napi::Value SpectrumGetSideMagnitudes(const Napi::CallbackInfo& info) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Napi::Value SpectrumGetChannelMaxMagnitudes(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
const auto& magnitudes = spectrum.getChannelMaxMagnitudes();
|
||||
Napi::Float32Array result = Napi::Float32Array::New(env, magnitudes.size());
|
||||
memcpy(result.Data(), magnitudes.data(), magnitudes.size() * sizeof(float));
|
||||
return result;
|
||||
}
|
||||
|
||||
Napi::Value SpectrumFillRawMagnitudes(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
if (info.Length() < 1 || !info[0].IsTypedArray()) {
|
||||
@@ -260,6 +268,22 @@ Napi::Value SpectrumFillSideMagnitudes(const Napi::CallbackInfo& info) {
|
||||
return Napi::Number::New(env, static_cast<double>(count));
|
||||
}
|
||||
|
||||
Napi::Value SpectrumFillChannelMaxMagnitudes(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
if (info.Length() < 1 || !info[0].IsTypedArray()) {
|
||||
Napi::TypeError::New(env, "Expected output Float32Array").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
}
|
||||
|
||||
Napi::Float32Array output = info[0].As<Napi::Float32Array>();
|
||||
const auto& magnitudes = spectrum.getChannelMaxMagnitudes();
|
||||
const size_t count = std::min(output.ElementLength(), magnitudes.size());
|
||||
if (count > 0) {
|
||||
memcpy(output.Data(), magnitudes.data(), count * sizeof(float));
|
||||
}
|
||||
return Napi::Number::New(env, static_cast<double>(count));
|
||||
}
|
||||
|
||||
Napi::Value SpectrumProcess(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
if (info.Length() < 1 || !info[0].IsTypedArray()) {
|
||||
@@ -686,9 +710,11 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
||||
specExports.Set("fillRawMagnitudes", Napi::Function::New(env, SpectrumFillRawMagnitudes));
|
||||
specExports.Set("fillMagnitudes", Napi::Function::New(env, SpectrumFillMagnitudes));
|
||||
specExports.Set("fillSideMagnitudes", Napi::Function::New(env, SpectrumFillSideMagnitudes));
|
||||
specExports.Set("fillChannelMaxMagnitudes", Napi::Function::New(env, SpectrumFillChannelMaxMagnitudes));
|
||||
specExports.Set("getRawMagnitudes", Napi::Function::New(env, SpectrumGetRawMagnitudes));
|
||||
specExports.Set("getMagnitudes", Napi::Function::New(env, SpectrumGetMagnitudes));
|
||||
specExports.Set("getSideMagnitudes", Napi::Function::New(env, SpectrumGetSideMagnitudes));
|
||||
specExports.Set("getChannelMaxMagnitudes", Napi::Function::New(env, SpectrumGetChannelMaxMagnitudes));
|
||||
specExports.Set("process", Napi::Function::New(env, SpectrumProcess));
|
||||
specExports.Set("binToFrequency", Napi::Function::New(env, SpectrumBinToFrequency));
|
||||
specExports.Set("reset", Napi::Function::New(env, SpectrumReset));
|
||||
|
||||
+53
-60
@@ -15,12 +15,16 @@ Spectrum::Spectrum(size_t fftSize)
|
||||
historyBuffer_.resize(fftSize, 0.0f);
|
||||
sideHistoryBuffer_.resize(fftSize, 0.0f);
|
||||
windowedInput_.resize(fftSize);
|
||||
magnitudes_.resize(fftSize / 2);
|
||||
midSpectrum_.resize(fftSize);
|
||||
sideSpectrum_.resize(fftSize);
|
||||
rawMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
// Initialize to silence (-100.0f dB)
|
||||
smoothedMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
sideRawMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
sideSmoothedMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
leftSmoothedMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
rightSmoothedMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
channelMaxMagnitudes_.resize(fftSize / 2, -100.0f);
|
||||
}
|
||||
|
||||
void Spectrum::setFFTSize(size_t size) {
|
||||
@@ -30,12 +34,16 @@ void Spectrum::setFFTSize(size_t size) {
|
||||
historyBuffer_.assign(size, 0.0f);
|
||||
sideHistoryBuffer_.assign(size, 0.0f);
|
||||
windowedInput_.resize(size);
|
||||
magnitudes_.resize(size / 2);
|
||||
midSpectrum_.resize(size);
|
||||
sideSpectrum_.resize(size);
|
||||
rawMagnitudes_.assign(size / 2, -100.0f);
|
||||
// Initialize to silence (-100.0f dB)
|
||||
smoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
sideRawMagnitudes_.assign(size / 2, -100.0f);
|
||||
sideSmoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
leftSmoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
rightSmoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
channelMaxMagnitudes_.assign(size / 2, -100.0f);
|
||||
bufferedSamples_ = 0;
|
||||
}
|
||||
}
|
||||
@@ -94,69 +102,52 @@ void Spectrum::pushZeroHistory(std::vector<float>& history, size_t length) {
|
||||
std::fill(history.begin() + keep, history.end(), 0.0f);
|
||||
}
|
||||
|
||||
void Spectrum::updateMagnitudesForHistory(
|
||||
const std::vector<float>& history,
|
||||
std::vector<float>& rawMagnitudes,
|
||||
std::vector<float>& smoothedMagnitudes
|
||||
) {
|
||||
if (history.empty() || magnitudes_.empty()) {
|
||||
return;
|
||||
float Spectrum::magnitudeToDb(float magnitude, float correctionDb) const {
|
||||
const float db = 20.0f * log10f(std::max(magnitude, 1e-10f)) + correctionDb;
|
||||
return std::clamp(db, -120.0f, 12.0f);
|
||||
}
|
||||
|
||||
void Spectrum::updateSmoothedMagnitude(float db, float& smoothedMagnitude) {
|
||||
if (bufferedSamples_ < fftSize_) {
|
||||
smoothedMagnitude = db;
|
||||
} else {
|
||||
smoothedMagnitude = smoothing_ * smoothedMagnitude + (1.0f - smoothing_) * db;
|
||||
}
|
||||
|
||||
// Always analyze a full FFT frame from the rolling buffer.
|
||||
applyWindow(history.data(), windowedInput_.data(), fftSize_);
|
||||
|
||||
// Perform FFT
|
||||
fft_->forward(windowedInput_.data(), magnitudes_.data());
|
||||
|
||||
// Convert to dB and apply smoothing
|
||||
for (size_t i = 0; i < magnitudes_.size(); i++) {
|
||||
float mag = magnitudes_[i];
|
||||
|
||||
// Convert to dB
|
||||
// Add epsilon to avoid log(0)
|
||||
float db = 20.0f * log10f(std::max(mag, 1e-10f));
|
||||
|
||||
// Compensate Hann window coherent gain (about -6 dB).
|
||||
db += 6.0f;
|
||||
|
||||
// Clamp to a stable display range.
|
||||
db = std::clamp(db, -120.0f, 12.0f);
|
||||
rawMagnitudes[i] = db;
|
||||
|
||||
if (bufferedSamples_ < fftSize_) {
|
||||
smoothedMagnitudes[i] = db;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply temporal smoothing only (no bin-to-bin averaging).
|
||||
smoothedMagnitudes[i] = smoothing_ * smoothedMagnitudes[i] + (1.0f - smoothing_) * db;
|
||||
|
||||
// Safety check
|
||||
if (!std::isfinite(smoothedMagnitudes[i])) {
|
||||
smoothedMagnitudes[i] = -100.0f;
|
||||
}
|
||||
if (!std::isfinite(smoothedMagnitude)) {
|
||||
smoothedMagnitude = -100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void Spectrum::updateMagnitudes() {
|
||||
updateMagnitudesForHistory(historyBuffer_, rawMagnitudes_, smoothedMagnitudes_);
|
||||
updateMagnitudesForHistory(sideHistoryBuffer_, sideRawMagnitudes_, sideSmoothedMagnitudes_);
|
||||
}
|
||||
if (historyBuffer_.empty() || rawMagnitudes_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Spectrum::updateSilentSideMagnitudes() {
|
||||
const float silentDb = -120.0f;
|
||||
for (size_t i = 0; i < sideSmoothedMagnitudes_.size(); i++) {
|
||||
sideRawMagnitudes_[i] = silentDb;
|
||||
if (bufferedSamples_ < fftSize_) {
|
||||
sideSmoothedMagnitudes_[i] = silentDb;
|
||||
continue;
|
||||
}
|
||||
applyWindow(historyBuffer_.data(), windowedInput_.data(), fftSize_);
|
||||
fft_->forward(windowedInput_.data(), midSpectrum_.data());
|
||||
applyWindow(sideHistoryBuffer_.data(), windowedInput_.data(), fftSize_);
|
||||
fft_->forward(windowedInput_.data(), sideSpectrum_.data());
|
||||
|
||||
sideSmoothedMagnitudes_[i] = smoothing_ * sideSmoothedMagnitudes_[i] + (1.0f - smoothing_) * silentDb;
|
||||
if (!std::isfinite(sideSmoothedMagnitudes_[i])) {
|
||||
sideSmoothedMagnitudes_[i] = -100.0f;
|
||||
}
|
||||
const float scale = 2.0f / static_cast<float>(fftSize_);
|
||||
const float coherentGain = fftSize_ > 1
|
||||
? static_cast<float>(fftSize_ - 1) / (2.0f * static_cast<float>(fftSize_))
|
||||
: 1.0f;
|
||||
const float correctionDb = -20.0f * log10f(coherentGain);
|
||||
for (size_t i = 0; i < rawMagnitudes_.size(); i++) {
|
||||
const std::complex<float> mid = midSpectrum_[i];
|
||||
const std::complex<float> side = sideSpectrum_[i];
|
||||
const float midDb = magnitudeToDb(std::abs(mid) * scale, correctionDb);
|
||||
const float sideDb = magnitudeToDb(std::abs(side) * scale, correctionDb);
|
||||
const float leftDb = magnitudeToDb(std::abs(mid + side) * scale, correctionDb);
|
||||
const float rightDb = magnitudeToDb(std::abs(mid - side) * scale, correctionDb);
|
||||
|
||||
rawMagnitudes_[i] = midDb;
|
||||
sideRawMagnitudes_[i] = sideDb;
|
||||
updateSmoothedMagnitude(midDb, smoothedMagnitudes_[i]);
|
||||
updateSmoothedMagnitude(sideDb, sideSmoothedMagnitudes_[i]);
|
||||
updateSmoothedMagnitude(leftDb, leftSmoothedMagnitudes_[i]);
|
||||
updateSmoothedMagnitude(rightDb, rightSmoothedMagnitudes_[i]);
|
||||
channelMaxMagnitudes_[i] = std::max(leftSmoothedMagnitudes_[i], rightSmoothedMagnitudes_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +156,7 @@ void Spectrum::pushSamples(const float* input, size_t length) {
|
||||
pushHistory(historyBuffer_, input, length);
|
||||
pushZeroHistory(sideHistoryBuffer_, length);
|
||||
bufferedSamples_ = length >= fftSize_ ? fftSize_ : std::min(fftSize_, bufferedSamples_ + length);
|
||||
updateMagnitudesForHistory(historyBuffer_, rawMagnitudes_, smoothedMagnitudes_);
|
||||
updateSilentSideMagnitudes();
|
||||
updateMagnitudes();
|
||||
return;
|
||||
}
|
||||
updateMagnitudes();
|
||||
@@ -215,6 +205,9 @@ void Spectrum::reset() {
|
||||
std::fill(smoothedMagnitudes_.begin(), smoothedMagnitudes_.end(), -100.0f);
|
||||
std::fill(sideRawMagnitudes_.begin(), sideRawMagnitudes_.end(), -100.0f);
|
||||
std::fill(sideSmoothedMagnitudes_.begin(), sideSmoothedMagnitudes_.end(), -100.0f);
|
||||
std::fill(leftSmoothedMagnitudes_.begin(), leftSmoothedMagnitudes_.end(), -100.0f);
|
||||
std::fill(rightSmoothedMagnitudes_.begin(), rightSmoothedMagnitudes_.end(), -100.0f);
|
||||
std::fill(channelMaxMagnitudes_.begin(), channelMaxMagnitudes_.end(), -100.0f);
|
||||
bufferedSamples_ = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
// Read the latest smoothed magnitudes without mutating analyzer state.
|
||||
const std::vector<float>& getMagnitudes() const { return smoothedMagnitudes_; }
|
||||
const std::vector<float>& getSideMagnitudes() const { return sideSmoothedMagnitudes_; }
|
||||
const std::vector<float>& getChannelMaxMagnitudes() const { return channelMaxMagnitudes_; }
|
||||
|
||||
// Process audio and get spectrum data
|
||||
// Returns magnitude data (size = fftSize / 2)
|
||||
@@ -46,22 +47,22 @@ private:
|
||||
std::vector<float> historyBuffer_;
|
||||
std::vector<float> sideHistoryBuffer_;
|
||||
std::vector<float> windowedInput_;
|
||||
std::vector<float> magnitudes_;
|
||||
std::vector<std::complex<float>> midSpectrum_;
|
||||
std::vector<std::complex<float>> sideSpectrum_;
|
||||
std::vector<float> rawMagnitudes_;
|
||||
std::vector<float> smoothedMagnitudes_;
|
||||
std::vector<float> sideRawMagnitudes_;
|
||||
std::vector<float> sideSmoothedMagnitudes_;
|
||||
std::vector<float> leftSmoothedMagnitudes_;
|
||||
std::vector<float> rightSmoothedMagnitudes_;
|
||||
std::vector<float> channelMaxMagnitudes_;
|
||||
size_t bufferedSamples_;
|
||||
|
||||
void applyWindow(const float* input, float* output, size_t length);
|
||||
void pushHistory(std::vector<float>& history, const float* input, size_t length);
|
||||
void pushZeroHistory(std::vector<float>& history, size_t length);
|
||||
void updateMagnitudesForHistory(
|
||||
const std::vector<float>& history,
|
||||
std::vector<float>& rawMagnitudes,
|
||||
std::vector<float>& smoothedMagnitudes
|
||||
);
|
||||
void updateSilentSideMagnitudes();
|
||||
float magnitudeToDb(float magnitude, float correctionDb) const;
|
||||
void updateSmoothedMagnitude(float db, float& smoothedMagnitude);
|
||||
void updateMagnitudes();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user