#pragma once #include #include #include #include #include #include #include // ============================================================ // A wave frame: 2048 samples with six mip levels (0 = full bandwidth) struct WavetableFrame { std::array, 6> mip; }; // A complete morph set contains 16 frames struct WaveMorph { std::array 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* 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&, 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 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* 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& 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 morphBuffer; float morphState { 0.0f }; float morphLoopPhase { 0.0f }; int morphLoopDirection { 1 }; int morphLoopStage { 0 }; float morphLoopStagePhase { 0.0f }; std::atomic morphDisplay { 0.0f }; juce::LinearSmoothedValue presetFade { 1.0f }; juce::dsp::Chorus chorus; juce::dsp::Reverb reverb; juce::dsp::Reverb::Parameters reverbParams; // -------- wave construction helpers ---------- static void normalize (std::vector& t); static void addSine (std::vector& t, int harmonic, float amp); static void removeDC (std::vector& t); static void enforceZeroStart (std::vector& t); static WaveMorph buildAdditiveMorph (std::function 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