From bb1e6de3dfbe16459694d47c60bf22b327814750 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 10 Dec 2025 16:40:00 +0000 Subject: [PATCH] xiasri latest, and channel strip changes for 4D input --- MEMLNaut-NISPS.ino | 7 +- XIASRIAudioApp.hpp | 142 ++++++++++++++++++++++++----- XiasriAnalysis.cpp | 40 +++++--- modes/MEMLNautModeChannelStrip.hpp | 6 +- modes/MEMLNautModeXIASRI.hpp | 2 +- src/daisysp/Effects/pitchshifter.h | 6 +- voicespaces/ChannelStrip/basic.hpp | 49 +++++++--- 7 files changed, 195 insertions(+), 57 deletions(-) diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index 57bcdd0..3b7c537 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -13,7 +13,6 @@ #include "ChannelStripAudioApp.hpp" //interface -// #include "src/memllib/examples/InterfaceRL.hpp" #include "src/memllib/hardware/memlnaut/display/XYPadView.hpp" #include "src/memllib/hardware/memlnaut/display/MessageView.hpp" #include "src/memllib/hardware/memlnaut/display/VoiceSpaceSelectView.hpp" @@ -30,19 +29,15 @@ // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI // #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip +// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth MEMLNAUT_MODE_TYPE AUDIO_MEM MEMLNautModeHub; -// MEMLNAUT_MODE_TYPE AUDIO_MEM soundAnalysisMIDIMode; -// MEMLNautModeChannelStrip AUDIO_MEM channelStripMode; -// MEMLNautModePAFSynth AUDIO_MEM pafSynthMode; - MEMLNautMode auto* AUDIO_MEM currentMode = &MEMLNautModeHub; #define APP_SRAM __not_in_flash("app") -bool core1_disable_systick = true; bool core1_separate_stack = true; uint32_t get_rosc_entropy_seed(int bits) { diff --git a/XIASRIAudioApp.hpp b/XIASRIAudioApp.hpp index 010ad3a..78d037f 100644 --- a/XIASRIAudioApp.hpp +++ b/XIASRIAudioApp.hpp @@ -13,11 +13,13 @@ #include "voicespaces/VoiceSpaces.hpp" #include "src/memllib/synth/OnePoleSmoother.hpp" #include "src/memllib/synth/maximilian.h" +#include "src/daisysp/Effects/pitchshifter.h" -template //params just go out as MIDI for this one + +template class XIASRIAudioApp : public AudioAppBase { public: @@ -57,37 +59,113 @@ public: __attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override { float mix = x.L + x.R; + mix *= 2.0f; smoother.Process(neuralNetOutputs.data(), smoothParams.data()); - dl1mix = smoothParams[0] * smoothParams[0] * 0.4f; - dl2mix = smoothParams[1] * smoothParams[1] * 0.4f; - dl3mix = smoothParams[2] * smoothParams[2] * 0.8f; - allp1fb = smoothParams[4] * 0.99f; - allp2fb = smoothParams[5] * 0.99f; + // float dl1mix = smoothParams[0] * 0.6f; + // float dl2mix = smoothParams[1] * 0.6f; + // float dl3mix = smoothParams[2] * 0.8f; + // float ymix = smoothParams[12] * 1.0f; + + // float allp1fb = smoothParams[3] * 0.96f; + // float allp2fb = smoothParams[4] * 0.96f; + // float allp3fb = smoothParams[5] * 0.96f; + + // float comb1fb = (smoothParams[6] * 0.95f); + // float comb2fb = (smoothParams[7] * 0.95f); + // float comb3fb = (smoothParams[8] * 0.95f); + + // float dl1fb = (smoothParams[9] * 0.95f); + // float dl2fb = (smoothParams[10] * 0.95f); + // float dl3fb = (smoothParams[11] * 0.95f); + + // float y = dcb.play(mix, 0.99f) * 3.f; + + // float y1 = allp1.allpass(y, 73, allp1fb); + // y1 = comb1.combfb(y1, 255, comb1fb); + + // float y2 = allp2.allpass(y, 987, allp2fb); + // y2 = comb2.combfb(y2, 1616, comb2fb); + + // float y3 = comb3.combfb(y3, 847, comb3fb); + // y3 = allp3.allpass(y, 707, allp3fb); + + // y = y1 + y2 + y3; + + // float d1 = (dl1.play(y, 7000, dl1fb) * dl1mix); + // float d2 = (dl2.play(y, 18000, dl2fb) * dl2mix); + // float d3 = (dl3.play(y, 2399, dl3fb) * dl3mix); + + + // y = (y * ymix) + d1 + d2 + d3; + + // y = y * 1.9f; + + // y = tanhf(y); + + float dl1mix = smoothParams[0] * 0.4f; + float dl2mix = smoothParams[1] * 0.4f; + float dl3mix = smoothParams[2] * 0.8f; + float dl4mix = smoothParams[22] * smoothParams[22] * 0.41f; + + float allp1fb = smoothParams[4] * 0.99f; + float allp2fb = smoothParams[5] * 0.99f; float comb1fb = (smoothParams[6] * 0.95f); float comb2fb = (smoothParams[7] * 0.95f); float dl1fb = (smoothParams[8] * 0.95f); float dl2fb = (smoothParams[9] * 0.95f); float dl3fb = (smoothParams[10] * 0.95f); + float dl4fb = (smoothParams[23] * 0.95f); + // Wet-dry mix between 0.2 and 1 + wetdry_mix_ = (smoothParams[11] * 0.7f) + 0.3f; + // Pitch shift transposition between -12 and +12 semitones + // float pitchshift_transpose = (smoothParams[12] * 24.f) - 12.f; // Scale to -12 to +12 semitones + pitchshifter_.SetTransposition(12.f + smoothParams[12]); + //pitchshifter_.SetTransposition(-5.f); + // Set pitch shifter mix + pitchshifter_mix_ = smoothParams[13] * 0.99f; - float y = dcb.play(mix, 0.99f) * 3.f; + float allp3fb = smoothParams[14] * 0.99f; + float allp4fb = smoothParams[15] * 0.99f; + float allp5fb = smoothParams[16] * 0.99f; + float allp6fb = smoothParams[17] * 0.99f; + float allp3fbmix = smoothParams[18]; + float allp4fbmix = smoothParams[19]; + float allp5fbmix = smoothParams[20]; + float allp6fbmix = smoothParams[21]; + + // // PROCESS + float pitchshifted = pitchshifter_.Process(mix); + // // Mix the original signal with the pitch-shifted signal + pitchshifted = (mix * (1.f - pitchshifter_mix_)) + (pitchshifted * pitchshifter_mix_); + + float y = dcb.play(pitchshifted, 0.99f) * 2.f; + // float y = dcb.play(pitchshifted, 0.99f) * 3.f; float y1 = allp1.allpass(y, 30, allp1fb); y1 = comb1.combfb(y1, 127, comb1fb); - float y2 = allp2.allpass(y, 500, allp2fb); + float y2 = allp2.allpass(y, 482, allp2fb); y2 = comb2.combfb(y2, 808, comb2fb); - y = y1 + y2; + float y3 = allp3.allpass(y, 19, allp3fb) * allp3fbmix; + float y4 = allp4.allpass(y, 69, allp4fb) * allp4fbmix; + float y5 = allp5.allpass(y, 131, allp5fb) * allp5fbmix; + float y6 = allp6.allpass(y, 287, allp6fb) * allp6fbmix; + y = y1 + y2 + y3 + y4 +y5 +y6; float d1 = (dl1.play(y, 3500, dl1fb) * dl1mix); - float d2 = (dl2.play(y, 9000, dl2fb) * dl2mix); - float d3 = (dl3.play(y, 1199, dl3fb) * dl3mix); + float d2 = (dl2.play(y, 7886, dl2fb) * dl2mix); + float d3 = (dl3.play(y, 299, dl3fb) * dl3mix); + float d4 = (dl4.play(y, 15873, dl4fb) * dl4mix); y = y + d1 + d2 + d3; - y = tanhf(y); + // Mix dry + y = (y * wetdry_mix_) + (mix * (1.f - wetdry_mix_)); + + y = tanhf(y*1.2f); stereosample_t ret { y, y }; return ret; @@ -97,6 +175,7 @@ public: { AudioAppBase::Setup(sample_rate, interface); maxiSettings::sampleRate = sample_rate; + pitchshifter_.Init(sample_rate); } __attribute__((always_inline)) void ProcessParams(const std::array& params) @@ -110,23 +189,44 @@ protected: std::array neuralNetOutputs{0}, smoothParams{0}; + // maxiDelayline<10000> dl1; + // maxiDelayline<30100> dl2; + // maxiDelayline<2401> dl3; + + // maxiReverbFilters<300> allp1; + // maxiReverbFilters<1000> allp2; + // maxiReverbFilters<1000> allp3; + + // maxiReverbFilters<300> comb1; + // maxiReverbFilters<2000> comb2; + // maxiReverbFilters<1000> comb3; + + // maxiDCBlocker dcb; + maxiDelayline<5000> dl1; - maxiDelayline<15100> dl2; + maxiDelayline<8000> dl2; maxiDelayline<1201> dl3; + maxiDelayline<16000> dl4; maxiReverbFilters<300> allp1; - maxiReverbFilters<1000> allp2; - maxiReverbFilters<300> comb1; - maxiReverbFilters<1000> comb2; + maxiReverbFilters<500> allp2; + maxiReverbFilters<500> allp3; + maxiReverbFilters<500> allp4; + maxiReverbFilters<500> allp5; + maxiReverbFilters<500> allp6; + maxiReverbFilters<200> comb1; + maxiReverbFilters<900> comb2; + + + - float dl1mix = 0.0f; - float dl2mix = 0.0f; - float dl3mix = 0.0f; - float allp1fb=0.5f; - float allp2fb=0.5f; maxiDCBlocker dcb; + daisysp::PitchShifter pitchshifter_; + float pitchshifter_mix_{0.5f}; + float wetdry_mix_{0.5f}; + OnePoleSmoother smoother{150.f, kSampleRate}; }; diff --git a/XiasriAnalysis.cpp b/XiasriAnalysis.cpp index 099c06a..f8c86fe 100644 --- a/XiasriAnalysis.cpp +++ b/XiasriAnalysis.cpp @@ -6,6 +6,8 @@ #include "src/memllib/utils/Maths.hpp" #include "src/memllib/PicoDefs.hpp" +#define COMMONHPFFREQ 20.f +#define ZXFREQ 4000.f XiasriAnalysis::XiasriAnalysis(const float sample_rate) : sample_rate_(sample_rate), @@ -13,9 +15,9 @@ XiasriAnalysis::XiasriAnalysis(const float sample_rate) : zc_median_filter_(kZC_MedianFilterSize) { // Initialize filters and detectors - common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, 20.f, 0.707f, 0); + common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, COMMONHPFFREQ, 0.707f, 0); // Zero crossing - zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, 4000.0f, 0.707f, 0); + zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, ZXFREQ, 0.707f, 0); elapsed_samples_ = 0; // Envelope follower ef_follower_.setAttack(10.0f); @@ -33,20 +35,28 @@ XiasriAnalysis::XiasriAnalysis(const float sample_rate) : void XiasriAnalysis::ReinitFilters() { // Reinitialize all filters with correct maxiSettings sample rate - common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, 20.f, 0.707f, 0); - zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, 4000.0f, 0.707f, 0); + common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, COMMONHPFFREQ, 0.707f, 0); + zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, ZXFREQ, 0.707f, 0); br_lpf1_.set(maxiBiquad::filterTypes::LOWPASS, 1000.0f, 0.707f, 0); br_hpf2_.set(maxiBiquad::filterTypes::HIGHPASS, 1000.0f, 0.707f, 0); br_lpf2_.set(maxiBiquad::filterTypes::LOWPASS, 4000.0f, 0.707f, 0); + ef_follower_.setAttack(10.0f); + ef_follower_.setRelease(100.0f); + for (size_t i = 0; i < kBR_NBands; ++i) { + br_follower_[i].setAttack(10.f); + br_follower_[i].setRelease(100.f); + } + } // Use fast approximation (~10 cycles): -__force_inline float fast_log2(float x) { - static constexpr float INV_MANTISSA_SCALE = 1.0f / 8388608.0f; // 1/(2^23) - union { float f; uint32_t i; } u = {x}; - return (float)(((u.i >> 23) & 0xFF) - 127) + - (float)(u.i & 0x7FFFFF) * INV_MANTISSA_SCALE; // Multiply instead of divide -} +// __force_inline float fast_log2(float x) { +// static constexpr float INV_MANTISSA_SCALE = 1.0f / 8388608.0f; +// uint32_t bits; +// memcpy(&bits, &x, sizeof(float)); // Standards-compliant type punning +// return (float)(((bits >> 23) & 0xFF) - 127) + +// (float)(bits & 0x7FFFFF) * INV_MANTISSA_SCALE; +// } inline float logEnvelopeFast(float linearEnv) { // -60 dBFS corresponds to a linear amplitude ratio of 10^(-60/20) = 10^(-3) = 0.001 @@ -69,11 +79,17 @@ inline float logEnvelopeFast(float linearEnv) { // Clamp input to minimum envelope value linearEnv = (linearEnv > MIN_ENV) ? linearEnv : MIN_ENV; - float log2_val = fast_log2(linearEnv); + float log2_val = log2f(linearEnv); // Map to [0,1]: (log2_val - log2_min) / (log2_max - log2_min) float y = (log2_val - LOG2_MIN_ENV) * INV_LOG_RANGE; + // PERIODIC_RUN( + // Serial.printf("%f %f %f\n", linearEnv, log2_val, y); + // ,100 + // ); + + // Clamp to [0,1] range (should be unnecessary given our math, but safety first) y = (y > 1.0f) ? 1.0f : y; y = (y < 0.0f) ? 0.0f : y; @@ -149,7 +165,7 @@ XiasriAnalysis::parameters_t AUDIO_FUNC(XiasriAnalysis::Process)(const float x) params.energy = ef_y; params.attack = ef_d_dy; params.brightness = br_high; - params.energy_crude = fabsf(x); // Use fabsf instead of std::abs + params.energy_crude = fabsf(x); return params; } \ No newline at end of file diff --git a/modes/MEMLNautModeChannelStrip.hpp b/modes/MEMLNautModeChannelStrip.hpp index 345e311..2b1ddd2 100644 --- a/modes/MEMLNautModeChannelStrip.hpp +++ b/modes/MEMLNautModeChannelStrip.hpp @@ -10,7 +10,7 @@ class MEMLNautModeChannelStrip { public: - constexpr static size_t kN_InputParams = 3; // joystick x, y, rotate + constexpr static size_t kN_InputParams = 4; // joystick x, y, left and right joysticks ChannelStripAudioApp<> audioAppChannelStrip; std::array::nVoiceSpaces> voiceSpaceList; InterfaceRL interface; @@ -19,9 +19,9 @@ public: void setupInterface() { interface.setup(kN_InputParams, ChannelStripAudioApp<>::kN_Params); - interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK); + interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); //set 4D joystick interfacePtr = make_non_owning(interface); -} + } String getHelpTitle() { return "Channel Strip Mode"; diff --git a/modes/MEMLNautModeXIASRI.hpp b/modes/MEMLNautModeXIASRI.hpp index 1310a7b..403dda7 100644 --- a/modes/MEMLNautModeXIASRI.hpp +++ b/modes/MEMLNautModeXIASRI.hpp @@ -42,7 +42,7 @@ public: midi_interf = new_midi_interf; midi_interf->Setup(0); midi_interf->SetMIDISendChannel(1); - interface.bindMIDI(midi_interf); + interface.bindMIDI(midi_interf, true); } void addViews() { diff --git a/src/daisysp/Effects/pitchshifter.h b/src/daisysp/Effects/pitchshifter.h index 8b4bc52..d07daee 100644 --- a/src/daisysp/Effects/pitchshifter.h +++ b/src/daisysp/Effects/pitchshifter.h @@ -21,9 +21,9 @@ https://opensource.org/licenses/MIT. /** Shift can be 30-100 ms lets just start with 50 for now. 0.050 * SR = 2400 samples (at 48kHz) */ -#define SHIFT_BUFFER_SIZE 16384 -//#define SHIFT_BUFFER_SIZE 4800 -//#define SHIFT_BUFFER_SIZE 8192 +// #define SHIFT_BUFFER_SIZE 16384 +// #define SHIFT_BUFFER_SIZE 4800 +#define SHIFT_BUFFER_SIZE 8192 //#define SHIFT_BUFFER_SIZE 1024 namespace daisysp diff --git a/voicespaces/ChannelStrip/basic.hpp b/voicespaces/ChannelStrip/basic.hpp index 48131fb..27ce114 100644 --- a/voicespaces/ChannelStrip/basic.hpp +++ b/voicespaces/ChannelStrip/basic.hpp @@ -2,7 +2,7 @@ #define __VOICE_SPACE_CHSTRIP_BASIC_HPP__ #define VOICE_SPACE_CHSTRIP_NEVE66_BODY \ - preGain=0.5f + (params[0] * 4.f); \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ \ inLowPassCutoff = 2000.f + (params[7] * params[7] * 18000.f); \ inHighPassCutoff = 30.f + (params[8] * params[8] * 270.f); \ @@ -27,10 +27,10 @@ compAttack = 0.002f + (params[12] * 10.f); \ compRelease = 30.0f + (params[13] * params[13] * 2970.f); \ \ - postGain=0.5f + (params[23] * 4.f); \ + postGain=0.5f + (params[23] * params[23] * 4.f); \ #define VOICE_SPACE_CHSTRIP_SSL4KGIST_BODY \ - preGain=0.5f + (params[0] * 4.f); \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ inLowPassCutoff = 3000.f + (params[7] * params[7] * 18000.f); \ inHighPassCutoff = 10.f + (params[8] * params[8] * 340.f); \ \ @@ -55,10 +55,10 @@ compAttack = 0.08f + (params[12] * 3.f); \ compRelease = 100.0f + (params[13] * params [13] * 3900.f); \ \ - postGain=0.5f + (params[23] * 4.f); \ + postGain=0.5f + (params[23] * params[23] * 4.f); \ #define VOICE_SPACE_CHSTRIP_SSL9KINDA_BODY \ - preGain=0.5f + (params[0] * 4.f); \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ inLowPassCutoff = 3000.f + (params[7] * params[7] * 18000.f); \ inHighPassCutoff = 10.f + (params[8] * params[8] * 490.f); \ \ @@ -83,17 +83,16 @@ compAttack = 0.08f + (params[12] * 3.f); \ compRelease = 100.0f + (params[13] * params [13] * 3900.f); \ \ - postGain=0.5f + (params[23] * 4.f); \ + postGain=0.5f + (params[23] * params[23] * 4.f); \ #define VOICE_SPACE_CHSTRIP_MALE_VOX_BODY \ - preGain=0.5f + (params[0] * 4.f); \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ inLowPassCutoff = 1000.f + (params[7] * params[7] * 19000.f); \ inHighPassCutoff = 10.f + (params[8] * params[8] * 1990.f); \ compThreshold = params[10] * -30.f; \ compRatio = 2.0f + (params[11] * 6.f); \ compAttack = 0.08f + (params[12] * 50.f); \ compRelease = 50.0f + (params[13] * 500.f); \ - postGain=0.5f + (params[23] * 4.f); \ \ lowShelfFreq = 60.f + (params[14] * params[14] * 240.f); \ lowShelfQ = 0.6f + (params[15]*4.4f); \ @@ -110,18 +109,18 @@ highShelfFreq = 1000.f + (params[17] * params[17] * 7000.f); \ highShelfQ = 0.6f + (params[18]*4.4f); \ highShelfGain = -18.f + (params[19]*36.f);\ + postGain=0.5f + (params[23] * params[23] * 4.f); \ + - #define VOICE_SPACE_CHSTRIP_FEMALE_VOX_BODY \ - preGain=0.5f + (params[0] * 4.f); \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ inLowPassCutoff = 1000.f + (params[7] * params[7] * 19000.f); \ inHighPassCutoff = 10.f + (params[8] * params[8] * 1990.f); \ compThreshold = params[10] * -30.f; \ compRatio = 2.0f + (params[11] * 6.f); \ compAttack = 0.08f + (params[12] * 50.f); \ compRelease = 50.0f + (params[13] * 500.f); \ - postGain=0.5f + (params[23] * 4.f); \ \ lowShelfFreq = 120.f + (params[14] * params[14] * 180.f); \ lowShelfQ = 0.6f + (params[15]*4.4f); \ @@ -138,5 +137,33 @@ highShelfFreq = 1000.f + (params[17] * params[17] * 9000.f); \ highShelfQ = 0.6f + (params[18]*4.4f); \ highShelfGain = -18.f + (params[19]*36.f);\ + postGain=0.5f + (params[23] * params[23] * 4.f); \ + + +#define VOICE_SPACE_CHSTRIP_EMI \ + preGain=0.5f + (params[0] * params[0] * 4.f); \ + inLowPassCutoff = 1000.f + (params[7] * params[7] * 19000.f); \ + inHighPassCutoff = 10.f + (params[8] * params[8] * 1990.f); \ + compThreshold = params[10] * -30.f; \ + compRatio = 2.0f + (params[11] * 6.f); \ + compAttack = 0.08f + (params[12] * 50.f); \ + compRelease = 50.0f + (params[13] * 500.f); \ + \ + lowShelfFreq = 120.f + (params[14] * params[14] * 180.f); \ + lowShelfQ = 0.6f + (params[15]*4.4f); \ + lowShelfGain = -18.f + (params[16]*36.f);\ + \ + peak0Freq = 120.f + (params[1] * params[1] * 380.f); \ + peak0Q = 0.6f + (params[5]*4.4f); \ + peak0Gain = -18.f + (params[6]*36.f);\ + \ + peak1Freq = 300.f + (params[4] * params[4] * 9700.f); \ + peak1Q = 0.6f + (params[5]*4.4f); \ + peak1Gain = -18.f + (params[6]*36.f);\ + \ + highShelfFreq = 1000.f + (params[17] * params[17] * 9000.f); \ + highShelfQ = 0.6f + (params[18]*4.4f); \ + highShelfGain = -18.f + (params[19]*36.f);\ + postGain=0.5f + (params[23] * params[23] * 4.f); \ #endif