mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-16 00:00:31 +02:00
fix spectrum sideline being JS not native
This commit is contained in:
@@ -174,6 +174,19 @@ Napi::Value SpectrumPushSamples(const Napi::CallbackInfo& info) {
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
Napi::Value SpectrumPushStereoSamples(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
if (info.Length() < 2 || !info[0].IsTypedArray() || !info[1].IsTypedArray()) {
|
||||
Napi::TypeError::New(env, "Expected left and right Float32Array").ThrowAsJavaScriptException();
|
||||
return env.Null();
|
||||
}
|
||||
Napi::Float32Array leftData = info[0].As<Napi::Float32Array>();
|
||||
Napi::Float32Array rightData = info[1].As<Napi::Float32Array>();
|
||||
const size_t length = std::min(leftData.ElementLength(), rightData.ElementLength());
|
||||
spectrum.pushStereoSamples(leftData.Data(), rightData.Data(), length);
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
Napi::Value SpectrumGetMagnitudes(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
const auto& magnitudes = spectrum.getMagnitudes();
|
||||
@@ -190,6 +203,14 @@ Napi::Value SpectrumGetRawMagnitudes(const Napi::CallbackInfo& info) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Napi::Value SpectrumGetSideMagnitudes(const Napi::CallbackInfo& info) {
|
||||
Napi::Env env = info.Env();
|
||||
const auto& magnitudes = spectrum.getSideMagnitudes();
|
||||
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()) {
|
||||
@@ -222,6 +243,22 @@ Napi::Value SpectrumFillMagnitudes(const Napi::CallbackInfo& info) {
|
||||
return Napi::Number::New(env, static_cast<double>(count));
|
||||
}
|
||||
|
||||
Napi::Value SpectrumFillSideMagnitudes(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.getSideMagnitudes();
|
||||
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()) {
|
||||
@@ -644,10 +681,13 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
||||
specExports.Set("setSampleRate", Napi::Function::New(env, SpectrumSetSampleRate));
|
||||
specExports.Set("setSmoothing", Napi::Function::New(env, SpectrumSetSmoothing));
|
||||
specExports.Set("pushSamples", Napi::Function::New(env, SpectrumPushSamples));
|
||||
specExports.Set("pushStereoSamples", Napi::Function::New(env, SpectrumPushStereoSamples));
|
||||
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("getRawMagnitudes", Napi::Function::New(env, SpectrumGetRawMagnitudes));
|
||||
specExports.Set("getMagnitudes", Napi::Function::New(env, SpectrumGetMagnitudes));
|
||||
specExports.Set("getSideMagnitudes", Napi::Function::New(env, SpectrumGetSideMagnitudes));
|
||||
specExports.Set("process", Napi::Function::New(env, SpectrumProcess));
|
||||
specExports.Set("binToFrequency", Napi::Function::New(env, SpectrumBinToFrequency));
|
||||
specExports.Set("reset", Napi::Function::New(env, SpectrumReset));
|
||||
|
||||
+97
-23
@@ -13,11 +13,14 @@ Spectrum::Spectrum(size_t fftSize)
|
||||
, bufferedSamples_(0) {
|
||||
fft_ = std::make_unique<DSP::FFT>(fftSize);
|
||||
historyBuffer_.resize(fftSize, 0.0f);
|
||||
sideHistoryBuffer_.resize(fftSize, 0.0f);
|
||||
windowedInput_.resize(fftSize);
|
||||
magnitudes_.resize(fftSize / 2);
|
||||
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);
|
||||
}
|
||||
|
||||
void Spectrum::setFFTSize(size_t size) {
|
||||
@@ -25,11 +28,14 @@ void Spectrum::setFFTSize(size_t size) {
|
||||
fftSize_ = size;
|
||||
fft_ = std::make_unique<DSP::FFT>(size);
|
||||
historyBuffer_.assign(size, 0.0f);
|
||||
sideHistoryBuffer_.assign(size, 0.0f);
|
||||
windowedInput_.resize(size);
|
||||
magnitudes_.resize(size / 2);
|
||||
rawMagnitudes_.resize(size / 2, -100.0f);
|
||||
rawMagnitudes_.assign(size / 2, -100.0f);
|
||||
// Initialize to silence (-100.0f dB)
|
||||
smoothedMagnitudes_.resize(size / 2, -100.0f);
|
||||
smoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
sideRawMagnitudes_.assign(size / 2, -100.0f);
|
||||
sideSmoothedMagnitudes_.assign(size / 2, -100.0f);
|
||||
bufferedSamples_ = 0;
|
||||
}
|
||||
}
|
||||
@@ -57,31 +63,48 @@ void Spectrum::applyWindow(const float* input, float* output, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
void Spectrum::pushHistory(const float* input, size_t length) {
|
||||
void Spectrum::pushHistory(std::vector<float>& history, const float* input, size_t length) {
|
||||
if (length == 0 || fftSize_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep only the most recent fftSize_ samples.
|
||||
if (length >= fftSize_) {
|
||||
std::memcpy(historyBuffer_.data(), input + (length - fftSize_), fftSize_ * sizeof(float));
|
||||
bufferedSamples_ = fftSize_;
|
||||
std::memcpy(history.data(), input + (length - fftSize_), fftSize_ * sizeof(float));
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t keep = fftSize_ - length;
|
||||
std::move(historyBuffer_.begin() + length, historyBuffer_.end(), historyBuffer_.begin());
|
||||
std::memcpy(historyBuffer_.data() + keep, input, length * sizeof(float));
|
||||
bufferedSamples_ = std::min(fftSize_, bufferedSamples_ + length);
|
||||
std::move(history.begin() + length, history.end(), history.begin());
|
||||
std::memcpy(history.data() + keep, input, length * sizeof(float));
|
||||
}
|
||||
|
||||
void Spectrum::updateMagnitudes() {
|
||||
if (historyBuffer_.empty() || magnitudes_.empty()) {
|
||||
void Spectrum::pushZeroHistory(std::vector<float>& history, size_t length) {
|
||||
if (length == 0 || fftSize_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (length >= fftSize_) {
|
||||
std::fill(history.begin(), history.end(), 0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t keep = fftSize_ - length;
|
||||
std::move(history.begin() + length, history.end(), history.begin());
|
||||
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;
|
||||
}
|
||||
|
||||
// Always analyze a full FFT frame from the rolling buffer.
|
||||
applyWindow(historyBuffer_.data(), windowedInput_.data(), fftSize_);
|
||||
applyWindow(history.data(), windowedInput_.data(), fftSize_);
|
||||
|
||||
// Perform FFT
|
||||
fft_->forward(windowedInput_.data(), magnitudes_.data());
|
||||
@@ -99,37 +122,85 @@ void Spectrum::updateMagnitudes() {
|
||||
|
||||
// Clamp to a stable display range.
|
||||
db = std::clamp(db, -120.0f, 12.0f);
|
||||
rawMagnitudes_[i] = db;
|
||||
rawMagnitudes[i] = db;
|
||||
|
||||
if (bufferedSamples_ < fftSize_) {
|
||||
smoothedMagnitudes_[i] = db;
|
||||
smoothedMagnitudes[i] = db;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply temporal smoothing only (no bin-to-bin averaging).
|
||||
smoothedMagnitudes_[i] = smoothing_ * smoothedMagnitudes_[i] + (1.0f - smoothing_) * db;
|
||||
smoothedMagnitudes[i] = smoothing_ * smoothedMagnitudes[i] + (1.0f - smoothing_) * db;
|
||||
|
||||
// Safety check
|
||||
if (!std::isfinite(smoothedMagnitudes_[i])) {
|
||||
smoothedMagnitudes_[i] = -100.0f;
|
||||
if (!std::isfinite(smoothedMagnitudes[i])) {
|
||||
smoothedMagnitudes[i] = -100.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spectrum::updateMagnitudes() {
|
||||
updateMagnitudesForHistory(historyBuffer_, rawMagnitudes_, smoothedMagnitudes_);
|
||||
updateMagnitudesForHistory(sideHistoryBuffer_, sideRawMagnitudes_, sideSmoothedMagnitudes_);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
sideSmoothedMagnitudes_[i] = smoothing_ * sideSmoothedMagnitudes_[i] + (1.0f - smoothing_) * silentDb;
|
||||
if (!std::isfinite(sideSmoothedMagnitudes_[i])) {
|
||||
sideSmoothedMagnitudes_[i] = -100.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spectrum::pushSamples(const float* input, size_t length) {
|
||||
if (input != nullptr && length > 0) {
|
||||
pushHistory(input, length);
|
||||
pushHistory(historyBuffer_, input, length);
|
||||
pushZeroHistory(sideHistoryBuffer_, length);
|
||||
bufferedSamples_ = length >= fftSize_ ? fftSize_ : std::min(fftSize_, bufferedSamples_ + length);
|
||||
updateMagnitudesForHistory(historyBuffer_, rawMagnitudes_, smoothedMagnitudes_);
|
||||
updateSilentSideMagnitudes();
|
||||
return;
|
||||
}
|
||||
updateMagnitudes();
|
||||
}
|
||||
|
||||
void Spectrum::pushStereoSamples(const float* left, const float* right, size_t length) {
|
||||
if (left != nullptr && right != nullptr && length > 0 && fftSize_ > 0) {
|
||||
if (length >= fftSize_) {
|
||||
const size_t start = length - fftSize_;
|
||||
for (size_t i = 0; i < fftSize_; i++) {
|
||||
const float leftValue = left[start + i];
|
||||
const float rightValue = right[start + i];
|
||||
historyBuffer_[i] = (leftValue + rightValue) * 0.5f;
|
||||
sideHistoryBuffer_[i] = (leftValue - rightValue) * 0.5f;
|
||||
}
|
||||
bufferedSamples_ = fftSize_;
|
||||
} else {
|
||||
const size_t keep = fftSize_ - length;
|
||||
std::move(historyBuffer_.begin() + length, historyBuffer_.end(), historyBuffer_.begin());
|
||||
std::move(sideHistoryBuffer_.begin() + length, sideHistoryBuffer_.end(), sideHistoryBuffer_.begin());
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const float leftValue = left[i];
|
||||
const float rightValue = right[i];
|
||||
historyBuffer_[keep + i] = (leftValue + rightValue) * 0.5f;
|
||||
sideHistoryBuffer_[keep + i] = (leftValue - rightValue) * 0.5f;
|
||||
}
|
||||
bufferedSamples_ = std::min(fftSize_, bufferedSamples_ + length);
|
||||
}
|
||||
}
|
||||
updateMagnitudes();
|
||||
}
|
||||
|
||||
const std::vector<float>& Spectrum::process(const float* audioData, size_t length) {
|
||||
if (audioData != nullptr && length > 0) {
|
||||
pushHistory(audioData, length);
|
||||
}
|
||||
|
||||
updateMagnitudes();
|
||||
|
||||
pushSamples(audioData, length);
|
||||
return smoothedMagnitudes_;
|
||||
}
|
||||
|
||||
@@ -139,8 +210,11 @@ float Spectrum::binToFrequency(int bin) const {
|
||||
|
||||
void Spectrum::reset() {
|
||||
std::fill(historyBuffer_.begin(), historyBuffer_.end(), 0.0f);
|
||||
std::fill(sideHistoryBuffer_.begin(), sideHistoryBuffer_.end(), 0.0f);
|
||||
std::fill(rawMagnitudes_.begin(), rawMagnitudes_.end(), -100.0f);
|
||||
std::fill(smoothedMagnitudes_.begin(), smoothedMagnitudes_.end(), -100.0f);
|
||||
std::fill(sideRawMagnitudes_.begin(), sideRawMagnitudes_.end(), -100.0f);
|
||||
std::fill(sideSmoothedMagnitudes_.begin(), sideSmoothedMagnitudes_.end(), -100.0f);
|
||||
bufferedSamples_ = 0;
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -18,12 +18,14 @@ public:
|
||||
|
||||
// Feed new samples into the rolling history and update the latest magnitudes.
|
||||
void pushSamples(const float* input, size_t length);
|
||||
void pushStereoSamples(const float* left, const float* right, size_t length);
|
||||
|
||||
// Read the latest raw clamped dB magnitudes without mutating analyzer state.
|
||||
const std::vector<float>& getRawMagnitudes() const { return rawMagnitudes_; }
|
||||
|
||||
// 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_; }
|
||||
|
||||
// Process audio and get spectrum data
|
||||
// Returns magnitude data (size = fftSize / 2)
|
||||
@@ -42,14 +44,24 @@ private:
|
||||
|
||||
std::unique_ptr<DSP::FFT> fft_;
|
||||
std::vector<float> historyBuffer_;
|
||||
std::vector<float> sideHistoryBuffer_;
|
||||
std::vector<float> windowedInput_;
|
||||
std::vector<float> magnitudes_;
|
||||
std::vector<float> rawMagnitudes_;
|
||||
std::vector<float> smoothedMagnitudes_;
|
||||
std::vector<float> sideRawMagnitudes_;
|
||||
std::vector<float> sideSmoothedMagnitudes_;
|
||||
size_t bufferedSamples_;
|
||||
|
||||
void applyWindow(const float* input, float* output, size_t length);
|
||||
void pushHistory(const float* input, 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();
|
||||
void updateMagnitudes();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user