mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
init
This commit is contained in:
commit
dca008e860
21 changed files with 3746 additions and 0 deletions
231
Source/DSP/Voice.h
Normal file
231
Source/DSP/Voice.h
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#pragma once
|
||||
#include "Oscillator.h"
|
||||
#include "Filter.h"
|
||||
#include "Envelope.h"
|
||||
#include "LFO.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
struct SubtractiveSound : public juce::SynthesiserSound {
|
||||
bool appliesToNote(int /*midiNoteNumber*/) override { return true; }
|
||||
bool appliesToChannel(int /*midiChannel*/) override { return true; }
|
||||
};
|
||||
|
||||
class SubtractiveVoice : public juce::SynthesiserVoice {
|
||||
public:
|
||||
SubtractiveVoice() = default;
|
||||
|
||||
bool canPlaySound(juce::SynthesiserSound* sound) override {
|
||||
return dynamic_cast<SubtractiveSound*>(sound) != nullptr;
|
||||
}
|
||||
|
||||
void startNote(int midiNoteNumber, float velocity,
|
||||
juce::SynthesiserSound* sound, int currentPitchWheelPosition) override {
|
||||
currentMidiNote = midiNoteNumber;
|
||||
noteVelocity = velocity;
|
||||
|
||||
double sr = getSampleRate();
|
||||
if (sr <= 0.0) sr = 44100.0;
|
||||
osc1.prepare(sr);
|
||||
osc2.prepare(sr);
|
||||
env.prepare(sr);
|
||||
filterEnv.prepare(sr);
|
||||
filter.prepare(sr);
|
||||
filterR.prepare(sr);
|
||||
lfo1.prepare(sr);
|
||||
lfo2.prepare(sr);
|
||||
lfo1.reset();
|
||||
lfo2.reset();
|
||||
|
||||
baseFreq1 = 440.0f * std::pow(2.0f, (static_cast<float>(midiNoteNumber) - 69.0f) / 12.0f)
|
||||
* std::pow(2.0f, semitone1 / 12.0f + fineTune1 / 1200.0f) * std::pow(2.0f, octave1);
|
||||
baseFreq2 = 440.0f * std::pow(2.0f, (static_cast<float>(midiNoteNumber) - 69.0f) / 12.0f)
|
||||
* std::pow(2.0f, semitone2 / 12.0f + fineTune2 / 1200.0f) * std::pow(2.0f, octave2);
|
||||
|
||||
osc1.setFrequency(baseFreq1);
|
||||
osc2.setFrequency(baseFreq2);
|
||||
osc1.setWaveform(waveform1);
|
||||
osc2.setWaveform(waveform2);
|
||||
osc1.setPan(pan);
|
||||
osc2.setPan(pan);
|
||||
|
||||
osc1.setPhase(0.0f);
|
||||
osc2.setPhase(phaseOffset2);
|
||||
|
||||
filter.setCoefficients(filterCutoff, filterResonance, filterType);
|
||||
filterR.setCoefficients(filterCutoff, filterResonance, filterType);
|
||||
|
||||
env.noteOn();
|
||||
filterEnv.noteOn();
|
||||
}
|
||||
|
||||
void stopNote(float velocity, bool allowTailOff) override {
|
||||
env.noteOff();
|
||||
filterEnv.noteOff();
|
||||
|
||||
if (!allowTailOff || !env.isActive())
|
||||
clearCurrentNote();
|
||||
}
|
||||
|
||||
void renderNextBlock(juce::AudioBuffer<float>& outputBuffer,
|
||||
int startSample, int numSamples) override {
|
||||
if (!isVoiceActive()) return;
|
||||
|
||||
for (int i = startSample; i < startSample + numSamples; ++i) {
|
||||
float envSample = env.getNextSample();
|
||||
float fEnvSample = filterEnv.getNextSample();
|
||||
|
||||
if (!env.isActive()) {
|
||||
clearCurrentNote();
|
||||
return;
|
||||
}
|
||||
|
||||
// LFO outputs
|
||||
float lfo1Out = lfo1.getNextSample();
|
||||
float lfo2Out = lfo2.getNextSample();
|
||||
|
||||
// Apply pitch bend + LFO to oscillator frequencies
|
||||
float pitchBendMult = std::pow(2.0f, pitchBendSemitones / 12.0f);
|
||||
float freq1 = baseFreq1 * pitchBendMult;
|
||||
float freq2 = baseFreq2 * pitchBendMult;
|
||||
|
||||
auto applyPitchLfo = [&](float lfoOut, LFODest dest) {
|
||||
float semitoneShift = lfoOut * 12.0f; // +/- 12 semitones range
|
||||
float pitchMult = std::pow(2.0f, semitoneShift / 12.0f);
|
||||
if (dest == LFODest::OSC1Freq || dest == LFODest::BothOsc)
|
||||
freq1 *= pitchMult;
|
||||
if (dest == LFODest::OSC2Freq || dest == LFODest::BothOsc)
|
||||
freq2 *= pitchMult;
|
||||
};
|
||||
|
||||
if (lfo1Depth > 0.001f)
|
||||
applyPitchLfo(lfo1Out, lfo1Dest);
|
||||
if (lfo2Depth > 0.001f)
|
||||
applyPitchLfo(lfo2Out, lfo2Dest);
|
||||
|
||||
osc1.setFrequency(freq1);
|
||||
osc2.setFrequency(freq2);
|
||||
|
||||
float left = 0.0f, right = 0.0f;
|
||||
osc1.process(&left, &right);
|
||||
osc2.process(&left, &right);
|
||||
|
||||
left *= noteVelocity * envSample;
|
||||
right *= noteVelocity * envSample;
|
||||
|
||||
// Filter cutoff with env + keytrack
|
||||
float modCutoff = filterCutoff * std::pow(2.0f, fEnvSample * filterEnvAmount * 4.0f);
|
||||
float keyTrackFactor = std::pow(2.0f, (static_cast<float>(currentMidiNote) - 60.0f) / 12.0f * keyTrack);
|
||||
modCutoff *= keyTrackFactor;
|
||||
|
||||
// Apply LFO to filter cutoff
|
||||
if (lfo1Depth > 0.001f && lfo1Dest == LFODest::FilterCutoff)
|
||||
modCutoff *= std::pow(2.0f, lfo1Out * 4.0f);
|
||||
if (lfo2Depth > 0.001f && lfo2Dest == LFODest::FilterCutoff)
|
||||
modCutoff *= std::pow(2.0f, lfo2Out * 4.0f);
|
||||
|
||||
modCutoff = std::clamp(modCutoff, 25.0f, 1200.0f);
|
||||
|
||||
filter.setCoefficients(modCutoff, filterResonance, filterType);
|
||||
filterR.setCoefficients(modCutoff, filterResonance, filterType);
|
||||
|
||||
left = filter.process(left);
|
||||
right = filterR.process(right);
|
||||
|
||||
left = std::tanh(left * drive) / std::tanh(drive);
|
||||
right = std::tanh(right * drive) / std::tanh(drive);
|
||||
|
||||
outputBuffer.addSample(0, i, left * outputLevel);
|
||||
outputBuffer.addSample(1, i, right * outputLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void pitchWheelMoved(int newPitchWheelValue) override {}
|
||||
void controllerMoved(int controllerNumber, int newControllerValue) override {}
|
||||
|
||||
void setParameters(Waveform wf1, Waveform wf2,
|
||||
float oct1, float oct2,
|
||||
float semi1, float semi2,
|
||||
float fine1, float fine2,
|
||||
float lvl1, float lvl2,
|
||||
float phOff2,
|
||||
float cut, float res, FilterType ft,
|
||||
float fEnvAmt, float kTrack,
|
||||
float att, float dec, float sus, float rel,
|
||||
float fAtt, float fDec, float fSus, float fRel,
|
||||
float p, float drv, float outLvl,
|
||||
float l1Rate, float l1Depth, int l1Shape, int l1Dest,
|
||||
float l2Rate, float l2Depth, int l2Shape, int l2Dest) {
|
||||
waveform1 = wf1;
|
||||
waveform2 = wf2;
|
||||
octave1 = oct1;
|
||||
octave2 = oct2;
|
||||
semitone1 = semi1;
|
||||
semitone2 = semi2;
|
||||
fineTune1 = fine1;
|
||||
fineTune2 = fine2;
|
||||
osc1.setLevel(lvl1);
|
||||
osc2.setLevel(lvl2);
|
||||
phaseOffset2 = phOff2;
|
||||
filterCutoff = cut;
|
||||
filterResonance = res;
|
||||
filterType = ft;
|
||||
filterEnvAmount = fEnvAmt;
|
||||
keyTrack = kTrack;
|
||||
pan = p;
|
||||
drive = std::max(drv, 1.001f);
|
||||
outputLevel = outLvl;
|
||||
|
||||
lfo1Depth = l1Depth;
|
||||
lfo1Dest = static_cast<LFODest>(l1Dest);
|
||||
lfo1.setParameters(l1Rate, l1Depth, static_cast<LFOShape>(l1Shape), lfo1Dest);
|
||||
|
||||
lfo2Depth = l2Depth;
|
||||
lfo2Dest = static_cast<LFODest>(l2Dest);
|
||||
lfo2.setParameters(l2Rate, l2Depth, static_cast<LFOShape>(l2Shape), lfo2Dest);
|
||||
|
||||
env.setAttack(att);
|
||||
env.setDecay(dec);
|
||||
env.setSustain(sus);
|
||||
env.setRelease(rel);
|
||||
|
||||
filterEnv.setAttack(fAtt);
|
||||
filterEnv.setDecay(fDec);
|
||||
filterEnv.setSustain(fSus);
|
||||
filterEnv.setRelease(fRel);
|
||||
}
|
||||
|
||||
void setPitchBend(float semitones) { pitchBendSemitones = semitones; }
|
||||
|
||||
private:
|
||||
Oscillator osc1, osc2;
|
||||
Filter filter;
|
||||
Filter filterR;
|
||||
ADSREnvelope env;
|
||||
ADSREnvelope filterEnv;
|
||||
LFO lfo1, lfo2;
|
||||
|
||||
Waveform waveform1 = Waveform::Saw;
|
||||
Waveform waveform2 = Waveform::Square;
|
||||
float octave1 = 0.0f, octave2 = -1.0f;
|
||||
float semitone1 = 0.0f, semitone2 = 0.0f;
|
||||
float fineTune1 = 0.0f, fineTune2 = 7.0f;
|
||||
float phaseOffset2 = 0.5f;
|
||||
float filterCutoff = 800.0f;
|
||||
float filterResonance = 0.7f;
|
||||
FilterType filterType = FilterType::LowPass12;
|
||||
float filterEnvAmount = 0.0f;
|
||||
float keyTrack = 0.5f;
|
||||
float pan = 0.0f;
|
||||
float drive = 1.5f;
|
||||
float outputLevel = 0.8f;
|
||||
float baseFreq1 = 440.0f;
|
||||
float baseFreq2 = 440.0f;
|
||||
float lfo1Depth = 0.0f;
|
||||
float lfo2Depth = 0.0f;
|
||||
LFODest lfo1Dest = LFODest::FilterCutoff;
|
||||
LFODest lfo2Dest = LFODest::FilterCutoff;
|
||||
float pitchBendSemitones = 0.0f;
|
||||
int currentMidiNote = 60;
|
||||
float noteVelocity = 1.0f;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue