Skip to content

Commit

Permalink
Update GlitchSprinkler (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryukau committed Jul 15, 2024
1 parent 1b9faed commit ed05b5a
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 64 deletions.
145 changes: 93 additions & 52 deletions GlitchSprinkler/source/dsp/dspcore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ void Voice::reset()
noteCent = 0;
noteVelocity = 0;

rngArpeggio.seed(pv[ID::seed]->getInt());
unisonIndex = 1;
unisonPan = double(0.5);
unisonPanNext = double(0.5);
unisonCentNext = double(0);

rngArpeggio.seed(pv[ID::seed]->getInt() + unisonIndex);

arpeggioTie = 1;
arpeggioTimer = 0;
Expand All @@ -119,6 +124,7 @@ void DSPCore::reset()
polynomial.updateCoefficients(true);
isPolynomialUpdated = true;

nextSteal = 0;
for (auto &x : voices) x.reset();

startup();
Expand Down Expand Up @@ -157,7 +163,7 @@ std::array<double, 2> Voice::processFrame()

if (++arpeggioLoopCounter >= core.arpeggioLoopLength) {
arpeggioLoopCounter = 0;
rngArpeggio.seed(pv[ID::seed]->getInt());
rngArpeggio.seed(pv[ID::seed]->getInt() + unisonIndex);
}
}
}
Expand Down Expand Up @@ -201,7 +207,7 @@ std::array<double, 2> Voice::processFrame()
}
}

return {sig, sig};
return {(double(1) - unisonPan) * sig, unisonPan * sig};
}

void DSPCore::process(const size_t length, float *out0, float *out1)
Expand Down Expand Up @@ -246,44 +252,61 @@ void DSPCore::process(const size_t length, float *out0, float *out1)

