Stream 6: extract firmware glue under firmware/
Move the Arduino sketch into firmware/MEMLNaut-NISPS/ and bridge the
hardware (memllib) to the platform-agnostic nisps/ library through a
slim glue layer. Delete the legacy root-level *AudioApp.hpp,
modes/MEMLNautMode*.hpp, voicespaces/, IMLInterface.hpp, XiasriAnalysis,
and the src/memlp submodule.
Glue layout (firmware/MEMLNaut-NISPS/glue/):
audio_driver.hpp - bridge memllib block callback to Mode::process
via per-Mode templated trampoline (no virtual dispatch)
peripherals.hpp - joystick/pots/buttons -> Mode::set_input + ML primitives
midi_io.hpp - MIDI in -> mode.note_on/update_bpm/set_playing,
drains mode ControlEvent ring -> MIDI UART
mode_select.hpp - using-aliases mapping MEMLNautMode<Name> to
nisps::modes::*Mode (build script rewrites the
#define MEMLNAUT_MODE_TYPE line)
input_router.hpp / output_router.hpp - top-level wire/drain entry points
The sketch tree uses src/{memllib,daisysp,nisps} symlinks because
Arduino-CLI rejects ".." in include paths from sketch-tree headers.
mode_select.hpp #undefs Arduino's sq/min/max/abs/round macros before
including nisps headers (some nisps engines use those identifiers as
method names). The audio bridge struct is extern in the header and
defined in the .ino because inline + __not_in_flash section attribute
collide at link time.
Verification: arduino-cli compile succeeds for PAFSynth, ChannelStrip,
and BreakOr (rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3,
-std=gnu++20). Host C++ tests under nisps/build still pass (3 binaries,
110+ tests). Build script (scripts/build-firmware.sh) updated to point
at the new sketch path; mode-rewrite logic unchanged.
Closes meml-gkm.
This commit is contained in:
parent
3021552a38
commit
5fc37f760e
69 changed files with 728 additions and 5748 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,6 +1,3 @@
|
||||||
[submodule "src/memlp"]
|
|
||||||
path = src/memlp
|
|
||||||
url = https://github.com/MusicallyEmbodiedML/memlp.git
|
|
||||||
[submodule "src/memllib"]
|
[submodule "src/memllib"]
|
||||||
path = src/memllib
|
path = src/memllib
|
||||||
url = https://github.com/MusicallyEmbodiedML/memllib.git
|
url = https://github.com/MusicallyEmbodiedML/memllib.git
|
||||||
|
|
|
||||||
30
CLAUDE.md
30
CLAUDE.md
|
|
@ -238,7 +238,7 @@ Tests use the `?debug=1` probe (`window.__nisps`) for programmatic access to the
|
||||||
This is an Arduino project targeting the MEMLNaut RP2350 hardware. Build and flash it with the repo-local helper scripts, which wrap the correct board target and compiler settings.
|
This is an Arduino project targeting the MEMLNaut RP2350 hardware. Build and flash it with the repo-local helper scripts, which wrap the correct board target and compiler settings.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Initialize submodules (required for memllib and memlp)
|
# Initialize submodules (required for memllib)
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
|
||||||
# Build only
|
# Build only
|
||||||
|
|
@ -254,8 +254,8 @@ scripts/flash-firmware.sh
|
||||||
scripts/build-and-flash-firmware.sh
|
scripts/build-and-flash-firmware.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20 via `compiler.cpp.extra_flags=-std=gnu++20`.
|
The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20 via `compiler.cpp.extra_flags=-std=gnu++20`. The sketch lives at `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino`; everything platform-agnostic (ML, DSP, engines, modes) is under `nisps/` and reached via in-sketch `src/` symlinks.
|
||||||
If no variant is passed to `build-firmware.sh` in an interactive shell, it parses the available `MEMLNautMode*` options from `MEMLNaut-NISPS.ino`, prompts for one, and rewrites the active `MEMLNAUT_MODE_TYPE` before compiling.
|
If no variant is passed to `build-firmware.sh` in an interactive shell, it parses the available `MEMLNautMode*` options from the sketch, prompts for one, and rewrites the active `MEMLNAUT_MODE_TYPE` before compiling.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
@ -269,7 +269,7 @@ Inter-core synchronization uses memory barriers (`MEMORY_BARRIER()`, `WRITE_VOLA
|
||||||
|
|
||||||
### Mode System
|
### Mode System
|
||||||
|
|
||||||
The active mode is selected at compile-time via `#define MEMLNAUT_MODE_TYPE` in `MEMLNaut-NISPS.ino`. Modes implement the `MEMLNautMode` concept (see `modes/MEMLNautMode.hpp`):
|
The active mode is selected at compile-time via `#define MEMLNAUT_MODE_TYPE` in `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino`. The macro expands to a type alias defined in `firmware/MEMLNaut-NISPS/glue/mode_select.hpp` that maps each `MEMLNautMode<Name>` identifier to a concrete `nisps::modes::*Mode` type. Each mode satisfies the C++20 `nisps::Mode` concept (`nisps/core/concepts.hpp`):
|
||||||
|
|
||||||
| Mode | Purpose |
|
| Mode | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
|
|
@ -277,25 +277,27 @@ The active mode is selected at compile-time via `#define MEMLNAUT_MODE_TYPE` in
|
||||||
| `MEMLNautModePAFSynth` | PAF (Phase Aligned Formant) synthesis with MIDI |
|
| `MEMLNautModePAFSynth` | PAF (Phase Aligned Formant) synthesis with MIDI |
|
||||||
| `MEMLNautModeXIASRI` | Audio-reactive mode using machine listening analysis |
|
| `MEMLNautModeXIASRI` | Audio-reactive mode using machine listening analysis |
|
||||||
| `MEMLNautModeSoundAnalysisMIDI` | Sound analysis with MIDI output |
|
| `MEMLNautModeSoundAnalysisMIDI` | Sound analysis with MIDI output |
|
||||||
|
| `MEMLNautModeBreakOr` | Break\|\| 8-track ratio sequencer |
|
||||||
|
| `MEMLNautModeVerbFX` | Reverb/effects engine |
|
||||||
|
| `MEMLNautModeElysiamorfs` | Elysiamorf granular sequencer |
|
||||||
|
| `MEMLNautModeMEMLCelium` | MEMLCelium dual-voice synth + sequencer |
|
||||||
|
|
||||||
### Voice Spaces
|
### Voice Spaces
|
||||||
|
|
||||||
Voice spaces map ML output parameters to audio engine parameters. They are defined as lambda functions that translate a normalized parameter array into synthesizer/processor settings. See `voicespaces/` for examples:
|
Voice spaces map ML output vectors → engine parameters. They live as static lambdas inside each engine in `nisps/engines/*.hpp` (no longer in a separate `voicespaces/` directory). The mode picks a voice space at runtime via `engine().set_voice_space(idx)`.
|
||||||
- PAF synth presets: `VoiceSpace1.hpp`, `VoiceSpaceQuadDetune.hpp`, etc.
|
|
||||||
- Channel strip presets: `voicespaces/ChannelStrip/basic.hpp` (Neve, SSL emulations)
|
|
||||||
|
|
||||||
### Key Components
|
### Key Components
|
||||||
|
|
||||||
- **IMLInterface** (`IMLInterface.hpp`): Interactive ML interface using an MLP for inference/training
|
- **`nisps::ModeBase`** (`nisps/modes/base.hpp`): CRTP scaffold; absorbs input forwarding, ML inference, voice-space dispatch, control-event ring buffer.
|
||||||
- **InterfaceRL**: Reinforcement learning interface from memllib that handles joystick input and learning
|
- **`nisps::ml::MLP<NIn, NHidden..., NOut>`** (`nisps/ml/mlp.hpp`): Templated MLP with SGD/RMSProp, RL primitives (`move_weights`, `draw_weights`).
|
||||||
- **AudioAppBase**: Template base class for audio applications
|
- **`firmware/glue/audio_driver.hpp`**: Bridge from memllib `AudioDriver` per-block callback to `Mode::process(stereosample_t)` per-sample.
|
||||||
- **XiasriAnalysis**: Real-time audio feature extraction (pitch, aperiodicity, energy, brightness)
|
- **`firmware/glue/peripherals.hpp`**: Joystick / pots / buttons → `Mode::set_input` and ML primitives.
|
||||||
|
- **`nisps::AnalysisEngine`** (`nisps/engines/analysis.hpp`): Real-time audio feature extraction (pitch, aperiodicity, energy, brightness).
|
||||||
|
|
||||||
### Submodules (in `src/`)
|
### Submodules (in `src/`)
|
||||||
|
|
||||||
- **memllib**: Hardware abstraction, audio drivers, synth components, RL interfaces
|
- **memllib**: Hardware abstraction, audio drivers, MIDI, display.
|
||||||
- **memlp**: MLP (Multi-Layer Perceptron) implementation for embedded ML
|
- **daisysp**: DSP library (filters, drums, effects, synthesis).
|
||||||
- **daisysp**: DSP library (filters, drums, effects, synthesis)
|
|
||||||
|
|
||||||
## Memory Sections
|
## Memory Sections
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
#ifndef __CHANNEL_STRIP_AUDIO_APP_HPP__
|
|
||||||
#define __CHANNEL_STRIP_AUDIO_APP_HPP__
|
|
||||||
|
|
||||||
#include "src/memllib/audio/AudioAppBase.hpp" // Added missing include
|
|
||||||
#include "src/memllib/synth/maximilian.h" // Added missing include for maxiSettings, maxiOsc, maxiTrigger, maxiDelayline, maxiEnvGen, maxiLine
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory> // Added for std::shared_ptr
|
|
||||||
|
|
||||||
#include "src/memllib/synth/maxiPAF.hpp"
|
|
||||||
#include "src/memllib/interface/InterfaceBase.hpp" // Added missing include
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
#include "voicespaces/VoiceSpaces.hpp"
|
|
||||||
|
|
||||||
#include "voicespaces/ChannelStrip/basic.hpp"
|
|
||||||
|
|
||||||
template<size_t BUFSIZE>
|
|
||||||
class maxiRingBufLite {
|
|
||||||
public:
|
|
||||||
maxiRingBufLite();
|
|
||||||
|
|
||||||
/*!Add the latest value to the buffer \param x A value*/
|
|
||||||
__force_inline void push(float x) {
|
|
||||||
buf[idx] = x;
|
|
||||||
idx++;
|
|
||||||
if (idx==BUFSIZE) {
|
|
||||||
idx=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \returns The size of the buffer*/
|
|
||||||
size_t size() {return BUFSIZE;}
|
|
||||||
|
|
||||||
/*! \returns the value at the front of the buffer*/
|
|
||||||
__force_inline float head() {return idx == 0 ? buf[BUFSIZE-1] : buf[idx-1];}
|
|
||||||
|
|
||||||
/*! \returns the oldest value in the buffer, for a particular window size \param N The size of the window, N < the size of the buffer*/
|
|
||||||
__force_inline float tail(const size_t N) {
|
|
||||||
float val=0;
|
|
||||||
if (idx >= N) {
|
|
||||||
val = buf[idx-N];
|
|
||||||
}else{
|
|
||||||
size_t tailIdx = BUFSIZE - (N-idx);
|
|
||||||
val = buf[tailIdx];
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
using reduceFunction = std::function<float(float, float)>;
|
|
||||||
/**
|
|
||||||
* Apply a function of the previous N values in the buffer
|
|
||||||
* \param N The number of values in the window
|
|
||||||
* \param func A function in the form float func(float previousResult, float nextValue)
|
|
||||||
* \param initval The initial value to pass into the function (usually 0)
|
|
||||||
* \returns The last result of the function, after passing in all values from the window
|
|
||||||
* Example: this function will sum the values in the window:
|
|
||||||
* auto sumfunc = [](float val, float n) {return val + n;};
|
|
||||||
*/
|
|
||||||
float reduce(size_t N, reduceFunction func, float initval) {
|
|
||||||
float val=0;
|
|
||||||
if (idx >= N) {
|
|
||||||
for(size_t i=idx-N; i < idx; i++) {
|
|
||||||
val = func(val, buf[i]);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
//first chunk
|
|
||||||
for(size_t i=F64_ARRAY_SIZE(buf)-(N-idx); i < buf.size(); i++) {
|
|
||||||
val = func(val, buf[i]);
|
|
||||||
}
|
|
||||||
//second chunk
|
|
||||||
for(int i=0; i < idx; i++) {
|
|
||||||
val = func(val, buf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<float, BUFSIZE> buf{};
|
|
||||||
size_t idx=0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Calculate the Root Mean Square of a signal over a window of time
|
|
||||||
// * This is a good measurement of the amount of power in a signal
|
|
||||||
// */
|
|
||||||
// template<size_t BUFSIZE>
|
|
||||||
// class maxiRMSLite {
|
|
||||||
// public:
|
|
||||||
// maxiRMS();
|
|
||||||
|
|
||||||
// /*!Configure the analyser \param maxLength The maximum length of time to analyse (ms) \param windowSize The size of the window of time to analyse initially (ms, <= maxLength) */
|
|
||||||
// void setup(float maxLength, float windowSize) {
|
|
||||||
// buf.setup(maxiConvert::msToSamps(maxLength));
|
|
||||||
// setWindowSize(windowSize);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*!Set the size of the analysis window \param newWindowSize the size of the analysis window (in ms). Large values will smooth out the measurement, and make it less responsive to transients*/
|
|
||||||
// void setWindowSize(float newWindowSize) {
|
|
||||||
// size_t windowSizeInSamples = maxiConvert::msToSamps(newWindowSize);
|
|
||||||
// if (windowSizeInSamples <= buf.size() && windowSizeInSamples > 0) {
|
|
||||||
// windowSize = windowSizeInSamples;
|
|
||||||
// windowSizeInv = 1.f / static_cast<float>(windowSize);
|
|
||||||
// }
|
|
||||||
// runningRMS = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*!Find out the size of the analysis window (in ms)*/
|
|
||||||
// float getWindowSize() {
|
|
||||||
// return maxiConvert::sampsToMs(windowSize);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /*Analyse the signal \param signal a signal \returns RMS*/
|
|
||||||
// float play(float signal) {
|
|
||||||
// float sigPow2 = (signal * signal);
|
|
||||||
// runningRMS -= buf.tail(windowSize);
|
|
||||||
// buf.push(sigPow2);
|
|
||||||
// runningRMS += sigPow2;
|
|
||||||
// return sqrtf(runningRMS * windowSizeInv);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private:
|
|
||||||
// maxiRingBuf<BUFSIZE> buf;
|
|
||||||
// size_t windowSize=BUFSIZE; // in samples
|
|
||||||
// float windowSizeInv=1.f/BUFSIZE;
|
|
||||||
// float runningRMS=0;
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<size_t NPARAMS=24>
|
|
||||||
class ChannelStripAudioApp : public AudioAppBase<NPARAMS>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr size_t kN_Params = NPARAMS;
|
|
||||||
static constexpr size_t nVoiceSpaces=6;
|
|
||||||
|
|
||||||
enum class controlMessages {
|
|
||||||
MSG_BYPASS_ALL=0,
|
|
||||||
MSG_BYPASS_EQ,
|
|
||||||
MSG_BYPASS_COMP,
|
|
||||||
MSG_BYPASS_PREPOSTGAIN,
|
|
||||||
MSG_BYPASS_INFILTERS,
|
|
||||||
};
|
|
||||||
|
|
||||||
queue_t controlMessageQueue;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelStripAudioApp() : AudioAppBase<NPARAMS>() {
|
|
||||||
|
|
||||||
auto voiceSpaceNeve66 = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_NEVE66_BODY
|
|
||||||
};
|
|
||||||
auto voiceSpaceSSL4K = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_SSL4KGIST_BODY
|
|
||||||
};
|
|
||||||
auto voiceSpaceMaleVox = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_MALE_VOX_BODY
|
|
||||||
};
|
|
||||||
auto voiceSpaceFemaleVox = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_FEMALE_VOX_BODY
|
|
||||||
};
|
|
||||||
auto voiceSpaceSSL9K = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_SSL9KINDA_BODY
|
|
||||||
};
|
|
||||||
auto voiceSpaceNeve80 = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
VOICE_SPACE_CHSTRIP_NEVE_80
|
|
||||||
};
|
|
||||||
voiceSpaces[0] = {"WannabeNeve66", voiceSpaceNeve66};
|
|
||||||
voiceSpaces[1] = {"SSL 4K G-ist", voiceSpaceSSL4K};
|
|
||||||
voiceSpaces[2] = {"SSL 9K-inda", voiceSpaceSSL9K};
|
|
||||||
voiceSpaces[3] = {"MaleVox", voiceSpaceMaleVox};
|
|
||||||
voiceSpaces[4] = {"FemaleVox", voiceSpaceFemaleVox};
|
|
||||||
voiceSpaces[5] = {"Neve 80", voiceSpaceNeve80};
|
|
||||||
|
|
||||||
currentVoiceSpace = voiceSpaces[0].mappingFunction;
|
|
||||||
|
|
||||||
queue_init(&controlMessageQueue, sizeof(controlMessages), 1);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
__attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override
|
|
||||||
{
|
|
||||||
float y = x[0];
|
|
||||||
float y1 = x[1];
|
|
||||||
if (!bypassAll) {
|
|
||||||
|
|
||||||
if (!bypassPrePostGain) {
|
|
||||||
y = tanhf(y * preGain);
|
|
||||||
y1 = tanhf(y1 * preGain);
|
|
||||||
}
|
|
||||||
if (!bypassInFilters)
|
|
||||||
{
|
|
||||||
y = inLowPass.loresChamberlain(y, inLowPassCutoff, 1.f);
|
|
||||||
y = inHighPass.hiresChamberlain(y, inHighPassCutoff, 1.f);
|
|
||||||
|
|
||||||
y1 = inLowPass1.loresChamberlain(y1, inLowPassCutoff, 1.f);
|
|
||||||
y1 = inHighPass1.hiresChamberlain(y1, inHighPassCutoff, 1.f);
|
|
||||||
}
|
|
||||||
if (!bypassEQ) {
|
|
||||||
y = peak0.play(y);
|
|
||||||
y = peak1.play(y);
|
|
||||||
y = lowshelf.play(y);
|
|
||||||
y = highshelf.play(y);
|
|
||||||
|
|
||||||
y1 = peak0_1.play(y1);
|
|
||||||
y1 = peak1_1.play(y1);
|
|
||||||
y1 = lowshelf1.play(y1);
|
|
||||||
// y1 = highshelf1.play(y1);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!bypassComp) {
|
|
||||||
y = dyn.compress(y, compThreshold, compRatio, 0.f);
|
|
||||||
y1 = dyn1.compress(y1, compThreshold, compRatio, 0.f);
|
|
||||||
}
|
|
||||||
if (!bypassPrePostGain) {
|
|
||||||
y = tanhf(y * postGain);
|
|
||||||
y1 = tanhf(y1 * postGain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stereosample_t ret { y, y1};
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Setup(float sample_rate, std::shared_ptr<InterfaceBase> interface) override
|
|
||||||
{
|
|
||||||
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
|
|
||||||
maxiSettings::sampleRate = sample_rate;
|
|
||||||
dyn.setLookAhead(0);
|
|
||||||
dyn.setAttackHigh(50);
|
|
||||||
dyn.setReleaseHigh(200);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline)) void ProcessParams(const std::array<float, NPARAMS>& params)
|
|
||||||
{
|
|
||||||
controlMessages msg;
|
|
||||||
while (queue_try_remove(&controlMessageQueue, &msg)) {
|
|
||||||
Serial.printf("ChannelStripAudioApp: received control message %d\n", static_cast<int>(msg));
|
|
||||||
switch(msg) {
|
|
||||||
case controlMessages::MSG_BYPASS_ALL:
|
|
||||||
bypassAll = !bypassAll;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_BYPASS_EQ:
|
|
||||||
bypassEQ = !bypassEQ;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_BYPASS_COMP:
|
|
||||||
bypassComp = !bypassComp;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_BYPASS_PREPOSTGAIN:
|
|
||||||
bypassPrePostGain = !bypassPrePostGain;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_BYPASS_INFILTERS:
|
|
||||||
bypassInFilters = !bypassInFilters;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentVoiceSpace(params);
|
|
||||||
dyn.setAttackHigh(compAttack);
|
|
||||||
dyn.setReleaseHigh(compRelease);
|
|
||||||
peak0.set(maxiBiquad::PEAK, peak0Freq, peak0Q, peak0Gain);
|
|
||||||
peak1.set(maxiBiquad::PEAK, peak1Freq, peak1Q, peak1Gain);
|
|
||||||
lowshelf.set(maxiBiquad::LOWSHELF, 100.f, 2.f, 3.f);
|
|
||||||
highshelf.set(maxiBiquad::HIGHSHELF, 1000.f, 2.f, 3.f);
|
|
||||||
|
|
||||||
dyn1.setAttackHigh(compAttack);
|
|
||||||
dyn1.setReleaseHigh(compRelease);
|
|
||||||
peak0_1.set(maxiBiquad::PEAK, peak0Freq, peak0Q, peak0Gain);
|
|
||||||
peak1_1.set(maxiBiquad::PEAK, peak1Freq, peak1Q, peak1Gain);
|
|
||||||
lowshelf1.set(maxiBiquad::LOWSHELF, 100.f, 2.f, 3.f);
|
|
||||||
highshelf1.set(maxiBiquad::HIGHSHELF, 1000.f, 2.f, 3.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
float sampleRatef = maxiSettings::getSampleRate();
|
|
||||||
|
|
||||||
float preGain=1.f;
|
|
||||||
float postGain=1.f;
|
|
||||||
|
|
||||||
maxiFilter inHighPass, inLowPass;
|
|
||||||
maxiFilter inHighPass1, inLowPass1;
|
|
||||||
|
|
||||||
float inLowPassCutoff=200.f;
|
|
||||||
float inHighPassCutoff=2000.f;
|
|
||||||
|
|
||||||
float compThreshold=0.f;
|
|
||||||
float compRatio = 1.f;
|
|
||||||
float compAttack=10.f;
|
|
||||||
float compRelease=50.f;
|
|
||||||
|
|
||||||
float peak0Freq=100.f;
|
|
||||||
float peak0Q=1.f;
|
|
||||||
float peak0Gain=1.f;
|
|
||||||
|
|
||||||
float peak1Freq=1000.f;
|
|
||||||
float peak1Q=1.f;
|
|
||||||
float peak1Gain=1.f;
|
|
||||||
|
|
||||||
float lowShelfFreq=1000.f;
|
|
||||||
float lowShelfQ=1.f;
|
|
||||||
float lowShelfGain=1.f;
|
|
||||||
|
|
||||||
float highShelfFreq=1000.f;
|
|
||||||
float highShelfQ=1.f;
|
|
||||||
float highShelfGain=1.f;
|
|
||||||
|
|
||||||
bool bypassAll = false;
|
|
||||||
bool bypassEQ = false;
|
|
||||||
bool bypassComp = false;
|
|
||||||
bool bypassPrePostGain = false;
|
|
||||||
bool bypassInFilters = false;
|
|
||||||
|
|
||||||
maxiDynamicsLite dyn, dyn1;
|
|
||||||
|
|
||||||
maxiBiquad lowshelf;
|
|
||||||
maxiBiquad peak0;
|
|
||||||
maxiBiquad peak1;
|
|
||||||
maxiBiquad highshelf;
|
|
||||||
|
|
||||||
maxiBiquad lowshelf1;
|
|
||||||
maxiBiquad peak0_1;
|
|
||||||
maxiBiquad peak1_1;
|
|
||||||
maxiBiquad highshelf1;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
276
IMLInterface.hpp
276
IMLInterface.hpp
|
|
@ -1,276 +0,0 @@
|
||||||
#ifndef IMINTERFACE_HPP
|
|
||||||
#define IMINTERFACE_HPP
|
|
||||||
|
|
||||||
// #include "src/memllib/audio/AudioAppBase.hpp"
|
|
||||||
#include "src/memllib/interface/InterfaceBase.hpp"
|
|
||||||
|
|
||||||
#include "src/memlp/Dataset.hpp"
|
|
||||||
#include "src/memlp/MLP.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IMLInterface : public InterfaceBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IMLInterface() : InterfaceBase() {}
|
|
||||||
|
|
||||||
void setup(size_t n_inputs, size_t n_outputs) override
|
|
||||||
{
|
|
||||||
InterfaceBase::setup(n_inputs, n_outputs);
|
|
||||||
// Additional setup code specific to IMLInterface
|
|
||||||
n_inputs_ = n_inputs;
|
|
||||||
n_outputs_ = n_outputs;
|
|
||||||
|
|
||||||
MLSetup_();
|
|
||||||
n_iterations_ = 1000;
|
|
||||||
input_state_.resize(n_inputs, 0.5f);
|
|
||||||
output_state_.resize(n_outputs, 0);
|
|
||||||
// Init/reset state machine
|
|
||||||
training_mode_ = INFERENCE_MODE;
|
|
||||||
perform_inference_ = true;
|
|
||||||
input_updated_ = false;
|
|
||||||
|
|
||||||
Serial.println("IMLInterface setup done");
|
|
||||||
Serial.print("Address of n_inputs_: ");
|
|
||||||
Serial.println(reinterpret_cast<uintptr_t>(&n_inputs_));
|
|
||||||
Serial.print("Inputs: ");
|
|
||||||
Serial.print(n_inputs_);
|
|
||||||
Serial.print(", Outputs: ");
|
|
||||||
Serial.println(n_outputs_);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum training_mode_t {
|
|
||||||
INFERENCE_MODE,
|
|
||||||
TRAINING_MODE
|
|
||||||
};
|
|
||||||
|
|
||||||
void SetTrainingMode(training_mode_t training_mode)
|
|
||||||
{
|
|
||||||
Serial.print("Training mode: ");
|
|
||||||
Serial.println(training_mode == INFERENCE_MODE ? "Inference" : "Training");
|
|
||||||
|
|
||||||
if (training_mode == INFERENCE_MODE && training_mode_ == TRAINING_MODE) {
|
|
||||||
// Train the network!
|
|
||||||
MLTraining_();
|
|
||||||
}
|
|
||||||
training_mode_ = training_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessInput()
|
|
||||||
{
|
|
||||||
// Check if input is updated
|
|
||||||
if (perform_inference_ && input_updated_) {
|
|
||||||
MLInference_(input_state_);
|
|
||||||
input_updated_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetInput(size_t index, float value)
|
|
||||||
{
|
|
||||||
// Serial.print("Input ");
|
|
||||||
// Serial.print(index);
|
|
||||||
// Serial.print(" set to: ");
|
|
||||||
// Serial.println(value);
|
|
||||||
|
|
||||||
if (index >= n_inputs_) {
|
|
||||||
Serial.print("Input index ");
|
|
||||||
Serial.print(index);
|
|
||||||
Serial.println(" out of bounds.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value < 0) {
|
|
||||||
value = 0;
|
|
||||||
} else if (value > 1.0) {
|
|
||||||
value = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state of input
|
|
||||||
input_state_[index] = value;
|
|
||||||
input_updated_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum saving_mode_t {
|
|
||||||
STORE_VALUE_MODE,
|
|
||||||
STORE_POSITION_MODE,
|
|
||||||
};
|
|
||||||
|
|
||||||
void SaveInput(saving_mode_t mode)
|
|
||||||
{
|
|
||||||
if (STORE_VALUE_MODE == mode) {
|
|
||||||
|
|
||||||
Serial.println("Move input to position...");
|
|
||||||
perform_inference_ = false;
|
|
||||||
|
|
||||||
} else { // STORE_POSITION_MODE
|
|
||||||
|
|
||||||
Serial.println("Creating example in this position.");
|
|
||||||
// Save pair in the dataset
|
|
||||||
dataset_->Add(input_state_, output_state_);
|
|
||||||
perform_inference_ = true;
|
|
||||||
MLInference_(input_state_);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearData()
|
|
||||||
{
|
|
||||||
if (training_mode_ == TRAINING_MODE) {
|
|
||||||
Serial.println("Clearing dataset...");
|
|
||||||
dataset_->Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Randomise()
|
|
||||||
{
|
|
||||||
if (training_mode_ == TRAINING_MODE) {
|
|
||||||
Serial.println("Randomising weights...");
|
|
||||||
MLRandomise_();
|
|
||||||
MLInference_(input_state_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetIterations(size_t iterations)
|
|
||||||
{
|
|
||||||
n_iterations_ = iterations;
|
|
||||||
Serial.print("Iterations set to: ");
|
|
||||||
Serial.println(n_iterations_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
size_t n_inputs_;
|
|
||||||
size_t n_outputs_;
|
|
||||||
size_t n_iterations_;
|
|
||||||
|
|
||||||
// State machine
|
|
||||||
training_mode_t training_mode_;
|
|
||||||
bool perform_inference_;
|
|
||||||
bool input_updated_;
|
|
||||||
|
|
||||||
// Controls/sensors
|
|
||||||
std::vector<float> input_state_;
|
|
||||||
std::vector<float> output_state_;
|
|
||||||
|
|
||||||
// MLP core
|
|
||||||
std::unique_ptr<Dataset> dataset_;
|
|
||||||
std::unique_ptr<MLP<float>> mlp_;
|
|
||||||
MLP<float>::mlp_weights mlp_stored_weights_;
|
|
||||||
bool randomised_state_;
|
|
||||||
|
|
||||||
void MLSetup_()
|
|
||||||
{
|
|
||||||
// Constants for MLP init
|
|
||||||
const unsigned int kBias = 1;
|
|
||||||
const std::vector<ACTIVATION_FUNCTIONS> layers_activfuncs = {
|
|
||||||
RELU, RELU, RELU, SIGMOID
|
|
||||||
};
|
|
||||||
const bool use_constant_weight_init = false;
|
|
||||||
const float constant_weight_init = 0;
|
|
||||||
// Layer size definitions
|
|
||||||
const std::vector<size_t> layers_nodes = {
|
|
||||||
n_inputs_ + kBias,
|
|
||||||
10, 10, 14,
|
|
||||||
n_outputs_
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create dataset
|
|
||||||
dataset_ = std::make_unique<Dataset>();
|
|
||||||
// Create MLP
|
|
||||||
mlp_ = std::make_unique<MLP<float>>(
|
|
||||||
layers_nodes,
|
|
||||||
layers_activfuncs,
|
|
||||||
loss::LOSS_MSE,
|
|
||||||
use_constant_weight_init,
|
|
||||||
constant_weight_init
|
|
||||||
);
|
|
||||||
|
|
||||||
// State machine
|
|
||||||
randomised_state_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLInference_(std::vector<float> input)
|
|
||||||
{
|
|
||||||
if (!dataset_ || !mlp_) {
|
|
||||||
Serial.println("ML not initialized!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.size() != n_inputs_) {
|
|
||||||
Serial.print("Input size mismatch - ");
|
|
||||||
Serial.print("Expected: ");
|
|
||||||
Serial.print(n_inputs_);
|
|
||||||
Serial.print(", Got: ");
|
|
||||||
Serial.println(input.size());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.push_back(1.0f); // Add bias term
|
|
||||||
// Perform inference
|
|
||||||
std::vector<float> output(n_outputs_);
|
|
||||||
mlp_->GetOutput(input, &output);
|
|
||||||
// Process inferenced data
|
|
||||||
output_state_ = output;
|
|
||||||
SendParamsToQueue(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLRandomise_()
|
|
||||||
{
|
|
||||||
if (!mlp_) {
|
|
||||||
Serial.println("ML not initialized!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomize weights
|
|
||||||
mlp_stored_weights_ = mlp_->GetWeights();
|
|
||||||
mlp_->DrawWeights();
|
|
||||||
randomised_state_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLTraining_()
|
|
||||||
{
|
|
||||||
if (!mlp_) {
|
|
||||||
Serial.println("ML not initialized!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Restore old weights
|
|
||||||
if (randomised_state_) {
|
|
||||||
mlp_->SetWeights(mlp_stored_weights_);
|
|
||||||
}
|
|
||||||
randomised_state_ = false;
|
|
||||||
|
|
||||||
// Prepare for training
|
|
||||||
// Extract dataset to training pair
|
|
||||||
MLP<float>::training_pair_t dataset(dataset_->GetFeatures(), dataset_->GetLabels());
|
|
||||||
// Check and report on dataset size
|
|
||||||
Serial.print("Feature size ");
|
|
||||||
Serial.print(dataset.first.size());
|
|
||||||
Serial.print(", label size ");
|
|
||||||
Serial.println(dataset.second.size());
|
|
||||||
if (!dataset.first.size() || !dataset.second.size()) {
|
|
||||||
Serial.println("Empty dataset!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.print("Feature dim ");
|
|
||||||
Serial.print(dataset.first[0].size());
|
|
||||||
Serial.print(", label dim ");
|
|
||||||
Serial.println(dataset.second[0].size());
|
|
||||||
if (!dataset.first[0].size() || !dataset.second[0].size()) {
|
|
||||||
Serial.println("Empty dataset dimensions!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Training loop
|
|
||||||
Serial.print("Training for max ");
|
|
||||||
Serial.print(n_iterations_);
|
|
||||||
Serial.println(" iterations...");
|
|
||||||
float loss = mlp_->Train(dataset,
|
|
||||||
1.,
|
|
||||||
n_iterations_,
|
|
||||||
0.00001,
|
|
||||||
false);
|
|
||||||
Serial.print("Trained, loss = ");
|
|
||||||
Serial.println(loss, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // IMINTERFACE_HPP
|
|
||||||
43
MAP.md
43
MAP.md
|
|
@ -4,22 +4,32 @@ MEMLNaut-NISPS: Neural Interactive Shaping of Parameter Spaces. Two living artef
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
### Firmware (Arduino, RP2040)
|
### Firmware (Arduino, RP2350)
|
||||||
- `MEMLNaut-NISPS.ino` — top-level sketch; dual-core setup, mode selected at compile-time via `#define MEMLNAUT_MODE_TYPE`.
|
- `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino` — sketch entry point; dual-core orchestration, mode selected at compile-time via `#define MEMLNAUT_MODE_TYPE`.
|
||||||
- `IMLInterface.hpp` — interactive ML controller; wraps MLP + RL with STORE_VALUE / STORE_POSITION modes.
|
- `firmware/MEMLNaut-NISPS/glue/` — hardware bindings:
|
||||||
- `modes/MEMLNautMode.hpp` — concept/contract satisfied by each mode class.
|
- `audio_driver.hpp` — bridges memllib `AudioDriver` block callback → `Mode::process(stereosample_t)` per-sample loop.
|
||||||
- `modes/MEMLNautMode*.hpp` — mode implementations (PAFSynth, ChannelStrip, XIASRI, SoundAnalysisMIDI, VerbFX, BreakOr, Elysiamorfs).
|
- `peripherals.hpp` — wires joystick / pots / buttons → `Mode::set_input` and ML primitives (`draw_weights`, `move_weights`, `train`, `reset`).
|
||||||
- `modes/AudioApps/` — audio engines used by the newer modes (VerbFX, BreakOr, Elysiamorf, RatioSeq).
|
- `midi_io.hpp` — incoming MIDI → mode `note_on/update_bpm/set_playing`; drains mode `ControlEvent` ring → MIDI UART.
|
||||||
- `PAFSynthAudioApp.hpp`, `ChannelStripAudioApp.hpp`, `ThruAudioApp.hpp`, `XIASRIAudioApp.hpp` — older audio engines at the repo root.
|
- `mode_select.hpp` — type aliases mapping `MEMLNautMode<Name>` identifiers to `nisps::modes::*Mode` C++ types. Build script rewrites the active line.
|
||||||
- `XiasriAnalysis.{hpp,cpp}` — real-time audio feature extraction (pitch, aperiodicity, brightness, etc.).
|
- `input_router.hpp`, `output_router.hpp` — top-level `wire_inputs()` / `drain_outputs()` entry points.
|
||||||
- `voicespaces/` — lambdas mapping MLP output vectors to synth parameters. Subdir `ChannelStrip/` for EQ/comp presets.
|
- `firmware/MEMLNaut-NISPS/src/{memllib,daisysp,nisps}` — symlinks into the repo's submodules and `nisps/` library. Required by Arduino-CLI's sketch tree convention.
|
||||||
- `src/memllib/`, `src/memlp/` — git submodules (hardware abstraction + MLP). **Not auto-initialized** — build breaks without `git submodule update --init --recursive`.
|
- `firmware/README.md` — structure, build, and verification notes.
|
||||||
|
- `nisps/` — platform-agnostic C++20 ML / DSP / engines / modes (see "nisps library" below). The firmware glue layer composes these.
|
||||||
|
- `src/memllib/` — git submodule (hardware abstraction). **Not auto-initialized** — build breaks without `git submodule update --init --recursive`.
|
||||||
- `src/daisysp/` — vendored DSP library (filters, drums, effects).
|
- `src/daisysp/` — vendored DSP library (filters, drums, effects).
|
||||||
- `data/` — preset/asset CSVs.
|
- `data/` — preset/asset CSVs.
|
||||||
|
|
||||||
### nisps-core (platform-agnostic C++20 header-only)
|
### nisps library (platform-agnostic C++20)
|
||||||
- `nisps-core/include/nisps/` — `iml.hpp`, `mlp.hpp`, `layer.hpp`, `node.hpp`, `dataset.hpp`, `loss.hpp`, `utils.hpp`.
|
- `nisps/core/` — foundational types, perf attrs, concepts (`AudioEngine`, `Mode`, `MLEngine`), fixed buffers, RNG, math.
|
||||||
- `nisps-core/test/`, `nisps-core/examples/`, `nisps-core/README.md`, `CHANGELOG.md`.
|
- `nisps/ml/mlp.hpp` + `activations.hpp`, `loss.hpp`, `training.hpp`, `init.hpp`, `rl.hpp`, `stats.hpp` — MLP class template with SGD, RMSProp, RL primitives.
|
||||||
|
- `nisps/dsp/` — biquad, delay, reverb, filter, env, osc, pitch_shift, dc_blocker — extracted DSP primitives.
|
||||||
|
- `nisps/engines/` — `paf_synth.hpp`, `channel_strip.hpp`, `xiasri.hpp`, `verb_fx.hpp`, `memlcelium.hpp`, `breakor.hpp`, `elysiamorf.hpp`, `analysis.hpp`, plus `base.hpp` with `NoOpEngine`. Each satisfies `AudioEngine`.
|
||||||
|
- `nisps/modes/` — one mode per engine, all derive from `ModeBase` CRTP scaffold. Schemas in `nisps/modes/generated/` (codegen output).
|
||||||
|
- `nisps/wasm/` — Emscripten bindings (used by the playground build, not the firmware).
|
||||||
|
- `nisps/CMakeLists.txt` + `nisps/build/` — host-target tests.
|
||||||
|
|
||||||
|
### nisps-core (legacy; superseded by `nisps/`)
|
||||||
|
- `nisps-core/` — earlier header-only extraction. Kept until cleanup stream.
|
||||||
|
|
||||||
### Playground (browser ML demo)
|
### Playground (browser ML demo)
|
||||||
- `playground/index.html` — hub linking to the three variants.
|
- `playground/index.html` — hub linking to the three variants.
|
||||||
|
|
@ -75,10 +85,11 @@ MEMLNaut-NISPS: Neural Interactive Shaping of Parameter Spaces. Two living artef
|
||||||
- `playground/RECONCILIATION.md` is supposed to track features landed in a-app but not yet in b/c. File does not currently exist — if you add a-only features, either create it or explicitly accept the drift.
|
- `playground/RECONCILIATION.md` is supposed to track features landed in a-app but not yet in b/c. File does not currently exist — if you add a-only features, either create it or explicitly accept the drift.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
- `memllib` and `memlp` submodules are **not auto-checked-out** — a fresh clone will fail to compile the firmware silently.
|
- `memllib` submodule is **not auto-checked-out** — a fresh clone will fail to compile the firmware silently. (`src/memlp` was deleted; `nisps/ml/mlp.hpp` replaces it.)
|
||||||
|
- The Arduino sketch lives at `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino` (Arduino-CLI requires sketch dir name == sketch file name). It reaches `nisps/`, `src/memllib/`, `src/daisysp/` via symlinks under `firmware/MEMLNaut-NISPS/src/` because Arduino's preprocessor refuses `..` in includes from sketch headers.
|
||||||
|
- `firmware/MEMLNaut-NISPS/glue/mode_select.hpp` `#undef`s Arduino's `sq`/`min`/`max`/`abs`/`round` macros before pulling nisps headers — the nisps engines use those identifiers as method names.
|
||||||
|
- The audio bridge struct `nisps_firmware::g_active_mode_bridge` is `extern` in `glue/audio_driver.hpp` and defined in the .ino; combining `inline` with the `__not_in_flash` section attribute creates a comdat / section conflict at link time.
|
||||||
- Double-scaled loss: C++ `Train()` and WASM `train_ex` both divide by `n` when no sample weights are supplied (known, backward-compat, tracked as `meml-ues`).
|
- Double-scaled loss: C++ `Train()` and WASM `train_ex` both divide by `n` when no sample weights are supplied (known, backward-compat, tracked as `meml-ues`).
|
||||||
- `VerbFXAudioApp` / `MEMLNautModeVerbFX` have **entire analysis blocks commented out** — the mode was migrated from analysis-driven to joystick-driven and cleanup is unfinished. Don't assume XiasriAnalysis wiring is live there.
|
|
||||||
- `XiasriAnalysis` output struct is union-cast to a float array inside XIASRI mode — fragile if the struct layout changes.
|
|
||||||
- Recent fixes cluster around the modular voice: matrix rebuild, `mod_amp` positive-only floor, MLP bypass when untrained, worklet blob-url registration — modular mode is under active churn, so expect rough edges.
|
- Recent fixes cluster around the modular voice: matrix rebuild, `mod_amp` positive-only floor, MLP bypass when untrained, worklet blob-url registration — modular mode is under active churn, so expect rough edges.
|
||||||
- `a-app.js` is the single source of truth. **Do not** reflexively mirror changes to `b-app.js` / `c-app.js` — they are frozen legacy variants.
|
- `a-app.js` is the single source of truth. **Do not** reflexively mirror changes to `b-app.js` / `c-app.js` — they are frozen legacy variants.
|
||||||
- `window.__nisps` only exists with `?debug=1`; Playwright helpers expect this.
|
- `window.__nisps` only exists with `?debug=1`; Playwright helpers expect this.
|
||||||
|
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
//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"
|
|
||||||
#include "src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "src/memllib/audio/AudioDriver.hpp"
|
|
||||||
#include "src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
|
||||||
#include "hardware/structs/bus_ctrl.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
//sound
|
|
||||||
#include "src/memllib/audio/AudioAppBase.hpp"
|
|
||||||
#include "PAFSynthAudioApp.hpp"
|
|
||||||
#include "ChannelStripAudioApp.hpp"
|
|
||||||
|
|
||||||
//interface
|
|
||||||
#include "src/memllib/hardware/memlnaut/display/XYPadView.hpp"
|
|
||||||
#include "src/memllib/hardware/memlnaut/display/MessageView.hpp"
|
|
||||||
#include "src/memllib/hardware/memlnaut/display/VoiceSpaceSelectView.hpp"
|
|
||||||
|
|
||||||
//modes
|
|
||||||
#include "modes/MEMLNautMode.hpp"
|
|
||||||
#include "modes/MEMLNautModePAFSynth.hpp"
|
|
||||||
#include "modes/MEMLNautModeChannelStrip.hpp"
|
|
||||||
#include "modes/MEMLNautModeSoundAnalysisMIDI.hpp"
|
|
||||||
#include "modes/MEMLNautModeXIASRI.hpp"
|
|
||||||
#include "modes/MEMLNautModeBreakOr.hpp"
|
|
||||||
#include "modes/MEMLNautModeVerbFX.hpp"
|
|
||||||
#include "modes/MEMLNautModeElysiamorfs.hpp"
|
|
||||||
#include "modes/MEMLNautModeMEMLCelium.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 MEMLNautModeElysiamorfs
|
|
||||||
// #define MEMLNAUT_MODE_TYPE MEMLNautModeChannelStrip
|
|
||||||
// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth
|
|
||||||
#define MEMLNAUT_MODE_TYPE MEMLNautModeMEMLCelium
|
|
||||||
|
|
||||||
MEMLNAUT_MODE_TYPE AUDIO_MEM MEMLNautModeHub;
|
|
||||||
|
|
||||||
MEMLNautMode auto* AUDIO_MEM currentMode = &MEMLNautModeHub;
|
|
||||||
|
|
||||||
|
|
||||||
#define APP_SRAM __not_in_flash("app")
|
|
||||||
|
|
||||||
bool core1_separate_stack = true;
|
|
||||||
|
|
||||||
uint32_t get_rosc_entropy_seed(int bits) {
|
|
||||||
uint32_t seed = 0;
|
|
||||||
for (int i = 0; i < bits; ++i) {
|
|
||||||
// Wait for a bit of time to allow jitter to accumulate
|
|
||||||
busy_wait_us_32(5);
|
|
||||||
// Pull LSB from ROSC rand output
|
|
||||||
seed <<= 1;
|
|
||||||
seed |= (rosc_hw->randombit & 1);
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Global objects
|
|
||||||
|
|
||||||
std::shared_ptr<MIDIInOut> APP_SRAM midi_interf;
|
|
||||||
|
|
||||||
// Inter-core communication
|
|
||||||
volatile bool APP_SRAM core_0_ready = false;
|
|
||||||
volatile bool APP_SRAM core_1_ready = false;
|
|
||||||
volatile bool APP_SRAM serial_ready = false;
|
|
||||||
volatile bool APP_SRAM interface_ready = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add these macros near other globals
|
|
||||||
#define MEMORY_BARRIER() __sync_synchronize()
|
|
||||||
#define WRITE_VOLATILE(var, val) \
|
|
||||||
do { \
|
|
||||||
MEMORY_BARRIER(); \
|
|
||||||
(var) = (val); \
|
|
||||||
MEMORY_BARRIER(); \
|
|
||||||
} while (0)
|
|
||||||
#define READ_VOLATILE(var) ({ MEMORY_BARRIER(); typeof(var) __temp = (var); MEMORY_BARRIER(); __temp; })
|
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
set_sys_clock_khz(AudioDriver::GetSysClockSpeed(), true);
|
|
||||||
|
|
||||||
bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_PROC1_BITS;
|
|
||||||
|
|
||||||
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.");
|
|
||||||
WRITE_VOLATILE(serial_ready, true);
|
|
||||||
|
|
||||||
// Setup board
|
|
||||||
MEMLNaut::Initialize();
|
|
||||||
pinMode(33, OUTPUT);
|
|
||||||
|
|
||||||
currentMode->setupInterface();
|
|
||||||
|
|
||||||
// Setup interface with memory barrier protection
|
|
||||||
WRITE_VOLATILE(interface_ready, true);
|
|
||||||
Serial.println("Bound interface to MEMLNaut.");
|
|
||||||
|
|
||||||
|
|
||||||
WRITE_VOLATILE(core_0_ready, true);
|
|
||||||
while (!READ_VOLATILE(core_1_ready)) {
|
|
||||||
MEMORY_BARRIER();
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
currentMode->addViews();
|
|
||||||
|
|
||||||
std::shared_ptr<MessageView> helpView = std::make_shared<MessageView>("Help");
|
|
||||||
String title = currentMode->getHelpTitle();
|
|
||||||
helpView->post(title);
|
|
||||||
helpView->post("TA: Down: Clear replay memory");
|
|
||||||
helpView->post("MA: Up: Randomise / Down: Jolt ");
|
|
||||||
helpView->post("MB: Up: Positive reward");
|
|
||||||
helpView->post("MB: Down: Negative reward");
|
|
||||||
helpView->post("X: Learning rate");
|
|
||||||
helpView->post("Y: Reward Scale");
|
|
||||||
helpView->post("Z: Exploration noise");
|
|
||||||
helpView->post("Joystick: Explore / SW: Drag sound");
|
|
||||||
MEMLNaut::Instance()->disp->AddView(helpView);
|
|
||||||
|
|
||||||
MEMLNaut::Instance()->addSystemInfoView();
|
|
||||||
|
|
||||||
Serial.println("Finished initialising core 0.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PERF_DECLARE(MLSTATS);
|
|
||||||
|
|
||||||
#define ML_INFERENCE_PERIOD_US 5000
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
|
|
||||||
PERIODIC_RUN_US(
|
|
||||||
PERF_BEGIN(MLSTATS);
|
|
||||||
currentMode->processAnalysisParams();
|
|
||||||
MEMLNaut::Instance()->loop();
|
|
||||||
PERF_END(MLSTATS);
|
|
||||||
, ML_INFERENCE_PERIOD_US)
|
|
||||||
|
|
||||||
//show profiling stats
|
|
||||||
PERIODIC_RUN_US(
|
|
||||||
static size_t blip_counter = 0;
|
|
||||||
if (blip_counter++ > 10) {
|
|
||||||
blip_counter = 0;
|
|
||||||
Serial.println(".");
|
|
||||||
// Blink LED
|
|
||||||
digitalWrite(33, HIGH);
|
|
||||||
constexpr float audioHeadroomMul = 1.0 / (1000000 * 48.0 / kSampleRate);
|
|
||||||
Serial.printf("ml: %d, aud: %d, q: %f\n", PERF_GET_MEAN(MLSTATS), AUDIOLOOP_MEAN, AUDIOLOOP_MEAN * audioHeadroomMul);
|
|
||||||
} else {
|
|
||||||
// Un-blink LED
|
|
||||||
digitalWrite(33, LOW);
|
|
||||||
},
|
|
||||||
100000)
|
|
||||||
|
|
||||||
currentMode->loopCore0();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AUDIO_FUNC(audio_block_callback)(float in[][kBufferSize], float out[][kBufferSize], size_t n_channels, size_t n_frames) {
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n_frames; ++i) {
|
|
||||||
|
|
||||||
stereosample_t x{
|
|
||||||
in[0][i],
|
|
||||||
in[1][i]
|
|
||||||
},
|
|
||||||
y;
|
|
||||||
|
|
||||||
y = currentMode->process(x);
|
|
||||||
|
|
||||||
out[0][i] = y.L;
|
|
||||||
out[1][i] = y.R;
|
|
||||||
|
|
||||||
// PERIODIC_RUN(
|
|
||||||
// Serial.printf("x: %f\n", x.L + x.R);
|
|
||||||
// , 100);
|
|
||||||
|
|
||||||
currentMode->analyse(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void setup1() {
|
|
||||||
while (!READ_VOLATILE(serial_ready)) {
|
|
||||||
MEMORY_BARRIER();
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!READ_VOLATILE(interface_ready)) {
|
|
||||||
MEMORY_BARRIER();
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (midi_interf) {
|
|
||||||
currentMode->setupMIDI(midi_interf);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMode->setupAudio(AudioDriver::GetSampleRate());
|
|
||||||
|
|
||||||
AudioDriver::SetBlockCallback(audio_block_callback);
|
|
||||||
|
|
||||||
// Start audio driver
|
|
||||||
AudioDriver::Setup();
|
|
||||||
|
|
||||||
WRITE_VOLATILE(core_1_ready, true);
|
|
||||||
while (!READ_VOLATILE(core_0_ready)) {
|
|
||||||
MEMORY_BARRIER();
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("Finished initialising core 1.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop1() {
|
|
||||||
// Audio app parameter processing loop
|
|
||||||
PERIODIC_RUN_US(
|
|
||||||
currentMode->loop();
|
|
||||||
, ML_INFERENCE_PERIOD_US)
|
|
||||||
|
|
||||||
PERIODIC_RUN_US(
|
|
||||||
midi_interf->Poll();
|
|
||||||
, 1000)
|
|
||||||
}
|
|
||||||
|
|
@ -1,358 +0,0 @@
|
||||||
#ifndef __PAF_SYNTH_AUDIO_APP_HPP__
|
|
||||||
#define __PAF_SYNTH_AUDIO_APP_HPP__
|
|
||||||
|
|
||||||
#include "src/memllib/audio/AudioAppBase.hpp" // Added missing include
|
|
||||||
#include "src/memllib/synth/maximilian.h" // Added missing include for maxiSettings, maxiOsc, maxiTrigger, maxiDelayline, maxiEnvGen, maxiLine
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory> // Added for std::shared_ptr
|
|
||||||
|
|
||||||
#include "src/memllib/synth/maxiPAF.hpp"
|
|
||||||
#include "src/memllib/synth/ADSRLite.hpp"
|
|
||||||
#include "src/memllib/interface/InterfaceBase.hpp" // Added missing include
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include "voicespaces/VoiceSpaces.hpp"
|
|
||||||
|
|
||||||
#include "voicespaces/VoiceSpace1.hpp"
|
|
||||||
#include "voicespaces/VoiceSpace2.hpp"
|
|
||||||
#include "voicespaces/VoiceSpacePerc.hpp"
|
|
||||||
#include "voicespaces/VoiceSpaceSingle1.hpp"
|
|
||||||
#include "voicespaces/VoiceSpaceQuadDetune.hpp"
|
|
||||||
#include "voicespaces/VoiceSpaceQuadOct.hpp"
|
|
||||||
#include "voicespaces/VoiceSpaceQuadDist.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// #define ARPEGGIATOR
|
|
||||||
|
|
||||||
template<size_t NPARAMS=33>
|
|
||||||
// template<size_t NPARAMS=1>
|
|
||||||
class PAFSynthAudioApp : 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PAFSynthAudioApp() : 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;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maxiOsc testosc;
|
|
||||||
stereosample_t __force_inline Process(const stereosample_t x) override
|
|
||||||
{
|
|
||||||
float x1[1];
|
|
||||||
|
|
||||||
// float p0 = testosc.sinewave(baseFreq);
|
|
||||||
float fbsmooth = (fbzm1 * fbSmoothAlpha) + (feedback * (1.f-fbSmoothAlpha));
|
|
||||||
fbzm1 = fbsmooth;
|
|
||||||
|
|
||||||
float freq0 = baseFreq * (1.f + fbsmooth);
|
|
||||||
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;
|
|
||||||
|
|
||||||
const float freq3 = freq2 * detune3;
|
|
||||||
|
|
||||||
paf3.play(x1, 1, freq3, freq3 + (paf3_cf * freq3), paf3_bw * freq3, paf3_vib, paf3_vfr, paf3_shift, 1);
|
|
||||||
const float p3 = *x1 * p3Gain;
|
|
||||||
|
|
||||||
float y = p0 + p1 + p2 + p3;
|
|
||||||
|
|
||||||
const float rm = p0 * p1 * p2 * p3;
|
|
||||||
//
|
|
||||||
y = y + (rm * rmGain);
|
|
||||||
|
|
||||||
float shape = sinf(y * TWOPI);
|
|
||||||
shape = sinf(((shape * TWOPI) * sineShapeGain) + sineShapeASym);
|
|
||||||
y = y + (shape * sineShapeMix);
|
|
||||||
|
|
||||||
#ifdef ARPEGGIATOR
|
|
||||||
// const float ph = phasorOsc.phasor(1);
|
|
||||||
// const bool euclidNewNote = euclidean(ph, 12, euclidN, 0, 0.1f);
|
|
||||||
// if(zxdetect.onZX(euclidNewNote)) {
|
|
||||||
// envamp=0.8f;
|
|
||||||
// freqIndex++;
|
|
||||||
// if(freqIndex >= nFREQs) {
|
|
||||||
// freqIndex = 0;
|
|
||||||
// }
|
|
||||||
// baseFreq = frequencies[freqIndex];
|
|
||||||
// }else{
|
|
||||||
// // constexpr float envdec = 0.2f/9000.f;
|
|
||||||
// envamp -= envdec;
|
|
||||||
// if (envamp < 0.f) {
|
|
||||||
// envamp = 0.f;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#else
|
|
||||||
// if(newNote) {
|
|
||||||
// newNote = false;
|
|
||||||
// envamp=0.8f;
|
|
||||||
// }else{
|
|
||||||
// // constexpr float envdec = 0.2f/9000.f;
|
|
||||||
// envamp -= envdec;
|
|
||||||
// if (envamp < 0.f) {
|
|
||||||
// envamp = 0.f;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#endif
|
|
||||||
float envval = env.play();
|
|
||||||
y = y * envval;
|
|
||||||
|
|
||||||
// #ifndef ARPEGGIATOR
|
|
||||||
// y *= noteVel;
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
y = tanhf(y);
|
|
||||||
|
|
||||||
float d1 = (dl1.play(y, delayMax, dlfb) * dl1mix);
|
|
||||||
y = y + d1;// + d2;
|
|
||||||
stereosample_t ret { y, y };
|
|
||||||
feedback = y * feedbackGain;
|
|
||||||
// frame++;
|
|
||||||
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);
|
|
||||||
|
|
||||||
arpFreq = frequencies[0];
|
|
||||||
envamp=1.f;
|
|
||||||
|
|
||||||
env.setup(500,500,0.8,1000,sampleRatef);
|
|
||||||
|
|
||||||
queue_init(&qMIDINoteOn, sizeof(uint8_t)*2, 1);
|
|
||||||
queue_init(&qMIDINoteOff, sizeof(uint8_t)*2, 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float mtof(uint8_t note) {
|
|
||||||
// Convert MIDI note to frequency
|
|
||||||
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)) {
|
|
||||||
// Serial.printf("PAFSynthAudioApp::ProcessParams - Received MIDI Note On: %d, Velocity: %d\n", midimsg[0], midimsg[1]);
|
|
||||||
baseFreq = mtof(midimsg[0]);
|
|
||||||
// Serial.printf("PAFSynthAudioApp::loop - Note On: %d, Freq: %.2f Hz, Velocity: %d\n", midimsg[0], baseFreq, midimsg[1]);
|
|
||||||
noteVel = midimsg[1] / 127.0f; // Normalize velocity to [0, 1]
|
|
||||||
noteVel = noteVel * noteVel; // Square the velocity for more pronounced effect
|
|
||||||
newNote = true;
|
|
||||||
env.trigger(noteVel);
|
|
||||||
currNote = midimsg[0];
|
|
||||||
}
|
|
||||||
if (firstParamsReceived && queue_try_remove(&qMIDINoteOff, &midimsg)) {
|
|
||||||
// Serial.printf("PAFSynthAudioApp::ProcessParams - Received MIDI Note On: %d, Velocity: %d\n", midimsg[0], midimsg[1]);
|
|
||||||
if (currNote == midimsg[0]) {
|
|
||||||
env.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AudioAppBase<NPARAMS>::loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessParams(const std::array<float, NPARAMS>& params)
|
|
||||||
{
|
|
||||||
firstParamsReceived = true;
|
|
||||||
currentVoiceSpace(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_t qMIDINoteOn, qMIDINoteOff;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
maxiPAFOperator paf0;
|
|
||||||
maxiPAFOperator paf1;
|
|
||||||
maxiPAFOperator paf2;
|
|
||||||
maxiPAFOperator paf3;
|
|
||||||
|
|
||||||
maxiDelayline<11000> dl1;
|
|
||||||
// maxiDelayline<15100> dl2;
|
|
||||||
|
|
||||||
maxiOsc pulse;
|
|
||||||
|
|
||||||
ADSRLite env;
|
|
||||||
|
|
||||||
float frame=0;
|
|
||||||
|
|
||||||
float feedback=0.f, feedbackGain=0.f;
|
|
||||||
|
|
||||||
float p0Gain=1.f, p1Gain = 1.f, p2Gain=1.f, p3Gain=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 paf0_bw = 100;
|
|
||||||
float paf1_bw = 5000;
|
|
||||||
float paf2_bw = 5000;
|
|
||||||
float paf3_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 dl1mix = 0.0f;
|
|
||||||
float dl2mix = 0.0f;
|
|
||||||
float dlfb = 0.5f;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
maxiOsc phasorOsc;
|
|
||||||
maxiTrigger zxdetect;
|
|
||||||
|
|
||||||
size_t euclidN=4;
|
|
||||||
|
|
||||||
float baseFreq = 50.0f; // Base frequency for the synth
|
|
||||||
bool newNote=false;
|
|
||||||
float noteVel = 0.f;
|
|
||||||
bool firstParamsReceived = false;
|
|
||||||
|
|
||||||
float envdec=0.2f/9000.f; // Decay rate for the envelope
|
|
||||||
|
|
||||||
float sampleRatef = maxiSettings::getSampleRate();
|
|
||||||
|
|
||||||
float fbzm1=0.f;
|
|
||||||
size_t delayMax=10;
|
|
||||||
float fbSmoothAlpha=0.95f;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __PAF_SYNTH_AUDIO_APP_HPP__
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#ifndef __THRU_AUDIO_APP_HPP__
|
|
||||||
#define __THRU_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"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<size_t NPARAMS=8> //params just go out as MIDI for this one
|
|
||||||
class ThruAudioApp : 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ThruAudioApp() : AudioAppBase<NPARAMS>() {
|
|
||||||
auto voiceSpaceThru = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
};
|
|
||||||
|
|
||||||
voiceSpaces[0] = {"thru", voiceSpaceThru};
|
|
||||||
|
|
||||||
currentVoiceSpace = voiceSpaces[0].mappingFunction;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
__attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override
|
|
||||||
{
|
|
||||||
// float y = x.L + x.R;
|
|
||||||
// y = filt.play(y);
|
|
||||||
// return stereosample_t{y, y};
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Setup(float sample_rate, std::shared_ptr<InterfaceBase> interface) override
|
|
||||||
{
|
|
||||||
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
|
|
||||||
maxiSettings::sampleRate = sample_rate;
|
|
||||||
filt.set(maxiBiquad::filterTypes::HIGHPASS, 20.f, 0.707f, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline)) void ProcessParams(const std::array<float, NPARAMS>& params)
|
|
||||||
{
|
|
||||||
currentVoiceSpace(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
maxiBiquad filt;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
#ifndef __XIASRI_AUDIO_APP_HPP__
|
|
||||||
#define __XIASRI_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 XIASRIAudioApp : 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XIASRIAudioApp() : AudioAppBase<NPARAMS>() {
|
|
||||||
// auto voiceSpaceXIASRI = [this](const std::array<float, NPARAMS>& params) {
|
|
||||||
// };
|
|
||||||
|
|
||||||
// voiceSpaces[0] = {"XIASRI", voiceSpaceXIASRI};
|
|
||||||
|
|
||||||
// currentVoiceSpace = voiceSpaces[0].mappingFunction;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
__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.6f;
|
|
||||||
// float dl2mix = smoothParams[1] * 0.6f;
|
|
||||||
// float dl3mix = smoothParams[2] * 0.8f;
|
|
||||||
// float ymix = smoothParams[12] * 1.0f;
|
|
||||||
|
|
||||||
// float allp1fb = smoothParams[3] * 0.96f;
|
|
||||||
// float allp2fb = smoothParams[4] * 0.96f;
|
|
||||||
// float allp3fb = smoothParams[5] * 0.96f;
|
|
||||||
|
|
||||||
// float comb1fb = (smoothParams[6] * 0.95f);
|
|
||||||
// float comb2fb = (smoothParams[7] * 0.95f);
|
|
||||||
// float comb3fb = (smoothParams[8] * 0.95f);
|
|
||||||
|
|
||||||
// float dl1fb = (smoothParams[9] * 0.95f);
|
|
||||||
// float dl2fb = (smoothParams[10] * 0.95f);
|
|
||||||
// float dl3fb = (smoothParams[11] * 0.95f);
|
|
||||||
|
|
||||||
// float y = dcb.play(mix, 0.99f) * 3.f;
|
|
||||||
|
|
||||||
// float y1 = allp1.allpass(y, 73, allp1fb);
|
|
||||||
// y1 = comb1.combfb(y1, 255, comb1fb);
|
|
||||||
|
|
||||||
// float y2 = allp2.allpass(y, 987, allp2fb);
|
|
||||||
// y2 = comb2.combfb(y2, 1616, comb2fb);
|
|
||||||
|
|
||||||
// float y3 = comb3.combfb(y3, 847, comb3fb);
|
|
||||||
// y3 = allp3.allpass(y, 707, allp3fb);
|
|
||||||
|
|
||||||
// y = y1 + y2 + y3;
|
|
||||||
|
|
||||||
// float d1 = (dl1.play(y, 7000, dl1fb) * dl1mix);
|
|
||||||
// float d2 = (dl2.play(y, 18000, dl2fb) * dl2mix);
|
|
||||||
// float d3 = (dl3.play(y, 2399, dl3fb) * dl3mix);
|
|
||||||
|
|
||||||
|
|
||||||
// y = (y * ymix) + d1 + d2 + d3;
|
|
||||||
|
|
||||||
// y = y * 1.9f;
|
|
||||||
|
|
||||||
// y = tanhf(y);
|
|
||||||
|
|
||||||
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<InterfaceBase> interface) override
|
|
||||||
{
|
|
||||||
AudioAppBase<NPARAMS>::Setup(sample_rate, interface);
|
|
||||||
maxiSettings::sampleRate = sample_rate;
|
|
||||||
pitchshifter_.Init(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};
|
|
||||||
|
|
||||||
// maxiDelayline<10000> dl1;
|
|
||||||
// maxiDelayline<30100> dl2;
|
|
||||||
// maxiDelayline<2401> dl3;
|
|
||||||
|
|
||||||
// maxiReverbFilters<300> allp1;
|
|
||||||
// maxiReverbFilters<1000> allp2;
|
|
||||||
// maxiReverbFilters<1000> allp3;
|
|
||||||
|
|
||||||
// maxiReverbFilters<300> comb1;
|
|
||||||
// maxiReverbFilters<2000> comb2;
|
|
||||||
// maxiReverbFilters<1000> comb3;
|
|
||||||
|
|
||||||
// maxiDCBlocker dcb;
|
|
||||||
|
|
||||||
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<kN_Params> smoother{150.f, kSampleRate};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
#include "XiasriAnalysis.hpp"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "src/memllib/hardware/memlnaut/Pins.hpp"
|
|
||||||
#include "src/memllib/utils/Maths.hpp"
|
|
||||||
#include "src/memllib/PicoDefs.hpp"
|
|
||||||
|
|
||||||
#define COMMONHPFFREQ 20.f
|
|
||||||
#define ZXFREQ 4000.f
|
|
||||||
|
|
||||||
XiasriAnalysis::XiasriAnalysis(const float sample_rate) :
|
|
||||||
sample_rate_(sample_rate),
|
|
||||||
one_over_sample_rate_(1.0f / sample_rate),
|
|
||||||
zc_median_filter_(kZC_MedianFilterSize) {
|
|
||||||
|
|
||||||
// Initialize filters and detectors
|
|
||||||
common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, COMMONHPFFREQ, 0.707f, 0);
|
|
||||||
// Zero crossing
|
|
||||||
zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, ZXFREQ, 0.707f, 0);
|
|
||||||
elapsed_samples_ = 0;
|
|
||||||
// Envelope follower
|
|
||||||
ef_follower_.setAttack(10.0f);
|
|
||||||
ef_follower_.setRelease(100.0f);
|
|
||||||
ef_deriv_y_ = 0;
|
|
||||||
// Brightness
|
|
||||||
br_lpf1_.set(maxiBiquad::filterTypes::LOWPASS, 1000.0f, 0.707f, 0);
|
|
||||||
br_hpf2_.set(maxiBiquad::filterTypes::HIGHPASS, 1000.0f, 0.707f, 0);
|
|
||||||
br_lpf2_.set(maxiBiquad::filterTypes::LOWPASS, 4000.0f, 0.707f, 0);
|
|
||||||
for (size_t i = 0; i < kBR_NBands; ++i) {
|
|
||||||
br_follower_[i].setAttack(10.f);
|
|
||||||
br_follower_[i].setRelease(100.f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XiasriAnalysis::ReinitFilters() {
|
|
||||||
// Reinitialize all filters with correct maxiSettings sample rate
|
|
||||||
common_hpf_.set(maxiBiquad::filterTypes::HIGHPASS, COMMONHPFFREQ, 0.707f, 0);
|
|
||||||
zc_lpf_.set(maxiBiquad::filterTypes::LOWPASS, ZXFREQ, 0.707f, 0);
|
|
||||||
br_lpf1_.set(maxiBiquad::filterTypes::LOWPASS, 1000.0f, 0.707f, 0);
|
|
||||||
br_hpf2_.set(maxiBiquad::filterTypes::HIGHPASS, 1000.0f, 0.707f, 0);
|
|
||||||
br_lpf2_.set(maxiBiquad::filterTypes::LOWPASS, 4000.0f, 0.707f, 0);
|
|
||||||
ef_follower_.setAttack(10.0f);
|
|
||||||
ef_follower_.setRelease(100.0f);
|
|
||||||
for (size_t i = 0; i < kBR_NBands; ++i) {
|
|
||||||
br_follower_[i].setAttack(10.f);
|
|
||||||
br_follower_[i].setRelease(100.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use fast approximation (~10 cycles):
|
|
||||||
// __force_inline float fast_log2(float x) {
|
|
||||||
// static constexpr float INV_MANTISSA_SCALE = 1.0f / 8388608.0f;
|
|
||||||
// uint32_t bits;
|
|
||||||
// memcpy(&bits, &x, sizeof(float)); // Standards-compliant type punning
|
|
||||||
// return (float)(((bits >> 23) & 0xFF) - 127) +
|
|
||||||
// (float)(bits & 0x7FFFFF) * INV_MANTISSA_SCALE;
|
|
||||||
// }
|
|
||||||
|
|
||||||
inline float logEnvelopeFast(float linearEnv) {
|
|
||||||
// -60 dBFS corresponds to a linear amplitude ratio of 10^(-60/20) = 10^(-3) = 0.001
|
|
||||||
static constexpr float MIN_ENV = 1e-3f; // 10^(-60dB/20dB) = 0.001 linear
|
|
||||||
|
|
||||||
// Mathematical derivation:
|
|
||||||
// We want to map linear amplitude [0.001, 1.0] to normalized range [0, 1]
|
|
||||||
// where 0.001 corresponds to -60 dBFS and 1.0 corresponds to 0 dBFS
|
|
||||||
//
|
|
||||||
// Using logarithmic mapping: output = (log2(input) - log2(min)) / (log2(max) - log2(min))
|
|
||||||
// log2(0.001) = log2(10^-3) = -3 * log2(10) ≈ -9.966
|
|
||||||
// log2(1.0) = 0
|
|
||||||
// Range = 0 - (-9.966) = 9.966
|
|
||||||
|
|
||||||
static constexpr float LOG2_MIN_ENV = -3.0f * 3.321928095f; // -3 * log2(10) ≈ -9.966
|
|
||||||
static constexpr float LOG2_MAX_ENV = 0.0f; // log2(1.0) = 0
|
|
||||||
static constexpr float LOG_RANGE = LOG2_MAX_ENV - LOG2_MIN_ENV; // 9.966
|
|
||||||
static constexpr float INV_LOG_RANGE = 1.0f / LOG_RANGE; // 1 / 9.966 ≈ 0.1003
|
|
||||||
|
|
||||||
// Clamp input to minimum envelope value
|
|
||||||
linearEnv = (linearEnv > MIN_ENV) ? linearEnv : MIN_ENV;
|
|
||||||
|
|
||||||
float log2_val = log2f(linearEnv);
|
|
||||||
|
|
||||||
// Map to [0,1]: (log2_val - log2_min) / (log2_max - log2_min)
|
|
||||||
float y = (log2_val - LOG2_MIN_ENV) * INV_LOG_RANGE;
|
|
||||||
|
|
||||||
// PERIODIC_RUN(
|
|
||||||
// Serial.printf("%f %f %f\n", linearEnv, log2_val, y);
|
|
||||||
// ,100
|
|
||||||
// );
|
|
||||||
|
|
||||||
|
|
||||||
// Clamp to [0,1] range (should be unnecessary given our math, but safety first)
|
|
||||||
y = (y > 1.0f) ? 1.0f : y;
|
|
||||||
y = (y < 0.0f) ? 0.0f : y;
|
|
||||||
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((hot, flatten))
|
|
||||||
XiasriAnalysis::parameters_t AUDIO_FUNC(XiasriAnalysis::Process)(const float x) {
|
|
||||||
parameters_t params = {};
|
|
||||||
|
|
||||||
// Pre-filter
|
|
||||||
float pre_filtered = common_hpf_.play(x);
|
|
||||||
|
|
||||||
// Zero crossing detection
|
|
||||||
float zc_y = zc_lpf_.play(pre_filtered);
|
|
||||||
bool positive_zero_crossing = zc_detector_.zx(zc_y);
|
|
||||||
if (positive_zero_crossing) {
|
|
||||||
size_t median_elapsed_samples = zc_median_filter_.process(elapsed_samples_);
|
|
||||||
zc_buffer_.push(median_elapsed_samples);
|
|
||||||
elapsed_samples_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert zero crossing value to pitch
|
|
||||||
size_t zc_value = zc_buffer_[zc_buffer_.size() - 1];
|
|
||||||
|
|
||||||
// OPTIMIZED: Direct division instead of divide-after-multiply
|
|
||||||
float pitch = sample_rate_ / static_cast<float>(zc_value);
|
|
||||||
float normalized_pitch = maxiMap::clamp((pitch - kPitchMin) * kPitchScale, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
elapsed_samples_++;
|
|
||||||
|
|
||||||
// Aperiodicity calculation
|
|
||||||
float zc_copy[kZC_ZCBufferSize];
|
|
||||||
for (size_t i = 0; i < kZC_ZCBufferSize; ++i) {
|
|
||||||
zc_copy[i] = static_cast<float>(zc_buffer_[i]);
|
|
||||||
}
|
|
||||||
float mad = meanAbsoluteDeviation(zc_copy, kZC_ZCBufferSize);
|
|
||||||
|
|
||||||
// OPTIMIZED: Multiply by reciprocal
|
|
||||||
float medianPeriod = static_cast<float>(zc_value);
|
|
||||||
float medianPeriodRcpr = 1.0f / (medianPeriod + 1.0f);
|
|
||||||
float relativeMad = mad * medianPeriodRcpr;
|
|
||||||
|
|
||||||
static constexpr float ONE_OVER_RELATIVE_MAD_MAX = 1.0f / 0.3f;
|
|
||||||
float normalizedAperiodicity = fminf(1.0f, relativeMad * ONE_OVER_RELATIVE_MAD_MAX);
|
|
||||||
|
|
||||||
// Envelope follower
|
|
||||||
float ef_y = ef_follower_.play(pre_filtered);
|
|
||||||
ef_y = logEnvelopeFast(ef_y);
|
|
||||||
|
|
||||||
float ef_d_dy = ef_y - ef_deriv_y_;
|
|
||||||
ef_deriv_y_ = ef_y;
|
|
||||||
|
|
||||||
// OPTIMIZED: Branchless rectify and scale
|
|
||||||
ef_d_dy = fmaxf(0.0f, fminf(ef_d_dy * 10.0f, 1.0f));
|
|
||||||
|
|
||||||
// Brightness calculation
|
|
||||||
float br_low = br_lpf1_.play(pre_filtered);
|
|
||||||
float br_high = br_hpf2_.play(pre_filtered);
|
|
||||||
br_high = br_lpf2_.play(br_high);
|
|
||||||
|
|
||||||
br_low = br_follower_[0].play(br_low);
|
|
||||||
br_high = br_follower_[1].play(br_high);
|
|
||||||
|
|
||||||
// OPTIMIZED: Branchless with reciprocal
|
|
||||||
float br_energy = br_low + br_high + 1e-8f;
|
|
||||||
br_high = fminf(br_high / br_energy, 1.0f); // Compiler may optimize this division
|
|
||||||
|
|
||||||
// Fill parameters
|
|
||||||
params.pitch = normalized_pitch;
|
|
||||||
params.aperiodicity = normalizedAperiodicity;
|
|
||||||
params.energy = ef_y;
|
|
||||||
params.attack = ef_d_dy;
|
|
||||||
params.brightness = br_high;
|
|
||||||
params.energy_crude = fabsf(x);
|
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
#ifndef __SAX_ANALYSIS_HPP__
|
|
||||||
#define __SAX_ANALYSIS_HPP__
|
|
||||||
|
|
||||||
#include "src/memllib/audio/AudioDriver.hpp"
|
|
||||||
#include "src/memllib/utils/MedianFilter.h"
|
|
||||||
#include "src/memllib/utils/CircularBuffer.hpp"
|
|
||||||
#include "src/memllib/synth/maximilian.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
|
|
||||||
class XiasriAnalysis {
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct parameters_t {
|
|
||||||
float pitch;
|
|
||||||
float aperiodicity;
|
|
||||||
float energy;
|
|
||||||
float attack;
|
|
||||||
float brightness;
|
|
||||||
float energy_crude;
|
|
||||||
};
|
|
||||||
static constexpr size_t kN_Params = sizeof(parameters_t) / sizeof(float);
|
|
||||||
|
|
||||||
XiasriAnalysis(const float sample_rate);
|
|
||||||
|
|
||||||
parameters_t Process(const float x);
|
|
||||||
|
|
||||||
// Reinitialize filters after maxiSettings is properly configured
|
|
||||||
void ReinitFilters();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const float sample_rate_;
|
|
||||||
const float one_over_sample_rate_;
|
|
||||||
// Pre-filter
|
|
||||||
maxiBiquad common_hpf_;
|
|
||||||
// Zero crossing
|
|
||||||
static constexpr size_t kZC_MedianFilterSize = 16;
|
|
||||||
static constexpr size_t kZC_ZCBufferSize = 32;
|
|
||||||
static constexpr float kPitchMin = 20.0f;
|
|
||||||
static constexpr float kPitchMax = 800.0f;
|
|
||||||
static constexpr float kPitchRange = kPitchMax - kPitchMin;
|
|
||||||
static constexpr float kPitchScale = 1.0f / kPitchRange;
|
|
||||||
maxiBiquad zc_lpf_;
|
|
||||||
maxiZeroCrossingDetector zc_detector_;
|
|
||||||
size_t elapsed_samples_;
|
|
||||||
MedianFilter<size_t> zc_median_filter_;
|
|
||||||
CircularBuffer<size_t, kZC_ZCBufferSize> zc_buffer_;
|
|
||||||
// Envelope follower
|
|
||||||
maxiEnvelopeFollowerF ef_follower_;
|
|
||||||
float ef_deriv_y_;
|
|
||||||
// Brightness
|
|
||||||
static constexpr size_t kBR_NBands = 2;
|
|
||||||
maxiBiquad br_lpf1_;
|
|
||||||
maxiBiquad br_hpf2_;
|
|
||||||
maxiBiquad br_lpf2_;
|
|
||||||
maxiEnvelopeFollowerF br_follower_[kBR_NBands];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // __SAX_ANALYSIS_HPP__
|
|
||||||
189
firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino
Normal file
189
firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// firmware/MEMLNaut-NISPS.ino — Thin entry point.
|
||||||
|
//
|
||||||
|
// The heavy lifting now lives under:
|
||||||
|
// - nisps/... — platform-agnostic ML, DSP, engines, modes
|
||||||
|
// - firmware/glue/... — hardware bindings (audio driver, MIDI,
|
||||||
|
// peripherals, input/output routers)
|
||||||
|
//
|
||||||
|
// This file does only:
|
||||||
|
// 1. Pick a mode at compile time (`MEMLNAUT_MODE_TYPE`).
|
||||||
|
// 2. Instantiate it (in `AUDIO_MEM` so it lives in SRAM).
|
||||||
|
// 3. setup() / loop() on core 0:
|
||||||
|
// - boot board
|
||||||
|
// - bind peripherals → mode.set_input
|
||||||
|
// - bind MIDI in → mode.note_on/update_bpm/...
|
||||||
|
// - run mode.tick_control() at ML cadence (5ms)
|
||||||
|
// 4. setup1() / loop1() on core 1:
|
||||||
|
// - register the audio bridge so AudioDriver streams into mode.process
|
||||||
|
// - pump engine events / drain MIDI out at sub-ms cadence
|
||||||
|
|
||||||
|
// ---- Hardware ----
|
||||||
|
#include "src/memllib/PicoDefs.hpp"
|
||||||
|
#include "src/memllib/audio/AudioDriver.hpp"
|
||||||
|
#include "src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
||||||
|
#include "src/memllib/interface/MIDIInOut.hpp"
|
||||||
|
#include "src/memllib/utils/perf.hpp"
|
||||||
|
#include "hardware/structs/bus_ctrl.h"
|
||||||
|
|
||||||
|
// ---- Glue ----
|
||||||
|
#include "glue/audio_driver.hpp"
|
||||||
|
#include "glue/input_router.hpp"
|
||||||
|
#include "glue/midi_io.hpp"
|
||||||
|
#include "glue/mode_select.hpp"
|
||||||
|
#include "glue/output_router.hpp"
|
||||||
|
|
||||||
|
// ---- Mode selection ----
|
||||||
|
// Build script rewrites which line is uncommented.
|
||||||
|
// #define MEMLNAUT_MODE_TYPE MEMLNautModeSoundAnalysisMIDI
|
||||||
|
// #define MEMLNAUT_MODE_TYPE MEMLNautModeXIASRI
|
||||||
|
// #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
|
||||||
|
// #define MEMLNAUT_MODE_TYPE MEMLNautModeMEMLCelium
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using ActiveMode = MEMLNAUT_MODE_TYPE;
|
||||||
|
|
||||||
|
ActiveMode AUDIO_MEM g_mode;
|
||||||
|
|
||||||
|
// Definition of the audio bridge declared in glue/audio_driver.hpp.
|
||||||
|
// Lives in the audio SRAM section so the per-block callback dereferences it
|
||||||
|
// without paying flash latency.
|
||||||
|
volatile nisps_firmware::ActiveModeBridge AUDIO_MEM nisps_firmware::g_active_mode_bridge{};
|
||||||
|
|
||||||
|
// Global MIDI handle (shared across cores like the legacy entry point did).
|
||||||
|
std::shared_ptr<MIDIInOut> APP_SRAM g_midi;
|
||||||
|
|
||||||
|
// Inter-core handshake flags (mirror legacy semantics).
|
||||||
|
volatile bool APP_SRAM g_core0_ready = false;
|
||||||
|
volatile bool APP_SRAM g_core1_ready = false;
|
||||||
|
volatile bool APP_SRAM g_serial_ready = false;
|
||||||
|
volatile bool APP_SRAM g_iface_ready = false;
|
||||||
|
|
||||||
|
bool core1_separate_stack = true;
|
||||||
|
|
||||||
|
// Audio block callback — placed in SRAM via __not_in_flash_func so the audio
|
||||||
|
// ISR avoids XIP latency. Forwards into the (header-inline) dispatch helper.
|
||||||
|
void AUDIO_FUNC(audio_block_callback)(
|
||||||
|
float in[][kBufferSize],
|
||||||
|
float out[][kBufferSize],
|
||||||
|
size_t n_channels,
|
||||||
|
size_t n_frames) {
|
||||||
|
nisps_firmware::dispatch_audio_block(in, out, n_channels, n_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_rosc_entropy_seed(int bits) {
|
||||||
|
uint32_t seed = 0;
|
||||||
|
for (int i = 0; i < bits; ++i) {
|
||||||
|
busy_wait_us_32(5);
|
||||||
|
seed <<= 1;
|
||||||
|
seed |= (rosc_hw->randombit & 1);
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Core 0 — UI / hardware polling / ML inference
|
||||||
|
// =====================================================================
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
set_sys_clock_khz(AudioDriver::GetSysClockSpeed(), true);
|
||||||
|
bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS
|
||||||
|
| BUSCTRL_BUS_PRIORITY_DMA_R_BITS
|
||||||
|
| BUSCTRL_BUS_PRIORITY_PROC1_BITS;
|
||||||
|
|
||||||
|
const uint32_t seed = get_rosc_entropy_seed(32);
|
||||||
|
srand(seed);
|
||||||
|
g_mode.ml().seed(static_cast<uint64_t>(seed));
|
||||||
|
g_mode.ml().draw_weights(g_mode.param_schema().default_spread);
|
||||||
|
|
||||||
|
g_midi = std::make_shared<MIDIInOut>();
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Serial initialised.");
|
||||||
|
WRITE_VOLATILE(g_serial_ready, true);
|
||||||
|
|
||||||
|
MEMLNaut::Initialize();
|
||||||
|
pinMode(33, OUTPUT);
|
||||||
|
|
||||||
|
// Wire hardware → mode I/O channels.
|
||||||
|
nisps_firmware::wire_inputs(g_mode);
|
||||||
|
|
||||||
|
WRITE_VOLATILE(g_iface_ready, true);
|
||||||
|
Serial.println("Bound peripherals to mode.");
|
||||||
|
|
||||||
|
WRITE_VOLATILE(g_core0_ready, true);
|
||||||
|
while (!READ_VOLATILE(g_core1_ready)) {
|
||||||
|
MEMORY_BARRIER();
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMLNaut::Instance()->addSystemInfoView();
|
||||||
|
Serial.println("Finished initialising core 0.");
|
||||||
|
}
|
||||||
|
|
||||||
|
PERF_DECLARE(MLSTATS);
|
||||||
|
|
||||||
|
#define ML_INFERENCE_PERIOD_US 5000
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
PERIODIC_RUN_US({
|
||||||
|
PERF_BEGIN(MLSTATS);
|
||||||
|
g_mode.tick_control();
|
||||||
|
MEMLNaut::Instance()->loop();
|
||||||
|
PERF_END(MLSTATS);
|
||||||
|
}, ML_INFERENCE_PERIOD_US)
|
||||||
|
|
||||||
|
PERIODIC_RUN_US({
|
||||||
|
static size_t blip_counter = 0;
|
||||||
|
if (blip_counter++ > 10) {
|
||||||
|
blip_counter = 0;
|
||||||
|
Serial.println(".");
|
||||||
|
digitalWrite(33, HIGH);
|
||||||
|
constexpr float audioHeadroomMul = 1.0f / (1000000.f * 48.0f / kSampleRate);
|
||||||
|
Serial.printf("ml: %d, aud: %d, q: %f\n",
|
||||||
|
PERF_GET_MEAN(MLSTATS),
|
||||||
|
AUDIOLOOP_MEAN,
|
||||||
|
AUDIOLOOP_MEAN * audioHeadroomMul);
|
||||||
|
} else {
|
||||||
|
digitalWrite(33, LOW);
|
||||||
|
}
|
||||||
|
}, 100000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Core 1 — real-time audio + MIDI I/O drain
|
||||||
|
// =====================================================================
|
||||||
|
|
||||||
|
void setup1() {
|
||||||
|
while (!READ_VOLATILE(g_serial_ready)) { MEMORY_BARRIER(); delay(1); }
|
||||||
|
while (!READ_VOLATILE(g_iface_ready)) { MEMORY_BARRIER(); delay(1); }
|
||||||
|
|
||||||
|
if (g_midi) {
|
||||||
|
g_midi->Setup(/*n_outputs=*/16);
|
||||||
|
g_midi->SetMIDISendChannel(1);
|
||||||
|
nisps_firmware::bind_midi_input(g_midi, g_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mode.setup(static_cast<float>(AudioDriver::GetSampleRate()));
|
||||||
|
|
||||||
|
nisps_firmware::register_audio_engine(g_mode, &audio_block_callback);
|
||||||
|
AudioDriver::Setup();
|
||||||
|
|
||||||
|
WRITE_VOLATILE(g_core1_ready, true);
|
||||||
|
while (!READ_VOLATILE(g_core0_ready)) { MEMORY_BARRIER(); delay(1); }
|
||||||
|
Serial.println("Finished initialising core 1.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop1() {
|
||||||
|
PERIODIC_RUN_US({
|
||||||
|
nisps_firmware::drain_outputs(g_midi, g_mode);
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
PERIODIC_RUN_US({
|
||||||
|
if (g_midi) g_midi->Poll();
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
114
firmware/MEMLNaut-NISPS/glue/audio_driver.hpp
Normal file
114
firmware/MEMLNaut-NISPS/glue/audio_driver.hpp
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
// firmware/glue/audio_driver.hpp — Bridge memllib AudioDriver to nisps modes.
|
||||||
|
//
|
||||||
|
// memllib's AudioDriver delivers samples via a *block* callback shaped like:
|
||||||
|
// void cb(float in[][kBufferSize], float out[][kBufferSize], size_t nch, size_t nf)
|
||||||
|
// The nisps `Mode` concept exposes a per-sample `process(stereosample_t)`.
|
||||||
|
//
|
||||||
|
// We register a free function as the AudioDriver block callback and pump it
|
||||||
|
// frame-by-frame into the active mode. The mode pointer is held in a
|
||||||
|
// templated free function that captures it by reference at call-site (so we
|
||||||
|
// avoid std::function indirection in the audio path).
|
||||||
|
//
|
||||||
|
// The two `stereosample_t` types — the pre-existing firmware POD and the new
|
||||||
|
// nisps namespaced one — have identical memory layout (two floats L,R) so the
|
||||||
|
// bridge does an explicit field-wise copy. No reinterpret_cast.
|
||||||
|
//
|
||||||
|
// All hot code lives in flash-resident SRAM via NISPS_AUDIO_FUNC.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "../src/nisps/core/perf.hpp"
|
||||||
|
#include "../src/nisps/core/types.hpp"
|
||||||
|
#include "../src/memllib/audio/AudioDriver.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
// Pointer to the active mode. The audio block callback reads through this.
|
||||||
|
// Set during setup1() before AudioDriver::Setup() is called. Marked
|
||||||
|
// `__not_in_flash("audio")` so the audio ISR path does not pay flash latency.
|
||||||
|
struct ActiveModeBridge {
|
||||||
|
void* mode_ptr = nullptr; // type-erased Mode*
|
||||||
|
void (*process_block)(void*,
|
||||||
|
float[][kBufferSize],
|
||||||
|
float[][kBufferSize],
|
||||||
|
size_t, size_t) = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defined in the .ino (not inline-in-header) — `inline` and `__not_in_flash`
|
||||||
|
// section attributes don't combine cleanly: comdat groups want shared
|
||||||
|
// linkage, named sections want unique ownership. The .ino owns the
|
||||||
|
// definition.
|
||||||
|
extern volatile ActiveModeBridge AUDIO_MEM g_active_mode_bridge;
|
||||||
|
|
||||||
|
// Templated trampoline: instantiated once per concrete Mode type. It does the
|
||||||
|
// per-sample loop and the field-wise stereosample_t copy. Templated rather
|
||||||
|
// than virtual because the audio path forbids virtual dispatch (architecture
|
||||||
|
// §3.5).
|
||||||
|
//
|
||||||
|
// We can't use `__not_in_flash_func(name)` on a templated function because
|
||||||
|
// that macro stringifies the function name into the section attribute, and
|
||||||
|
// the template instantiations all share a single section name — collisions
|
||||||
|
// are benign at link time but a per-instantiation section would be cleaner.
|
||||||
|
// Use `__attribute__((hot))` instead for these templated trampolines and
|
||||||
|
// rely on the platform linker default for placement.
|
||||||
|
template <typename Mode>
|
||||||
|
__attribute__((hot)) static void process_block_typed(
|
||||||
|
void* mode_ptr,
|
||||||
|
float in[][kBufferSize],
|
||||||
|
float out[][kBufferSize],
|
||||||
|
size_t /*n_channels*/,
|
||||||
|
size_t n_frames) {
|
||||||
|
auto* mode = static_cast<Mode*>(mode_ptr);
|
||||||
|
for (size_t i = 0; i < n_frames; ++i) {
|
||||||
|
::stereosample_t fw_in{in[0][i], in[1][i]};
|
||||||
|
::nisps::stereosample_t in_ns{fw_in.L, fw_in.R};
|
||||||
|
const auto out_ns = mode->process(in_ns);
|
||||||
|
out[0][i] = out_ns.L;
|
||||||
|
out[1][i] = out_ns.R;
|
||||||
|
|
||||||
|
// Modes that override `analyse(stereosample_t)` (e.g. SoundAnalysisMIDI)
|
||||||
|
// see the input frame here, before the next sample.
|
||||||
|
if constexpr (requires { mode->analyse(in_ns); }) {
|
||||||
|
mode->analyse(in_ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner forward — the .ino provides the actual block callback (with
|
||||||
|
// `__not_in_flash_func` placement) and forwards into this. Keeping the
|
||||||
|
// non-template body in a non-inline function avoids comdat / section
|
||||||
|
// conflicts.
|
||||||
|
inline void dispatch_audio_block(
|
||||||
|
float in[][kBufferSize],
|
||||||
|
float out[][kBufferSize],
|
||||||
|
size_t n_channels,
|
||||||
|
size_t n_frames) {
|
||||||
|
auto& bridge = const_cast<ActiveModeBridge&>(g_active_mode_bridge);
|
||||||
|
if (bridge.mode_ptr == nullptr || bridge.process_block == nullptr) {
|
||||||
|
for (size_t i = 0; i < n_frames; ++i) {
|
||||||
|
out[0][i] = 0.f;
|
||||||
|
out[1][i] = 0.f;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bridge.process_block(bridge.mode_ptr, in, out, n_channels, n_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public registration: call from setup1() with the active mode and the
|
||||||
|
// `__not_in_flash_func`-placed block callback that forwards into
|
||||||
|
// `dispatch_audio_block`.
|
||||||
|
template <typename Mode>
|
||||||
|
inline void register_audio_engine(Mode& mode, audiocallback_block_fptr_t block_cb) {
|
||||||
|
ActiveModeBridge b{};
|
||||||
|
b.mode_ptr = static_cast<void*>(&mode);
|
||||||
|
b.process_block = &process_block_typed<Mode>;
|
||||||
|
__sync_synchronize();
|
||||||
|
const_cast<ActiveModeBridge&>(g_active_mode_bridge) = b;
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
AudioDriver::SetBlockCallback(block_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
23
firmware/MEMLNaut-NISPS/glue/input_router.hpp
Normal file
23
firmware/MEMLNaut-NISPS/glue/input_router.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// firmware/glue/input_router.hpp — High-level input wiring entry point.
|
||||||
|
//
|
||||||
|
// Currently a thin re-export over `peripherals.hpp`. The intent is for this
|
||||||
|
// to grow into a schema-aware router (read mode.param_schema().input_channels
|
||||||
|
// names, look up matching hardware sources by name, attach callbacks). For
|
||||||
|
// now the trivial positional mapping in `bind_peripherals()` is sufficient
|
||||||
|
// since every existing mode's first N channels are joystick X/Y/Z(/W).
|
||||||
|
//
|
||||||
|
// Kept as its own header so growth of routing logic doesn't bloat
|
||||||
|
// peripherals.hpp.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "peripherals.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
template <typename Mode>
|
||||||
|
inline void wire_inputs(Mode& mode) {
|
||||||
|
bind_peripherals(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
101
firmware/MEMLNaut-NISPS/glue/midi_io.hpp
Normal file
101
firmware/MEMLNaut-NISPS/glue/midi_io.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// firmware/glue/midi_io.hpp — Bridge MIDIInOut <-> Mode.
|
||||||
|
//
|
||||||
|
// Two directions:
|
||||||
|
//
|
||||||
|
// IN (MIDI bytes from UART → Mode):
|
||||||
|
// - Note on/off: dispatched to mode.note_on/note_off if the mode
|
||||||
|
// provides them (PAFSynth, MEMLCelium). Otherwise dropped.
|
||||||
|
// - BPM (tempo) update: dispatched to mode.update_bpm if available
|
||||||
|
// (BreakOr, Elysiamorf).
|
||||||
|
// - Transport (start/stop): dispatched to mode.set_playing if available.
|
||||||
|
//
|
||||||
|
// OUT (Mode ControlEvent ring → MIDI bytes):
|
||||||
|
// - Drained on the firmware loop1() at audio-rate-adjacent cadence.
|
||||||
|
// - NoteOn/NoteOff/CC/Clock are translated to MIDIInOut queue calls
|
||||||
|
// and flushed once per drain cycle.
|
||||||
|
//
|
||||||
|
// All cross-thread comms uses memllib's existing pico queue (in-bound MIDI
|
||||||
|
// callbacks fire on core 1; mode events are pushed on the audio thread).
|
||||||
|
// The mode's RingBuffer is SPSC-safe; we don't need an extra queue here.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "../src/nisps/core/perf.hpp"
|
||||||
|
#include "../src/nisps/modes/base.hpp"
|
||||||
|
#include "../src/memllib/interface/MIDIInOut.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
// Bind incoming MIDI to the mode. Sets the note + bpm + transport callbacks
|
||||||
|
// on the MIDIInOut interface. Type-trait dispatch ensures we only attach
|
||||||
|
// callbacks the mode supports.
|
||||||
|
template <typename Mode>
|
||||||
|
inline void bind_midi_input(std::shared_ptr<MIDIInOut> midi, Mode& mode) {
|
||||||
|
if (!midi) return;
|
||||||
|
|
||||||
|
// Note callback — modes that handle notes expose `note_on(byte, byte)`
|
||||||
|
// and `note_off(byte)`.
|
||||||
|
midi->SetNoteCallback([&mode](bool note_on, uint8_t note, uint8_t vel) {
|
||||||
|
if constexpr (requires { mode.note_on(note, vel); }) {
|
||||||
|
if (note_on) mode.note_on(note, vel);
|
||||||
|
}
|
||||||
|
if constexpr (requires { mode.note_off(note); }) {
|
||||||
|
if (!note_on) mode.note_off(note);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// BPM updates from MIDI clock.
|
||||||
|
if constexpr (requires { mode.update_bpm(120.f); }) {
|
||||||
|
midi->SetBPMCallback([&mode](float bpm) {
|
||||||
|
mode.update_bpm(bpm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport (start/stop).
|
||||||
|
if constexpr (requires { mode.set_playing(true); }) {
|
||||||
|
midi->SetTransportCallback([&mode](bool playing) {
|
||||||
|
mode.set_playing(playing);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drain the mode's ControlEvent ring and dispatch each event to MIDI out.
|
||||||
|
// Called at ~1 kHz from loop1(). Non-blocking; no allocations.
|
||||||
|
template <typename Mode>
|
||||||
|
NISPS_AUDIO_FUNC(inline void drain_mode_events(std::shared_ptr<MIDIInOut> midi, Mode& mode)) {
|
||||||
|
if (!midi) return;
|
||||||
|
std::array<::nisps::ControlEvent, 32u> buf{};
|
||||||
|
const std::size_t n = mode.pop_control_events(std::span<::nisps::ControlEvent>(buf));
|
||||||
|
for (std::size_t i = 0u; i < n; ++i) {
|
||||||
|
const auto& e = buf[i];
|
||||||
|
switch (e.kind) {
|
||||||
|
case ::nisps::ControlEvent::Kind::NoteOn:
|
||||||
|
midi->queueNoteOn(e.data1, e.data2);
|
||||||
|
break;
|
||||||
|
case ::nisps::ControlEvent::Kind::NoteOff:
|
||||||
|
midi->queueNoteOff(e.data1, e.data2);
|
||||||
|
break;
|
||||||
|
case ::nisps::ControlEvent::Kind::ControlChange:
|
||||||
|
midi->queueCC(e.data1, e.data2);
|
||||||
|
break;
|
||||||
|
case ::nisps::ControlEvent::Kind::Clock:
|
||||||
|
midi->queueClock();
|
||||||
|
break;
|
||||||
|
case ::nisps::ControlEvent::Kind::None:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n > 0u) {
|
||||||
|
(void)midi->flushQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
59
firmware/MEMLNaut-NISPS/glue/mode_select.hpp
Normal file
59
firmware/MEMLNaut-NISPS/glue/mode_select.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
// firmware/glue/mode_select.hpp — Compile-time mode selection.
|
||||||
|
//
|
||||||
|
// The active firmware mode is chosen at compile time via `MEMLNAUT_MODE_TYPE`
|
||||||
|
// (the .ino's master macro). The macro expands to one of the canonical mode
|
||||||
|
// type aliases below; build scripts rewrite the active line.
|
||||||
|
//
|
||||||
|
// Each alias maps a short human-readable name (`MEMLNautModePAFSynth`) to a
|
||||||
|
// concrete `nisps::modes::*Mode` C++ type. The legacy
|
||||||
|
// `modes/MEMLNautMode*.hpp` wrappers are gone; the same identifier now refers
|
||||||
|
// to the new platform-agnostic mode type.
|
||||||
|
//
|
||||||
|
// This indirection (via using-aliases) lets the build script's mode-rewrite
|
||||||
|
// logic stay near-identical: it still sees lines of the form
|
||||||
|
// #define MEMLNAUT_MODE_TYPE MEMLNautModePAFSynth
|
||||||
|
// and rewrites between alternatives.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Arduino's Common.h defines `sq(x)`, `min(a,b)`, `max(a,b)`, `abs(x)`,
|
||||||
|
// `round(x)` etc. as preprocessor macros. nisps/ uses some of these as
|
||||||
|
// regular function/method names (e.g. nisps::AnalysisEngine::sq()). Undef
|
||||||
|
// the Arduino macros locally before pulling in nisps headers; downstream
|
||||||
|
// firmware code that wants Arduino's macro behaviour can re-include
|
||||||
|
// Arduino.h after this point.
|
||||||
|
#ifdef sq
|
||||||
|
# undef sq
|
||||||
|
#endif
|
||||||
|
#ifdef min
|
||||||
|
# undef min
|
||||||
|
#endif
|
||||||
|
#ifdef max
|
||||||
|
# undef max
|
||||||
|
#endif
|
||||||
|
#ifdef abs
|
||||||
|
# undef abs
|
||||||
|
#endif
|
||||||
|
#ifdef round
|
||||||
|
# undef round
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../src/nisps/modes/breakor.hpp"
|
||||||
|
#include "../src/nisps/modes/channel_strip.hpp"
|
||||||
|
#include "../src/nisps/modes/elysiamorf.hpp"
|
||||||
|
#include "../src/nisps/modes/memlcelium.hpp"
|
||||||
|
#include "../src/nisps/modes/paf_synth.hpp"
|
||||||
|
#include "../src/nisps/modes/sound_analysis_midi.hpp"
|
||||||
|
#include "../src/nisps/modes/verb_fx.hpp"
|
||||||
|
#include "../src/nisps/modes/xiasri.hpp"
|
||||||
|
|
||||||
|
// ---- Public name → nisps type aliases ----
|
||||||
|
// Build script greps for `MEMLNautMode*` lines, so we keep the prefix.
|
||||||
|
using MEMLNautModePAFSynth = ::nisps::modes::PAFSynthMode;
|
||||||
|
using MEMLNautModeChannelStrip = ::nisps::modes::ChannelStripMode;
|
||||||
|
using MEMLNautModeXIASRI = ::nisps::modes::XIASRIMode;
|
||||||
|
using MEMLNautModeSoundAnalysisMIDI = ::nisps::modes::SoundAnalysisMIDIMode;
|
||||||
|
using MEMLNautModeBreakOr = ::nisps::modes::BreakOrMode;
|
||||||
|
using MEMLNautModeVerbFX = ::nisps::modes::VerbFXMode;
|
||||||
|
using MEMLNautModeElysiamorfs = ::nisps::modes::ElysiamorfMode;
|
||||||
|
using MEMLNautModeMEMLCelium = ::nisps::modes::MEMLCeliumMode;
|
||||||
28
firmware/MEMLNaut-NISPS/glue/output_router.hpp
Normal file
28
firmware/MEMLNaut-NISPS/glue/output_router.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// firmware/glue/output_router.hpp — Drains mode events to hardware sinks.
|
||||||
|
//
|
||||||
|
// Two sinks today:
|
||||||
|
// - MIDI (via midi_io.hpp's `drain_mode_events`)
|
||||||
|
// - I2C (BreakOr's per-track CV outputs — TODO: integrate USeqI2C once
|
||||||
|
// stream 12 lands the legacy USeqI2C cleanup)
|
||||||
|
//
|
||||||
|
// Called from loop1() at audio-rate-adjacent cadence (sub-millisecond is
|
||||||
|
// fine; MIDI bytes pace themselves through the UART DMA queue).
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "midi_io.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
template <typename Mode>
|
||||||
|
inline void drain_outputs(std::shared_ptr<MIDIInOut> midi, Mode& mode) {
|
||||||
|
// Sequencer modes run their event pump first, then we drain.
|
||||||
|
if constexpr (requires { mode.pump_engine_events(); }) {
|
||||||
|
mode.pump_engine_events();
|
||||||
|
}
|
||||||
|
drain_mode_events(midi, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
116
firmware/MEMLNaut-NISPS/glue/peripherals.hpp
Normal file
116
firmware/MEMLNaut-NISPS/glue/peripherals.hpp
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// firmware/glue/peripherals.hpp — Joystick, buttons, toggles → mode input
|
||||||
|
// channels.
|
||||||
|
//
|
||||||
|
// The MEMLNaut hardware exposes:
|
||||||
|
// - 3 analog joystick axes (X, Y, Z) via `setJoyXCallback` / `Y` / `Z`,
|
||||||
|
// each delivering a float in [0, 1].
|
||||||
|
// - Up to 5 rotary pots (RVGain1, RVZ1, RVY1, RVX1, ADC3) — same callback
|
||||||
|
// shape.
|
||||||
|
// - 4 momentary buttons (MomA1/2, MomB1/2) and 5 toggles (TogA1/2, TogB1/2,
|
||||||
|
// JoySW). Buttons fire `void()` on press; toggles fire `void(bool)` on
|
||||||
|
// edge.
|
||||||
|
//
|
||||||
|
// The `Mode` concept's input channels are abstract floats in [0, 1]. Each
|
||||||
|
// concrete mode's schema names its channels (e.g. paf_synth: joy_x, joy_y,
|
||||||
|
// joy_z, joy_w). The glue maps the *first N* analog inputs onto channels
|
||||||
|
// `[0, N)` — that's the trivial mapping that matches every concrete mode's
|
||||||
|
// schema today (joystick first, optional 4D extra pot for joy_w).
|
||||||
|
//
|
||||||
|
// Buttons drive the InteractiveML primitives directly:
|
||||||
|
// MomA1 (TA up) : randomise / draw weights
|
||||||
|
// MomA2 (TA down) : clear examples (reset dataset)
|
||||||
|
// MomB1 (MA up) : randomise (synonym, deliberate)
|
||||||
|
// MomB2 (MA down) : jolt / move_weights
|
||||||
|
// TogB1 : add example (latched: when high, capture current
|
||||||
|
// inputs+outputs as a training pair)
|
||||||
|
// TogB2 : train (rising-edge → call ml.train())
|
||||||
|
//
|
||||||
|
// This is a sparse subset of the legacy InterfaceRL — full RL UX comes back
|
||||||
|
// in stream 9 (browser) and stream 12 (firmware UI). Goal here: hardware
|
||||||
|
// can express the *abstract* RL primitives so the mode keeps moving.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../src/nisps/core/perf.hpp"
|
||||||
|
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
// Number of analog input channels we forward to the mode. Modes with fewer
|
||||||
|
// inputs ignore the surplus (set_input(idx, ...) silently rejects out-of-
|
||||||
|
// range idx in ModeBase).
|
||||||
|
inline constexpr std::size_t kAnalogInputCount = 4u; // X, Y, Z, plus one pot
|
||||||
|
|
||||||
|
// A small POD that the .ino owns. Captured by lambda below.
|
||||||
|
template <typename Mode>
|
||||||
|
struct PeripheralBindings {
|
||||||
|
Mode* mode = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wire all hardware → mode bindings. Must be called *after*
|
||||||
|
// MEMLNaut::Initialize() (so MEMLNaut::Instance() is valid). The Mode
|
||||||
|
// reference must outlive the program (it's a static in the .ino).
|
||||||
|
template <typename Mode>
|
||||||
|
inline void bind_peripherals(Mode& mode) {
|
||||||
|
auto* meml = MEMLNaut::Instance();
|
||||||
|
if (!meml) return;
|
||||||
|
|
||||||
|
// ---- Analog inputs → mode input channels ----
|
||||||
|
meml->setJoyXCallback([&mode](float v) { mode.set_input(0u, v); });
|
||||||
|
meml->setJoyYCallback([&mode](float v) { mode.set_input(1u, v); });
|
||||||
|
meml->setJoyZCallback([&mode](float v) { mode.set_input(2u, v); });
|
||||||
|
// Fourth analog input: use ADC3 (the spare). Modes that don't expose a
|
||||||
|
// joy_w channel just don't see it (set_input drops out-of-range idx).
|
||||||
|
meml->setADC3Callback([&mode](float v) { mode.set_input(3u, v); });
|
||||||
|
|
||||||
|
// ---- Rotary pots ----
|
||||||
|
// Reuse RVGain1 as a "tempo" knob for sequencer modes. The mode is free
|
||||||
|
// to ignore via the same channel-bounds drop. RVGain1 is also used by
|
||||||
|
// InterfaceRL legacy as an audio output volume knob — for now we hand
|
||||||
|
// it to the ML interface for non-sequencer modes by setting the output
|
||||||
|
// master volume directly via AudioDriver.
|
||||||
|
meml->setRVGain1Callback([](float v) {
|
||||||
|
AudioDriver::SetMasterVolume(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- Buttons / toggles → ML primitives ----
|
||||||
|
// Button presses are momentary (rising edge only). Toggles fire on
|
||||||
|
// both edges with the new state.
|
||||||
|
|
||||||
|
// Randomise weights (large draw)
|
||||||
|
meml->setMomA1Callback([&mode]() {
|
||||||
|
mode.ml().draw_weights(mode.param_schema().default_spread);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear training dataset (best-effort: if the MLP exposes reset()
|
||||||
|
// we use it; otherwise we draw new weights as a degraded fallback).
|
||||||
|
meml->setMomA2Callback([&mode]() {
|
||||||
|
mode.ml().reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jolt / move_weights (positive direction)
|
||||||
|
meml->setMomB1Callback([&mode]() {
|
||||||
|
mode.ml().move_weights(0.5f, mode.param_schema().default_spread);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move weights (negative-feedback jolt — same call, different speed sign
|
||||||
|
// would flip exploration direction; the MLP API takes magnitude).
|
||||||
|
meml->setMomB2Callback([&mode]() {
|
||||||
|
mode.ml().move_weights(0.25f, mode.param_schema().default_spread);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle B2: rising edge → train.
|
||||||
|
meml->setTogB2Callback([&mode](bool state) {
|
||||||
|
if (state) {
|
||||||
|
(void)mode.ml().train();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
1
firmware/MEMLNaut-NISPS/src/daisysp
Symbolic link
1
firmware/MEMLNaut-NISPS/src/daisysp
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../src/daisysp
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/PicoDefs.hpp
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/PicoDefs.hpp
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/PicoDefs.hpp
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/audio
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/audio
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/audio
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/hardware
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/hardware
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/hardware
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/interface
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/interface
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/interface
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/synth
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/synth
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/synth
|
||||||
1
firmware/MEMLNaut-NISPS/src/memllib/utils
Symbolic link
1
firmware/MEMLNaut-NISPS/src/memllib/utils
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../src/memllib/utils
|
||||||
1
firmware/MEMLNaut-NISPS/src/nisps/core
Symbolic link
1
firmware/MEMLNaut-NISPS/src/nisps/core
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../nisps/core
|
||||||
1
firmware/MEMLNaut-NISPS/src/nisps/dsp
Symbolic link
1
firmware/MEMLNaut-NISPS/src/nisps/dsp
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../nisps/dsp
|
||||||
1
firmware/MEMLNaut-NISPS/src/nisps/engines
Symbolic link
1
firmware/MEMLNaut-NISPS/src/nisps/engines
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../nisps/engines
|
||||||
1
firmware/MEMLNaut-NISPS/src/nisps/ml
Symbolic link
1
firmware/MEMLNaut-NISPS/src/nisps/ml
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../nisps/ml
|
||||||
1
firmware/MEMLNaut-NISPS/src/nisps/modes
Symbolic link
1
firmware/MEMLNaut-NISPS/src/nisps/modes
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../nisps/modes
|
||||||
42
firmware/README.md
Normal file
42
firmware/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# `firmware/` — Arduino sketch + hardware glue
|
||||||
|
|
||||||
|
Thin shell around `nisps/`. The platform-agnostic ML / DSP / engines / modes live there; this directory contains:
|
||||||
|
|
||||||
|
- `MEMLNaut-NISPS.ino` — entry point. Picks one mode at compile time, hosts the dual-core lifecycle (Core 0: ML + UI, Core 1: audio + MIDI), and connects glue to mode.
|
||||||
|
- `glue/audio_driver.hpp` — bridges memllib's `AudioDriver` block callback to `Mode::process(stereosample_t)`. Templated trampoline so no virtual dispatch in audio path.
|
||||||
|
- `glue/peripherals.hpp` — wires MEMLNaut joystick / pots / buttons to `Mode::set_input(idx, value)` and the ML primitives (`draw_weights`, `move_weights`, `train`, `reset`).
|
||||||
|
- `glue/midi_io.hpp` — binds incoming MIDI to `mode.note_on/note_off/update_bpm/set_playing` (where supported); drains outgoing `ControlEvent`s from the mode's ring buffer to the MIDI UART.
|
||||||
|
- `glue/input_router.hpp` — `wire_inputs()` entry point, currently a thin re-export over peripherals. Will grow into a schema-aware router.
|
||||||
|
- `glue/output_router.hpp` — drains engine events + mode events; called from `loop1()`.
|
||||||
|
- `glue/mode_select.hpp` — type aliases mapping `MEMLNautMode<Name>` identifiers to `nisps::modes::<Name>Mode` C++ types. Build script rewrites `MEMLNAUT_MODE_TYPE` between the alternatives.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/build-firmware.sh # interactive variant prompt
|
||||||
|
scripts/build-firmware.sh PAFSynth # build a specific variant
|
||||||
|
scripts/build-and-flash-firmware.sh # build + flash via UF2
|
||||||
|
```
|
||||||
|
|
||||||
|
Target: `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` with `compiler.cpp.extra_flags=-std=gnu++20`.
|
||||||
|
|
||||||
|
## Include paths
|
||||||
|
|
||||||
|
Arduino-CLI compiles every `*.cpp` / `*.hpp` reachable from the sketch directory and from any submodule under `src/`. Headers under `nisps/` are included via relative path (`#include "../../nisps/..."`). No `-I` flag injection needed.
|
||||||
|
|
||||||
|
`src/memllib` and `src/daisysp` are git submodules; the `firmware-common.sh` wrapper rejects builds when submodules drift from the recorded revision.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
You cannot test on hardware from a sandbox. We verify three things:
|
||||||
|
|
||||||
|
1. `arduino-cli compile` succeeds for at least three modes (PAFSynth, ChannelStrip, BreakOr).
|
||||||
|
2. Host C++ tests in `nisps/build` still pass — the firmware refactor must not perturb the platform-agnostic library.
|
||||||
|
3. `git grep` confirms no remaining references to deleted root-level files.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Root-level `MEMLNaut-NISPS.ino`, `IMLInterface.hpp`, `*AudioApp.hpp`, `XiasriAnalysis.{cpp,hpp}` — replaced by `nisps/engines/*` and `firmware/MEMLNaut-NISPS.ino`.
|
||||||
|
- Root-level `modes/MEMLNautMode*.hpp` and `modes/AudioApps/` — replaced by `nisps/modes/*` and the `mode_select.hpp` type aliases.
|
||||||
|
- Root-level `voicespaces/` — replaced by static voice space data inlined into engines (`nisps/engines/paf_synth.hpp` etc.).
|
||||||
|
- `src/memlp/` submodule — replaced by `nisps/ml/`.
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
#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>
|
|
||||||
|
|
||||||
#include "../../src/memllib/interface/InterfaceBase.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include "../../voicespaces/VoiceSpaces.hpp"
|
|
||||||
|
|
||||||
#include "RatioSeq.hpp"
|
|
||||||
#include "RatioSeqEngine.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;
|
|
||||||
|
|
||||||
RatioSeqEngine<NSEQUENCES> seqEngine;
|
|
||||||
|
|
||||||
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>() {
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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){
|
|
||||||
seqEngine.midiClockPhasor += seqEngine.midiClockPhasorInc;
|
|
||||||
if (seqEngine.midiClockPhasor >= 1.f) {
|
|
||||||
seqEngine.midiClockPhasor -= 1.f;
|
|
||||||
midiIO->queueClock();
|
|
||||||
midiIO->flushQueue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sequencerClockMode == MIDI_CLOCK) {
|
|
||||||
int phasorResetValue=0;
|
|
||||||
if (queue_try_remove(&barPhaseResetQueue, &phasorResetValue)) {
|
|
||||||
seqEngine.resetBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
midiIO = new_midi_interf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() override {
|
|
||||||
AudioAppBase<NPARAMS>::loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessParams(const std::array<float, NPARAMS>& params)
|
|
||||||
{
|
|
||||||
firstParamsReceived = true;
|
|
||||||
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();
|
|
||||||
midiIO->flushQueue();
|
|
||||||
} else {
|
|
||||||
midiIO->queueClockStart();
|
|
||||||
midiIO->flushQueue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seqEngine.updateParams(params, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimeSignature(float beats, float division) {
|
|
||||||
seqEngine.setTimeSignature(beats, division);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
float i2cValues[8] = {0,0,0,0,0,0,0,0};
|
|
||||||
|
|
||||||
float sampleRatef;
|
|
||||||
bool firstParamsReceived = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __BREAKOR_AUDIO_APP_HPP__
|
|
||||||
|
|
@ -1,324 +0,0 @@
|
||||||
#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__
|
|
||||||
|
|
||||||
|
|
@ -1,519 +0,0 @@
|
||||||
#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=56>
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Focus group bitmasks
|
|
||||||
static constexpr uint32_t kFocusSeq = (1u << 0);
|
|
||||||
static constexpr uint32_t kFocusSyn = (1u << 1);
|
|
||||||
|
|
||||||
// Per-param group membership: params 0-13 = sequencer, 14-55 = synthesis
|
|
||||||
static constexpr std::array<uint32_t, NPARAMS> kParamGroupMask = {
|
|
||||||
// 0-13: sequencer (14 entries)
|
|
||||||
kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq,
|
|
||||||
kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq, kFocusSeq,
|
|
||||||
// 14-55: synthesis (42 entries)
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn, kFocusSyn,
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
const float rm = v1p0 * v1p1 * v1p2;// * p3;
|
|
||||||
|
|
||||||
v1 = ((1.0 - rmGain) * v1) + (rm * rmGain);
|
|
||||||
|
|
||||||
v1 = v1 * v1Envval;
|
|
||||||
/////
|
|
||||||
|
|
||||||
float mix = v0 + v1;
|
|
||||||
|
|
||||||
float shape = sinf(mix * TWOPI);
|
|
||||||
shape = sinf(((shape * TWOPI) * sineShapeGain) + sineShapeASym);
|
|
||||||
mix = mix + (shape * sineShapeMix);
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
v0PitchEnv.trigger(1.0);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
v1AmpEnv.trigger(noteVel);
|
|
||||||
v1PitchEnv.trigger(1.0);
|
|
||||||
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();
|
|
||||||
v0PitchEnv.release();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
v1AmpEnv.release();
|
|
||||||
v1PitchEnv.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.01f;
|
|
||||||
detune2 = 1.02f;
|
|
||||||
// detune3 = 1.2f;
|
|
||||||
|
|
||||||
size_t paramIdx = 14; // 0-13 reserved for seqEngine (2 sequencers × 7 params)
|
|
||||||
auto sqParam = [&]() { const float p = params[paramIdx++]; return p * p; };
|
|
||||||
|
|
||||||
baseFreq = 60.f + (params[paramIdx++] * 10.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++] * 100.f);
|
|
||||||
paf1_bw = 10.f + (params[paramIdx++] * 100.f);
|
|
||||||
paf2_bw = 10.f + (params[paramIdx++] * 100.f);
|
|
||||||
// paf3_bw = 10.f + (params[paramIdx++] * 100.f);
|
|
||||||
|
|
||||||
paf0_vib = sqParam() * 0.01f;
|
|
||||||
paf1_vib = paf0_vib;
|
|
||||||
paf2_vib = paf0_vib;
|
|
||||||
// paf3_vib = 0.f;
|
|
||||||
|
|
||||||
paf0_vfr = sqParam() * 15.0f;
|
|
||||||
paf1_vfr = paf0_vfr;
|
|
||||||
paf2_vfr = paf0_vfr;
|
|
||||||
// paf3_vfr = 0.f;
|
|
||||||
|
|
||||||
paf0_shift = -100.f + (params[paramIdx++] * 200.f);
|
|
||||||
paf1_shift = -100.f + (params[paramIdx++] * 200.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 + sqParam() * 200.f,
|
|
||||||
0.01 + (params[paramIdx++] * 0.5f), 1.f + sqParam() * 800.f, sampleRatef);
|
|
||||||
|
|
||||||
v0PitchEnv.setup(0.01f + (params[paramIdx++] * 3.f),
|
|
||||||
0.5f + sqParam() * 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; //0.1f;
|
|
||||||
|
|
||||||
fbSmoothAlpha=0.5f;
|
|
||||||
|
|
||||||
//v1
|
|
||||||
v1p0Gain=1.f;
|
|
||||||
v1p1Gain=1.f;
|
|
||||||
v1p2Gain=1.f;
|
|
||||||
|
|
||||||
v1BaseFreq = 300.f + (params[paramIdx++] * 10.f);
|
|
||||||
v1Detune1 = 1.0f + (params[paramIdx++] * 1.0f);
|
|
||||||
v1Detune2 = 1.0f + (params[paramIdx++] * 1.0f);
|
|
||||||
|
|
||||||
v1paf0_cf = (params[paramIdx++] * 2.f);
|
|
||||||
v1paf1_cf = (params[paramIdx++] * 2.f);
|
|
||||||
v1paf2_cf = (params[paramIdx++] * 2.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 = -500.f + (params[paramIdx++] * 1000.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 + sqParam() * 100.f,
|
|
||||||
0.01 + (params[paramIdx++] * 0.3f), 1.f + sqParam() * 200.f, sampleRatef);
|
|
||||||
|
|
||||||
v1PitchEnv.setup(0.01f + (params[paramIdx++] * 3.f),
|
|
||||||
0.5f + sqParam() * 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__
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#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__
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
#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__
|
|
||||||
|
|
@ -1,356 +0,0 @@
|
||||||
#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 "../../voicespaces/VerbFX/basic.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/resonant.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/soft.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/cathedral.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/shimmer.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/chamber.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/metallic.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/granular.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/diffuse.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/dark.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/bright.hpp"
|
|
||||||
#include "../../voicespaces/VerbFX/harmonic.hpp"
|
|
||||||
#include "../../src/memllib/synth/OnePoleSmoother.hpp"
|
|
||||||
#include "../../src/memllib/synth/maximilian.h"
|
|
||||||
#include "../../src/daisysp/Effects/pitchshifter.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<size_t NPARAMS=47>
|
|
||||||
class VerbFXAudioApp : public AudioAppBase<NPARAMS>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr size_t kN_Params = NPARAMS;
|
|
||||||
static constexpr size_t nVoiceSpaces=12;
|
|
||||||
|
|
||||||
|
|
||||||
std::array<VoiceSpace<NPARAMS>, nVoiceSpaces> voiceSpaces;
|
|
||||||
|
|
||||||
VoiceSpaceFn<NPARAMS> currentVoiceSpace;
|
|
||||||
|
|
||||||
enum class controlMessages {
|
|
||||||
MSG_ENABLE_FILTERBANK=0,
|
|
||||||
MSG_ENABLE_REVERB,
|
|
||||||
MSG_ENABLE_SHORT_DELAY,
|
|
||||||
MSG_ENABLE_MEDIUM_DELAY,
|
|
||||||
MSG_ENABLE_LONG_DELAY,
|
|
||||||
MSG_ENABLE_DELAY_TO_REVERB,
|
|
||||||
};
|
|
||||||
|
|
||||||
queue_t controlMessageQueue;
|
|
||||||
queue_t wetdryQueue;
|
|
||||||
|
|
||||||
void setWetDryQueued(float value) {
|
|
||||||
queue_try_add(&wetdryQueue, &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enableFilterbank=true;
|
|
||||||
bool enableReverb=true;
|
|
||||||
bool enableShortDelay=true;
|
|
||||||
bool enableMediumDelay=true;
|
|
||||||
bool enableLongDelay=true;
|
|
||||||
bool enableDelayToReverb=true;
|
|
||||||
|
|
||||||
|
|
||||||
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>() {
|
|
||||||
auto voiceSpaceDefault = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_DEFAULT_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[0] = {"Default", voiceSpaceDefault};
|
|
||||||
auto voiceSpaceResonant = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_RESONANT_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[1] = {"Resonant", voiceSpaceResonant};
|
|
||||||
auto voiceSpaceSoft = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_SOFT_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[2] = {"Soft", voiceSpaceSoft};
|
|
||||||
auto voiceSpaceCathedral = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_CATHEDRAL_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[3] = {"Cathedral", voiceSpaceCathedral};
|
|
||||||
auto voiceSpaceShimmer = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_SHIMMER_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[4] = {"Shimmer", voiceSpaceShimmer};
|
|
||||||
auto voiceSpaceChamber = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_CHAMBER_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[5] = {"Chamber", voiceSpaceChamber};
|
|
||||||
auto voiceSpaceMetallic = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_METALLIC_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[6] = {"Metallic", voiceSpaceMetallic};
|
|
||||||
auto voiceSpaceGranular = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_GRANULAR_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[7] = {"Granular", voiceSpaceGranular};
|
|
||||||
auto voiceSpaceDiffuse = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_DIFFUSE_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[8] = {"Diffuse", voiceSpaceDiffuse};
|
|
||||||
auto voiceSpaceDark = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_DARK_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[9] = {"Dark", voiceSpaceDark};
|
|
||||||
auto voiceSpaceBright = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_BRIGHT_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[10] = {"Bright", voiceSpaceBright};
|
|
||||||
auto voiceSpaceHarmonic = [this](const std::array<float, NPARAMS>& smoothParams) {
|
|
||||||
VOICE_SPACE_VERBFX_HARMONIC_BODY
|
|
||||||
};
|
|
||||||
voiceSpaces[11] = {"Harmonic", voiceSpaceHarmonic};
|
|
||||||
currentVoiceSpace = voiceSpaces[0].mappingFunction;
|
|
||||||
queue_init(&controlMessageQueue, sizeof(controlMessages), 1);
|
|
||||||
queue_init(&wetdryQueue, sizeof(float), 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
__attribute__((hot)) stereosample_t __force_inline Process(const stereosample_t x) override
|
|
||||||
{
|
|
||||||
static float verbFB = 0.f;
|
|
||||||
static float delaysFB = 0.f;
|
|
||||||
|
|
||||||
float mix = x.L + x.R;
|
|
||||||
|
|
||||||
smoother.Process(neuralNetOutputs.data(), smoothParams.data());
|
|
||||||
|
|
||||||
//mapping
|
|
||||||
currentVoiceSpace(smoothParams);
|
|
||||||
|
|
||||||
//XFADE
|
|
||||||
|
|
||||||
const float filterBankDelayFBLevel = sqrtf(filterBankDelayXFade);
|
|
||||||
const float filterBankDelayFBLevelInv = sqrtf(1.f - filterBankDelayXFade);
|
|
||||||
|
|
||||||
/////////////////// FILTERBANK
|
|
||||||
|
|
||||||
float filterBankIn = mix + (filterBankDelayFBLevel * ddelayFeedback);
|
|
||||||
float filterBankOut=mix;
|
|
||||||
|
|
||||||
if (enableFilterbank) {
|
|
||||||
filterBankOut = filterBank0.bandpassChamberlain(filterBankIn, filterBankF0, filterBankRes0);
|
|
||||||
filterBankOut += filterBank1.bandpassChamberlain(filterBankIn, filterBankF1, filterBankRes1);
|
|
||||||
filterBankOut += filterBank2.bandpassChamberlain(filterBankIn, filterBankF2, filterBankRes2);
|
|
||||||
filterBankOut += filterBank3.bandpassChamberlain(filterBankIn, filterBankF3, filterBankRes3);
|
|
||||||
filterBankOut += filterBank4.bandpassChamberlain(filterBankIn, filterBankF4, filterBankRes4);
|
|
||||||
filterBankOut += filterBank5.bandpassChamberlain(filterBankIn, filterBankF5, filterBankRes5);
|
|
||||||
filterBankOut += filterBank6.bandpassChamberlain(filterBankIn, filterBankF6, filterBankRes6);
|
|
||||||
filterBankOut += filterBank7.bandpassChamberlain(filterBankIn, filterBankF7, filterBankRes7);
|
|
||||||
|
|
||||||
filterBankOut *= 0.125f;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////// DELAYS
|
|
||||||
float delayIn = filterBankOut;
|
|
||||||
|
|
||||||
float delayed = enableLongDelay ? ddelay.read(ddelayTime) : 0.f;
|
|
||||||
ddelay.write((delayIn * filterBankDelayFBLevelInv) + ((ddelayFeedback + (delayIn * filterBankDelayFBLevel)) * delayed));
|
|
||||||
|
|
||||||
float delayed1 = enableMediumDelay ? ddelay1.read(ddelayTime1) : 0.f;
|
|
||||||
ddelay1.write(delayIn + (ddelayFeedback1 * delayed1));
|
|
||||||
|
|
||||||
float delayed2 = enableShortDelay ? ddelay2.read(ddelayTime2) : 0.f;
|
|
||||||
ddelay2.write(delayIn + (ddelayFeedback2 * delayed2));
|
|
||||||
|
|
||||||
float a = fminf(delayMorph * 2.f, 1.f);
|
|
||||||
float b = fmaxf(delayMorph * 2.f - 1.f, 0.f);
|
|
||||||
constexpr float kEqualMix = 0.57735f; // 1/sqrt(3), constant-power equal mix
|
|
||||||
float w_short = kEqualMix + delayBlend * (sqrtf(1.f - a) - kEqualMix);
|
|
||||||
float w_medium = kEqualMix + delayBlend * (sqrtf(a) * sqrtf(1.f - b) - kEqualMix);
|
|
||||||
float w_long = kEqualMix + delayBlend * (sqrtf(a) * sqrtf(b) - kEqualMix);
|
|
||||||
float delaySum = (w_short * delayed2) + (w_medium * delayed1) + (w_long * delayed);
|
|
||||||
|
|
||||||
//////////////// VERB
|
|
||||||
float verbIn = enableReverb ? filterBankOut : 0.f;
|
|
||||||
if (enableDelayToReverb && enableReverb) {
|
|
||||||
verbIn += (delayToVerbLevel * delaySum);
|
|
||||||
}
|
|
||||||
float verbOut=0.f;
|
|
||||||
|
|
||||||
|
|
||||||
verbOut = lpcomb0.lpcombfb(filterBankOut, SIZE_comb0, lp0fb, lp0cutoff);
|
|
||||||
verbOut += lpcomb1.lpcombfb(filterBankOut, SIZE_comb1, lp1fb, lp1cutoff);
|
|
||||||
verbOut += lpcomb2.lpcombfb(filterBankOut, SIZE_comb2, lp2fb, lp2cutoff);
|
|
||||||
verbOut += lpcomb3.lpcombfb(filterBankOut, SIZE_comb3, lp3fb, lp3cutoff);
|
|
||||||
verbOut += lpcomb4.lpcombfb(filterBankOut, SIZE_comb4, lp4fb, lp4cutoff);
|
|
||||||
verbOut += lpcomb5.lpcombfb(filterBankOut, SIZE_comb5, lp5fb, lp5cutoff);
|
|
||||||
verbOut += lpcomb6.lpcombfb(filterBankOut, SIZE_comb6, lp6fb, lp6cutoff);
|
|
||||||
verbOut += lpcomb7.lpcombfb(filterBankOut, SIZE_comb7, lp7fb, lp7cutoff);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
verbOut = allp0.allpass(verbOut, SIZE_allp0, allp0fb);
|
|
||||||
verbOut = allp1.allpass(verbOut, SIZE_allp1, allp1fb);
|
|
||||||
verbOut = allp2.allpass(verbOut, SIZE_allp2, allp2fb);
|
|
||||||
verbOut = allp3.allpass(verbOut, SIZE_allp3, allp3fb);
|
|
||||||
|
|
||||||
float y= (sqrtf(verbVsDelayLevel) * delaySum) + (sqrtf(1.f - verbVsDelayLevel) * verbOut);
|
|
||||||
|
|
||||||
//feedback
|
|
||||||
delaysFB = delaySum;
|
|
||||||
verbFB = verbOut;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Mix dry
|
|
||||||
y = (y * sqrtf(wetdry_mix_)) + (mix * sqrtf(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)
|
|
||||||
{
|
|
||||||
controlMessages msg;
|
|
||||||
while (queue_try_remove(&controlMessageQueue, &msg)) {
|
|
||||||
switch(msg) {
|
|
||||||
case controlMessages::MSG_ENABLE_FILTERBANK:
|
|
||||||
enableFilterbank = !enableFilterbank;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_ENABLE_REVERB:
|
|
||||||
enableReverb = !enableReverb;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_ENABLE_SHORT_DELAY:
|
|
||||||
enableShortDelay = !enableShortDelay;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_ENABLE_MEDIUM_DELAY:
|
|
||||||
enableMediumDelay = !enableMediumDelay;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_ENABLE_LONG_DELAY:
|
|
||||||
enableLongDelay = !enableLongDelay;
|
|
||||||
break;
|
|
||||||
case controlMessages::MSG_ENABLE_DELAY_TO_REVERB:
|
|
||||||
enableDelayToReverb = !enableDelayToReverb;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
float v;
|
|
||||||
if (queue_try_remove(&wetdryQueue, &v)) wetdryKnobValue = v;
|
|
||||||
}
|
|
||||||
if (wetdryKnobValue >= 0.f) {
|
|
||||||
wetdry_mix_ = wetdryKnobValue;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
maxiFilter filterBank0;
|
|
||||||
maxiFilter filterBank1;
|
|
||||||
maxiFilter filterBank2;
|
|
||||||
maxiFilter filterBank3;
|
|
||||||
maxiFilter filterBank4;
|
|
||||||
maxiFilter filterBank5;
|
|
||||||
maxiFilter filterBank6;
|
|
||||||
maxiFilter filterBank7;
|
|
||||||
|
|
||||||
DynamicDelay<16384> ddelay;
|
|
||||||
DynamicDelay<2048> ddelay1;
|
|
||||||
DynamicDelay<512> ddelay2;
|
|
||||||
|
|
||||||
maxiDCBlocker dcb;
|
|
||||||
|
|
||||||
float wetdry_mix_{0.5f};
|
|
||||||
float wetdryKnobValue{0.5f};
|
|
||||||
|
|
||||||
// mapping
|
|
||||||
float lp0fb{0}, lp0cutoff{0};
|
|
||||||
float lp1fb{0}, lp1cutoff{0};
|
|
||||||
float lp2fb{0}, lp2cutoff{0};
|
|
||||||
float lp3fb{0}, lp3cutoff{0};
|
|
||||||
float lp4fb{0}, lp4cutoff{0};
|
|
||||||
float lp5fb{0}, lp5cutoff{0};
|
|
||||||
float lp6fb{0}, lp6cutoff{0};
|
|
||||||
float lp7fb{0}, lp7cutoff{0};
|
|
||||||
float allp0fb{0}, allp1fb{0}, allp2fb{0}, allp3fb{0};
|
|
||||||
float filterBankF0{0}, filterBankF1{0}, filterBankF2{0}, filterBankF3{0};
|
|
||||||
float filterBankF4{0}, filterBankF5{0}, filterBankF6{0}, filterBankF7{0};
|
|
||||||
float filterBankRes0{0}, filterBankRes1{0}, filterBankRes2{0}, filterBankRes3{0};
|
|
||||||
float filterBankRes4{0}, filterBankRes5{0}, filterBankRes6{0}, filterBankRes7{0};
|
|
||||||
float ddelayTime{0}, ddelayFeedback{0};
|
|
||||||
float ddelayTime1{0}, ddelayFeedback1{0};
|
|
||||||
float ddelayTime2{0}, ddelayFeedback2{0};
|
|
||||||
float verbVsDelayLevel{0}, delayToVerbLevel{0}, filterBankDelayXFade{0};
|
|
||||||
float delayMorph{0.5f}, delayBlend{0.f};
|
|
||||||
|
|
||||||
OnePoleSmoother<kN_Params> smoother{150.f, kSampleRate};
|
|
||||||
|
|
||||||
// maxiDynamicsLite limiter;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <concepts>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include "WString.h"
|
|
||||||
#include "../src/memllib/interface/InterfaceBase.hpp"
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../src/memllib/audio/AudioDriver.hpp"
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept MEMLNautMode = requires(T proc) {
|
|
||||||
{proc.getHelpTitle()} -> std::same_as<String>;
|
|
||||||
// {proc.getNParams()} -> std::same_as<size_t>;
|
|
||||||
// {proc.setVoiceSpace(size_t{})} -> std::same_as<void>;
|
|
||||||
{proc.setupMIDI(std::shared_ptr<MIDIInOut>{})} -> std::same_as<void>;
|
|
||||||
{proc.addViews()} -> std::same_as<void>;
|
|
||||||
{proc.setupAudio(float{})} -> std::same_as<void>;
|
|
||||||
{proc.loop()} -> std::same_as<void>;
|
|
||||||
// {proc.getVoiceSpaceList()} -> std::same_as<std::span<String>>;
|
|
||||||
{proc.process(stereosample_t{})} -> std::same_as<stereosample_t>;
|
|
||||||
{proc.analyse(stereosample_t{})} -> std::same_as<void>;
|
|
||||||
{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>;
|
|
||||||
};
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
#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 = MEMLNAUT_ANALOG_INPUTS;
|
|
||||||
|
|
||||||
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);
|
|
||||||
interface.setModeInfo("breakor", "BreakOr");
|
|
||||||
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
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../ChannelStripAudioApp.hpp"
|
|
||||||
#include "../src/memllib/examples/InterfaceRL.hpp"
|
|
||||||
#include "MEMLNautMode.hpp"
|
|
||||||
#include "../src/memllib/PicoDefs.hpp"
|
|
||||||
#include <memory>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
class MEMLNautModeChannelStrip {
|
|
||||||
public:
|
|
||||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
|
||||||
ChannelStripAudioApp<> audioAppChannelStrip;
|
|
||||||
std::array<String, ChannelStripAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
|
||||||
InterfaceRL interface;
|
|
||||||
std::shared_ptr<InterfaceRL> interfacePtr;
|
|
||||||
std::shared_ptr<BlockSelectView> bypassView;
|
|
||||||
|
|
||||||
void setupInterface() {
|
|
||||||
interface.setup(kN_InputParams, ChannelStripAudioApp<>::kN_Params);
|
|
||||||
interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true); //set 4D joystick
|
|
||||||
interface.setModeInfo("chstrip", "ChannelStrip");
|
|
||||||
interfacePtr = make_non_owning(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHelpTitle() {
|
|
||||||
return "Channel Strip Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline stereosample_t process(stereosample_t x) {
|
|
||||||
return audioAppChannelStrip.Process(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupMIDI(std::shared_ptr<MIDIInOut> midi_interf) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void addViews() {
|
|
||||||
bypassView = std::make_shared<BlockSelectView>("Bypasses", TFT_YELLOW, 5, 80, 70, TFT_BLACK,
|
|
||||||
std::vector<String>{ "All", "EQ", "Comp", "PPG", "InFilt" });
|
|
||||||
bypassView->SetOnSelectCallback([this] (size_t id) {
|
|
||||||
Serial.println("Bypass toggled: " + String(id));
|
|
||||||
bypassView->toggleAlt(id-1);
|
|
||||||
queue_t& audioAppQ = audioAppChannelStrip.controlMessageQueue;
|
|
||||||
switch(id) {
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
auto msg = ChannelStripAudioApp<>::controlMessages::MSG_BYPASS_ALL;
|
|
||||||
queue_try_add(&audioAppQ, &msg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
auto msg = ChannelStripAudioApp<>::controlMessages::MSG_BYPASS_EQ;
|
|
||||||
queue_try_add(&audioAppQ, &msg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
auto msg = ChannelStripAudioApp<>::controlMessages::MSG_BYPASS_COMP;
|
|
||||||
queue_try_add(&audioAppQ, &msg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
{
|
|
||||||
auto msg = ChannelStripAudioApp<>::controlMessages::MSG_BYPASS_PREPOSTGAIN;
|
|
||||||
queue_try_add(&audioAppQ, &msg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
{
|
|
||||||
auto msg = ChannelStripAudioApp<>::controlMessages::MSG_BYPASS_INFILTERS;
|
|
||||||
queue_try_add(&audioAppQ, &msg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
MEMLNaut::Instance()->disp->AddView(bypassView);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
audioAppChannelStrip.setVoiceSpace(idx);
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void setupAudio(float sample_rate) {
|
|
||||||
audioAppChannelStrip.Setup(sample_rate, interfacePtr);
|
|
||||||
voiceSpaceList = audioAppChannelStrip.getVoiceSpaceNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline void loop() {
|
|
||||||
audioAppChannelStrip.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t getNMIDICtrlOutputs() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void processAnalysisParams() {}
|
|
||||||
|
|
||||||
void analyse(stereosample_t) {}
|
|
||||||
|
|
||||||
void loopCore0() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
#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 = MEMLNAUT_ANALOG_INPUTS;
|
|
||||||
|
|
||||||
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);
|
|
||||||
interface.setModeInfo("elysia", "Elysiamorfs");
|
|
||||||
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
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../src/memllib/hardware/memlnaut/display/XYPadView.hpp"
|
|
||||||
#include "../src/memllib/hardware/memlnaut/display/BlockSelectView.hpp"
|
|
||||||
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
|
||||||
#include "AudioApps/MEMLCeliumAudioApp.hpp"
|
|
||||||
#include "../src/memllib/examples/InterfaceRL.hpp"
|
|
||||||
#include "../src/memllib/audio/FocusManager.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;
|
|
||||||
|
|
||||||
FocusManager<MEMLCeliumAudioApp<>::kN_Params, 2> focusManager;
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
focusManager.setGroupName(0, "Seq");
|
|
||||||
focusManager.setGroupName(1, "Synth");
|
|
||||||
focusManager.setParamGroups(MEMLCeliumAudioApp<>::kParamGroupMask);
|
|
||||||
interface.paramTransformHook = [this](std::vector<float>& p) {
|
|
||||||
focusManager.applyInPlace(p);
|
|
||||||
};
|
|
||||||
|
|
||||||
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() {
|
|
||||||
// Focus screen — select which parameter groups are live
|
|
||||||
auto focusView = std::make_shared<BlockSelectView>(
|
|
||||||
"Focus", TFT_CYAN, 2, 120, 70, TFT_BLACK,
|
|
||||||
std::vector<String>{"Seq", "Synth"}, TFT_DARKGREY, 2);
|
|
||||||
|
|
||||||
focusView->SetOnSelectCallback([this, focusView](size_t id) {
|
|
||||||
size_t groupIdx = id - 1;
|
|
||||||
uint32_t newMask = focusManager.getSelectedMask() ^ (1u << groupIdx);
|
|
||||||
focusManager.setFocus(newMask, interface.getLastAction());
|
|
||||||
focusView->toggleAlt(groupIdx);
|
|
||||||
});
|
|
||||||
MEMLNaut::Instance()->disp->InsertViewAfter(interface.rlStatsView, focusView);
|
|
||||||
|
|
||||||
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() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../src/memllib/hardware/memlnaut/display/XYPadView.hpp"
|
|
||||||
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
|
||||||
#include "../PAFSynthAudioApp.hpp"
|
|
||||||
#include "../src/memllib/examples/InterfaceRL.hpp"
|
|
||||||
#include "../src/memllib/PicoDefs.hpp"
|
|
||||||
#include "MEMLNautMode.hpp"
|
|
||||||
#include <memory>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
class MEMLNautModePAFSynth {
|
|
||||||
public:
|
|
||||||
constexpr static size_t kN_InputParams = MEMLNAUT_ANALOG_INPUTS;
|
|
||||||
|
|
||||||
inline static PAFSynthAudioApp<> audioAppPAFSynth;
|
|
||||||
std::array<String, PAFSynthAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
|
||||||
|
|
||||||
InterfaceRL interface;
|
|
||||||
std::shared_ptr<InterfaceRL> interfacePtr;
|
|
||||||
|
|
||||||
void setupInterface() {
|
|
||||||
interface.setup(kN_InputParams, PAFSynthAudioApp<>::kN_Params);
|
|
||||||
interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK, true);
|
|
||||||
interface.setModeInfo("pafsynth", "PAFSynth");
|
|
||||||
interfacePtr = make_non_owning(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHelpTitle() {
|
|
||||||
return "PAF Synth Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline stereosample_t process(stereosample_t x) {
|
|
||||||
return audioAppPAFSynth.Process(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupAudio(float sample_rate) {
|
|
||||||
audioAppPAFSynth.Setup(sample_rate, interfacePtr);
|
|
||||||
voiceSpaceList = audioAppPAFSynth.getVoiceSpaceNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline void loop() {
|
|
||||||
audioAppPAFSynth.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(&audioAppPAFSynth.qMIDINoteOn, &midimsg);
|
|
||||||
}else{
|
|
||||||
uint8_t midimsg[2] = { note_number, vel_value };
|
|
||||||
queue_try_add(&audioAppPAFSynth.qMIDINoteOff, &midimsg);
|
|
||||||
}
|
|
||||||
// Serial.printf("MIDI Note %d: %d %d\n", note_number, vel_value, noteon);
|
|
||||||
});
|
|
||||||
// Serial.println("MIDI note callback set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<XYPadView> noteTrigView = std::make_shared<XYPadView>("Play", TFT_SILVER);
|
|
||||||
|
|
||||||
// Cache MIDI notes being echoed
|
|
||||||
static bool is_playing_note = false;
|
|
||||||
static uint8_t last_note_number = 0;
|
|
||||||
|
|
||||||
noteTrigView->SetOnTouchCallback([this](float x, float y) {
|
|
||||||
// Serial.printf("Note trigger at: %.2f, %.2f\n", x, y);
|
|
||||||
// If a note is already playing, stop it
|
|
||||||
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(&audioAppPAFSynth.qMIDINoteOn, &midimsg);
|
|
||||||
midi_interf->sendNoteOn(midimsg[0], midimsg[1]);
|
|
||||||
last_note_number = midimsg[0];
|
|
||||||
is_playing_note = true; // Set flag to indicate a note is playing
|
|
||||||
// Serial.printf("sending %d %d\n",midimsg[0], midimsg[1]);
|
|
||||||
});
|
|
||||||
noteTrigView->SetOnTouchReleaseCallback([this](float x, float y) {
|
|
||||||
// Serial.printf("Note release at: %.2f, %.2f\n", x, y);
|
|
||||||
uint8_t midimsg[2] = {last_note_number,0};
|
|
||||||
queue_try_add(&audioAppPAFSynth.qMIDINoteOff, &midimsg);
|
|
||||||
midi_interf->sendNoteOff(last_note_number, 0);
|
|
||||||
is_playing_note = false; // Reset flag when note is released
|
|
||||||
});
|
|
||||||
MEMLNaut::Instance()->disp->AddView(noteTrigView);
|
|
||||||
};
|
|
||||||
|
|
||||||
// size_t getNMIDICtrlOutputs() {
|
|
||||||
// return 16;
|
|
||||||
// }
|
|
||||||
|
|
||||||
inline void processAnalysisParams() {}
|
|
||||||
|
|
||||||
void analyse(stereosample_t) {}
|
|
||||||
|
|
||||||
void loopCore0() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../ThruAudioApp.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 MEMLNautModeSoundAnalysisMIDI {
|
|
||||||
public:
|
|
||||||
constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params + 3; //ML + joystick
|
|
||||||
InterfaceRL interface;
|
|
||||||
std::shared_ptr<InterfaceRL> interfacePtr;
|
|
||||||
XiasriAnalysis mlAnalysis{kSampleRate};
|
|
||||||
SharedBuffer<float, XiasriAnalysis::kN_Params> machine_list_buffer;
|
|
||||||
|
|
||||||
ThruAudioApp<> audioAppSoundAnalysisMIDI;
|
|
||||||
std::array<String, ThruAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
|
||||||
std::shared_ptr<MIDIInOut> midi_interf;
|
|
||||||
|
|
||||||
void setupInterface() {
|
|
||||||
interface.setup(kN_InputParams, ThruAudioApp<>::kN_Params);
|
|
||||||
interface.bindInterface(InterfaceRL::INPUT_MODES::JOYSTICK_AND_MACHINE_LISTENING);
|
|
||||||
interface.setModeInfo("samidi", "SoundAnalMIDI");
|
|
||||||
interfacePtr = make_non_owning(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHelpTitle() {
|
|
||||||
return "Sound Analysis MIDI Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline stereosample_t process(stereosample_t x) {
|
|
||||||
return audioAppSoundAnalysisMIDI.Process(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupMIDI(std::shared_ptr<MIDIInOut> new_midi_interf) {
|
|
||||||
midi_interf = new_midi_interf;
|
|
||||||
midi_interf->Setup(8);
|
|
||||||
midi_interf->SetMIDISendChannel(1);
|
|
||||||
interface.bindMIDI(midi_interf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addViews() {
|
|
||||||
};
|
|
||||||
|
|
||||||
void setupAudio(float sample_rate) {
|
|
||||||
audioAppSoundAnalysisMIDI.Setup(sample_rate, interfacePtr);
|
|
||||||
// Reinitialize XiasriAnalysis filters after maxiSettings is properly configured
|
|
||||||
mlAnalysis.ReinitFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline void loop() {
|
|
||||||
audioAppSoundAnalysisMIDI.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// size_t getNMIDICtrlOutputs() {
|
|
||||||
// return 8;
|
|
||||||
// }
|
|
||||||
|
|
||||||
__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() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
#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::array<String, VerbFXAudioApp<>::nVoiceSpaces> voiceSpaceList;
|
|
||||||
std::shared_ptr<MIDIInOut> midi_interf;
|
|
||||||
std::shared_ptr<BlockSelectView> enableView;
|
|
||||||
|
|
||||||
|
|
||||||
void setupInterface() {
|
|
||||||
interface.setup(kN_InputParams, VerbFXAudioApp<>::kN_Params);
|
|
||||||
interface.setRVX1Override([this](float value) {
|
|
||||||
audioAppVerbFX.setWetDryQueued(value);
|
|
||||||
});
|
|
||||||
interface.bindInterface(MEMLNAUT_INPUT_MODE, JOYSTICK_IS_4D);
|
|
||||||
interface.setModeInfo("verbfx", "VerbFX");
|
|
||||||
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() {
|
|
||||||
enableView = std::make_shared<BlockSelectView>("FX Enable", TFT_YELLOW, 6, 80, 70, TFT_BLACK,
|
|
||||||
std::vector<String>{ "FilterBnk", "Reverb", "ShortDly", "MedDly", "LongDly", "Dly->Verb" },
|
|
||||||
TFT_BLUE, 2);
|
|
||||||
enableView->SetOnSelectCallback([this](size_t id) {
|
|
||||||
enableView->toggleAlt(id - 1);
|
|
||||||
queue_t& q = audioAppVerbFX.controlMessageQueue;
|
|
||||||
switch(id) {
|
|
||||||
case 1: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_FILTERBANK; queue_try_add(&q, &msg); } break;
|
|
||||||
case 2: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_REVERB; queue_try_add(&q, &msg); } break;
|
|
||||||
case 3: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_SHORT_DELAY; queue_try_add(&q, &msg); } break;
|
|
||||||
case 4: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_MEDIUM_DELAY; queue_try_add(&q, &msg); } break;
|
|
||||||
case 5: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_LONG_DELAY; queue_try_add(&q, &msg); } break;
|
|
||||||
case 6: { auto msg = VerbFXAudioApp<>::controlMessages::MSG_ENABLE_DELAY_TO_REVERB; queue_try_add(&q, &msg); } break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
MEMLNaut::Instance()->disp->AddView(enableView);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
audioAppVerbFX.setVoiceSpace(idx);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
void setupAudio(float sample_rate) {
|
|
||||||
audioAppVerbFX.Setup(sample_rate, interfacePtr);
|
|
||||||
voiceSpaceList = audioAppVerbFX.getVoiceSpaceNames();
|
|
||||||
// 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() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../src/memllib/interface/MIDIInOut.hpp"
|
|
||||||
#include "../XIASRIAudioApp.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 MEMLNautModeXIASRI {
|
|
||||||
public:
|
|
||||||
constexpr static size_t kN_InputParams = XiasriAnalysis::kN_Params; //ML
|
|
||||||
InterfaceRL interface;
|
|
||||||
std::shared_ptr<InterfaceRL> interfacePtr;
|
|
||||||
XiasriAnalysis mlAnalysis{kSampleRate};
|
|
||||||
SharedBuffer<float, XiasriAnalysis::kN_Params> machine_list_buffer;
|
|
||||||
|
|
||||||
XIASRIAudioApp<> audioAppXIASRI;
|
|
||||||
std::shared_ptr<MIDIInOut> midi_interf;
|
|
||||||
|
|
||||||
void setupInterface() {
|
|
||||||
interface.setup(kN_InputParams, XIASRIAudioApp<>::kN_Params);
|
|
||||||
interface.bindInterface(InterfaceRL::INPUT_MODES::MACHINE_LISTENING);
|
|
||||||
interface.setModeInfo("xiasri", "XIASRI");
|
|
||||||
interfacePtr = make_non_owning(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHelpTitle() {
|
|
||||||
return "XIASRI Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline stereosample_t process(stereosample_t x) {
|
|
||||||
return audioAppXIASRI.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) {
|
|
||||||
audioAppXIASRI.Setup(sample_rate, interfacePtr);
|
|
||||||
// Reinitialize XiasriAnalysis filters after maxiSettings is properly configured
|
|
||||||
mlAnalysis.ReinitFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
__force_inline void loop() {
|
|
||||||
audioAppXIASRI.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() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
SKETCH_PATH="${MEMLNAUT_FIRMWARE_SKETCH:-$REPO_ROOT/MEMLNaut-NISPS.ino}"
|
SKETCH_PATH="${MEMLNAUT_FIRMWARE_SKETCH:-$REPO_ROOT/firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino}"
|
||||||
SKETCH_NAME="$(basename "$SKETCH_PATH")"
|
SKETCH_NAME="$(basename "$SKETCH_PATH")"
|
||||||
BUILD_DIR="${MEMLNAUT_FIRMWARE_BUILD_DIR:-/tmp/memlnaut-firmware-build}"
|
BUILD_DIR="${MEMLNAUT_FIRMWARE_BUILD_DIR:-/tmp/memlnaut-firmware-build}"
|
||||||
UF2_PATH_DEFAULT="${MEMLNAUT_FIRMWARE_UF2:-$BUILD_DIR/${SKETCH_NAME}.uf2}"
|
UF2_PATH_DEFAULT="${MEMLNAUT_FIRMWARE_UF2:-$BUILD_DIR/${SKETCH_NAME}.uf2}"
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 697773271e34b3ae711e698d81668b8e11e1055a
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_CHSTRIP_BASIC_HPP__
|
|
||||||
#define __VOICE_SPACE_CHSTRIP_BASIC_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_NEVE66_BODY \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
\
|
|
||||||
inLowPassCutoff = 2000.f + (params[7] * params[7] * 18000.f); \
|
|
||||||
inHighPassCutoff = 30.f + (params[8] * params[8] * 270.f); \
|
|
||||||
\
|
|
||||||
lowShelfFreq = 31.5f + (params[14] * params[14] * 313.5f); \
|
|
||||||
lowShelfQ = 0.6f + (params[15] * 4.4f); \
|
|
||||||
lowShelfGain = -15.f + (params[16] * 30.f);\
|
|
||||||
\
|
|
||||||
peak0Freq = 200.f + (params[1] * params[1] * 1800.f); \
|
|
||||||
peak0Q = 0.6f + (params[5] * 4.4f); \
|
|
||||||
peak0Gain = -18.f + (params[6] * 36.f);\
|
|
||||||
peak1Freq = 800.f + (params[4] * params[4] * 7200.f); \
|
|
||||||
peak1Q = 0.6f + (params[5] * 4.4f); \
|
|
||||||
peak1Gain = -18.f + (params[6] * 36.f);\
|
|
||||||
\
|
|
||||||
highShelfFreq = 1600.f + (params[17] * params[17] * 14400.f); \
|
|
||||||
highShelfQ = 0.6f + (params[18] * 4.4f); \
|
|
||||||
highShelfGain = -18.f + (params[19] * 36.f);\
|
|
||||||
\
|
|
||||||
compThreshold = 20 + (params[10] * -40.f); \
|
|
||||||
compRatio = 1.0f + (params[11] * 19.f); \
|
|
||||||
compAttack = 0.002f + (params[12] * 10.f); \
|
|
||||||
compRelease = 30.0f + (params[13] * params[13] * 2970.f); \
|
|
||||||
\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_SSL4KGIST_BODY \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
inLowPassCutoff = 3000.f + (params[7] * params[7] * 18000.f); \
|
|
||||||
inHighPassCutoff = 10.f + (params[8] * params[8] * 340.f); \
|
|
||||||
\
|
|
||||||
lowShelfFreq = 30.f + (params[14] * params[14] * 420.f); \
|
|
||||||
lowShelfQ = 0.6f + (params[15] * 4.4f); \
|
|
||||||
lowShelfGain = -18.f + (params[16] * 36.f);\
|
|
||||||
\
|
|
||||||
peak0Freq = 200.f + (params[1] * params[1] * 2300.f); \
|
|
||||||
peak0Q = 0.6f + (params[5] * 4.4f); \
|
|
||||||
peak0Gain = -22.f + (params[6] * 44.f);\
|
|
||||||
\
|
|
||||||
peak1Freq = 600.f + (params[4] * params[4] * 6400.f); \
|
|
||||||
peak1Q = 0.6f + (params[5] * 4.4f); \
|
|
||||||
peak1Gain = -22.f + (params[6] * 44.f);\
|
|
||||||
\
|
|
||||||
highShelfFreq = 1500.f + (params[17] * params[17] * 14500.f); \
|
|
||||||
highShelfQ = 0.6f + (params[18] * 4.4f); \
|
|
||||||
highShelfGain = -20.f + (params[19] * 40.f);\
|
|
||||||
\
|
|
||||||
compThreshold = 10.f + (params[10] * -30.f); \
|
|
||||||
compRatio = 1.0f + (params[11] * params[11] * 20.f); \
|
|
||||||
compAttack = 0.08f + (params[12] * 3.f); \
|
|
||||||
compRelease = 100.0f + (params[13] * params [13] * 3900.f); \
|
|
||||||
\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_SSL9KINDA_BODY \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
inLowPassCutoff = 3000.f + (params[7] * params[7] * 18000.f); \
|
|
||||||
inHighPassCutoff = 10.f + (params[8] * params[8] * 490.f); \
|
|
||||||
\
|
|
||||||
lowShelfFreq = 40.f + (params[14] * params[14] * 560.f); \
|
|
||||||
lowShelfQ = 0.6f + (params[15] * 4.4f); \
|
|
||||||
lowShelfGain = -20.f + (params[16] * 40.f);\
|
|
||||||
\
|
|
||||||
peak0Freq = 200.f + (params[1] * params[1] * 1800.f); \
|
|
||||||
peak0Q = 0.5f + (params[5] * 2.f); \
|
|
||||||
peak0Gain = -20.f + (params[6] * 40.f);\
|
|
||||||
\
|
|
||||||
peak1Freq = 600.f + (params[4] * params[4] * 6400.f); \
|
|
||||||
peak1Q = 0.5f + (params[5] * 2.f); \
|
|
||||||
peak1Gain = -20.f + (params[6] * 40.f);\
|
|
||||||
\
|
|
||||||
highShelfFreq = 1500.f + (params[17] * params[17] * 20500.f); \
|
|
||||||
highShelfQ = 0.6f + (params[18] * 4.4f); \
|
|
||||||
highShelfGain = -20.f + (params[19] * 40.f);\
|
|
||||||
\
|
|
||||||
compThreshold = 10.f + (params[10] * -30.f); \
|
|
||||||
compRatio = 1.0f + (params[11] * params[11] * 20.f); \
|
|
||||||
compAttack = 0.08f + (params[12] * 3.f); \
|
|
||||||
compRelease = 100.0f + (params[13] * params [13] * 3900.f); \
|
|
||||||
\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_MALE_VOX_BODY \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
inLowPassCutoff = 1000.f + (params[7] * params[7] * 19000.f); \
|
|
||||||
inHighPassCutoff = 10.f + (params[8] * params[8] * 1990.f); \
|
|
||||||
compThreshold = params[10] * -30.f; \
|
|
||||||
compRatio = 2.0f + (params[11] * 6.f); \
|
|
||||||
compAttack = 0.08f + (params[12] * 50.f); \
|
|
||||||
compRelease = 50.0f + (params[13] * 500.f); \
|
|
||||||
\
|
|
||||||
lowShelfFreq = 60.f + (params[14] * params[14] * 240.f); \
|
|
||||||
lowShelfQ = 0.6f + (params[15]*4.4f); \
|
|
||||||
lowShelfGain = -18.f + (params[16]*36.f);\
|
|
||||||
\
|
|
||||||
peak0Freq = 60.f + (params[1] * params[1] * 440.f); \
|
|
||||||
peak0Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak0Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
peak1Freq = 300.f + (params[4] * params[4] * 7700.f); \
|
|
||||||
peak1Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak1Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
highShelfFreq = 1000.f + (params[17] * params[17] * 7000.f); \
|
|
||||||
highShelfQ = 0.6f + (params[18]*4.4f); \
|
|
||||||
highShelfGain = -18.f + (params[19]*36.f);\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_FEMALE_VOX_BODY \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
inLowPassCutoff = 1000.f + (params[7] * params[7] * 19000.f); \
|
|
||||||
inHighPassCutoff = 10.f + (params[8] * params[8] * 1990.f); \
|
|
||||||
compThreshold = params[10] * -30.f; \
|
|
||||||
compRatio = 2.0f + (params[11] * 6.f); \
|
|
||||||
compAttack = 0.08f + (params[12] * 50.f); \
|
|
||||||
compRelease = 50.0f + (params[13] * 500.f); \
|
|
||||||
\
|
|
||||||
lowShelfFreq = 120.f + (params[14] * params[14] * 180.f); \
|
|
||||||
lowShelfQ = 0.6f + (params[15]*4.4f); \
|
|
||||||
lowShelfGain = -18.f + (params[16]*36.f);\
|
|
||||||
\
|
|
||||||
peak0Freq = 120.f + (params[1] * params[1] * 380.f); \
|
|
||||||
peak0Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak0Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
peak1Freq = 300.f + (params[4] * params[4] * 9700.f); \
|
|
||||||
peak1Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak1Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
highShelfFreq = 1000.f + (params[17] * params[17] * 9000.f); \
|
|
||||||
highShelfQ = 0.6f + (params[18]*4.4f); \
|
|
||||||
highShelfGain = -18.f + (params[19]*36.f);\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
|
|
||||||
#define VOICE_SPACE_CHSTRIP_NEVE_80 \
|
|
||||||
preGain=0.5f + (params[0] * params[0] * 4.f); \
|
|
||||||
constexpr float loPassFrequencies[] = {190, 1200, 3900, 5600, 9200}; \
|
|
||||||
inLowPassCutoff = loPassFrequencies[static_cast<size_t>(params[7] * 3.999999)]; \
|
|
||||||
constexpr float hiPassFrequencies[] = {27, 47, 92, 150, 270}; \
|
|
||||||
inHighPassCutoff = hiPassFrequencies[static_cast<size_t>(params[8] * 3.999999)]; \
|
|
||||||
\
|
|
||||||
compThreshold = params[10] * -30.f; \
|
|
||||||
constexpr float compRatios[] = {1.5, 2, 3, 4, 6}; \
|
|
||||||
compRatio = compRatios[static_cast<size_t>(params[11] * 3.999999)]; \
|
|
||||||
compAttack = params[12] > 0.5f ? 5.f : 1.f; \
|
|
||||||
constexpr float compReleases[] = {400, 800, 1500}; \
|
|
||||||
compRelease = compReleases[static_cast<size_t>(params[13] * 1.999999)]; \
|
|
||||||
\
|
|
||||||
constexpr float lowShelfFrequencies[] = {33, 56, 100, 190, 330};\
|
|
||||||
lowShelfFreq = lowShelfFrequencies[static_cast<size_t>(params[14] * 3.999999)];\
|
|
||||||
lowShelfQ = 0.6f + (params[15]*4.4f); \
|
|
||||||
lowShelfGain = -18.f + (params[16]*36.f);\
|
|
||||||
\
|
|
||||||
constexpr float lowPeakFrequencies[] = {220, 270, 330, 390, 470, 560, 690, 820, 1000, 1200}; \
|
|
||||||
peak0Freq = lowPeakFrequencies[static_cast<size_t>(params[1] * 8.999999)]; \
|
|
||||||
peak0Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak0Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
constexpr float highPeakFrequencies[] = {1500, 1900, 2200, 2700, 3300, 3900, 4700, 5600, 6900, 8200}; \
|
|
||||||
peak1Freq = highPeakFrequencies[static_cast<size_t>(params[4] * 8.999999)]; \
|
|
||||||
peak1Q = 0.6f + (params[5]*4.4f); \
|
|
||||||
peak1Gain = -18.f + (params[6]*36.f);\
|
|
||||||
\
|
|
||||||
constexpr float highShelfFrequencies[] = {3300, 4700, 6900, 10000, 15000}; \
|
|
||||||
highShelfFreq = highShelfFrequencies[static_cast<size_t>(params[17] * 3.999999)]; \
|
|
||||||
highShelfQ = 0.6f + (params[18]*4.4f); \
|
|
||||||
highShelfGain = -18.f + (params[19]*36.f);\
|
|
||||||
\
|
|
||||||
postGain=0.5f + (params[23] * params[23] * 4.f); \
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_BASIC_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_BASIC_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_DEFAULT_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.98f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = smoothParams[17] * 0.9f;\
|
|
||||||
allp1fb = smoothParams[18] * 0.9f;\
|
|
||||||
allp2fb = smoothParams[19] * 0.9f;\
|
|
||||||
allp3fb = smoothParams[20] * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (smoothParams[29] * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + (smoothParams[30] * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (smoothParams[31] * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + (smoothParams[32] * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (smoothParams[33] * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + (smoothParams[34] * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + (smoothParams[35] * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + (smoothParams[36] * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = (smoothParams[38] * 0.98f);\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = (smoothParams[40] * 0.98f);\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = (smoothParams[42] * 0.98f);\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_BRIGHT_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_BRIGHT_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_BRIGHT_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
allp0fb = smoothParams[17] * 0.9f;\
|
|
||||||
allp1fb = smoothParams[18] * 0.9f;\
|
|
||||||
allp2fb = smoothParams[19] * 0.9f;\
|
|
||||||
allp3fb = smoothParams[20] * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (sqrtf(smoothParams[21]) * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (sqrtf(smoothParams[22]) * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (sqrtf(smoothParams[23]) * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (sqrtf(smoothParams[24]) * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (sqrtf(smoothParams[25]) * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (sqrtf(smoothParams[26]) * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (sqrtf(smoothParams[27]) * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (sqrtf(smoothParams[28]) * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + ((smoothParams[29] * smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + ((smoothParams[31] * smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (sqrtf(smoothParams[33]) * 25.f);\
|
|
||||||
filterBankRes5 = 1.f + (sqrtf(smoothParams[34]) * 25.f);\
|
|
||||||
filterBankRes6 = 1.f + (sqrtf(smoothParams[35]) * 25.f);\
|
|
||||||
filterBankRes7 = 1.f + (sqrtf(smoothParams[36]) * 25.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = smoothParams[38] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = smoothParams[40] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = smoothParams[42] * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_CATHEDRAL_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_CATHEDRAL_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_CATHEDRAL_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = sqrtf(smoothParams[1]) * 0.98f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp1fb = sqrtf(smoothParams[3]) * 0.98f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp2fb = sqrtf(smoothParams[5]) * 0.98f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp3fb = sqrtf(smoothParams[7]) * 0.98f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp4fb = sqrtf(smoothParams[9]) * 0.98f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp5fb = sqrtf(smoothParams[11]) * 0.98f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp6fb = sqrtf(smoothParams[13]) * 0.98f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp7fb = sqrtf(smoothParams[15]) * 0.98f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
allp0fb = sqrtf(smoothParams[17]) * 0.9f;\
|
|
||||||
allp1fb = sqrtf(smoothParams[18]) * 0.9f;\
|
|
||||||
allp2fb = sqrtf(smoothParams[19]) * 0.9f;\
|
|
||||||
allp3fb = sqrtf(smoothParams[20]) * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (smoothParams[29] * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + (smoothParams[30] * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (smoothParams[31] * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + (smoothParams[32] * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (smoothParams[33] * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + (smoothParams[34] * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + (smoothParams[35] * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + (smoothParams[36] * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (sqrtf(smoothParams[37]) * 16373.f);\
|
|
||||||
ddelayFeedback = sqrtf(smoothParams[38]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (sqrtf(smoothParams[39]) * 2037.f);\
|
|
||||||
ddelayFeedback1 = sqrtf(smoothParams[40]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (sqrtf(smoothParams[41]) * 501.f);\
|
|
||||||
ddelayFeedback2 = sqrtf(smoothParams[42]) * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43] * smoothParams[43];\
|
|
||||||
delayToVerbLevel = sqrtf(smoothParams[44]) * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_CHAMBER_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_CHAMBER_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_CHAMBER_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = (smoothParams[1] * smoothParams[1]) * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp1fb = (smoothParams[3] * smoothParams[3]) * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp2fb = (smoothParams[5] * smoothParams[5]) * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp3fb = (smoothParams[7] * smoothParams[7]) * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp4fb = (smoothParams[9] * smoothParams[9]) * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp5fb = (smoothParams[11] * smoothParams[11]) * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp6fb = (smoothParams[13] * smoothParams[13]) * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
lp7fb = (smoothParams[15] * smoothParams[15]) * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.1f;\
|
|
||||||
\
|
|
||||||
allp0fb = (smoothParams[17] * smoothParams[17]) * 0.9f;\
|
|
||||||
allp1fb = (smoothParams[18] * smoothParams[18]) * 0.9f;\
|
|
||||||
allp2fb = (smoothParams[19] * smoothParams[19]) * 0.9f;\
|
|
||||||
allp3fb = (smoothParams[20] * smoothParams[20]) * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + ((smoothParams[29] * smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + ((smoothParams[31] * smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + ((smoothParams[33] * smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + ((smoothParams[35] * smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + ((smoothParams[37] * smoothParams[37]) * 16373.f);\
|
|
||||||
ddelayFeedback = (smoothParams[38] * smoothParams[38]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + ((smoothParams[39] * smoothParams[39]) * 2037.f);\
|
|
||||||
ddelayFeedback1 = (smoothParams[40] * smoothParams[40]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + ((smoothParams[41] * smoothParams[41]) * 501.f);\
|
|
||||||
ddelayFeedback2 = (smoothParams[42] * smoothParams[42]) * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_DARK_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_DARK_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_DARK_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.3f) + 0.02f;\
|
|
||||||
\
|
|
||||||
allp0fb = smoothParams[17] * 0.9f;\
|
|
||||||
allp1fb = smoothParams[18] * 0.9f;\
|
|
||||||
allp2fb = smoothParams[19] * 0.9f;\
|
|
||||||
allp3fb = smoothParams[20] * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + ((smoothParams[21] * smoothParams[21]) * 40.f);\
|
|
||||||
filterBankF1 = 80.f + ((smoothParams[22] * smoothParams[22]) * 80.f);\
|
|
||||||
filterBankF2 = 160.f + ((smoothParams[23] * smoothParams[23]) * 160.f);\
|
|
||||||
filterBankF3 = 320.f + ((smoothParams[24] * smoothParams[24]) * 320.f);\
|
|
||||||
filterBankF4 = 640.f + ((smoothParams[25] * smoothParams[25]) * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + ((smoothParams[26] * smoothParams[26]) * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + ((smoothParams[27] * smoothParams[27]) * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + ((smoothParams[28] * smoothParams[28]) * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (sqrtf(smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + (sqrtf(smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (sqrtf(smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + (sqrtf(smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + ((smoothParams[33] * smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + ((smoothParams[35] * smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = smoothParams[38] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = smoothParams[40] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = smoothParams[42] * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_DIFFUSE_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_DIFFUSE_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_DIFFUSE_BODY \
|
|
||||||
filterBankDelayXFade = sqrtf(smoothParams[0]); \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = sqrtf(smoothParams[17]) * 0.95f;\
|
|
||||||
allp1fb = sqrtf(smoothParams[18]) * 0.95f;\
|
|
||||||
allp2fb = sqrtf(smoothParams[19]) * 0.95f;\
|
|
||||||
allp3fb = sqrtf(smoothParams[20]) * 0.95f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + ((smoothParams[29] * smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + ((smoothParams[31] * smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + ((smoothParams[33] * smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + ((smoothParams[35] * smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = sqrtf(smoothParams[38]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = sqrtf(smoothParams[40]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = sqrtf(smoothParams[42]) * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_GRANULAR_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_GRANULAR_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_GRANULAR_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = (smoothParams[1] * smoothParams[1]) * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = (smoothParams[3] * smoothParams[3]) * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = (smoothParams[5] * smoothParams[5]) * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = (smoothParams[7] * smoothParams[7]) * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = (smoothParams[9] * smoothParams[9]) * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = (smoothParams[11] * smoothParams[11]) * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = (smoothParams[13] * smoothParams[13]) * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = (smoothParams[15] * smoothParams[15]) * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = (smoothParams[17] * smoothParams[17]) * 0.9f;\
|
|
||||||
allp1fb = (smoothParams[18] * smoothParams[18]) * 0.9f;\
|
|
||||||
allp2fb = (smoothParams[19] * smoothParams[19]) * 0.9f;\
|
|
||||||
allp3fb = (smoothParams[20] * smoothParams[20]) * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + ((smoothParams[29] * smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + ((smoothParams[31] * smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + ((smoothParams[33] * smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + ((smoothParams[35] * smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = (smoothParams[38] * smoothParams[38]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = (smoothParams[40] * smoothParams[40]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = sqrtf(smoothParams[42]) * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = sqrtf(smoothParams[43]);\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45] * smoothParams[45];\
|
|
||||||
delayBlend = sqrtf(smoothParams[46]);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_HARMONIC_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_HARMONIC_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_HARMONIC_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.98f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = smoothParams[17] * 0.9f;\
|
|
||||||
allp1fb = smoothParams[18] * 0.9f;\
|
|
||||||
allp2fb = smoothParams[19] * 0.9f;\
|
|
||||||
allp3fb = smoothParams[20] * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 80.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 180.f + (smoothParams[22] * 40.f);\
|
|
||||||
filterBankF2 = 280.f + (smoothParams[23] * 40.f);\
|
|
||||||
filterBankF3 = 380.f + (smoothParams[24] * 40.f);\
|
|
||||||
filterBankF4 = 480.f + (smoothParams[25] * 40.f);\
|
|
||||||
filterBankF5 = 580.f + (smoothParams[26] * 40.f);\
|
|
||||||
filterBankF6 = 680.f + (smoothParams[27] * 40.f);\
|
|
||||||
filterBankF7 = 780.f + (smoothParams[28] * 40.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (sqrtf(smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + (sqrtf(smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (sqrtf(smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + (sqrtf(smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (sqrtf(smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + (sqrtf(smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + (sqrtf(smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + (sqrtf(smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = smoothParams[38] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = smoothParams[40] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = smoothParams[42] * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_METALLIC_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_METALLIC_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_METALLIC_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.9f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = sqrtf(smoothParams[17]) * 0.95f;\
|
|
||||||
allp1fb = sqrtf(smoothParams[18]) * 0.95f;\
|
|
||||||
allp2fb = sqrtf(smoothParams[19]) * 0.95f;\
|
|
||||||
allp3fb = sqrtf(smoothParams[20]) * 0.95f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (sqrtf(smoothParams[29]) * 25.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (sqrtf(smoothParams[31]) * 25.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (sqrtf(smoothParams[33]) * 25.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + (sqrtf(smoothParams[35]) * 25.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = smoothParams[38] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = smoothParams[40] * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = smoothParams[42] * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_RESONANT_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_RESONANT_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_RESONANT_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = smoothParams[1] * 0.99f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = smoothParams[3] * 0.99f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = smoothParams[5] * 0.99f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = smoothParams[7] * 0.99f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = smoothParams[9] * 0.99f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = smoothParams[11] * 0.99f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = smoothParams[13] * 0.99;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = smoothParams[15] * 0.99f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = smoothParams[17] * 0.99f;\
|
|
||||||
allp1fb = smoothParams[18] * 0.99f;\
|
|
||||||
allp2fb = smoothParams[19] * 0.99f;\
|
|
||||||
allp3fb = smoothParams[20] * 0.99f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (sqrtf(smoothParams[29]) * 25.f);\
|
|
||||||
filterBankRes1 = 1.f + (sqrtf(smoothParams[30]) * 25.f);\
|
|
||||||
filterBankRes2 = 1.f + (sqrtf(smoothParams[31]) * 25.f);\
|
|
||||||
filterBankRes3 = 1.f + (sqrtf(smoothParams[32]) * 25.f);\
|
|
||||||
filterBankRes4 = 1.f + (sqrtf(smoothParams[33]) * 25.f);\
|
|
||||||
filterBankRes5 = 1.f + (sqrtf(smoothParams[34]) * 25.f);\
|
|
||||||
filterBankRes6 = 1.f + (sqrtf(smoothParams[35]) * 25.f);\
|
|
||||||
filterBankRes7 = 1.f + (sqrtf(smoothParams[36]) * 25.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = (smoothParams[38] * 0.99f);\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = (smoothParams[40] * 0.99f);\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = (smoothParams[42] * 0.99f);\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_SHIMMER_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_SHIMMER_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_SHIMMER_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = sqrtf(smoothParams[1]) * 0.98f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = sqrtf(smoothParams[3]) * 0.98f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = sqrtf(smoothParams[5]) * 0.98f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = sqrtf(smoothParams[7]) * 0.98f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = sqrtf(smoothParams[9]) * 0.98f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = sqrtf(smoothParams[11]) * 0.98f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = sqrtf(smoothParams[13]) * 0.98f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = sqrtf(smoothParams[15]) * 0.98f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.4f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = sqrtf(smoothParams[17]) * 0.99f;\
|
|
||||||
allp1fb = sqrtf(smoothParams[18]) * 0.99f;\
|
|
||||||
allp2fb = sqrtf(smoothParams[19]) * 0.99f;\
|
|
||||||
allp3fb = sqrtf(smoothParams[20]) * 0.99f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + (sqrtf(smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + (sqrtf(smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + (sqrtf(smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + (sqrtf(smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + (sqrtf(smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + (sqrtf(smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + (sqrtf(smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + (sqrtf(smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = sqrtf(smoothParams[38]) * 0.95f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = sqrtf(smoothParams[40]) * 0.95f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = sqrtf(smoothParams[42]) * 0.95f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43] * smoothParams[43];\
|
|
||||||
delayToVerbLevel = sqrtf(smoothParams[44]) * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_VERBFX_SOFT_HPP__
|
|
||||||
#define __VOICE_SPACE_VERBFX_SOFT_HPP__
|
|
||||||
|
|
||||||
#define VOICE_SPACE_VERBFX_SOFT_BODY \
|
|
||||||
filterBankDelayXFade = smoothParams[0]; \
|
|
||||||
\
|
|
||||||
lp0fb = (smoothParams[1] * smoothParams[1]) * 0.9f;\
|
|
||||||
lp0cutoff = (smoothParams[2] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp1fb = (smoothParams[3] * smoothParams[3]) * 0.9f;\
|
|
||||||
lp1cutoff = (smoothParams[4] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp2fb = (smoothParams[5] * smoothParams[5]) * 0.9f;\
|
|
||||||
lp2cutoff = (smoothParams[6] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp3fb = (smoothParams[7] * smoothParams[7]) * 0.9f;\
|
|
||||||
lp3cutoff = (smoothParams[8] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp4fb = (smoothParams[9] * smoothParams[9]) * 0.9f;\
|
|
||||||
lp4cutoff = (smoothParams[10] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp5fb = (smoothParams[11] * smoothParams[11]) * 0.98f;\
|
|
||||||
lp5cutoff = (smoothParams[12] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp6fb = (smoothParams[13] * smoothParams[13]) * 0.9f;\
|
|
||||||
lp6cutoff = (smoothParams[14] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
lp7fb = (smoothParams[15] * smoothParams[15]) * 0.9f;\
|
|
||||||
lp7cutoff = (smoothParams[16] * 0.5f) + 0.05f;\
|
|
||||||
\
|
|
||||||
allp0fb = (smoothParams[17] * smoothParams[17]) * 0.9f;\
|
|
||||||
allp1fb = (smoothParams[18] * smoothParams[18]) * 0.9f;\
|
|
||||||
allp2fb = (smoothParams[19] * smoothParams[19]) * 0.9f;\
|
|
||||||
allp3fb = (smoothParams[20] * smoothParams[20]) * 0.9f;\
|
|
||||||
\
|
|
||||||
filterBankF0 = 40.f + (smoothParams[21] * 40.f);\
|
|
||||||
filterBankF1 = 80.f + (smoothParams[22] * 80.f);\
|
|
||||||
filterBankF2 = 160.f + (smoothParams[23] * 160.f);\
|
|
||||||
filterBankF3 = 320.f + (smoothParams[24] * 320.f);\
|
|
||||||
filterBankF4 = 640.f + (smoothParams[25] * 640.f);\
|
|
||||||
filterBankF5 = 1280.f + (smoothParams[26] * 1280.f);\
|
|
||||||
filterBankF6 = 2560.f + (smoothParams[27] * 2560.f);\
|
|
||||||
filterBankF7 = 5120.f + (smoothParams[28] * 5120.f);\
|
|
||||||
\
|
|
||||||
filterBankRes0 = 1.f + ((smoothParams[29] * smoothParams[29]) * 19.f);\
|
|
||||||
filterBankRes1 = 1.f + ((smoothParams[30] * smoothParams[30]) * 19.f);\
|
|
||||||
filterBankRes2 = 1.f + ((smoothParams[31] * smoothParams[31]) * 19.f);\
|
|
||||||
filterBankRes3 = 1.f + ((smoothParams[32] * smoothParams[32]) * 19.f);\
|
|
||||||
filterBankRes4 = 1.f + ((smoothParams[33] * smoothParams[33]) * 19.f);\
|
|
||||||
filterBankRes5 = 1.f + ((smoothParams[34] * smoothParams[34]) * 19.f);\
|
|
||||||
filterBankRes6 = 1.f + ((smoothParams[35] * smoothParams[35]) * 19.f);\
|
|
||||||
filterBankRes7 = 1.f + ((smoothParams[36] * smoothParams[36]) * 19.f);\
|
|
||||||
\
|
|
||||||
ddelayTime = 10.f + (smoothParams[37] * 16373.f);\
|
|
||||||
ddelayFeedback = (smoothParams[38] * smoothParams[38]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime1 = 10.f + (smoothParams[39] * 2037.f);\
|
|
||||||
ddelayFeedback1 = (smoothParams[40] * smoothParams[40]) * 0.98f;\
|
|
||||||
\
|
|
||||||
ddelayTime2 = 10.f + (smoothParams[41] * 501.f);\
|
|
||||||
ddelayFeedback2 = (smoothParams[42] * smoothParams[42]) * 0.98f;\
|
|
||||||
\
|
|
||||||
verbVsDelayLevel = smoothParams[43];\
|
|
||||||
delayToVerbLevel = smoothParams[44] * 0.99f;\
|
|
||||||
\
|
|
||||||
delayMorph = smoothParams[45];\
|
|
||||||
delayBlend = smoothParams[46];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_1_HPP__
|
|
||||||
#define __VOICE_SPACE_1_HPP__
|
|
||||||
|
|
||||||
// VoiceSpace1 - Rowantares
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_1_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=1.f; \
|
|
||||||
p2Gain=0.f; \
|
|
||||||
p3Gain=0.f; \
|
|
||||||
\
|
|
||||||
detune1 = 2.0f; \
|
|
||||||
detune2 = 1.0f; \
|
|
||||||
detune3 = 1.0f; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[2] * 1.f); \
|
|
||||||
paf1_cf = (params[3] * 1.f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[5] * 200.f); \
|
|
||||||
paf1_bw = 10.f + (params[6] * 200.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = params[8] * params[8] * 0.05f; \
|
|
||||||
paf1_vib = (params[9] * params[9] * 0.05f); \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[11] * params[11]* 5.0f); \
|
|
||||||
paf1_vfr = (params[12] * params[12] * 5.f); \
|
|
||||||
\
|
|
||||||
paf0_shift = -50.f + (params[14] * 100.f); \
|
|
||||||
paf1_shift = -50.f + (params[15] * 100.f); \
|
|
||||||
\
|
|
||||||
dl1mix = params[17] * params[17] * 0.8f; \
|
|
||||||
\
|
|
||||||
dlfb = params[19] * 0.9f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f + params[30] * 200.f,1.f + params[20] * params[20] * 500.f,0.01 + (params[31] * 0.5f), 1.f + params[32] * 500.f, sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[26] * params[26]; \
|
|
||||||
sineShapeASym = params[27] * params[27] * 0.1f; \
|
|
||||||
sineShapeMix = params[28]; \
|
|
||||||
\
|
|
||||||
rmGain = params[29] * params[29]; \
|
|
||||||
feedbackGain = 0.0f; \
|
|
||||||
\
|
|
||||||
delayMax=1000; \
|
|
||||||
fbSmoothAlpha=0.f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_1_HPP__
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_2_HPP__
|
|
||||||
#define __VOICE_SPACE_2_HPP__
|
|
||||||
|
|
||||||
// VoiceSpace2 - Neemeda
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_2_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=1.f; \
|
|
||||||
p2Gain=1.f; \
|
|
||||||
p3Gain=1.f; \
|
|
||||||
\
|
|
||||||
detune1 = 2.0f; \
|
|
||||||
detune2 = 0.5f; \
|
|
||||||
detune3 = 1.0f; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[2] * 1.f); \
|
|
||||||
paf1_cf = (params[3] * 1.f); \
|
|
||||||
paf2_cf = (params[4] * 1.f); \
|
|
||||||
paf3_cf = (params[21] * 1.f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[5] * 400.f); \
|
|
||||||
paf1_bw = 10.f + (params[6] * 300.f); \
|
|
||||||
paf2_bw = 10.f + (params[7] * 200.f); \
|
|
||||||
paf3_bw = 10.f + (params[22] * 100.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = params[8] * params[8] * 0.1f; \
|
|
||||||
paf1_vib = (params[9] * params[9] * 0.05f); \
|
|
||||||
paf2_vib = (params[10] * params[10] * 0.05); \
|
|
||||||
paf3_vib = (params[23] * params[23] * 0.05f); \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[11] * params[11]* 5.0f); \
|
|
||||||
paf1_vfr = (params[12] * params[12] * 5.f); \
|
|
||||||
paf2_vfr = (params[13] * params[13] * 10.f); \
|
|
||||||
paf3_vfr = (params[24] * params[24] * 10.f); \
|
|
||||||
\
|
|
||||||
paf0_shift = -50.f + (params[14] * 200.f); \
|
|
||||||
paf1_shift = -50.f + (params[15] * 200.f); \
|
|
||||||
paf2_shift = -50.f + (params[16] * 300.f); \
|
|
||||||
paf3_shift = -50.f + (params[25] * 400.f); \
|
|
||||||
\
|
|
||||||
dl1mix = params[17] * params[17] * 0.3f; \
|
|
||||||
\
|
|
||||||
dlfb = params[19] * 0.95f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f + params[30] * 200.f,1.f + params[20] * params[20] * 500.f,0.01 + (params[31] * 0.5f), 1.f + params[32] * 500.f, sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[26] * params[26]; \
|
|
||||||
sineShapeASym = params[27] * params[27] * 0.2f; \
|
|
||||||
sineShapeMix = params[28]; \
|
|
||||||
\
|
|
||||||
rmGain = params[29] * params[29]; \
|
|
||||||
feedbackGain = 0.01f; \
|
|
||||||
\
|
|
||||||
delayMax=3000; \
|
|
||||||
fbSmoothAlpha=0.94f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_2_HPP__
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_PERC_HPP__
|
|
||||||
#define __VOICE_SPACE_PERC_HPP__
|
|
||||||
|
|
||||||
// VoiceSpacePerc - Aquillow
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_PERC_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=1.f; \
|
|
||||||
p2Gain=1.f; \
|
|
||||||
p3Gain=1.f; \
|
|
||||||
\
|
|
||||||
detune1 = 1.0f; \
|
|
||||||
detune2 = 1.1f; \
|
|
||||||
detune3 = 1.2f; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[2] * 2.f); \
|
|
||||||
paf1_cf = (params[3] * 2.f); \
|
|
||||||
paf2_cf = (params[4] * 2.f); \
|
|
||||||
paf3_cf = (params[21] * 2.f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[5] * 400.f); \
|
|
||||||
paf1_bw = 10.f + (params[6] * 50.f); \
|
|
||||||
paf2_bw = 10.f + (params[7] * 50.f); \
|
|
||||||
paf3_bw = 10.f + (params[22] * 100.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = params[8] * params[8] * 0.01f; \
|
|
||||||
paf1_vib = (params[9] * params[9] * 0.01f); \
|
|
||||||
paf2_vib = (params[10] * params[10] * 0.01); \
|
|
||||||
paf3_vib = (params[23] * params[23] * 0.01f); \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[11] * params[11]* 15.0f); \
|
|
||||||
paf1_vfr = (params[12] * params[12] * 15.f); \
|
|
||||||
paf2_vfr = (params[13] * params[13] * 15.f); \
|
|
||||||
paf3_vfr = (params[24] * params[24] * 15.f); \
|
|
||||||
\
|
|
||||||
paf0_shift = -500.f + (params[14] * 500.f); \
|
|
||||||
paf1_shift = -300.f + (params[15] * 300.f); \
|
|
||||||
paf2_shift = -300.f + (params[16] * 300.f); \
|
|
||||||
paf3_shift = -300.f + (params[25] * 300.f); \
|
|
||||||
\
|
|
||||||
dl1mix = params[17] * params[17] * 0.5f; \
|
|
||||||
\
|
|
||||||
dlfb = params[19] * 0.95f; \
|
|
||||||
\
|
|
||||||
env.setup(0.2f + (params[30] * 1.f),0.5f + params[20] * params[20] * 100.f,0.01 + (params[31] * 0.1f),1.f+ params[32] * params[32] * 300.f, sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[26]; \
|
|
||||||
sineShapeASym = params[27]* 0.5f; \
|
|
||||||
sineShapeMix = params[28]; \
|
|
||||||
\
|
|
||||||
rmGain = params[29]; \
|
|
||||||
feedbackGain = 0.1f; \
|
|
||||||
\
|
|
||||||
delayMax=178; \
|
|
||||||
fbSmoothAlpha=0.5f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_PERC_HPP__
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_QUAD_DETUNE_HPP__
|
|
||||||
#define __VOICE_SPACE_QUAD_DETUNE_HPP__
|
|
||||||
|
|
||||||
// VoiceSpaceQuadDetune - Ellipticacacia
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_QUAD_DETUNE_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=1.f; \
|
|
||||||
p2Gain=1.f; \
|
|
||||||
p3Gain=0.8f; \
|
|
||||||
\
|
|
||||||
const float factor = 1.f + (params[17] * 0.2f); \
|
|
||||||
detune1 = 1.f * factor; \
|
|
||||||
detune2 = detune1 * factor; \
|
|
||||||
detune3 = detune2 * factor; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[0] * 1.0f); \
|
|
||||||
paf1_cf = (params[0] * 2.0f); \
|
|
||||||
paf2_cf = (params[1] * 3.0f); \
|
|
||||||
paf3_cf = (params[1] * 5.0f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[2] * 500.f); \
|
|
||||||
paf1_bw = 10.f + (params[3] * 500.f); \
|
|
||||||
paf2_bw = 10.f + (params[4] * 500.f); \
|
|
||||||
paf3_bw = 10.f + (params[5] * 2000.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = (params[18] * params[18] * 0.05f); \
|
|
||||||
paf1_vib = paf0_vib; \
|
|
||||||
paf2_vib = 0.f; \
|
|
||||||
paf3_vib = 0.f; \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[19] * params[19] * 15.f); \
|
|
||||||
paf1_vfr = paf0_vfr; \
|
|
||||||
paf2_vfr = 0.f; \
|
|
||||||
paf3_vfr = 0.f; \
|
|
||||||
\
|
|
||||||
paf0_shift=0.f; \
|
|
||||||
paf1_shift=0.f; \
|
|
||||||
paf2_shift=0.f; \
|
|
||||||
paf3_shift = -40.f + (params[9] * 80.f); \
|
|
||||||
\
|
|
||||||
dl1mix = 0.f; \
|
|
||||||
\
|
|
||||||
dlfb = 0.f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f+(params[10] * 20.f) , 1.f + (params[11] * 200.f), params[12] * 0.4f, 10.f + (params[13] * 300.f), sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[14] * params[14] * 0.2f; \
|
|
||||||
sineShapeASym = params[15] * 0.05f; \
|
|
||||||
sineShapeMix = params[16] * 0.3f; \
|
|
||||||
\
|
|
||||||
rmGain = 0.f; \
|
|
||||||
feedbackGain = 0.0f; \
|
|
||||||
\
|
|
||||||
delayMax=1000; \
|
|
||||||
fbSmoothAlpha=0.f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_QUAD_DETUNE_HPP__
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_QUAD_DIST_HPP__
|
|
||||||
#define __VOICE_SPACE_QUAD_DIST_HPP__
|
|
||||||
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_QUAD_DIST_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=params[24]; \
|
|
||||||
p2Gain=params[25]; \
|
|
||||||
p3Gain=params[26]; \
|
|
||||||
\
|
|
||||||
const float factor = 1.f + (params[17] + params[27] * 0.6f); \
|
|
||||||
detune1 = (1.f * factor) * 0.5f; \
|
|
||||||
detune2 = (1.f * factor * factor) * 2.f; \
|
|
||||||
detune3 = (detune2 * factor) * 2.f; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[0] * 4.0f); \
|
|
||||||
paf1_cf = (params[0] * 4.0f); \
|
|
||||||
paf2_cf = (params[1] * 8.0f); \
|
|
||||||
paf3_cf = (params[1] * 8.0f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[2] * 5000.f); \
|
|
||||||
paf1_bw = 10.f + (params[3] * 5000.f); \
|
|
||||||
paf2_bw = 10.f + (params[4] * 5000.f); \
|
|
||||||
paf3_bw = 10.f + (params[5] * 2000.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = (params[18] * params[18] * 0.05f); \
|
|
||||||
paf1_vib = paf0_vib * 2.f; \
|
|
||||||
paf2_vib = params[28] * 0.1f; \
|
|
||||||
paf3_vib = params[29] * 0.1f; \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[19] * 15.f); \
|
|
||||||
paf1_vfr = (params[28] * 15.f); \
|
|
||||||
paf2_vfr = (params[29] * 15.f); \
|
|
||||||
paf3_vfr = (params[30] * 15.f); \
|
|
||||||
\
|
|
||||||
paf0_shift=0.f; \
|
|
||||||
paf1_shift=-800.f + (params[27] * 600.f);; \
|
|
||||||
paf2_shift = -300.f + (params[8] * 600.f); \
|
|
||||||
paf3_shift = -350.f + (params[9] * 100.f); \
|
|
||||||
\
|
|
||||||
dl1mix = params[20] * params[20] * 0.1f; \
|
|
||||||
\
|
|
||||||
dlfb = params[21] * params[21] * 0.7f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f+(params[10] * 20.f) , 1.f + (params[11] * 200.f), params[12] * 0.4f, 10.f + (params[13] * 300.f), sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[14] * params[14] * 0.9f; \
|
|
||||||
sineShapeASym = params[15] * 0.5f; \
|
|
||||||
sineShapeMix = params[16] * 0.8f; \
|
|
||||||
\
|
|
||||||
rmGain = params[22] * params[22] * 0.99f; \
|
|
||||||
feedbackGain = params[23] * params[23] * 0.7f; \
|
|
||||||
\
|
|
||||||
delayMax=10000; \
|
|
||||||
fbSmoothAlpha=0.9f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_QUAD_DETUNE_HPP__
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_QUAD_OCT_HPP__
|
|
||||||
#define __VOICE_SPACE_QUAD_OCT_HPP__
|
|
||||||
|
|
||||||
// VoiceSpaceQuadOct - Elderstar
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_QUAD_OCT_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=params[24]; \
|
|
||||||
p2Gain=params[25]; \
|
|
||||||
p3Gain=params[26]; \
|
|
||||||
\
|
|
||||||
const float factor = 1.f + (params[17] + params[27] * 0.2f); \
|
|
||||||
detune1 = (1.f * factor) * 0.5f; \
|
|
||||||
detune2 = (1.f * factor * factor) * 2.f; \
|
|
||||||
detune3 = (detune2 * factor) * 2.f; \
|
|
||||||
\
|
|
||||||
paf0_cf = (params[0] * 1.0f); \
|
|
||||||
paf1_cf = (params[0] * 2.0f); \
|
|
||||||
paf2_cf = (params[1] * 3.0f); \
|
|
||||||
paf3_cf = (params[1] * 5.0f); \
|
|
||||||
\
|
|
||||||
paf0_bw = 10.f + (params[2] * 500.f); \
|
|
||||||
paf1_bw = 10.f + (params[3] * 500.f); \
|
|
||||||
paf2_bw = 10.f + (params[4] * 500.f); \
|
|
||||||
paf3_bw = 10.f + (params[5] * 2000.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = (params[18] * params[18] * 0.05f); \
|
|
||||||
paf1_vib = paf0_vib * 2.f; \
|
|
||||||
paf2_vib = params[28] * 0.1f; \
|
|
||||||
paf3_vib = params[29] * 0.1f; \
|
|
||||||
\
|
|
||||||
paf0_vfr = (params[19] * params[19] * 15.f); \
|
|
||||||
paf1_vfr = paf0_vfr; \
|
|
||||||
paf2_vfr = 0.f; \
|
|
||||||
paf3_vfr = 0.f; \
|
|
||||||
\
|
|
||||||
paf0_shift=0.f; \
|
|
||||||
paf1_shift=0.f; \
|
|
||||||
paf2_shift = -100.f + (params[8] * 200.f); \
|
|
||||||
paf3_shift = -150.f + (params[9] * 300.f); \
|
|
||||||
\
|
|
||||||
dl1mix = params[20] * params[20] * 0.1f; \
|
|
||||||
\
|
|
||||||
dlfb = params[21] * params[21] * 0.7f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f+(params[10] * 20.f) , 1.f + (params[11] * 200.f), params[12] * 0.4f, 10.f + (params[13] * 300.f), sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[14] * params[14] * 0.9f; \
|
|
||||||
sineShapeASym = params[15] * 0.5f; \
|
|
||||||
sineShapeMix = params[16] * 0.8f; \
|
|
||||||
\
|
|
||||||
rmGain = params[22] * params[22] * 0.7f; \
|
|
||||||
feedbackGain = params[23] * params[23] * 0.4f; \
|
|
||||||
\
|
|
||||||
delayMax=4000; \
|
|
||||||
fbSmoothAlpha=0.9f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_QUAD_OCT_HPP__
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
#ifndef __VOICE_SPACE_SINGLE_1_HPP__
|
|
||||||
#define __VOICE_SPACE_SINGLE_1_HPP__
|
|
||||||
|
|
||||||
// VoiceSpaceSingle1 - Magnetarch
|
|
||||||
// Macro to generate the voice space lambda body inline
|
|
||||||
#define VOICE_SPACE_SINGLE_1_BODY \
|
|
||||||
p0Gain=1.f; \
|
|
||||||
p1Gain=0.f; \
|
|
||||||
p2Gain=0.f; \
|
|
||||||
p3Gain=0.f; \
|
|
||||||
float p1 = params[0] + params[7] + params[8];\
|
|
||||||
p1 = ((sinf(p1 * TWOPI)) + 1.f) * 0.5f;\
|
|
||||||
float p2 = params[9] + params[10] + params[11];\
|
|
||||||
p2 = ((sinf(p2 * TWOPI)) + 1.f) * 0.5f;\
|
|
||||||
float p3 = params[12] + params[13] + params[14] + params[15];\
|
|
||||||
p3 = ((sinf(p3 * TWOPI)) + 1.f) * 0.5f;\
|
|
||||||
paf0_cf = (p1 * 2.0f); \
|
|
||||||
paf0_bw = 10.f + (p2 * 700.f); \
|
|
||||||
\
|
|
||||||
paf0_vib = 0.f; \
|
|
||||||
\
|
|
||||||
paf0_vfr = 0.f; \
|
|
||||||
\
|
|
||||||
paf0_shift = -20.f + (p3 * 40.f); \
|
|
||||||
\
|
|
||||||
dl1mix = 0.f; \
|
|
||||||
\
|
|
||||||
dlfb = 0.f; \
|
|
||||||
\
|
|
||||||
env.setup(1.f+(params[1] * 50.f) , 1.f + (params[2] * 300.f), params[3] * 0.7f, 10.f + (params[4] * 500.f), sampleRatef ); \
|
|
||||||
\
|
|
||||||
sineShapeGain = params[5] * params[5] * 0.2f; \
|
|
||||||
sineShapeASym = 0.f; \
|
|
||||||
sineShapeMix = params[6] * 0.3f; \
|
|
||||||
\
|
|
||||||
rmGain = 0.f; \
|
|
||||||
feedbackGain = 0.0f; \
|
|
||||||
\
|
|
||||||
delayMax=1000; \
|
|
||||||
fbSmoothAlpha=0.f;
|
|
||||||
|
|
||||||
#endif // __VOICE_SPACE_SINGLE_1_HPP__
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef MEML_MEMLNAUT_NISPS_VOICESPACES_VOICESPACES_HPP
|
|
||||||
#define MEML_MEMLNAUT_NISPS_VOICESPACES_VOICESPACES_HPP
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <functional>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
template<size_t NPARAMS>
|
|
||||||
using VoiceSpaceFn = std::function<void(const std::array<float, NPARAMS>&)>;
|
|
||||||
|
|
||||||
template<size_t NPARAMS>
|
|
||||||
struct VoiceSpace {
|
|
||||||
char name[16]="default";
|
|
||||||
VoiceSpaceFn<NPARAMS> mappingFunction = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MEML_MEMLNAUT_NISPS_VOICESPACES_VOICESPACES_HPP
|
|
||||||
Loading…
Reference in a new issue