From b8a7a6a9a18f44e8cd2974e1c2fb530051f83025 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Tue, 13 Jan 2026 17:20:19 +0000 Subject: [PATCH 01/15] memory storage modes --- src/memllib | 2 +- src/memlp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memllib b/src/memllib index 799130e..42a937b 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 799130e740a392839587f24c3747b27e841957b1 +Subproject commit 42a937b24d79ec962acf03b55090b022d47176df diff --git a/src/memlp b/src/memlp index 83f5a84..bf52e8e 160000 --- a/src/memlp +++ b/src/memlp @@ -1 +1 @@ -Subproject commit 83f5a84089d4a61604c6c429738c3f68f67bd103 +Subproject commit bf52e8e279343312348a7056b538861cc81ef462 From 706a969cdcd2030c82f53d09f0fe178c8a083704 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sat, 7 Feb 2026 10:36:26 +0000 Subject: [PATCH 02/15] memory storeage modes --- MEMLNaut-NISPS.ino | 9 +++++---- modes/MEMLNautModePAFSynth.hpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index 3b7c537..ffe01f8 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -27,8 +27,8 @@ //hook up the memlnaut mode // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI -// #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI -#define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip +#define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI +// #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth MEMLNAUT_MODE_TYPE AUDIO_MEM MEMLNautModeHub; @@ -86,6 +86,9 @@ void setup() { uint32_t seed = get_rosc_entropy_seed(32); srand(seed); + midi_interf = std::make_shared(); + // Serial.println("MIDI setup complete."); + Serial.begin(115200); // while (!Serial) {} Serial.println("Serial initialised."); @@ -101,8 +104,6 @@ void setup() { WRITE_VOLATILE(interface_ready, true); Serial.println("Bound interface to MEMLNaut."); - midi_interf = std::make_shared(); - Serial.println("MIDI setup complete."); if (midi_interf) { currentMode->setupMIDI(midi_interf); } diff --git a/modes/MEMLNautModePAFSynth.hpp b/modes/MEMLNautModePAFSynth.hpp index c0f6606..5235538 100644 --- a/modes/MEMLNautModePAFSynth.hpp +++ b/modes/MEMLNautModePAFSynth.hpp @@ -12,7 +12,7 @@ class MEMLNautModePAFSynth { public: - constexpr static size_t kN_InputParams = 3; // joystick x, y, rotate + constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate inline static PAFSynthAudioApp<> audioAppPAFSynth; std::array::nVoiceSpaces> voiceSpaceList; @@ -22,7 +22,7 @@ public: void setupInterface() { interface.setup(kN_InputParams, PAFSynthAudioApp<>::kN_Params); - interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK); + interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); interfacePtr = make_non_owning(interface); } From dd78891783bd42727a3612dd66c93ea7f8d38594 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sun, 22 Feb 2026 11:39:27 +0000 Subject: [PATCH 03/15] start of break|| --- MEMLNaut-NISPS.ino | 4 +- modes/AudioApps/BreakOrAudioApp.hpp | 97 ++++++++++++++++++++++++ modes/MEMLNautModeBreakOr.hpp | 111 ++++++++++++++++++++++++++++ modes/MEMLNautModeChannelStrip.hpp | 11 --- modes/MEMLNautModePAFSynth.hpp | 11 --- src/memllib | 2 +- src/memlp | 2 +- 7 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 modes/AudioApps/BreakOrAudioApp.hpp create mode 100644 modes/MEMLNautModeBreakOr.hpp diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index ffe01f8..d6460cd 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -23,11 +23,13 @@ #include "modes/MEMLNautModeChannelStrip.hpp" #include "modes/MEMLNautModeSoundAnalysisMIDI.hpp" #include "modes/MEMLNautModeXIASRI.hpp" +#include "modes/MEMLNautModeBreakOr.hpp" //hook up the memlnaut mode // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI -#define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI +// #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI +#define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr // #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp new file mode 100644 index 0000000..726158c --- /dev/null +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -0,0 +1,97 @@ +#ifndef __BREAKOR_AUDIO_APP_HPP__ +#define __BREAKOR_AUDIO_APP_HPP__ + +#include "../../src/memllib/audio/AudioAppBase.hpp" +#include "../../src/memllib/synth/maximilian.h" + +#include +#include +#include // Added for std::shared_ptr + +#include "../../src/memllib/interface/InterfaceBase.hpp" // Added missing include + +#include + +#include "../../voicespaces/VoiceSpaces.hpp" + + + +template + +class BreakOrAudioApp : public AudioAppBase +{ +public: + static constexpr size_t kN_Params = NPARAMS; + static constexpr size_t nVoiceSpaces=0; + + std::array, nVoiceSpaces> voiceSpaces; + + VoiceSpaceFn currentVoiceSpace; + + std::array getVoiceSpaceNames() { + std::array names; + for(size_t i=0; i < voiceSpaces.size(); i++) { + names[i] = voiceSpaces[i].name; + } + return names; + } + + void setVoiceSpace(size_t i) { + if (i < voiceSpaces.size()) { + currentVoiceSpace = voiceSpaces[i].mappingFunction; + } + } + + BreakOrAudioApp() : AudioAppBase() { + // currentVoiceSpace = voiceSpaces[0].mappingFunction; + + }; + + bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) + { + // Euclidean function + const float fi = phase * n; + int i = static_cast(fi); + const float rem = fi - i; + if (i == n) + { + i--; + } + const int idx = ((i + n - offset) * k) % n; + return (idx < k && rem < pulseWidth) ? 1 : 0; + } + + stereosample_t __force_inline Process(const stereosample_t x) override + { + stereosample_t ret { 0.f,0.f }; + return ret; + } + + void Setup(float sample_rate, std::shared_ptr interface) override + { + AudioAppBase::Setup(sample_rate, interface); + maxiSettings::sampleRate = sample_rate; + sampleRatef = static_cast(sample_rate); + } + + + void loop() override { + AudioAppBase::loop(); + } + + void ProcessParams(const std::array& params) + { + firstParamsReceived = true; + // currentVoiceSpace(params); + } + + + +protected: + + float sampleRatef; + bool firstParamsReceived = false; +}; + +#endif // __BREAKOR_AUDIO_APP_HPP__ + diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp new file mode 100644 index 0000000..f79c701 --- /dev/null +++ b/modes/MEMLNautModeBreakOr.hpp @@ -0,0 +1,111 @@ +#ifndef MEMLNAUT_MODE_RHYTHM_BOX_HPP +#define MEMLNAUT_MODE_RHYTHM_BOX_HPP + +//Break|| + +#include "../src/memllib/interface/MIDIInOut.hpp" +#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp" +#include "AudioApps/BreakOrAudioApp.hpp" +#include "../src/memllib/examples/InterfaceRL.hpp" +#include "../src/memllib/PicoDefs.hpp" +#include "MEMLNautMode.hpp" +#include +#include + +class MEMLNautModeBreakOr { +public: + constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate + + inline static BreakOrAudioApp<> audioAppBreakOr; + std::array::nVoiceSpaces> voiceSpaceList; + + InterfaceRL interface; + std::shared_ptr interfacePtr; + + void setupInterface() { + interface.setup(kN_InputParams, BreakOrAudioApp<>::kN_Params); + interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); + interfacePtr = make_non_owning(interface); + } + + String getHelpTitle() { + return "Break||"; + } + + __force_inline stereosample_t process(stereosample_t x) { + return audioAppBreakOr.Process(x); + } + + void setupAudio(float sample_rate) { + audioAppBreakOr.Setup(sample_rate, interfacePtr); + voiceSpaceList = audioAppBreakOr.getVoiceSpaceNames(); + } + + __force_inline void loop() { + audioAppBreakOr.loop(); + } + + std::shared_ptr midi_interf; + + void setupMIDI(std::shared_ptr new_midi_interf) { + midi_interf = new_midi_interf; + midi_interf->Setup(16); + midi_interf->SetMIDISendChannel(1); + interface.bindMIDI(midi_interf); + } + + void addViews() { + // std::shared_ptr voiceSpaceSelectView; + // voiceSpaceSelectView = std::make_shared("Voice Spaces"); + + // MEMLNaut::Instance()->disp->InsertViewAfter(interface.rlStatsView, voiceSpaceSelectView); + // voiceSpaceSelectView->setOptions(voiceSpaceList); //set by core 1 on startup + // voiceSpaceSelectView->setNewVoiceCallback( + // [this](size_t idx) { + // audioAppPAFSynth.setVoiceSpace(idx); + // }); + + + }; + + inline void processAnalysisParams() {} + + void analyse(stereosample_t) {} + +}; + + +/* + // BODY + Value result = Value::nil(); + + auto ratios = args[0].as_sequential(); + const auto pulseWidth = args[1].as_float(); + const auto phase = args[2].as_float(); + + double trig = 0; + double ratioSum = 0; + for (Value v : ratios) + { + ratioSum += v.as_float(); + } + double phaseAdj = ratioSum * phase; + double accumulatedSum = 0; + double lastAccumulatedSum = 0; + for (Value v : ratios) + { + accumulatedSum += v.as_float(); + if (phaseAdj <= accumulatedSum) + { + // check pulse width + double beatPhase = (phaseAdj - lastAccumulatedSum) / + (accumulatedSum - lastAccumulatedSum); + trig = beatPhase <= pulseWidth; + break; + } + lastAccumulatedSum = accumulatedSum; + } + result = Value(trig); +*/ + +#endif // MEMLNAUT_MODE_RHYTHM_BOX_HPP \ No newline at end of file diff --git a/modes/MEMLNautModeChannelStrip.hpp b/modes/MEMLNautModeChannelStrip.hpp index da9caeb..e9eefe4 100644 --- a/modes/MEMLNautModeChannelStrip.hpp +++ b/modes/MEMLNautModeChannelStrip.hpp @@ -26,17 +26,6 @@ public: String getHelpTitle() { return "Channel Strip Mode"; } - // size_t getNParams() { - // return ChannelStripAudioApp<>::kN_Params; - // } - - // void setVoiceSpace(size_t i) { - // audioAppChannelStrip.setVoiceSpace(i); - // } - - // std::span getVoiceSpaceList() { - // return voiceSpaceList; - // } __force_inline stereosample_t process(stereosample_t x) { return audioAppChannelStrip.Process(x); diff --git a/modes/MEMLNautModePAFSynth.hpp b/modes/MEMLNautModePAFSynth.hpp index 5235538..9249943 100644 --- a/modes/MEMLNautModePAFSynth.hpp +++ b/modes/MEMLNautModePAFSynth.hpp @@ -29,17 +29,6 @@ public: String getHelpTitle() { return "PAF Synth Mode"; } - // size_t getNParams() { - // return PAFSynthAudioApp<>::kN_Params; - // } - - // void setVoiceSpace(size_t i) { - // audioAppPAFSynth.setVoiceSpace(i); - // } - - // std::span getVoiceSpaceList() { - // return voiceSpaceList; - // } __force_inline stereosample_t process(stereosample_t x) { return audioAppPAFSynth.Process(x); diff --git a/src/memllib b/src/memllib index 42a937b..5bae63d 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 42a937b24d79ec962acf03b55090b022d47176df +Subproject commit 5bae63daa89332f333ace163818b23308cdf3518 diff --git a/src/memlp b/src/memlp index bf52e8e..5d2935d 160000 --- a/src/memlp +++ b/src/memlp @@ -1 +1 @@ -Subproject commit bf52e8e279343312348a7056b538861cc81ef462 +Subproject commit 5d2935d6420f5e6ab0ad8321bab5125cce2fb500 From 9498f7727826a16bc2e1686a7e6b1ff578ab448d Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sun, 22 Feb 2026 12:41:09 +0000 Subject: [PATCH 04/15] midi routing from audio app --- MEMLNaut-NISPS.ino | 6 +++--- modes/AudioApps/BreakOrAudioApp.hpp | 18 ++++++++++++++++-- modes/MEMLNautModeBreakOr.hpp | 6 ++++-- src/memllib | 2 +- src/memlp | 2 +- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index d6460cd..93105d7 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -106,9 +106,6 @@ void setup() { WRITE_VOLATILE(interface_ready, true); Serial.println("Bound interface to MEMLNaut."); - if (midi_interf) { - currentMode->setupMIDI(midi_interf); - } WRITE_VOLATILE(core_0_ready, true); while (!READ_VOLATILE(core_1_ready)) { @@ -203,6 +200,9 @@ void setup1() { delay(1); } + if (midi_interf) { + currentMode->setupMIDI(midi_interf); + } currentMode->setupAudio(AudioDriver::GetSampleRate()); diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 726158c..05ad5a0 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -3,12 +3,14 @@ #include "../../src/memllib/audio/AudioAppBase.hpp" #include "../../src/memllib/synth/maximilian.h" +#include "../../src/memllib/interface/MIDIInOut.hpp" + #include #include #include // Added for std::shared_ptr -#include "../../src/memllib/interface/InterfaceBase.hpp" // Added missing include +#include "../../src/memllib/interface/InterfaceBase.hpp" #include @@ -24,6 +26,8 @@ public: static constexpr size_t kN_Params = NPARAMS; static constexpr size_t nVoiceSpaces=0; + std::shared_ptr midiIO; + std::array, nVoiceSpaces> voiceSpaces; VoiceSpaceFn currentVoiceSpace; @@ -63,6 +67,12 @@ public: stereosample_t __force_inline Process(const stereosample_t x) override { + phasor0 += 1.f; + if (phasor0>=48000.f) { + phasor0-=48000.f; + midiIO->queueNoteOn(36, 127); + midiIO->flushQueue(); + } stereosample_t ret { 0.f,0.f }; return ret; } @@ -73,7 +83,9 @@ public: maxiSettings::sampleRate = sample_rate; sampleRatef = static_cast(sample_rate); } - + void setupMIDI(std::shared_ptr new_midi_interf) { + midiIO = new_midi_interf; + } void loop() override { AudioAppBase::loop(); @@ -91,6 +103,8 @@ protected: float sampleRatef; bool firstParamsReceived = false; + + float phasor0=0.f; }; #endif // __BREAKOR_AUDIO_APP_HPP__ diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp index f79c701..a2bc557 100644 --- a/modes/MEMLNautModeBreakOr.hpp +++ b/modes/MEMLNautModeBreakOr.hpp @@ -5,7 +5,7 @@ #include "../src/memllib/interface/MIDIInOut.hpp" #include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp" -#include "AudioApps/BreakOrAudioApp.hpp" +#include "./AudioApps/BreakOrAudioApp.hpp" #include "../src/memllib/examples/InterfaceRL.hpp" #include "../src/memllib/PicoDefs.hpp" #include "MEMLNautMode.hpp" @@ -39,6 +39,7 @@ public: void setupAudio(float sample_rate) { audioAppBreakOr.Setup(sample_rate, interfacePtr); voiceSpaceList = audioAppBreakOr.getVoiceSpaceNames(); + audioAppBreakOr.setupMIDI(midi_interf); } __force_inline void loop() { @@ -49,8 +50,9 @@ public: void setupMIDI(std::shared_ptr new_midi_interf) { midi_interf = new_midi_interf; - midi_interf->Setup(16); + midi_interf->Setup(0); midi_interf->SetMIDISendChannel(1); + midi_interf->SetMIDINoteChannel(10); interface.bindMIDI(midi_interf); } diff --git a/src/memllib b/src/memllib index 5bae63d..1693e46 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 5bae63daa89332f333ace163818b23308cdf3518 +Subproject commit 1693e46e0b38c5920ebdf6cdf2a9107503003c8d diff --git a/src/memlp b/src/memlp index 5d2935d..2eaef77 160000 --- a/src/memlp +++ b/src/memlp @@ -1 +1 @@ -Subproject commit 5d2935d6420f5e6ab0ad8321bab5125cce2fb500 +Subproject commit 2eaef772c985e221e13c62676eaa293cf75d87e2 From 497f1c8dfb324ece5924bfe422ff40abd3fc40c4 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sun, 22 Feb 2026 21:03:50 +0000 Subject: [PATCH 05/15] breakcore!!! --- modes/AudioApps/BreakOrAudioApp.hpp | 108 +++++++++++++++++++++++++--- modes/MEMLNautModeBreakOr.hpp | 32 --------- 2 files changed, 100 insertions(+), 40 deletions(-) diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 05ad5a0..29cbfea 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -16,10 +16,42 @@ #include "../../voicespaces/VoiceSpaces.hpp" +struct ratioSeqState { + std::array ratios{1.f}; + float phasor=0.f; + float phasorInc=0.f; + bool lastTrig = false; + float phasorMul = 1.f; + float ratioSum=1.f; + int midiNote = 36; + float pulseWidth = 0.5f; +}; + +template +inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { + bool trig = 0; + float phaseAdj = seq.ratioSum * seq.phasor; + float accumulatedSum = 0; + float lastAccumulatedSum = 0; + for (size_t v : seq.ratios) + { + accumulatedSum += v; + if (phaseAdj <= accumulatedSum) + { + // check pulse width + //TODO: could precalc acc-lastacc + float beatPhase = (phaseAdj - lastAccumulatedSum) / + (accumulatedSum - lastAccumulatedSum); + trig = beatPhase <= seq.pulseWidth; + break; + } + lastAccumulatedSum = accumulatedSum; + } + return trig; +} -template - +template class BreakOrAudioApp : public AudioAppBase { public: @@ -48,7 +80,6 @@ public: BreakOrAudioApp() : AudioAppBase() { // currentVoiceSpace = voiceSpaces[0].mappingFunction; - }; bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) @@ -67,12 +98,31 @@ public: stereosample_t __force_inline Process(const stereosample_t x) override { - phasor0 += 1.f; - if (phasor0>=48000.f) { - phasor0-=48000.f; - midiIO->queueNoteOn(36, 127); + if (sequencingSampleCounter==0) { + for(auto &seq: ratioSeqStates) { + //update phasor + seq.phasor += (seq.phasorInc * seq.phasorMul); + if (seq.phasor >= 1.f) { + seq.phasor -= 1.f; + } + bool trig = ratioSeq<3>(seq); + if (trig && !seq.lastTrig) { + if (trig) { + midiIO->queueNoteOn(seq.midiNote, 127); + }else{ + midiIO->queueNoteOff(seq.midiNote, 0); + } + } + seq.lastTrig = trig; + } midiIO->flushQueue(); } + + sequencingSampleCounter ++; + if (sequencingSampleCounter >= sequencingSampleDiv) { + sequencingSampleCounter = 0; + } + stereosample_t ret { 0.f,0.f }; return ret; } @@ -82,7 +132,13 @@ public: AudioAppBase::Setup(sample_rate, interface); maxiSettings::sampleRate = sample_rate; sampleRatef = static_cast(sample_rate); + updateBPM(90.f); + size_t midiNote = 36; + for(auto &seq: ratioSeqStates) { + seq.midiNote = midiNote++; + } } + void setupMIDI(std::shared_ptr new_midi_interf) { midiIO = new_midi_interf; } @@ -95,6 +151,34 @@ public: { firstParamsReceived = true; // currentVoiceSpace(params); + size_t paramIdx = 0; + for(auto &v: ratioSeqStates) { + float sum=0.f; + for(size_t i=0; i < v.ratios.size(); i++) { + v.ratios[i] = (float)(int)(params[paramIdx++] * 7.f) + 1.f; + sum += v.ratios[i]; + } + v.ratioSum = sum; + v.phasorMul = ((float)(int)(params[paramIdx++] * 2.f) * 0.5f)+ 1.f; + } + + } + + void updateBPM(float newBPM) { + bpm = newBPM; + float beatLengthInSeconds = 60.f / bpm; + float barLengthInSeconds = beatLengthInSeconds * timeSigBeats; + float barLengthInSamples = barLengthInSeconds * (sampleRatef/ sequencingSampleDiv); + float barPhasorInc = 1.f/ barLengthInSamples; + for(auto &v: ratioSeqStates) { + v.phasorInc = barPhasorInc; + } + } + + void setTimeSignature(float beats, float division) { + timeSigBeats = beats; + timeSigDivision = division; + updateBPM(bpm); // Recalculate phasor increment with new time signature } @@ -104,7 +188,15 @@ protected: float sampleRatef; bool firstParamsReceived = false; - float phasor0=0.f; + float bpm=90.f; + float timeSigBeats=4.f; + float timeSigDivision=4.f; + + size_t sequencingSampleDiv = 50; // How often the sequencer should update (in Hz) + size_t sequencingSampleCounter = 0; + + std::array ratioSeqStates; + }; #endif // __BREAKOR_AUDIO_APP_HPP__ diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp index a2bc557..856814f 100644 --- a/modes/MEMLNautModeBreakOr.hpp +++ b/modes/MEMLNautModeBreakOr.hpp @@ -77,37 +77,5 @@ public: }; -/* - // BODY - Value result = Value::nil(); - - auto ratios = args[0].as_sequential(); - const auto pulseWidth = args[1].as_float(); - const auto phase = args[2].as_float(); - - double trig = 0; - double ratioSum = 0; - for (Value v : ratios) - { - ratioSum += v.as_float(); - } - double phaseAdj = ratioSum * phase; - double accumulatedSum = 0; - double lastAccumulatedSum = 0; - for (Value v : ratios) - { - accumulatedSum += v.as_float(); - if (phaseAdj <= accumulatedSum) - { - // check pulse width - double beatPhase = (phaseAdj - lastAccumulatedSum) / - (accumulatedSum - lastAccumulatedSum); - trig = beatPhase <= pulseWidth; - break; - } - lastAccumulatedSum = accumulatedSum; - } - result = Value(trig); -*/ #endif // MEMLNAUT_MODE_RHYTHM_BOX_HPP \ No newline at end of file From 6707781b3b64961c8517c627f0262372e6a54b26 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 25 Feb 2026 16:30:52 +0000 Subject: [PATCH 06/15] phase offset --- modes/AudioApps/BreakOrAudioApp.hpp | 17 ++++++++++++----- src/memlp | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 29cbfea..c4bab8a 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -20,6 +20,7 @@ struct ratioSeqState { std::array ratios{1.f}; float phasor=0.f; float phasorInc=0.f; + float phaseOffset = 0.f; bool lastTrig = false; float phasorMul = 1.f; float ratioSum=1.f; @@ -30,7 +31,11 @@ struct ratioSeqState { template inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { bool trig = 0; - float phaseAdj = seq.ratioSum * seq.phasor; + float offsetPhase = seq.phaseOffset + seq.phasor; + if (offsetPhase >= 1.f) { + offsetPhase -= 1.f; + } + float phaseAdj = seq.ratioSum * offsetPhase; float accumulatedSum = 0; float lastAccumulatedSum = 0; for (size_t v : seq.ratios) @@ -39,7 +44,6 @@ inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { if (phaseAdj <= accumulatedSum) { // check pulse width - //TODO: could precalc acc-lastacc float beatPhase = (phaseAdj - lastAccumulatedSum) / (accumulatedSum - lastAccumulatedSum); trig = beatPhase <= seq.pulseWidth; @@ -133,9 +137,10 @@ public: maxiSettings::sampleRate = sample_rate; sampleRatef = static_cast(sample_rate); updateBPM(90.f); - size_t midiNote = 36; + const size_t midiNotes[NSEQUENCES] = {36,37,38,39,40, 42,43,45,47,48}; + size_t midiNote = 0; for(auto &seq: ratioSeqStates) { - seq.midiNote = midiNote++; + seq.midiNote = midiNotes[midiNote++]; } } @@ -160,6 +165,7 @@ public: } v.ratioSum = sum; v.phasorMul = ((float)(int)(params[paramIdx++] * 2.f) * 0.5f)+ 1.f; + v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; } } @@ -190,9 +196,10 @@ protected: float bpm=90.f; float timeSigBeats=4.f; + float timeSigBeatsInv=1.f/timeSigBeats; float timeSigDivision=4.f; - size_t sequencingSampleDiv = 50; // How often the sequencer should update (in Hz) + size_t sequencingSampleDiv = 50; size_t sequencingSampleCounter = 0; std::array ratioSeqStates; diff --git a/src/memlp b/src/memlp index 2eaef77..abbfdf9 160000 --- a/src/memlp +++ b/src/memlp @@ -1 +1 @@ -Subproject commit 2eaef772c985e221e13c62676eaa293cf75d87e2 +Subproject commit abbfdf90b47f90e7e02221f1b6df6c88f0f70ce5 From 16a38fbb66e4d008f1ca8387b44e77918b002d77 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 25 Feb 2026 16:57:02 +0000 Subject: [PATCH 07/15] refactor storage, no longer need node.hs --- src/memlp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memlp b/src/memlp index abbfdf9..dd1e1f3 160000 --- a/src/memlp +++ b/src/memlp @@ -1 +1 @@ -Subproject commit abbfdf90b47f90e7e02221f1b6df6c88f0f70ce5 +Subproject commit dd1e1f36eac66f95f14daad48ad9def2e4c5f3ec From 1656e7a4a423d3b4a64d053139bfebacc8fc6258 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 25 Feb 2026 17:33:19 +0000 Subject: [PATCH 08/15] phaseMul options --- modes/AudioApps/BreakOrAudioApp.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index c4bab8a..6f8f6da 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -164,9 +164,11 @@ public: sum += v.ratios[i]; } v.ratioSum = sum; - v.phasorMul = ((float)(int)(params[paramIdx++] * 2.f) * 0.5f)+ 1.f; + static float muls[7] = {0.25f, 0.33f, 0.5f, 1.f, 1.5f, 2.f, 3.f}; + v.phasorMul = muls[(int)(params[paramIdx++] * 6.999999f)]; v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; } + // Serial.printf("pm: %f", ratioSeqStates[0].phasorMul); } From 3ddbcdfed96f89038257be2bee9c034698a14bcd Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Tue, 3 Mar 2026 11:53:50 +0000 Subject: [PATCH 09/15] break|| midi clock and seq control --- MEMLNaut-NISPS.ino | 2 + modes/AudioApps/BreakOrAudioApp.hpp | 123 ++++++++++++++------ modes/AudioApps/VerbFXAudioApp.hpp | 169 ++++++++++++++++++++++++++++ modes/MEMLNautModeBreakOr.hpp | 31 +++++ modes/MEMLNautModeVerbFX.hpp | 83 ++++++++++++++ 5 files changed, 376 insertions(+), 32 deletions(-) create mode 100644 modes/AudioApps/VerbFXAudioApp.hpp create mode 100644 modes/MEMLNautModeVerbFX.hpp diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index 93105d7..c6a955f 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -24,11 +24,13 @@ #include "modes/MEMLNautModeSoundAnalysisMIDI.hpp" #include "modes/MEMLNautModeXIASRI.hpp" #include "modes/MEMLNautModeBreakOr.hpp" +#include "modes/MEMLNautModeVerbFX.hpp" //hook up the memlnaut mode // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI // #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI +// #define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr // #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 6f8f6da..f6c6804 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -26,19 +26,23 @@ struct ratioSeqState { float ratioSum=1.f; int midiNote = 36; float pulseWidth = 0.5f; + + std::array ampRatios{1.f}; + float ampRatioSum=1.f; + }; template -inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { +inline bool __not_in_flash_func(ratioSeq)(float phasor, float phaseOffset, float ratioSum, const std::array &ratios, float pulseWidth) { bool trig = 0; - float offsetPhase = seq.phaseOffset + seq.phasor; + float offsetPhase = phaseOffset + phasor; if (offsetPhase >= 1.f) { offsetPhase -= 1.f; } - float phaseAdj = seq.ratioSum * offsetPhase; + float phaseAdj = ratioSum * offsetPhase; float accumulatedSum = 0; float lastAccumulatedSum = 0; - for (size_t v : seq.ratios) + for (size_t v : ratios) { accumulatedSum += v; if (phaseAdj <= accumulatedSum) @@ -46,7 +50,7 @@ inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { // check pulse width float beatPhase = (phaseAdj - lastAccumulatedSum) / (accumulatedSum - lastAccumulatedSum); - trig = beatPhase <= seq.pulseWidth; + trig = beatPhase <= pulseWidth; break; } lastAccumulatedSum = accumulatedSum; @@ -55,13 +59,18 @@ inline bool __not_in_flash_func(ratioSeq)(ratioSeqState &seq) { } -template +template class BreakOrAudioApp : public AudioAppBase { public: static constexpr size_t kN_Params = NPARAMS; static constexpr size_t nVoiceSpaces=0; + queue_t bpmQueue; + queue_t sequencerControlQueue; + + bool sequencerPlaying = false; + std::shared_ptr midiIO; std::array, nVoiceSpaces> voiceSpaces; @@ -83,7 +92,9 @@ public: } BreakOrAudioApp() : AudioAppBase() { - // currentVoiceSpace = voiceSpaces[0].mappingFunction; + // currentVoiceSpace = voiceSpaces[0].mappingFunction; + queue_init(&bpmQueue, sizeof(float), 1); + queue_init(&sequencerControlQueue, sizeof(int), 1); }; bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) @@ -102,31 +113,41 @@ public: stereosample_t __force_inline Process(const stereosample_t x) override { - if (sequencingSampleCounter==0) { - for(auto &seq: ratioSeqStates) { - //update phasor - seq.phasor += (seq.phasorInc * seq.phasorMul); - if (seq.phasor >= 1.f) { - seq.phasor -= 1.f; - } - bool trig = ratioSeq<3>(seq); - if (trig && !seq.lastTrig) { - if (trig) { - midiIO->queueNoteOn(seq.midiNote, 127); - }else{ - midiIO->queueNoteOff(seq.midiNote, 0); - } - } - seq.lastTrig = trig; + if (sequencerPlaying) { + midiClockPhasor += midiClockPhasorInc; + if (midiClockPhasor >= 1.f) { + midiClockPhasor -= 1.f; + midiIO->queueClock(); + midiIO->flushQueue(); + } + if (sequencingSampleCounter==0) { + for(auto &seq: ratioSeqStates) { + //update phasor + seq.phasor += (seq.phasorInc * seq.phasorMul); + if (seq.phasor >= 1.f) { + seq.phasor -= 1.f; + } + bool trig = ratioSeq<3>(seq.phasor, seq.phaseOffset, seq.ratioSum, seq.ratios, seq.pulseWidth); + bool highAmp = ratioSeq<2>(seq.phasor, seq.phaseOffset, seq.ampRatioSum, seq.ampRatios, 0.5f); + if (trig && !seq.lastTrig) { + if (trig) { + midiIO->queueNoteOn(seq.midiNote, highAmp ? 127 : 64); + }else{ + midiIO->queueNoteOff(seq.midiNote, 0); + } + } + seq.lastTrig = trig; + } + midiIO->flushQueue(); } - midiIO->flushQueue(); - } - sequencingSampleCounter ++; - if (sequencingSampleCounter >= sequencingSampleDiv) { - sequencingSampleCounter = 0; - } + sequencingSampleCounter ++; + if (sequencingSampleCounter >= sequencingSampleDiv) { + sequencingSampleCounter = 0; + } + + } stereosample_t ret { 0.f,0.f }; return ret; } @@ -155,18 +176,50 @@ public: void ProcessParams(const std::array& params) { firstParamsReceived = true; + if (queue_try_remove(&bpmQueue, &bpm)) { + bpm = 30.f + (bpm * 200.f); // Scale BPM from [0,1] to [30,230] + updateBPM(bpm); + } + int sequencerControl; + if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) { + sequencerPlaying = sequencerControl == 1; + if (!sequencerPlaying) { + midiIO->queueClockStop(); + // Send note offs for all sequences when stopping + for(auto &seq: ratioSeqStates) { + midiIO->queueNoteOff(seq.midiNote, 0); + seq.phasor = 0.f; // Reset phasor to start when stopping + } + midiIO->flushQueue(); + midiClockPhasor = 0.f; // Reset MIDI clock phasor when stopping + sequencingSampleCounter = 0; // Reset sequencing sample counter + + }else{ + midiIO->queueClockStart(); + midiIO->flushQueue(); + } + } // currentVoiceSpace(params); size_t paramIdx = 0; for(auto &v: ratioSeqStates) { float sum=0.f; for(size_t i=0; i < v.ratios.size(); i++) { - v.ratios[i] = (float)(int)(params[paramIdx++] * 7.f) + 1.f; + v.ratios[i] = (float)(int)(params[paramIdx++] * 3.f) + 1.f; sum += v.ratios[i]; } v.ratioSum = sum; - static float muls[7] = {0.25f, 0.33f, 0.5f, 1.f, 1.5f, 2.f, 3.f}; - v.phasorMul = muls[(int)(params[paramIdx++] * 6.999999f)]; + // static float muls[7] = {0.25f, 0.33f, 0.5f, 1.f, 1.5f, 2.f, 3.f}; + // v.phasorMul = muls[(int)(params[paramIdx++] * 6.999999f)]; + static float muls[4] = {0.5f, 1.f, 2.f, 4.f}; + v.phasorMul = muls[(int)(params[paramIdx++] * 3.999999f)]; v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; + + sum=0.f; + for(size_t i=0; i < v.ampRatios.size(); i++) { + v.ampRatios[i] = (float)(int)(params[paramIdx++] * 3.f) + 1.f; + sum += v.ampRatios[i]; + } + v.ampRatioSum = sum; } // Serial.printf("pm: %f", ratioSeqStates[0].phasorMul); @@ -181,6 +234,9 @@ public: for(auto &v: ratioSeqStates) { v.phasorInc = barPhasorInc; } + float midiClockLengthInSeconds = beatLengthInSeconds / 24.f; + float midiClockLengthInSamples = midiClockLengthInSeconds * sampleRatef; + midiClockPhasorInc = 1.f / midiClockLengthInSamples; } void setTimeSignature(float beats, float division) { @@ -205,6 +261,9 @@ protected: size_t sequencingSampleCounter = 0; std::array ratioSeqStates; + + float midiClockPhasor=0; + float midiClockPhasorInc=0; }; diff --git a/modes/AudioApps/VerbFXAudioApp.hpp b/modes/AudioApps/VerbFXAudioApp.hpp new file mode 100644 index 0000000..735f0ac --- /dev/null +++ b/modes/AudioApps/VerbFXAudioApp.hpp @@ -0,0 +1,169 @@ +#ifndef __VERBFX_AUDIO_APP_HPP__ +#define __VERBFX_AUDIO_APP_HPP__ + +#include "../../src/memllib/audio/AudioAppBase.hpp" // Added missing include + +#include +#include +#include + +//#include "src/memllib/interface/InterfaceBase.hpp" // Added missing include + +#include +#include "../../voicespaces/VoiceSpaces.hpp" +#include "../../src/memllib/synth/OnePoleSmoother.hpp" +#include "../../src/memllib/synth/maximilian.h" +#include "../../src/daisysp/Effects/pitchshifter.h" + + + + + +template +class VerbFXAudioApp : public AudioAppBase +{ +public: + static constexpr size_t kN_Params = NPARAMS; + static constexpr size_t nVoiceSpaces=1; + + + std::array, nVoiceSpaces> voiceSpaces; + + VoiceSpaceFn currentVoiceSpace; + + std::array getVoiceSpaceNames() { + std::array names; + for(size_t i=0; i < voiceSpaces.size(); i++) { + names[i] = voiceSpaces[i].name; + } + return names; + } + + void setVoiceSpace(size_t i) { + if (i < voiceSpaces.size()) { + currentVoiceSpace = voiceSpaces[i].mappingFunction; + } + } + + VerbFXAudioApp() : AudioAppBase() { + }; + + + __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()); + + 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 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, 482, allp2fb); + y2 = comb2.combfb(y2, 808, comb2fb); + + 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, 7886, dl2fb) * dl2mix); + float d3 = (dl3.play(y, 299, dl3fb) * dl3mix); + float d4 = (dl4.play(y, 15873, dl4fb) * dl4mix); + + + y = y + d1 + d2 + d3; + + // Mix dry + y = (y * wetdry_mix_) + (mix * (1.f - wetdry_mix_)); + + y = tanhf(y*1.2f); + + stereosample_t ret { y, y }; + return ret; + } + + void Setup(float sample_rate, std::shared_ptr interface) override + { + AudioAppBase::Setup(sample_rate, interface); + maxiSettings::sampleRate = sample_rate; + pitchshifter_.Init(sample_rate); + } + + __attribute__((always_inline)) void ProcessParams(const std::array& params) + { + // currentVoiceSpace(params); + neuralNetOutputs = params; + } + + +protected: + + std::array neuralNetOutputs{0}, smoothParams{0}; + + + maxiDelayline<5000> dl1; + maxiDelayline<8000> dl2; + maxiDelayline<1201> dl3; + maxiDelayline<16000> dl4; + + maxiReverbFilters<300> allp1; + maxiReverbFilters<500> allp2; + maxiReverbFilters<500> allp3; + maxiReverbFilters<500> allp4; + maxiReverbFilters<500> allp5; + maxiReverbFilters<500> allp6; + maxiReverbFilters<200> comb1; + maxiReverbFilters<900> comb2; + + maxiDCBlocker dcb; + + daisysp::PitchShifter pitchshifter_; + float pitchshifter_mix_{0.5f}; + float wetdry_mix_{0.5f}; + + OnePoleSmoother smoother{150.f, kSampleRate}; + +}; + +#endif diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp index 856814f..27818e5 100644 --- a/modes/MEMLNautModeBreakOr.hpp +++ b/modes/MEMLNautModeBreakOr.hpp @@ -22,10 +22,36 @@ public: InterfaceRL interface; std::shared_ptr interfacePtr; + bool sequencerPlaying = false; + void setupInterface() { interface.setup(kN_InputParams, BreakOrAudioApp<>::kN_Params); interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); interfacePtr = make_non_owning(interface); + + MEMLNaut::Instance()->setTogA2Callback([this](bool state) { // scr_ref no longer captured directly + Serial.println(state ? "TogA2 ON" : "TogA2 OFF"); + if (state) { + sequencerPlaying = !sequencerPlaying; + queue_try_add(&audioAppBreakOr.sequencerControlQueue, &sequencerPlaying); + } + + // //tiggle up + // if (state) { + // //store output + // savedAction = action; + // actionBeingDragged=true; + // msgView->post("Where do you want it?"); + // }else{ + // //button up + // if (actionBeingDragged) { + // this->storeExperience(1.f, controlInput, savedAction); + // actionBeingDragged=false; + // msgView->post("Here!"); + // } + // } + }); + } String getHelpTitle() { @@ -40,6 +66,11 @@ public: audioAppBreakOr.Setup(sample_rate, interfacePtr); voiceSpaceList = audioAppBreakOr.getVoiceSpaceNames(); audioAppBreakOr.setupMIDI(midi_interf); + MEMLNaut::Instance()->setRVGain1Callback([this](float value) { + // Serial.printf("Volume control: %f\n", value); + queue_try_add(&audioAppBreakOr.bpmQueue, &value); + }, 0); // Set threshold to 0 to trigger on any change + } __force_inline void loop() { diff --git a/modes/MEMLNautModeVerbFX.hpp b/modes/MEMLNautModeVerbFX.hpp new file mode 100644 index 0000000..a264b6b --- /dev/null +++ b/modes/MEMLNautModeVerbFX.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "../src/memllib/interface/MIDIInOut.hpp" +#include "./AudioApps/VerbFXAudioApp.hpp" +#include "MEMLNautMode.hpp" +#include +#include +#include "../XiasriAnalysis.hpp" +#include "../src/memllib/utils/sharedMem.hpp" +#include "../src/memllib/audio/AudioDriver.hpp" +#include "../src/memllib/examples/InterfaceRL.hpp" +#include "../src/memllib/PicoDefs.hpp" + + + +class MEMLNautModeVerbFX { +public: + constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params; //ML + InterfaceRL interface; + std::shared_ptr interfacePtr; + XiasriAnalysis mlAnalysis{kSampleRate}; + SharedBuffer machine_list_buffer; + + VerbFXAudioApp<> audioAppVerbFX; + std::shared_ptr midi_interf; + + void setupInterface() { + interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params); + interface.bindInterface(InterfaceRL::INPUT_MODES::MACHINE_LISTENING); + interfacePtr = make_non_owning(interface); + } + + String getHelpTitle() { + return "VerbFX Mode"; + } + + __force_inline stereosample_t process(stereosample_t x) { + return audioAppVerbFX.Process(x); + } + + void setupMIDI(std::shared_ptr new_midi_interf) { + midi_interf = new_midi_interf; + midi_interf->Setup(0); + midi_interf->SetMIDISendChannel(1); + interface.bindMIDI(midi_interf, true); + } + + void addViews() { + }; + + void setupAudio(float sample_rate) { + audioAppVerbFX.Setup(sample_rate, interfacePtr); + // Reinitialize XiasriAnalysis filters after maxiSettings is properly configured + mlAnalysis.ReinitFilters(); + } + + __force_inline void loop() { + audioAppVerbFX.loop(); + } + + __force_inline void analyse(stereosample_t x) { + union { + XiasriAnalysis::parameters_t p; + float v[XiasriAnalysis::kN_Params]; + } param_u; + param_u.p = mlAnalysis.Process(x.L + x.R); + // Write params into shared_buffer + machine_list_buffer.writeNonBlocking(param_u.v, XiasriAnalysis::kN_Params); + } + + __force_inline void processAnalysisParams() { + // Read SharedBuffer + std::vector mlist_params(XiasriAnalysis::kN_Params, 0); + machine_list_buffer.readNonBlocking(mlist_params); + // Send parameters to RL interface + interface.readAnalysisParameters(mlist_params); + // PERIODIC_RUN( + // Serial.printf("%f %f %f\n", mlist_params[0], mlist_params[1], mlist_params[2]); + // , 100); + + } + +}; From 9906cbd15454a5b334c63dc1a8b9253d4a51f63b Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Thu, 5 Mar 2026 22:25:21 +0000 Subject: [PATCH 10/15] i2c refactor and speed up --- MEMLNaut-NISPS.ino | 4 +- modes/AudioApps/BreakOrAudioApp.hpp | 49 +++++++++++++++++-------- modes/MEMLNautMode.hpp | 1 + modes/MEMLNautModeBreakOr.hpp | 31 +++++++++------- modes/MEMLNautModeChannelStrip.hpp | 1 + modes/MEMLNautModePAFSynth.hpp | 2 + modes/MEMLNautModeSoundAnalysisMIDI.hpp | 2 + modes/MEMLNautModeVerbFX.hpp | 6 ++- modes/MEMLNautModeXIASRI.hpp | 2 + src/memllib | 2 +- 10 files changed, 67 insertions(+), 33 deletions(-) diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index c6a955f..63e4531 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -164,6 +164,8 @@ void loop() { digitalWrite(33, LOW); }, 100000) + + currentMode->loopCore0(); } @@ -231,5 +233,5 @@ void loop1() { PERIODIC_RUN_US( midi_interf->Poll(); - , 10000) + , 1000) } diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index f6c6804..0af9b1d 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -12,6 +12,7 @@ #include "../../src/memllib/interface/InterfaceBase.hpp" + #include #include "../../voicespaces/VoiceSpaces.hpp" @@ -59,7 +60,7 @@ inline bool __not_in_flash_func(ratioSeq)(float phasor, float phaseOffset, float } -template +template class BreakOrAudioApp : public AudioAppBase { public: @@ -68,6 +69,8 @@ public: queue_t bpmQueue; queue_t sequencerControlQueue; + queue_t i2cOutQueue; + bool sequencerPlaying = false; @@ -95,6 +98,7 @@ public: // currentVoiceSpace = voiceSpaces[0].mappingFunction; queue_init(&bpmQueue, sizeof(float), 1); queue_init(&sequencerControlQueue, sizeof(int), 1); + queue_init(&i2cOutQueue, sizeof(float) * 8, 1); }; bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) @@ -121,14 +125,19 @@ public: midiIO->flushQueue(); } if (sequencingSampleCounter==0) { + + barPhasor += barPhasorInc; + if (barPhasor >= 1.f) { + barPhasor -= 1.f; + } + int i2cIdx=0; for(auto &seq: ratioSeqStates) { //update phasor - seq.phasor += (seq.phasorInc * seq.phasorMul); - if (seq.phasor >= 1.f) { - seq.phasor -= 1.f; - } - bool trig = ratioSeq<3>(seq.phasor, seq.phaseOffset, seq.ratioSum, seq.ratios, seq.pulseWidth); - bool highAmp = ratioSeq<2>(seq.phasor, seq.phaseOffset, seq.ampRatioSum, seq.ampRatios, 0.5f); + float seqPhasor = barPhasor * seq.phasorMul; + seqPhasor = fmodf(seqPhasor + seq.phaseOffset, 1.f); // Wrap phasor to [0,1] + + bool trig = ratioSeq<3>(seqPhasor, seq.phaseOffset, seq.ratioSum, seq.ratios, seq.pulseWidth); + bool highAmp = ratioSeq<2>(seqPhasor, seq.phaseOffset, seq.ampRatioSum, seq.ampRatios, 0.5f); if (trig && !seq.lastTrig) { if (trig) { midiIO->queueNoteOn(seq.midiNote, highAmp ? 127 : 64); @@ -137,8 +146,10 @@ public: } } seq.lastTrig = trig; + i2cValues[i2cIdx++] = trig; } midiIO->flushQueue(); + queue_try_add(&i2cOutQueue, &i2cValues); } sequencingSampleCounter ++; @@ -158,7 +169,7 @@ public: maxiSettings::sampleRate = sample_rate; sampleRatef = static_cast(sample_rate); updateBPM(90.f); - const size_t midiNotes[NSEQUENCES] = {36,37,38,39,40, 42,43,45,47,48}; + const size_t midiNotes[NSEQUENCES] = {36,37,38,39,40, 42,43,45/*,47,48*/}; size_t midiNote = 0; for(auto &seq: ratioSeqStates) { seq.midiNote = midiNotes[midiNote++]; @@ -174,7 +185,9 @@ public: } void ProcessParams(const std::array& params) - { + { + // if (sequencerPlaying) { + // } firstParamsReceived = true; if (queue_try_remove(&bpmQueue, &bpm)) { bpm = 30.f + (bpm * 200.f); // Scale BPM from [0,1] to [30,230] @@ -198,6 +211,7 @@ public: midiIO->queueClockStart(); midiIO->flushQueue(); } + Serial.printf("Sequencer %s\n", sequencerPlaying ? "Playing" : "Stopped"); } // currentVoiceSpace(params); size_t paramIdx = 0; @@ -210,7 +224,7 @@ public: v.ratioSum = sum; // static float muls[7] = {0.25f, 0.33f, 0.5f, 1.f, 1.5f, 2.f, 3.f}; // v.phasorMul = muls[(int)(params[paramIdx++] * 6.999999f)]; - static float muls[4] = {0.5f, 1.f, 2.f, 4.f}; + static float muls[4] = {1.f, 2.f, 4.f, 8.f}; v.phasorMul = muls[(int)(params[paramIdx++] * 3.999999f)]; v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; @@ -230,10 +244,10 @@ public: float beatLengthInSeconds = 60.f / bpm; float barLengthInSeconds = beatLengthInSeconds * timeSigBeats; float barLengthInSamples = barLengthInSeconds * (sampleRatef/ sequencingSampleDiv); - float barPhasorInc = 1.f/ barLengthInSamples; - for(auto &v: ratioSeqStates) { - v.phasorInc = barPhasorInc; - } + barPhasorInc = 1.f/ barLengthInSamples; + // for(auto &v: ratioSeqStates) { + // v.phasorInc = barPhasorInc; + // } float midiClockLengthInSeconds = beatLengthInSeconds / 24.f; float midiClockLengthInSamples = midiClockLengthInSeconds * sampleRatef; midiClockPhasorInc = 1.f / midiClockLengthInSamples; @@ -249,6 +263,8 @@ public: protected: + float i2cValues[8] = {0,0,0,0,0,0,0,0}; + float sampleRatef; bool firstParamsReceived = false; @@ -256,8 +272,11 @@ protected: float timeSigBeats=4.f; float timeSigBeatsInv=1.f/timeSigBeats; float timeSigDivision=4.f; + + float barPhasorInc=0.f; + float barPhasor; - size_t sequencingSampleDiv = 50; + size_t sequencingSampleDiv = 400; size_t sequencingSampleCounter = 0; std::array ratioSeqStates; diff --git a/modes/MEMLNautMode.hpp b/modes/MEMLNautMode.hpp index ad8c52a..5581259 100644 --- a/modes/MEMLNautMode.hpp +++ b/modes/MEMLNautMode.hpp @@ -23,5 +23,6 @@ concept MEMLNautMode = requires(T proc) { {proc.processAnalysisParams()} -> std::same_as; // {proc.getNMIDICtrlOutputs()} -> std::same_as; {proc.setupInterface()} -> std::same_as; + {proc.loopCore0()} -> std::same_as; requires std::same_as; }; diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp index 27818e5..87d5e9f 100644 --- a/modes/MEMLNautModeBreakOr.hpp +++ b/modes/MEMLNautModeBreakOr.hpp @@ -11,11 +11,14 @@ #include "MEMLNautMode.hpp" #include #include +#include "../src/memllib/interface/USeqI2C.hpp" + class MEMLNautModeBreakOr { public: constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate + USeqI2C i2cOut; inline static BreakOrAudioApp<> audioAppBreakOr; std::array::nVoiceSpaces> voiceSpaceList; @@ -24,6 +27,8 @@ public: bool sequencerPlaying = false; + + void setupInterface() { interface.setup(kN_InputParams, BreakOrAudioApp<>::kN_Params); interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); @@ -36,22 +41,9 @@ public: queue_try_add(&audioAppBreakOr.sequencerControlQueue, &sequencerPlaying); } - // //tiggle up - // if (state) { - // //store output - // savedAction = action; - // actionBeingDragged=true; - // msgView->post("Where do you want it?"); - // }else{ - // //button up - // if (actionBeingDragged) { - // this->storeExperience(1.f, controlInput, savedAction); - // actionBeingDragged=false; - // msgView->post("Here!"); - // } - // } }); + i2cOut.begin(); } String getHelpTitle() { @@ -105,6 +97,17 @@ public: void analyse(stereosample_t) {} + float i2cValues[8]; + void loopCore0() { + PERIODIC_RUN_US( + if (queue_try_remove(&audioAppBreakOr.i2cOutQueue, &i2cValues)) { + // Serial.printf("i2c received: %f %f %f %f %f %f %f %f\n", i2cValues[0], i2cValues[1], i2cValues[2], i2cValues[3], i2cValues[4], i2cValues[5], i2cValues[6], i2cValues[7]); + bool i2cSuccess = i2cOut.sendValues(i2cValues, 8); + } + , 5000) + + } + }; diff --git a/modes/MEMLNautModeChannelStrip.hpp b/modes/MEMLNautModeChannelStrip.hpp index e9eefe4..593b2a1 100644 --- a/modes/MEMLNautModeChannelStrip.hpp +++ b/modes/MEMLNautModeChannelStrip.hpp @@ -105,5 +105,6 @@ public: void analyse(stereosample_t) {} + void loopCore0() {} }; diff --git a/modes/MEMLNautModePAFSynth.hpp b/modes/MEMLNautModePAFSynth.hpp index 9249943..3fd7152 100644 --- a/modes/MEMLNautModePAFSynth.hpp +++ b/modes/MEMLNautModePAFSynth.hpp @@ -115,4 +115,6 @@ public: void analyse(stereosample_t) {} + void loopCore0() {} + }; diff --git a/modes/MEMLNautModeSoundAnalysisMIDI.hpp b/modes/MEMLNautModeSoundAnalysisMIDI.hpp index ea06092..58c846f 100644 --- a/modes/MEMLNautModeSoundAnalysisMIDI.hpp +++ b/modes/MEMLNautModeSoundAnalysisMIDI.hpp @@ -85,4 +85,6 @@ public: } + void loopCore0() {} + }; diff --git a/modes/MEMLNautModeVerbFX.hpp b/modes/MEMLNautModeVerbFX.hpp index a264b6b..a58d284 100644 --- a/modes/MEMLNautModeVerbFX.hpp +++ b/modes/MEMLNautModeVerbFX.hpp @@ -15,7 +15,7 @@ class MEMLNautModeVerbFX { public: - constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params; //ML + constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params +4; //ML + joystick InterfaceRL interface; std::shared_ptr interfacePtr; XiasriAnalysis mlAnalysis{kSampleRate}; @@ -26,7 +26,7 @@ public: void setupInterface() { interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params); - interface.bindInterface(InterfaceRL::INPUT_MODES::MACHINE_LISTENING); + interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK_AND_MACHINE_LISTENING); interfacePtr = make_non_owning(interface); } @@ -79,5 +79,7 @@ public: // , 100); } + + void loopCore0() {} }; diff --git a/modes/MEMLNautModeXIASRI.hpp b/modes/MEMLNautModeXIASRI.hpp index 403dda7..77a10f5 100644 --- a/modes/MEMLNautModeXIASRI.hpp +++ b/modes/MEMLNautModeXIASRI.hpp @@ -80,4 +80,6 @@ public: } + void loopCore0() {} + }; diff --git a/src/memllib b/src/memllib index 1693e46..1e420bf 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 1693e46e0b38c5920ebdf6cdf2a9107503003c8d +Subproject commit 1e420bf8ed869d58df75663d3dbf6870aa543de1 From 1af45be1687de637f2021dd17c868e1d5f629d6b Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Fri, 6 Mar 2026 10:04:30 +0000 Subject: [PATCH 11/15] midi sync --- modes/AudioApps/BreakOrAudioApp.hpp | 31 ++++++++++++++++++++++------- modes/MEMLNautModeBreakOr.hpp | 27 ++++++++++++++++++++----- src/memllib | 2 +- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 0af9b1d..52d0de0 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -70,6 +70,12 @@ public: queue_t bpmQueue; queue_t sequencerControlQueue; queue_t i2cOutQueue; + queue_t barPhaseResetQueue; + + enum SequencerClockModes { + INTERNAL, + MIDI_CLOCK + } sequencerClockMode = INTERNAL; bool sequencerPlaying = false; @@ -99,6 +105,7 @@ public: queue_init(&bpmQueue, sizeof(float), 1); queue_init(&sequencerControlQueue, sizeof(int), 1); queue_init(&i2cOutQueue, sizeof(float) * 8, 1); + queue_init(&barPhaseResetQueue, sizeof(int), 1); }; bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) @@ -118,11 +125,13 @@ public: stereosample_t __force_inline Process(const stereosample_t x) override { if (sequencerPlaying) { - midiClockPhasor += midiClockPhasorInc; - if (midiClockPhasor >= 1.f) { - midiClockPhasor -= 1.f; - midiIO->queueClock(); - midiIO->flushQueue(); + if (sequencerClockMode == INTERNAL){ + midiClockPhasor += midiClockPhasorInc; + if (midiClockPhasor >= 1.f) { + midiClockPhasor -= 1.f; + midiIO->queueClock(); + midiIO->flushQueue(); + } } if (sequencingSampleCounter==0) { @@ -130,6 +139,13 @@ public: if (barPhasor >= 1.f) { barPhasor -= 1.f; } + if (sequencerClockMode == MIDI_CLOCK) { + int phasorResetValue=0; + if (queue_try_remove(&barPhaseResetQueue, &phasorResetValue)) { + barPhasor = 0.f; + Serial.println("Bar phase reset triggered by queue"); + } + } int i2cIdx=0; for(auto &seq: ratioSeqStates) { //update phasor @@ -163,11 +179,12 @@ public: return ret; } - void Setup(float sample_rate, std::shared_ptr interface) override + void Setup(float sample_rate, std::shared_ptr interface, SequencerClockModes clockMode) { AudioAppBase::Setup(sample_rate, interface); maxiSettings::sampleRate = sample_rate; sampleRatef = static_cast(sample_rate); + sequencerClockMode = clockMode; updateBPM(90.f); const size_t midiNotes[NSEQUENCES] = {36,37,38,39,40, 42,43,45/*,47,48*/}; size_t midiNote = 0; @@ -190,7 +207,7 @@ public: // } firstParamsReceived = true; if (queue_try_remove(&bpmQueue, &bpm)) { - bpm = 30.f + (bpm * 200.f); // Scale BPM from [0,1] to [30,230] + // bpm = 30.f + (bpm * 200.f); // Scale BPM from [0,1] to [30,230] updateBPM(bpm); } int sequencerControl; diff --git a/modes/MEMLNautModeBreakOr.hpp b/modes/MEMLNautModeBreakOr.hpp index 87d5e9f..32c6b4c 100644 --- a/modes/MEMLNautModeBreakOr.hpp +++ b/modes/MEMLNautModeBreakOr.hpp @@ -25,10 +25,10 @@ public: InterfaceRL interface; std::shared_ptr interfacePtr; + BreakOrAudioApp<>::SequencerClockModes clockMode = BreakOrAudioApp<>::MIDI_CLOCK; + bool sequencerPlaying = false; - - void setupInterface() { interface.setup(kN_InputParams, BreakOrAudioApp<>::kN_Params); interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); @@ -55,12 +55,13 @@ public: } void setupAudio(float sample_rate) { - audioAppBreakOr.Setup(sample_rate, interfacePtr); + audioAppBreakOr.Setup(sample_rate, interfacePtr, clockMode); voiceSpaceList = audioAppBreakOr.getVoiceSpaceNames(); audioAppBreakOr.setupMIDI(midi_interf); MEMLNaut::Instance()->setRVGain1Callback([this](float value) { - // Serial.printf("Volume control: %f\n", value); - queue_try_add(&audioAppBreakOr.bpmQueue, &value); + if (clockMode == BreakOrAudioApp<>::INTERNAL) { + queue_try_add(&audioAppBreakOr.bpmQueue, &value); + } }, 0); // Set threshold to 0 to trigger on any change } @@ -76,6 +77,22 @@ public: midi_interf->Setup(0); midi_interf->SetMIDISendChannel(1); midi_interf->SetMIDINoteChannel(10); + midi_interf->SetNoteCallback([this](bool noteon, uint8_t note_number, uint8_t vel_value) { + Serial.printf("MIDI Note %d: %d %d\n", note_number, vel_value, noteon); + if (note_number==0) { + int resetTrigger=1; + queue_try_add(&audioAppBreakOr.barPhaseResetQueue, &resetTrigger); // value doesn't matter, just a trigger + Serial.println("Bar phase reset triggered by MIDI note 0"); + + } + }); + midi_interf->SetBPMCallback([this](float bpm) { + // Serial.printf("Estimated BPM: %.2f\n", bpm); + if (clockMode == BreakOrAudioApp<>::MIDI_CLOCK) { + queue_try_add(&audioAppBreakOr.bpmQueue, &bpm); + } + + }); interface.bindMIDI(midi_interf); } diff --git a/src/memllib b/src/memllib index 1e420bf..12842a8 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 1e420bf8ed869d58df75663d3dbf6870aa543de1 +Subproject commit 12842a8ae7e7a9d93976fb1291f21f162381b2f4 From c4659ce1d78c0a7be4d20fb6ca558a10dca84481 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sat, 7 Mar 2026 15:14:39 +0000 Subject: [PATCH 12/15] fm ops and clock estimation fix --- MEMLNaut-NISPS.ino | 6 +- modes/AudioApps/BreakOrAudioApp.hpp | 43 +--- modes/AudioApps/ElysiamorfAudioApp.hpp | 324 +++++++++++++++++++++++++ modes/AudioApps/RatioSeq.hpp | 49 ++++ modes/MEMLNautModeElysiamorfs.hpp | 121 +++++++++ 5 files changed, 499 insertions(+), 44 deletions(-) create mode 100644 modes/AudioApps/ElysiamorfAudioApp.hpp create mode 100644 modes/AudioApps/RatioSeq.hpp create mode 100644 modes/MEMLNautModeElysiamorfs.hpp diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index 63e4531..3400ce1 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -25,13 +25,15 @@ #include "modes/MEMLNautModeXIASRI.hpp" #include "modes/MEMLNautModeBreakOr.hpp" #include "modes/MEMLNautModeVerbFX.hpp" +#include "modes/MEMLNautModeElysiamorfs.hpp" -//hook up the memlnaut mode +//hook up the memlnaut mode // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI // #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI // #define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX -#define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr +// #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr +#define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs // #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth diff --git a/modes/AudioApps/BreakOrAudioApp.hpp b/modes/AudioApps/BreakOrAudioApp.hpp index 52d0de0..9d96a40 100644 --- a/modes/AudioApps/BreakOrAudioApp.hpp +++ b/modes/AudioApps/BreakOrAudioApp.hpp @@ -17,47 +17,7 @@ #include "../../voicespaces/VoiceSpaces.hpp" -struct ratioSeqState { - std::array ratios{1.f}; - float phasor=0.f; - float phasorInc=0.f; - float phaseOffset = 0.f; - bool lastTrig = false; - float phasorMul = 1.f; - float ratioSum=1.f; - int midiNote = 36; - float pulseWidth = 0.5f; - - std::array ampRatios{1.f}; - float ampRatioSum=1.f; - -}; - -template -inline bool __not_in_flash_func(ratioSeq)(float phasor, float phaseOffset, float ratioSum, const std::array &ratios, float pulseWidth) { - bool trig = 0; - float offsetPhase = phaseOffset + phasor; - if (offsetPhase >= 1.f) { - offsetPhase -= 1.f; - } - float phaseAdj = ratioSum * offsetPhase; - float accumulatedSum = 0; - float lastAccumulatedSum = 0; - for (size_t v : ratios) - { - accumulatedSum += v; - if (phaseAdj <= accumulatedSum) - { - // check pulse width - float beatPhase = (phaseAdj - lastAccumulatedSum) / - (accumulatedSum - lastAccumulatedSum); - trig = beatPhase <= pulseWidth; - break; - } - lastAccumulatedSum = accumulatedSum; - } - return trig; -} +#include "RatioSeq.hpp" template @@ -143,7 +103,6 @@ public: int phasorResetValue=0; if (queue_try_remove(&barPhaseResetQueue, &phasorResetValue)) { barPhasor = 0.f; - Serial.println("Bar phase reset triggered by queue"); } } int i2cIdx=0; diff --git a/modes/AudioApps/ElysiamorfAudioApp.hpp b/modes/AudioApps/ElysiamorfAudioApp.hpp new file mode 100644 index 0000000..7913822 --- /dev/null +++ b/modes/AudioApps/ElysiamorfAudioApp.hpp @@ -0,0 +1,324 @@ +#ifndef __ELYSIAMORF_AUDIO_APP_HPP__ +#define __ELYSIAMORF_AUDIO_APP_HPP__ + +#include "../../src/memllib/audio/AudioAppBase.hpp" +#include "../../src/memllib/synth/maximilian.h" +#include "../../src/memllib/interface/MIDIInOut.hpp" + + +#include +#include +#include // Added for std::shared_ptr + +#include "../../src/memllib/interface/InterfaceBase.hpp" + + +#include + +#include "../../voicespaces/VoiceSpaces.hpp" + + +struct FMOp { + float prevOutput = 0.f; + + // phase: external phasor 0-1 (e.g. barPhasor * phasorMul) + // modIn: signal from another operator [-1, 1], 0 = none + // freqMul: cycles per phase period (1 = once per bar, 2 = twice, 0.5 = half, etc.) + // modIndex: depth of modulation — scales how much modIn shifts the phase + // fbLevel: feedback 0-1 — feeds prevOutput back into own phase + float __force_inline process(float phase, float modIn, + float freqMul, float modIndex, + float fbLevel) { + float p = fmodf(phase * freqMul + modIndex * modIn + fbLevel * prevOutput, 1.f); + if (p < 0.f) p += 1.f; + float out = sinf(TWO_PI * p); + prevOutput = out; + return out; + } +}; + +// Convenience: 2-op FM pair matching the (phase, carrierFreq, modFreq, modIndex, fbLevel) signature. +// fbLevel sits on the modulator (DX7 style). carrier and modulator are caller-owned FMOp instances. +float __force_inline fmPair(float phase, + float carrierFreq, float modFreq, + float modIndex, float fbLevel, + FMOp& carrier, FMOp& modulator) { + float modOut = modulator.process(phase, 0.f, modFreq, 0.f, fbLevel); + return carrier.process(phase, modOut, carrierFreq, modIndex, 0.f); +} + +struct fmSeqState { + float phaseOffset = 0.f; + float phasorMul = 1.f; + float ratio=1.f; + float carrierFreq=1.f; + float modFreq = 2.f; + float modIndex = 2.f; + float fbLevel = 0.f; + FMOp carrier; + FMOp modulator; +}; + + +#include "RatioSeq.hpp" + +template +class ElysiamorfAudioApp : public AudioAppBase +{ +public: + static constexpr size_t kN_Params = NPARAMS; + static constexpr size_t nVoiceSpaces=0; + + queue_t bpmQueue; + queue_t sequencerControlQueue; + queue_t i2cOutQueue; + queue_t barPhaseResetQueue; + + enum SequencerClockModes { + INTERNAL, + MIDI_CLOCK + } sequencerClockMode = INTERNAL; + + + bool sequencerPlaying = false; + + std::shared_ptr midiIO; + + std::array, nVoiceSpaces> voiceSpaces; + + VoiceSpaceFn currentVoiceSpace; + + std::array getVoiceSpaceNames() { + std::array names; + for(size_t i=0; i < voiceSpaces.size(); i++) { + names[i] = voiceSpaces[i].name; + } + return names; + } + + void setVoiceSpace(size_t i) { + if (i < voiceSpaces.size()) { + currentVoiceSpace = voiceSpaces[i].mappingFunction; + } + } + + ElysiamorfAudioApp() : AudioAppBase() { + // currentVoiceSpace = voiceSpaces[0].mappingFunction; + queue_init(&bpmQueue, sizeof(float), 1); + queue_init(&sequencerControlQueue, sizeof(int), 1); + queue_init(&i2cOutQueue, sizeof(float) * 8, 1); + queue_init(&barPhaseResetQueue, sizeof(int), 1); + }; + + bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth) + { + // Euclidean function + const float fi = phase * n; + int i = static_cast(fi); + const float rem = fi - i; + if (i == n) + { + i--; + } + const int idx = ((i + n - offset) * k) % n; + return (idx < k && rem < pulseWidth) ? 1 : 0; + } + + stereosample_t __force_inline Process(const stereosample_t x) override + { + if (sequencerPlaying) { + if (sequencerClockMode == INTERNAL){ + midiClockPhasor += midiClockPhasorInc; + if (midiClockPhasor >= 1.f) { + midiClockPhasor -= 1.f; + midiIO->queueClock(); + midiIO->flushQueue(); + } + } + if (sequencingSampleCounter==0) { + + barPhasor += barPhasorInc; + if (barPhasor >= 1.f) { + barPhasor -= 1.f; + } + if (sequencerClockMode == MIDI_CLOCK) { + int phasorResetValue=0; + if (queue_try_remove(&barPhaseResetQueue, &phasorResetValue)) { + barPhasor = 0.f; + } + } + // int i2cIdx=0; + int ccIndex=0; + static int ccNumbers[8] = {1,2,3,4,5,9,11,12}; + // for(auto &seq: ratioSeqStates) { + // //update phasor + // float seqPhasor = barPhasor * seq.phasorMul; + // seqPhasor = fmodf(seqPhasor + seq.phaseOffset, 1.f); // Wrap phasor to [0,1] + + // bool trig = ratioSeq<3>(seqPhasor, seq.phaseOffset, seq.ratioSum, seq.ratios, seq.pulseWidth); + // bool highAmp = ratioSeq<2>(seqPhasor, seq.phaseOffset, seq.ampRatioSum, seq.ampRatios, 0.5f); + // if (trig != seq.lastTrig) { + // midiIO->queueCC(ccNumbers[ccIndex++], trig ? highAmp ? 127 : 64 : 0); + // // Serial.printf("Seq %d - Phasor: %f, Trig: %d, HighAmp: %d\n", &seq - &ratioSeqStates[0], seqPhasor, trig, highAmp); + // } + // seq.lastTrig = trig; + // // i2cValues[i2cIdx++] = trig; + // } + for(auto &seq: fmSeqStates) { + //update phasor + float seqPhasor = barPhasor * seq.phasorMul; + seqPhasor = fmodf(seqPhasor + seq.phaseOffset, 1.f); // Wrap phasor to [0,1] + float fmVal = fmPair(seqPhasor, seq.carrierFreq, seq.modFreq, seq.modIndex, seq.fbLevel, seq.carrier, seq.modulator); + fmVal = (fmVal + 1.f) * 0.5f; // Normalize to [0,1] + fmVal *= 127.f; // Scale to MIDI range + // if (ccIndex==0) { + // Serial.printf("Seq %d - Phasor: %f, FM Val: %f\n", &seq - &fmSeqStates[0], seqPhasor, fmVal); + // } + midiIO->queueCC(ccNumbers[ccIndex++], static_cast(fmVal)); + } + midiIO->flushQueue(); + // queue_try_add(&i2cOutQueue, &i2cValues); + } + + sequencingSampleCounter ++; + if (sequencingSampleCounter >= sequencingSampleDiv) { + sequencingSampleCounter = 0; + } + + + } + stereosample_t ret { 0.f,0.f }; + return ret; + } + + void Setup(float sample_rate, std::shared_ptr interface, SequencerClockModes clockMode) + { + AudioAppBase::Setup(sample_rate, interface); + maxiSettings::sampleRate = sample_rate; + sampleRatef = static_cast(sample_rate); + sequencerClockMode = clockMode; + updateBPM(90.f); + } + + void setupMIDI(std::shared_ptr new_midi_interf) { + midiIO = new_midi_interf; + } + + void loop() override { + AudioAppBase::loop(); + } + + void ProcessParams(const std::array& params) + { + firstParamsReceived = true; + if (queue_try_remove(&bpmQueue, &bpm)) { + updateBPM(bpm); + Serial.printf("BPM updated: %f\n", bpm); + } + int sequencerControl; + if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) { + sequencerPlaying = sequencerControl == 1; + if (!sequencerPlaying) { + midiIO->queueClockStop(); + // Send note offs for all sequences when stopping + for(auto &seq: ratioSeqStates) { + midiIO->queueNoteOff(seq.midiNote, 0); + seq.phasor = 0.f; // Reset phasor to start when stopping + } + midiIO->flushQueue(); + midiClockPhasor = 0.f; // Reset MIDI clock phasor when stopping + sequencingSampleCounter = 0; // Reset sequencing sample counter + + }else{ + midiIO->queueClockStart(); + midiIO->flushQueue(); + } + Serial.printf("Sequencer %s\n", sequencerPlaying ? "Playing" : "Stopped"); + } + // currentVoiceSpace(params); + // size_t paramIdx = 0; + // for(auto &v: ratioSeqStates) { + // float sum=0.f; + // for(size_t i=0; i < v.ratios.size(); i++) { + // v.ratios[i] = (float)(int)(params[paramIdx++] * 3.f) + 1.f; + // sum += v.ratios[i]; + // } + // v.ratioSum = sum; + // // static float muls[7] = {0.25f, 0.33f, 0.5f, 1.f, 1.5f, 2.f, 3.f}; + // // v.phasorMul = muls[(int)(params[paramIdx++] * 6.999999f)]; + // static float muls[4] = {1.f, 2.f, 4.f, 8.f}; + // v.phasorMul = muls[(int)(params[paramIdx++] * 3.999999f)]; + // v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; + + // sum=0.f; + // for(size_t i=0; i < v.ampRatios.size(); i++) { + // v.ampRatios[i] = (float)(int)(params[paramIdx++] * 3.f) + 1.f; + // sum += v.ampRatios[i]; + // } + // v.ampRatioSum = sum; + // } + size_t paramIdx = 0; + for(auto &v: fmSeqStates) { + v.carrierFreq = (0.25f + params[paramIdx++] * 0.75f) * 0.125f; // 1 to 10 + v.modFreq = (0.25f + params[paramIdx++] * 0.75f) * 0.25f; // 1 to 10 + v.modIndex = params[paramIdx++] * 4.f; // 0 to 10 + v.fbLevel = 0.f;//params[paramIdx++] ; // 0 to 1 + static float muls[4] = {1.f}; + v.phasorMul = 1.f;//muls[(int)(params[paramIdx++])]; + v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; + } + // Serial.printf("pm: %f", ratioSeqStates[0].phasorMul); + + } + + void updateBPM(float newBPM) { + bpm = newBPM; + float beatLengthInSeconds = 60.f / bpm; + float barLengthInSeconds = beatLengthInSeconds * timeSigBeats; + float barLengthInSamples = barLengthInSeconds * (sampleRatef/ sequencingSampleDiv); + barPhasorInc = 1.f/ barLengthInSamples; + // for(auto &v: ratioSeqStates) { + // v.phasorInc = barPhasorInc; + // } + float midiClockLengthInSeconds = beatLengthInSeconds / 24.f; + float midiClockLengthInSamples = midiClockLengthInSeconds * sampleRatef; + midiClockPhasorInc = 1.f / midiClockLengthInSamples; + } + + void setTimeSignature(float beats, float division) { + timeSigBeats = beats; + timeSigDivision = division; + updateBPM(bpm); // Recalculate phasor increment with new time signature + } + + + +protected: + + float i2cValues[8] = {0,0,0,0,0,0,0,0}; + + float sampleRatef; + bool firstParamsReceived = false; + + float bpm=90.f; + float timeSigBeats=4.f; + float timeSigBeatsInv=1.f/timeSigBeats; + float timeSigDivision=4.f; + + float barPhasorInc=0.f; + float barPhasor; + + size_t sequencingSampleDiv = 500; + size_t sequencingSampleCounter = 0; + + std::array ratioSeqStates; + std::array fmSeqStates; + + + float midiClockPhasor=0; + float midiClockPhasorInc=0; + +}; + +#endif // __ELYSIAMORF_AUDIO_APP_HPP__ + diff --git a/modes/AudioApps/RatioSeq.hpp b/modes/AudioApps/RatioSeq.hpp new file mode 100644 index 0000000..c522764 --- /dev/null +++ b/modes/AudioApps/RatioSeq.hpp @@ -0,0 +1,49 @@ +#ifndef __RATIO_SEQ_HPP__ +#define __RATIO_SEQ_HPP__ + +#include +#include + +struct ratioSeqState { + std::array ratios{1.f}; + float phasor=0.f; + float phasorInc=0.f; + float phaseOffset = 0.f; + bool lastTrig = false; + float phasorMul = 1.f; + float ratioSum=1.f; + int midiNote = 36; + float pulseWidth = 0.5f; + + std::array ampRatios{1.f}; + float ampRatioSum=1.f; + +}; + +template +inline bool __not_in_flash_func(ratioSeq)(float phasor, float phaseOffset, float ratioSum, const std::array &ratios, float pulseWidth) { + bool trig = 0; + float offsetPhase = phaseOffset + phasor; + if (offsetPhase >= 1.f) { + offsetPhase -= 1.f; + } + float phaseAdj = ratioSum * offsetPhase; + float accumulatedSum = 0; + float lastAccumulatedSum = 0; + for (size_t v : ratios) + { + accumulatedSum += v; + if (phaseAdj <= accumulatedSum) + { + // check pulse width + float beatPhase = (phaseAdj - lastAccumulatedSum) / + (accumulatedSum - lastAccumulatedSum); + trig = beatPhase <= pulseWidth; + break; + } + lastAccumulatedSum = accumulatedSum; + } + return trig; +} + +#endif // __RATIO_SEQ_HPP__ diff --git a/modes/MEMLNautModeElysiamorfs.hpp b/modes/MEMLNautModeElysiamorfs.hpp new file mode 100644 index 0000000..5d6f6f8 --- /dev/null +++ b/modes/MEMLNautModeElysiamorfs.hpp @@ -0,0 +1,121 @@ +#ifndef MEMLNAUT_MODE_ELYSIAMORFS_HPP +#define MEMLNAUT_MODE_ELYSIAMORFS_HPP + +//Elysiamorfs + +#include "../src/memllib/interface/MIDIInOut.hpp" +#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp" +#include "./AudioApps/ElysiamorfAudioApp.hpp" +#include "../src/memllib/examples/InterfaceRL.hpp" +#include "../src/memllib/PicoDefs.hpp" +#include "MEMLNautMode.hpp" +#include +#include +#include "../src/memllib/interface/USeqI2C.hpp" + + +class MEMLNautModeElysiamorfs { +public: + constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate + + USeqI2C i2cOut; + inline static ElysiamorfAudioApp<> audioAppElysiamorfs; + std::array::nVoiceSpaces> voiceSpaceList; + + InterfaceRL interface; + std::shared_ptr interfacePtr; + + ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::MIDI_CLOCK; + + bool sequencerPlaying = false; + + void setupInterface() { + interface.setup(kN_InputParams, ElysiamorfAudioApp<>::kN_Params); + interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); + interfacePtr = make_non_owning(interface); + + MEMLNaut::Instance()->setTogA2Callback([this](bool state) { // scr_ref no longer captured directly + Serial.println(state ? "TogA2 ON" : "TogA2 OFF"); + if (state) { + sequencerPlaying = !sequencerPlaying; + queue_try_add(&audioAppElysiamorfs.sequencerControlQueue, &sequencerPlaying); + } + + }); + + i2cOut.begin(); + } + + String getHelpTitle() { + return "Elysiamorfs"; + } + + __force_inline stereosample_t process(stereosample_t x) { + return audioAppElysiamorfs.Process(x); + } + + void setupAudio(float sample_rate) { + audioAppElysiamorfs.Setup(sample_rate, interfacePtr, clockMode); + voiceSpaceList = audioAppElysiamorfs.getVoiceSpaceNames(); + audioAppElysiamorfs.setupMIDI(midi_interf); + MEMLNaut::Instance()->setRVGain1Callback([this](float value) { + if (clockMode == ElysiamorfAudioApp<>::INTERNAL) { + float bpm = 30.f + (value * 200.f); // Scale BPM from [0,1] to [30,230] + queue_try_add(&audioAppElysiamorfs.bpmQueue, &bpm); + } + }, 0); // Set threshold to 0 to trigger on any change + + } + + __force_inline void loop() { + audioAppElysiamorfs.loop(); + } + + std::shared_ptr midi_interf; + + void setupMIDI(std::shared_ptr new_midi_interf) { + midi_interf = new_midi_interf; + midi_interf->Setup(0); + // midi_interf->SetMIDISendChannel(1); + // midi_interf->SetMIDINoteChannel(10); + midi_interf->SetNoteCallback([this](bool noteon, uint8_t note_number, uint8_t vel_value) { + // Serial.printf("MIDI Note %d: %d %d\n", note_number, vel_value, noteon); + if (note_number==0) { + int resetTrigger=1; + queue_try_add(&audioAppElysiamorfs.barPhaseResetQueue, &resetTrigger); // value doesn't matter, just a trigger + Serial.println("Bar phase reset triggered by MIDI note 0"); + + } + }); + midi_interf->SetBPMCallback([this](float bpm) { + if (clockMode == ElysiamorfAudioApp<>::MIDI_CLOCK) { + queue_try_add(&audioAppElysiamorfs.bpmQueue, &bpm); + } + + }); + interface.bindMIDI(midi_interf); + } + + void addViews() { + }; + + inline void processAnalysisParams() {} + + void analyse(stereosample_t) {} + + float i2cValues[8]; + void loopCore0() { + // PERIODIC_RUN_US( + // if (queue_try_remove(&audioAppElysiamorfs.i2cOutQueue, &i2cValues)) { + // // Serial.printf("i2c received: %f %f %f %f %f %f %f %f\n", i2cValues[0], i2cValues[1], i2cValues[2], i2cValues[3], i2cValues[4], i2cValues[5], i2cValues[6], i2cValues[7]); + // bool i2cSuccess = i2cOut.sendValues(i2cValues, 8); + // } + // , 5000) + + } + +}; + + + +#endif // MEMLNAUT_MODE_ELYSIAMORFS_HPP From 5d38e36e0e5f564c136d7d8cd16b8c86c84e58b1 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Sat, 7 Mar 2026 15:14:53 +0000 Subject: [PATCH 13/15] memlib --- src/memllib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memllib b/src/memllib index 12842a8..52b445b 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 12842a8ae7e7a9d93976fb1291f21f162381b2f4 +Subproject commit 52b445b3b252ca42c3f27cc3ade199e1f21ba654 From 6c66802b4e7cf1b58a243a6f932c2db7638fd73e Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 18 Mar 2026 16:29:54 +0000 Subject: [PATCH 14/15] verbfx freeverb --- MEMLNaut-NISPS.ino | 9 +- modes/AudioApps/ElysiamorfAudioApp.hpp | 6 +- modes/AudioApps/VerbFXAudioApp.hpp | 134 +++++++++++++------------ modes/MEMLNautModeElysiamorfs.hpp | 2 +- modes/MEMLNautModeVerbFX.hpp | 34 +++---- 5 files changed, 97 insertions(+), 88 deletions(-) diff --git a/MEMLNaut-NISPS.ino b/MEMLNaut-NISPS.ino index 3400ce1..b50b9c0 100644 --- a/MEMLNaut-NISPS.ino +++ b/MEMLNaut-NISPS.ino @@ -1,3 +1,8 @@ +//machine config + +#define JOYSTICK_IS_4D false +#define MEMLNAUT_ANALOG_INPUTS 3 + (JOYSTICK_IS_4D ? 1 : 0) +#define MEMLNAUT_INPUT_MODE InterfaceRL::INPUT_MODES::JOYSTICK //hardware #include "src/memllib/utils/perf.hpp" @@ -31,9 +36,9 @@ // #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI // #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI -// #define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX +#define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX // #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr -#define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs +// #define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs // #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip // #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth diff --git a/modes/AudioApps/ElysiamorfAudioApp.hpp b/modes/AudioApps/ElysiamorfAudioApp.hpp index 7913822..d43cbd4 100644 --- a/modes/AudioApps/ElysiamorfAudioApp.hpp +++ b/modes/AudioApps/ElysiamorfAudioApp.hpp @@ -213,7 +213,7 @@ public: firstParamsReceived = true; if (queue_try_remove(&bpmQueue, &bpm)) { updateBPM(bpm); - Serial.printf("BPM updated: %f\n", bpm); + // Serial.printf("BPM updated: %f\n", bpm); } int sequencerControl; if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) { @@ -263,8 +263,8 @@ public: v.modFreq = (0.25f + params[paramIdx++] * 0.75f) * 0.25f; // 1 to 10 v.modIndex = params[paramIdx++] * 4.f; // 0 to 10 v.fbLevel = 0.f;//params[paramIdx++] ; // 0 to 1 - static float muls[4] = {1.f}; - v.phasorMul = 1.f;//muls[(int)(params[paramIdx++])]; + static float muls[4] = {1.f,2.f, 3.f,4.f}; + v.phasorMul = muls[(int)(params[paramIdx++]* 3.999f) ]; v.phaseOffset = ((int)(params[paramIdx++] * timeSigBeats)) * timeSigBeatsInv; } // Serial.printf("pm: %f", ratioSeqStates[0].phasorMul); diff --git a/modes/AudioApps/VerbFXAudioApp.hpp b/modes/AudioApps/VerbFXAudioApp.hpp index 735f0ac..eb4f0d2 100644 --- a/modes/AudioApps/VerbFXAudioApp.hpp +++ b/modes/AudioApps/VerbFXAudioApp.hpp @@ -52,72 +52,65 @@ 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()); - 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 allp1fb = smoothParams[4] * 0.99f; + // float allp2fb = smoothParams[5] * 0.99f; + // float allp3fb = smoothParams[14] * 0.99f; - 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; + wetdry_mix_ = (smoothParams[0] * 0.9f) + 0.1f; + const float lp0fb = smoothParams[1] * 0.98f; + const float lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f; - 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]; + const float lp1fb = smoothParams[3] * 0.98f; + const float lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f; - // // PROCESS - float pitchshifted = pitchshifter_.Process(mix); - // // Mix the original signal with the pitch-shifted signal - pitchshifted = (mix * (1.f - pitchshifter_mix_)) + (pitchshifted * pitchshifter_mix_); + const float lp2fb = smoothParams[5] * 0.98f; + const float lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f; - 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); + const float lp3fb = smoothParams[7] * 0.98f; + const float lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f; - float y2 = allp2.allpass(y, 482, allp2fb); - y2 = comb2.combfb(y2, 808, comb2fb); + const float lp4fb = smoothParams[9] * 0.98f; + const float lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f; - 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, 7886, dl2fb) * dl2mix); - float d3 = (dl3.play(y, 299, dl3fb) * dl3mix); - float d4 = (dl4.play(y, 15873, dl4fb) * dl4mix); + const float lp5fb = smoothParams[11] * 0.98f; + const float lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f; + + const float lp6fb = smoothParams[13] * 0.98f; + const float lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f; + + const float lp7fb = smoothParams[15] * 0.98f; + const float lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f; + + float lp0 = lpcomb0.lpcombfb(mix, SIZE_comb0, lp0fb, lp0cutoff); + float lp1 = lpcomb1.lpcombfb(mix, SIZE_comb1, lp1fb, lp1cutoff); + float lp2 = lpcomb2.lpcombfb(mix, SIZE_comb2, lp2fb, lp2cutoff); + float lp3 = lpcomb3.lpcombfb(mix, SIZE_comb3, lp3fb, lp3cutoff); + float lp4 = lpcomb4.lpcombfb(mix, SIZE_comb4, lp4fb, lp4cutoff); + float lp5 = lpcomb5.lpcombfb(mix, SIZE_comb5, lp5fb, lp5cutoff); + float lp6 = lpcomb6.lpcombfb(mix, SIZE_comb6, lp6fb, lp6cutoff); + float lp7 = lpcomb7.lpcombfb(mix, SIZE_comb7, lp7fb, lp7cutoff); + + float y = lp0 + lp1 + lp2 + lp3 + lp4 + lp5 + lp6 + lp7; - y = y + d1 + d2 + d3; + const float allp0fb = smoothParams[17] * 0.98f; + const float allp1fb = smoothParams[18] * 0.98f; + const float allp2fb = smoothParams[19] * 0.98f; + const float allp3fb = smoothParams[20] * 0.98f; + + y = allp0.allpass(y, SIZE_allp0, allp0fb); + y = allp1.allpass(y, SIZE_allp1, allp1fb); + y = allp2.allpass(y, SIZE_allp2, allp2fb); + y = allp3.allpass(y, SIZE_allp3, allp3fb); + // Mix dry y = (y * wetdry_mix_) + (mix * (1.f - wetdry_mix_)); - y = tanhf(y*1.2f); stereosample_t ret { y, y }; return ret; @@ -127,7 +120,6 @@ public: { AudioAppBase::Setup(sample_rate, interface); maxiSettings::sampleRate = sample_rate; - pitchshifter_.Init(sample_rate); } __attribute__((always_inline)) void ProcessParams(const std::array& params) @@ -141,25 +133,37 @@ protected: std::array neuralNetOutputs{0}, smoothParams{0}; + // https://ccrma.stanford.edu/~jos/pasp/Freeverb.html + static constexpr size_t SIZE_allp0=244; + static constexpr size_t SIZE_allp1=605; + static constexpr size_t SIZE_allp2=479; + static constexpr size_t SIZE_allp3=371; - maxiDelayline<5000> dl1; - maxiDelayline<8000> dl2; - maxiDelayline<1201> dl3; - maxiDelayline<16000> dl4; + maxiReverbFilters allp0; + maxiReverbFilters allp1; + maxiReverbFilters allp2; + maxiReverbFilters allp3; - maxiReverbFilters<300> allp1; - maxiReverbFilters<500> allp2; - maxiReverbFilters<500> allp3; - maxiReverbFilters<500> allp4; - maxiReverbFilters<500> allp5; - maxiReverbFilters<500> allp6; - maxiReverbFilters<200> comb1; - maxiReverbFilters<900> comb2; + static constexpr size_t SIZE_comb0=1694; + static constexpr size_t SIZE_comb1=1759; + static constexpr size_t SIZE_comb2=1622; + static constexpr size_t SIZE_comb3=1547; + static constexpr size_t SIZE_comb4=1379; + static constexpr size_t SIZE_comb5=1464; + static constexpr size_t SIZE_comb6=1283; + static constexpr size_t SIZE_comb7=1205; + + maxiReverbFilters lpcomb0; + maxiReverbFilters lpcomb1; + maxiReverbFilters lpcomb2; + maxiReverbFilters lpcomb3; + maxiReverbFilters lpcomb4; + maxiReverbFilters lpcomb5; + maxiReverbFilters lpcomb6; + maxiReverbFilters lpcomb7; maxiDCBlocker dcb; - daisysp::PitchShifter pitchshifter_; - float pitchshifter_mix_{0.5f}; float wetdry_mix_{0.5f}; OnePoleSmoother smoother{150.f, kSampleRate}; diff --git a/modes/MEMLNautModeElysiamorfs.hpp b/modes/MEMLNautModeElysiamorfs.hpp index 5d6f6f8..60b6a7c 100644 --- a/modes/MEMLNautModeElysiamorfs.hpp +++ b/modes/MEMLNautModeElysiamorfs.hpp @@ -25,7 +25,7 @@ public: InterfaceRL interface; std::shared_ptr interfacePtr; - ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::MIDI_CLOCK; + ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::INTERNAL; bool sequencerPlaying = false; diff --git a/modes/MEMLNautModeVerbFX.hpp b/modes/MEMLNautModeVerbFX.hpp index a58d284..696687b 100644 --- a/modes/MEMLNautModeVerbFX.hpp +++ b/modes/MEMLNautModeVerbFX.hpp @@ -15,18 +15,18 @@ class MEMLNautModeVerbFX { public: - constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params +4; //ML + joystick + constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS; InterfaceRL interface; std::shared_ptr interfacePtr; - XiasriAnalysis mlAnalysis{kSampleRate}; - SharedBuffer machine_list_buffer; + // XiasriAnalysis mlAnalysis{kSampleRate}; + // SharedBuffer machine_list_buffer; VerbFXAudioApp<> audioAppVerbFX; std::shared_ptr midi_interf; void setupInterface() { interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params); - interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK_AND_MACHINE_LISTENING); + interface.bindInterface(MEMLNAUT_INPUT_MODE, JOYSTICK_IS_4D); interfacePtr = make_non_owning(interface); } @@ -51,7 +51,7 @@ public: void setupAudio(float sample_rate) { audioAppVerbFX.Setup(sample_rate, interfacePtr); // Reinitialize XiasriAnalysis filters after maxiSettings is properly configured - mlAnalysis.ReinitFilters(); + // mlAnalysis.ReinitFilters(); } __force_inline void loop() { @@ -59,21 +59,21 @@ public: } __force_inline void analyse(stereosample_t x) { - union { - XiasriAnalysis::parameters_t p; - float v[XiasriAnalysis::kN_Params]; - } param_u; - param_u.p = mlAnalysis.Process(x.L + x.R); - // Write params into shared_buffer - machine_list_buffer.writeNonBlocking(param_u.v, XiasriAnalysis::kN_Params); + // union { + // XiasriAnalysis::parameters_t p; + // float v[XiasriAnalysis::kN_Params]; + // } param_u; + // param_u.p = mlAnalysis.Process(x.L + x.R); + // // Write params into shared_buffer + // machine_list_buffer.writeNonBlocking(param_u.v, XiasriAnalysis::kN_Params); } __force_inline void processAnalysisParams() { - // Read SharedBuffer - std::vector mlist_params(XiasriAnalysis::kN_Params, 0); - machine_list_buffer.readNonBlocking(mlist_params); - // Send parameters to RL interface - interface.readAnalysisParameters(mlist_params); + // // Read SharedBuffer + // std::vector mlist_params(XiasriAnalysis::kN_Params, 0); + // machine_list_buffer.readNonBlocking(mlist_params); + // // Send parameters to RL interface + // interface.readAnalysisParameters(mlist_params); // PERIODIC_RUN( // Serial.printf("%f %f %f\n", mlist_params[0], mlist_params[1], mlist_params[2]); // , 100); From ecacfb9d0c93274fcc2d24973d9bc2ef62778900 Mon Sep 17 00:00:00 2001 From: chriskiefer Date: Wed, 18 Mar 2026 16:30:19 +0000 Subject: [PATCH 15/15] memlib --- src/memllib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memllib b/src/memllib index 52b445b..5d67d15 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 52b445b3b252ca42c3f27cc3ade199e1f21ba654 +Subproject commit 5d67d157228572f458b2744296c3e9585289187d