#include "PluginProcessor.h" #include "PluginEditor.h" MindballAudioProcessor::MindballAudioProcessor() : AudioProcessor (BusesProperties() .withInput ("Input", juce::AudioChannelSet::stereo(), true) .withOutput ("Output", juce::AudioChannelSet::stereo(), true)), apvts (*this, nullptr, "Parameters", createParameterLayout()) { } juce::AudioProcessorValueTreeState::ParameterLayout MindballAudioProcessor::createParameterLayout() { juce::AudioProcessorValueTreeState::ParameterLayout layout; layout.add (std::make_unique ( ParameterIDs::feedback, "Feedback", juce::NormalisableRange (0.0f, 1.0f, 0.0001f, 1.0f), 0.35f)); layout.add (std::make_unique ( ParameterIDs::locut, "Lo-Cut", juce::NormalisableRange (20.0f, 4000.0f, 1.0f, 0.3f), 100.0f)); layout.add (std::make_unique ( ParameterIDs::hicut, "Hi-Cut", juce::NormalisableRange (500.0f, 18000.0f, 1.0f, 0.3f), 12000.0f)); layout.add (std::make_unique ( ParameterIDs::time, "Time", juce::NormalisableRange (10.0f, 3000.0f, 1.0f, 0.4f), 300.0f)); layout.add (std::make_unique ( ParameterIDs::timeSync, "Time (Sync)", juce::StringArray { "1/16", "1/8", "1/4", "1/3", "1/2", "3/4", "1", "3/2", "2", "3", "4", "8" }, 4)); layout.add (std::make_unique ( ParameterIDs::drywet, "Dry/Wet", juce::NormalisableRange (0.0f, 1.0f, 0.001f, 1.0f), 0.4f)); layout.add (std::make_unique (ParameterIDs::sync, "Time Sync", false)); layout.add (std::make_unique ( ParameterIDs::mode, "Mode", juce::StringArray { "Normal", "Invert", "Ping-Pong", "SuperPong" }, 0)); layout.add (std::make_unique (ParameterIDs::doubleTap, "Double", false)); layout.add (std::make_unique (ParameterIDs::center, "Center", false)); layout.add (std::make_unique (ParameterIDs::autopan, "Auto-Pan", false)); layout.add (std::make_unique ( ParameterIDs::panDepth, "Pan Depth", juce::NormalisableRange (0.0f, 1.0f, 0.001f, 1.0f), 1.0f)); layout.add (std::make_unique (ParameterIDs::bypass, "Bypass", false)); return layout; } const juce::String MindballAudioProcessor::getName() const { return JucePlugin_Name; } bool MindballAudioProcessor::acceptsMidi() const { return false; } bool MindballAudioProcessor::producesMidi() const { return false; } double MindballAudioProcessor::getTailLengthSeconds() const { return 10.0; } int MindballAudioProcessor::getNumPrograms() { return 1; } int MindballAudioProcessor::getCurrentProgram() { return 0; } void MindballAudioProcessor::setCurrentProgram (int) {} const juce::String MindballAudioProcessor::getProgramName (int) { return {}; } void MindballAudioProcessor::changeProgramName (int, const juce::String&) {} void MindballAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/) { engine.prepare (sampleRate, maxDelaySeconds); meterDecay = static_cast (std::exp (-1.0 / (0.4 * sampleRate))); } void MindballAudioProcessor::releaseResources() { engine.clear(); } bool MindballAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { const auto mainIn = layouts.getMainInputChannelSet(); const auto mainOut = layouts.getMainOutputChannelSet(); return mainIn == mainOut && (mainIn == juce::AudioChannelSet::mono() || mainIn == juce::AudioChannelSet::stereo()); } void MindballAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer&) { juce::ScopedNoDenormals noDenormals; const int numSamples = buffer.getNumSamples(); const int numChannels = buffer.getNumChannels(); if (numSamples == 0 || numChannels == 0) return; const bool bypass = *apvts.getRawParameterValue (ParameterIDs::bypass) > 0.5f; if (bypass) { if (!wasBypassed) { engine.clear(); wasBypassed = true; } meterDryL = meterDryR = meterWetL = meterWetR = 0.0f; dryMeterL.store (0.0f); dryMeterR.store (0.0f); wetMeterL.store (0.0f); wetMeterR.store (0.0f); return; } wasBypassed = false; double bpm = 120.0; if (auto* playHead = getPlayHead()) { juce::AudioPlayHead::CurrentPositionInfo pos; if (playHead->getCurrentPosition (pos)) bpm = pos.bpm > 0 ? pos.bpm : 120.0; } mindball::DelayEngine::Params p; p.feedback = *apvts.getRawParameterValue (ParameterIDs::feedback); p.loCutHz = *apvts.getRawParameterValue (ParameterIDs::locut); p.hiCutHz = *apvts.getRawParameterValue (ParameterIDs::hicut); p.timeMs = *apvts.getRawParameterValue (ParameterIDs::time); p.timeSyncIndex = static_cast (*apvts.getRawParameterValue (ParameterIDs::timeSync)); p.dryWet = *apvts.getRawParameterValue (ParameterIDs::drywet); p.mode = static_cast (*apvts.getRawParameterValue (ParameterIDs::mode)); p.sync = *apvts.getRawParameterValue (ParameterIDs::sync) > 0.5f; p.doubleTap = *apvts.getRawParameterValue (ParameterIDs::doubleTap) > 0.5f; p.center = *apvts.getRawParameterValue (ParameterIDs::center) > 0.5f; p.autoPan = *apvts.getRawParameterValue (ParameterIDs::autopan) > 0.5f; p.panDepth = *apvts.getRawParameterValue (ParameterIDs::panDepth); p.bpm = bpm; wetScratch.setSize (2, numSamples); if (numChannels >= 2) { engine.process (buffer.getReadPointer (0), buffer.getReadPointer (1), buffer.getWritePointer (0), buffer.getWritePointer (1), numSamples, p, wetScratch.getWritePointer (0), wetScratch.getWritePointer (1)); } else { engine.process (buffer.getReadPointer (0), buffer.getReadPointer (0), buffer.getWritePointer (0), buffer.getWritePointer (0), numSamples, p, wetScratch.getWritePointer (0), wetScratch.getWritePointer (1)); } const float* outL = buffer.getReadPointer (0); const float* outR = numChannels >= 2 ? buffer.getReadPointer (1) : outL; const float* wetL = wetScratch.getReadPointer (0); const float* wetR = wetScratch.getReadPointer (1); for (int i = 0; i < numSamples; ++i) { const float dryL = outL[i] - wetL[i]; const float dryR = outR[i] - wetR[i]; meterDryL = std::max (std::abs (dryL), meterDryL * meterDecay); meterDryR = std::max (std::abs (dryR), meterDryR * meterDecay); meterWetL = std::max (std::abs (wetL[i]), meterWetL * meterDecay); meterWetR = std::max (std::abs (wetR[i]), meterWetR * meterDecay); } dryMeterL.store (meterDryL); dryMeterR.store (meterDryR); wetMeterL.store (meterWetL); wetMeterR.store (meterWetR); } juce::AudioProcessorEditor* MindballAudioProcessor::createEditor() { return new MindballAudioProcessorEditor (*this); } bool MindballAudioProcessor::hasEditor() const { return true; } void MindballAudioProcessor::getStateInformation (juce::MemoryBlock& destData) { auto state = apvts.copyState(); std::unique_ptr xml (state.createXml()); copyXmlToBinary (*xml, destData); } void MindballAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { std::unique_ptr xml (getXmlFromBinary (data, sizeInBytes)); if (xml != nullptr) apvts.replaceState (juce::ValueTree::fromXml (*xml)); } juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new MindballAudioProcessor(); }