Merge branch 'MusicallyEmbodiedML:main' into main

This commit is contained in:
w1n5t0n 2026-03-22 02:26:50 +02:00 committed by GitHub
commit cf44abed9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1184 additions and 34 deletions

View file

@ -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"
@ -23,12 +28,18 @@
#include "modes/MEMLNautModeChannelStrip.hpp"
#include "modes/MEMLNautModeSoundAnalysisMIDI.hpp"
#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 MEMLNautModeChannelStrip
#define MEMLNAUT_MODE_TYPE MEMLNautModeVerbFX
// #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr
// #define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs
// #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip
// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth
MEMLNAUT_MODE_TYPE AUDIO_MEM MEMLNautModeHub;
@ -86,6 +97,9 @@ void setup() {
uint32_t seed = get_rosc_entropy_seed(32);
srand(seed);
midi_interf = std::make_shared<MIDIInOut>();
// Serial.println("MIDI setup complete.");
Serial.begin(115200);
// while (!Serial) {}
Serial.println("Serial initialised.");
@ -101,11 +115,6 @@ void setup() {
WRITE_VOLATILE(interface_ready, true);
Serial.println("Bound interface to MEMLNaut.");
midi_interf = std::make_shared<MIDIInOut>();
Serial.println("MIDI setup complete.");
if (midi_interf) {
currentMode->setupMIDI(midi_interf);
}
WRITE_VOLATILE(core_0_ready, true);
while (!READ_VOLATILE(core_1_ready)) {
@ -162,6 +171,8 @@ void loop() {
digitalWrite(33, LOW);
},
100000)
currentMode->loopCore0();
}
@ -200,6 +211,9 @@ void setup1() {
delay(1);
}
if (midi_interf) {
currentMode->setupMIDI(midi_interf);
}
currentMode->setupAudio(AudioDriver::GetSampleRate());
@ -226,5 +240,5 @@ void loop1() {
PERIODIC_RUN_US(
midi_interf->Poll();
, 10000)
, 1000)
}

View file

@ -0,0 +1,266 @@
#ifndef __BREAKOR_AUDIO_APP_HPP__
#define __BREAKOR_AUDIO_APP_HPP__
#include "../../src/memllib/audio/AudioAppBase.hpp"
#include "../../src/memllib/synth/maximilian.h"
#include "../../src/memllib/interface/MIDIInOut.hpp"
#include <cstddef>
#include <cstdint>
#include <memory> // Added for std::shared_ptr
#include "../../src/memllib/interface/InterfaceBase.hpp"
#include <span>
#include "../../voicespaces/VoiceSpaces.hpp"
#include "RatioSeq.hpp"
template<size_t NPARAMS=56, size_t NSEQUENCES=8>
class BreakOrAudioApp : public AudioAppBase<NPARAMS>
{
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<MIDIInOut> midiIO;
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
std::array<String, nVoiceSpaces> getVoiceSpaceNames() {
std::array<String, nVoiceSpaces> 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<NPARAMS>() {
// 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<int>(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;
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) {
if (trig) {
midiIO->queueNoteOn(seq.midiNote, highAmp ? 127 : 64);
}else{
midiIO->queueNoteOff(seq.midiNote, 0);
}
}
seq.lastTrig = trig;
i2cValues[i2cIdx++] = trig;
}
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<InterfaceBase> interface, SequencerClockModes clockMode)
{
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
maxiSettings::sampleRate = sample_rate;
sampleRatef = static_cast<float>(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;
for(auto &seq: ratioSeqStates) {
seq.midiNote = midiNotes[midiNote++];
}
}
void setupMIDI(std::shared_ptr<MIDIInOut> new_midi_interf) {
midiIO = new_midi_interf;
}
void loop() override {
AudioAppBase<NPARAMS>::loop();
}
void ProcessParams(const std::array<float, NPARAMS>& 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]
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();
}
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;
}
// 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 = 400;
size_t sequencingSampleCounter = 0;
std::array<ratioSeqState, NSEQUENCES> ratioSeqStates;
float midiClockPhasor=0;
float midiClockPhasorInc=0;
};
#endif // __BREAKOR_AUDIO_APP_HPP__

View file