void DSPCore::noteOn(NoteInfo &info)
{
using ID = ParameterID::ID;
auto &pv = param.value;

if (info.channel == pitchModifierChannel) {
activeModifier.push_back(info);
return;
}

activeNote.push_back(info);

size_t replaceIndex = 0;
const size_t nUnison = 1 + pv[ID::unisonVoice]->getInt();
noteIndices.resize(nUnison);
if (isPolyphonic) {
double mostQuiet = std::numeric_limits<double>::max();
for (size_t idx = 0; idx < voices.size(); ++idx) {
if (voices[idx].state == Voice::State::rest) {
replaceIndex = idx;
break;
}
if (mostQuiet > voices[idx].lastGain) {
mostQuiet = voices[idx].lastGain;
replaceIndex = idx;
}
for (auto &idx : noteIndices) {
idx = nextSteal;
if (++nextSteal >= voices.size()) nextSteal = 0;
}
voices[replaceIndex].noteId = info.id;
voices[replaceIndex].noteNumber = info.noteNumber;
voices[replaceIndex].noteCent = info.cent;
voices[replaceIndex].noteVelocity = velocityMap.map(info.velocity);
voices[replaceIndex].state = Voice::State::active;
} else {
voices[replaceIndex].noteId = -1;
voices[replaceIndex].state = Voice::State::active;
std::iota(noteIndices.begin(), noteIndices.end(), size_t(0));
}

auto &newVoice = voices[replaceIndex];
if (newVoice.state == Voice::State::rest && newVoice.phaseCounter == 0) {
newVoice.arpeggioTimer = 0;
newVoice.arpeggioLoopCounter = 0;
const auto unisonDetuneCent = pv[ID::unisonDetuneCent]->getDouble();
const auto unisonPanSpread = pv[ID::unisonPanSpread]->getDouble();
const auto unisonPanOffset = (double(1) - unisonPanSpread) / double(2);
for (unsigned idx = 0; idx < noteIndices.size(); ++idx) {
auto &vc = voices[noteIndices[idx]];

if (isPolyphonic) {
vc.noteId = info.id;
vc.noteNumber = info.noteNumber;
vc.noteCent = info.cent;
vc.noteVelocity = velocityMap.map(info.velocity);
} else {
vc.noteId = -1;
}
vc.unisonGain = double(1) / double(nUnison);
vc.state = Voice::State::active;
vc.unisonIndex = idx + 1;
vc.rngArpeggio.seed(seed + vc.unisonIndex);

if (nUnison <= 1) {
vc.unisonPanNext = double(0.5);
vc.unisonCentNext = double(0);
} else {
const double ratio = double(idx) / double(nUnison - 1);
vc.unisonPanNext = ratio * unisonPanSpread + unisonPanOffset;
vc.unisonCentNext = ratio * unisonDetuneCent;
}

newVoice.updateNote();
} else if (!param.value[ParameterID::arpeggioSwitch]->getInt()) {
newVoice.scheduleUpdateNote = true;
if (vc.state == Voice::State::rest && vc.phaseCounter == 0) {
vc.arpeggioTimer = 0;
vc.arpeggioLoopCounter = 0;
vc.updateNote();
} else if (!param.value[ParameterID::arpeggioSwitch]->getInt()) {
vc.scheduleUpdateNote = true;
}
}
}

Expand Down Expand Up @@ -344,15 +367,40 @@ void Voice::updateNote()
noteCent = note.cent;
}

unisonPan = unisonPanNext;

// Pitch & phase.
const auto tuning = static_cast<Tuning>(pv[ID::tuning]->getInt());
auto transposeOctave = int(pv[ID::transposeOctave]->getInt()) - transposeOctaveOffset;
auto transposeSemitone
= int(pv[ID::transposeSemitone]->getInt()) - transposeSemitoneOffset;
auto transposeCent = pv[ID::transposeCent]->getDouble();
auto transposeRatio = getPitchRatio(
transposeSemitone + noteNumber - 69, transposeOctave, transposeCent + noteCent,
static_cast<Tuning>(pv[ID::tuning]->getInt()));
auto freqHz = core.pitchModifier * transposeRatio * double(440);

auto getNaturalFreq = [&]() {
auto transposeRatio = getPitchRatio(
transposeSemitone + noteNumber - 69, transposeOctave,
transposeCent + noteCent + unisonCentNext, tuning);
return core.pitchModifier * transposeRatio * double(440);
};
auto getDiscreteFreq = [&]() {
auto semitones = transposeSemitone + double(noteNumber - 127) / double(12);
auto cents = (transposeCent + noteCent) / double(1200);
auto transposeRatio = std::exp2(transposeOctave + semitones + cents);
auto freqHz = core.pitchModifier * transposeRatio * core.sampleRate;
switch (tuning) {
default:
case Tuning::discrete2:
return freqHz / double(2);
case Tuning::discrete3:
return freqHz / double(3);
case Tuning::discrete5:
return freqHz / double(5);
case Tuning::discrete7:
return freqHz / double(7);
}
};

auto freqHz = tuning >= Tuning::discrete2 ? getDiscreteFreq() : getNaturalFreq();

if (useArpeggiator) {
auto pitchDriftCent = pv[ID::arpeggioPicthDriftCent]->getDouble();
Expand All @@ -370,9 +418,9 @@ void Voice::updateNote()
} else if (arpeggioScale == PitchScale::et5Chromatic) {
arpRatio *= getRandomPitch(rngArpeggio, scaleEt5, tuningRatioEt5);
} else if (arpeggioScale == PitchScale::et12Major) {
arpRatio *= getRandomPitch(rngArpeggio, scaleEt12Major, tuningRatioEt12);
arpRatio *= getRandomPitch(rngArpeggio, scaleEt12ChurchC, tuningRatioEt12);
} else if (arpeggioScale == PitchScale::et12Minor) {
arpRatio *= getRandomPitch(rngArpeggio, scaleEt12Minor, tuningRatioEt12);
arpRatio *= getRandomPitch(rngArpeggio, scaleEt12ChurchA, tuningRatioEt12);
} else if (arpeggioScale == PitchScale::overtone32) {
std::uniform_int_distribution<int> dist{1, 32};
arpRatio *= double(dist(rngArpeggio));
Expand All @@ -386,7 +434,7 @@ void Voice::updateNote()
}

freqHz = std::min(double(0.5) * core.sampleRate, freqHz);
phasePeriod = uint_fast32_t(core.sampleRate / freqHz);
phasePeriod = uint_fast32_t(std::ceil(core.sampleRate / freqHz));
phaseCounter = 0;

// Oscillator.
Expand All @@ -411,7 +459,7 @@ void Voice::updateNote()
std::uniform_real_distribution<double> restDist{double(0), double(1)};
decayGain = useArpeggiator && restDist(rngArpeggio) < restChance || freqHz == 0
? double(0)
: double(1) / decayRatio;
: unisonGain / decayRatio;
lastGain = decayGain;
}

