From e94d4c980ed4085b2add0e57b4dffc4acdddf996 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Tue, 23 Jun 2026 20:38:08 -0500 Subject: [PATCH] finally good audio --- src/synth/Filter.hpp | 2 +- src/synth/Instruments/PianoString.cpp | 67 ++++++++++++++++++--------- src/synth/Instruments/PianoString.hpp | 11 +++-- src/synth/Synth.cpp | 2 +- 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/synth/Filter.hpp b/src/synth/Filter.hpp index fa28a0f..39b5378 100644 --- a/src/synth/Filter.hpp +++ b/src/synth/Filter.hpp @@ -33,7 +33,7 @@ private: void calculateCoefficients(); Type type_ = Type::BiquadLowpass; - float sampleRate_ = 44100.0f; + float sampleRate_ = 1.0f; float frequency_ = 6000.0f; float q_ = 0.707f; diff --git a/src/synth/Instruments/PianoString.cpp b/src/synth/Instruments/PianoString.cpp index a6d3694..98854a6 100644 --- a/src/synth/Instruments/PianoString.cpp +++ b/src/synth/Instruments/PianoString.cpp @@ -12,7 +12,6 @@ PianoString::PianoString(ConfigService* config, LoggerService* logger) : Instrum void PianoString::noteOn(float frequency, float velocity) { - logger_->log("Piano", LogFlag::Debug, "Note On"); active_ = true; frequency_ = frequency; @@ -45,7 +44,6 @@ void PianoString::noteOn(float frequency, float velocity) { } void PianoString::noteOff() { - logger_->log("Piano", LogFlag::Debug, "Note Off"); damping_ = 10.0f; recalculateConstants(); } @@ -67,41 +65,68 @@ bool PianoString::isActive() { float PianoString::process(bool& scopeTrigger) { - // simulate over string - for(size_t i = 2; i < segmentCount_ - 2; i++) { - float y_xx = stringY_current_[i-1] - 2.0f*stringY_current_[i] + stringY_current_[i+1]; - float y_xxxx = stringY_current_[i-2] - 4.0f*stringY_current_[i-1] + 6.0f*stringY_current_[i] - 4.0f*stringY_current_[i+1] + stringY_current_[i+2]; - stringY_next_[i] = a1_ * stringY_current_[i] + a2_ * stringY_previous_[i] + r2_ * y_xx - s2_ * y_xxxx; - } - stringY_next_[0] = 0.0f; - stringY_next_[1] = 0.0f; - stringY_next_[segmentCount_-1] = 0.0f; - stringY_next_[segmentCount_-2] = 0.0f; + // supersample + for(uint32_t n = 0; n < samplingSteps_; n++) { - stringY_previous_ = stringY_current_; - stringY_current_ = stringY_next_; + // simulate over string + for(size_t i = 2; i < segmentCount_ - 2; i++) { + float y_xx = stringY_current_[i-1] - 2.0f*stringY_current_[i] + stringY_current_[i+1]; + float y_xxxx = stringY_current_[i-2] - 4.0f*stringY_current_[i-1] + 6.0f*stringY_current_[i] - 4.0f*stringY_current_[i+1] + stringY_current_[i+2]; + stringY_next_[i] = a1_ * stringY_current_[i] + a2_ * stringY_previous_[i] + r2_ * y_xx - s2_ * y_xxxx; + } + stringY_next_[0] = 0.0f; + stringY_next_[1] = 0.0f; + stringY_next_[segmentCount_-1] = 0.0f; + stringY_next_[segmentCount_-2] = 0.0f; + + stringY_previous_ = stringY_current_; + stringY_current_ = stringY_next_; + } + + phaseTracker_ += frequency_ / sampleRate_; + if(phaseTracker_ > 1.0f) { + phaseTracker_ -= 1.0f; + scopeTrigger = true; + } float sampleOut = stringY_next_[static_cast(samplePosition_*L_*segmentCount_)]; rms_ = 0.99f * rms_ + 0.01f * sampleOut*sampleOut; - return 5.0f * sampleOut; + return sampleOut; } void PianoString::recalculateConstants() { - L_ = stringLength_ * std::pow(1.005f, -frequency_ + 20.0f) + 0.5f; + L_ = stringLength_ * std::pow(1.001f, -frequency_ + 20.0f) + 0.5f; - waveVelocity_ = 1.0f * frequency_ * L_; + waveVelocity_ = 2.0f * frequency_ * L_; + + samplingSteps_ = 0; + do { + samplingSteps_++; + float effectiveSampleRate_ = sampleRate_ * static_cast(samplingSteps_); + dt_ = 1.0f / effectiveSampleRate_; + + float a = (waveVelocity_*dt_/L_)*(waveVelocity_*dt_/L_); + float b = 4.0f*(stiffness_*dt_/(L_*L_))*(stiffness_*dt_/(L_*L_)); + if(stiffness_ < 0.00001f) { // avoid divide by zero + segmentCount_ = static_cast((L_ * effectiveSampleRate_)/waveVelocity_) - 1; + } else { + segmentCount_ = static_cast(std::sqrt((-a+std::sqrt(a*a+4.0*b))/(2.0*b))) - 1; + } + + } while (segmentCount_ < 80); + + segmentCount_ = std::min(segmentCount_, static_cast(80)); dx_ = L_ / static_cast(segmentCount_); - dt_ = 1.0f / sampleRate_; r1_ = waveVelocity_ * dt_/dx_; - r2_ = std::pow(waveVelocity_ * dt_/dx_, 2.0f); - s1_ = stiffness_ * dt_/std::pow(dx_, 2.0f); - s2_ = std::pow(stiffness_ * dt_/std::pow(dx_, 2.0f), 2.0f); + r2_ = (waveVelocity_ * dt_/dx_)*(waveVelocity_ * dt_/dx_); + s1_ = stiffness_ * dt_/(dx_*dx_); + s2_ = (stiffness_ * dt_/(dx_*dx_))*(stiffness_ * dt_/(dx_*dx_)); a1_ = 2.0f - 2.0f * damping_ * dt_; a2_ = 2.0f * damping_ * dt_ - 1.0f; } diff --git a/src/synth/Instruments/PianoString.hpp b/src/synth/Instruments/PianoString.hpp index 966d203..e8f8120 100644 --- a/src/synth/Instruments/PianoString.hpp +++ b/src/synth/Instruments/PianoString.hpp @@ -35,15 +35,15 @@ private: // constants // string parameters - size_t segmentCount_ = 50; + size_t segmentCount_ = 80; static constexpr float rho_ = 8000.0f; // density, steel, kg/m^3 static constexpr float radius_ = 0.001f; // meters static constexpr float stringTension_ = 1200.0f; // string tension, N - static constexpr float stiffness_ = 0.000f; // stiffness coefficient + static constexpr float stiffness_ = 0.0f; // stiffness coefficient float damping_ = 0.5f; // damping coefficient - static constexpr float stringLength_ = 5.0f; // length of string at lowest frequency (20 hz) + static constexpr float stringLength_ = 4.0f; // length of string at lowest frequency (20 hz) static constexpr float strikePosition_ = 0.2f; // x of impulse location - static constexpr float impulseWidth_ = 0.02f; // x of impulse width + static constexpr float impulseWidth_ = 0.1f; // x of impulse width static constexpr float impulseVelocity_ = 10000.0f; // x/t of impulse magnitude static constexpr float samplePosition_ = 0.1f; // percentage along L of sampling for audio float crossSectionalArea_ = pi * std::pow(radius_, 2.0f); // string cross sectional area, assuming circular @@ -54,6 +54,9 @@ private: // f0_ = waveVelocity_ / (2.0f * stringLength_); // fundamental frequency of a non-stiff string // f1_ = f0_ * std::sqrt(1.0f + stiffness_); // fundamental frequency of the stiff string + uint32_t samplingSteps_ = 1; + float phaseTracker_ = 0.0f; + float dx_ = L_ / static_cast(segmentCount_); float dt_ = 1.0f / sampleRate_; diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index 12074bf..b11f570 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -63,7 +63,7 @@ void Synth::process(float* out, size_t nFrames) { mix += voices_[j].process(temp); if(j == lowestVoice) triggered = temp; } - mix = tanh(mix/4.0f); // prevent clipping + mix = tanh(mix); // prevent clipping sampleOut = filter_.biquadProcess(mix); out[2*i] = sampleOut;