Upload files to "DrawWavesAndRevisedAudio"
This commit is contained in:
143
DrawWavesAndRevisedAudio/PluginProcessor.h
Normal file
143
DrawWavesAndRevisedAudio/PluginProcessor.h
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <complex>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// A wave frame: 2048 samples with six mip levels (0 = full bandwidth)
|
||||||
|
struct WavetableFrame
|
||||||
|
{
|
||||||
|
std::array<std::vector<float>, 6> mip;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A complete morph set contains 16 frames
|
||||||
|
struct WaveMorph
|
||||||
|
{
|
||||||
|
std::array<WavetableFrame, 16> frames;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
class WavetableSynthAudioProcessor : public juce::AudioProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// -------- engine configuration ----------
|
||||||
|
static constexpr int kBrowserCols = 4;
|
||||||
|
static constexpr int kBrowserRows = 10;
|
||||||
|
static constexpr int kBrowserCapacity = kBrowserCols * kBrowserRows; // 40
|
||||||
|
static constexpr int kFactorySlots = 20; // first 20 fixed
|
||||||
|
static constexpr int kMorphFrames = 16;
|
||||||
|
static constexpr int kMipLevels = 6;
|
||||||
|
static constexpr int kTableSize = 2048;
|
||||||
|
|
||||||
|
// --- minimal editor accessors (public) -------------------------------------
|
||||||
|
int getBrowserCols() const noexcept { return kBrowserCols; }
|
||||||
|
int getBrowserRows() const noexcept { return kBrowserRows; }
|
||||||
|
int getWaveTableCount() const noexcept { return (int) waves.size(); }
|
||||||
|
const std::vector<float>* getWaveTablePtr (int index) const noexcept
|
||||||
|
{
|
||||||
|
// Preview uses the first frame at the widest mip level.
|
||||||
|
return (index >= 0 && index < (int) waves.size())
|
||||||
|
? &waves[(size_t) index].frames[0].mip[0] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- juce plumbing ----------
|
||||||
|
WavetableSynthAudioProcessor();
|
||||||
|
~WavetableSynthAudioProcessor() override = default;
|
||||||
|
|
||||||
|
// AudioProcessor
|
||||||
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
||||||
|
void releaseResources() override {}
|
||||||
|
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
juce::AudioProcessorEditor* createEditor() override;
|
||||||
|
bool hasEditor() const override { return true; }
|
||||||
|
|
||||||
|
// Meta
|
||||||
|
const juce::String getName() const override { return "WavetableSynth"; }
|
||||||
|
bool acceptsMidi() const override { return true; }
|
||||||
|
bool producesMidi() const override { return false; }
|
||||||
|
bool isMidiEffect() const override { return false; }
|
||||||
|
double getTailLengthSeconds() const override { return 0.0; }
|
||||||
|
|
||||||
|
// Programs (unused)
|
||||||
|
int getNumPrograms() override { return 1; }
|
||||||
|
int getCurrentProgram() override { return 0; }
|
||||||
|
void setCurrentProgram (int) override {}
|
||||||
|
const juce::String getProgramName (int) override { return {}; }
|
||||||
|
void changeProgramName (int, const juce::String&) override {}
|
||||||
|
|
||||||
|
// State
|
||||||
|
void getStateInformation (juce::MemoryBlock& destData) override;
|
||||||
|
void setStateInformation (const void* data, int sizeInBytes) override;
|
||||||
|
|
||||||
|
// Parameters
|
||||||
|
juce::AudioProcessorValueTreeState apvts;
|
||||||
|
static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
||||||
|
|
||||||
|
// -------- public data used by editor ----------
|
||||||
|
std::vector<WaveMorph> waves; // size <= 40; first 20 are factory
|
||||||
|
int defaultTableCount { kFactorySlots };
|
||||||
|
int nextUserInsert { 0 }; // round-robin inside user region
|
||||||
|
|
||||||
|
// For the editor thumbnails (take the widest-band level 0)
|
||||||
|
const std::vector<float>* getPreviewTablePtr (int index) const;
|
||||||
|
|
||||||
|
// API for editor to push user waves (single cycle, any shape)
|
||||||
|
// Internally this builds a mip set by partial truncation.
|
||||||
|
int addOrReplaceUserWavetable (const std::vector<float>& singleCycle01);
|
||||||
|
void notifyPresetLoaded();
|
||||||
|
float getMorphDisplayValue() const noexcept { return morphDisplay.load (std::memory_order_relaxed); }
|
||||||
|
bool isMorphLoopActive() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ---------- synthesis core ----------
|
||||||
|
juce::Synthesiser synth;
|
||||||
|
std::vector<float> morphBuffer;
|
||||||
|
float morphState { 0.0f };
|
||||||
|
float morphLoopPhase { 0.0f };
|
||||||
|
int morphLoopDirection { 1 };
|
||||||
|
int morphLoopStage { 0 };
|
||||||
|
float morphLoopStagePhase { 0.0f };
|
||||||
|
std::atomic<float> morphDisplay { 0.0f };
|
||||||
|
juce::LinearSmoothedValue<float> presetFade { 1.0f };
|
||||||
|
|
||||||
|
juce::dsp::Chorus<float> chorus;
|
||||||
|
juce::dsp::Reverb reverb;
|
||||||
|
juce::dsp::Reverb::Parameters reverbParams;
|
||||||
|
|
||||||
|
// -------- wave construction helpers ----------
|
||||||
|
static void normalize (std::vector<float>& t);
|
||||||
|
static void addSine (std::vector<float>& t, int harmonic, float amp);
|
||||||
|
static void removeDC (std::vector<float>& t);
|
||||||
|
static void enforceZeroStart (std::vector<float>& t);
|
||||||
|
static WaveMorph buildAdditiveMorph (std::function<float(int)> ampFn,
|
||||||
|
bool oddOnly = false,
|
||||||
|
float phaseAlt = +1.0f);
|
||||||
|
static WaveMorph makeSine();
|
||||||
|
static WaveMorph makeSaw();
|
||||||
|
static WaveMorph makeSquare();
|
||||||
|
static WaveMorph makeTriangle();
|
||||||
|
static WaveMorph makePulse (float duty); // 0<d<1
|
||||||
|
static WaveMorph makeEven();
|
||||||
|
static WaveMorph makeOdd();
|
||||||
|
static WaveMorph makeHalfSineRect();
|
||||||
|
static WaveMorph makeBell();
|
||||||
|
static WaveMorph makeOrgan();
|
||||||
|
void buildFactoryWaves();
|
||||||
|
|
||||||
|
// choose mip level for a given fundamental
|
||||||
|
int chooseMipLevel (float fundamentalHz) const;
|
||||||
|
|
||||||
|
const WaveMorph* getWavePtr (int index) const;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavetableSynthAudioProcessor)
|
||||||
|
|
||||||
|
friend class WavetableVoice;
|
||||||
|
};
|
||||||
331
DrawWavesAndRevisedAudio/PresetsCode.cpp
Normal file
331
DrawWavesAndRevisedAudio/PresetsCode.cpp
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace PresetsCode
|
||||||
|
{
|
||||||
|
struct Preset { juce::String name; std::map<juce::String, float> values; };
|
||||||
|
struct Category{ juce::String name; std::vector<Preset> presets; };
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
constexpr int presetsPerCategory = 30;
|
||||||
|
constexpr int modeVariantCount = presetsPerCategory / 2;
|
||||||
|
|
||||||
|
using SlotSet = std::array<int, 3>;
|
||||||
|
using SlotTriplet = std::array<SlotSet, 3>;
|
||||||
|
|
||||||
|
struct GMProfile
|
||||||
|
{
|
||||||
|
juce::String name;
|
||||||
|
float master;
|
||||||
|
float morph;
|
||||||
|
float cutoff;
|
||||||
|
float attack;
|
||||||
|
float decay;
|
||||||
|
float sustain;
|
||||||
|
float release;
|
||||||
|
float lfoRate;
|
||||||
|
float lfoDepth;
|
||||||
|
float fEnvAmount;
|
||||||
|
float fxMix;
|
||||||
|
SlotTriplet slotSets;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CategoryNameSet
|
||||||
|
{
|
||||||
|
std::array<const char*, modeVariantCount> solo;
|
||||||
|
std::array<const char*, modeVariantCount> layered;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::array<CategoryNameSet, 16> nameSets
|
||||||
|
{{
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Concert Grand", "Studio Upright", "Warm Parlor", "Bright Stage", "Dusty Keys",
|
||||||
|
"Midnight Felt", "Glass Hammer", "Sunlit Room", "Jazz Touch", "Ringing Octaves",
|
||||||
|
"Soft Pedal", "Pop Punch", "Wide Hammer", "Twilight Piano", "Crystal Keys" },
|
||||||
|
{ "Grand Stack", "Hammer Chorus", "Layered Uprights", "Glassy Spread", "Warm Hall Blend",
|
||||||
|
"Hybrid Grand", "Bright Layer", "Felt Cloud", "Cinema Piano", "Dream Keys",
|
||||||
|
"Bell Piano", "Wide Hammered", "Velvet Layers", "Studio Swirl", "Concert Bloom" }
|
||||||
|
}, // Piano
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Solo Vibraphone", "Celesta Light", "Glassy Chimes", "Soft Mallets", "Toy Box",
|
||||||
|
"Crystal Bars", "Marimba Glow", "Bell Cascade", "Clav Accent", "Metal Steps",
|
||||||
|
"Trem Vibra", "Dry Mallet", "Shimmer Keys", "Clockwork", "Glass Struck" },
|
||||||
|
{ "Mallet Ensemble", "Celesta Pad", "Chime Stack", "Bell Layers", "Crystal Curtain",
|
||||||
|
"Hybrid Mallets", "Wide Glock", "Mallet Dream", "Spectral Bells", "Golden Bars",
|
||||||
|
"Bright Resonance", "Airy Mallets", "Wide Celesta", "Fantasy Chimes", "Silver Rain" }
|
||||||
|
}, // Chromatic Percussion
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Pure Drawbar", "Chapel Soft", "Perc Click", "Full Stops", "Hollow Tone",
|
||||||
|
"Jazz Wheel", "Old Console", "Far Bar", "Vox Accent", "Tremolo Pipes",
|
||||||
|
"Slow Leslie", "Rock Bars", "Warm Console", "Combo Organ", "Ancient Reed" },
|
||||||
|
{ "Twin Drawbars", "Cathedral Stack", "Rotary Blend", "Organ Swell", "Thick Console",
|
||||||
|
"Wide Vox", "Dual Chapel", "Gospel Layers", "Airy Pipes", "Hybrid Harm",
|
||||||
|
"Bright Rotary", "Deep Drawbar", "Vintage Stack", "Layered Stops", "Electric Chapel" }
|
||||||
|
}, // Organ
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Clean Pluck", "Bright Strum", "Muted Pick", "Hollow Body", "Steel Shimmer",
|
||||||
|
"Warm Nylon", "Slide Touch", "Jazz Octave", "Edge Pluck", "Dry Funk",
|
||||||
|
"Airy Harmonics", "Silver Strings", "Wooden Pick", "Folk Finger", "Soft Trem" },
|
||||||
|
{ "Guitar Stack", "Stereo Strum", "Pluck Ensemble", "Wide Harmonics", "Chorus Nylon",
|
||||||
|
"Electric Blend", "Picked Layers", "Dream Guitar", "Shimmer Strings", "Deep Strummer",
|
||||||
|
"Twin Slides", "Wide Trem", "Hybrid Pluck", "Sparkle Guitar", "Ambient Strum" }
|
||||||
|
}, // Guitar
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Fingered Bass", "Picked Attack", "Deep Sub", "Warm Round", "Clav Bass",
|
||||||
|
"Snappy Slap", "Muted Thump", "Synth Pluck", "Punchy Low", "Airless Bass",
|
||||||
|
"Moog Tone", "Dry Root", "Soft Sub", "Acoustic Body", "Neo Bass" },
|
||||||
|
{ "Layered Low", "Sub Stack", "Punch Blend", "Bass Split", "Wide Slap",
|
||||||
|
"Deep Synth", "Hybrid Bass", "Chorus Low", "Twin Thump", "Boom Texture",
|
||||||
|
"Bass Ensemble", "Stereo Sub", "Growl Stack", "Fundamental Wall", "Airy Sub" }
|
||||||
|
}, // Bass
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Solo Violin", "Warm Viola", "Cello Bow", "Silk Section", "Muted Strings",
|
||||||
|
"Spiccato", "Wide Legato", "Bright Ensemble", "Soft Layer", "Orchestral Pad",
|
||||||
|
"Tremolo Lace", "Vintage Strings", "Glass Harmonics", "Warm Stack", "Solo Cello" },
|
||||||
|
{ "String Layers", "Cinematic Swell", "Wide Arcs", "Orchestral Cloud", "Hybrid Strings",
|
||||||
|
"Chamber Blend", "Symphonic Rise", "Dream Swell", "Shimmer Strings", "Soft Orchestra",
|
||||||
|
"Glass Orchestra", "Air Ensemble", "Epic Layer", "Velvet Section", "Neo Strings" }
|
||||||
|
}, // Strings
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Light Ensemble", "Soft Choir", "Mixed Section", "Warm Stack", "Bright Choir",
|
||||||
|
"Octave Layers", "Air Voices", "Analog Ensemble", "Voiced Pad", "Stacked Strings",
|
||||||
|
"Gentle Chorus", "Full Section", "Ethereal Group", "Pop Chorus", "Glass Ensemble" },
|
||||||
|
{ "Cathedral Ensemble", "Choir Spread", "Hybrid Voices", "Epic Stack", "Layered Choir",
|
||||||
|
"Wide Ensemble", "Dream Choir", "Symphonic Pad", "Lush Assembly", "Stereo Ensemble",
|
||||||
|
"Glorious Layer", "Angelic Swell", "Velvet Choir", "Bright Assembly", "Atmos Ensemble" }
|
||||||
|
}, // Ensemble
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Solo Trumpet", "Muted Brass", "Warm Horn", "Bright Brass", "Soft Flugel",
|
||||||
|
"Tenor Bones", "Sharp Brass", "French Stack", "Hero Lead", "Vintage Brass",
|
||||||
|
"Orchestral Horn", "Fan Brass", "Soft Cornet", "Bold Bones", "Hybrid Brass" },
|
||||||
|
{ "Brass Wall", "Epic Horns", "Stadium Brass", "Layered Trumpets", "Octave Brass",
|
||||||
|
"Brass Ensemble", "Hero Stack", "Warm Brass Pad", "Cinema Brass", "Wide Brass",
|
||||||
|
"Glory Brass", "Tone Tower", "Bright Brass Pad", "Deep Horn Stack", "Hybrid Fanfare" }
|
||||||
|
}, // Brass
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Solo Clarinet", "Warm Sax", "Bright Reed", "Soft Oboe", "Jazz Alto",
|
||||||
|
"Bassoon Tone", "Smooth Tenor", "Piercing Reed", "Dry Clar", "Wide Reed",
|
||||||
|
"Dark Oboe", "Airy Bassoon", "Pop Sax", "Velvet Reed", "Vintage Clar" },
|
||||||
|
{ "Reed Ensemble", "Twin Sax", "Clarinet Choir", "Hybrid Reed", "Smooth Section",
|
||||||
|
"Layered Oboes", "Wide Reed Pad", "Cinema Reed", "Stacked Sax", "Woodwind Wall",
|
||||||
|
"Airy Reed", "Jazz Stack", "Bright Reeds", "Shadow Reed", "Velvet Woodwind" }
|
||||||
|
}, // Reed
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Concert Flute", "Breathy Flute", "Piccolo Star", "Soft Recorder", "Glass Flute",
|
||||||
|
"Warm Alto", "Fife Tone", "Wind Whisper", "Sharp Pipe", "Native Flute",
|
||||||
|
"Bright Piccolo", "Calm Pipes", "Open Whistle", "Night Flute", "Ancient Pipe" },
|
||||||
|
{ "Pipe Ensemble", "Choir Flutes", "Layered Wind", "Dream Flutes", "Wide Pipes",
|
||||||
|
"Ethereal Flutes", "Airy Stack", "Silver Cascade", "Mystic Flutes", "Breath Layers",
|
||||||
|
"Stereo Pipes", "Hybrid Flutes", "Clouded Pipes", "Shining Wind", "Sky Columns" }
|
||||||
|
}, // Pipe
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Blade Lead", "Sync Razor", "Analog Sizzle", "Digital Edge", "Saw Glide",
|
||||||
|
"Pulse Sprint", "FM Piercer", "Reso Runner", "Mono Glide", "Square Force",
|
||||||
|
"Acid Beam", "Metal Lead", "Chiptone", "Warm Solo", "Vox Glide" },
|
||||||
|
{ "Twin Blades", "Stereo Lead", "Octave Runner", "Layered Saw", "Wide Screamer",
|
||||||
|
"Poly Stack", "Hybrid Rush", "Air Lead", "Reso Layers", "Arena Solo",
|
||||||
|
"Neon Drive", "Cosmic Lead", "Lucid Glide", "Turbo Layers", "Sky Blade" }
|
||||||
|
}, // Synth Lead
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Warm Blanket", "Glass Pad", "Analog Mist", "Digital Halo", "Soft Spread",
|
||||||
|
"Bright Pad", "Choral Sweep", "Nocturne Pad", "Vintage Pad", "FM Veil",
|
||||||
|
"Dusty Pad", "Air Sweep", "Silk Bed", "Calm Horizon", "Wide Prism" },
|
||||||
|
{ "Pad Cathedral", "Luminous Pad", "Dream Orbit", "Velvet Layers", "Motion Cloud",
|
||||||
|
"Evolving Pad", "Glass Ensemble", "Solar Drift", "Shimmer Sky", "Deep Nebula",
|
||||||
|
"Aurora Pad", "Liquid Glow", "Expanse Pad", "Twin Horizons", "Ethereal Bloom" }
|
||||||
|
}, // Synth Pad
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Rise Sweep", "Noise Hit", "Circuit Zap", "Reso Drop", "Granular Wash",
|
||||||
|
"Bit Spark", "Gated Rush", "Pulse Radar", "Morph Click", "Glitch Line",
|
||||||
|
"Bleed Pad", "Searing Rise", "Swarm FX", "Vapor Hit", "Spectra Pop" },
|
||||||
|
{ "FX Cascade", "Binary Storm", "Shimmer FX", "Impact Stack", "Laser Field",
|
||||||
|
"Wave Crash", "Dual Sweep", "Horizon FX", "Voltage Rain", "Sky Burst",
|
||||||
|
"Nebula FX", "Falling Stars", "Circuit Bloom", "Phase Impact", "Aurora FX" }
|
||||||
|
}, // Synth Effects
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Sitar Pluck", "Shamisen Tone", "Kalimba Bell", "Gamelan Fan", "Erhu Bow",
|
||||||
|
"Santur Ring", "Kora Waves", "Baglama Run", "Oud Mellow", "Bansuri Air",
|
||||||
|
"Tin Whistle", "Didgeridoo Drone", "Taiko Rim", "Ngoni Pulse", "Guqin Flow" },
|
||||||
|
{ "World Layers", "Tribal Echo", "Desert Wind", "Eastern Stack", "Cultural Bloom",
|
||||||
|
"Panorama Folk", "Nomad Chorus", "Festival Mix", "Temple Air", "Jungle Drift",
|
||||||
|
"Azure Bazaar", "Wide Kalimba", "Storm Ritual", "Mystic Caravan", "Earth Ensemble" }
|
||||||
|
}, // Ethnic
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Analog Kick", "Tight Snare", "Electro Tom", "Clack Hit", "Wood Block",
|
||||||
|
"Glass Ping", "Tribal Drum", "Metal Knock", "Click Perc", "Pop Rim",
|
||||||
|
"Snap Clap", "Deep Boom", "Air Hit", "Short Noise", "Soft Knock" },
|
||||||
|
{ "Perc Stack", "Rhythm Wall", "Hybrid Drum", "Layered Hit", "Wide Impact",
|
||||||
|
"Perc Texture", "Motion Perc", "Tribal Layers", "Circuit Beat", "Wide Clap",
|
||||||
|
"Perc Storm", "Drum Machine", "Stage Impact", "Thunder Perc", "Break Stack" }
|
||||||
|
}, // Percussive
|
||||||
|
CategoryNameSet{
|
||||||
|
{ "Wind Rush", "Ocean Drone", "Metallic Fall", "SciFi Ping", "Air Sweep",
|
||||||
|
"Machine Loop", "Space Beep", "Rumble FX", "Glass Growl", "Signal Warp",
|
||||||
|
"Foley Hit", "Distant Boom", "Echo Drops", "Industrial Spin", "Night Noise" },
|
||||||
|
{ "FX Universe", "Storm Front", "Space Layers", "Dread Drones", "Cinematic Field",
|
||||||
|
"Signal Swarm", "Nightscape", "Future Machine", "Thunder Field", "Layered Noise",
|
||||||
|
"Alien Atmos", "Mystery Floor", "Flash Impact", "Vortex FX", "Deep Horizon" }
|
||||||
|
} // Sound Effects
|
||||||
|
}};
|
||||||
|
|
||||||
|
inline juce::String makePresetName (const GMProfile& profile,
|
||||||
|
size_t profileIndex,
|
||||||
|
int variantIndex,
|
||||||
|
bool bitimbral)
|
||||||
|
{
|
||||||
|
const int localIndex = bitimbral ? (variantIndex - modeVariantCount) : variantIndex;
|
||||||
|
const auto& descriptorSet = bitimbral ? nameSets[(size_t) profileIndex].layered
|
||||||
|
: nameSets[(size_t) profileIndex].solo;
|
||||||
|
const size_t clamped = (size_t) juce::jlimit (0, (int) descriptorSet.size() - 1, localIndex);
|
||||||
|
return profile.name + " " + juce::String (descriptorSet[clamped]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Preset makePreset (const juce::String& name,
|
||||||
|
const GMProfile& profile,
|
||||||
|
int variantIndex,
|
||||||
|
bool bitimbral)
|
||||||
|
{
|
||||||
|
const float spread = static_cast<float> ((variantIndex % 5) - 2);
|
||||||
|
|
||||||
|
const float master = juce::jlimit (0.50f, 0.95f, profile.master + spread * 0.01f);
|
||||||
|
const float morph = juce::jlimit (0.00f, 1.00f, profile.morph + spread * 0.02f + (bitimbral ? 0.06f : -0.04f));
|
||||||
|
const float cutoff = juce::jlimit (250.0f, 18000.0f, profile.cutoff + 320.0f * static_cast<float> ((variantIndex % 7) - 3));
|
||||||
|
const float attack = juce::jlimit (0.001f, 4.000f, profile.attack * (1.0f + spread * 0.03f));
|
||||||
|
const float decay = juce::jlimit (0.050f, 3.500f, profile.decay * (1.0f + spread * 0.04f));
|
||||||
|
const float sustain = juce::jlimit (0.00f, 1.00f, profile.sustain + spread * 0.02f);
|
||||||
|
const float release = juce::jlimit (0.050f, 4.000f, profile.release * (1.0f + spread * 0.05f));
|
||||||
|
const float lfoRate = juce::jlimit (0.050f, 8.000f, profile.lfoRate + 0.12f * static_cast<float> ((variantIndex % 8) - 3));
|
||||||
|
const float lfoDepth = juce::jlimit (0.00f, 1.00f, profile.lfoDepth + spread * 0.03f);
|
||||||
|
const float fEnvAmt = juce::jlimit (0.10f, 0.90f, profile.fEnvAmount + spread * 0.02f);
|
||||||
|
const float fxMix = juce::jlimit (0.00f, 1.00f, profile.fxMix + spread * 0.04f);
|
||||||
|
|
||||||
|
const float chRate = juce::jlimit (0.20f, 1.80f, 0.55f + fxMix * 0.80f + spread * 0.03f);
|
||||||
|
const float chDepth = juce::jlimit (0.00f, 1.00f, 0.18f + fxMix * 0.55f);
|
||||||
|
const float chDelay = juce::jlimit (5.00f, 20.00f, 8.0f + fxMix * 6.5f + (bitimbral ? 1.5f : -1.0f));
|
||||||
|
const float chFb = juce::jlimit (0.00f, 0.60f, 0.04f + fxMix * 0.14f);
|
||||||
|
const float chMix = juce::jlimit (0.00f, 0.60f, fxMix * 0.45f);
|
||||||
|
|
||||||
|
const float rvRoom = juce::jlimit (0.00f, 1.00f, 0.28f + fxMix * 0.62f);
|
||||||
|
const float rvDamp = juce::jlimit (0.00f, 1.00f, 0.20f + fxMix * 0.40f);
|
||||||
|
const float rvWidth = juce::jlimit (0.00f, 1.00f, 0.82f + fxMix * 0.15f);
|
||||||
|
const float rvWet = juce::jlimit (0.00f, 1.00f, 0.12f + fxMix * 0.36f);
|
||||||
|
|
||||||
|
const auto& slotChoice = profile.slotSets[(size_t) (variantIndex % profile.slotSets.size())];
|
||||||
|
|
||||||
|
std::map<juce::String, float> values
|
||||||
|
{
|
||||||
|
{ "MASTER", master },
|
||||||
|
{ "MORPH", morph },
|
||||||
|
{ "MORPH_LOOP_ON", (bitimbral || (variantIndex % 3 == 0)) ? 1.0f : 0.0f },
|
||||||
|
{ "MORPH_LOOP_MODE", static_cast<float> ((variantIndex / 5) % 3) },
|
||||||
|
{ "CUTOFF", cutoff },
|
||||||
|
{ "ATTACK", attack },
|
||||||
|
{ "DECAY", decay },
|
||||||
|
{ "SUSTAIN", sustain },
|
||||||
|
{ "RELEASE", release },
|
||||||
|
{ "LFO_RATE", lfoRate },
|
||||||
|
{ "LFO_DEPTH", lfoDepth },
|
||||||
|
{ "FENV_A", juce::jlimit (0.001f, 2.000f, attack * 0.55f) },
|
||||||
|
{ "FENV_D", juce::jlimit (0.050f, 2.400f, decay * 1.05f) },
|
||||||
|
{ "FENV_S", juce::jlimit (0.00f, 1.00f, sustain * 0.48f) },
|
||||||
|
{ "FENV_R", juce::jlimit (0.050f, 2.800f, release * 1.15f) },
|
||||||
|
{ "FENV_AMT", fEnvAmt },
|
||||||
|
{ "OSC2_MUTE", bitimbral ? 0.0f : 1.0f },
|
||||||
|
{ "CHORUS_ON", fxMix > 0.32f ? 1.0f : 0.0f },
|
||||||
|
{ "REVERB_ON", fxMix > 0.18f ? 1.0f : 0.0f },
|
||||||
|
{ "CH_RATE", chRate },
|
||||||
|
{ "CH_DEPTH", chDepth },
|
||||||
|
{ "CH_DELAY", chDelay },
|
||||||
|
{ "CH_FB", chFb },
|
||||||
|
{ "CH_MIX", chMix },
|
||||||
|
{ "RV_ROOM", rvRoom },
|
||||||
|
{ "RV_DAMP", rvDamp },
|
||||||
|
{ "RV_WIDTH", rvWidth },
|
||||||
|
{ "RV_WET", rvWet },
|
||||||
|
{ "SLOT_A", (float) slotChoice[0] },
|
||||||
|
{ "SLOT_B", (float) slotChoice[1] },
|
||||||
|
{ "SLOT_C", (float) slotChoice[2] }
|
||||||
|
};
|
||||||
|
|
||||||
|
return { name, std::move (values) };
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// ----------------------- Library -----------------------------------------
|
||||||
|
inline std::vector<Category> getCategories()
|
||||||
|
{
|
||||||
|
using namespace detail;
|
||||||
|
|
||||||
|
const std::array<GMProfile, 16> gmProfiles
|
||||||
|
{{
|
||||||
|
{ "Piano", 0.72f, 0.18f, 6800.0f, 0.020f, 0.280f, 0.65f, 0.320f, 0.40f, 0.22f, 0.35f, 0.28f,
|
||||||
|
SlotTriplet{ SlotSet{ 0, 3, 11 }, SlotSet{ 0, 9, 15 }, SlotSet{ 0, 3, 19 } } },
|
||||||
|
{ "Chromatic Percussion",0.68f, 0.22f, 7200.0f, 0.012f, 0.240f, 0.50f, 0.300f, 0.45f, 0.30f, 0.42f, 0.24f,
|
||||||
|
SlotTriplet{ SlotSet{ 11, 12, 14 }, SlotSet{ 11, 9, 13 }, SlotSet{ 11, 12, 5 } } },
|
||||||
|
{ "Organ", 0.70f, 0.55f, 8400.0f, 0.040f, 0.480f, 0.80f, 0.600f, 0.90f, 0.40f, 0.28f, 0.35f,
|
||||||
|
SlotTriplet{ SlotSet{ 10, 7, 0 }, SlotSet{ 10, 1, 2 }, SlotSet{ 10, 5, 8 } } },
|
||||||
|
{ "Guitar", 0.66f, 0.32f, 6200.0f, 0.015f, 0.450f, 0.55f, 0.450f, 0.65f, 0.35f, 0.40f, 0.30f,
|
||||||
|
SlotTriplet{ SlotSet{ 3, 4, 12 }, SlotSet{ 3, 6, 1 }, SlotSet{ 3, 4, 9 } } },
|
||||||
|
{ "Bass", 0.75f, 0.25f, 2800.0f, 0.008f, 0.300f, 0.78f, 0.220f, 0.30f, 0.25f, 0.55f, 0.18f,
|
||||||
|
SlotTriplet{ SlotSet{ 0, 6, 4 }, SlotSet{ 0, 5, 12 }, SlotSet{ 0, 6, 14 } } },
|
||||||
|
{ "Strings", 0.72f, 0.48f, 5600.0f, 0.180f, 0.850f, 0.70f, 1.100f, 0.28f, 0.60f, 0.36f, 0.45f,
|
||||||
|
SlotTriplet{ SlotSet{ 3, 11, 15 }, SlotSet{ 3, 9, 18 }, SlotSet{ 3, 11, 0 } } },
|
||||||
|
{ "Ensemble", 0.74f, 0.50f, 5400.0f, 0.200f, 0.900f, 0.72f, 1.400f, 0.32f, 0.58f, 0.40f, 0.48f,
|
||||||
|
SlotTriplet{ SlotSet{ 11, 1, 0 }, SlotSet{ 11, 18, 15 }, SlotSet{ 11, 7, 3 } } },
|
||||||
|
{ "Brass", 0.70f, 0.42f, 7800.0f, 0.060f, 0.550f, 0.68f, 0.500f, 0.45f, 0.30f, 0.50f, 0.30f,
|
||||||
|
SlotTriplet{ SlotSet{ 2, 10, 15 }, SlotSet{ 2, 11, 1 }, SlotSet{ 2, 9, 13 } } },
|
||||||
|
{ "Reed", 0.68f, 0.34f, 7600.0f, 0.050f, 0.480f, 0.65f, 0.580f, 0.52f, 0.38f, 0.44f, 0.28f,
|
||||||
|
SlotTriplet{ SlotSet{ 8, 10, 0 }, SlotSet{ 8, 9, 3 }, SlotSet{ 8, 5, 11 } } },
|
||||||
|
{ "Pipe", 0.67f, 0.38f, 7200.0f, 0.100f, 0.600f, 0.70f, 0.900f, 0.40f, 0.32f, 0.36f, 0.32f,
|
||||||
|
SlotTriplet{ SlotSet{ 0, 9, 11 }, SlotSet{ 0, 9, 18 }, SlotSet{ 0, 9, 5 } } },
|
||||||
|
{ "Synth Lead", 0.73f, 0.62f, 12800.0f, 0.015f, 0.320f, 0.78f, 0.260f, 1.20f, 0.55f, 0.48f, 0.38f,
|
||||||
|
SlotTriplet{ SlotSet{ 1, 2, 5 }, SlotSet{ 1, 14, 6 }, SlotSet{ 1, 2, 13 } } },
|
||||||
|
{ "Synth Pad", 0.76f, 0.58f, 6500.0f, 0.350f, 1.100f, 0.75f, 2.100f, 0.22f, 0.60f, 0.40f, 0.55f,
|
||||||
|
SlotTriplet{ SlotSet{ 15, 18, 11 }, SlotSet{ 15, 19, 0 }, SlotSet{ 15, 9, 3 } } },
|
||||||
|
{ "Synth Effects", 0.68f, 0.70f, 9000.0f, 0.080f, 1.400f, 0.40f, 1.800f, 0.90f, 0.70f, 0.62f, 0.60f,
|
||||||
|
SlotTriplet{ SlotSet{ 5, 11, 1 }, SlotSet{ 14, 19, 2 }, SlotSet{ 6, 11, 12 } } },
|
||||||
|
{ "Ethnic", 0.69f, 0.40f, 6800.0f, 0.030f, 0.650f, 0.58f, 0.700f, 0.58f, 0.36f, 0.42f, 0.34f,
|
||||||
|
SlotTriplet{ SlotSet{ 11, 3, 12 }, SlotSet{ 11, 9, 4 }, SlotSet{ 11, 3, 0 } } },
|
||||||
|
{ "Percussive", 0.71f, 0.28f, 5200.0f, 0.012f, 0.420f, 0.30f, 0.380f, 0.75f, 0.44f, 0.60f, 0.26f,
|
||||||
|
SlotTriplet{ SlotSet{ 5, 12, 14 }, SlotSet{ 5, 13, 4 }, SlotSet{ 5, 9, 6 } } },
|
||||||
|
{ "Sound Effects", 0.65f, 0.75f, 9900.0f, 0.050f, 1.600f, 0.35f, 2.400f, 1.10f, 0.80f, 0.70f, 0.65f,
|
||||||
|
SlotTriplet{ SlotSet{ 11, 5, 1 }, SlotSet{ 11, 6, 2 }, SlotSet{ 19, 14, 1 } } }
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::vector<Category> cats;
|
||||||
|
cats.reserve (gmProfiles.size());
|
||||||
|
|
||||||
|
for (size_t profileIndex = 0; profileIndex < gmProfiles.size(); ++profileIndex)
|
||||||
|
{
|
||||||
|
const auto& profile = gmProfiles[(size_t) profileIndex];
|
||||||
|
Category category;
|
||||||
|
category.name = profile.name;
|
||||||
|
category.presets.reserve (presetsPerCategory);
|
||||||
|
|
||||||
|
for (int variant = 0; variant < presetsPerCategory; ++variant)
|
||||||
|
{
|
||||||
|
const bool bitimbral = variant >= modeVariantCount;
|
||||||
|
const auto presetName = makePresetName (profile, profileIndex, variant, bitimbral);
|
||||||
|
|
||||||
|
category.presets.push_back (makePreset (presetName, profile, variant, bitimbral));
|
||||||
|
}
|
||||||
|
|
||||||
|
cats.push_back (std::move (category));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a preset to the APVTS (safe: uses convertTo0to1)
|
||||||
|
inline void loadPreset (juce::AudioProcessorValueTreeState& apvts, const Preset& p)
|
||||||
|
{
|
||||||
|
for (const auto& kv : p.values)
|
||||||
|
if (auto* prm = apvts.getParameter (kv.first))
|
||||||
|
prm->setValueNotifyingHost (prm->convertTo0to1 (kv.second));
|
||||||
|
}
|
||||||
|
} // namespace PresetsCode
|
||||||
Reference in New Issue
Block a user