Add JP-8000 like "supersaw" waveform

This commit is contained in:
Armin 2026-08-12 20:13:57 +02:00
commit fd1625a4b5
4 changed files with 140 additions and 5 deletions

View file

@ -19,6 +19,7 @@ enum class Waveform {
Sinc,
ExpPulse,
DoubleSine,
SuperSaw,
NumWaveforms
};
@ -28,6 +29,8 @@ public:
this->sampleRate = sampleRate;
phase = 0.0;
phaseIncrement = 0.0;
for (int i = 0; i < numSuperSaws; ++i)
superSawPhase[i] = i * (1.0f / static_cast<float>(numSuperSaws));
}
void setFrequency(float freq) {
@ -106,6 +109,20 @@ public:
output = 0.5f * std::sin(phase * 2.0f * pi)
+ 0.5f * std::sin(phase * 4.0f * pi);
break;
case Waveform::SuperSaw: {
static constexpr int numSaws = 7;
static constexpr float detunes[numSaws] = {
-12.0f, -8.0f, -4.0f, 0.0f, 4.0f, 8.0f, 12.0f
};
output = 0.0f;
for (int i = 0; i < numSaws; ++i) {
superSawPhase[i] += phaseIncrement * std::pow(2.0f, detunes[i] / 1200.0f);
if (superSawPhase[i] >= 1.0f) superSawPhase[i] -= 1.0f;
output += 2.0f * (superSawPhase[i] - 0.5f);
}
output /= static_cast<float>(numSaws);
break;
}
default:
output = 0.0f;
break;
@ -126,10 +143,12 @@ public:
float getCurrentPhase() const { return phase; }
private:
static constexpr int numSuperSaws = 7;
static constexpr float pi = 3.14159265358979323846f;
float frequency = 440.0f;
float phase = 0.0f;
float phaseIncrement = 0.0f;
float superSawPhase[numSuperSaws] = { 0.0f };
float level = 0.7f;
float pan = 0.0f;
float semitoneOffset = 0.0f;

View file

@ -494,7 +494,7 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
setupLabel(osc1Label, "OSC 1");
setupCB(osc1WaveBox, osc1WaveAttach, "osc1Wave", {"Sine", "Saw", "Square", "Triangle", "Noise",
"Pulse 25%", "Pulse 12%", "Rev Saw", "Full Rect", "Half Rect",
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine"});
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine", "SuperSaw"});
setupParam(osc1OctKnob, osc1OctAttach, "osc1Oct", "OCT");
setupParam(osc1SemiKnob, osc1SemiAttach, "osc1Semi", "SEMI");
setupParam(osc1FineKnob, osc1FineAttach, "osc1Fine", "FINE");
@ -503,7 +503,7 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
setupLabel(osc2Label, "OSC 2");
setupCB(osc2WaveBox, osc2WaveAttach, "osc2Wave", {"Sine", "Saw", "Square", "Triangle", "Noise",
"Pulse 25%", "Pulse 12%", "Rev Saw", "Full Rect", "Half Rect",
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine"});
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine", "SuperSaw"});
setupParam(osc2OctKnob, osc2OctAttach, "osc2Oct", "OCT");
setupParam(osc2SemiKnob, osc2SemiAttach, "osc2Semi", "SEMI");
setupParam(osc2FineKnob, osc2FineAttach, "osc2Fine", "FINE");

View file

@ -55,7 +55,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
juce::ParameterID{"osc1Wave", 1}, "OSC1 Wave",
juce::StringArray{"Sine", "Saw", "Square", "Triangle", "Noise",
"Pulse 25%", "Pulse 12%", "Rev Saw", "Full Rect", "Half Rect",
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine"}, 1));
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine", "SuperSaw"}, 1));
layout.add(std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"osc1Oct", 1}, "OSC1 Octave",
juce::NormalisableRange<float>(-3.0f, 3.0f, 1.0f), 0.0f));
@ -73,7 +73,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
juce::ParameterID{"osc2Wave", 1}, "OSC2 Wave",
juce::StringArray{"Sine", "Saw", "Square", "Triangle", "Noise",
"Pulse 25%", "Pulse 12%", "Rev Saw", "Full Rect", "Half Rect",
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine"}, 3));
"Stair 4", "Stair 8", "Soft Sine", "Sinc", "Exp Pulse", "Double Sine", "SuperSaw"}, 3));
layout.add(std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"osc2Oct", 1}, "OSC2 Octave",
juce::NormalisableRange<float>(-3.0f, 3.0f, 1.0f), -1.0f));

