#pragma once #include #include #include #include "DSP/Voice.h" #include "DSP/Distortion.h" #include "DSP/Compressor.h" #include "DSP/Delay.h" #include "DSP/Reverb.h" #include "PresetManager.h" #include #include const juce::StringArray& getSemitoneChoices(); class ChromaFlockProcessor : public juce::AudioProcessor { public: ChromaFlockProcessor(); ~ChromaFlockProcessor() override; void prepareToPlay(double sampleRate, int samplesPerBlock) override; void releaseResources() override; void processBlock(juce::AudioBuffer&, juce::MidiBuffer&) override; juce::AudioProcessorEditor* createEditor() override; bool hasEditor() const override { return true; } const juce::String getName() const override { return "ChromaFlock"; } 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; } 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 {} void getStateInformation(juce::MemoryBlock& destData) override; void setStateInformation(const void* data, int sizeInBytes) override; juce::AudioProcessorValueTreeState apvts; PresetManager presetManager; static constexpr int scopeBufferSize = 1024; std::array scopeBuffer{}; std::atomic scopeWritePos{0}; static constexpr int fftOrder = 11; static constexpr int fftSize = 1 << fftOrder; std::array fftInput{}; std::atomic fftWritePos{0}; float getRmsLevel() const { return rmsLevel.load(); } void noteOn(int midiNote, float velocity) { if (midiNote >= 0 && midiNote < 128) activeNotes[midiNote].store(true, std::memory_order_relaxed); synth.noteOn(1, juce::jlimit(0, 127, midiNote + getTransposeSemitones()), velocity); } void noteOff(int midiNote) { if (midiNote >= 0 && midiNote < 128) activeNotes[midiNote].store(false, std::memory_order_relaxed); synth.noteOff(1, juce::jlimit(0, 127, midiNote + getTransposeSemitones()), 0.0f, true); } bool isNoteActive(int midiNote) const { return midiNote >= 0 && midiNote < 128 && activeNotes[midiNote].load(std::memory_order_relaxed); } int getTransposeSemitones() const; std::atomic pitchBendValue{0.0f}; float getPitchBend() const { return pitchBendValue.load(std::memory_order_relaxed); } private: juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); void updateVoiceParameters(); juce::Synthesiser synth; static constexpr int maxVoices = 16; Distortion distortion; Compressor compressor; Delay delay; ReverbFX reverbFX; float autoPanPhase = 0.0f; double currentSampleRate = 44100.0; double currentBpm = 120.0; std::atomic rmsLevel{0.0f}; std::array, 128> activeNotes{}; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockProcessor) };