drum voice
This commit is contained in:
parent
c633b9c2ab
commit
d095688cd9
10 changed files with 808 additions and 132 deletions
|
|
@ -31,6 +31,7 @@
|
|||
#include "modes/MEMLNautModeBreakOr.hpp"
|
||||
#include "modes/MEMLNautModeVerbFX.hpp"
|
||||
#include "modes/MEMLNautModeElysiamorfs.hpp"
|
||||
#include "modes/MEMLNautModeMEMLCelium.hpp"
|
||||
|
||||
//hook up the memlnaut mode
|
||||
|
||||
|
|
@ -40,7 +41,8 @@
|
|||
// #define MEMLNAUT_MODE_TYPE MEMLNautModeBreakOr
|
||||
// #define MEMLNAUT_MODE_TYPE MEMLNautModeElysiamorfs
|
||||
// #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip
|
||||
#define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth
|
||||
// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth
|
||||
#define MEMLNAUT_MODE_TYPE MEMLNautModeMEMLCelium
|
||||
|
||||
MEMLNAUT_MODE_TYPE AUDIO_MEM MEMLNautModeHub;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory> // Added for std::shared_ptr
|
||||
#include <memory>
|
||||
|
||||
#include "../../src/memllib/interface/InterfaceBase.hpp"
|
||||
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include "../../voicespaces/VoiceSpaces.hpp"
|
||||
|
||||
#include "RatioSeq.hpp"
|
||||
#include "RatioSeqEngine.hpp"
|
||||
|
||||
|
||||
template<size_t NPARAMS=56, size_t NSEQUENCES=8>
|
||||
|
|
@ -37,11 +38,12 @@ public:
|
|||
MIDI_CLOCK
|
||||
} sequencerClockMode = INTERNAL;
|
||||
|
||||
|
||||
bool sequencerPlaying = false;
|
||||
|
||||
std::shared_ptr<MIDIInOut> midiIO;
|
||||
|
||||
RatioSeqEngine<NSEQUENCES> seqEngine;
|
||||
|
||||
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
|
||||
|
||||
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
|
||||
|
|
@ -61,7 +63,6 @@ public:
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
@ -70,7 +71,6 @@ public:
|
|||
|
||||
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;
|
||||
|
|
@ -86,54 +86,30 @@ public:
|
|||
{
|
||||
if (sequencerPlaying) {
|
||||
if (sequencerClockMode == INTERNAL){
|
||||
midiClockPhasor += midiClockPhasorInc;
|
||||
if (midiClockPhasor >= 1.f) {
|
||||
midiClockPhasor -= 1.f;
|
||||
seqEngine.midiClockPhasor += seqEngine.midiClockPhasorInc;
|
||||
if (seqEngine.midiClockPhasor >= 1.f) {
|
||||
seqEngine.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;
|
||||
seqEngine.resetBar();
|
||||
}
|
||||
}
|
||||
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;
|
||||
if (seqEngine.tick()) {
|
||||
// Capture trigger state for I2C after tick
|
||||
for (size_t i = 0; i < NSEQUENCES; i++) {
|
||||
i2cValues[i] = seqEngine.states[i].lastTrig;
|
||||
}
|
||||
midiIO->flushQueue();
|
||||
queue_try_add(&i2cOutQueue, &i2cValues);
|
||||
}
|
||||
|
||||
sequencingSampleCounter ++;
|
||||
if (sequencingSampleCounter >= sequencingSampleDiv) {
|
||||
sequencingSampleCounter = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
stereosample_t ret { 0.f,0.f };
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -144,12 +120,21 @@ public:
|
|||
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++];
|
||||
|
||||
const size_t midiNotes[NSEQUENCES] = {36,37,38,39,40, 42,43,45};
|
||||
for (size_t i = 0; i < NSEQUENCES; i++) {
|
||||
seqEngine.states[i].midiNote = midiNotes[i];
|
||||
}
|
||||
|
||||
seqEngine.setup(sample_rate);
|
||||
seqEngine.updateBPM(90.f);
|
||||
|
||||
seqEngine.onNoteOn = [this](size_t seqIdx, int velocity) {
|
||||
midiIO->queueNoteOn(seqEngine.states[seqIdx].midiNote, velocity);
|
||||
};
|
||||
seqEngine.onNoteOff = [this](size_t seqIdx) {
|
||||
midiIO->queueNoteOff(seqEngine.states[seqIdx].midiNote, 0);
|
||||
};
|
||||
}
|
||||
|
||||
void setupMIDI(std::shared_ptr<MIDIInOut> new_midi_interf) {
|
||||
|
|
@ -162,105 +147,37 @@ public:
|
|||
|
||||
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);
|
||||
float newBPM;
|
||||
if (queue_try_remove(&bpmQueue, &newBPM)) {
|
||||
seqEngine.updateBPM(newBPM);
|
||||
}
|
||||
int sequencerControl;
|
||||
if (queue_try_remove(&sequencerControlQueue, &sequencerControl)) {
|
||||
sequencerPlaying = sequencerControl == 1;
|
||||
seqEngine.setPlaying(sequencerPlaying);
|
||||
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{
|
||||
} 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;
|
||||
seqEngine.updateParams(params, 0);
|
||||
}
|
||||
|
||||
void setTimeSignature(float beats, float division) {
|
||||
timeSigBeats = beats;
|
||||
timeSigDivision = division;
|
||||
updateBPM(bpm); // Recalculate phasor increment with new time signature
|
||||
seqEngine.setTimeSignature(beats, division);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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__
|
||||
|
||||
|
|
|
|||
493
modes/AudioApps/MEMLCeliumAudioApp.hpp
Normal file
493
modes/AudioApps/MEMLCeliumAudioApp.hpp
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
#ifndef __MEMLCELIUM_AUDIO_APP_HPP__
|
||||
#define __MEMLCELIUM_AUDIO_APP_HPP__
|
||||
|
||||
#include "../../src/memllib/audio/AudioAppBase.hpp"
|
||||
#include "../../src/memllib/synth/maximilian.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "../../src/memllib/synth/maxiPAF.hpp"
|
||||
#include "../../src/memllib/synth/ADSRLite.hpp"
|
||||
#include "../../src/memllib/interface/InterfaceBase.hpp"
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "../../voicespaces/VoiceSpaces.hpp"
|
||||
|
||||
#include "RatioSeqEngine.hpp"
|
||||
|
||||
static constexpr size_t kMEMLCeliumNSequences = 2;
|
||||
|
||||
// MIDI notes assigned to each sequencer index
|
||||
// static constexpr uint8_t kMEMLCeliumSeqNotes[kMEMLCeliumNSequences] = {60};
|
||||
|
||||
template<size_t NPARAMS=64>
|
||||
class MEMLCeliumAudioApp : public AudioAppBase<NPARAMS>
|
||||
{
|
||||
public:
|
||||
static constexpr size_t kN_Params = NPARAMS;
|
||||
static constexpr size_t nFREQs = 17;
|
||||
static constexpr float frequencies[nFREQs] = {100, 200, 400,800, 400, 800, 100,1600,100,400,100,50,1600,200,100,800,400};
|
||||
static constexpr size_t nVoiceSpaces=7;
|
||||
|
||||
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
|
||||
|
||||
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
|
||||
|
||||
RatioSeqEngine<kMEMLCeliumNSequences> seqEngine;
|
||||
|
||||
queue_t sequencerControlQueue;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
MEMLCeliumAudioApp() : AudioAppBase<NPARAMS>() {
|
||||
// auto voiceSpace1 = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_1_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpace2 = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_2_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpacePerc = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_PERC_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpaceSingle1 = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_SINGLE_1_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpaceQuadDetune = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_QUAD_DETUNE_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpaceQuadOct = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_QUAD_OCT_BODY
|
||||
// };
|
||||
|
||||
// auto voiceSpaceQuadDist = [this](const std::array<float, NPARAMS>& params) {
|
||||
// VOICE_SPACE_QUAD_DIST_BODY
|
||||
// };
|
||||
|
||||
|
||||
// voiceSpaces[0] = {"Ellipticacacia", voiceSpaceQuadDetune};
|
||||
// voiceSpaces[1] = {"Rowantares", voiceSpace1};
|
||||
// voiceSpaces[2] = {"Neemeda", voiceSpace2};
|
||||
// voiceSpaces[3] = {"Aquillow", voiceSpacePerc};
|
||||
// voiceSpaces[4] = {"Magnetarch", voiceSpaceSingle1};
|
||||
// voiceSpaces[5] = {"Elderstar", voiceSpaceQuadOct};
|
||||
// voiceSpaces[6] = {"Ipeleiades", voiceSpaceQuadDist};
|
||||
|
||||
// currentVoiceSpace = voiceSpaces[0].mappingFunction;
|
||||
|
||||
queue_init(&sequencerControlQueue, sizeof(int), 1);
|
||||
queue_init(&qMIDINoteOn, sizeof(uint8_t)*2, 1);
|
||||
queue_init(&qMIDINoteOff, sizeof(uint8_t)*2, 1);
|
||||
};
|
||||
|
||||
bool __force_inline euclidean(float phase, const size_t n, const size_t k, const size_t offset, const float pulseWidth)
|
||||
{
|
||||
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
|
||||
{
|
||||
seqEngine.tick();
|
||||
|
||||
float x1[1];
|
||||
|
||||
float envval = v0AmpEnv.play();
|
||||
float v0PitchEnvVal = v0PitchEnv.play() * v0PitchEmph;
|
||||
|
||||
float fbsmooth = (fbzm1 * fbSmoothAlpha) + (feedback * (1.f-fbSmoothAlpha));
|
||||
fbzm1 = fbsmooth;
|
||||
|
||||
float freq0 = baseFreq * (1.f + fbsmooth) + (v0PitchEnvVal * baseFreq);
|
||||
paf0.play(x1, 1, freq0, freq0 + (paf0_cf * freq0), paf0_bw, paf0_vib, paf0_vfr, paf0_shift, 0);
|
||||
float p0 = *x1 * p0Gain;
|
||||
|
||||
const float freq1 = freq0 * detune1;
|
||||
|
||||
paf1.play(x1, 1, freq1, freq1 + (paf1_cf * freq1), paf1_bw, paf1_vib, paf1_vfr, paf1_shift, 1);
|
||||
const float p1 = *x1 * p1Gain;
|
||||
|
||||
const float freq2 = freq1 * detune2;
|
||||
|
||||
paf2.play(x1, 1, freq2, freq2 + (paf2_cf * freq2), paf2_bw, paf2_vib, paf2_vfr, paf2_shift, 1);
|
||||
const float p2 = *x1 * p2Gain;
|
||||
|
||||
float v0 = p0 + p1 + p2;// + p3;
|
||||
|
||||
const float rm = p0 * p1 * p2;// * p3;
|
||||
|
||||
v0 = v0 + (rm * rmGain);
|
||||
|
||||
|
||||
float shape = sinf(v0 * TWOPI);
|
||||
shape = sinf(((shape * TWOPI) * sineShapeGain) + sineShapeASym);
|
||||
v0 = v0 + (shape * sineShapeMix);
|
||||
|
||||
v0 = v0 * envval;
|
||||
feedback = v0 * feedbackGain;
|
||||
|
||||
//v1
|
||||
float v1Envval = v1AmpEnv.play();
|
||||
float v1PitchEnvVal = v1PitchEnv.play() * v1PitchEmph;
|
||||
|
||||
float v1freq0 = v1BaseFreq + (v1PitchEnvVal * v1BaseFreq);
|
||||
v1paf0.play(x1, 1, v1freq0, v1freq0 + (v1paf0_cf * v1freq0), v1paf0_bw, 0, 0, v1paf0_shift, 0);
|
||||
float v1p0 = *x1 * v1p0Gain;
|
||||
|
||||
const float v1freq1 = v1freq0 * v1Detune1;
|
||||
|
||||
v1paf1.play(x1, 1, v1freq1, v1freq1 + (v1paf1_cf * v1freq1), v1paf1_bw, 0, 0, v1paf1_shift, 1);
|
||||
const float v1p1 = *x1 * v1p1Gain;
|
||||
|
||||
const float v1freq2 = v1freq1 * v1Detune2;
|
||||
|
||||
v1paf2.play(x1, 1, v1freq2, v1freq2 + (v1paf2_cf * freq2), v1paf2_bw, 0, 0, v1paf2_shift, 1);
|
||||
const float v1p2 = *x1 * v1p2Gain;
|
||||
|
||||
float v1 = v1p0 + v1p1 + v1p2;
|
||||
|
||||
v1 = v1 * v1Envval;
|
||||
/////
|
||||
|
||||
float mix = v0 + v1;
|
||||
|
||||
mix = tanhf(mix);
|
||||
|
||||
stereosample_t ret { mix, mix };
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Setup(float sample_rate, std::shared_ptr<InterfaceBase> interface) override
|
||||
{
|
||||
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
|
||||
maxiSettings::sampleRate = sample_rate;
|
||||
sampleRatef = static_cast<float>(sample_rate);
|
||||
|
||||
paf0.init();
|
||||
paf0.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
paf1.init();
|
||||
paf1.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
paf2.init();
|
||||
paf2.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
paf3.init();
|
||||
paf3.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
v1paf0.init();
|
||||
v1paf0.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
v1paf1.init();
|
||||
v1paf1.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
v1paf2.init();
|
||||
v1paf2.setsr(maxiSettings::getSampleRate(), 1);
|
||||
|
||||
arpFreq = frequencies[0];
|
||||
envamp=1.f;
|
||||
|
||||
v0AmpEnv.setup(500,500,0.8,1000,sampleRatef);
|
||||
v0PitchEnv.setup(10,500,0.f,100,sampleRatef);
|
||||
v1AmpEnv.setup(500,500,0.8,1000,sampleRatef);
|
||||
v1PitchEnv.setup(10,500,0.f,100,sampleRatef);
|
||||
|
||||
seqEngine.setup(sample_rate);
|
||||
seqEngine.updateBPM(120.f);
|
||||
seqEngine.setPlaying(true);
|
||||
|
||||
seqEngine.onNoteOn = [this](size_t seqIdx, int velocity) {
|
||||
// uint8_t note = kMEMLCeliumSeqNotes[seqIdx];
|
||||
// uint8_t midimsg[2] = { note, static_cast<uint8_t>(velocity) };
|
||||
// queue_try_add(&qMIDINoteOn, &midimsg);
|
||||
// baseFreq = 80.f;
|
||||
noteVel = velocity / 127.0f;
|
||||
noteVel = noteVel * noteVel;
|
||||
switch(seqIdx) {
|
||||
case 0:
|
||||
v0AmpEnv.trigger(noteVel);
|
||||
break;
|
||||
case 1:
|
||||
v1AmpEnv.trigger(noteVel);
|
||||
break;
|
||||
}
|
||||
};
|
||||
seqEngine.onNoteOff = [this](size_t seqIdx) {
|
||||
// uint8_t note = kMEMLCeliumSeqNotes[seqIdx];
|
||||
// uint8_t midimsg[2] = { note, 0 };
|
||||
// queue_try_add(&qMIDINoteOff, &midimsg);
|
||||
switch(seqIdx) {
|
||||
case 0:
|
||||
v0AmpEnv.release();
|
||||
break;
|
||||
case 1:
|
||||
v1AmpEnv.release();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline float mtof(uint8_t note) {
|
||||
return 440.0f * exp2f((note - 69) / 12.0f);
|
||||
}
|
||||
|
||||
size_t currNote=0;
|
||||
void loop() override {
|
||||
// uint8_t midimsg[2];
|
||||
// if (firstParamsReceived && queue_try_remove(&qMIDINoteOn, &midimsg)) {
|
||||
// baseFreq = 80.f;
|
||||
// noteVel = midimsg[1] / 127.0f;
|
||||
// noteVel = noteVel * noteVel;
|
||||
// newNote = true;
|
||||
// env.trigger(noteVel);
|
||||
// currNote = midimsg[0];
|
||||
// }
|
||||
// if (firstParamsReceived && queue_try_remove(&qMIDINoteOff, &midimsg)) {
|
||||
// if (currNote == midimsg[0]) {
|
||||
// env.release();
|
||||
// }
|
||||
// }
|
||||
AudioAppBase<NPARAMS>::loop();
|
||||
}
|
||||
|
||||
void ProcessParams(const std::array<float, NPARAMS>& params)
|
||||
{
|
||||
firstParamsReceived = true;
|
||||
int seqControl;
|
||||
if (queue_try_remove(&sequencerControlQueue, &seqControl)) {
|
||||
seqEngine.setPlaying(seqControl == 1);
|
||||
}
|
||||
seqEngine.updateParams(params, 0);
|
||||
p0Gain=1.f;
|
||||
p1Gain=1.f;
|
||||
p2Gain=1.f;
|
||||
p3Gain=1.f;
|
||||
|
||||
detune1 = 1.0f;
|
||||
detune2 = 1.1f;
|
||||
// detune3 = 1.2f;
|
||||
|
||||
size_t paramIdx = 14; // 0-13 reserved for seqEngine (2 sequencers × 7 params)
|
||||
|
||||
baseFreq = 20.f + (params[paramIdx++] * 80.f);
|
||||
|
||||
paf0_cf = (params[paramIdx++] * 2.f);
|
||||
paf1_cf = (params[paramIdx++] * 2.f);
|
||||
paf2_cf = (params[paramIdx++] * 2.f);
|
||||
// paf3_cf = (params[paramIdx++] * 2.f);
|
||||
|
||||
paf0_bw = 10.f + (params[paramIdx++] * 400.f);
|
||||
paf1_bw = 10.f + (params[paramIdx++] * 50.f);
|
||||
paf2_bw = 10.f + (params[paramIdx++] * 50.f);
|
||||
// paf3_bw = 10.f + (params[paramIdx++] * 100.f);
|
||||
|
||||
paf0_vib = params[paramIdx++] * params[paramIdx++] * 0.01f;
|
||||
paf1_vib = paf0_vib;
|
||||
paf2_vib = paf0_vib;
|
||||
// paf3_vib = 0.f;
|
||||
|
||||
paf0_vfr = (params[paramIdx++] * params[paramIdx++]* 15.0f);
|
||||
paf1_vfr = paf0_vfr;
|
||||
paf2_vfr = paf0_vfr;
|
||||
// paf3_vfr = 0.f;
|
||||
|
||||
paf0_shift = -500.f + (params[paramIdx++] * 500.f);
|
||||
paf1_shift = -300.f + (params[paramIdx++] * 600.f);
|
||||
paf2_shift = -100.f + (params[paramIdx++] * 200.f);
|
||||
// paf3_shift = -300.f + (params[paramIdx++] * 300.f);
|
||||
|
||||
v0AmpEnv.setup(0.01f + (params[paramIdx++] * 1.f),
|
||||
0.5f + params[paramIdx++] * params[paramIdx++] * 200.f,
|
||||
0.01 + (params[paramIdx++] * 0.5f),1.f+ params[paramIdx++] * params[paramIdx++] * 800.f, sampleRatef );
|
||||
|
||||
v0PitchEnv.setup(0.01f + (params[paramIdx++] * 3.f),
|
||||
0.5f + params[paramIdx++] * params[paramIdx++] * 100.f,
|
||||
0.f, 0.1f, sampleRatef );
|
||||
|
||||
v0PitchEmph = params[paramIdx++]* 50.f;
|
||||
|
||||
sineShapeGain = params[paramIdx++];
|
||||
sineShapeASym = params[paramIdx++]* 0.5f;
|
||||
sineShapeMix = params[paramIdx++];
|
||||
|
||||
rmGain = params[paramIdx++];
|
||||
feedbackGain = 0.1f;
|
||||
|
||||
fbSmoothAlpha=0.5f;
|
||||
|
||||
//v1
|
||||
v1p0Gain=1.f;
|
||||
v1p1Gain=1.f;
|
||||
v1p2Gain=1.f;
|
||||
|
||||
v1BaseFreq = 100.f + (params[paramIdx++] * 400.f);
|
||||
v1Detune1 = 0.5f + (params[paramIdx++] * 3.5f);
|
||||
v1Detune2 = 0.5f + (params[paramIdx++] * 3.5f);
|
||||
|
||||
v1paf0_cf = (params[paramIdx++] * 3.f);
|
||||
v1paf1_cf = (params[paramIdx++] * 3.f);
|
||||
v1paf2_cf = (params[paramIdx++] * 3.f);
|
||||
|
||||
v1paf0_bw = 10.f + (params[paramIdx++] * 400.f);
|
||||
v1paf1_bw = 10.f + (params[paramIdx++] * 600.f);
|
||||
v1paf2_bw = 10.f + (params[paramIdx++] * 500.f);
|
||||
|
||||
v1paf0_shift = -800.f + (params[paramIdx++] * 800.f);
|
||||
v1paf1_shift = -300.f + (params[paramIdx++] * 600.f);
|
||||
v1paf2_shift = -100.f + (params[paramIdx++] * 200.f);
|
||||
|
||||
v1AmpEnv.setup(0.01f + (params[paramIdx++] * 1.f),
|
||||
0.5f + params[paramIdx++] * params[paramIdx++] * 100.f,
|
||||
0.01 + (params[paramIdx++] * 0.3f), 1.f + params[paramIdx++] * params[paramIdx++] * 200.f, sampleRatef);
|
||||
|
||||
v1PitchEnv.setup(0.01f + (params[paramIdx++] * 3.f),
|
||||
0.5f + params[paramIdx++] * params[paramIdx++] * 100.f,
|
||||
0.f, 0.1f, sampleRatef);
|
||||
|
||||
v1PitchEmph = params[paramIdx++] * 10.f;
|
||||
|
||||
// currentVoiceSpace(params);
|
||||
}
|
||||
|
||||
queue_t qMIDINoteOn, qMIDINoteOff;
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
maxiPAFOperator paf0;
|
||||
maxiPAFOperator paf1;
|
||||
maxiPAFOperator paf2;
|
||||
maxiPAFOperator paf3;
|
||||
|
||||
maxiPAFOperator v1paf0;
|
||||
maxiPAFOperator v1paf1;
|
||||
maxiPAFOperator v1paf2;
|
||||
|
||||
|
||||
maxiOsc pulse;
|
||||
|
||||
ADSRLite v0AmpEnv, v0PitchEnv, v1AmpEnv, v1PitchEnv;
|
||||
|
||||
float frame=0;
|
||||
|
||||
float feedback=0.f, feedbackGain=0.f;
|
||||
|
||||
float p0Gain=1.f, p1Gain = 1.f, p2Gain=1.f, p3Gain=1.f;
|
||||
float v1p0Gain=1.f, v1p1Gain=1.f, v1p2Gain=1.f;
|
||||
|
||||
float v0PitchEmph = 1.f;
|
||||
float v1PitchEmph = 1.f;
|
||||
|
||||
float paf0_freq = 100;
|
||||
float paf1_freq = 100;
|
||||
float paf2_freq = 50;
|
||||
float paf3_freq = 50;
|
||||
|
||||
float paf0_cf = 200;
|
||||
float paf1_cf = 250;
|
||||
float paf2_cf = 250;
|
||||
float paf3_cf = 250;
|
||||
|
||||
float v1paf0_cf = 200;
|
||||
float v1paf1_cf = 250;
|
||||
float v1paf2_cf = 250;
|
||||
|
||||
float paf0_bw = 100;
|
||||
float paf1_bw = 5000;
|
||||
float paf2_bw = 5000;
|
||||
float paf3_bw = 5000;
|
||||
|
||||
float v1paf0_bw = 100;
|
||||
float v1paf1_bw = 5000;
|
||||
float v1paf2_bw = 5000;
|
||||
|
||||
float paf0_vib = 0;
|
||||
float paf1_vib = 1;
|
||||
float paf2_vib = 1;
|
||||
float paf3_vib = 1;
|
||||
|
||||
float paf0_vfr = 2;
|
||||
float paf1_vfr = 2;
|
||||
float paf2_vfr = 2;
|
||||
float paf3_vfr = 2;
|
||||
|
||||
float paf0_shift = 0;
|
||||
float paf1_shift = 0;
|
||||
float paf2_shift = 0;
|
||||
float paf3_shift = 0;
|
||||
|
||||
float v1paf0_shift = 0;
|
||||
float v1paf1_shift = 0;
|
||||
float v1paf2_shift = 0;
|
||||
|
||||
|
||||
float rmGain = 0.f;
|
||||
|
||||
float sineShapeGain=0.1;
|
||||
float sineShapeASym = 0.f;
|
||||
float sineShapeMix = 0.f;
|
||||
float sineShapeMixInv = 1.f;
|
||||
size_t counter=0;
|
||||
size_t freqIndex = 0;
|
||||
size_t freqOffset = 0;
|
||||
float arpFreq=50;
|
||||
|
||||
maxiLine line;
|
||||
float envamp=0.f;
|
||||
|
||||
float detune1 = 1.0;
|
||||
float detune2 = 1.0;
|
||||
float detune3 = 1.0;
|
||||
|
||||
float v1Detune1 = 1.5;
|
||||
float v1Detune2 = 2.1;
|
||||
|
||||
maxiOsc phasorOsc;
|
||||
maxiTrigger zxdetect;
|
||||
|
||||
size_t euclidN=4;
|
||||
|
||||
float baseFreq = 50.0f;
|
||||
float v1BaseFreq = 200.0f;
|
||||
bool newNote=false;
|
||||
float noteVel = 0.f;
|
||||
bool firstParamsReceived = false;
|
||||
|
||||
// float envdec=0.2f/9000.f;
|
||||
|
||||
float sampleRatef = maxiSettings::getSampleRate();
|
||||
|
||||
float fbzm1=0.f;
|
||||
size_t delayMax=10;
|
||||
float fbSmoothAlpha=0.95f;
|
||||
|
||||
};
|
||||
|
||||
#endif // __MEMLCELIUM_AUDIO_APP_HPP__
|
||||
143
modes/AudioApps/RatioSeqEngine.hpp
Normal file
143
modes/AudioApps/RatioSeqEngine.hpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#ifndef __RATIO_SEQ_ENGINE_HPP__
|
||||
#define __RATIO_SEQ_ENGINE_HPP__
|
||||
|
||||
#include "RatioSeq.hpp"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
template<size_t NSEQUENCES>
|
||||
class RatioSeqEngine {
|
||||
public:
|
||||
std::array<ratioSeqState, NSEQUENCES> states;
|
||||
|
||||
std::function<void(size_t seqIdx, int velocity)> onNoteOn;
|
||||
std::function<void(size_t seqIdx)> onNoteOff;
|
||||
std::function<void()> onBarReset;
|
||||
|
||||
void setup(float sample_rate) {
|
||||
sampleRatef = sample_rate;
|
||||
updateBPM(bpm);
|
||||
}
|
||||
|
||||
__force_inline void updateBPM(float newBPM) {
|
||||
bpm = newBPM;
|
||||
float beatLengthInSeconds = 60.f / bpm;
|
||||
float barLengthInSeconds = beatLengthInSeconds * timeSigBeats;
|
||||
float barLengthInSamples = barLengthInSeconds * (sampleRatef / sequencingSampleDiv);
|
||||
barPhasorInc = 1.f / barLengthInSamples;
|
||||
float midiClockLengthInSeconds = beatLengthInSeconds / 24.f;
|
||||
float midiClockLengthInSamples = midiClockLengthInSeconds * sampleRatef;
|
||||
midiClockPhasorInc = 1.f / midiClockLengthInSamples;
|
||||
}
|
||||
|
||||
void setTimeSignature(float beats, float division) {
|
||||
timeSigBeats = beats;
|
||||
timeSigBeatsInv = 1.f / beats;
|
||||
timeSigDivision = division;
|
||||
updateBPM(bpm);
|
||||
}
|
||||
|
||||
void setPlaying(bool play) {
|
||||
playing = play;
|
||||
if (!playing) {
|
||||
if (onNoteOff) {
|
||||
for (size_t i = 0; i < NSEQUENCES; i++) {
|
||||
onNoteOff(i);
|
||||
}
|
||||
}
|
||||
barPhasor = 0.f;
|
||||
midiClockPhasor = 0.f;
|
||||
sequencingSampleCounter = 0;
|
||||
for (auto &s : states) {
|
||||
s.phasor = 0.f;
|
||||
s.lastTrig = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__force_inline void resetBar() {
|
||||
barPhasor = 0.f;
|
||||
if (onBarReset) onBarReset();
|
||||
}
|
||||
|
||||
// Returns true on a sequencing tick. Call once per audio sample from Process().
|
||||
__force_inline bool tick() {
|
||||
if (!playing) return false;
|
||||
|
||||
bool ticked = false;
|
||||
if (sequencingSampleCounter == 0) {
|
||||
barPhasor += barPhasorInc;
|
||||
if (barPhasor >= 1.f) {
|
||||
barPhasor -= 1.f;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NSEQUENCES; i++) {
|
||||
auto &seq = states[i];
|
||||
float seqPhasor = barPhasor * seq.phasorMul;
|
||||
seqPhasor = fmodf(seqPhasor + seq.phaseOffset, 1.f);
|
||||
|
||||
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 (onNoteOn) onNoteOn(i, highAmp ? 127 : 64);
|
||||
} else if (!trig && seq.lastTrig) {
|
||||
if (onNoteOff) onNoteOff(i);
|
||||
}
|
||||
seq.lastTrig = trig;
|
||||
}
|
||||
ticked = true;
|
||||
}
|
||||
|
||||
sequencingSampleCounter++;
|
||||
if (sequencingSampleCounter >= sequencingSampleDiv) {
|
||||
sequencingSampleCounter = 0;
|
||||
}
|
||||
|
||||
return ticked;
|
||||
}
|
||||
|
||||
// Call from loop() (not audio path). startIdx is the first param index to consume.
|
||||
template<size_t NPARAMS>
|
||||
void updateParams(const std::array<float, NPARAMS>& params, size_t startIdx) {
|
||||
size_t paramIdx = startIdx;
|
||||
for (auto &v : states) {
|
||||
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[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;
|
||||
}
|
||||
}
|
||||
|
||||
float midiClockPhasorInc = 0.f;
|
||||
float midiClockPhasor = 0.f;
|
||||
bool playing = false;
|
||||
|
||||
private:
|
||||
float sampleRatef = 48000.f;
|
||||
float bpm = 120.f;
|
||||
float timeSigBeats = 4.f;
|
||||
float timeSigBeatsInv = 0.25f;
|
||||
float timeSigDivision = 4.f;
|
||||
float barPhasor = 0.f;
|
||||
float barPhasorInc = 0.f;
|
||||
size_t sequencingSampleDiv = 400;
|
||||
size_t sequencingSampleCounter = 0;
|
||||
};
|
||||
|
||||
#endif // __RATIO_SEQ_ENGINE_HPP__
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class MEMLNautModeBreakOr {
|
||||
public:
|
||||
constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate
|
||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
||||
|
||||
USeqI2C i2cOut;
|
||||
inline static BreakOrAudioApp<> audioAppBreakOr;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class MEMLNautModeChannelStrip {
|
||||
public:
|
||||
constexpr static size_t kN_InputParams = 4; // joystick x, y, left and right joysticks
|
||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
||||
ChannelStripAudioApp<> audioAppChannelStrip;
|
||||
std::array<String, ChannelStripAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
||||
InterfaceRL interface;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class MEMLNautModeElysiamorfs {
|
||||
public:
|
||||
constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate
|
||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
||||
|
||||
USeqI2C i2cOut;
|
||||
inline static ElysiamorfAudioApp<> audioAppElysiamorfs;
|
||||
|
|
|
|||
121
modes/MEMLNautModeMEMLCelium.hpp
Normal file
121
modes/MEMLNautModeMEMLCelium.hpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
||||
#include "../src/memllib/hardware/memlnaut/display/XYPadView.hpp"
|
||||
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
||||
#include "AudioApps/MEMLCeliumAudioApp.hpp"
|
||||
#include "../src/memllib/examples/InterfaceRL.hpp"
|
||||
#include "../src/memllib/PicoDefs.hpp"
|
||||
#include "MEMLNautMode.hpp"
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
class MEMLNautModeMEMLCelium {
|
||||
public:
|
||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
||||
|
||||
inline static MEMLCeliumAudioApp<> audioAppMEMLCelium;
|
||||
std::array<String, MEMLCeliumAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
||||
|
||||
InterfaceRL interface;
|
||||
std::shared_ptr<InterfaceRL> interfacePtr;
|
||||
|
||||
bool sequencerPlaying = false;
|
||||
|
||||
|
||||
void setupInterface() {
|
||||
interface.setup(kN_InputParams, MEMLCeliumAudioApp<>::kN_Params);
|
||||
interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true);
|
||||
interface.setModeInfo("memlcelium", "MEMLCelium");
|
||||
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(&audioAppMEMLCelium.sequencerControlQueue, &sequencerPlaying);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
String getHelpTitle() {
|
||||
return "MEMLCelium Mode";
|
||||
}
|
||||
|
||||
__force_inline stereosample_t process(stereosample_t x) {
|
||||
return audioAppMEMLCelium.Process(x);
|
||||
}
|
||||
|
||||
void setupAudio(float sample_rate) {
|
||||
audioAppMEMLCelium.Setup(sample_rate, interfacePtr);
|
||||
voiceSpaceList = audioAppMEMLCelium.getVoiceSpaceNames();
|
||||
}
|
||||
|
||||
__force_inline void loop() {
|
||||
audioAppMEMLCelium.loop();
|
||||
}
|
||||
|
||||
std::shared_ptr<MIDIInOut> midi_interf;
|
||||
|
||||
void setupMIDI(std::shared_ptr<MIDIInOut> new_midi_interf) {
|
||||
midi_interf = new_midi_interf;
|
||||
midi_interf->Setup(16);
|
||||
midi_interf->SetMIDISendChannel(1);
|
||||
interface.bindMIDI(midi_interf);
|
||||
|
||||
midi_interf->SetNoteCallback([this](bool noteon, uint8_t note_number, uint8_t vel_value) {
|
||||
if (noteon) {
|
||||
uint8_t midimsg[2] = { note_number, vel_value };
|
||||
queue_try_add(&audioAppMEMLCelium.qMIDINoteOn, &midimsg);
|
||||
}else{
|
||||
uint8_t midimsg[2] = { note_number, vel_value };
|
||||
queue_try_add(&audioAppMEMLCelium.qMIDINoteOff, &midimsg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addViews() {
|
||||
std::shared_ptr<VoiceSpaceSelectView> voiceSpaceSelectView;
|
||||
voiceSpaceSelectView = std::make_shared<VoiceSpaceSelectView>("Voice Spaces");
|
||||
|
||||
MEMLNaut::Instance()->disp->InsertViewAfter(interface.rlStatsView, voiceSpaceSelectView);
|
||||
voiceSpaceSelectView->setOptions(voiceSpaceList);
|
||||
voiceSpaceSelectView->setNewVoiceCallback(
|
||||
[this](size_t idx) {
|
||||
audioAppMEMLCelium.setVoiceSpace(idx);
|
||||
});
|
||||
|
||||
std::shared_ptr<XYPadView> noteTrigView = std::make_shared<XYPadView>("Play", TFT_SILVER);
|
||||
|
||||
static bool is_playing_note = false;
|
||||
static uint8_t last_note_number = 0;
|
||||
|
||||
noteTrigView->SetOnTouchCallback([this](float x, float y) {
|
||||
if (is_playing_note) {
|
||||
midi_interf->sendNoteOff(last_note_number, 0);
|
||||
is_playing_note = false;
|
||||
}
|
||||
uint8_t noteVel = static_cast<uint8_t>(powf(y, 0.5f) * 127.f);
|
||||
uint8_t midimsg[2] = {static_cast<uint8_t>(x * 127.f), noteVel};
|
||||
queue_try_add(&audioAppMEMLCelium.qMIDINoteOn, &midimsg);
|
||||
midi_interf->sendNoteOn(midimsg[0], midimsg[1]);
|
||||
last_note_number = midimsg[0];
|
||||
is_playing_note = true;
|
||||
});
|
||||
noteTrigView->SetOnTouchReleaseCallback([this](float x, float y) {
|
||||
uint8_t midimsg[2] = {last_note_number,0};
|
||||
queue_try_add(&audioAppMEMLCelium.qMIDINoteOff, &midimsg);
|
||||
midi_interf->sendNoteOff(last_note_number, 0);
|
||||
is_playing_note = false;
|
||||
});
|
||||
MEMLNaut::Instance()->disp->AddView(noteTrigView);
|
||||
};
|
||||
|
||||
inline void processAnalysisParams() {}
|
||||
|
||||
void analyse(stereosample_t) {}
|
||||
|
||||
void loopCore0() {}
|
||||
|
||||
};
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
class MEMLNautModePAFSynth {
|
||||
public:
|
||||
constexpr static size_t kN_InputParams = 4; // joystick x, y, rotate
|
||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
||||
|
||||
inline static PAFSynthAudioApp<> audioAppPAFSynth;
|
||||
std::array<String, PAFSynthAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 3310e517ac6f412fd6391fa122890e4af695ddcb
|
||||
Subproject commit 6bf7d2c362541047f02fdbf2f6dc359873bcd51f
|
||||
Loading…
Reference in a new issue