Expand All @@ -438,27 +486,20 @@ void DSPCore::noteOff(int_fast32_t noteId)
if (itNote == activeNote.end()) return;
activeNote.erase(itNote);

if (isPolyphonic) {
auto itVoice = std::find_if(voices.begin(), voices.end(), [&](const Voice &vc) {
return vc.noteId == noteId;
});
if (itVoice != voices.end()) {
auto &voice = *itVoice;
voice.noteId = -1;
voice.state = noteOffState;
voice.resetArpeggio(pv[ID::seed]->getInt());
}
} else if (activeNote.empty()) {
voices[0].noteId = -1;
voices[0].state = noteOffState;
voices[0].resetArpeggio(pv[ID::seed]->getInt());
if (!isPolyphonic && !activeNote.empty()) return;
const auto targetId = isPolyphonic ? noteId : -1;
for (auto &vc : voices) {
if (vc.noteId != targetId) continue;
vc.noteId = -1;
vc.state = noteOffState;
vc.resetArpeggio(pv[ID::seed]->getInt() + vc.unisonIndex);
}
}
}

void Voice::resetArpeggio(unsigned seed)
{
rngArpeggio.seed(seed);
rngArpeggio.seed(seed + unisonIndex);
arpeggioTie = 0;
arpeggioTimer = 0;
arpeggioLoopCounter = 0;
Expand Down
13 changes: 13 additions & 0 deletions GlitchSprinkler/source/dsp/dspcore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Voice {
int32_t noteNumber = 0;
double noteCent = 0;
double noteVelocity = 0;
double notePan = 0.5;

std::minstd_rand rngArpeggio{0};

Expand All @@ -67,6 +68,12 @@ class Voice {
uint_fast32_t phasePeriod = 0;
uint_fast32_t phaseCounter = 0;

unsigned unisonIndex = 1;
double unisonGain = double(1);
double unisonPan = double(0.5);
double unisonPanNext = double(0.5);
double unisonCentNext = double(0);

double oscSync = double(1);
double fmIndex = double(0);
double saturationGain = double(1);
Expand Down Expand Up @@ -115,6 +122,9 @@ class DSPCore {
std::vector<NoteInfo> midiNotes;
std::vector<NoteInfo> activeNote;
std::vector<NoteInfo> activeModifier;
std::vector<size_t> noteIndices;

unsigned nextSteal = 0;
std::vector<Voice> voices;

DSPCore()
Expand All @@ -123,6 +133,9 @@ class DSPCore {
activeNote.reserve(2048);
activeModifier.reserve(2048);

noteIndices.reserve(2048);
std::fill(noteIndices.begin(), noteIndices.end(), size_t(0));

voices.reserve(256);
for (size_t i = 0; i < 256; ++i) voices.emplace_back(*this);
}
Expand Down
20 changes: 18 additions & 2 deletions GlitchSprinkler/source/dsp/tuning.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,24 @@ std::array<double, 12> tuningRatioEt12{
double(1.4142135623730951), double(1.4983070768766815), double(1.5874010519681994),
double(1.681792830507429), double(1.7817974362806785), double(1.887748625363387),
};
std::array<size_t, 7> scaleEt12Major{0, 2, 4, 5, 7, 9, 11};
std::array<size_t, 7> scaleEt12Minor{0, 2, 3, 5, 7, 8, 10};
std::array<size_t, 7> scaleEt12ChurchC{0, 2, 4, 5, 7, 9, 11}; // Ionic, Major
std::array<size_t, 7> scaleEt12ChurchD{0, 2, 3, 5, 7, 9, 10}; // Dorian
std::array<size_t, 7> scaleEt12ChurchE{0, 1, 3, 5, 7, 8, 10}; // Frygian
std::array<size_t, 7> scaleEt12ChurchF{0, 2, 4, 6, 7, 9, 11}; // Lydian
std::array<size_t, 7> scaleEt12ChurchG{0, 2, 4, 5, 7, 9, 10}; // Mixolydian
std::array<size_t, 7> scaleEt12ChurchA{0, 2, 3, 5, 7, 8, 10}; // Aeolian, Minor
std::array<size_t, 7> scaleEt12ChurchB{0, 1, 3, 5, 6, 8, 10}; // Locrian
std::array<size_t, 3> scaleEt12Sus2{0, 2, 7};
std::array<size_t, 3> scaleEt12Sus4{0, 5, 7};
std::array<size_t, 6> scaleEt12Chord2_4_7_9_11{0, 2, 4, 7, 9, 11};
std::array<size_t, 4> scaleEt12Maj7{0, 4, 7, 11};
std::array<size_t, 4> scaleEt12Min7{0, 3, 7, 10};
std::array<size_t, 8> scaleEt12MajHillege{0, 4, 7, 11, 14, 18, 21, 25};
std::array<size_t, 8> scaleEt12MinHillege{0, 3, 7, 10, 14, 17, 21, 24};
std::array<size_t, 6> scaleEt12WholeTone2{0, 2, 4, 6, 8, 10};
std::array<size_t, 4> scaleEt12WholeTone3{0, 3, 6, 9};
std::array<size_t, 3> scaleEt12WholeTone4{0, 4, 8};
std::array<size_t, 6> scaleEt12Blues{0, 3, 5, 6, 7, 10};

/**
```javascript
Expand Down
29 changes: 26 additions & 3 deletions GlitchSprinkler/source/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ bool Editor::prepareUI()
{
"ET 12", "ET 5",
"Just 5 Major", "Just 5 Minor",
"Just 7", "- Reserved 05 -",
"- Reserved 06 -", "- Reserved 07 -",
"- Reserved 08 -", "- Reserved 09 -",
"Just 7", "Discrete 2",
"Discrete 3", "Discrete 5",
"Discrete 7", "- Reserved 09 -",
"- Reserved 10 -", "- Reserved 11 -",
"- Reserved 12 -", "- Reserved 13 -",
"- Reserved 14 -", "- Reserved 15 -",
Expand Down Expand Up @@ -290,6 +290,29 @@ bool Editor::prepareUI()
arpLeft1, arpTop12, labelWidth, labelHeight, uiTextSize, ID::randomizeFmIndex,
Scales::randomizeFmIndex, false, 5);

// Unison.
constexpr auto unisonTop0 = arpTop12 + labelY;
constexpr auto unisonTop1 = unisonTop0 + 1 * labelY;
constexpr auto unisonTop2 = unisonTop0 + 2 * labelY;
constexpr auto unisonTop3 = unisonTop0 + 3 * labelY;
constexpr auto unisonLeft0 = left8;
constexpr auto unisonLeft1 = unisonLeft0 + labelWidth + 2 * margin;
addGroupLabel(
unisonLeft0, unisonTop0, groupLabelWidth, labelHeight, uiTextSize, "Unison");

addLabel(unisonLeft0, unisonTop1, labelWidth, labelHeight, uiTextSize, "nVoice");
addTextKnob(
unisonLeft1, unisonTop1, labelWidth, labelHeight, uiTextSize, ID::unisonVoice,
Scales::unisonVoice, false, 0, 1);
addLabel(unisonLeft0, unisonTop2, labelWidth, labelHeight, uiTextSize, "Detune [cent]");
addTextKnob(
unisonLeft1, unisonTop2, labelWidth, labelHeight, uiTextSize, ID::unisonDetuneCent,
Scales::unisonDetuneCent, false, 5);
addLabel(unisonLeft0, unisonTop3, labelWidth, labelHeight, uiTextSize, "Pan Spread");
addTextKnob(
unisonLeft1, unisonTop3, labelWidth, labelHeight, uiTextSize, ID::unisonPanSpread,
Scales::defaultScale, false, 5);

// Randomize button.
const auto randomButtonTop = top0 + 18 * labelY;
const auto randomButtonLeft = left0 + labelWidth + 2 * margin;
Expand Down
3 changes: 3 additions & 0 deletions GlitchSprinkler/source/parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,8 @@ UIntScale<double> Scales::arpeggioScale(31);
LinearScale<double> Scales::arpeggioPicthDriftCent(0, 100);
UIntScale<double> Scales::arpeggioOctave(7);

UIntScale<double> Scales::unisonVoice(127);
LinearScale<double> Scales::unisonDetuneCent(0, 1200);

} // namespace Synth
} // namespace Steinberg
Loading

0 comments on commit ed05b5a

Please sign in to comment.