horizont/Source/PluginProcessor.cpp

316 lines
9.3 KiB
C++

#include "PluginProcessor.h"
#include "PluginEditor.h"
HorizontAudioProcessor::HorizontAudioProcessor()
: AudioProcessor (BusesProperties()
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)),
apvts (*this, nullptr, "Parameters", createParameterLayout()),
presets (getPresets())
{
lowCutParam = apvts.getRawParameterValue ("lowCut");
highCutParam = apvts.getRawParameterValue ("highCut");
decayParam = apvts.getRawParameterValue ("decay");
sizeParam = apvts.getRawParameterValue ("size");
diffusionParam = apvts.getRawParameterValue ("diffusion");
brightnessParam = apvts.getRawParameterValue ("brightness");
feedbackParam = apvts.getRawParameterValue ("feedback");
pitchParam = apvts.getRawParameterValue ("pitch");
dryWetParam = apvts.getRawParameterValue ("dryWet");
}
HorizontAudioProcessor::~HorizontAudioProcessor()
{
}
juce::AudioProcessorValueTreeState::ParameterLayout HorizontAudioProcessor::createParameterLayout()
{
juce::AudioProcessorValueTreeState::ParameterLayout layout;
layout.add (std::make_unique<juce::AudioParameterFloat> ("lowCut", "Low Cut",
juce::NormalisableRange<float> (20.0f, 500.0f, 0.0f, 0.35f), 40.0f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("highCut", "High Cut",
juce::NormalisableRange<float> (800.0f, 20000.0f, 0.0f, 0.25f), 14000.0f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("decay", "Decay",
juce::NormalisableRange<float> (0.2f, 10.0f, 0.0f, 0.5f), 3.5f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("size", "Size",
juce::NormalisableRange<float> (0.4f, 2.0f, 0.0f, 0.5f), 1.0f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("diffusion", "Diffusion",
juce::NormalisableRange<float> (0.0f, 1.0f, 0.0f), 0.6f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("brightness", "Brightness",
juce::NormalisableRange<float> (0.0f, 1.0f, 0.0f), 0.75f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("feedback", "Feedback",
juce::NormalisableRange<float> (0.0f, 1.0f, 0.0f), 0.6f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("pitch", "Pitch",
juce::NormalisableRange<float> (-12.0f, 12.0f, 0.0f), 0.0f));
layout.add (std::make_unique<juce::AudioParameterFloat> ("dryWet", "Dry/Wet",
juce::NormalisableRange<float> (0.0f, 1.0f, 0.0f), 0.35f));
return layout;
}
void HorizontAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
wetScratch.resize ((size_t) juce::jmax (samplesPerBlock, 1));
dryScratch.resize ((size_t) juce::jmax (samplesPerBlock, 1));
wetFifo.reset();
engine.prepare (sampleRate);
updateParameters();
}
void HorizontAudioProcessor::releaseResources()
{
engine.reset();
}
bool HorizontAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
if (layouts.getMainOutputChannelSet().isDisabled())
return false;
const int outCh = layouts.getMainOutputChannelSet().size();
const int inCh = layouts.getMainInputChannelSet().size();
if (inCh == 0)
return outCh <= 2;
return inCh <= 2 && outCh <= 2;
}
void HorizontAudioProcessor::updateParameters()
{
engine.setLowCut (lowCutParam->load());
engine.setHighCut (highCutParam->load());
engine.setDecay (decayParam->load());
engine.setSize (sizeParam->load());
engine.setDiffusion (diffusionParam->load());
engine.setBrightness (brightnessParam->load());
engine.setFeedback (feedbackParam->load());
engine.setPitch (pitchParam->load());
engine.setDryWet (dryWetParam->load());
}
void HorizontAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
juce::ScopedNoDenormals noDenormals;
const int numSamples = buffer.getNumSamples();
if (numSamples == 0)
return;
const int numOut = juce::jmin (buffer.getNumChannels(), 2);
if (numOut == 0)
return;
for (int ch = numOut; ch < buffer.getNumChannels(); ++ch)
buffer.clear (ch, 0, numSamples);
updateParameters();
const int inputChannels = getChannelCountOfBus (true, 0);
const bool haveLeft = buffer.getNumChannels() > 0 && inputChannels > 0;
const bool haveRight = buffer.getNumChannels() > 1 && inputChannels > 1;
const bool canCaptureWet = wetScratch.size() >= (size_t) numSamples;
const float wetVal = dryWetParam->load();
const float dryGain = 1.0f - wetVal;
for (int s = 0; s < numSamples; ++s)
{
const float inL = haveLeft ? buffer.getSample (0, s) : 0.0f;
const float inR = haveRight ? buffer.getSample (1, s) : inL;
float outL = 0.0f;
float outR = 0.0f;
engine.processSample (inL, inR, outL, outR);
buffer.setSample (0, s, outL);
if (numOut > 1)
buffer.setSample (1, s, outR);
if (canCaptureWet)
{
wetScratch[(size_t) s] = engine.getLastWetL() * wetVal;
dryScratch[(size_t) s] = inL * dryGain;
}
}
if (canCaptureWet)
{
pushWetSamples (wetScratch.data(), numSamples);
appendDrySamples (dryScratch.data(), numSamples);
}
}
void HorizontAudioProcessor::pushWetSamples (const float* src, int numToWrite)
{
if (numToWrite <= 0)
return;
numToWrite = juce::jmin (numToWrite, wetFifo.getFreeSpace());
if (numToWrite <= 0)
return;
int i = 0;
auto handle = wetFifo.write (numToWrite);
handle.forEach ([&] (int idx) { wetRing.setSample (0, idx, src[i++]); });
}
int HorizontAudioProcessor::readWetSamples (float* dest, int numToRead)
{
const int avail = wetFifo.getNumReady();
if (avail <= 0)
return 0;
numToRead = juce::jmin (numToRead, avail);
int i = 0;
auto handle = wetFifo.read (numToRead);
handle.forEach ([&] (int idx) { dest[i++] = wetRing.getSample (0, idx); });
return numToRead;
}
void HorizontAudioProcessor::appendDrySamples (const float* src, int numToWrite)
{
if (numToWrite <= 0)
return;
size_t w = dryWritePos.load (std::memory_order_relaxed);
for (int i = 0; i < numToWrite; ++i)
{
dryRingLive[w] = src[i];
w = (w + 1) % (size_t) dryRingSize;
}
dryWritePos.store (w, std::memory_order_release);
dryTotalWritten.fetch_add ((size_t) numToWrite, std::memory_order_release);
}
int HorizontAudioProcessor::copyRecentDrySamples (float* dest, int numToRead)
{
numToRead = juce::jmin (numToRead, dryRingSize);
const size_t written = dryTotalWritten.load (std::memory_order_acquire);
const size_t wp = dryWritePos.load (std::memory_order_acquire);
if (written < (size_t) numToRead)
{
const int avail = (int) written;
const int lead = numToRead - avail;
std::fill (dest, dest + lead, 0.0f);
for (int i = 0; i < avail; ++i)
{
const size_t idx = (wp + (size_t) dryRingSize - (size_t) avail + (size_t) i) % (size_t) dryRingSize;
dest[lead + i] = dryRingLive[idx];
}
return numToRead;
}
for (int i = 0; i < numToRead; ++i)
{
const size_t idx = (wp + (size_t) dryRingSize - (size_t) numToRead + (size_t) i) % (size_t) dryRingSize;
dest[i] = dryRingLive[idx];
}
return numToRead;
}
juce::AudioProcessorEditor* HorizontAudioProcessor::createEditor()
{
return new HorizontAudioProcessorEditor (*this, apvts);
}
bool HorizontAudioProcessor::hasEditor() const
{
return true;
}
const juce::String HorizontAudioProcessor::getName() const
{
return "Horizont";
}
bool HorizontAudioProcessor::acceptsMidi() const
{
return false;
}
bool HorizontAudioProcessor::producesMidi() const
{
return false;
}
double HorizontAudioProcessor::getTailLengthSeconds() const
{
return 10.0;
}
int HorizontAudioProcessor::getNumPrograms()
{
return (int) presets.size();
}
int HorizontAudioProcessor::getCurrentProgram()
{
return currentProgram;
}
void HorizontAudioProcessor::setCurrentProgram (int index)
{
if (index >= 0 && index < (int) presets.size())
{
currentProgram = index;
applyPreset (presets[(size_t) index]);
}
}
const juce::String HorizontAudioProcessor::getProgramName (int index)
{
if (index >= 0 && index < (int) presets.size())
return presets[(size_t) index].name;
return "Default";
}
void HorizontAudioProcessor::changeProgramName (int, const juce::String&)
{
}
void HorizontAudioProcessor::applyPreset (const Preset& preset)
{
for (const auto& v : preset.values)
{
if (auto* param = apvts.getParameter (v.id))
param->setValueNotifyingHost (param->convertTo0to1 (v.value));
}
}
void HorizontAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
auto state = apvts.copyState();
std::unique_ptr<juce::XmlElement> xml (state.createXml());
copyXmlToBinary (*xml, destData);
}
void HorizontAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
std::unique_ptr<juce::XmlElement> xml (getXmlFromBinary (data, sizeInBytes));
if (xml != nullptr)
apvts.replaceState (juce::ValueTree::fromXml (*xml));
updateParameters();
}
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new HorizontAudioProcessor();
}