View file

@ -190,7 +190,7 @@ private:
// --- PAD ---
add("Warm Pad", "Pad", {
{"osc1Wave", 1}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.5f},
{"osc1Wave", 16}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.5f},
{"osc2Wave", 3}, {"osc2Oct", 0}, {"osc2Semi", 0}, {"osc2Fine", 12}, {"osc2Level", 0.5f},
{"phaseOffset", 0.5f},
{"filterType", 0}, {"filterCutoff", 400}, {"filterRes", 0.2f}, {"filterEnvAmt", 0.1f}, {"keyTrack", 0.5f},
@ -1577,5 +1577,121 @@ private:
add("Techno Stab", "Techno", {{{"osc1Wave", 1}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.7f}, {"osc2Wave", 2}, {"osc2Oct", 0}, {"osc2Semi", 7}, {"osc2Fine", 5}, {"osc2Level", 0.5f}, {"phaseOffset", 0.5f}, {"filterType", 0}, {"filterCutoff", 1200}, {"filterRes", 0.4f}, {"filterEnvAmt", 0.7f}, {"keyTrack", 0.6f}, {"fEnvAttack", 0.001f}, {"fEnvDecay", 0.18f}, {"fEnvSustain", 0.0f}, {"fEnvRelease", 0.12f}, {"envAttack", 0.001f}, {"envDecay", 0.18f}, {"envSustain", 0.0f}, {"envRelease", 0.12f}, {"pan", 0}, {"drive", 1.8f}, {"masterLevel", 0.622}, {"pitchBendRange", 2}, {"distType", 0}, {"distAmount", 0}, {"compThreshold", -14}, {"compRatio", 4}, {"compAttack", 1}, {"compRelease", 40}, {"compMakeup", 3}, {"autoPanRate", 0}, {"autoPanDepth", 0}, {"autoPanPhase", 0}, {"lfo1Rate", 1.5f}, {"lfo1Depth", 0.15f}, {"lfo1Shape", 0}, {"lfo1Dest", 3}, {"lfo2Rate", 0}, {"lfo2Depth", 0}, {"lfo2Shape", 0}, {"lfo2Dest", 0}, {"delayTime", 200}, {"delayFeedback", 0.2f}, {"delayMix", 0.1f}, {"lfo1Sync", 1}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 1}, {"delayBeat", 4}, {"reverbSize", 0.3f}, {"reverbDamping", 0.6f}, {"reverbMix", 0.1f}}});
add("Acid Pulse", "Techno", {{{"osc1Wave", 1}, {"osc1Oct", -1}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.8f}, {"osc2Wave", 1}, {"osc2Oct", -1}, {"osc2Semi", 0}, {"osc2Fine", 8}, {"osc2Level", 0.5f}, {"phaseOffset", 0.5f}, {"filterType", 0}, {"filterCutoff", 300}, {"filterRes", 0.8f}, {"filterEnvAmt", 0.8f}, {"keyTrack", 0.3f}, {"fEnvAttack", 0.001f}, {"fEnvDecay", 0.2f}, {"fEnvSustain", 0.1f}, {"fEnvRelease", 0.15f}, {"envAttack", 0.001f}, {"envDecay", 0.25f}, {"envSustain", 0.5f}, {"envRelease", 0.2f}, {"pan", 0}, {"drive", 2.2f}, {"masterLevel", 0.464}, {"pitchBendRange", 2}, {"distType", 0}, {"distAmount", 0}, {"compThreshold", -12}, {"compRatio", 5}, {"compAttack", 2}, {"compRelease", 40}, {"compMakeup", 4}, {"autoPanRate", 0}, {"autoPanDepth", 0}, {"autoPanPhase", 0}, {"lfo1Rate", 0.8f}, {"lfo1Depth", 0.18f}, {"lfo1Shape", 2}, {"lfo1Dest", 3}, {"lfo2Rate", 0}, {"lfo2Depth", 0}, {"lfo2Shape", 0}, {"lfo2Dest", 0}, {"delayTime", 120}, {"delayFeedback", 0.3f}, {"delayMix", 0.15f}, {"lfo1Sync", 1}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 1}, {"delayBeat", 4}, {"reverbSize", 0.2f}, {"reverbDamping", 0.7f}, {"reverbMix", 0.08f}}});
// ======== SUPERSAW ========
add("JP8K Supersaw Bass", "Bass", {
{"osc1Wave", 16}, {"osc1Oct", -1}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.6f},
{"osc2Wave", 0}, {"osc2Oct", -1}, {"osc2Semi", 0}, {"osc2Fine", 0}, {"osc2Level", 0.8f},
{"phaseOffset", 0.5f},
{"filterType", 0}, {"filterCutoff", 350}, {"filterRes", 0.55f}, {"filterEnvAmt", 0.5f}, {"keyTrack", 0.4f},
{"fEnvAttack", 0.001f}, {"fEnvDecay", 0.25f}, {"fEnvSustain", 0.2f}, {"fEnvRelease", 0.2f},
{"envAttack", 0.001f}, {"envDecay", 0.3f}, {"envSustain", 0.7f}, {"envRelease", 0.2f},
{"pan", 0}, {"drive", 1.8f}, {"masterLevel", 0.5f},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0}, {"distSymmetry", 0}, {"distTone", 1.0f},
{"compThreshold", -16}, {"compRatio", 4}, {"compAttack", 5}, {"compRelease", 60}, {"compMakeup", 3},
{"autoPanRate", 2}, {"autoPanDepth", 0}, {"autoPanPhase", 0},
{"lfo1Rate", 0.3f}, {"lfo1Depth", 0.15f}, {"lfo1Shape", 0}, {"lfo1Dest", 0},
{"lfo2Rate", 0}, {"lfo2Depth", 0}, {"lfo2Shape", 0}, {"lfo2Dest", 0},
{"delayTime", 120}, {"delayFeedback", 0.15f}, {"delayMix", 0.1f}, {"lfo1Sync", 0}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 0}, {"delayBeat", 4},
{"reverbSize", 0.15f}, {"reverbDamping", 0.8f}, {"reverbMix", 0.05f}
});
add("JP8K Feedback Bass", "Bass", {
{"osc1Wave", 16}, {"osc1Oct", -1}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.7f},
{"osc2Wave", 16}, {"osc2Oct", -1}, {"osc2Semi", 7}, {"osc2Fine", 10}, {"osc2Level", 0.6f},
{"phaseOffset", 0.35f},
{"filterType", 0}, {"filterCutoff", 500}, {"filterRes", 0.7f}, {"filterEnvAmt", 0.8f}, {"keyTrack", 0.5f},
{"fEnvAttack", 0.001f}, {"fEnvDecay", 0.2f}, {"fEnvSustain", 0.15f}, {"fEnvRelease", 0.2f},
{"envAttack", 0.001f}, {"envDecay", 0.25f}, {"envSustain", 0.75f}, {"envRelease", 0.2f},
{"pan", 0}, {"drive", 2.2f}, {"masterLevel", 0.45},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0},
{"compThreshold", -14}, {"compRatio", 5}, {"compAttack", 3}, {"compRelease", 50}, {"compMakeup", 4},
{"autoPanRate", 2}, {"autoPanDepth", 0}, {"autoPanPhase", 0},
{"lfo1Rate", 0.4f}, {"lfo1Depth", 0.2f}, {"lfo1Shape", 0}, {"lfo1Dest", 0},
{"lfo2Rate", 0}, {"lfo2Depth", 0}, {"lfo2Shape", 0}, {"lfo2Dest", 0},
{"delayTime", 100}, {"delayFeedback", 0.2f}, {"delayMix", 0.1f}, {"lfo1Sync", 0}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 0}, {"delayBeat", 4},
{"reverbSize", 0.1f}, {"reverbDamping", 0.85f}, {"reverbMix", 0.03f}
});
add("JP8K Trance Lead", "Lead", {
{"osc1Wave", 16}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.6f},
{"osc2Wave", 16}, {"osc2Oct", 0}, {"osc2Semi", 12}, {"osc2Fine", -6}, {"osc2Level", 0.5f},
{"phaseOffset", 0.5f},
{"filterType", 0}, {"filterCutoff", 3200}, {"filterRes", 0.35f}, {"filterEnvAmt", 0.7f}, {"keyTrack", 0.7f},
{"fEnvAttack", 0.001f}, {"fEnvDecay", 0.15f}, {"fEnvSustain", 0.0f}, {"fEnvRelease", 0.12f},
{"envAttack", 0.001f}, {"envDecay", 0.2f}, {"envSustain", 0.8f}, {"envRelease", 0.25f},
{"pan", 0}, {"drive", 2.0f}, {"masterLevel", 0.55},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0}, {"distSymmetry", 0}, {"distTone", 1.0f},
{"compThreshold", -14}, {"compRatio", 3}, {"compAttack", 2}, {"compRelease", 60}, {"compMakeup", 3},
{"autoPanRate", 0}, {"autoPanDepth", 0}, {"autoPanPhase", 0},
{"lfo1Rate", 5.5f}, {"lfo1Depth", 0.12f}, {"lfo1Shape", 0}, {"lfo1Dest", 0},
{"lfo2Rate", 0.6f}, {"lfo2Depth", 0.1f}, {"lfo2Shape", 1}, {"lfo2Dest", 0},
{"delayTime", 150}, {"delayFeedback", 0.2f}, {"delayMix", 0.1f}, {"lfo1Sync", 1}, {"lfo1Beat", 4}, {"lfo2Sync", 1}, {"lfo2Beat", 4}, {"delaySync", 1}, {"delayBeat", 4},
{"reverbSize", 0.2f}, {"reverbDamping", 0.7f}, {"reverbMix", 0.08f}
});
add("JP8K Hoover", "Lead", {
{"osc1Wave", 16}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", -7}, {"osc1Level", 0.6f},
{"osc2Wave", 16}, {"osc2Oct", 0}, {"osc2Semi", 7}, {"osc2Fine", 12}, {"osc2Level", 0.6f},
{"phaseOffset", 0.25f},
{"filterType", 2}, {"filterCutoff", 1000}, {"filterRes", 0.75f}, {"filterEnvAmt", 0.6f}, {"keyTrack", 0.5f},
{"fEnvAttack", 0.001f}, {"fEnvDecay", 0.25f}, {"fEnvSustain", 0.3f}, {"fEnvRelease", 0.2f},
{"envAttack", 0.005f}, {"envDecay", 0.25f}, {"envSustain", 0.8f}, {"envRelease", 0.2f},
{"pan", 0}, {"drive", 2.5f}, {"masterLevel", 0.42},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0},
{"compThreshold", -12}, {"compRatio", 5}, {"compAttack", 2}, {"compRelease", 40}, {"compMakeup", 4},
{"autoPanRate", 0}, {"autoPanDepth", 0}, {"autoPanPhase", 0},
{"lfo1Rate", 4.0f}, {"lfo1Depth", 0.2f}, {"lfo1Shape", 0}, {"lfo1Dest", 0},
{"lfo2Rate", 0}, {"lfo2Depth", 0}, {"lfo2Shape", 0}, {"lfo2Dest", 0},
{"delayTime", 140}, {"delayFeedback", 0.25f}, {"delayMix", 0.12f}, {"lfo1Sync", 1}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 1}, {"delayBeat", 4},
{"reverbSize", 0.25f}, {"reverbDamping", 0.6f}, {"reverbMix", 0.1f}
});
add("JP8K Supersaw Pad", "Pad", {
{"osc1Wave", 16}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 0}, {"osc1Level", 0.5f},
{"osc2Wave", 16}, {"osc2Oct", 0}, {"osc2Semi", 7}, {"osc2Fine", -6}, {"osc2Level", 0.5f},
{"phaseOffset", 0.5f},
{"filterType", 0}, {"filterCutoff", 900}, {"filterRes", 0.3f}, {"filterEnvAmt", 0.4f}, {"keyTrack", 0.6f},
{"fEnvAttack", 1.5f}, {"fEnvDecay", 2.0f}, {"fEnvSustain", 0.6f}, {"fEnvRelease", 3.0f},
{"envAttack", 1.5f}, {"envDecay", 2.0f}, {"envSustain", 0.8f}, {"envRelease", 4.0f},
{"pan", 0}, {"drive", 1.2f}, {"masterLevel", 0.65},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0}, {"distSymmetry", 0}, {"distTone", 1.0f},
{"compThreshold", -22}, {"compRatio", 2}, {"compAttack", 25}, {"compRelease", 250}, {"compMakeup", 2},
{"autoPanRate", 0.15f}, {"autoPanDepth", 0.35f}, {"autoPanPhase", 0},
{"lfo1Rate", 0.12f}, {"lfo1Depth", 0.5f}, {"lfo1Shape", 0}, {"lfo1Dest", 4},
{"lfo2Rate", 0.06f}, {"lfo2Depth", 0.12f}, {"lfo2Shape", 1}, {"lfo2Dest", 0},
{"delayTime", 550}, {"delayFeedback", 0.4f}, {"delayMix", 0.25f}, {"lfo1Sync", 0}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 0}, {"delayBeat", 4},
{"reverbSize", 0.85f}, {"reverbDamping", 0.2f}, {"reverbMix", 0.5f}
});
add("JP8K Dream Pad", "Pad", {
{"osc1Wave", 16}, {"osc1Oct", 0}, {"osc1Semi", 0}, {"osc1Fine", 5}, {"osc1Level", 0.45f},
{"osc2Wave", 16}, {"osc2Oct", 1}, {"osc2Semi", 0}, {"osc2Fine", -5}, {"osc2Level", 0.4f},
{"phaseOffset", 0.6f},
{"filterType", 0}, {"filterCutoff", 1800}, {"filterRes", 0.15f}, {"filterEnvAmt", 0.3f}, {"keyTrack", 0.7f},
{"fEnvAttack", 1.0f}, {"fEnvDecay", 2.0f}, {"fEnvSustain", 0.7f}, {"fEnvRelease", 3.0f},
{"envAttack", 2.0f}, {"envDecay", 1.5f}, {"envSustain", 0.85f}, {"envRelease", 5.0f},
{"pan", 0}, {"drive", 1.1f}, {"masterLevel", 0.6f},
{"pitchBendRange", 2},
{"distType", 0}, {"distAmount", 0}, {"distSymmetry", 0}, {"distTone", 1.0f},
{"compThreshold", -25}, {"compRatio", 2}, {"compAttack", 30}, {"compRelease", 300}, {"compMakeup", 3},
{"autoPanRate", 0.2f}, {"autoPanDepth", 0.4f}, {"autoPanPhase", 0},
{"lfo1Rate", 0.15f}, {"lfo1Depth", 0.45f}, {"lfo1Shape", 0}, {"lfo1Dest", 4},
{"lfo2Rate", 0.05f}, {"lfo2Depth", 0.15f}, {"lfo2Shape", 0}, {"lfo2Dest", 0},
{"delayTime", 600}, {"delayFeedback", 0.5f}, {"delayMix", 0.3f}, {"lfo1Sync", 0}, {"lfo1Beat", 4}, {"lfo2Sync", 0}, {"lfo2Beat", 4}, {"delaySync", 0}, {"delayBeat", 4},
{"reverbSize", 0.9f}, {"reverbDamping", 0.1f}, {"reverbMix", 0.6f}
});
}
};