Upload files to "Source"

This commit is contained in:
ed
2025-10-25 17:56:37 +00:00
parent a43db68120
commit d9f672cd10
2 changed files with 695 additions and 17 deletions

View File

@@ -1,13 +1,54 @@
#include "PluginProcessor.h" #include "PluginProcessor.h"
#include "PluginEditor.h" #include "PluginEditor.h"
#include "WavetableOsc.h"
#include <array>
#include <map>
//============================================================================== //==============================================================================
NeuralSynthAudioProcessor::NeuralSynthAudioProcessor() NeuralSynthAudioProcessor::NeuralSynthAudioProcessor()
: parameters(*this, nullptr, "PARAMETERS", createParameterLayout()) : parameters(*this, nullptr, "PARAMETERS", createParameterLayout())
, AudioProcessor(BusesProperties().withOutput("Output", juce::AudioChannelSet::stereo(), true)) , AudioProcessor(BusesProperties().withOutput("Output", juce::AudioChannelSet::stereo(), true))
, audioEngine(sp) , audioEngine(sp)
, factoryPresets(makeFactoryPresets())
{ {
parameters.addParameterListener("waveform", this); parameters.addParameterListener("wt_phase", this);
parameters.addParameterListener("wt_on", this);
parameters.addParameterListener("wt_morph", this);
parameters.addParameterListener("wt_bank", this);
parameters.addParameterListener("wt_lfoRate", this);
parameters.addParameterListener("wt_lfoDepth", this);
parameters.addParameterListener("wt_lfoShape", this);
parameters.addParameterListener("wt_level", this);
parameters.addParameterListener("wt2_phase", this);
parameters.addParameterListener("wt2_on", this);
parameters.addParameterListener("wt2_morph", this);
parameters.addParameterListener("wt2_bank", this);
parameters.addParameterListener("wt2_lfoRate", this);
parameters.addParameterListener("wt2_lfoDepth", this);
parameters.addParameterListener("wt2_lfoShape", this);
parameters.addParameterListener("wt2_level", this);
sp.wtPhase = parameters.getRawParameterValue("wt_phase");
sp.wtOn = parameters.getRawParameterValue("wt_on");
sp.wtMorph = parameters.getRawParameterValue("wt_morph");
sp.wtBank = parameters.getRawParameterValue("wt_bank");
sp.wtLfoRate = parameters.getRawParameterValue("wt_lfoRate");
sp.wtLfoDepth= parameters.getRawParameterValue("wt_lfoDepth");
sp.wtLfoShape= parameters.getRawParameterValue("wt_lfoShape");
sp.wtLevel = parameters.getRawParameterValue("wt_level");
sp.wt2Phase = parameters.getRawParameterValue("wt2_phase");
sp.wt2On = parameters.getRawParameterValue("wt2_on");
sp.wt2Morph = parameters.getRawParameterValue("wt2_morph");
sp.wt2Bank = parameters.getRawParameterValue("wt2_bank");
sp.wt2LfoRate= parameters.getRawParameterValue("wt2_lfoRate");
sp.wt2LfoDepth= parameters.getRawParameterValue("wt2_lfoDepth");
sp.wt2LfoShape= parameters.getRawParameterValue("wt2_lfoShape");
sp.wt2Level = parameters.getRawParameterValue("wt2_level");
if (! factoryPresets.empty())
applyPreset(0);
// === Per-panel bypass (default OFF) === // === Per-panel bypass (default OFF) ===
sp.chorusOn = parameters.getRawParameterValue("chorus_on"); sp.chorusOn = parameters.getRawParameterValue("chorus_on");
@@ -158,6 +199,12 @@ void NeuralSynthAudioProcessor::changeProgramName (int, const juce::String&) {}
void NeuralSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) void NeuralSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{ {
audioEngine.prepare({ sampleRate, (juce::uint32)samplesPerBlock, 2 }); audioEngine.prepare({ sampleRate, (juce::uint32)samplesPerBlock, 2 });
const auto numChannels = (juce::uint32) juce::jmax (1, getTotalNumOutputChannels());
juce::dsp::ProcessSpec limiterSpec { sampleRate, (juce::uint32) samplesPerBlock, numChannels };
outputLimiter.reset();
outputLimiter.prepare (limiterSpec);
outputLimiter.setThreshold (-0.8f);
outputLimiter.setRelease (0.05f);
midiMessageCollector.reset(sampleRate); midiMessageCollector.reset(sampleRate);
} }
@@ -174,15 +221,6 @@ bool NeuralSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layou
void NeuralSynthAudioProcessor::processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midiMessages) void NeuralSynthAudioProcessor::processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midiMessages)
{ {
const int newWaveform = sp.waveform.exchange(-1);
if (newWaveform != -1) {
audioEngine.applyToVoices([newWaveform](NeuralSynthVoice* v)
{
v->changeWaveform(newWaveform);
});
}
juce::ScopedNoDenormals noDenormals; juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels(); auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels();
@@ -193,6 +231,8 @@ void NeuralSynthAudioProcessor::processBlock(juce::AudioSampleBuffer& buffer, ju
buffer.clear(i, 0, buffer.getNumSamples()); buffer.clear(i, 0, buffer.getNumSamples());
audioEngine.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples()); audioEngine.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());
juce::dsp::AudioBlock<float> outputBlock (buffer);
outputLimiter.process (juce::dsp::ProcessContextReplacing<float> (outputBlock));
scopeDataCollector.process(buffer.getReadPointer(0), (size_t)buffer.getNumSamples()); scopeDataCollector.process(buffer.getReadPointer(0), (size_t)buffer.getNumSamples());
} }
@@ -210,9 +250,26 @@ void NeuralSynthAudioProcessor::setStateInformation (const void* data, int sizeI
void NeuralSynthAudioProcessor::parameterChanged(const juce::String& id, float newValue) void NeuralSynthAudioProcessor::parameterChanged(const juce::String& id, float newValue)
{ {
if (id == "wt_bank" && ! presetChangeInProgress)
{
const int targetBank = juce::jlimit(0, (int)WT::FactoryLibrary::get().size() - 1,
(int) std::lround(newValue));
int matched = -1;
for (int i = 0; i < (int) factoryPresets.size(); ++i)
{
if (factoryPresets[(size_t)i].wtBankIndex == targetBank)
{
matched = i;
break;
}
}
currentPresetIndex = matched;
}
else if (id == "wt2_bank" && ! presetChangeInProgress)
{
juce::ignoreUnused(newValue); juce::ignoreUnused(newValue);
if (id == "waveform") currentPresetIndex = -1;
sp.waveform.store((int)newValue, std::memory_order_release); }
} }
//============================================================================== //==============================================================================
@@ -230,13 +287,610 @@ void NeuralSynthAudioProcessor::buildParams(std::vector<std::unique_ptr<juce::Ra
} }
} }
std::vector<NeuralSynthAudioProcessor::PresetDefinition> NeuralSynthAudioProcessor::makeFactoryPresets()
{
std::vector<PresetDefinition> presets;
const auto& wtLibrary = WT::FactoryLibrary::get();
if (wtLibrary.empty())
return presets;
presets.reserve(220);
std::map<juce::String, std::vector<int>> categoryToIndices;
for (int i = 0; i < (int) wtLibrary.size(); ++i)
categoryToIndices[wtLibrary[(size_t) i].category].push_back(i);
const std::array<juce::String, 12> requestedCategories = {
"Electric Piano", "Organ", "Bass", "Drums", "Strings",
"Brass", "Choir", "Pad", "SFX", "Lead", "Pluck", "Misc"
};
static const std::array<std::pair<int, const char*>, 20> gmDrumInfo = {{
{35, "Acoustic Bass Drum"},
{36, "Bass Drum 1"},
{37, "Side Stick"},
{38, "Acoustic Snare"},
{39, "Hand Clap"},
{40, "Electric Snare"},
{41, "Low Floor Tom"},
{42, "Closed Hi-Hat"},
{43, "High Floor Tom"},
{44, "Pedal Hi-Hat"},
{45, "Low Tom"},
{46, "Open Hi-Hat"},
{47, "Low-Mid Tom"},
{48, "Hi-Mid Tom"},
{49, "Crash Cymbal 1"},
{50, "High Tom"},
{51, "Ride Cymbal 1"},
{52, "Chinese Cymbal"},
{53, "Ride Bell"},
{54, "Tambourine"}
}};
auto clampMorph = [](float v) { return juce::jlimit(0.0f, 15.0f, v); };
auto clampUnit = [](float v) { return juce::jlimit(0.0f, 1.0f, v); };
auto clampLfoDepth = [](float v) { return juce::jlimit(0.0f, 8.0f, v); };
auto clampLfoRate = [](float v) { return juce::jlimit(0.01f, 10.0f, v); };
auto clampCutoff = [](float v) { return juce::jlimit(20.0f, 20000.0f, v); };
auto clampRes = [](float v) { return juce::jlimit(0.1f, 10.0f, v); };
auto clampDb = [](float v) { return juce::jlimit(-24.0f, 24.0f, v); };
for (const auto& category : requestedCategories)
{
auto mapIt = categoryToIndices.find(category);
if (mapIt == categoryToIndices.end())
continue;
const auto& indices = mapIt->second;
if (indices.size() < 20)
continue;
for (int variant = 0; variant < 20; ++variant)
{
const bool layered = variant >= 10;
const int primaryIndex = indices[(size_t) (variant % indices.size())];
const int secondaryIndex = layered ? indices[(size_t) ((variant + 5) % indices.size())]
: primaryIndex;
const float t = (float) variant / 19.0f;
const float subT = (float) (variant % 10) / 9.0f;
const bool isDrumCategory = (category == "Drums");
std::pair<int, const char*> gmInfo { 0, "" };
if (isDrumCategory)
gmInfo = gmDrumInfo[(size_t) variant];
std::map<juce::String, float> values;
auto set = [&](const juce::String& param, float value)
{
values[param] = value;
};
set("wt_on", 1.0f);
set("wt_bank", (float) primaryIndex);
set("wt_morph", clampMorph(2.0f + 8.0f * t));
set("wt_phase", 0.0f);
set("wt_lfoRate", clampLfoRate(0.25f + 0.8f * t));
set("wt_lfoDepth", clampLfoDepth(0.6f + 2.0f * t));
set("wt_lfoShape", (float) (variant % 4));
set("wt_level", clampUnit(layered ? 0.72f : 0.85f));
set("wt2_on", layered ? 1.0f : 0.0f);
set("wt2_bank", (float) secondaryIndex);
set("wt2_morph", layered ? clampMorph(4.0f + 6.0f * (1.0f - t)) : 0.0f);
set("wt2_phase", layered ? 0.25f : 0.0f);
set("wt2_lfoRate", clampLfoRate(layered ? (0.4f + 1.3f * (1.0f - t)) : 0.3f));
set("wt2_lfoDepth", clampLfoDepth(layered ? (1.0f + 2.0f * subT) : 0.0f));
set("wt2_lfoShape", layered ? (float) ((variant + 1) % 4) : 0.0f);
set("wt2_level", layered ? clampUnit(0.5f + 0.3f * t) : 0.0f);
set("chorus_on", 0.0f);
set("chorus_rate", clampUnit(0.3f));
set("chorus_depth", clampUnit(0.3f));
set("chorus_centre", clampUnit(0.5f));
set("chorus_feedback", clampUnit(0.12f));
set("chorus_mix", clampUnit(0.2f));
set("delay_on", 0.0f);
set("delay_delay", clampUnit(0.2f));
set("reverb_on", 0.0f);
set("reverb_roomSize", clampUnit(0.3f));
set("reverb_damping", clampUnit(0.5f));
set("reverb_wetLevel", clampUnit(0.18f));
set("reverb_dryLevel", clampUnit(0.85f));
set("reverb_width", clampUnit(0.9f));
set("reverb_freezeMode", 0.0f);
set("flanger_on", 0.0f);
set("flanger_rate", clampUnit(0.35f));
set("flanger_depth", 2.5f);
set("flanger_feedback", clampUnit(0.15f));
set("flanger_dryMix", clampUnit(0.25f));
set("flanger_phase", clampUnit(0.2f));
set("flanger_delay", 0.2f);
set("distortion_on", 0.0f);
set("distortion_drive", 10.0f);
set("distortion_mix", clampUnit(0.2f));
set("distortion_bias", 0.0f);
set("distortion_tone", juce::jlimit(100.0f, 8000.0f, 2400.0f));
set("distortion_shape", 0.0f);
set("filter_on", 0.0f);
set("filter_cutoff", clampCutoff(2000.0f));
set("filter_resonance", clampRes(0.7f));
set("filter_type", 0.0f);
set("filter_drive", 0.0f);
set("filter_mod", 0.0f);
set("filter_key", 0.0f);
set("adsr_attack", clampUnit(0.02f));
set("adsr_decay", clampUnit(0.3f));
set("adsr_sustain", clampUnit(0.7f));
set("adsr_release", clampUnit(0.4f));
set("fenv_attack", juce::jlimit(0.0f, 2.0f, 0.03f));
set("fenv_decay", juce::jlimit(0.0f, 2.0f, 0.3f));
set("fenv_sustain", clampUnit(0.5f));
set("fenv_release", juce::jlimit(0.0f, 4.0f, 0.4f));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.2f));
set("eq_on", 1.0f);
set("lowEQ", clampDb(0.0f));
set("midEQ", clampDb(0.0f));
set("highEQ", clampDb(0.0f));
set("master", layered ? -8.0f : -6.0f);
if (category == "Electric Piano")
{
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.18f + 0.25f * t));
set("chorus_depth", clampUnit(0.35f + 0.2f * t));
set("chorus_mix", clampUnit(0.2f + 0.15f * t));
set("chorus_feedback", clampUnit(0.18f + 0.1f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.35f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.2f + 0.12f * t));
set("reverb_damping", clampUnit(0.45f + 0.1f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(1500.0f + 700.0f * t));
set("filter_resonance", clampRes(0.8f + 0.3f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.35f + 0.15f * t));
set("adsr_attack", clampUnit(0.02f + 0.03f * t));
set("adsr_decay", clampUnit(0.25f + 0.1f * t));
set("adsr_sustain", clampUnit(0.65f + 0.1f * t));
set("adsr_release", clampUnit(0.45f + 0.18f * t));
set("wt_level", clampUnit(layered ? 0.7f : 0.83f));
if (layered)
{
set("wt2_level", clampUnit(0.55f + 0.2f * t));
set("wt2_lfoRate", clampLfoRate(0.35f + 0.9f * (1.0f - t)));
set("wt2_lfoDepth", clampLfoDepth(1.1f + 1.4f * t));
}
set("lowEQ", clampDb(1.5f));
set("midEQ", clampDb(-1.0f + 2.5f * t));
set("highEQ", clampDb(2.0f + 3.0f * t));
}
else if (category == "Organ")
{
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.45f + 0.15f * t));
set("chorus_depth", clampUnit(0.3f + 0.1f * t));
set("chorus_mix", clampUnit(0.33f + 0.08f * t));
set("chorus_feedback", clampUnit(0.15f + 0.05f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.25f + 0.2f * t));
set("reverb_wetLevel", clampUnit(0.16f + 0.1f * t));
set("reverb_damping", clampUnit(0.4f + 0.15f * t));
set("filter_on", 0.0f);
set("adsr_attack", clampUnit(0.01f));
set("adsr_decay", clampUnit(0.3f));
set("adsr_sustain", clampUnit(1.0f));
set("adsr_release", clampUnit(0.3f + 0.2f * t));
set("fenv_amount", 0.0f);
set("wt_morph", clampMorph(3.0f + 5.0f * t));
set("wt_lfoRate", clampLfoRate(0.4f + 0.45f * t));
set("wt_lfoDepth", clampLfoDepth(0.25f + 0.35f * t));
if (layered)
{
set("wt2_level", clampUnit(0.5f + 0.2f * t));
set("wt2_lfoDepth", clampLfoDepth(0.4f + 0.5f * t));
set("distortion_on", variant >= 15 ? 1.0f : 0.0f);
if (variant >= 15)
{
set("distortion_drive", 9.0f + 5.0f * t);
set("distortion_mix", clampUnit(0.18f + 0.12f * t));
set("distortion_shape", 1.0f);
}
}
set("midEQ", clampDb(1.5f + 1.0f * t));
set("highEQ", clampDb(2.0f + 1.5f * t));
set("master", layered ? -8.0f : -5.0f);
}
else if (category == "Bass")
{
set("wt_morph", clampMorph(1.5f + 5.5f * t));
set("wt_lfoRate", clampLfoRate(0.2f + 0.6f * t));
set("wt_lfoDepth", clampLfoDepth(0.5f + 1.3f * t));
set("wt_level", clampUnit(0.92f));
set("adsr_attack", clampUnit(0.005f));
set("adsr_decay", clampUnit(0.18f + 0.08f * t));
set("adsr_sustain", clampUnit(0.45f - 0.15f * t));
set("adsr_release", clampUnit(0.22f + 0.12f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(180.0f + 720.0f * t));
set("filter_resonance", clampRes(0.85f + 0.35f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.6f + 0.25f * t));
set("distortion_on", (layered || variant >= 6) ? 1.0f : 0.0f);
if (values["distortion_on"] > 0.5f)
{
set("distortion_drive", 14.0f + 8.0f * t);
set("distortion_mix", clampUnit(0.25f + 0.25f * t));
set("distortion_shape", 2.0f);
}
set("lowEQ", clampDb(3.5f + 2.0f * t));
set("midEQ", clampDb(-2.5f + 1.8f * t));
set("highEQ", clampDb(-5.0f + 2.0f * t));
if (layered)
{
set("wt2_level", clampUnit(0.6f + 0.2f * t));
set("wt2_lfoDepth", clampLfoDepth(0.8f + 1.6f * t));
set("wt2_lfoRate", clampLfoRate(0.3f + 0.7f * t));
}
set("master", layered ? -7.5f : -5.0f);
}
else if (category == "Drums")
{
set("wt_morph", clampMorph(1.0f + 9.0f * t));
set("wt_lfoRate", clampLfoRate(1.2f + 2.5f * t));
set("wt_lfoDepth", clampLfoDepth(0.3f + 2.8f * t));
set("wt_level", clampUnit(0.88f));
set("adsr_attack", clampUnit(0.001f));
set("adsr_decay", clampUnit(0.12f + 0.08f * t));
set("adsr_sustain", clampUnit(0.05f));
set("adsr_release", clampUnit(0.18f + 0.12f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, -0.2f + 0.4f * t));
set("distortion_on", 1.0f);
set("distortion_drive", 16.0f + 10.0f * t);
set("distortion_mix", clampUnit(0.35f + 0.2f * t));
set("distortion_shape", (float) (variant % 3));
set("distortion_bias", juce::jlimit(-1.0f, 1.0f, 0.05f * (variant % 5)));
set("reverb_on", layered ? 1.0f : (variant >= 8 ? 1.0f : 0.0f));
if (values["reverb_on"] > 0.5f)
{
set("reverb_roomSize", clampUnit(layered ? (0.45f + 0.3f * t) : 0.35f + 0.15f * t));
set("reverb_wetLevel", clampUnit(layered ? (0.3f + 0.18f * t) : 0.18f + 0.1f * t));
set("reverb_damping", clampUnit(0.45f + 0.25f * t));
}
set("filter_on", layered ? 1.0f : 0.0f);
if (values["filter_on"] > 0.5f)
{
set("filter_cutoff", clampCutoff(800.0f + 1200.0f * t));
set("filter_resonance", clampRes(1.0f + 0.6f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.1f + 0.35f * t));
}
if (layered)
{
set("wt2_level", clampUnit(0.6f + 0.25f * t));
set("wt2_lfoRate", clampLfoRate(0.9f + 1.6f * (1.0f - t)));
set("wt2_lfoDepth", clampLfoDepth(1.3f + 1.7f * t));
}
set("lowEQ", clampDb(4.0f + 2.0f * t));
set("midEQ", clampDb(-4.0f + 3.0f * t));
set("highEQ", clampDb(3.0f + 4.0f * t));
set("master", layered ? -8.5f : -6.5f);
}
else if (category == "Strings")
{
set("wt_morph", clampMorph(3.0f + 9.0f * t));
set("wt_lfoRate", clampLfoRate(0.18f + 0.4f * t));
set("wt_lfoDepth", clampLfoDepth(0.8f + 1.8f * t));
set("adsr_attack", clampUnit(0.22f + 0.18f * t));
set("adsr_decay", clampUnit(0.3f + 0.12f * t));
set("adsr_sustain", clampUnit(0.85f));
set("adsr_release", clampUnit(0.6f + 0.25f * t));
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.25f + 0.1f * t));
set("chorus_depth", clampUnit(0.45f + 0.15f * t));
set("chorus_mix", clampUnit(0.3f + 0.1f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.55f + 0.2f * t));
set("reverb_wetLevel", clampUnit(0.35f + 0.15f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(2600.0f + 1500.0f * t));
set("filter_resonance", clampRes(0.85f + 0.25f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.25f + 0.1f * t));
if (layered)
{
set("wt2_level", clampUnit(0.5f + 0.25f * t));
set("wt2_lfoDepth", clampLfoDepth(1.0f + 1.5f * t));
}
set("midEQ", clampDb(-1.0f + 2.0f * t));
set("highEQ", clampDb(2.5f + 2.5f * t));
set("master", -7.5f);
}
else if (category == "Brass")
{
set("adsr_attack", clampUnit(0.05f + 0.05f * t));
set("adsr_decay", clampUnit(0.25f + 0.1f * t));
set("adsr_sustain", clampUnit(0.75f));
set("adsr_release", clampUnit(0.35f + 0.15f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(2200.0f + 1800.0f * t));
set("filter_resonance", clampRes(1.0f + 0.3f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.55f + 0.25f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.4f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.3f + 0.1f * t));
set("distortion_on", (layered || variant >= 8) ? 1.0f : 0.0f);
if (values["distortion_on"] > 0.5f)
{
set("distortion_drive", 10.0f + 6.0f * t);
set("distortion_mix", clampUnit(0.18f + 0.18f * t));
set("distortion_shape", 1.0f);
}
set("flanger_on", layered ? 1.0f : 0.0f);
if (values["flanger_on"] > 0.5f)
{
set("flanger_rate", clampUnit(0.35f + 0.25f * t));
set("flanger_depth", 4.0f + 3.0f * t);
set("flanger_dryMix", clampUnit(0.5f));
}
if (layered)
{
set("wt2_level", clampUnit(0.52f + 0.22f * t));
set("wt2_lfoDepth", clampLfoDepth(0.8f + 1.4f * t));
}
set("lowEQ", clampDb(2.0f + 1.0f * t));
set("midEQ", clampDb(1.0f + 1.5f * t));
set("highEQ", clampDb(1.0f + 2.5f * t));
}
else if (category == "Choir")
{
set("adsr_attack", clampUnit(0.3f + 0.2f * t));
set("adsr_decay", clampUnit(0.4f + 0.1f * t));
set("adsr_sustain", clampUnit(0.9f));
set("adsr_release", clampUnit(0.55f + 0.3f * t));
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.2f + 0.15f * t));
set("chorus_depth", clampUnit(0.5f + 0.2f * t));
set("chorus_mix", clampUnit(0.35f + 0.15f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.6f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.4f + 0.15f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(1800.0f + 800.0f * t));
set("filter_resonance", clampRes(0.7f + 0.2f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.15f + 0.08f * t));
set("wt_lfoDepth", clampLfoDepth(1.0f + 2.0f * t));
if (layered)
{
set("wt2_level", clampUnit(0.48f + 0.2f * t));
set("wt2_lfoDepth", clampLfoDepth(1.2f + 1.5f * t));
}
set("midEQ", clampDb(-1.5f + 1.5f * t));
set("highEQ", clampDb(3.0f + 2.0f * t));
set("master", -7.5f);
}
else if (category == "Pad")
{
set("adsr_attack", clampUnit(0.35f + 0.25f * t));
set("adsr_decay", clampUnit(0.35f + 0.15f * t));
set("adsr_sustain", clampUnit(0.85f));
set("adsr_release", clampUnit(0.7f + 0.35f * t));
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.18f + 0.12f * t));
set("chorus_depth", clampUnit(0.55f + 0.2f * t));
set("chorus_mix", clampUnit(0.35f + 0.15f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.65f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.4f + 0.2f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(1500.0f + 900.0f * t));
set("filter_resonance", clampRes(0.8f + 0.25f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.2f + 0.12f * t));
set("flanger_on", (layered || variant % 3 == 0) ? 1.0f : 0.0f);
if (values["flanger_on"] > 0.5f)
{
set("flanger_rate", clampUnit(0.25f + 0.15f * t));
set("flanger_depth", 5.0f + 3.0f * t);
set("flanger_dryMix", clampUnit(0.4f));
}
set("wt_lfoRate", clampLfoRate(0.15f + 0.35f * t));
set("wt_lfoDepth", clampLfoDepth(1.5f + 2.5f * t));
if (layered)
{
set("wt2_level", clampUnit(0.5f + 0.3f * t));
set("wt2_lfoDepth", clampLfoDepth(1.3f + 2.0f * t));
}
set("master", -8.5f);
}
else if (category == "SFX")
{
set("wt_morph", clampMorph(4.0f + 10.0f * t));
set("wt_lfoRate", clampLfoRate(1.2f + 3.5f * t));
set("wt_lfoDepth", clampLfoDepth(2.5f + 5.0f * t));
set("wt_lfoShape", (float) (variant % 4));
set("wt_phase", (variant % 2 == 0) ? 0.0f : 0.5f);
set("chorus_on", 1.0f);
set("chorus_rate", clampUnit(0.35f + 0.3f * t));
set("chorus_depth", clampUnit(0.45f + 0.25f * t));
set("chorus_mix", clampUnit(0.3f + 0.2f * t));
set("flanger_on", 1.0f);
set("flanger_rate", clampUnit(0.45f + 0.4f * t));
set("flanger_depth", 6.0f + 4.0f * t);
set("flanger_phase", clampUnit(0.2f + 0.5f * t));
set("flanger_dryMix", clampUnit(0.5f));
set("distortion_on", 1.0f);
set("distortion_drive", 18.0f + 7.0f * t);
set("distortion_mix", clampUnit(0.35f + 0.2f * t));
set("distortion_shape", (float) ((variant + 1) % 3));
set("distortion_bias", juce::jlimit(-1.0f, 1.0f, -0.2f + 0.4f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.7f + 0.2f * t));
set("reverb_wetLevel", clampUnit(0.45f + 0.2f * t));
if (layered)
{
set("wt2_level", clampUnit(0.6f + 0.25f * t));
set("wt2_morph", clampMorph(6.0f + 7.0f * (1.0f - t)));
set("wt2_lfoRate", clampLfoRate(0.8f + 4.0f * (1.0f - t)));
set("wt2_lfoDepth", clampLfoDepth(2.0f + 4.0f * t));
set("wt2_lfoShape", (float) ((variant + 2) % 4));
}
set("lowEQ", clampDb(-4.0f + 4.0f * t));
set("midEQ", clampDb(3.0f - 4.0f * t));
set("highEQ", clampDb(6.0f + 4.0f * t));
set("master", -12.0f);
}
else if (category == "Lead")
{
set("wt_morph", clampMorph(3.0f + 8.0f * t));
set("wt_lfoRate", clampLfoRate(0.4f + 1.2f * t));
set("wt_lfoDepth", clampLfoDepth(1.0f + 2.0f * t));
set("adsr_attack", clampUnit(0.01f + 0.02f * t));
set("adsr_decay", clampUnit(0.18f + 0.08f * t));
set("adsr_sustain", clampUnit(0.85f));
set("adsr_release", clampUnit(0.25f + 0.1f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(2300.0f + 3200.0f * t));
set("filter_resonance", clampRes(0.9f + 0.25f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.45f + 0.25f * t));
set("distortion_on", 1.0f);
set("distortion_drive", 12.0f + 10.0f * t);
set("distortion_mix", clampUnit(0.25f + 0.25f * t));
set("distortion_shape", (float) ((variant % 2) + 1));
set("chorus_on", (layered || variant % 3 == 0) ? 1.0f : 0.0f);
if (values["chorus_on"] > 0.5f)
{
set("chorus_rate", clampUnit(0.3f + 0.2f * t));
set("chorus_depth", clampUnit(0.4f + 0.2f * t));
set("chorus_mix", clampUnit(0.25f + 0.15f * t));
}
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.35f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.22f + 0.15f * t));
if (layered)
{
set("wt2_level", clampUnit(0.52f + 0.22f * t));
set("wt2_lfoDepth", clampLfoDepth(1.1f + 1.6f * t));
}
set("lowEQ", clampDb(-1.5f + 1.5f * t));
set("midEQ", clampDb(2.0f + 1.5f * t));
set("highEQ", clampDb(4.0f + 3.0f * t));
set("master", layered ? -7.5f : -6.0f);
}
else if (category == "Pluck")
{
set("wt_morph", clampMorph(2.5f + 7.0f * t));
set("wt_lfoRate", clampLfoRate(0.25f + 0.7f * t));
set("wt_lfoDepth", clampLfoDepth(0.6f + 1.8f * t));
set("adsr_attack", clampUnit(0.005f));
set("adsr_decay", clampUnit(0.22f + 0.15f * t));
set("adsr_sustain", clampUnit(0.2f + 0.1f * t));
set("adsr_release", clampUnit(0.25f + 0.1f * t));
set("fenv_amount", juce::jlimit(-1.0f, 1.0f, 0.4f + 0.2f * t));
set("filter_on", 1.0f);
set("filter_cutoff", clampCutoff(1800.0f + 2000.0f * t));
set("filter_resonance", clampRes(1.1f + 0.3f * t));
set("delay_on", 1.0f);
set("delay_delay", clampUnit(0.25f + 0.25f * t));
set("reverb_on", 1.0f);
set("reverb_roomSize", clampUnit(0.45f + 0.25f * t));
set("reverb_wetLevel", clampUnit(0.25f + 0.15f * t));
set("chorus_on", 0.0f);
set("distortion_on", variant >= 12 ? 1.0f : 0.0f);
if (values["distortion_on"] > 0.5f)
{
set("distortion_drive", 10.0f + 8.0f * t);
set("distortion_mix", clampUnit(0.2f + 0.2f * t));
set("distortion_shape", 2.0f);
}
if (layered)
{
set("wt2_level", clampUnit(0.48f + 0.25f * t));
set("wt2_lfoDepth", clampLfoDepth(0.9f + 1.5f * t));
}
set("lowEQ", clampDb(-1.0f + 2.0f * t));
set("midEQ", clampDb(-2.0f + 3.0f * t));
set("highEQ", clampDb(3.0f + 2.5f * t));
}
juce::String presetName;
if (isDrumCategory)
{
presetName = juce::String::formatted("GM %d %s%s",
gmInfo.first,
gmInfo.second,
layered ? " Stack" : "");
}
else
{
presetName = category + (layered ? " Duo " : " Solo ")
+ juce::String(variant + 1).paddedLeft('0', 2);
}
auto scaleLevel = [&](const juce::String& paramId, float factor)
{
auto it = values.find(paramId);
if (it != values.end())
it->second = juce::jlimit(0.0f, 1.0f, it->second * factor);
};
scaleLevel("wt_level", 0.75f);
scaleLevel("wt2_level", 0.75f);
if (auto it = values.find("master"); it != values.end())
it->second = juce::jlimit(-24.0f, 24.0f, it->second - 4.0f);
PresetDefinition def;
def.category = category;
def.name = presetName.trim();
def.wtBankIndex = primaryIndex;
def.wt2BankIndex = layered ? secondaryIndex : -1;
def.parameterValues.reserve(values.size());
for (const auto& entry : values)
def.parameterValues.emplace_back(entry.first, entry.second);
presets.push_back(std::move(def));
}
}
return presets;
}
void NeuralSynthAudioProcessor::setParameterValue(const juce::String& paramID, float value)
{
if (parameters.getParameter(paramID) != nullptr)
parameters.getParameterAsValue(paramID) = value;
}
void NeuralSynthAudioProcessor::applyPreset(int index)
{
if (factoryPresets.empty())
return;
index = juce::jlimit(0, (int) factoryPresets.size() - 1, index);
const auto& preset = factoryPresets[(size_t) index];
juce::ScopedValueSetter<bool> guard(presetChangeInProgress, true, false);
for (const auto& entry : preset.parameterValues)
setParameterValue(entry.first, entry.second);
currentPresetIndex = index;
}
juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::createParameterLayout() juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::createParameterLayout()
{ {
std::vector<std::unique_ptr<juce::RangedAudioParameter>> params; std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
params.push_back(std::make_unique<juce::AudioParameterChoice>( params.push_back(std::make_unique<juce::AudioParameterBool>(
"waveform", "Waveform", "wt_on", "Layer A On", true));
juce::StringArray{ "Sine", "Saw", "Square", "Triangle" }, 0)); params.push_back(std::make_unique<juce::AudioParameterBool>(
"wt2_on", "Layer B On", false));
// Per-panel bypass toggles (default OFF) // Per-panel bypass toggles (default OFF)
params.push_back(std::make_unique<juce::AudioParameterBool>("chorus_on", "Chorus On", false)); params.push_back(std::make_unique<juce::AudioParameterBool>("chorus_on", "Chorus On", false));
@@ -255,9 +909,11 @@ juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::c
buildParams(params, "flanger"); buildParams(params, "flanger");
buildParams(params, "distortion"); buildParams(params, "distortion");
buildParams(params, "filter"); buildParams(params, "filter");
buildParams(params, "wt");
buildParams(params, "wt2");
params.push_back(std::make_unique<juce::AudioParameterFloat>("master", "Master", params.push_back(std::make_unique<juce::AudioParameterFloat>("master", "Master",
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.1f)); juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), -6.0f));
params.push_back(std::make_unique<juce::AudioParameterFloat>("lowEQ", "Low Gain", params.push_back(std::make_unique<juce::AudioParameterFloat>("lowEQ", "Low Gain",
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.5f)); juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.5f));

View File

@@ -12,6 +12,15 @@ class NeuralSynthAudioProcessor : public juce::AudioProcessor,
private juce::AudioProcessorValueTreeState::Listener private juce::AudioProcessorValueTreeState::Listener
{ {
public: public:
struct PresetDefinition
{
juce::String category;
juce::String name;
int wtBankIndex;
int wt2BankIndex { -1 };
std::vector<std::pair<juce::String, float>> parameterValues;
};
NeuralSynthAudioProcessor(); NeuralSynthAudioProcessor();
~NeuralSynthAudioProcessor() override; ~NeuralSynthAudioProcessor() override;
@@ -53,6 +62,10 @@ public:
const std::string& paramGroup); const std::string& paramGroup);
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
void applyPreset(int index);
const std::vector<PresetDefinition>& getFactoryPresets() const noexcept { return factoryPresets; }
int getCurrentPresetIndex() const noexcept { return currentPresetIndex; }
// Utilities // Utilities
juce::MidiMessageCollector& getMidiMessageCollector() noexcept { return midiMessageCollector; } juce::MidiMessageCollector& getMidiMessageCollector() noexcept { return midiMessageCollector; }
AudioBufferQueue<float>& getAudioBufferQueue() noexcept { return audioBufferQueue; } AudioBufferQueue<float>& getAudioBufferQueue() noexcept { return audioBufferQueue; }
@@ -87,4 +100,13 @@ private:
// Scope collector (uses audioBufferQueue, so declare after it) // Scope collector (uses audioBufferQueue, so declare after it)
ScopeDataCollector<float> scopeDataCollector { audioBufferQueue }; ScopeDataCollector<float> scopeDataCollector { audioBufferQueue };
std::vector<PresetDefinition> factoryPresets;
int currentPresetIndex { -1 };
bool presetChangeInProgress { false };
static std::vector<PresetDefinition> makeFactoryPresets();
void setParameterValue(const juce::String& paramID, float value);
juce::dsp::Limiter<float> outputLimiter;
}; };