#include "PluginProcessor.h" #include "PluginEditor.h" #include "WavetableOsc.h" #include #include //============================================================================== NeuralSynthAudioProcessor::NeuralSynthAudioProcessor() : parameters(*this, nullptr, "PARAMETERS", createParameterLayout()) , AudioProcessor(BusesProperties().withOutput("Output", juce::AudioChannelSet::stereo(), true)) , audioEngine(sp) , factoryPresets(makeFactoryPresets()) { 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) === sp.chorusOn = parameters.getRawParameterValue("chorus_on"); sp.delayOn = parameters.getRawParameterValue("delay_on"); sp.reverbOn = parameters.getRawParameterValue("reverb_on"); sp.flangerOn = parameters.getRawParameterValue("flanger_on"); sp.distortionOn = parameters.getRawParameterValue("distortion_on"); sp.filterOn = parameters.getRawParameterValue("filter_on"); sp.eqOn = parameters.getRawParameterValue("eq_on"); // === Chorus === parameters.addParameterListener("chorus_rate", this); parameters.addParameterListener("chorus_depth", this); parameters.addParameterListener("chorus_centre", this); parameters.addParameterListener("chorus_feedback", this); parameters.addParameterListener("chorus_mix", this); sp.chorusRate = parameters.getRawParameterValue("chorus_rate"); sp.chorusDepth = parameters.getRawParameterValue("chorus_depth"); sp.chorusCentre = parameters.getRawParameterValue("chorus_centre"); sp.chorusFeedback = parameters.getRawParameterValue("chorus_feedback"); sp.chorusMix = parameters.getRawParameterValue("chorus_mix"); // === Delay === parameters.addParameterListener("delay_delay", this); sp.delayTime = parameters.getRawParameterValue("delay_delay"); // === Reverb === parameters.addParameterListener("reverb_roomSize", this); parameters.addParameterListener("reverb_damping", this); parameters.addParameterListener("reverb_wetLevel", this); parameters.addParameterListener("reverb_dryLevel", this); parameters.addParameterListener("reverb_width", this); parameters.addParameterListener("reverb_freezeMode", this); sp.reverbRoomSize = parameters.getRawParameterValue("reverb_roomSize"); sp.reverbDamping = parameters.getRawParameterValue("reverb_damping"); sp.reverbWetLevel = parameters.getRawParameterValue("reverb_wetLevel"); sp.reverbDryLevel = parameters.getRawParameterValue("reverb_dryLevel"); sp.reverbWidth = parameters.getRawParameterValue("reverb_width"); sp.reverbFreezeMode= parameters.getRawParameterValue("reverb_freezeMode"); // === Amp ADSR === parameters.addParameterListener("adsr_attack", this); parameters.addParameterListener("adsr_decay", this); parameters.addParameterListener("adsr_sustain", this); parameters.addParameterListener("adsr_release", this); sp.adsrAttack = parameters.getRawParameterValue("adsr_attack"); sp.adsrDecay = parameters.getRawParameterValue("adsr_decay"); sp.adsrSustain = parameters.getRawParameterValue("adsr_sustain"); sp.adsrRelease = parameters.getRawParameterValue("adsr_release"); // === Filter Env === parameters.addParameterListener("fenv_attack", this); parameters.addParameterListener("fenv_decay", this); parameters.addParameterListener("fenv_sustain", this); parameters.addParameterListener("fenv_release", this); parameters.addParameterListener("fenv_amount", this); sp.fenvAttack = parameters.getRawParameterValue("fenv_attack"); sp.fenvDecay = parameters.getRawParameterValue("fenv_decay"); sp.fenvSustain = parameters.getRawParameterValue("fenv_sustain"); sp.fenvRelease = parameters.getRawParameterValue("fenv_release"); sp.fenvAmount = parameters.getRawParameterValue("fenv_amount"); // === Filter base === parameters.addParameterListener("filter_cutoff", this); parameters.addParameterListener("filter_resonance", this); parameters.addParameterListener("filter_type", this); parameters.addParameterListener("filter_drive", this); parameters.addParameterListener("filter_mod", this); parameters.addParameterListener("filter_key", this); sp.filterCutoff = parameters.getRawParameterValue("filter_cutoff"); sp.filterResonance = parameters.getRawParameterValue("filter_resonance"); sp.filterType = parameters.getRawParameterValue("filter_type"); sp.filterDrive = parameters.getRawParameterValue("filter_drive"); sp.filterMod = parameters.getRawParameterValue("filter_mod"); sp.filterKey = parameters.getRawParameterValue("filter_key"); // === Distortion === parameters.addParameterListener("distortion_drive", this); parameters.addParameterListener("distortion_mix", this); parameters.addParameterListener("distortion_bias", this); parameters.addParameterListener("distortion_tone", this); parameters.addParameterListener("distortion_shape", this); sp.distortionDrive = parameters.getRawParameterValue("distortion_drive"); sp.distortionMix = parameters.getRawParameterValue("distortion_mix"); sp.distortionBias = parameters.getRawParameterValue("distortion_bias"); sp.distortionTone = parameters.getRawParameterValue("distortion_tone"); sp.distortionShape = parameters.getRawParameterValue("distortion_shape"); // === Master / EQ === parameters.addParameterListener("master", this); parameters.addParameterListener("lowEQ", this); parameters.addParameterListener("midEQ", this); parameters.addParameterListener("highEQ", this); sp.masterDbls = parameters.getRawParameterValue("master"); sp.lowGainDbls = parameters.getRawParameterValue("lowEQ"); sp.midGainDbls = parameters.getRawParameterValue("midEQ"); sp.highGainDbls = parameters.getRawParameterValue("highEQ"); } NeuralSynthAudioProcessor::~NeuralSynthAudioProcessor() = default; //============================================================================== const juce::String NeuralSynthAudioProcessor::getName() const { return JucePlugin_Name; } bool NeuralSynthAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool NeuralSynthAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } bool NeuralSynthAudioProcessor::isMidiEffect() const { #if JucePlugin_IsMidiEffect return true; #else return false; #endif } double NeuralSynthAudioProcessor::getTailLengthSeconds() const { return 0.0; } int NeuralSynthAudioProcessor::getNumPrograms() { return 1; } int NeuralSynthAudioProcessor::getCurrentProgram() { return 0; } void NeuralSynthAudioProcessor::setCurrentProgram (int) {} const juce::String NeuralSynthAudioProcessor::getProgramName (int) { return {}; } void NeuralSynthAudioProcessor::changeProgramName (int, const juce::String&) {} //============================================================================== void NeuralSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { 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); } void NeuralSynthAudioProcessor::releaseResources() {} bool NeuralSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false; return true; } void NeuralSynthAudioProcessor::processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midiMessages) { juce::ScopedNoDenormals noDenormals; auto totalNumInputChannels = getTotalNumInputChannels(); auto totalNumOutputChannels = getTotalNumOutputChannels(); midiMessageCollector.removeNextBlockOfMessages(midiMessages, buffer.getNumSamples()); for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i) buffer.clear(i, 0, buffer.getNumSamples()); audioEngine.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples()); juce::dsp::AudioBlock outputBlock (buffer); outputLimiter.process (juce::dsp::ProcessContextReplacing (outputBlock)); scopeDataCollector.process(buffer.getReadPointer(0), (size_t)buffer.getNumSamples()); } //============================================================================== bool NeuralSynthAudioProcessor::hasEditor() const { return true; } juce::AudioProcessorEditor* NeuralSynthAudioProcessor::createEditor() { return new NeuralSynthAudioProcessorEditor (*this); } //============================================================================== void NeuralSynthAudioProcessor::getStateInformation (juce::MemoryBlock& destData) { juce::ignoreUnused(destData); } void NeuralSynthAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { juce::ignoreUnused(data, sizeInBytes); } 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); currentPresetIndex = -1; } } //============================================================================== // This creates new instances of the plugin.. juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new NeuralSynthAudioProcessor(); } void NeuralSynthAudioProcessor::buildParams(std::vector>& params, const std::string& paramGroup) { const auto& paramGroupSettings = PARAM_SETTINGS.at(paramGroup); for (const auto& [name, s] : paramGroupSettings) { params.push_back(std::make_unique( paramGroup + "_" + name, s.label, juce::NormalisableRange(s.min, s.max, s.interval), s.defValue)); } } std::vector NeuralSynthAudioProcessor::makeFactoryPresets() { std::vector presets; const auto& wtLibrary = WT::FactoryLibrary::get(); if (wtLibrary.empty()) return presets; presets.reserve(220); std::map> categoryToIndices; for (int i = 0; i < (int) wtLibrary.size(); ++i) categoryToIndices[wtLibrary[(size_t) i].category].push_back(i); const std::array requestedCategories = { "Electric Piano", "Organ", "Bass", "Drums", "Strings", "Brass", "Choir", "Pad", "SFX", "Lead", "Pluck", "Misc" }; static const std::array, 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 gmInfo { 0, "" }; if (isDrumCategory) gmInfo = gmDrumInfo[(size_t) variant]; std::map 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 guard(presetChangeInProgress, true, false); for (const auto& entry : preset.parameterValues) setParameterValue(entry.first, entry.second); currentPresetIndex = index; } juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::createParameterLayout() { std::vector> params; params.push_back(std::make_unique( "wt_on", "Layer A On", true)); params.push_back(std::make_unique( "wt2_on", "Layer B On", false)); // Per-panel bypass toggles (default OFF) params.push_back(std::make_unique("chorus_on", "Chorus On", false)); params.push_back(std::make_unique("delay_on", "Delay On", false)); params.push_back(std::make_unique("reverb_on", "Reverb On", false)); params.push_back(std::make_unique("flanger_on", "Flanger On", false)); params.push_back(std::make_unique("distortion_on", "Distortion On", false)); params.push_back(std::make_unique("filter_on", "Filter On", false)); params.push_back(std::make_unique("eq_on", "EQ On", false)); buildParams(params, "adsr"); buildParams(params, "fenv"); buildParams(params, "chorus"); buildParams(params, "delay"); buildParams(params, "reverb"); buildParams(params, "flanger"); buildParams(params, "distortion"); buildParams(params, "filter"); buildParams(params, "wt"); buildParams(params, "wt2"); params.push_back(std::make_unique("master", "Master", juce::NormalisableRange(-24.0f, 24.0f, 0.1f), -6.0f)); params.push_back(std::make_unique("lowEQ", "Low Gain", juce::NormalisableRange(-24.0f, 24.0f, 0.1f), 0.5f)); params.push_back(std::make_unique("midEQ", "Mid EQ", juce::NormalisableRange(-24.0f, 24.0f, 0.1f), 0.8f)); params.push_back(std::make_unique("highEQ", "High EQ", juce::NormalisableRange(-24.0f, 24.0f, 0.1f), 1.0f)); return { params.begin(), params.end() }; }