mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
add per-oscillator coarse tune (ctune1/ctune2); add blueprint image
This commit is contained in:
parent
35d6cd014a
commit
21aac2d355
8 changed files with 35 additions and 10 deletions
15
README.md
15
README.md
|
|
@ -7,8 +7,17 @@ state-variable filter, dual envelopes, an LFO, a built-in FX unit (chorus /
|
||||||
delay / space), and a factory patch bank - all edited and browsed through the
|
delay / space), and a factory patch bank - all edited and browsed through the
|
||||||
on-screen LCD instead of a conventional panel of knobs.
|
on-screen LCD instead of a conventional panel of knobs.
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What it is at a glance
|
## What it is at a glance
|
||||||
|
|
@ -126,7 +135,7 @@ The interface is a small nested menu system:
|
||||||
|
|
||||||
| Screen | Parameters |
|
| Screen | Parameters |
|
||||||
|--------|------------|
|
|--------|------------|
|
||||||
| **VOICE** | OSC1 WAVE, OSC2 WAVE, DETUNE, OSC BAL |
|
| **VOICE** | OSC1 WAVE, OSC2 WAVE, COARSE TUNE 1, COARSE TUNE 2, DETUNE 1, DETUNE 2, OSC BAL |
|
||||||
| **FILTER** | FILTER TYPE, CUTOFF, RESONANCE, FILT ENV, KEY TRACK |
|
| **FILTER** | FILTER TYPE, CUTOFF, RESONANCE, FILT ENV, KEY TRACK |
|
||||||
| **ENVELOPE** | AMP ATTACK, AMP DECAY, AMP SUSTAIN, AMP RELEASE, FENV ATTACK, FENV DECAY, FENV SUSTAIN, FENV RELEASE |
|
| **ENVELOPE** | AMP ATTACK, AMP DECAY, AMP SUSTAIN, AMP RELEASE, FENV ATTACK, FENV DECAY, FENV SUSTAIN, FENV RELEASE |
|
||||||
| **LFO** | LFO RATE, LFO SHAPE, LFO DEPTH, LFO TARGET |
|
| **LFO** | LFO RATE, LFO SHAPE, LFO DEPTH, LFO TARGET |
|
||||||
|
|
@ -174,7 +183,9 @@ the host map onto the factory bank.
|
||||||
|
|
||||||
- **Voice engine** - each voice is a classic two-osc → mix → SVF filter →
|
- **Voice engine** - each voice is a classic two-osc → mix → SVF filter →
|
||||||
amp-envelope chain. The oscillator mix is a crossfade between OSC1 and
|
amp-envelope chain. The oscillator mix is a crossfade between OSC1 and
|
||||||
OSC2, and detune offsets OSC2's frequency (OSC1 × `2^(detune/1200)`).
|
OSC2, and each oscillator has its own tuning: a coarse tune in semitones
|
||||||
|
(`2^(ctune/12)`) plus a fine detune in cents (`2^(detuneN/1200)`, DETUNE 1 →
|
||||||
|
OSC1, DETUNE 2 → OSC2).
|
||||||
- **Filter** - a stable Chamberlin state-variable filter; cutoff is modulated
|
- **Filter** - a stable Chamberlin state-variable filter; cutoff is modulated
|
||||||
by the filter envelope, key tracking, the LFO, and the mod wheel, with a
|
by the filter envelope, key tracking, the LFO, and the mod wheel, with a
|
||||||
smoothed control signal to suppress zipper noise.
|
smoothed control signal to suppress zipper noise.
|
||||||
|
|
|
||||||
|
|
@ -644,8 +644,8 @@ void LcdPanel::drawParamScreen (juce::Graphics& g)
|
||||||
pxTextM (g, 3, 1, paramScreenTitle (paramScreenIdx), 1.0f);
|
pxTextM (g, 3, 1, paramScreenTitle (paramScreenIdx), 1.0f);
|
||||||
drawFilled (g, 3, 6, gridW - 6, 1, 0.35f);
|
drawFilled (g, 3, 6, gridW - 6, 1, 0.35f);
|
||||||
|
|
||||||
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::detune1, prm::Id::detune, prm::Id::oscMix,
|
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::ctune1, prm::Id::ctune2,
|
||||||
prm::Id::xmod, prm::Id::xmix, prm::Id::evolve, prm::Id::warp };
|
prm::Id::detune1, prm::Id::detune, prm::Id::oscMix };
|
||||||
|
|
||||||
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
||||||
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||||
|
|
@ -745,7 +745,7 @@ int LcdPanel::paramCountForScreen (int idx) const
|
||||||
{
|
{
|
||||||
switch (idx)
|
switch (idx)
|
||||||
{
|
{
|
||||||
case 0: return 5;
|
case 0: return 7;
|
||||||
case 1: return 5;
|
case 1: return 5;
|
||||||
case 2: return 8;
|
case 2: return 8;
|
||||||
case 3: return 4;
|
case 3: return 4;
|
||||||
|
|
@ -844,7 +844,8 @@ void LcdPanel::stepParam (prm::Id id, int dir, bool coarse)
|
||||||
|
|
||||||
static prm::Id paramForCursor (int screenIdx, int row)
|
static prm::Id paramForCursor (int screenIdx, int row)
|
||||||
{
|
{
|
||||||
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::detune1, prm::Id::detune, prm::Id::oscMix };
|
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::ctune1, prm::Id::ctune2,
|
||||||
|
prm::Id::detune1, prm::Id::detune, prm::Id::oscMix };
|
||||||
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
||||||
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||||
static const prm::Id lfo[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
static const prm::Id lfo[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ static const Desc table[num] = {
|
||||||
{ "xmix", "XMIX AMT", "XMX", 0, 1, 0, false, 0.01f, 0.08f },
|
{ "xmix", "XMIX AMT", "XMX", 0, 1, 0, false, 0.01f, 0.08f },
|
||||||
{ "evolve", "EVOLVE", "EVO", 0, 1, 0, false, 0.01f, 0.08f },
|
{ "evolve", "EVOLVE", "EVO", 0, 1, 0, false, 0.01f, 0.08f },
|
||||||
{ "warp", "WARP", "WRP", 0, 1, 0, false, 0.01f, 0.08f },
|
{ "warp", "WARP", "WRP", 0, 1, 0, false, 0.01f, 0.08f },
|
||||||
|
{ "ctune1", "COARSE TUNE 1","CT1", -24, 24, 0, true, 1, 1 },
|
||||||
|
{ "ctune2", "COARSE TUNE 2","CT2", -24, 24, 0, true, 1, 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const Desc& desc (Id id) { return table[static_cast<int>(id)]; }
|
const Desc& desc (Id id) { return table[static_cast<int>(id)]; }
|
||||||
|
|
@ -218,6 +220,11 @@ juce::String formatParam (Id id, float v)
|
||||||
const int t = (int) v;
|
const int t = (int) v;
|
||||||
return juce::String (t > 0 ? "+" : "") + juce::String (t) + "ST";
|
return juce::String (t > 0 ? "+" : "") + juce::String (t) + "ST";
|
||||||
}
|
}
|
||||||
|
case Id::ctune1:
|
||||||
|
case Id::ctune2: {
|
||||||
|
const int t = (int) v;
|
||||||
|
return juce::String (t > 0 ? "+" : "") + juce::String (t) + "ST";
|
||||||
|
}
|
||||||
case Id::hq: return v >= 0.5f ? "HQ" : "ECO";
|
case Id::hq: return v >= 0.5f ? "HQ" : "ECO";
|
||||||
case Id::xmod: return xmodTypeName ((int) v);
|
case Id::xmod: return xmodTypeName ((int) v);
|
||||||
case Id::xmix:
|
case Id::xmix:
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,11 @@ enum class Id : int {
|
||||||
hq,
|
hq,
|
||||||
arpOn, arpRate, arpPattern, arpOct, arpGate,
|
arpOn, arpRate, arpPattern, arpOct, arpGate,
|
||||||
detune1,
|
detune1,
|
||||||
xmod, xmix, evolve, warp
|
xmod, xmix, evolve, warp,
|
||||||
|
ctune1, ctune2
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int num = 40;
|
constexpr int num = 42;
|
||||||
|
|
||||||
struct Desc {
|
struct Desc {
|
||||||
const char* id;
|
const char* id;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ enum { O_SIN = 0, O_TRI, O_SAW, O_PUL, O_NOI, O_FM, O_PWM, O_SUP, O_DSW, O_WRP,
|
||||||
// 24 vol 25 pan 26 poly 27 glide 28 tune 29 hq
|
// 24 vol 25 pan 26 poly 27 glide 28 tune 29 hq
|
||||||
// 30 arpOn 31 arpRate 32 arpPattern 33 arpOct 34 arpGate
|
// 30 arpOn 31 arpRate 32 arpPattern 33 arpOct 34 arpGate
|
||||||
// 35 detune1 36 xmod 37 xmix 38 evolve 39 warp
|
// 35 detune1 36 xmod 37 xmix 38 evolve 39 warp
|
||||||
|
// 40 ctune1 41 ctune2 (default 0; not stored per-patch)
|
||||||
|
|
||||||
static const Patch defs[] = {
|
static const Patch defs[] = {
|
||||||
// == POLY PAD ==========================================================
|
// == POLY PAD ==========================================================
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,8 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||||
const float detuneRatio2 = std::exp2f (p.detune / 1200.0f);
|
const float detuneRatio2 = std::exp2f (p.detune / 1200.0f);
|
||||||
const float detuneRatio1 = std::exp2f (p.detune1 / 1200.0f);
|
const float detuneRatio1 = std::exp2f (p.detune1 / 1200.0f);
|
||||||
const float tuneRatio = std::exp2f (p.tune / 12.0f);
|
const float tuneRatio = std::exp2f (p.tune / 12.0f);
|
||||||
|
const float ctuneRatio1 = std::exp2f (p.ctune1 / 12.0f);
|
||||||
|
const float ctuneRatio2 = std::exp2f (p.ctune2 / 12.0f);
|
||||||
const float maxFc = 0.4f * (float) sr;
|
const float maxFc = 0.4f * (float) sr;
|
||||||
const float minFc = 30.0f;
|
const float minFc = 30.0f;
|
||||||
|
|
||||||
|
|
@ -299,10 +301,10 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||||
bendSmooth += (bendCents - bendSmooth) * 0.08f;
|
bendSmooth += (bendCents - bendSmooth) * 0.08f;
|
||||||
freq *= std::exp2f (bendSmooth / 1200.0f);
|
freq *= std::exp2f (bendSmooth / 1200.0f);
|
||||||
|
|
||||||
const float d1 = (float) (freq / sr) * detuneRatio1 * std::exp2f (v.dtSp / 1200.0f);
|
const float d1 = (float) (freq / sr) * detuneRatio1 * ctuneRatio1 * std::exp2f (v.dtSp / 1200.0f);
|
||||||
// soundscape detune sway: osc2 slowly zooms its detune in/out
|
// soundscape detune sway: osc2 slowly zooms its detune in/out
|
||||||
const float swayT = std::exp2f (evoSlow * p.evolve * 16.0f / 1200.0f);
|
const float swayT = std::exp2f (evoSlow * p.evolve * 16.0f / 1200.0f);
|
||||||
const float d2 = (float) (freq / sr) * detuneRatio2 * swayT;
|
const float d2 = (float) (freq / sr) * detuneRatio2 * ctuneRatio2 * swayT;
|
||||||
|
|
||||||
v.ph1 += d1; if (v.ph1 >= 1.0) v.ph1 -= 1.0;
|
v.ph1 += d1; if (v.ph1 >= 1.0) v.ph1 -= 1.0;
|
||||||
v.ph1r += d1;
|
v.ph1r += d1;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ struct VParams
|
||||||
{
|
{
|
||||||
float osc1w = 2, osc2w = 3, detune = 8, oscMix = 52.f;
|
float osc1w = 2, osc2w = 3, detune = 8, oscMix = 52.f;
|
||||||
float detune1 = 0;
|
float detune1 = 0;
|
||||||
|
float ctune1 = 0, ctune2 = 0;
|
||||||
float fltType = 1, fltCut = 3400.f, fltRes = 0.18f, fltEnv = 52.f, fltKey = 40.f;
|
float fltType = 1, fltCut = 3400.f, fltRes = 0.18f, fltEnv = 52.f, fltKey = 40.f;
|
||||||
float ampA = 0.008f, ampD = 0.26f, ampS = 0.70f, ampR = 0.35f;
|
float ampA = 0.008f, ampD = 0.26f, ampS = 0.70f, ampR = 0.35f;
|
||||||
float fA = 0.02f, fD = 0.16f, fS = 0.26f, fR = 0.25f;
|
float fA = 0.02f, fD = 0.16f, fS = 0.26f, fR = 0.25f;
|
||||||
|
|
@ -21,6 +22,7 @@ struct VParams
|
||||||
auto get = [&] (const char* id) { return apvts.getRawParameterValue (id) -> load(); };
|
auto get = [&] (const char* id) { return apvts.getRawParameterValue (id) -> load(); };
|
||||||
osc1w = get ("osc1wave"); osc2w = get ("osc2wave"); detune = get ("detune");
|
osc1w = get ("osc1wave"); osc2w = get ("osc2wave"); detune = get ("detune");
|
||||||
detune1 = get ("detune1");
|
detune1 = get ("detune1");
|
||||||
|
ctune1 = get ("ctune1"); ctune2 = get ("ctune2");
|
||||||
oscMix = get ("oscmix");
|
oscMix = get ("oscmix");
|
||||||
fltType = get ("flttype"); fltCut = get ("fltcut"); fltRes = get ("fltres");
|
fltType = get ("flttype"); fltCut = get ("fltcut"); fltRes = get ("fltres");
|
||||||
fltEnv = get ("fltenv"); fltKey = get ("fltkey");
|
fltEnv = get ("fltenv"); fltKey = get ("fltkey");
|
||||||
|
|
|
||||||
BIN
blueprint.png
Normal file
BIN
blueprint.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
Loading…
Add table
Add a link
Reference in a new issue