@ -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 <cstddef>
#include <cstdint>
#include <memory> // Added for std::shared_ptr
#include "../../src/memllib/interface/InterfaceBase.hpp"
#include <span>
#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<size_t NPARAMS=56, size_t NSEQUENCES=8>
class ElysiamorfAudioApp : public AudioAppBase<NPARAMS>
{
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<MIDIInOut> midiIO;
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
std::array<String, nVoiceSpaces> getVoiceSpaceNames() {
std::array<String, nVoiceSpaces> 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<NPARAMS>() {
// 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<int>(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<uint8_t>(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<InterfaceBase> interface, SequencerClockModes clockMode)
{
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
maxiSettings::sampleRate = sample_rate;
sampleRatef = static_cast<float>(sample_rate);
sequencerClockMode = clockMode;
updateBPM(90.f);
}
void setupMIDI(std::shared_ptr<MIDIInOut> new_midi_interf) {
midiIO = new_midi_interf;
}
void loop() override {
AudioAppBase<NPARAMS>::loop();
}
void ProcessParams(const std::array<float, NPARAMS>& 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,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);
}
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<ratioSeqState, NSEQUENCES> ratioSeqStates;
std::array<fmSeqState, NSEQUENCES> fmSeqStates;
float midiClockPhasor=0;
float midiClockPhasorInc=0;
};
#endif // __ELYSIAMORF_AUDIO_APP_HPP__

View file

@ -0,0 +1,49 @@
#ifndef __RATIO_SEQ_HPP__
#define __RATIO_SEQ_HPP__
#include <array>
#include <cstddef>
struct ratioSeqState {
std::array<float, 3> 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<float, 2> ampRatios{1.f};
float ampRatioSum=1.f;
};
template<size_t seqLength>
inline bool __not_in_flash_func(ratioSeq)(float phasor, float phaseOffset, float ratioSum, const std::array<float, seqLength> &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__

View file

@ -0,0 +1,173 @@
#ifndef __VERBFX_AUDIO_APP_HPP__
#define __VERBFX_AUDIO_APP_HPP__
#include "../../src/memllib/audio/AudioAppBase.hpp" // Added missing include
#include <cstddef>
#include <cstdint>
#include <memory>
//#include "src/memllib/interface/InterfaceBase.hpp" // Added missing include
#include <span>
#include "../../voicespaces/VoiceSpaces.hpp"
#include "../../src/memllib/synth/OnePoleSmoother.hpp"
#include "../../src/memllib/synth/maximilian.h"
#include "../../src/daisysp/Effects/pitchshifter.h"
template<size_t NPARAMS=24>
class VerbFXAudioApp : public AudioAppBase<NPARAMS>
{
public:
static constexpr size_t kN_Params = NPARAMS;
static constexpr size_t nVoiceSpaces=1;
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
std::array<String, nVoiceSpaces> getVoiceSpaceNames() {
std::array<String, nVoiceSpaces> 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<NPARAMS>() {
};
__attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override
{
float mix = x.L + x.R;
smoother.Process(neuralNetOutputs.data(), smoothParams.data());
// float allp1fb = smoothParams[4] * 0.99f;
// float allp2fb = smoothParams[5] * 0.99f;
// float allp3fb = smoothParams[14] * 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;
const float lp1fb = smoothParams[3] * 0.98f;
const float lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;
const float lp2fb = smoothParams[5] * 0.98f;
const float lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;
const float lp3fb = smoothParams[7] * 0.98f;
const float lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;
const float lp4fb = smoothParams[9] * 0.98f;
const float lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;
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;
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_));
stereosample_t ret { y, y };
return ret;
}
void Setup(float sample_rate, std::shared_ptr<InterfaceBase> interface) override
{
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
maxiSettings::sampleRate = sample_rate;
}
__attribute__((always_inline)) void ProcessParams(const std::array<float, NPARAMS>& params)
{
// currentVoiceSpace(params);
neuralNetOutputs = params;
}
protected:
std::array<float,NPARAMS> 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;
maxiReverbFilters<SIZE_allp0> allp0;
maxiReverbFilters<SIZE_allp1> allp1;
maxiReverbFilters<SIZE_allp2> allp2;
maxiReverbFilters<SIZE_allp3> allp3;
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<SIZE_comb0> lpcomb0;
maxiReverbFilters<SIZE_comb1> lpcomb1;
maxiReverbFilters<SIZE_comb2> lpcomb2;
maxiReverbFilters<SIZE_comb3> lpcomb3;
maxiReverbFilters<SIZE_comb4> lpcomb4;
maxiReverbFilters<SIZE_comb5> lpcomb5;
maxiReverbFilters<SIZE_comb6> lpcomb6;
maxiReverbFilters<SIZE_comb7> lpcomb7;
maxiDCBlocker dcb;
float wetdry_mix_{0.5f};
OnePoleSmoother<kN_Params> smoother{150.f, kSampleRate};
};
#endif

View file

@ -23,5 +23,6 @@ concept MEMLNautMode = requires(T proc) {
{proc.processAnalysisParams()} -> std::same_as<void>;
// {proc.getNMIDICtrlOutputs()} -> std::same_as<size_t>;
{proc.setupInterface()} -> std::same_as<void>;
{proc.loopCore0()} -> std::same_as<void>;
requires std::same_as<decltype(T::kN_InputParams), const size_t>;
};

View file

@ -0,0 +1,132 @@
#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 <memory>
#include <array>
#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<String, BreakOrAudioApp<>::nVoiceSpaces> voiceSpaceList;
InterfaceRL interface;
std::shared_ptr<InterfaceRL> 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);
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);
}
});
i2cOut.begin();
}
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, clockMode);
voiceSpaceList = audioAppBreakOr.getVoiceSpaceNames();
audioAppBreakOr.setupMIDI(midi_interf);
MEMLNaut::Instance()->setRVGain1Callback([this](float value) {
if (clockMode == BreakOrAudioApp<>::INTERNAL) {
queue_try_add(&audioAppBreakOr.bpmQueue, &value);
}
}, 0); // Set threshold to 0 to trigger on any change
}
__force_inline void loop() {
audioAppBreakOr.loop();
}
std::shared_ptr<MIDIInOut> midi_interf;
void setupMIDI(std::shared_ptr<MIDIInOut> 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(&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);
}
void addViews() {
// std::shared_ptr<VoiceSpaceSelectView> voiceSpaceSelectView;
// voiceSpaceSelectView = std::make_shared<VoiceSpaceSelectView>("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) {}
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)
}
};
#endif // MEMLNAUT_MODE_RHYTHM_BOX_HPP

