Files
NeuralSynthEd/DrawWavesAndRevisedAudio/PluginProcessor.h

144 lines
5.5 KiB
C++

#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;
};