diff --git a/MEMLNaut-PAF-IML.ino b/MEMLNaut-PAF-IML.ino index b56ba8a..beb9642 100644 --- a/MEMLNaut-PAF-IML.ino +++ b/MEMLNaut-PAF-IML.ino @@ -1,4 +1,5 @@ // #include "src/memllib/interface/InterfaceBase.hpp" +#include "src/memllib/interface/MIDIInOut.hpp" #include "display.hpp" #include "src/memllib/audio/AudioAppBase.hpp" #include "src/memllib/audio/AudioDriver.hpp" @@ -32,6 +33,9 @@ uint32_t get_rosc_entropy_seed(int bits) { // Global objects std::shared_ptr APP_SRAM RLInterface; +std::shared_ptr APP_SRAM midi_interf; + + std::shared_ptr __scratch_y("audio") audio_app; // Inter-core communication @@ -41,6 +45,7 @@ volatile bool APP_SRAM serial_ready = false; volatile bool APP_SRAM interface_ready = false; + // We're only bound to the joystick inputs (x, y, rotate) constexpr size_t kN_InputParams = 3; @@ -87,6 +92,22 @@ void setup() RLInterface->bind_RL_interface(scr); Serial.println("Bound RL interface to MEMLNaut."); + midi_interf = std::make_shared(); + midi_interf->Setup(0); + midi_interf->SetMIDISendChannel(1); + Serial.println("MIDI setup complete."); + if (midi_interf) { + midi_interf->SetNoteCallback([RLInterface] (bool noteon, uint8_t note_number, uint8_t vel_value) { + if (noteon) { + uint8_t midimsg[2] = {note_number, vel_value }; + queue_try_add(&audio_app->qMIDINoteOn, &midimsg); + } + Serial.printf("MIDI Note %d: %d\n", note_number, vel_value); + }); + Serial.println("MIDI note callback set."); + } + + WRITE_VOLATILE(core_0_ready, true); while (!READ_VOLATILE(core_1_ready)) { @@ -115,6 +136,7 @@ void loop() // Un-blink LED digitalWrite(33, LOW); } + midi_interf->Poll(); delay(10); // Add a small delay to avoid flooding the serial output } diff --git a/PAFSynthAudioApp.cpp b/PAFSynthAudioApp.cpp index 4886469..35a2cbc 100644 --- a/PAFSynthAudioApp.cpp +++ b/PAFSynthAudioApp.cpp @@ -23,10 +23,10 @@ stereosample_t PAFSynthAudioApp::Process(const stereosample_t x) // const float trig = pulse.square(1); - paf0.play(x1, 1, arpFreq, arpFreq + (paf0_cf * arpFreq), paf0_bw * arpFreq, paf0_vib, paf0_vfr, paf0_shift, 0); + paf0.play(x1, 1, baseFreq, baseFreq + (paf0_cf * baseFreq), paf0_bw * baseFreq, paf0_vib, paf0_vfr, paf0_shift, 0); float y = x1[0]; - const float freq1 = arpFreq * detune; + const float freq1 = baseFreq * detune; paf1.play(x1, 1, freq1, freq1 + (paf1_cf * freq1), paf1_bw * freq1, paf1_vib, paf1_vfr, paf1_shift, 1); y += x1[0]; @@ -36,18 +36,20 @@ stereosample_t PAFSynthAudioApp::Process(const stereosample_t x) paf2.play(x1, 1, freq2, freq2 + (paf2_cf * freq2), paf2_bw * freq2, paf2_vib, paf2_vfr, paf2_shift, 1); y += x1[0]; - const float ph = phasorOsc.phasor(1); - const bool euclidNewNote = euclidean(ph, 12, euclidN, 0, 0.1f); + // const float ph = phasorOsc.phasor(1); + // const bool euclidNewNote = euclidean(ph, 12, euclidN, 0, 0.1f); // y = y * 0.3f; // const float envamp = env.play(counter==0); // const float envamp = line.play(counter==0); - if(zxdetect.onZX(euclidNewNote)) { - envamp=0.2f; - freqIndex++; - if(freqIndex >= 4) { - freqIndex = 0; - } - arpFreq = frequencies[freqIndex]; + if(newNote) { + // if(zxdetect.onZX(euclidNewNote)) { + newNote = false; + envamp=0.8f; + // freqIndex++; + // if(freqIndex >= 4) { + // freqIndex = 0; + // } + // arpFreq = frequencies[freqIndex]; }else{ constexpr float envdec = 0.2f/9000.f; envamp -= envdec; @@ -71,6 +73,8 @@ stereosample_t PAFSynthAudioApp::Process(const stereosample_t x) // Serial.println(envamp); // }) + y *= noteVel; + float d1 = (dl1.play(y, 3500, 0.8f) * dl1mix); // float d2 = (dl2.play(y, 15000, 0.8f) * dl2mix); y = y + d1;// + d2; @@ -83,6 +87,8 @@ void PAFSynthAudioApp::Setup(float sample_rate, std::shared_ptr i { AudioAppBase::Setup(sample_rate, interface); maxiSettings::sampleRate = sample_rate; + + paf0.init(); paf0.setsr(maxiSettings::getSampleRate(), 1); // paf0.freq(100, 0); @@ -118,10 +124,32 @@ void PAFSynthAudioApp::Setup(float sample_rate, std::shared_ptr i // line.prepare(1.f,0.f,100.f,false); // line.triggerEnable(true); envamp=1.f; + + queue_init(&qMIDINoteOn, sizeof(uint8_t)*2, 1); + } +float mtof(uint8_t note) { + // Convert MIDI note to frequency + return 440.0f * powf(2.0f, (note - 69) / 12.0f); +} + +void PAFSynthAudioApp::loop(){ + AudioAppBase::loop(); + uint8_t midimsg[2]; + if (firstParamsReceived && queue_try_remove(&qMIDINoteOn, &midimsg)) { + // Serial.printf("PAFSynthAudioApp::ProcessParams - Received MIDI Note On: %d, Velocity: %d\n", midimsg[0], midimsg[1]); + baseFreq = mtof(midimsg[0]); + noteVel = midimsg[1] / 127.0f; // Normalize velocity to [0, 1] + noteVel = noteVel * noteVel; // Square the velocity for more pronounced effect + newNote = true; + } +} + + void PAFSynthAudioApp::ProcessParams(const std::vector& params) { + firstParamsReceived = true; // // Map parameters to the synth // synth_.mapParameters(params); // //Serial.print("Params processed."); diff --git a/PAFSynthAudioApp.hpp b/PAFSynthAudioApp.hpp index b29d171..7c23ecc 100644 --- a/PAFSynthAudioApp.hpp +++ b/PAFSynthAudioApp.hpp @@ -31,6 +31,9 @@ public: void ProcessParams(const std::vector& params) override; + queue_t qMIDINoteOn, qMIDINoteOff; + + void loop() override; protected: maxiPAFOperator paf0; @@ -79,7 +82,7 @@ protected: float arpFreq=50; maxiLine line; - float envamp; + float envamp=0.f; float detune = 1.0; @@ -88,6 +91,11 @@ protected: size_t euclidN=4; + float baseFreq = 50.0f; // Base frequency for the synth + bool newNote=false; + float noteVel = 0.f; + bool firstParamsReceived = false; + }; #endif // __PAF_SYNTH_AUDIO_APP_HPP__