mirror of
https://codeberg.org/armin/monostep.git
synced 2026-09-01 04:10:46 +02:00
Prototype
This commit is contained in:
parent
c117906a7f
commit
4271fc34bb
14 changed files with 950 additions and 169 deletions
|
|
@ -17,6 +17,7 @@ struct Step
|
|||
int semitone = 0; // -12 .. +12
|
||||
float cents = 0.0f; // -50 .. +50
|
||||
bool slide = false;
|
||||
bool accent = false;
|
||||
};
|
||||
|
||||
class StepSequencer
|
||||
|
|
@ -31,6 +32,20 @@ public:
|
|||
s = Step{};
|
||||
}
|
||||
|
||||
void shift (int dir)
|
||||
{
|
||||
if (dir == 0)
|
||||
return;
|
||||
|
||||
std::array<Step, numSteps> rotated = steps;
|
||||
|
||||
for (int i = 0; i < numSteps; ++i)
|
||||
{
|
||||
const int from = (i - dir + numSteps) % numSteps;
|
||||
steps[i] = rotated[from];
|
||||
}
|
||||
}
|
||||
|
||||
int stepsPerBeatFromRate (int rateIndex) const
|
||||
{
|
||||
// 0: 1/4, 1: 1/8, 2: 1/8T, 3: 1/16, 4: 1/32
|
||||
|
|
|
|||
|
|
@ -13,8 +13,12 @@ public:
|
|||
{
|
||||
Waveform waveA = Waveform::saw;
|
||||
Waveform waveB = Waveform::square;
|
||||
float detuneRatio = 1.0f;
|
||||
float mix = 0.5f;
|
||||
float osc1Coarse = 0.0f;
|
||||
float osc2Coarse = 0.0f;
|
||||
float osc1Fine = 0.0f;
|
||||
float osc2Fine = 0.0f;
|
||||
float osc1Phase = 0.0f;
|
||||
float osc2Phase = 0.0f;
|
||||
float cutoff = 7000.0f;
|
||||
float resonance = 0.15f;
|
||||
float attack = 0.005f;
|
||||
|
|
@ -23,11 +27,22 @@ public:
|
|||
float release = 0.4f;
|
||||
float glide = 0.12f;
|
||||
float master = 0.9f;
|
||||
float drive = 0.0f;
|
||||
float ringMod = 0.0f;
|
||||
float detuneRatio = 1.0f;
|
||||
float mix = 0.5f;
|
||||
};
|
||||
|
||||
void addPhase (float phaseOffset)
|
||||
{
|
||||
phaseA = frac (phaseA + phaseOffset);
|
||||
phaseB = frac (phaseB + phaseOffset);
|
||||
}
|
||||
|
||||
void prepare (double sr)
|
||||
{
|
||||
sampleRate = (float) sr;
|
||||
accentSmoothCoef = 1.0f - std::exp (-1.0f / (0.003f * sampleRate));
|
||||
reset();
|
||||
}
|
||||
|
||||
|
|
@ -40,12 +55,16 @@ public:
|
|||
env = 0.0f;
|
||||
envStage = Stage::idle;
|
||||
gate = false;
|
||||
accentLevel = 1.0f;
|
||||
accentSmooth = 1.0f;
|
||||
}
|
||||
|
||||
void setParams (const Params& p) { params = p; }
|
||||
|
||||
bool isActive() const { return gate; }
|
||||
|
||||
void setAccentLevel (float level) { accentLevel = level; }
|
||||
|
||||
void noteOn (float freqHz, bool legato)
|
||||
{
|
||||
targetFreq = freqHz;
|
||||
|
|
@ -153,6 +172,9 @@ private:
|
|||
updateEnvelope();
|
||||
updateGlide();
|
||||
|
||||
// Smooth the accent gain so it ramps in/out instead of clicking.
|
||||
accentSmooth += (accentLevel - accentSmooth) * accentSmoothCoef;
|
||||
|
||||
const float incA = currentFreq / sampleRate;
|
||||
const float incB = (currentFreq * params.detuneRatio) / sampleRate;
|
||||
|
||||
|
|
@ -164,9 +186,24 @@ private:
|
|||
|
||||
float out = (1.0f - params.mix) * a + params.mix * b;
|
||||
|
||||
if (params.ringMod > 0.0f)
|
||||
out = (1.0f - params.ringMod) * out + params.ringMod * (a * b);
|
||||
|
||||
const float preFilter = out;
|
||||
out = processFilter (out);
|
||||
|
||||
return out * env * params.master;
|
||||
// Accented steps also get a touch of the unfiltered signal so they cut
|
||||
// through the mix (presence), not just a volume bump.
|
||||
if (accentSmooth > 1.0f)
|
||||
out += (accentSmooth - 1.0f) * 0.2f * preFilter;
|
||||
|
||||
if (params.drive > 0.0f)
|
||||
{
|
||||
const float wet = std::tanh (out * (1.0f + params.drive * 9.0f));
|
||||
out = (1.0f - params.drive) * out + params.drive * wet;
|
||||
}
|
||||
|
||||
return out * env * params.master * accentSmooth;
|
||||
}
|
||||
|
||||
float sampleRate = 44100.0f;
|
||||
|
|
@ -184,6 +221,9 @@ private:
|
|||
float env = 0.0f;
|
||||
Stage envStage = Stage::idle;
|
||||
bool gate = false;
|
||||
float accentLevel = 1.0f;
|
||||
float accentSmooth = 1.0f;
|
||||
float accentSmoothCoef = 0.01f;
|
||||
};
|
||||
|
||||
} // namespace monostep
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
namespace monostep
|
||||
{
|
||||
|
|
@ -11,7 +12,11 @@ enum class Waveform : int
|
|||
sine = 0,
|
||||
triangle = 1,
|
||||
saw = 2,
|
||||
square = 3
|
||||
square = 3,
|
||||
pulse = 4,
|
||||
noise = 5,
|
||||
sub = 6,
|
||||
pluck = 7
|
||||
};
|
||||
|
||||
inline float frac (float x)
|
||||
|
|
@ -63,6 +68,31 @@ inline float renderWave (Waveform w, float phase, float inc)
|
|||
v -= polyBlep (frac (f + 0.5f), inc);
|
||||
return v;
|
||||
}
|
||||
|
||||
case Waveform::pulse:
|
||||
{
|
||||
const float f = frac (phase);
|
||||
return (f < 0.3f) ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
case Waveform::sub:
|
||||
{
|
||||
const float f = frac (phase * 0.25f);
|
||||
return 4.0f * std::min (f, 1.0f - f) - 1.0f;
|
||||
}
|
||||
|
||||
case Waveform::noise:
|
||||
{
|
||||
thread_local static std::mt19937 rng { std::random_device {}() };
|
||||
thread_local static std::uniform_real_distribution<float> dist (-1.0f, 1.0f);
|
||||
return dist (rng);
|
||||
}
|
||||
|
||||
case Waveform::pluck:
|
||||
{
|
||||
const float f = frac (phase);
|
||||
return 2.0f * std::sin (f * 6.283185307179586f * 2.0f) * std::exp (-10.0f * f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue