30 #ifndef JucePlugin_PreferredChannelConfigurations
32 #if ! JucePlugin_IsMidiEffect 33 #if ! JucePlugin_IsSynth 46 std::lock_guard<std::mutex> lock(updatingParams);
52 return JucePlugin_Name;
57 #if JucePlugin_WantsMidiInput
66 #if JucePlugin_ProducesMidiOutput
75 #if JucePlugin_IsMidiEffect
133 size_t numChannels = getTotalNumInputChannels();
135 if (getTotalNumInputChannels() != getTotalNumOutputChannels())
137 std::string errMsg =
"Mismatch between input (" +
138 std::to_string(getTotalNumInputChannels()) +
140 std::to_string(getTotalNumOutputChannels()) +
141 ") channels. Forcing output to " +
142 std::to_string(numChannels) +
145 logger.dualPrint(Logger::Level::Error, errMsg);
149 for (size_t i = numChannels; i < irPipelines.size(); ++i)
151 irPipelines.pop_back();
154 for (size_t i = irPipelines.size(); i < numChannels; ++i)
156 irPipelines.emplace_back(
new IRPipeline(
this, (
int)i));
159 for (size_t i = numChannels; i < mainPipelines.size(); ++i)
161 mainPipelines.pop_back();
164 for (size_t i = mainPipelines.size(); i < numChannels; ++i)
166 mainPipelines.emplace_back(
new MainPipeline(
this));
182 #ifndef JucePlugin_PreferredChannelConfigurations
185 #if JucePlugin_IsMidiEffect 196 #if ! JucePlugin_IsSynth 215 SetThreadPriority(GetCurrentThread(),
216 THREAD_PRIORITY_TIME_CRITICAL);
220 auto activeParam = parameters.getParameter(PID_ACTIVE);
222 if (activeParam->getValue() < 0.5f)
224 activeParam->beginChangeGesture();
225 activeParam->setValueNotifyingHost(1.0f);
226 activeParam->endChangeGesture();
230 juce::ScopedNoDenormals noDenormals;
235 const int totalNumInputChannels = getTotalNumInputChannels();
236 const int totalNumOutputChannels = getTotalNumOutputChannels();
238 for (
int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
240 audio.clear(i, 0, audio.getNumSamples());
246 for (size_t i = irPipelines.size(); i < totalNumInputChannels; ++i)
248 irPipelines.emplace_back(
new IRPipeline(
this, (
int)i));
251 for (size_t i = mainPipelines.size(); i < totalNumInputChannels; ++i)
253 mainPipelines.emplace_back(
new MainPipeline(
this));
257 audioChannels = audio;
260 if (blocksProcessed % NUM_BLOCKS_PER_UPDATE_PARAMS)
262 std::unique_lock<std::mutex> lock(updatingParams, std::try_to_lock);
266 if (lock.owns_lock())
268 std::thread updateParamsThread(&AudioProcessor::updateParams,
269 this, getSampleRate());
275 updateParamsThread.detach();
279 #if REVERB_MULTITHREADED > 0
281 std::vector<std::thread> channelThreads;
283 for (
int i = 0; i < totalNumInputChannels; ++i)
285 channelThreads.emplace_back(&AudioProcessor::processChannel,
this, i);
289 for (
int i = 0; i < channelThreads.size(); ++i)
291 channelThreads[i].join();
294 for (
int i = 0; i < totalNumInputChannels; ++i)
303 SetThreadPriority(GetCurrentThread(),
304 THREAD_PRIORITY_NORMAL);
316 SetThreadPriority(GetCurrentThread(),
317 THREAD_PRIORITY_TIME_CRITICAL);
320 AudioBlock channelAudio = audioChannels.getSingleChannelBlock(channelIdx);
321 mainPipelines[channelIdx]->exec(channelAudio);
324 SetThreadPriority(GetCurrentThread(),
325 THREAD_PRIORITY_NORMAL);
344 SetThreadPriority(GetCurrentThread(),
345 THREAD_MODE_BACKGROUND_BEGIN);
349 std::lock_guard<std::mutex> lock(updatingParams);
352 const int numChannels = (
int)irPipelines.size();
354 jassert(mainPipelines.size() == numChannels);
357 for (
int i = 0; i < numChannels; ++i)
363 SetThreadPriority(GetCurrentThread(),
364 THREAD_MODE_BACKGROUND_END);
382 auto& processorLock = getCallbackLock();
384 auto& irPipeline = irPipelines[channelIdx];
385 auto& mainPipeline = mainPipelines[channelIdx];
387 AudioBlock irChannel;
390 irPipeline->updateSampleRate(sampleRate);
391 irPipeline->updateParams(parameters);
394 bool updateIR = irPipeline->needsToRun();
398 irChannel = irPipeline->exec();
404 juce::ScopedLock lock(processorLock);
406 mainPipeline->updateParams(parameters);
407 mainPipeline->updateSampleRate(sampleRate);
411 mainPipeline->loadIR(irChannel);
417 void AudioProcessor::processBlockBypassed(juce::AudioSampleBuffer& audio, juce::MidiBuffer& midi)
420 auto activeParam = parameters.getParameter(PID_ACTIVE);
422 if (activeParam->getValue() > 0.5f)
424 activeParam->beginChangeGesture();
425 activeParam->setValueNotifyingHost(0.0f);
426 activeParam->endChangeGesture();
431 this->juce::AudioProcessor::processBlockBypassed(audio, midi);
442 return new AudioProcessorEditor(*
this);
446 void AudioProcessor::getStateInformation(juce::MemoryBlock& destData)
448 std::unique_ptr<juce::XmlElement> xmlState(parameters.state.createXml());
452 copyXmlToBinary(*xmlState, destData);
456 logger.dualPrint(Logger::Level::Error,
"Failed to get state information");
460 void AudioProcessor::setStateInformation(
const void* data,
int sizeInBytes)
462 std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
464 if (xmlState && xmlState->hasTagName(parameters.state.getType()))
466 parameters.state = juce::ValueTree::fromXml(*xmlState);
470 logger.dualPrint(Logger::Level::Error,
"Failed to set state information");
479 if (paramsInitialised)
484 static const float gain12dB = juce::Decibels::decibelsToGain<
float>(15);
485 static const float gain6dBneg = juce::Decibels::decibelsToGain<
float>(-6);
486 static const float gain24dBneg = juce::Decibels::decibelsToGain<
float>(-24);
487 static const float gain50dBneg = juce::Decibels::decibelsToGain<
float>(-50);
488 static const float gain15dBInv = 1.0f / gain12dB;
490 auto fnGainTodBString = [](
float gain) {
return juce::String(juce::Decibels::gainToDecibels(gain), 2) +
" dB"; };
495 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(0) + PID_FILTER_FREQ_SUFFIX,
496 "EQ: Low-shelf cut-off frequency",
"<16-520 Hz>",
497 juce::Range<
float>(16.0f, 520.0f),
502 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(0) + PID_FILTER_Q_SUFFIX,
503 "EQ: Low-shelf Q factor",
"<0.71-1.41>",
504 juce::Range<
float>(0.71f, 1.41f),
508 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(0) + PID_FILTER_GAIN_SUFFIX,
509 "EQ: Low-shelf gain factor",
"<" + juce::String(gain15dBInv) +
" = no change>",
510 juce::Range<
float>(gain24dBneg, gain12dB),
512 fnGainTodBString,
nullptr );
518 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(1) + PID_FILTER_FREQ_SUFFIX,
519 "EQ: Peak filter 1 cut-off frequency",
"<250-1500 Hz>",
520 juce::NormalisableRange<
float>(250.0f, 1500.0f),
524 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(1) + PID_FILTER_Q_SUFFIX,
525 "EQ: Peak filter 1 Q factor",
"<0.71-6.5>",
526 juce::NormalisableRange<
float>(0.71f, 6.5f),
530 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(1) + PID_FILTER_GAIN_SUFFIX,
531 "EQ: Peak filter 1 gain factor",
"<" + juce::String(gain15dBInv) +
" = no change>",
532 juce::NormalisableRange<
float>(gain24dBneg, gain12dB),
534 fnGainTodBString,
nullptr );
540 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(2) + PID_FILTER_FREQ_SUFFIX,
541 "EQ: Peak filter 2 cut-off frequency",
"<2600-15600 Hz>",
542 juce::NormalisableRange<
float>(2600.0f, 15600.0f),
546 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(2) + PID_FILTER_Q_SUFFIX,
547 "EQ: Peak filter 2 Q factor",
"<0.71-6.50>",
548 juce::NormalisableRange<
float>(0.71f, 6.50f),
552 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(2) + PID_FILTER_GAIN_SUFFIX,
553 "EQ: Peak filter 2 gain factor",
"<" + juce::String(gain15dBInv) +
" = no change>",
554 juce::NormalisableRange<
float>(gain24dBneg, gain12dB),
556 fnGainTodBString,
nullptr );
562 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(3) + PID_FILTER_FREQ_SUFFIX,
563 "EQ: High-shelf cut-off frequency",
"<8000-21000 Hz>",
564 juce::NormalisableRange<
float>(8000.0f, 21000.0f),
568 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(3) + PID_FILTER_Q_SUFFIX,
569 "EQ: High-shelf Q factor",
"<0.71-1.41>",
570 juce::NormalisableRange<
float>(0.71f, 1.41f),
574 parameters.createAndAddParameter( PID_FILTER_PREFIX + std::to_string(3) + PID_FILTER_GAIN_SUFFIX,
575 "EQ: High-shelf gain factor",
"<" + juce::String(gain15dBInv) +
" = no change>",
576 juce::NormalisableRange<
float>(gain24dBneg, gain12dB),
578 fnGainTodBString,
nullptr );
584 parameters.createAndAddParameter( PID_IR_GAIN,
585 "Impulse response gain",
"<" + juce::String(1.0) +
" = no change>",
586 juce::NormalisableRange<
float>(0.0f, 1.0f),
588 fnGainTodBString,
nullptr );
593 parameters.createAndAddParameter( PID_IR_LENGTH,
594 "Reverb length",
"<s>",
595 juce::NormalisableRange<
float>(0.1f, 5.0f),
602 parameters.createAndAddParameter( PID_PREDELAY,
603 "Pre-delay",
"<0-1000 ms>",
604 juce::NormalisableRange<
float>(0.0f, 1000.0f),
612 parameters.createAndAddParameter( PID_WETRATIO,
613 "Dry/wet ratio",
"<0 = dry, 1 = wet>",
614 juce::NormalisableRange<
float>(0.0f, 1.0f),
622 parameters.createAndAddParameter( PID_AUDIO_OUT_GAIN,
623 "Output gain",
"<" + juce::String(gain15dBInv) +
"= no change>",
624 juce::NormalisableRange<
float>(gain50dBneg, 1.0f),
626 fnGainTodBString,
nullptr );
632 parameters.createAndAddParameter( PID_ACTIVE,
"Active",
"<y/n>",
633 juce::NormalisableRange<
float>(0.0f, 1.0f),
639 parameters.state = juce::ValueTree(juce::Identifier(JucePlugin_Name));
648 juce::ValueTree irFile(PID_IR_FILE_CHOICE);
651 if (irBank.buffers.begin() != irBank.buffers.end())
653 const juce::String& firstBankedIR = irBank.buffers.begin()->first;
654 irFile.setProperty(
"value", firstBankedIR,
nullptr);
657 parameters.state.addChild(irFile, -1,
nullptr);
663 paramsInitialised =
true;
670 juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
672 return new reverb::AudioProcessor();
float getParam(const juce::AudioProcessorValueTreeState ¶ms, const juce::String &blockId) const
Internal method used to get (and check) a parameter's value.
void updateParams(double sampleRate)
Update parameters across all channels.
void processChannel(int channelIdx)
Applies reverb effect on single channel using preconfigured pipelines.
void updateParamsForChannel(int channelIdx, double sampleRate)
Update parameters for a given channel.
static const IRBank & getInstance()
void prepareToPlay(double sampleRate, int samplesPerBlock) override
Prepare the processor before playback starts.
void releaseResources() override
Release resource when playback stops.
AudioProcessor()
Constructs a reverb audio processor & initialises its inner pipelines.
void processBlock(juce::AudioSampleBuffer &audio, juce::MidiBuffer &) override
Applies reverb effect on audio buffer using parameters given through the editor.