diff --git a/MEMLNaut-PAF-IML.ino b/MEMLNaut-PAF-IML.ino index 129941b..df84c61 100644 --- a/MEMLNaut-PAF-IML.ino +++ b/MEMLNaut-PAF-IML.ino @@ -1,4 +1,5 @@ // #include "src/memllib/interface/InterfaceBase.hpp" +#include "display.hpp" #include "src/memllib/audio/AudioAppBase.hpp" #include "src/memllib/audio/AudioDriver.hpp" #include "src/memllib/hardware/memlnaut/MEMLNaut.hpp" @@ -10,7 +11,7 @@ #define APP_SRAM __not_in_flash("app") - +display APP_SRAM scr; bool core1_disable_systick = true; bool core1_separate_stack = true; @@ -30,26 +31,102 @@ uint32_t get_rosc_entropy_seed(int bits) { class PAFSynthApp : public AudioAppBase { public: - static constexpr size_t kN_Params = 17; + static constexpr size_t kN_Params = 21; PAFSynthApp() : AudioAppBase() {} + bool 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; + } + + // // NOTE: Phasor is the last arg + // const double phasor = args.back().as_float(); + // const int n = args[0].as_int(); + // const int k = args[1].as_int(); + // const int offset = (args.size() >= 4) ? args[2].as_int() : 0; + // const float pulseWidth = (args.size() == 5) ? args[3].as_float() : 0.5; + // const float fi = phasor * n; + // int i = static_cast(fi); + // const float rem = fi - i; + // if (i == n) + // { + // i--; + // } + // const int idx = ((i + n - offset) * k) % n; + // result = Value(idx < k && rem < pulseWidth ? 1 : 0); + + // return result; + // } + + + stereosample_t __force_inline Process(const stereosample_t x) override { float x1[1]; - paf0.play(x1, 1, paf0_freq, paf0_cf, paf0_bw, paf0_vib, paf0_vfr, paf0_shift, 0); + // 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); float y = x1[0]; - paf1.play(x1, 1, paf1_freq, paf1_cf, paf1_bw, paf1_vib, paf1_vfr, paf1_shift, 1); + const float freq1 = arpFreq * detune; + + paf1.play(x1, 1, freq1, freq1 + (paf1_cf * freq1), paf1_bw * freq1, paf1_vib, paf1_vfr, paf1_shift, 1); y += x1[0]; - paf2.play(x1, 1, paf2_freq, paf2_cf, paf2_bw, paf2_vib, paf2_vfr, paf2_shift, 1); + const float freq2 = freq1 * detune; + + 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); + // 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]; + }else{ + constexpr float envdec = 0.2f/9000.f; + envamp -= envdec; + if (envamp < 0.f) { + envamp = 0.f; + } + } + // PERIODIC_DEBUG(3000, Serial.println(y);) + y = y * envamp* envamp; + // counter++; + // if(counter>=9000) { + // counter=0; + // freqIndex++; + // if(freqIndex >= 4) { + // freqIndex = 0; + // } + // arpFreq = frequencies[freqIndex]; + // } - y = y * 0.3f; + // PERIODIC_DEBUG(10000, { + // Serial.println(envamp); + // }) + float d1 = (dl1.play(y, 3500, 0.8f) * dl1mix); + // float d2 = (dl2.play(y, 15000, 0.8f) * dl2mix); + y = y + d1;// + d2; stereosample_t ret { y, y }; frame++; return ret; @@ -58,6 +135,7 @@ public: void Setup(float sample_rate, std::shared_ptr interface) override { AudioAppBase::Setup(sample_rate, interface); + maxiSettings::sampleRate = sample_rate; paf0.init(); paf0.setsr(maxiSettings::getSampleRate(), 1); // paf0.freq(100, 0); @@ -87,6 +165,12 @@ public: // paf2.vfr(5,0); // paf2.vib(0.1,0); // paf2.shift(6,0); + + env.setupAR(10,100); + arpFreq = frequencies[0]; + // line.prepare(1.f,0.f,100.f,false); + // line.triggerEnable(true); + envamp=1.f; } void ProcessParams(const std::vector& params) override @@ -97,26 +181,38 @@ public: // paf0_freq = 50.f + (params[0] * params[0] * 1000.f); // paf1_freq = 50.f + (params[1] * params[1] * 1000.f); - paf0_cf = paf0_freq + (params[2] * params[2] * paf0_freq * 4.f); - paf1_cf = paf0_freq + (params[3] * params[3] * paf1_freq * 16.f); - paf2_cf = paf0_freq + (params[4] * params[4] * paf2_freq * 16.f); + // paf0_cf = arpFreq + (params[2] * params[2] * arpFreq * 1.f); + // paf1_cf = arpFreq + (params[3] * params[3] * arpFreq * 1.f); + // paf2_cf = arpFreq + (params[4] * params[4] * arpFreq * 1.f); + paf0_cf = (params[2] * params[2] * 1.f); + paf1_cf = (params[3] * params[3] * 1.f); + paf2_cf = (params[4] * params[4] * 1.f); - paf0_bw = 10.f + (params[5] * paf0_freq); - paf1_bw = 10.f + (params[6] * paf1_freq); - paf2_bw = 10.f + (params[7] * paf2_freq); + // paf0_bw = 5.f + (params[5] * arpFreq * 0.5f); + // paf1_bw = 5.f + (params[6] * arpFreq * 0.5f); + // paf2_bw = 5.f + (params[7] * arpFreq * 0.5f); + paf0_bw = 0.1f + (params[5] * 2.f); + paf1_bw = 0.1f + (params[6] * 2.f); + paf2_bw = 0.1f + (params[7] * 2.f); paf0_vib = (params[8] * params[8] * 0.99f); paf1_vib = (params[9] * params[9] * 0.99f); paf2_vib = (params[10] * params[10] * 0.99f); - paf0_vfr = (params[11] * params[11]* 15.f); - paf1_vfr = (params[12] * params[12] * 15.f); - paf2_vfr = (params[13] * params[13] * 15.f); + paf0_vfr = (params[11] * params[11]* 10.f); + paf1_vfr = (params[12] * params[12] * 10.f); + paf2_vfr = (params[13] * params[13] * 10.f); paf0_shift = (params[14] * 1000.f); paf1_shift = (params[15] * 1000.f); paf2_shift = (params[16] * 1000.f); + dl1mix = params[17] * params[17] * 0.4f; + // dl2mix = params[18] * params[18] * 0.4f; + detune = 1.0f + (params[18] * 0.1); + + euclidN = static_cast(2 + (params[19] * 5)); + // Serial.printf("%f %f %f %f %f\n", paf0_cf, paf0_bw, paf0_vib, paf0_vfr, paf0_shift); } @@ -127,11 +223,18 @@ protected: maxiPAFOperator paf1; maxiPAFOperator paf2; + maxiDelayline<5000> dl1; + maxiDelayline<15100> dl2; + + maxiOsc pulse; + maxiEnvGen env; + + float frame=0; float paf0_freq = 100; - float paf1_freq = 101; - float paf2_freq = 102; + float paf1_freq = 100; + float paf2_freq = 50; float paf0_cf = 200; float paf1_cf = 250; @@ -152,6 +255,27 @@ protected: float paf0_shift = 0; float paf1_shift = 0; float paf2_shift = 0; + + float dl1mix = 0.0f; + float dl2mix = 0.0f; + + size_t counter=0; + const size_t nFREQs = 17; + const float frequencies[nFREQs] = {100, 200, 400,800, 400, 800, 100,1600,100,400,100,50,1600,200,100,800,400}; + size_t freqIndex = 0; + size_t freqOffset = 0; + float arpFreq=50; + + maxiLine line; + float envamp; + + float detune = 1.0; + + maxiOsc phasorOsc; + maxiTrigger zxdetect; + + size_t euclidN=4; + }; @@ -183,20 +307,31 @@ void bind_RL_interface(std::shared_ptr interface) { // Set up momentary switch callbacks MEMLNaut::Instance()->setMomA1Callback([interface] () { + static APP_SRAM std::vector msgs = {"Wow, incredible", "Awesome", "That's amazing", "Unbelievable+","I love it!!","More of this","Yes!!!!","A-M-A-Z-I-N-G"}; + String msg = msgs[rand() % msgs.size()]; interface->storeExperience(1.f); - Serial.println("Incredible"); + Serial.println(msg); + + scr.post(msg); }); MEMLNaut::Instance()->setMomA2Callback([interface] () { + static APP_SRAM std::vector msgs = {"Awful!","wtf? that sucks","Get rid of this sound","Totally shite","I hate this","Why even bother?","New sound please!","No, please no!!!","Thumbs down"}; + String msg = msgs[rand() % msgs.size()]; interface->storeExperience(-1.f); - Serial.println("That sucks"); + Serial.println(msg); + scr.post(msg); }); MEMLNaut::Instance()->setMomB1Callback([interface] () { interface->randomiseTheActor(); + interface->generateAction(true); Serial.println("The Actor is confused"); + scr.post("Actor: i'm confused"); }); MEMLNaut::Instance()->setMomB2Callback([interface] () { interface->randomiseTheCritic(); + interface->generateAction(true); Serial.println("The Critic is confounded"); + scr.post("Critic: totally confounded"); }); // Set up ADC callbacks MEMLNaut::Instance()->setJoyXCallback([interface] (float value) { @@ -213,6 +348,15 @@ void bind_RL_interface(std::shared_ptr interface) AudioDriver::setDACVolume(value); }); + MEMLNaut::Instance()->setRVX1Callback([interface] (float value) { + size_t divisor = 1 + (value * 100); + String msg = "Optimise every " + String(divisor); + scr.post(msg); + interface->setOptimiseDivisor(divisor); + Serial.println(msg); + }); + + // Set up loop callback MEMLNaut::Instance()->setLoopCallback([interface] () { interface->optimiseSometimes(); @@ -269,10 +413,17 @@ void bind_IML_interface(std::shared_ptr interface) enum MLMODES {IML, RL}; MLMODES APP_SRAM mlMode = RL; + +struct repeating_timer APP_SRAM timerDisplay; +inline bool __not_in_flash_func(displayUpdate)(__unused struct repeating_timer *t) { + scr.update(); + return true; +} + void setup() { - + scr.setup(); bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_PROC1_BITS; @@ -280,7 +431,7 @@ void setup() srand(seed); Serial.begin(115200); - while (!Serial) {} + // while (!Serial) {} Serial.println("Serial initialised."); WRITE_VOLATILE(serial_ready, true); @@ -328,6 +479,9 @@ void setup() delay(1); } + scr.post("MEMLNaut: let's go!"); + add_repeating_timer_ms(-39, displayUpdate, NULL, &timerDisplay); + Serial.println("Finished initialising core 0."); } @@ -396,7 +550,7 @@ void loop1() { // Audio app parameter processing loop audio_app->loop(); - delay(10); + delay(1); } extern "C" int getentropy (void * buffer, size_t how_many) { diff --git a/display.hpp b/display.hpp new file mode 100644 index 0000000..fbef737 --- /dev/null +++ b/display.hpp @@ -0,0 +1,46 @@ +#ifndef __DISPLAY_H +#define __DISPLAY_H + +#include +#include + +#define DISPLAY_MEM __not_in_flash("display") + +TFT_eSPI DISPLAY_MEM tft = TFT_eSPI(); // Invoke custom library + + +class display { +public: + void setup() { + tft.begin(); + tft.setRotation(1); + tft.fillScreen(TFT_BLACK); + tft.setFreeFont(&FreeSans9pt7b); + tft.setTextColor(0xFC9F); + + } + + void post(String str) { + redraw = true; + lines.push_back(str); + if(lines.size() > 11) { + lines.pop_front(); + } + } + + void update() { + if (redraw) { + redraw = false; + tft.fillScreen(TFT_BLACK); + constexpr int32_t lineheight = 20; + for(size_t i=0; i < lines.size(); i++) { + tft.drawString(lines[i].c_str(), 10,i*lineheight); + } + } + } +private: + std::deque lines; + bool redraw=false; +}; + +#endif \ No newline at end of file diff --git a/interfaceRL.hpp b/interfaceRL.hpp index 5015af4..755ff8b 100644 --- a/interfaceRL.hpp +++ b/interfaceRL.hpp @@ -181,8 +181,8 @@ public: newInput = true; } - void generateAction() { - if (newInput) { + void generateAction(bool donthesitate=false) { + if (newInput || donthesitate) { newInput = false; std::vector actorOutput; actorTarget->GetOutput(actorControlInput, &actorOutput); @@ -198,7 +198,7 @@ public: } void optimiseSometimes() { - if (optimiseCounter==optimiseDivisor) { + if (optimiseCounter>=optimiseDivisor) { optimise(); optimiseCounter=0; }else{ @@ -230,6 +230,11 @@ public: criticTarget->DrawWeights(); } + void setOptimiseDivisor(size_t newDiv) { + optimiseDivisor = newDiv; + } + + private: static constexpr size_t bias=1; @@ -239,7 +244,7 @@ private: bool newInput=false; const std::vector layers_activfuncs = { - RELU, RELU, TANH + RELU, RELU, SIGMOID }; size_t stateSize; diff --git a/src/memllib b/src/memllib index 2897119..a204b9d 160000 --- a/src/memllib +++ b/src/memllib @@ -1 +1 @@ -Subproject commit 2897119b7ecc6c81b919dda2d77a357dc40b6567 +Subproject commit a204b9d4f1e362a278c96d62a2126d0c43ea7345