xtc/examples/juce/Processor.h

66 lines
3.2 KiB
C
Raw Normal View History

2026-11-06 18:29:35 +01:00
#pragma once
#include <juce_audio_utils/juce_audio_utils.h>
#include <juce_dsp/juce_dsp.h>
#include "xtc/xtc_plugin.h"
#include <array>
#include <atomic>
// JUCE owns host integration/UI/DSP; synthesis remains behind xtc's C API.
class XtcJuceProcessor final : public juce::AudioProcessor {
public:
XtcJuceProcessor();
~XtcJuceProcessor() override;
const juce::String getName() const override { return "xtc JUCE Synth"; }
void prepareToPlay(double, int) override;
void releaseResources() override;
bool isBusesLayoutSupported(const BusesLayout&) const override;
void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
using juce::AudioProcessor::processBlock;
void reset() override;
bool acceptsMidi() const override { return true; }
bool producesMidi() const override { return false; }
double getTailLengthSeconds() const override { return 3.0; }
bool hasEditor() const override { return true; }
juce::AudioProcessorEditor* createEditor() override;
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram(int) override {}
const juce::String getProgramName(int) override { return "Default"; }
void changeProgramName(int, const juce::String&) override {}
void getStateInformation(juce::MemoryBlock&) override;
void setStateInformation(const void*, int) override;
bool restorePreset(const void*, int);
juce::AudioProcessorValueTreeState parameters;
// One GUI producer; one processBlock consumer. Never calls MidiKeyboardState
// or its locks on the audio thread. Overflow requests panic instead of stuck notes.
void enqueueNote(bool on, int channel, int note, float velocity) noexcept;
void panic() noexcept { panicRequested.store(true, std::memory_order_release); }
float peak() const noexcept { return meter.load(std::memory_order_relaxed); }
unsigned int droppedEvents() const noexcept { return dropped.load(std::memory_order_relaxed); }
bool ready() const noexcept { return prepared.load(std::memory_order_acquire); }
bool processingFailed() const noexcept { return failed.load(std::memory_order_relaxed); }
private:
static juce::AudioProcessorValueTreeState::ParameterLayout layout();
static constexpr int chunkFrames = 256, queueCapacity = 128;
xtc_plugin* synth = nullptr;
std::array<float, chunkFrames> scratch{};
std::array<xtc_midi_event, XTC_MIDI_MAX_EVENTS> events{};
std::array<xtc_midi_event, queueCapacity> keyboardEvents{};
juce::AbstractFifo keyboardQueue{queueCapacity};
juce::dsp::StateVariableTPTFilter<float> filter;
juce::dsp::Reverb reverb;
juce::dsp::Gain<float> gain;
std::atomic<float>* gainDb = nullptr;
std::atomic<float>* cutoff = nullptr;
std::atomic<float>* room = nullptr;
std::atomic<float>* wet = nullptr;
std::atomic<float>* effects = nullptr;
std::atomic<float> meter{0};
std::atomic<unsigned int> dropped{0};
std::atomic<bool> panicRequested{false}, prepared{false}, failed{false};
double rate = 48000;
uint64_t position = 0;
void discardKeyboard() noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(XtcJuceProcessor)
};