View file

@ -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<String> getVoiceSpaceList() {
// return voiceSpaceList;
// }
__force_inline stereosample_t process(stereosample_t x) {
return audioAppChannelStrip.Process(x);
@ -116,5 +105,6 @@ public:
void analyse(stereosample_t) {}
void loopCore0() {}
};

View file

@ -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 <memory>
#include <array>
#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<String, ElysiamorfAudioApp<>::nVoiceSpaces> voiceSpaceList;
InterfaceRL interface;
std::shared_ptr<InterfaceRL> interfacePtr;
ElysiamorfAudioApp<>::SequencerClockModes clockMode = ElysiamorfAudioApp<>::INTERNAL;
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<MIDIInOut> midi_interf;
void setupMIDI(std::shared_ptr<MIDIInOut> 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

View file

@ -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<String, PAFSynthAudioApp<>::nVoiceSpaces> voiceSpaceList;
@ -22,24 +22,13 @@ 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);
}
String getHelpTitle() {
return "PAF Synth Mode";
}
// size_t getNParams() {
// return PAFSynthAudioApp<>::kN_Params;
// }
// void setVoiceSpace(size_t i) {
// audioAppPAFSynth.setVoiceSpace(i);
// }
// std::span<String> getVoiceSpaceList() {
// return voiceSpaceList;
// }
__force_inline stereosample_t process(stereosample_t x) {
return audioAppPAFSynth.Process(x);
@ -126,4 +115,6 @@ public:
void analyse(stereosample_t) {}
void loopCore0() {}
};

View file

@ -85,4 +85,6 @@ public:
}
void loopCore0() {}
};

View file

@ -0,0 +1,85 @@
#pragma once
#include "../src/memllib/interface/MIDIInOut.hpp"
#include "./AudioApps/VerbFXAudioApp.hpp"
#include "MEMLNautMode.hpp"
#include <memory>
#include <array>
#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 = MEMLNAUT_ANALOG_INPUTS;
InterfaceRL interface;
std::shared_ptr<InterfaceRL> interfacePtr;
// XiasriAnalysis mlAnalysis{kSampleRate};
// SharedBuffer<float, XiasriAnalysis::kN_Params> machine_list_buffer;
VerbFXAudioApp<> audioAppVerbFX;
std::shared_ptr<MIDIInOut> midi_interf;
void setupInterface() {
interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params);
interface.bindInterface(MEMLNAUT_INPUT_MODE, JOYSTICK_IS_4D);
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<MIDIInOut> 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<float> 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);
}
void loopCore0() {}
};

View file

@ -80,4 +80,6 @@ public:
}
void loopCore0() {}
};

@ -1 +1 @@
Subproject commit 799130e740a392839587f24c3747b27e841957b1
Subproject commit 5d67d157228572f458b2744296c3e9585289187d

@ -1 +1 @@
Subproject commit 83f5a84089d4a61604c6c429738c3f68f67bd103
Subproject commit dd1e1f36eac66f95f14daad48ad9def2e4c5f3ec