IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/ 0000775 0025372 0025372 00000000000 15257173642 022627 5 ustar zmoelnig zmoelnig IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/ 0000775 0025372 0025372 00000000000 15257173642 024473 5 ustar zmoelnig zmoelnig IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/Source/ 0000775 0025372 0025372 00000000000 15257173642 025733 5 ustar zmoelnig zmoelnig IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/Source/PluginProcessor.cpp0000664 0025372 0025372 00000105241 15257173642 031600 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Author: Daniel Rudrich, Felix Holzmüller
Copyright (c) 2017, 2024 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
DualDelayAudioProcessor::DualDelayAudioProcessor() :
AudioProcessorBase (
#ifndef JucePlugin_PreferredChannelConfigurations
BusesProperties()
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
.withInput ("Input",
((juce::PluginHostType::getPluginLoadedAs()
== juce::AudioProcessor::wrapperType_VST3)
? juce::AudioChannelSet::ambisonic (1)
: juce::AudioChannelSet::ambisonic (maxOrder)),
true)
#endif
.withOutput ("Output",
((juce::PluginHostType::getPluginLoadedAs()
== juce::AudioProcessor::wrapperType_VST3)
? juce::AudioChannelSet::ambisonic (1)
: juce::AudioChannelSet::ambisonic (maxOrder)),
true)
#endif
,
#endif
createParameterLayout())
{
dryGain = parameters.getRawParameterValue ("dryGain");
orderSetting = parameters.getRawParameterValue ("orderSetting");
for (int i = 0; i < 2; ++i)
{
juce::String side = (i == 0) ? "L" : "R";
wetGain[i] = parameters.getRawParameterValue ("wetGain" + side);
delayBPM[i] = parameters.getRawParameterValue ("delayBPM" + side);
delayMult[i] = parameters.getRawParameterValue ("delayMult" + side);
sync[i] = parameters.getRawParameterValue ("sync" + side);
transformMode[i] = parameters.getRawParameterValue ("transformMode" + side);
yaw[i] = parameters.getRawParameterValue ("yaw" + side);
pitch[i] = parameters.getRawParameterValue ("pitch" + side);
roll[i] = parameters.getRawParameterValue ("roll" + side);
warpModeAz[i] = parameters.getRawParameterValue ("warpModeAz" + side);
warpModeEl[i] = parameters.getRawParameterValue ("warpModeEl" + side);
warpFactorAz[i] = parameters.getRawParameterValue ("warpFactorAz" + side);
warpFactorEl[i] = parameters.getRawParameterValue ("warpFactorEl" + side);
HPcutOff[i] = parameters.getRawParameterValue ("HPcutOff" + side);
LPcutOff[i] = parameters.getRawParameterValue ("LPcutOff" + side);
feedback[i] = parameters.getRawParameterValue ("feedback" + side);
xfeedback[i] = parameters.getRawParameterValue ("xfeedback" + side);
lfoRate[i] = parameters.getRawParameterValue ("lfoRate" + side);
lfoDepth[i] = parameters.getRawParameterValue ("lfoDepth" + side);
parameters.addParameterListener ("yaw" + side, this);
parameters.addParameterListener ("pitch" + side, this);
parameters.addParameterListener ("roll" + side, this);
parameters.addParameterListener ("warpModeAz" + side, this);
parameters.addParameterListener ("warpModeEl" + side, this);
parameters.addParameterListener ("warpFactorAz" + side, this);
parameters.addParameterListener ("warpFactorEl" + side, this);
parameters.addParameterListener ("lfoRate" + side, this);
parameters.addParameterListener ("HPcutOff" + side, this);
parameters.addParameterListener ("LPcutOff" + side, this);
LFO[i].initialise ([] (float phi) { return std::sin (phi); });
}
parameters.addParameterListener ("orderSetting", this);
}
DualDelayAudioProcessor::~DualDelayAudioProcessor()
{
}
//==============================================================================
int DualDelayAudioProcessor::getNumPrograms()
{
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int DualDelayAudioProcessor::getCurrentProgram()
{
return 0;
}
void DualDelayAudioProcessor::setCurrentProgram (int index)
{
}
const juce::String DualDelayAudioProcessor::getProgramName (int index)
{
return {};
}
void DualDelayAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
}
//==============================================================================
void DualDelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
checkInputAndOutput (this, *orderSetting, *orderSetting, true);
const int maxLfoDepth = static_cast (ceil (
parameters.getParameterRange ("lfoDepthL").getRange().getEnd() * sampleRate / 500.0f));
const int maxDelay =
static_cast (ceil (sampleRate * 60.0f
/ (parameters.getParameterRange ("delayBPML").start
* parameters.getParameterRange ("delayMultL").start)
+ maxLfoDepth));
juce::dsp::ProcessSpec specDelay;
specDelay.sampleRate = sampleRate;
specDelay.numChannels = numberOfOutputChannels;
specDelay.maximumBlockSize = samplesPerBlock;
juce::dsp::ProcessSpec specLFO;
specLFO.sampleRate = sampleRate;
specLFO.numChannels = 1;
specLFO.maximumBlockSize = samplesPerBlock;
for (int i = 0; i < 2; ++i)
{
LFO[i].prepare (specLFO);
LFO[i].setFrequency (*lfoRate[i], true);
for (int j = lowPassFilters[i].size(); --j >= 0;)
{
lowPassFilters[i][j]->reset();
highPassFilters[i][j]->reset();
updateFilterCoeffs = true;
}
delayBuffer[i].clear();
delayOutBuffer[i].clear();
delayLine[i].reset();
delayLine[i].prepare (specDelay);
delayLine[i].setMaximumDelayInSamples (maxDelay);
delayLine[i].setDelay (sampleRate * 60.0f / (*delayBPM[0] * *delayMult[0]));
delayTimeInterp[i].setFrequency (0.5f, sampleRate);
auto tmp_prdersetting = orderSetting->load();
rotator[i].updateParams (*yaw[i], *pitch[i], *roll[i], static_cast (*orderSetting));
warp[i].updateParams (
AmbisonicWarp::AzimuthWarpType (juce::roundToInt (warpModeAz[i]->load())),
AmbisonicWarp::ElevationWarpType (juce::roundToInt (warpModeEl[i]->load())),
*warpFactorAz[i],
*warpFactorEl[i]);
warp[i].setWorkingOrder (isqrt (numberOfOutputChannels) - 1);
}
}
void DualDelayAudioProcessor::releaseResources()
{
}
void DualDelayAudioProcessor::processBlock (juce::AudioSampleBuffer& buffer,
juce::MidiBuffer& midiMessages)
{
juce::ScopedNoDenormals noDenormals;
checkInputAndOutput (this, *orderSetting, *orderSetting);
const int totalNumInputChannels = getTotalNumInputChannels();
const int workingOrder =
juce::jmin (isqrt (buffer.getNumChannels()) - 1, input.getOrder(), output.getOrder());
const int nCh = squares[workingOrder + 1];
const double fs = getSampleRate();
const int spb = buffer.getNumSamples();
const float msToSamples = fs / 1000.0f;
// Sync delay time to host BPM
juce::Optional hostBPM;
juce::AudioPlayHead* playHead = getPlayHead();
if (updateFilterCoeffs)
{
for (int i = 0; i < 2; ++i)
updateFilters (i);
}
if (playHead != nullptr && (*sync[0] > 0.5f || *sync[1] > 0.5f))
{
hostBPM = playHead->getPosition()->getBpm();
if (hostBPM.hasValue())
{
auto hostBPMMapped = parameters.getParameter ("delayBPML")->convertTo0to1 (*hostBPM);
if (*sync[0] > 0.5f)
parameters.getParameter ("delayBPML")
->setValue (static_cast (hostBPMMapped));
if (*sync[1] > 0.5f)
parameters.getParameter ("delayBPMR")
->setValue (static_cast (hostBPMMapped));
}
}
//clear not used channels
for (int channel = nCh; channel < totalNumInputChannels; ++channel)
buffer.clear (channel, 0, spb);
for (int i = 0; i < 2; ++i)
{
int otherSide = (i + 1) % 2;
// Update rotator to current working order if it has changed
if (rotator[i].getOrder() != workingOrder)
rotator[i].updateParams (*yaw[i], *pitch[i], *roll[i], workingOrder);
if (warp[i].getWorkingOrder() != workingOrder)
warp[i].setWorkingOrder (workingOrder);
// Setting up delay interpolator
const float newDelay = fs * 60.0f / (*delayBPM[i] * *delayMult[i]);
delayTimeInterp[i].setTargetValue (newDelay);
// ========== Fill input buffer for delay line ==========
// Copy dry input to delay buffer
delayBuffer[i].makeCopyOf (buffer, true);
// Copy feedback and crossfeed to delay buffer
for (int channel = 0; channel < nCh; ++channel)
{
delayBuffer[i].addFrom (channel,
0,
delayOutBuffer[i],
channel,
0,
spb,
juce::Decibels::decibelsToGain (feedback[i]->load(), -59.91f));
delayBuffer[i].addFrom (
channel,
0,
delayOutBuffer[otherSide],
channel,
0,
spb,
juce::Decibels::decibelsToGain (xfeedback[otherSide]->load(), -59.91f));
// Apply filter to delayed signal
lowPassFilters[i][channel]->processSamples (delayBuffer[i].getWritePointer (channel),
spb);
highPassFilters[i][channel]->processSamples (delayBuffer[i].getWritePointer (channel),
spb);
}
// Rotate/warp delayed signal
if (*transformMode[i] < 0.5f)
rotator[i].process (&delayBuffer[i]);
else
warp[i].process (&delayBuffer[i]);
}
// ========== Add and get samples from delay line ==========
for (int i = 0; i < 2; ++i)
{
LFO[i].setFrequency (*lfoRate[i]);
// Cycle through it on sample basis for smooth delay time changes
for (int sample = 0; sample < spb; ++sample)
{
float currentDelay = delayTimeInterp[i].process()
+ LFO[i].processSample (1.0f) * msToSamples * *lfoDepth[i];
for (int channel = 0; channel < nCh; ++channel)
{
// Push samples into delay line
delayLine[i].pushSample (channel, delayBuffer[i].getSample (channel, sample));
// Pop samples from delay line to output buffer
delayOutBuffer[i].setSample (channel,
sample,
delayLine[i].popSample (channel, currentDelay));
}
}
}
// ========== Calculate output ==========
// Apply gain to direct signal
buffer.applyGain (juce::Decibels::decibelsToGain (dryGain->load(), -59.91f));
// Add delayed signal to output buffer
for (int i = 0; i < 2; ++i)
{
for (int channel = 0; channel < nCh; ++channel)
{
buffer.addFrom (channel,
0,
delayOutBuffer[i],
channel,
0,
spb,
juce::Decibels::decibelsToGain (wetGain[i]->load(), -59.91f));
}
}
}
//==============================================================================
bool DualDelayAudioProcessor::hasEditor() const
{
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* DualDelayAudioProcessor::createEditor()
{
return new DualDelayAudioProcessorEditor (*this, parameters);
}
//==============================================================================
void DualDelayAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
auto state = parameters.copyState();
auto oscConfig = state.getOrCreateChildWithName ("OSCConfig", nullptr);
oscConfig.copyPropertiesFrom (oscParameterInterface.getConfig(), nullptr);
std::unique_ptr xml (state.createXml());
copyXmlToBinary (*xml, destData);
}
void DualDelayAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
std::unique_ptr xmlState (getXmlFromBinary (data, sizeInBytes));
if (xmlState.get() != nullptr)
{
if (xmlState->hasTagName (parameters.state.getType()))
{
parameters.replaceState (juce::ValueTree::fromXml (*xmlState));
if (parameters.state.hasProperty ("OSCPort")) // legacy
{
oscParameterInterface.getOSCReceiver().connect (
parameters.state.getProperty ("OSCPort", juce::var (-1)));
parameters.state.removeProperty ("OSCPort", nullptr);
}
auto oscConfig = parameters.state.getChildWithName ("OSCConfig");
if (oscConfig.isValid())
oscParameterInterface.setConfig (oscConfig);
// Add compatibility layer for old delay parameters
juce::String ch[2] = { "L", "R" };
for (int i = 0; i < 2; ++i)
{
// rotation to yaw
auto oldTimeParameter = xmlState->getChildByAttribute ("id", "rotation" + ch[i]);
if (oldTimeParameter != nullptr)
{
auto parameterState =
parameters.state.getChildWithProperty ("id", "yaw" + ch[i]);
if (parameterState.isValid())
{
parameterState.setProperty (
"value",
oldTimeParameter->getStringAttribute ("value").getFloatValue(),
nullptr);
}
}
// ms to BPM
oldTimeParameter = xmlState->getChildByAttribute ("id", "delayTime" + ch[i]);
if (oldTimeParameter != nullptr)
{
float value =
60000.0f / oldTimeParameter->getStringAttribute ("value").getFloatValue();
auto delayState =
parameters.state.getChildWithProperty ("id", "delayBPM" + ch[i]);
const float maxBPM =
parameters.getParameterRange ("delayBPM" + ch[i]).getRange().getEnd();
const float maxMult =
parameters.getParameterRange ("delayMult" + ch[i]).getRange().getEnd();
// Set multiplier if BPM is too high
if (value > maxBPM)
{
float mult = static_cast (
juce::nextPowerOfTwo (static_cast (ceil (value / maxBPM))));
value /= mult;
// Limiting multiplier to maximum value, so it's at least somehow on beat
mult = juce::jmin (mult, maxMult);
auto multState =
parameters.state.getChildWithProperty ("id", "delayMult" + ch[i]);
if (multState.isValid())
multState.setProperty ("value", mult, nullptr);
}
if (delayState.isValid())
delayState.setProperty ("value", value, nullptr);
}
}
}
}
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new DualDelayAudioProcessor();
}
void DualDelayAudioProcessor::parameterChanged (const juce::String& parameterID, float newValue)
{
if (parameterID == "orderSetting")
{
userChangedIOSettings = true;
rotator[0].updateParams (*yaw[0], *pitch[0], *roll[0], static_cast (newValue) - 1);
rotator[1].updateParams (*yaw[1], *pitch[1], *roll[1], static_cast (newValue) - 1);
warp[1].setWorkingOrder (static_cast (newValue) - 1);
warp[0].setWorkingOrder (static_cast (newValue) - 1);
}
const int currentOrder = static_cast (*orderSetting);
bool sideIdx = parameterID.endsWith ("L") ? 0 : 1;
juce::String side = (sideIdx == 0) ? "L" : "R";
if (parameterID == "yaw" + side || parameterID == "pitch" + side
|| parameterID == "roll" + side)
rotator[sideIdx].updateParams (*yaw[sideIdx],
*pitch[sideIdx],
*roll[sideIdx],
currentOrder);
else if (parameterID == "warpModeAz" + side || parameterID == "warpModeEl" + side
|| parameterID == "warpFactorAz" + side || parameterID == "warpFactorEl" + side)
warp[sideIdx].updateParams (AmbisonicWarp::AzimuthWarpType (
juce::roundToInt (warpModeAz[sideIdx]->load())),
AmbisonicWarp::ElevationWarpType (
juce::roundToInt (warpModeEl[sideIdx]->load())),
*warpFactorAz[sideIdx],
*warpFactorEl[sideIdx]);
else if (parameterID.startsWith ("HPcutOff") || parameterID.startsWith ("LPcutOff"))
{
updateFilters (sideIdx);
}
}
void DualDelayAudioProcessor::updateBuffers()
{
DBG ("IOHelper: input size: " << input.getSize());
DBG ("IOHelper: output size: " << output.getSize());
const int nChannels = juce::jmin (input.getNumberOfChannels(), output.getNumberOfChannels());
const int _nChannels =
juce::jmin (input.getPreviousNumberOfChannels(), output.getPreviousNumberOfChannels());
const int samplesPerBlock = getBlockSize();
const double sampleRate = getSampleRate();
const int maxLfoDepth = static_cast (ceil (
parameters.getParameterRange ("lfoDepthL").getRange().getEnd() * sampleRate / 500.0f));
const int maxDelay =
static_cast (ceil (sampleRate * 60.0f
/ (parameters.getParameterRange ("delayBPML").start
* parameters.getParameterRange ("delayMultL").start)
+ maxLfoDepth));
for (int i = 0; i < 2; ++i)
{
delayBuffer[i].setSize (nChannels, samplesPerBlock);
delayBuffer[i].clear();
delayOutBuffer[i].setSize (nChannels, samplesPerBlock);
delayOutBuffer[i].clear();
if (nChannels > _nChannels)
{
for (int channel = _nChannels; channel < nChannels; ++channel)
{
lowPassFilters[i].add (new juce::IIRFilter());
highPassFilters[i].add (new juce::IIRFilter());
}
}
else
{
const int diff = _nChannels - nChannels;
lowPassFilters[i].removeRange (nChannels, diff);
highPassFilters[i].removeRange (nChannels, diff);
}
}
updateFilterCoeffs = true;
}
void DualDelayAudioProcessor::updateFilters (const int endIdx)
{
const int nCh = lowPassFilters[endIdx].size();
const double fs = getSampleRate();
for (int channel = 0; channel < nCh; ++channel)
{
lowPassFilters[endIdx][channel]->setCoefficients (juce::IIRCoefficients::makeLowPass (
fs,
juce::jmin (fs / 2.0, static_cast (LPcutOff[endIdx]->load()))));
highPassFilters[endIdx][channel]->setCoefficients (juce::IIRCoefficients::makeHighPass (
fs,
juce::jmin (fs / 2.0, static_cast (HPcutOff[endIdx]->load()))));
}
updateFilterCoeffs = false;
}
std::tuple DualDelayAudioProcessor::msToBPM (const float ms)
{
float transformedBPM = 60000.0f / ms;
float mult = 1.0f;
const float maxBPM = parameters.getParameterRange ("delayBPML").getRange().getEnd();
const float minBPM = parameters.getParameterRange ("delayBPML").getRange().getStart();
const float maxMult = parameters.getParameterRange ("delayMultL").getRange().getEnd();
const float minMult = parameters.getParameterRange ("delayMultL").getRange().getStart();
if (transformedBPM > maxBPM)
{
mult = static_cast (
juce::nextPowerOfTwo (static_cast (ceil (transformedBPM / maxBPM))));
transformedBPM /= mult;
mult = juce::jmin (mult, maxMult);
}
else if (transformedBPM < minBPM)
{
float transformedBPM_tmp = juce::jmax (transformedBPM / minMult,
minBPM); // Clip values to avoid division by zero
mult = static_cast (
juce::nextPowerOfTwo (static_cast (floor (transformedBPM / minBPM))));
mult = juce::jmax (mult, 1.0f) * minMult;
transformedBPM /= mult;
}
return { transformedBPM, mult };
}
//==============================================================================
std::vector>
DualDelayAudioProcessor::createParameterLayout()
{
// add your audio parameters here
std::vector> params;
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"orderSetting",
"Ambisonics Order",
"",
juce::NormalisableRange (0.0f, 8.0f, 1.0f),
0.0f,
[] (float value)
{
if (value >= 0.5f && value < 1.5f)
return "0th";
else if (value >= 1.5f && value < 2.5f)
return "1st";
else if (value >= 2.5f && value < 3.5f)
return "2nd";
else if (value >= 3.5f && value < 4.5f)
return "3rd";
else if (value >= 4.5f && value < 5.5f)
return "4th";
else if (value >= 5.5f && value < 6.5f)
return "5th";
else if (value >= 6.5f && value < 7.5f)
return "6th";
else if (value >= 7.5f)
return "7th";
else
return "Auto";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"useSN3D",
"Normalization",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
1.0f,
[] (float value)
{
if (value >= 0.5f)
return "SN3D";
else
return "N3D";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"dryGain",
"Dry amount",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
0.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"wetGainL",
"Wet amount left",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-6.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"wetGainR",
"Wet amount right",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-6.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"delayBPML",
"delay left",
"BPM",
juce::NormalisableRange (46.0f, 320.0f, 0.001f),
100.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"delayBPMR",
"delay right",
"BPM",
juce::NormalisableRange (46.0f, 320.0f, 0.001f),
120.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"delayMultL",
"delay multiplier left",
"",
juce::NormalisableRange (1.0f, 16.0f, 1.0f, 0.6f),
1.0f,
[] (float value) { return juce::String (std::round (value), 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"delayMultR",
"delay multiplier right",
"",
juce::NormalisableRange (1.0f, 16.0f, 1.0f, 0.6f),
1.0f,
[] (float value) { return juce::String (std::round (value), 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"syncL",
"sync BPM left",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value >= 0.5f) ? "on" : "off"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"syncR",
"sync BPM right",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value >= 0.5f) ? "on" : "off"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"transformModeL",
"transform mode left",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "warp" : "rotate"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"transformModeR",
"transform mode right",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "warp" : "rotate"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"yawL",
"yaw left",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
10.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"yawR",
"yaw right",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
-7.5f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"pitchL",
"pitch left",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
0.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"pitchR",
"pitch right",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
0.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"rollL",
"roll left",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
0.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"rollR",
"roll right",
juce::CharPointer_UTF8 (R"(°)"),
juce::NormalisableRange (-180.0f, 180.0f, 0.1f),
0.0f,
[] (float value) { return juce::String (value, 1); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpModeAzL",
"Warp mode azimuth left",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "One Point" : "Two Point"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpModeElL",
"warp mode elevation left",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "Pole" : "Equator"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpModeAzR",
"Warp mode azimuth right",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "One Point" : "Two Point"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpModeElR",
"warp mode elevation right",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
0.0f,
[] (float value) { return (value > 0.5f) ? "Pole" : "Equator"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpFactorAzL",
"warp factor azimuth left",
"",
juce::NormalisableRange (-0.9f, 0.9f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpFactorElL",
"warp factor elevation left",
"",
juce::NormalisableRange (-0.9f, 0.9f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpFactorAzR",
"warp factor azimuth right",
"",
juce::NormalisableRange (-0.9f, 0.9f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"warpFactorElR",
"warp factor elevation right",
"",
juce::NormalisableRange (-0.9f, 0.9f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"LPcutOffL",
"lowpass frequency left",
"Hz",
juce::NormalisableRange (20.0f, 20000.0f, 1.0f, 0.2),
20000.0f,
[] (float value) { return juce::String (value, 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"LPcutOffR",
"lowpass frequency right",
"Hz",
juce::NormalisableRange (20.0f, 20000.0f, 1.0f, 0.2),
20000.0f,
[] (float value) { return juce::String (value, 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"HPcutOffL",
"highpass frequency left",
"Hz",
juce::NormalisableRange (20.0f, 20000.0f, 1.0f, 0.2),
100.0f,
[] (float value) { return juce::String (value, 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"HPcutOffR",
"highpass frequency right",
"Hz",
juce::NormalisableRange (20.0f, 20000.0f, 1.0f, 0.2),
100.0f,
[] (float value) { return juce::String (value, 0); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"feedbackL",
"feedback left",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-8.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"feedbackR",
"feedback right",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-8.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"xfeedbackL",
"cross feedback left",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-20.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"xfeedbackR",
"cross feedback right",
"dB",
juce::NormalisableRange (-60.0f, 0.0f, 0.1f),
-20.0f,
[] (float value) { return (value >= -59.9f) ? juce::String (value, 1) : "-inf"; },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"lfoRateL",
"LFO left rate",
"Hz",
juce::NormalisableRange (0.0f, 10.0f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"lfoRateR",
"LFO right rate",
"Hz",
juce::NormalisableRange (0.0f, 10.0f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"lfoDepthL",
"LFO left depth",
"ms",
juce::NormalisableRange (0.0f, 1.0f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"lfoDepthR",
"LFO right depth",
"ms",
juce::NormalisableRange (0.0f, 1.0f, 0.01f),
0.0f,
[] (float value) { return juce::String (value, 2); },
nullptr));
return params;
}
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/Source/PluginEditor.cpp 0000664 0025372 0025372 00000135144 15257173642 031054 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Author: Daniel Rudrich
Copyright (c) 2017 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#include "PluginEditor.h"
#include "PluginProcessor.h"
//==============================================================================
DualDelayAudioProcessorEditor::DualDelayAudioProcessorEditor (
DualDelayAudioProcessor& p,
juce::AudioProcessorValueTreeState& vts) :
juce::AudioProcessorEditor (&p),
processor (p),
valueTreeState (vts),
footer (p.getOSCParameterInterface())
{
setLookAndFeel (&globalLaF);
addAndMakeVisible (&title);
title.setTitle (juce::String ("Dual"), juce::String ("Delay"));
title.setFont (globalLaF.robotoBold, globalLaF.robotoLight);
addAndMakeVisible (&footer);
cbNormalizationAttachement.reset (
new ComboBoxAttachment (valueTreeState,
"useSN3D",
*title.getInputWidgetPtr()->getNormCbPointer()));
cbOrderAttachement.reset (
new ComboBoxAttachment (valueTreeState,
"orderSetting",
*title.getInputWidgetPtr()->getOrderCbPointer()));
addAndMakeVisible (&SlDryGain);
SlDryGainAttachment.reset (new SliderAttachment (valueTreeState, "dryGain", SlDryGain));
SlDryGain.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlDryGain.setTextValueSuffix (" dB");
SlDryGain.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlDryGain.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
auto delayMSRange = juce::NormalisableRange (
60000.0f
/ (valueTreeState.getParameterRange ("delayBPML").end
* valueTreeState.getParameterRange ("delayMultL").end),
60000.0f
/ (valueTreeState.getParameterRange ("delayBPML").start
* valueTreeState.getParameterRange ("delayMultL").start),
0.1f);
// =========================== LEFT SIDE ==============================================================
addAndMakeVisible (&cbLeftTransfromMode);
cbLeftTransfromMode.setJustificationType (juce::Justification::centred);
cbLeftTransfromMode.addSectionHeading ("Mode");
cbLeftTransfromMode.addItem ("Rotate", 1);
cbLeftTransfromMode.addItem ("Warp", 2);
cbLeftTransfromModeAttachment.reset (
new ComboBoxAttachment (valueTreeState, "transformModeL", cbLeftTransfromMode));
cbLeftTransfromMode.addListener (this);
bool leftTrMode =
(valueTreeState.getParameter ("transformModeL")->getValue() > 0.5f) ? true : false;
SlLeftYaw.setVisible (! leftTrMode);
addChildComponent (&SlLeftYaw);
SlLeftYawAttachment.reset (new SliderAttachment (valueTreeState, "yawL", SlLeftYaw));
SlLeftYaw.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftYaw.setReverse (true);
SlLeftYaw.setTextValueSuffix (" deg");
SlLeftYaw.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftYaw.setRotaryParameters (juce::MathConstants::pi,
3 * juce::MathConstants::pi,
false);
SlLeftYaw.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
SlLeftYaw.addListener (this);
SlLeftPitch.setVisible (! leftTrMode);
addChildComponent (&SlLeftPitch);
SlLeftPitchAttachment.reset (new SliderAttachment (valueTreeState, "pitchL", SlLeftPitch));
SlLeftPitch.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftPitch.setReverse (true);
SlLeftPitch.setTextValueSuffix (" deg");
SlLeftPitch.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftPitch.setRotaryParameters (0.5 * juce::MathConstants::pi,
2.5 * juce::MathConstants::pi,
false);
SlLeftPitch.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
SlLeftPitch.addListener (this);
SlLeftRoll.setVisible (! leftTrMode);
addChildComponent (&SlLeftRoll);
SlLeftRollAttachment.reset (new SliderAttachment (valueTreeState, "rollL", SlLeftRoll));
SlLeftRoll.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftRoll.setReverse (false);
SlLeftRoll.setTextValueSuffix (" deg");
SlLeftRoll.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftRoll.setRotaryParameters (juce::MathConstants::pi,
3 * juce::MathConstants::pi,
false);
SlLeftRoll.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
SlLeftRoll.addListener (this);
cbLeftWarpTypeAz.setVisible (leftTrMode);
addChildComponent (&cbLeftWarpTypeAz);
cbLeftWarpTypeAz.setJustificationType (juce::Justification::centred);
cbLeftWarpTypeAz.addSectionHeading ("Warp type azimuth");
cbLeftWarpTypeAz.addItem ("One Point", 1);
cbLeftWarpTypeAz.addItem ("Two Point", 2);
cbLeftWarpTypeAzAttachment.reset (
new ComboBoxAttachment (valueTreeState, "warpModeAzL", cbLeftWarpTypeAz));
cbLeftWarpTypeEl.setVisible (leftTrMode);
addChildComponent (&cbLeftWarpTypeEl);
cbLeftWarpTypeEl.setJustificationType (juce::Justification::centred);
cbLeftWarpTypeEl.addSectionHeading ("Warp type elevation");
cbLeftWarpTypeEl.addItem ("Pole", 1);
cbLeftWarpTypeEl.addItem ("Equator", 2);
cbLeftWarpTypeElAttachment.reset (
new ComboBoxAttachment (valueTreeState, "warpModeElL", cbLeftWarpTypeEl));
SlLeftWarpFactorAz.setVisible (leftTrMode);
addChildComponent (&SlLeftWarpFactorAz);
SlLeftWarpFactorAzAttachment.reset (
new SliderAttachment (valueTreeState, "warpFactorAzL", SlLeftWarpFactorAz));
SlLeftWarpFactorAz.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftWarpFactorAz.setTextValueSuffix ("");
SlLeftWarpFactorAz.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
// SlLeftWarpFactorAz.setRotaryParameters (juce::MathConstants::pi,
// 3 * juce::MathConstants::pi,
// false);
SlLeftWarpFactorAz.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[0]);
SlLeftWarpFactorEl.setVisible (leftTrMode);
addChildComponent (&SlLeftWarpFactorEl);
SlLeftWarpFactorElAttachment.reset (
new SliderAttachment (valueTreeState, "warpFactorElL", SlLeftWarpFactorEl));
SlLeftWarpFactorEl.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftWarpFactorEl.setTextValueSuffix ("");
SlLeftWarpFactorEl.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
// SlLeftWarpFactorEl.setRotaryParameters (juce::MathConstants::pi,
// 3 * juce::MathConstants::pi,
// false);
SlLeftWarpFactorEl.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[0]);
addAndMakeVisible (&SlLeftDelay);
SlLeftDelayAttachment.reset (new SliderAttachment (valueTreeState, "delayBPML", SlLeftDelay));
SlLeftDelay.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftDelay.setTextValueSuffix (" BPM");
SlLeftDelay.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftDelay.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[1]);
SlLeftDelay.addListener (this);
SlLeftDelayMS.setVisible (false);
addChildComponent (&SlLeftDelayMS);
SlLeftDelayMS.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftDelayMS.setNormalisableRange (delayMSRange);
SlLeftDelayMS.setTextValueSuffix (" ms");
SlLeftDelayMS.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftDelayMS.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[1]);
SlLeftDelayMS.setValue ((60000.0f / *valueTreeState.getRawParameterValue ("delayBPML"))
/ *valueTreeState.getRawParameterValue ("delayMultL"));
SlLeftDelayMS.addListener (this);
addAndMakeVisible (&tbLeftSync);
btLeftSyncAttachment.reset (new ButtonAttachment (valueTreeState, "syncL", tbLeftSync));
tbLeftSync.setButtonText ("Sync");
tbLeftSync.setColour (juce::ToggleButton::tickColourId, globalLaF.ClWidgetColours[1]);
tbLeftSync.addListener (this);
addAndMakeVisible (&btLeftTap);
btLeftTap.setButtonText ("Tap");
btLeftTap.setColour (juce::TextButton::buttonColourId, globalLaF.ClWidgetColours[1]);
btLeftTap.addListener (this);
addAndMakeVisible (&SlLeftDelayMult);
SlLeftDelayMultAttachment.reset (
new SliderAttachment (valueTreeState, "delayMultL", SlLeftDelayMult));
SlLeftDelayMult.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftDelayMult.setTextValueSuffix ("");
SlLeftDelayMult.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftDelayMult.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[1]);
SlLeftDelayMult.addListener (this);
addAndMakeVisible (&SlLeftLfoRate);
SlLeftLfoRateAttachment.reset (
new SliderAttachment (valueTreeState, "lfoRateL", SlLeftLfoRate));
SlLeftLfoRate.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftLfoRate.setTextValueSuffix (" Hz");
SlLeftLfoRate.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftLfoRate.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[2]);
addAndMakeVisible (&SlLeftLfoDepth);
SlLeftLfoDepthAttachment.reset (
new SliderAttachment (valueTreeState, "lfoDepthL", SlLeftLfoDepth));
SlLeftLfoDepth.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftLfoDepth.setTextValueSuffix (" ms");
SlLeftLfoDepth.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftLfoDepth.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[2]);
addAndMakeVisible (&dblSlLeftFilter);
dblSlLeftFilterHpAttachment.reset (
new SliderAttachment (valueTreeState,
"HPcutOffL",
*dblSlLeftFilter.getLeftSliderAddress()));
dblSlLeftFilterLpAttachment.reset (
new SliderAttachment (valueTreeState,
"LPcutOffL",
*dblSlLeftFilter.getRightSliderAddress()));
dblSlLeftFilter.setRangeAndPosition (valueTreeState.getParameterRange ("HPcutOffL"),
valueTreeState.getParameterRange ("LPcutOffL"));
dblSlLeftFilter.setColour (globalLaF.ClWidgetColours[1]);
addAndMakeVisible (&SlLeftFb);
SlLeftFbAttachment.reset (new SliderAttachment (valueTreeState, "feedbackL", SlLeftFb));
SlLeftFb.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftFb.setTextValueSuffix (" dB");
SlLeftFb.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftFb.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[3]);
addAndMakeVisible (&SlLeftCrossFb);
SlLeftCrossFbAttachment.reset (
new SliderAttachment (valueTreeState, "xfeedbackL", SlLeftCrossFb));
SlLeftCrossFb.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftCrossFb.setTextValueSuffix (" dB");
SlLeftCrossFb.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftCrossFb.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[3]);
addAndMakeVisible (&SlLeftGain);
SlLeftGainAttachment.reset (new SliderAttachment (valueTreeState, "wetGainL", SlLeftGain));
SlLeftGain.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlLeftGain.setTextValueSuffix (" dB");
SlLeftGain.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlLeftGain.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
addAndMakeVisible (&btTimeMode);
btTimeMode.setButtonText ("BPM");
btTimeMode.setToggleable (true);
btTimeMode.setClickingTogglesState (! isModeMS);
btTimeMode.setColour (juce::TextButton::buttonColourId, globalLaF.ClWidgetColours[1]);
btTimeMode.setColour (juce::TextButton::buttonOnColourId, globalLaF.ClWidgetColours[1]);
btTimeMode.addListener (this);
// =========================== RIGHT SIDE ================================================================
addAndMakeVisible (&cbRightTransfromMode);
cbRightTransfromMode.setJustificationType (juce::Justification::centred);
cbRightTransfromMode.addSectionHeading ("Mode");
cbRightTransfromMode.addItem ("Rotate", 1);
cbRightTransfromMode.addItem ("Warp", 2);
cbRightTransfromModeAttachment.reset (
new ComboBoxAttachment (valueTreeState, "transformModeR", cbRightTransfromMode));
cbRightTransfromMode.addListener (this);
bool rightTrMode =
(valueTreeState.getParameter ("transformModeR")->getValue() > 0.5f) ? true : false;
SlRightYaw.setVisible (! rightTrMode);
addChildComponent (&SlRightYaw);
SlRightYawAttachment.reset (new SliderAttachment (valueTreeState, "yawR", SlRightYaw));
SlRightYaw.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightYaw.setReverse (true);
SlRightYaw.setTextValueSuffix (" deg");
SlRightYaw.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightYaw.setRotaryParameters (juce::MathConstants::pi,
3 * juce::MathConstants::pi,
false);
SlRightYaw.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
SlRightYaw.addListener (this);
SlRightPitch.setVisible (! rightTrMode);
addChildComponent (&SlRightPitch);
SlRightPitchAttachment.reset (new SliderAttachment (valueTreeState, "pitchR", SlRightPitch));
SlRightPitch.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightPitch.setReverse (true);
SlRightPitch.setTextValueSuffix (" deg");
SlRightPitch.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightPitch.setRotaryParameters (0.5 * juce::MathConstants::pi,
2.5 * juce::MathConstants::pi,
false);
SlRightPitch.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[0]);
SlRightPitch.addListener (this);
SlRightRoll.setVisible (! rightTrMode);
addChildComponent (&SlRightRoll);
SlRightRollAttachment.reset (new SliderAttachment (valueTreeState, "rollR", SlRightRoll));
SlRightRoll.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightRoll.setReverse (false);
SlRightRoll.setTextValueSuffix (" deg");
SlRightRoll.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightRoll.setRotaryParameters (juce::MathConstants::pi,
3 * juce::MathConstants::pi,
false);
SlRightRoll.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
SlRightRoll.addListener (this);
cbRightWarpTypeAz.setVisible (rightTrMode);
addChildComponent (&cbRightWarpTypeAz);
cbRightWarpTypeAz.setJustificationType (juce::Justification::centred);
cbRightWarpTypeAz.addSectionHeading ("Warp type azimuth");
cbRightWarpTypeAz.addItem ("One Point", 1);
cbRightWarpTypeAz.addItem ("Two Point", 2);
cbRightWarpTypeAzAttachment.reset (
new ComboBoxAttachment (valueTreeState, "warpModeAzR", cbRightWarpTypeAz));
cbRightWarpTypeEl.setVisible (rightTrMode);
addChildComponent (&cbRightWarpTypeEl);
cbRightWarpTypeEl.setJustificationType (juce::Justification::centred);
cbRightWarpTypeEl.addSectionHeading ("Warp type elevation");
cbRightWarpTypeEl.addItem ("Pole", 1);
cbRightWarpTypeEl.addItem ("Equator", 2);
cbRightWarpTypeElAttachment.reset (
new ComboBoxAttachment (valueTreeState, "warpModeElR", cbRightWarpTypeEl));
SlRightWarpFactorAz.setVisible (rightTrMode);
addChildComponent (&SlRightWarpFactorAz);
SlRightWarpFactorAzAttachment.reset (
new SliderAttachment (valueTreeState, "warpFactorAzR", SlRightWarpFactorAz));
SlRightWarpFactorAz.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightWarpFactorAz.setTextValueSuffix ("");
SlRightWarpFactorAz.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
// SlRightWarpFactorAz.setRotaryParameters (juce::MathConstants::pi,
// 3 * juce::MathConstants::pi,
// false);
SlRightWarpFactorAz.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[0]);
SlRightWarpFactorEl.setVisible (rightTrMode);
addChildComponent (&SlRightWarpFactorEl);
SlRightWarpFactorElAttachment.reset (
new SliderAttachment (valueTreeState, "warpFactorElR", SlRightWarpFactorEl));
SlRightWarpFactorEl.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightWarpFactorEl.setTextValueSuffix ("");
SlRightWarpFactorEl.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
// SlRightWarpFactorEl.setRotaryParameters (juce::MathConstants::pi,
// 3 * juce::MathConstants::pi,
// false);
SlRightWarpFactorEl.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[0]);
addAndMakeVisible (&SlRightDelay);
SlRightDelayAttachment.reset (new SliderAttachment (valueTreeState, "delayBPMR", SlRightDelay));
SlRightDelay.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightDelay.setTextValueSuffix (" BPM");
SlRightDelay.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightDelay.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[1]);
SlRightDelay.addListener (this);
SlRightDelayMS.setVisible (false);
addChildComponent (&SlRightDelayMS);
SlRightDelayMS.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightDelayMS.setNormalisableRange (delayMSRange);
SlRightDelayMS.setTextValueSuffix (" ms");
SlRightDelayMS.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightDelayMS.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[1]);
SlRightDelayMS.setValue ((60000.0f / *valueTreeState.getRawParameterValue ("delayBPMR"))
/ *valueTreeState.getRawParameterValue ("delayMultR"));
SlRightDelayMS.addListener (this);
addAndMakeVisible (&tbRightSync);
btRightSyncAttachment.reset (new ButtonAttachment (valueTreeState, "syncR", tbRightSync));
tbRightSync.setButtonText ("Sync");
tbRightSync.setColour (juce::ToggleButton::tickColourId, globalLaF.ClWidgetColours[1]);
tbRightSync.addListener (this);
addAndMakeVisible (&btRightTap);
btRightTap.setButtonText ("Tap");
btRightTap.setColour (juce::TextButton::buttonColourId, globalLaF.ClWidgetColours[1]);
// btLeftTap.setColour (juce::TextButton::buttonOnColourId, globalLaF.ClWidgetColours[1]);
btRightTap.addListener (this);
addAndMakeVisible (&SlRightDelayMult);
SlRightDelayMultAttachment.reset (
new SliderAttachment (valueTreeState, "delayMultR", SlRightDelayMult));
SlRightDelayMult.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightDelayMult.setTextValueSuffix ("");
SlRightDelayMult.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightDelayMult.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[1]);
SlRightDelayMult.addListener (this);
addAndMakeVisible (&SlRightLfoRate);
SlRightLfoRateAttachment.reset (
new SliderAttachment (valueTreeState, "lfoRateR", SlRightLfoRate));
SlRightLfoRate.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightLfoRate.setTextValueSuffix (" Hz");
SlRightLfoRate.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightLfoRate.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[2]);
addAndMakeVisible (&SlRightLfoDepth);
SlRightLfoDepthAttachment.reset (
new SliderAttachment (valueTreeState, "lfoDepthR", SlRightLfoDepth));
SlRightLfoDepth.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightLfoDepth.setTextValueSuffix (" ms");
SlRightLfoDepth.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightLfoDepth.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[2]);
addAndMakeVisible (&dblSlRightFilter);
dblSlRightFilterHpAttachment.reset (
new SliderAttachment (valueTreeState,
"HPcutOffR",
*dblSlRightFilter.getLeftSliderAddress()));
dblSlRightFilterLpAttachment.reset (
new SliderAttachment (valueTreeState,
"LPcutOffR",
*dblSlRightFilter.getRightSliderAddress()));
dblSlRightFilter.setRangeAndPosition (valueTreeState.getParameterRange ("HPcutOffR"),
valueTreeState.getParameterRange ("LPcutOffR"));
dblSlRightFilter.getLeftSliderAddress()->setTextValueSuffix (" Hz");
dblSlRightFilter.getRightSliderAddress()->setTextValueSuffix (" Hz");
dblSlRightFilter.setColour (globalLaF.ClWidgetColours[1]);
addAndMakeVisible (&SlRightFb);
SlRightFbAttachment.reset (new SliderAttachment (valueTreeState, "feedbackR", SlRightFb));
SlRightFb.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightFb.setTextValueSuffix (" dB");
SlRightFb.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightFb.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[3]);
addAndMakeVisible (&SlRightCrossFb);
SlRightCrossFbAttachment.reset (
new SliderAttachment (valueTreeState, "xfeedbackR", SlRightCrossFb));
SlRightCrossFb.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightCrossFb.setTextValueSuffix (" dB");
SlRightCrossFb.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightCrossFb.setColour (juce::Slider::rotarySliderOutlineColourId,
globalLaF.ClWidgetColours[3]);
addAndMakeVisible (&SlRightGain);
SlRightGainAttachment.reset (new SliderAttachment (valueTreeState, "wetGainR", SlRightGain));
SlRightGain.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
SlRightGain.setTextValueSuffix (" dB");
SlRightGain.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 50, 15);
SlRightGain.setColour (juce::Slider::rotarySliderOutlineColourId, globalLaF.ClWidgetColours[0]);
// ============ GROUPCOMPONENTS =========
addAndMakeVisible (&gcRotDelL);
gcRotDelL.setText ("Rotation & Delay");
gcRotDelL.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcRotDelR);
gcRotDelR.setText ("Rotation & Delay");
gcRotDelR.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcFiltL);
gcFiltL.setText ("Spectral Filter");
gcFiltL.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcFiltR);
gcFiltR.setText ("Spectral Filter");
gcFiltR.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcFbL);
gcFbL.setText ("Feedback");
gcFbL.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcFbR);
gcFbR.setText ("Feedback");
gcFbR.setTextLabelPosition (juce::Justification::centred);
addAndMakeVisible (&gcOutput);
gcOutput.setText ("Output Mix");
gcOutput.setTextLabelPosition (juce::Justification::centred);
// ============ LABELS =========
lbYawL.setVisible (! leftTrMode);
addChildComponent (&lbYawL);
lbYawL.setText ("Yaw");
lbPitchL.setVisible (! leftTrMode);
addChildComponent (&lbPitchL);
lbPitchL.setText ("Pitch");
lbRollL.setVisible (! leftTrMode);
addChildComponent (&lbRollL);
lbRollL.setText ("Roll");
addAndMakeVisible (&lbDelL);
lbDelL.setText ("Delay");
addAndMakeVisible (&lbDelMultL);
lbDelMultL.setText ("Mult");
addAndMakeVisible (&lbFbL);
lbFbL.setText ("Self");
addAndMakeVisible (&lbXFbL);
lbXFbL.setText ("Cross");
lbWarpFactorAzL.setVisible (leftTrMode);
addChildComponent (&lbWarpFactorAzL);
lbWarpFactorAzL.setText ("Warp Az");
lbWarpFactorElL.setVisible (leftTrMode);
addChildComponent (&lbWarpFactorElL);
lbWarpFactorElL.setText ("Warp El");
lbAzModeL.setVisible (leftTrMode);
addChildComponent (&lbAzModeL);
lbAzModeL.setText ("Az:");
lbElModeL.setVisible (leftTrMode);
addChildComponent (&lbElModeL);
lbElModeL.setText ("El:");
addAndMakeVisible (&lbYawR);
lbYawR.setText ("Yaw");
addAndMakeVisible (&lbPitchR);
lbPitchR.setText ("Pitch");
addAndMakeVisible (&lbRollR);
lbRollR.setText ("Roll");
addAndMakeVisible (&lbDelR);
lbDelR.setText ("Delay");
addAndMakeVisible (&lbDelMultR);
lbDelMultR.setText ("Mult");
addAndMakeVisible (&lbFbR);
lbFbR.setText ("Self");
addAndMakeVisible (&lbXFbR);
lbXFbR.setText ("Cross");
lbWarpFactorAzR.setVisible (rightTrMode);
addChildComponent (&lbWarpFactorAzR);
lbWarpFactorAzR.setText ("Warp Az");
lbWarpFactorElR.setVisible (rightTrMode);
addChildComponent (&lbWarpFactorElR);
lbWarpFactorElR.setText ("Warp El");
lbAzModeR.setVisible (rightTrMode);
addChildComponent (&lbAzModeR);
lbAzModeR.setText ("Az:");
lbElModeR.setVisible (rightTrMode);
addChildComponent (&lbElModeR);
lbElModeR.setText ("El:");
addAndMakeVisible (&lbGainL);
lbGainL.setText ("Delay I");
addAndMakeVisible (&lbGainR);
lbGainR.setText ("Delay II");
addAndMakeVisible (&lbGainDry);
lbGainDry.setText ("Dry");
addAndMakeVisible (&lbLfoL);
lbLfoL.setText ("Rate", "LFO", "Depth", false, true, false);
addAndMakeVisible (&lbLfoR);
lbLfoR.setText ("Rate", "LFO", "Depth", false, true, false);
addAndMakeVisible (&lbFilterL);
lbFilterL.setText ("HighPass", "Cutoff Frequency", "LowPass", false, true, false);
addAndMakeVisible (&lbFilterR);
lbFilterR.setText ("HighPass", "Cutoff Frequency", "LowPass", false, true, false);
setSize (780, 550);
startTimer (20);
}
DualDelayAudioProcessorEditor::~DualDelayAudioProcessorEditor()
{
setLookAndFeel (nullptr);
}
//==============================================================================
void DualDelayAudioProcessorEditor::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (globalLaF.ClBackground);
}
void DualDelayAudioProcessorEditor::resized()
{
// This is generally where you'll want to lay out the positions of any
// subcomponents in your editor..
const int leftRightMargin = 30;
const int headerHeight = 60;
const int footerHeight = 25;
const int textHeight = 14;
const int sliderHeight = 70;
const int sliderWidth = 55;
const int sliderSpacing = 10;
updateDelayUnit (! isModeMS);
juce::Rectangle area (getLocalBounds());
juce::Rectangle groupArea;
juce::Rectangle sliderRow;
juce::Rectangle footerArea (area.removeFromBottom (footerHeight));
footer.setBounds (footerArea);
area.removeFromLeft (leftRightMargin);
area.removeFromRight (leftRightMargin);
juce::Rectangle headerArea = area.removeFromTop (headerHeight);
title.setBounds (headerArea);
area.removeFromTop (10);
juce::Rectangle tempArea;
// ======== BEGIN: Rotations and Delays =========
tempArea = area.removeFromTop (40 + 2 * sliderHeight + 2 * textHeight);
// ------ left side ---------
groupArea = tempArea.removeFromLeft (5 * sliderWidth + 4 * sliderSpacing);
gcRotDelL.setBounds (groupArea);
groupArea.removeFromTop (30);
// First row
sliderRow = groupArea.removeFromTop (sliderHeight);
auto tmp_componentArea = sliderRow.removeFromLeft (sliderWidth);
btLeftTap.setBounds (tmp_componentArea.removeFromTop (45));
tmp_componentArea.removeFromTop (5);
tbLeftSync.setBounds (tmp_componentArea.removeFromTop (20));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
tmp_componentArea = sliderRow.removeFromLeft (sliderWidth);
SlLeftDelay.setBounds (tmp_componentArea);
SlLeftDelayMS.setBounds (tmp_componentArea);
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlLeftDelayMult.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlLeftLfoRate.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlLeftLfoDepth.setBounds (sliderRow.removeFromLeft (sliderWidth));
// First row labels
sliderRow = groupArea.removeFromTop (textHeight);
sliderRow.removeFromLeft (sliderWidth + sliderSpacing);
lbDelL.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbDelMultL.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbLfoL.setBounds (sliderRow.removeFromLeft (2 * sliderWidth + 10).reduced (15, 0));
// Second row
groupArea.removeFromTop (sliderSpacing);
sliderRow = groupArea.removeFromTop (sliderHeight);
tmp_componentArea = sliderRow.removeFromLeft (2 * sliderWidth + sliderSpacing);
cbLeftTransfromMode.setBounds (tmp_componentArea.removeFromTop (20));
tmp_componentArea.removeFromTop (5);
auto tmp_cbArea = tmp_componentArea.removeFromTop (20);
lbAzModeL.setBounds (tmp_cbArea.removeFromLeft (15));
tmp_cbArea.removeFromLeft (5);
cbLeftWarpTypeAz.setBounds (tmp_cbArea);
tmp_componentArea.removeFromTop (5);
tmp_cbArea = tmp_componentArea.removeFromTop (20);
lbElModeL.setBounds (tmp_cbArea.removeFromLeft (15));
tmp_cbArea.removeFromLeft (5);
cbLeftWarpTypeEl.setBounds (tmp_cbArea);
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
tmp_componentArea = sliderRow;
SlLeftYaw.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlLeftPitch.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlLeftRoll.setBounds (sliderRow);
tmp_componentArea.reduce ((sliderWidth + sliderSpacing) * 0.5f, 0);
SlLeftWarpFactorAz.setBounds (tmp_componentArea.removeFromLeft (sliderWidth));
tmp_componentArea.removeFromLeft (sliderSpacing);
SlLeftWarpFactorEl.setBounds (tmp_componentArea);
// Second Row Labels
sliderRow = groupArea.removeFromTop (textHeight);
sliderRow.removeFromLeft (2 * sliderWidth + 2 * sliderSpacing);
tmp_componentArea = sliderRow;
lbYawL.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbPitchL.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbRollL.setBounds (sliderRow);
tmp_componentArea.reduce ((sliderWidth + sliderSpacing) * 0.5f, 0);
lbWarpFactorAzL.setBounds (tmp_componentArea.removeFromLeft (sliderWidth));
tmp_componentArea.removeFromLeft (sliderSpacing);
lbWarpFactorElL.setBounds (tmp_componentArea);
// ------ right side --------
groupArea = tempArea.removeFromRight (5 * sliderWidth + 4 * sliderSpacing);
gcRotDelR.setBounds (groupArea);
groupArea.removeFromTop (30);
// First row
sliderRow = groupArea.removeFromTop (sliderHeight);
tmp_componentArea = sliderRow.removeFromLeft (sliderWidth);
btRightTap.setBounds (tmp_componentArea.removeFromTop (45));
tmp_componentArea.removeFromTop (5);
tbRightSync.setBounds (tmp_componentArea.removeFromTop (20));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
tmp_componentArea = sliderRow.removeFromLeft (sliderWidth);
SlRightDelay.setBounds (tmp_componentArea);
SlRightDelayMS.setBounds (tmp_componentArea);
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlRightDelayMult.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlRightLfoRate.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlRightLfoDepth.setBounds (sliderRow.removeFromLeft (sliderWidth));
// First row labels
sliderRow = groupArea.removeFromTop (textHeight);
sliderRow.removeFromLeft (sliderWidth + sliderSpacing);
lbDelR.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbDelMultR.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbLfoR.setBounds (sliderRow.removeFromLeft (2 * sliderWidth + 10).reduced (15, 0));
// Second row
groupArea.removeFromTop (sliderSpacing);
sliderRow = groupArea.removeFromTop (sliderHeight);
tmp_componentArea = sliderRow.removeFromLeft (2 * sliderWidth + sliderSpacing);
cbRightTransfromMode.setBounds (tmp_componentArea.removeFromTop (20));
tmp_componentArea.removeFromTop (5);
tmp_cbArea = tmp_componentArea.removeFromTop (20);
lbAzModeR.setBounds (tmp_cbArea.removeFromLeft (15));
tmp_cbArea.removeFromLeft (5);
cbRightWarpTypeAz.setBounds (tmp_cbArea);
tmp_componentArea.removeFromTop (5);
tmp_cbArea = tmp_componentArea.removeFromTop (20);
lbElModeR.setBounds (tmp_cbArea.removeFromLeft (15));
tmp_cbArea.removeFromLeft (5);
cbRightWarpTypeEl.setBounds (tmp_cbArea);
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
tmp_componentArea = sliderRow;
SlRightYaw.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlRightPitch.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing); // spacing between rotary sliders
SlRightRoll.setBounds (sliderRow);
tmp_componentArea.reduce ((sliderWidth + sliderSpacing) * 0.5f, 0);
SlRightWarpFactorAz.setBounds (tmp_componentArea.removeFromLeft (sliderWidth));
tmp_componentArea.removeFromLeft (sliderSpacing);
SlRightWarpFactorEl.setBounds (tmp_componentArea);
// Second Row Labels
sliderRow = groupArea.removeFromTop (textHeight);
sliderRow.removeFromLeft (2 * sliderWidth + 2 * sliderSpacing);
tmp_componentArea = sliderRow;
lbYawR.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbPitchR.setBounds (sliderRow.removeFromLeft (sliderWidth));
sliderRow.removeFromLeft (sliderSpacing);
lbRollR.setBounds (sliderRow);
tmp_componentArea.reduce ((sliderWidth + sliderSpacing) * 0.5f, 0);
lbWarpFactorAzR.setBounds (tmp_componentArea.removeFromLeft (sliderWidth));
tmp_componentArea.removeFromLeft (sliderSpacing);
lbWarpFactorElR.setBounds (tmp_componentArea);
// ======== END: Rotations and Delays =================
// ======== BEGIN: BPM/MS button ===========
int actualWidth = tempArea.getWidth();
int wantedWidth = 40;
tempArea.removeFromLeft (juce::roundToInt ((actualWidth - wantedWidth) / 2));
tempArea.removeFromTop (25 + textHeight);
btTimeMode.setBounds (tempArea.removeFromLeft (wantedWidth).removeFromTop (25));
// ======== BEGIN: BPM/MS button ===========
area.removeFromTop (30); // spacing
// ======== BEGIN: Filters =============
tempArea = area.removeFromTop (60 + textHeight);
// ----- left side ------
groupArea = tempArea.removeFromLeft (330);
gcFiltL.setBounds (groupArea);
groupArea.removeFromTop (30);
dblSlLeftFilter.setBounds (groupArea.removeFromTop (30));
lbFilterL.setBounds (groupArea.reduced (9, 0));
// ----- right side ------
groupArea = tempArea.removeFromRight (330);
gcFiltR.setBounds (groupArea);
groupArea.removeFromTop (30);
dblSlRightFilter.setBounds (groupArea.removeFromTop (30));
lbFilterR.setBounds (groupArea.reduced (9, 0));
// ======== END: Filters ===============
area.removeFromTop (30); // spacing
// ======== BEGIN: Feedback =============
tempArea = area.removeFromTop (30 + sliderHeight + textHeight);
// ------ left side -------------
groupArea = tempArea.removeFromLeft (120);
gcFbL.setBounds (groupArea);
groupArea.removeFromTop (30);
sliderRow = groupArea.removeFromTop (sliderHeight);
SlLeftFb.setBounds (sliderRow.removeFromLeft (55));
sliderRow.removeFromLeft (10);
SlLeftCrossFb.setBounds (sliderRow.removeFromLeft (55));
lbFbL.setBounds (groupArea.removeFromLeft (55));
groupArea.removeFromLeft (10);
lbXFbL.setBounds (groupArea.removeFromLeft (55));
// ------ right side -------------
groupArea = tempArea.removeFromRight (120);
gcFbR.setBounds (groupArea);
groupArea.removeFromTop (30);
sliderRow = groupArea.removeFromTop (sliderHeight);
SlRightFb.setBounds (sliderRow.removeFromLeft (55));
sliderRow.removeFromLeft (10);
SlRightCrossFb.setBounds (sliderRow.removeFromLeft (55));
lbFbR.setBounds (groupArea.removeFromLeft (55));
groupArea.removeFromLeft (10);
lbXFbR.setBounds (groupArea.removeFromLeft (55));
// ======== END: Feedback ===============
// ======== BEGIN: Output Mix ===========
actualWidth = tempArea.getWidth();
wantedWidth = 186;
tempArea.removeFromLeft (juce::roundToInt ((actualWidth - wantedWidth) / 2));
tempArea.setWidth (wantedWidth);
gcOutput.setBounds (tempArea);
tempArea.removeFromTop (30);
sliderRow = tempArea.removeFromTop (sliderHeight);
SlLeftGain.setBounds (sliderRow.removeFromLeft (55));
sliderRow.removeFromLeft (10); // spacing between rotary sliders
SlDryGain.setBounds (sliderRow.removeFromLeft (55));
sliderRow.removeFromLeft (10); // spacing between rotary sliders
SlRightGain.setBounds (sliderRow.removeFromLeft (55));
sliderRow.removeFromLeft (10); // spacing between rotary sliders
lbGainL.setBounds (tempArea.removeFromLeft (55));
tempArea.removeFromLeft (10); // spacing between rotary sliders
lbGainDry.setBounds (tempArea.removeFromLeft (55));
tempArea.removeFromLeft (10); // spacing between rotary sliders
lbGainR.setBounds (tempArea.removeFromLeft (55));
// ======== END: Output Mix =============
}
void DualDelayAudioProcessorEditor::buttonClicked (juce::Button* button)
{
if ((button == &btLeftTap) || (button == &btRightTap))
{
double currentTap = juce::Time::getMillisecondCounterHiRes();
double rawTapInterval = currentTap - lastTap;
if (rawTapInterval > maxTapIntervalMS)
{
tapIntervalMS = 0.0;
lastTap = currentTap;
}
else if (rawTapInterval > minTapIntervalMS)
{
if (tapIntervalMS > 0.0)
{
tapIntervalMS = tapBeta * tapIntervalMS + (1.0 - tapBeta) * rawTapInterval;
}
else
{
tapIntervalMS = rawTapInterval;
}
auto [bpm, mult] = processor.msToBPM (tapIntervalMS);
if (button == &btRightTap)
{
SlRightDelayMS.setValue (tapIntervalMS, juce::dontSendNotification);
SlRightDelay.setValue (bpm, juce::sendNotification);
SlRightDelayMult.setValue (mult, juce::sendNotification);
}
else
{
SlLeftDelayMS.setValue (tapIntervalMS, juce::dontSendNotification);
SlLeftDelay.setValue (bpm, juce::sendNotification);
SlLeftDelayMult.setValue (mult, juce::sendNotification);
}
lastTap = currentTap;
}
}
else if (button == &btTimeMode)
{
updateDelayUnit (! btTimeMode.getToggleState());
}
if (button == &tbLeftSync)
{
if (tbLeftSync.getToggleState())
{
// Disable slider when using host BPM
SlLeftDelayMS.setVisible (false);
SlLeftDelay.setVisible (false);
SlLeftDelayMult.setVisible (true);
lbDelL.setVisible (false);
}
else
{
// Restore default controls
updateDelayUnit (! isModeMS);
}
}
else if (button == &tbRightSync)
{
if (tbRightSync.getToggleState())
{
// Disable slider when using host BPM
SlRightDelayMS.setVisible (false);
SlRightDelay.setVisible (false);
SlRightDelayMult.setVisible (true);
lbDelR.setVisible (false);
}
else
{
// Restore default controls
updateDelayUnit (! isModeMS);
}
}
}
void DualDelayAudioProcessorEditor::sliderValueChanged (juce::Slider* slider)
{
if (slider == &SlLeftDelayMS)
{
if (isModeMS)
{
auto [bpm, mult] = processor.msToBPM (SlLeftDelayMS.getValue());
SlLeftDelay.setValue (bpm, juce::sendNotification);
SlLeftDelayMult.setValue (mult, juce::sendNotification);
}
}
else if (slider == &SlRightDelayMS)
{
if (isModeMS)
{
auto [bpm, mult] = processor.msToBPM (SlRightDelayMS.getValue());
SlRightDelay.setValue (bpm, juce::sendNotification);
SlRightDelayMult.setValue (mult, juce::sendNotification);
}
}
else if (slider == &SlLeftDelay)
{
float ms = (60000.0f / SlLeftDelay.getValue())
/ *valueTreeState.getRawParameterValue ("delayMultL");
SlLeftDelayMS.setValue (ms, juce::dontSendNotification);
}
else if (slider == &SlRightDelay)
{
float ms = (60000.0f / SlRightDelay.getValue())
/ *valueTreeState.getRawParameterValue ("delayMultR");
SlRightDelayMS.setValue (ms, juce::dontSendNotification);
}
else if (slider == &SlLeftDelayMult)
{
float ms = (60000.0f / SlLeftDelay.getValue()) / slider->getValue();
SlLeftDelayMS.setValue (ms, juce::dontSendNotification);
}
else if (slider == &SlRightDelayMult)
{
float ms = (60000.0f / SlRightDelay.getValue()) / slider->getValue();
SlRightDelayMS.setValue (ms, juce::dontSendNotification);
}
}
void DualDelayAudioProcessorEditor::comboBoxChanged (juce::ComboBox* comboBox)
{
if (comboBox == &cbLeftTransfromMode)
updateTransformMode (0);
else if (comboBox == &cbRightTransfromMode)
updateTransformMode (1);
}
void DualDelayAudioProcessorEditor::updateDelayUnit (bool isBPM)
{
if (! tbLeftSync.getToggleState())
{
SlLeftDelayMS.setVisible (! isBPM);
SlLeftDelay.setVisible (isBPM);
SlLeftDelayMult.setVisible (isBPM);
lbDelMultL.setVisible (isBPM);
}
if (! tbRightSync.getToggleState())
{
SlRightDelayMS.setVisible (! isBPM);
SlRightDelay.setVisible (isBPM);
SlRightDelayMult.setVisible (isBPM);
lbDelMultR.setVisible (isBPM);
}
btTimeMode.setButtonText (isBPM ? "BPM" : "ms");
isModeMS = ! isBPM;
}
void DualDelayAudioProcessorEditor::updateTransformMode (bool side)
{
if (side)
{
bool rightTrMode = cbRightTransfromMode.getSelectedId() == 1;
SlRightYaw.setVisible (rightTrMode);
SlRightPitch.setVisible (rightTrMode);
SlRightRoll.setVisible (rightTrMode);
lbYawR.setVisible (rightTrMode);
lbPitchR.setVisible (rightTrMode);
lbRollR.setVisible (rightTrMode);
cbRightWarpTypeAz.setVisible (! rightTrMode);
cbRightWarpTypeEl.setVisible (! rightTrMode);
SlRightWarpFactorAz.setVisible (! rightTrMode);
SlRightWarpFactorEl.setVisible (! rightTrMode);
lbWarpFactorAzR.setVisible (! rightTrMode);
lbWarpFactorElR.setVisible (! rightTrMode);
lbAzModeR.setVisible (! rightTrMode);
lbElModeR.setVisible (! rightTrMode);
}
else
{
bool leftTrMode = cbLeftTransfromMode.getSelectedId() == 1;
SlLeftYaw.setVisible (leftTrMode);
SlLeftPitch.setVisible (leftTrMode);
SlLeftRoll.setVisible (leftTrMode);
lbYawL.setVisible (leftTrMode);
lbPitchL.setVisible (leftTrMode);
lbRollL.setVisible (leftTrMode);
cbLeftWarpTypeAz.setVisible (! leftTrMode);
cbLeftWarpTypeEl.setVisible (! leftTrMode);
SlLeftWarpFactorAz.setVisible (! leftTrMode);
SlLeftWarpFactorEl.setVisible (! leftTrMode);
lbWarpFactorAzL.setVisible (! leftTrMode);
lbWarpFactorElL.setVisible (! leftTrMode);
lbAzModeL.setVisible (! leftTrMode);
lbElModeL.setVisible (! leftTrMode);
}
}
void DualDelayAudioProcessorEditor::timerCallback()
{
// === update titleBar widgets according to available input/output channel counts
auto sizes = processor.getMaxSize();
sizes.first = juce::jmin (sizes.first, sizes.second);
sizes.second = sizes.first;
title.setMaxSize (sizes);
// ==========================================
}
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/Source/PluginProcessor.h 0000664 0025372 0025372 00000011362 15257173642 031245 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Author: Daniel Rudrich, Felix Holzmüller
Copyright (c) 2017, 2024 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#pragma once
#include
#include "../../resources/AmbisonicRotator.h"
#include "../../resources/AmbisonicWarp.h"
#include "../../resources/AudioProcessorBase.h"
#include "../../resources/OnePoleFilter.h"
#include "../../resources/ambisonicTools.h"
#define ProcessorClass DualDelayAudioProcessor
#define OneOverSqrt2 0.7071067811865475f
//==============================================================================
/**
*/
class DualDelayAudioProcessor
: public AudioProcessorBase, IOTypes::Ambisonics<>, true>
{
public:
constexpr static int maxOrder = 7;
constexpr static int numberOfInputChannels = (maxOrder + 1) * (maxOrder + 1);
constexpr static int numberOfOutputChannels = (maxOrder + 1) * (maxOrder + 1);
//==============================================================================
DualDelayAudioProcessor();
~DualDelayAudioProcessor();
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
void processBlock (juce::AudioSampleBuffer&, juce::MidiBuffer&) override;
//==============================================================================
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
//==============================================================================
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override;
void changeProgramName (int index, const juce::String& newName) override;
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
void parameterChanged (const juce::String& parameterID, float newValue) override;
void updateBuffers() override;
void updateFilters (int idx);
std::tuple msToBPM (float ms);
//======= Parameters ===========================================================
std::vector> createParameterLayout();
//==============================================================================
private:
//==============================================================================
// parameters
std::atomic* dryGain;
std::atomic* wetGain[2];
std::atomic* delayBPM[2];
std::atomic* delayMult[2];
std::atomic* sync[2];
std::atomic* transformMode[2];
std::atomic* yaw[2];
std::atomic* pitch[2];
std::atomic* roll[2];
std::atomic* warpModeAz[2];
std::atomic* warpModeEl[2];
std::atomic* warpFactorAz[2];
std::atomic* warpFactorEl[2];
std::atomic* LPcutOff[2];
std::atomic* HPcutOff[2];
std::atomic* feedback[2];
std::atomic* xfeedback[2];
std::atomic* lfoRate[2];
std::atomic* lfoDepth[2];
std::atomic* orderSetting;
juce::dsp::Oscillator LFO[2];
AmbisonicRotator rotator[2];
AmbisonicWarp warp[2];
juce::OwnedArray lowPassFilters[2];
juce::OwnedArray highPassFilters[2];
juce::dsp::DelayLine delayLine[2];
juce::AudioBuffer delayBuffer[2];
juce::AudioBuffer delayOutBuffer[2];
OnePoleFilter delayTimeInterp[2];
bool updateFilterCoeffs = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DualDelayAudioProcessor)
};
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/Source/PluginEditor.h 0000664 0025372 0025372 00000014265 15257173642 030521 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Author: Daniel Rudrich
Copyright (c) 2017 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"
#include "../../resources/customComponents/DoubleSlider.h"
#include "../../resources/customComponents/ReverseSlider.h"
#include "../../resources/customComponents/SimpleLabel.h"
#include "../../resources/customComponents/TitleBar.h"
#include "../../resources/lookAndFeel/IEM_LaF.h"
typedef ReverseSlider::SliderAttachment SliderAttachment;
typedef juce::AudioProcessorValueTreeState::ComboBoxAttachment ComboBoxAttachment;
typedef juce::AudioProcessorValueTreeState::ButtonAttachment ButtonAttachment;
//==============================================================================
/**
*/
class DualDelayAudioProcessorEditor : public juce::AudioProcessorEditor,
private juce::Timer,
private juce::Time,
private juce::Button::Listener,
private juce::Slider::Listener,
private juce::ComboBox::Listener
{
public:
DualDelayAudioProcessorEditor (DualDelayAudioProcessor&, juce::AudioProcessorValueTreeState&);
~DualDelayAudioProcessorEditor();
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
void buttonClicked (juce::Button* button) override;
void sliderValueChanged (juce::Slider* slider) override;
void comboBoxChanged (juce::ComboBox* comboBox) override;
private:
LaF globalLaF;
void updateDelayUnit (bool isBPM);
void updateTransformMode (bool side);
void timerCallback() override;
DualDelayAudioProcessor& processor;
juce::AudioProcessorValueTreeState& valueTreeState;
TitleBar, NoIOWidget> title;
OSCFooter footer;
std::unique_ptr cbNormalizationAttachement;
std::unique_ptr cbOrderAttachement;
int maxPossibleOrder;
ReverseSlider SlDryGain;
std::unique_ptr SlDryGainAttachment;
// elements for left side
DoubleSlider dblSlLeftFilter;
ReverseSlider SlLeftYaw, SlLeftPitch, SlLeftRoll;
ReverseSlider SlLeftWarpFactorAz, SlLeftWarpFactorEl;
ReverseSlider SlLeftDelay, SlLeftDelayMS, SlLeftDelayMult, SlLeftLfoRate, SlLeftLfoDepth,
SlLeftFb, SlLeftCrossFb, SlLeftGain;
juce::ComboBox cbLeftTransfromMode, cbLeftWarpTypeAz, cbLeftWarpTypeEl;
juce::ToggleButton tbLeftSync;
juce::TextButton btLeftTap;
std::unique_ptr dblSlLeftFilterHpAttachment, dblSlLeftFilterLpAttachment;
std::unique_ptr SlLeftYawAttachment, SlLeftPitchAttachment,
SlLeftRollAttachment;
std::unique_ptr SlLeftWarpFactorAzAttachment, SlLeftWarpFactorElAttachment;
std::unique_ptr SlLeftDelayAttachment, SlLeftDelayMultAttachment,
SlLeftLfoRateAttachment, SlLeftLfoDepthAttachment, SlLeftFbAttachment,
SlLeftCrossFbAttachment, SlLeftGainAttachment;
std::unique_ptr cbLeftTransfromModeAttachment, cbLeftWarpTypeAzAttachment,
cbLeftWarpTypeElAttachment;
std::unique_ptr btLeftSyncAttachment;
// elements for right side
DoubleSlider dblSlRightFilter;
ReverseSlider SlRightYaw, SlRightPitch, SlRightRoll;
ReverseSlider SlRightWarpFactorAz, SlRightWarpFactorEl;
ReverseSlider SlRightDelay, SlRightDelayMS, SlRightDelayMult, SlRightLfoRate, SlRightLfoDepth,
SlRightFb, SlRightCrossFb, SlRightGain;
juce::ComboBox cbRightTransfromMode, cbRightWarpTypeAz, cbRightWarpTypeEl;
juce::ToggleButton tbRightSync;
juce::TextButton btRightTap;
std::unique_ptr dblSlRightFilterHpAttachment, dblSlRightFilterLpAttachment;
std::unique_ptr SlRightYawAttachment, SlRightPitchAttachment,
SlRightRollAttachment;
std::unique_ptr SlRightWarpFactorAzAttachment, SlRightWarpFactorElAttachment;
std::unique_ptr SlRightDelayAttachment, SlRightDelayMultAttachment,
SlRightLfoRateAttachment, SlRightLfoDepthAttachment, SlRightFbAttachment,
SlRightCrossFbAttachment, SlRightGainAttachment;
std::unique_ptr cbRightTransfromModeAttachment, cbRightWarpTypeAzAttachment,
cbRightWarpTypeElAttachment;
std::unique_ptr btRightSyncAttachment;
juce::TextButton btTimeMode;
// labels and groups
SimpleLabel lbYawL, lbPitchL, lbRollL, lbDelL, lbDelMultL, lbFbL, lbXFbL, lbWarpFactorAzL,
lbAzModeL, lbElModeL, lbWarpFactorElL;
SimpleLabel lbYawR, lbPitchR, lbRollR, lbDelR, lbDelMultR, lbFbR, lbXFbR, lbWarpFactorAzR,
lbAzModeR, lbElModeR, lbWarpFactorElR;
SimpleLabel lbGainL, lbGainR, lbGainDry;
TripleLabel lbLfoL, lbLfoR, lbFilterL, lbFilterR;
juce::GroupComponent gcRotDelL, gcRotDelR, gcFiltL, gcFiltR, gcFbL, gcFbR, gcOutput;
double lastTap = 0.0;
double tapIntervalMS = 0.0;
const double tapBeta = 0.8;
const double minTapIntervalMS = 100.0;
const double maxTapIntervalMS = 2000.0;
bool isModeMS { false };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DualDelayAudioProcessorEditor)
};
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/DualDelay/CMakeLists.txt 0000664 0025372 0025372 00000003654 15257173642 027243 0 ustar zmoelnig zmoelnig set (PLUGIN_TARGET "")
if (IEM_FORMATS)
juce_add_plugin (DualDelay
PLUGIN_MANUFACTURER_CODE IEMA
PLUGIN_CODE DuDe
ICON_BIG "../resources/Standalone/IEMPluginSuiteSmall.png"
ICON_SMALL "../resources/Standalone/IEMPluginSuiteSmall.png"
COMPANY_NAME "IEM"
PRODUCT_NAME "DualDelay"
FORMATS ${IEM_FORMATS}
VERSION ${DualDelay_VERSION}
LV2URI "http://plugins.iem.at/DualDelay"
VST2_CATEGORY kPlugSurroundFx kPlugCategSpacializer kPlugCategEffect
VST3_CATEGORIES Spatial Surround Delay Fx
AAX_CATEGORY SoundField Delay)
list (APPEND PLUGIN_TARGET DualDelay)
endif()
foreach (TARGET IN LISTS PLUGIN_TARGET)
juce_generate_juce_header (${TARGET})
target_sources (${TARGET} PRIVATE
Source/PluginEditor.cpp
Source/PluginEditor.h
Source/PluginProcessor.cpp
Source/PluginProcessor.h
../resources/OSC/OSCInputStream.h
../resources/OSC/OSCParameterInterface.cpp
../resources/OSC/OSCParameterInterface.h
../resources/OSC/OSCStatus.cpp
../resources/OSC/OSCStatus.h
../resources/OSC/OSCUtilities.h
../resources/efficientSHvanilla.cpp
)
target_compile_definitions (${TARGET} PRIVATE
JUCE_USE_CURL=0
JUCE_WEB_BROWSER=0
JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1
JUCE_USE_FLAC=0
JUCE_USE_OGGVORBIS=0
JUCE_USE_MP3AUDIOFORMAT=0
JUCE_USE_LAME_AUDIO_FORMAT=0
JUCE_USE_WINDOWS_MEDIA_FORMAT=0
JUCE_VST3_CAN_REPLACE_VST2=0)
target_include_directories (${TARGET} PRIVATE Source)
target_link_libraries (${TARGET} PRIVATE
LAF_fonts
IEM_logo
juce::juce_audio_utils
juce::juce_audio_plugin_client
juce::juce_osc
juce::juce_dsp
juce::juce_opengl
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags)
endforeach()
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/AllRADecoder/ 0000775 0025372 0025372 00000000000 15257173642 025050 5 ustar zmoelnig zmoelnig IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/AllRADecoder/Source/ 0000775 0025372 0025372 00000000000 15257173642 026310 5 ustar zmoelnig zmoelnig ././@LongLink 0000644 0000000 0000000 00000000150 00000000000 011577 L ustar root root IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/AllRADecoder/Source/PluginProcessor.cpp IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/AllRADecoder/Source/PluginProcessor.0000664 0025372 0025372 00000161512 15257173642 031455 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Authors: Daniel Rudrich, Franz Zotter
Copyright (c) 2017 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include
const juce::StringArray AllRADecoderAudioProcessor::weightsStrings =
juce::StringArray ("basic", "maxrE", "inphase");
//==============================================================================
AllRADecoderAudioProcessor::AllRADecoderAudioProcessor() :
AudioProcessorBase (
#ifndef JucePlugin_PreferredChannelConfigurations
BusesProperties()
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
.withInput ("Input",
((juce::PluginHostType::getPluginLoadedAs()
== juce::AudioProcessor::wrapperType_VST3)
? juce::AudioChannelSet::ambisonic (1)
: juce::AudioChannelSet::ambisonic (7)),
true)
#endif
.withOutput ("Output",
((juce::PluginHostType::getPluginLoadedAs()
== juce::AudioProcessor::wrapperType_VST3)
? juce::AudioChannelSet::ambisonic (1)
: juce::AudioChannelSet::ambisonic (7)),
true)
#endif
,
#endif
createParameterLayout()),
energyDistribution (juce::Image::PixelFormat::ARGB, 200, 100, true),
rEVector (juce::Image::PixelFormat::ARGB, 200, 100, true)
{
// get pointers to the parameters
inputOrderSetting = parameters.getRawParameterValue ("inputOrderSetting");
useSN3D = parameters.getRawParameterValue ("useSN3D");
decoderOrder = parameters.getRawParameterValue ("decoderOrder");
exportDecoder = parameters.getRawParameterValue ("exportDecoder");
exportLayout = parameters.getRawParameterValue ("exportLayout");
weights = parameters.getRawParameterValue ("weights");
// add listeners to parameter changes
parameters.addParameterListener ("inputOrderSetting", this);
parameters.addParameterListener ("useSN3D", this);
// global properties
juce::PropertiesFile::Options options;
options.applicationName = "AllRADecoder";
options.filenameSuffix = "settings";
options.folderName = "IEM";
options.osxLibrarySubFolder = "Preferences";
properties.reset (new juce::PropertiesFile (options));
lastDir = juce::File (properties->getValue ("presetFolder"));
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, 0.0f), 1),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 45.0f, 0.0f), 2, true),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 90.0f, 0.0f), 3),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 135.0f, 0.0f), 4),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 180.0f, 0.0f), 5),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -135.0f, 0.0f), 6),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -90.0f, 0.0f), 7),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -45.0f, 0.0f), 8),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 22.5f, 40.0f), 9),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 142.5f, 40.0f), 10),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -97.5f, 40.0f), 11),
&undoManager);
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, -90.0f), 12),
&undoManager);
loudspeakers.addListener (this);
prepareLayout();
}
AllRADecoderAudioProcessor::~AllRADecoderAudioProcessor()
{
}
int AllRADecoderAudioProcessor::getNumPrograms()
{
return 2; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int AllRADecoderAudioProcessor::getCurrentProgram()
{
return 0;
}
void AllRADecoderAudioProcessor::setCurrentProgram (int index)
{
if (index == 1)
{
loudspeakers.removeListener (this);
undoManager.beginNewTransaction();
loudspeakers.removeAllChildren (&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, 0.0f), 1),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -24.0f, 0.0f), 2),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -48.0f, 0.0f), 3),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -72.0f, 0.0f), 4),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -102.0f, 0.0f), 5),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -138.0f, 0.0f), 6),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -178.0f, 0.0f), 7),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 142.0f, 0.0f), 8),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 102.0f, 0.0f), 9),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 70.0f, 0.0f), 10),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 45.0f, 0.0f), 11),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 23.0f, 0.0f), 12),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -24.0f, 28.0f), 13),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -69.0f, 28.0f), 14),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -115.0f, 27.0f), 15),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -159.0f, 27.0f), 16),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 155.0f, 28.0f), 17),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 112.0f, 28.0f), 18),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 66.0f, 29.0f), 19),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 22.0f, 28.0f), 20),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, 60.0f), 21),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -91.0f, 59.0f), 22),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, -178.0f, 59.0f), 23),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 86.0f, 60.0f), 24),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, 90.0f), 25),
&undoManager);
loudspeakers.appendChild (
createLoudspeakerFromSpherical (juce::Vector3D (1.0f, 0.0f, -90.0f),
25,
true,
0.0f),
&undoManager);
loudspeakers.addListener (this);
prepareLayout();
updateTable = true;
}
}
const juce::String AllRADecoderAudioProcessor::getProgramName (int index)
{
if (index == 1)
return "IEM CUBE";
else
return "default";
}
void AllRADecoderAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
}
//==============================================================================
void AllRADecoderAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
checkInputAndOutput (this, *inputOrderSetting, 64, true);
juce::dsp::ProcessSpec specs;
specs.sampleRate = sampleRate;
specs.maximumBlockSize = samplesPerBlock;
specs.numChannels = 64;
decoder.prepare (specs);
noiseBurst.prepare (specs);
ambisonicNoiseBurst.prepare (specs);
}
void AllRADecoderAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
void AllRADecoderAudioProcessor::processBlock (juce::AudioBuffer& buffer,
juce::MidiBuffer& midiMessages)
{
checkInputAndOutput (this, *inputOrderSetting, 64, false);
juce::ScopedNoDenormals noDenormals;
// ====== is a decoder loaded? stop processing if not ===========
decoder.checkIfNewDecoderAvailable();
if (decoder.getCurrentDecoder() == nullptr)
{
buffer.clear();
}
else
{
ambisonicNoiseBurst.processBuffer (buffer);
const int nChIn = juce::jmin (decoder.getCurrentDecoder()->getNumInputChannels(),
buffer.getNumChannels(),
input.getNumberOfChannels());
const int nChOut = juce::jmin (decoder.getCurrentDecoder()->getNumOutputChannels(),
buffer.getNumChannels());
for (int ch = juce::jmax (nChIn, nChOut); ch < buffer.getNumChannels();
++ch) // clear all not needed channels
buffer.clear (ch, 0, buffer.getNumSamples());
decoder.setInputNormalization (*useSN3D >= 0.5f
? ReferenceCountedDecoder::Normalization::sn3d
: ReferenceCountedDecoder::Normalization::n3d);
const int L = buffer.getNumSamples();
auto inputAudioBlock =
juce::dsp::AudioBlock (buffer.getArrayOfWritePointers(), nChIn, L);
auto outputAudioBlock =
juce::dsp::AudioBlock (buffer.getArrayOfWritePointers(), nChOut, L);
decoder.process (inputAudioBlock, outputAudioBlock);
for (int ch = nChOut; ch < buffer.getNumChannels(); ++ch) // clear all not needed channels
buffer.clear (ch, 0, buffer.getNumSamples());
}
noiseBurst.processBuffer (buffer);
}
//==============================================================================
bool AllRADecoderAudioProcessor::hasEditor() const
{
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* AllRADecoderAudioProcessor::createEditor()
{
return new AllRADecoderAudioProcessorEditor (*this, parameters);
}
//==============================================================================
void AllRADecoderAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
if (parameters.state.getChildWithName ("Loudspeakers").isValid()
&& parameters.state.getChildWithName ("Loudspeakers") != loudspeakers)
{
parameters.state.removeChild (parameters.state.getChildWithName ("Loudspeakers"), nullptr);
}
parameters.state.appendChild (loudspeakers, nullptr);
auto state = parameters.copyState();
auto oscConfig = state.getOrCreateChildWithName ("OSCConfig", nullptr);
oscConfig.copyPropertiesFrom (oscParameterInterface.getConfig(), nullptr);
std::unique_ptr xml (state.createXml());
copyXmlToBinary (*xml, destData);
}
void AllRADecoderAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
std::unique_ptr xmlState (getXmlFromBinary (data, sizeInBytes));
if (xmlState != nullptr)
{
if (xmlState->hasTagName (parameters.state.getType()))
{
parameters.replaceState (juce::ValueTree::fromXml (*xmlState));
if (parameters.state.hasProperty ("OSCPort")) // legacy
{
oscParameterInterface.getOSCReceiver().connect (
parameters.state.getProperty ("OSCPort", juce::var (-1)));
parameters.state.removeProperty ("OSCPort", nullptr);
}
auto oscConfig = parameters.state.getChildWithName ("OSCConfig");
if (oscConfig.isValid())
oscParameterInterface.setConfig (oscConfig);
}
juce::XmlElement* lsps (xmlState->getChildByName ("Loudspeakers"));
if (lsps != nullptr)
{
loudspeakers.removeListener (this);
loudspeakers.removeAllChildren (nullptr);
const int nChilds = lsps->getNumChildElements();
for (int i = 0; i < nChilds; ++i)
{
juce::XmlElement* lsp (lsps->getChildElement (i));
if (lsp->getTagName() == "Element" || lsp->getTagName() == "Loudspeaker")
loudspeakers.appendChild (
createLoudspeakerFromSpherical (
juce::Vector3D (lsp->getDoubleAttribute ("Radius", 1.0),
lsp->getDoubleAttribute ("Azimuth"),
lsp->getDoubleAttribute ("Elevation")),
lsp->getIntAttribute ("Channel", -1),
lsp->getBoolAttribute ("Imaginary", false),
lsp->getDoubleAttribute ("Gain", 1.0)),
&undoManager);
}
undoManager.clearUndoHistory();
loudspeakers.addListener (this);
prepareLayout();
updateTable = true;
calculateDecoder();
}
}
}
//==============================================================================
void AllRADecoderAudioProcessor::parameterChanged (const juce::String& parameterID, float newValue)
{
DBG ("Parameter with ID " << parameterID << " has changed. New value: " << newValue);
if (parameterID == "inputOrderSetting" || parameterID == "outputOrderSetting")
userChangedIOSettings = true;
else if (parameterID == "useSN3D")
decoder.setInputNormalization (*useSN3D >= 0.5f
? ReferenceCountedDecoder::Normalization::sn3d
: ReferenceCountedDecoder::Normalization::n3d);
}
void AllRADecoderAudioProcessor::updateBuffers()
{
DBG ("IOHelper: input size: " << input.getSize());
DBG ("IOHelper: output size: " << output.getSize());
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new AllRADecoderAudioProcessor();
}
juce::Result AllRADecoderAudioProcessor::verifyLoudspeakers()
{
const int nLsps = loudspeakers.getNumChildren();
if (nLsps == 0)
return juce::Result::fail ("There are no loudspeakers.");
for (int i = 0; i < nLsps; ++i)
{
juce::ValueTree lsp = loudspeakers.getChild (i);
if (! lsp.isValid())
return juce::Result::fail ("Something went wrong. Try again!");
if (lsp.hasProperty ("Azimuth"))
{
const auto azimuth = lsp.getProperty ("Azimuth", juce::var());
if (! azimuth.isDouble() && ! azimuth.isInt())
return juce::Result::fail (
"Loudspeaker #" + juce::String (i + 1)
+ ": 'Azimuth' value is neither a double nor an integer.");
}
else
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": Missing 'Azumith' attribute.");
if (lsp.hasProperty ("Elevation")) //mandatory
{
const auto elevation = lsp.getProperty ("Elevation", juce::var());
if (! elevation.isDouble() && ! elevation.isInt())
return juce::Result::fail (
"Loudspeaker #" + juce::String (i + 1)
+ ": 'Elevation' value is neither a double nor an integer.");
}
else
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": Missing 'Elevation' attribute.");
if (lsp.hasProperty ("Radius")) //optional
{
const auto radius = lsp.getProperty ("Radius", juce::var());
if (! radius.isDouble() && ! radius.isInt())
return juce::Result::fail (
"Loudspeaker #" + juce::String (i + 1)
+ ": 'Radius' value is neither a double nor an integer.");
if ((float) radius < FLT_EPSILON)
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": Radius has to be greater than zero.");
}
bool isImaginary = false;
if (lsp.hasProperty ("IsImaginary")) //optional
{
const auto imaginary = lsp.getProperty ("IsImaginary", juce::var());
if (! imaginary.isBool())
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": 'IsImaginary' value is not a bool.");
isImaginary = imaginary;
}
if (! isImaginary)
{
if (lsp.hasProperty ("Channel")) //mandatory
{
const auto channel = lsp.getProperty ("Channel", juce::var());
if (! channel.isInt())
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": 'Channel' value is not an integer.");
}
else
return juce::Result::fail ("Loudspeaker #" + juce::String (i + 1)
+ ": Missing 'Channel' attribute.");
}
}
return juce::Result::ok();
}
juce::Result AllRADecoderAudioProcessor::calculateTris()
{
return juce::Result::ok();
}
juce::ValueTree AllRADecoderAudioProcessor::createLoudspeakerFromCartesian (
juce::Vector3D cartCoordinates,
int channel,
bool isImaginary,
float gain)
{
juce::Vector3D sphericalCoordinates = cartesianToSpherical (cartCoordinates);
return createLoudspeakerFromSpherical (sphericalCoordinates, channel, isImaginary, gain);
}
juce::ValueTree AllRADecoderAudioProcessor::createLoudspeakerFromSpherical (
juce::Vector3D sphericalCoordinates,
int channel,
bool isImaginary,
float gain)
{
return ConfigurationHelper::createElement (sphericalCoordinates.y,
sphericalCoordinates.z,
sphericalCoordinates.x,
channel,
isImaginary,
gain);
}
juce::Vector3D
AllRADecoderAudioProcessor::cartesianToSpherical (juce::Vector3D cartvect)
{
const float r = cartvect.length();
return juce::Vector3D (
r, // radius
juce::radiansToDegrees (std::atan2 (cartvect.y, cartvect.x)), // azimuth
juce::radiansToDegrees (
std::atan2 (cartvect.z,
std::sqrt (cartvect.x * cartvect.x + cartvect.y * cartvect.y))) // elevation
);
}
juce::Vector3D
AllRADecoderAudioProcessor::sphericalToCartesian (juce::Vector3D sphervect)
{
return juce::Vector3D (sphervect.x * std::cos (juce::degreesToRadians (sphervect.z))
* std::cos (juce::degreesToRadians (sphervect.y)),
sphervect.x * std::cos (juce::degreesToRadians (sphervect.z))
* std::sin (juce::degreesToRadians (sphervect.y)),
sphervect.x * std::sin (juce::degreesToRadians (sphervect.z)));
}
juce::Vector3D
AllRADecoderAudioProcessor::sphericalInRadiansToCartesian (juce::Vector3D sphervect)
{
return juce::Vector3D (sphervect.x * std::cos (sphervect.z) * cos (sphervect.y),
sphervect.x * std::cos (sphervect.z) * sin (sphervect.y),
sphervect.x * std::sin (sphervect.z));
}
void AllRADecoderAudioProcessor::playNoiseBurst (const int channel)
{
noiseBurst.setChannel (channel);
}
void AllRADecoderAudioProcessor::playAmbisonicNoiseBurst (const float azimuthInDegrees,
const float elevationInDegrees)
{
auto dec = decoder.getCurrentDecoder();
if (dec != nullptr)
{
ambisonicNoiseBurst.setOrder (decoder.getCurrentDecoder()->getOrder());
ambisonicNoiseBurst.setNormalization (*useSN3D >= 0.5f);
ambisonicNoiseBurst.play (azimuthInDegrees, elevationInDegrees);
}
}
void AllRADecoderAudioProcessor::addImaginaryLoudspeakerBelow()
{
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromCartesian (juce::Vector3D (0.0f, 0.0f, -1.0f),
highestChannelNumber + 1,
true,
0.0f),
&undoManager);
}
void AllRADecoderAudioProcessor::clearLoudspeakers()
{
undoManager.beginNewTransaction();
loudspeakers.removeAllChildren (&undoManager);
highestChannelNumber = 0;
prepareLayout();
}
void AllRADecoderAudioProcessor::addRandomPoint()
{
undoManager.beginNewTransaction();
loudspeakers.appendChild (
createLoudspeakerFromSpherical (
juce::Vector3D (
1.0f,
(static_cast (rand()) * 360.0f) / static_cast (RAND_MAX),
(static_cast (rand()) * 180.0f) / static_cast (RAND_MAX) - 90.0f),
highestChannelNumber + 1),
&undoManager);
}
void AllRADecoderAudioProcessor::convertLoudspeakersToArray()
{
imaginaryFlags.clear();
highestChannelNumber = 0;
int i = 0;
int imaginaryCount = 0;
for (juce::ValueTree::Iterator it = loudspeakers.begin(); it != loudspeakers.end(); ++it)
{
const bool isImaginary = (*it).getProperty ("Imaginary");
juce::Vector3D spherical;
spherical.x = isImaginary ? (float) (*it).getProperty ("Radius") : 1.0f;
spherical.y = (*it).getProperty ("Azimuth");
spherical.z = (*it).getProperty ("Elevation");
juce::Vector3D cart = sphericalToCartesian (spherical);
R3 newPoint { cart.x, cart.y, cart.z };
newPoint.lspNum = i;
if (isImaginary)
{
imaginaryFlags.setBit (i);
++imaginaryCount;
newPoint.realLspNum = -1;
}
else
newPoint.realLspNum = i - imaginaryCount;
newPoint.azimuth = (*it).getProperty ("Azimuth");
newPoint.elevation = (*it).getProperty ("Elevation");
newPoint.radius = (*it).getProperty ("Radius");
newPoint.isImaginary = isImaginary;
newPoint.gain = (*it).getProperty ("Gain");
newPoint.channel = (*it).getProperty ("Channel");
if (newPoint.channel > highestChannelNumber)
highestChannelNumber = newPoint.channel;
DBG (newPoint.lspNum << " \t " << newPoint.realLspNum << " \t " << newPoint.gain << " \t "
<< newPoint.x << " \t " << newPoint.y << " \t " << newPoint.z);
points.push_back (newPoint);
++i;
}
}
void AllRADecoderAudioProcessor::prepareLayout()
{
isLayoutReady = false;
wrapSphericalCoordinates();
juce::Result res = checkLayout();
if (res.failed())
{
DBG (res.getErrorMessage());
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::red;
newMessage.headline = "Improper layout";
newMessage.text = res.getErrorMessage();
messageToEditor = newMessage;
updateMessage = true;
}
else
{
DBG ("Layout is ready for creating a decoder!");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::cornflowerblue;
newMessage.headline = "Suitable layout";
newMessage.text = "The layout is ready to calculate a decoder.";
messageToEditor = newMessage;
updateMessage = true;
isLayoutReady = true;
}
}
juce::Result AllRADecoderAudioProcessor::checkLayout()
{
points.clear();
triangles.clear();
normals.clear();
// Check if loudspeakers are stored properly
juce::Result res = verifyLoudspeakers();
if (res.failed())
{
updateLoudspeakerVisualization = true;
return juce::Result::fail (res.getErrorMessage());
}
convertLoudspeakersToArray();
// Check for duplicates
std::vector outx;
std::vector noDuplicates;
const int nDuplicates = de_duplicateR3 (points, outx, noDuplicates);
if (nDuplicates > 0)
{
updateLoudspeakerVisualization = true;
return juce::Result::fail ("ERROR 1: There are duplicate loudspeakers.");
}
const int nLsps = loudspeakers.getNumChildren();
if (nLsps < 4)
{
updateLoudspeakerVisualization = true;
return juce::Result::fail ("ERROR 2: There are less than 4 loudspeakers! Add some more!");
}
// calculate convex hull
const int result = NewtonApple_hull_3D (points, triangles);
if (result != 1)
{
return juce::Result::fail (
"ERROR: An error occurred! The layout might be broken somehow. Try adding additional loudspeakers (e.g. imaginary ones) or make small changes to the coordinates.");
}
// normalise normal vectors
for (int i = 0; i < triangles.size(); ++i)
{
const Tri tri = triangles[i];
normals.push_back (juce::Vector3D (tri.er, tri.ec, tri.ez).normalised());
}
updateLoudspeakerVisualization = true;
juce::Array usedIndices;
// calculate centroid
juce::Vector3D centroid { 0.0f, 0.0f, 0.0f };
for (int i = 0; i < nLsps; ++i)
{
R3 lsp = points[i];
centroid += juce::Vector3D (lsp.x, lsp.y, lsp.z);
usedIndices.add (i);
}
centroid /= nLsps;
DBG ("centroid: x: " << centroid.x << " y: " << centroid.y << " z: " << centroid.z);
for (int i = 0; i < triangles.size(); ++i)
{
Tri tri = triangles[i];
juce::Vector3D a { points[tri.a].x, points[tri.a].y, points[tri.a].z };
usedIndices.removeFirstMatchingValue (tri.a);
usedIndices.removeFirstMatchingValue (tri.b);
usedIndices.removeFirstMatchingValue (tri.c);
const float dist = normals[i] * (a - centroid);
if (dist < 0.001f) // too flat!
{
return juce::Result::fail (
"ERROR 3: Convex hull is too flat. Check coordinates and/or try adding imaginary loudspeakers.");
}
if (normals[i] * a < 0.001f) // origin is not within hull
{
return juce::Result::fail (
"ERROR 4: Point of origin is not within the convex hull. Try adding imaginary loudspeakers.");
}
const int numberOfImaginaryLspsInTriangle = (int) imaginaryFlags[points[tri.a].lspNum]
+ (int) imaginaryFlags[points[tri.b].lspNum]
+ (int) imaginaryFlags[points[tri.c].lspNum];
if (numberOfImaginaryLspsInTriangle > 1)
{
return juce::Result::fail (
"ERROR 5: There is a triangle with more than one imaginary loudspeaker.");
}
}
if (usedIndices.size() > 0)
return juce::Result::fail (
"ERROR 6: There is at least one loudspeaker which is not part of the convex hull.");
if (imaginaryFlags.countNumberOfSetBits() == nLsps)
return juce::Result::fail ("ERROR 7: There are only imaginary loudspeakers.");
juce::Array routing;
for (int i = 0; i < nLsps; ++i)
{
if (! points[i].isImaginary)
{
const int channel = points[i].channel;
if (channel < 1)
return juce::Result::fail ("ERROR 8: A channel number is smaller than 1.");
if (routing.contains (channel))
return juce::Result::fail (
"ERROR 9: Channel number duplicates: a channel number may occur only once.");
else
routing.add (channel);
}
}
return juce::Result::ok();
}
juce::Result AllRADecoderAudioProcessor::calculateDecoder()
{
if (! isLayoutReady)
return juce::Result::fail ("Layout not ready!");
const int N = juce::roundToInt (decoderOrder->load()) + 1;
const auto ambisonicWeights =
ReferenceCountedDecoder::Weights (juce::roundToInt (weights->load()));
const int nCoeffs = juce::square (N + 1);
const int nLsps = (int) points.size();
const int nRealLsps = nLsps - imaginaryFlags.countNumberOfSetBits();
DBG ("Number of loudspeakers: " << nLsps << ". Number of real loudspeakers: " << nRealLsps);
juce::dsp::Matrix decoderMatrix (nRealLsps, nCoeffs);
juce::Array> inverseArray;
for (int t = 0; t < triangles.size(); ++t) //iterate over each triangle
{
Tri tri = triangles[t];
juce::Array triangleIndices (tri.a, tri.b, tri.c);
juce::dsp::Matrix L (3, 3);
for (int i = 0; i < 3; ++i)
{
L (0, i) = points[triangleIndices[i]].x;
L (1, i) = points[triangleIndices[i]].y;
L (2, i) = points[triangleIndices[i]].z;
if (points[triangleIndices[i]].isImaginary)
{
const float factor = 1.0f
/ std::sqrt (juce::square (points[triangleIndices[i]].x)
+ juce::square (points[triangleIndices[i]].y)
+ juce::square (points[triangleIndices[i]].z));
L (0, i) *= factor;
L (1, i) *= factor;
L (2, i) *= factor;
}
}
inverseArray.add (getInverse (L));
}
std::vector sh;
sh.resize (nCoeffs);
for (int i = 0; i < 5200; ++i) //iterate over each tDesign point
{
const juce::dsp::Matrix source (3, 1, tDesign5200[i]);
SHEval (N, source (0, 0), source (1, 0), source (2, 0), &sh[0], false);
const juce::dsp::Matrix gains (3, 1);
int t = 0;
for (; t < triangles.size(); ++t) //iterate over each triangle
{
Tri tri = triangles[t];
juce::Array triangleIndices (tri.a, tri.b, tri.c);
juce::Array imagFlags (points[tri.a].isImaginary,
points[tri.b].isImaginary,
points[tri.c].isImaginary);
juce::dsp::Matrix gains (3, 1);
gains = inverseArray.getUnchecked (t) * source;
if (gains (0, 0) >= -FLT_EPSILON && gains (1, 0) >= -FLT_EPSILON
&& gains (2, 0) >= -FLT_EPSILON)
{
// we found the corresponding triangle!
const float foo =
1.0f
/ std::sqrt (juce::square (gains (0, 0)) + juce::square (gains (1, 0))
+ juce::square (gains (2, 0)));
gains = gains * foo;
if (imagFlags.contains (true))
{
const int imagGainIdx = imagFlags.indexOf (
true); // which of the three corresponds to the imaginary loudspeaker
const int imaginaryLspIdx = triangleIndices[imagGainIdx];
juce::Array realGainIndex (0, 1, 2);
realGainIndex.remove (imagGainIdx);
juce::Array connectedLsps;
connectedLsps.add (imaginaryLspIdx);
for (int k = 0; k < triangles.size(); ++k) //iterate over each triangle
{
Tri probe = triangles[k];
juce::Array probeTriangleIndices (probe.a, probe.b, probe.c);
if (probeTriangleIndices.contains (imaginaryLspIdx))
{ // found searched imaginaryLspIdx in that triangle
for (int j = 0; j < 3; ++j)
{
if (! connectedLsps.contains (probeTriangleIndices[j]))
connectedLsps.add (probeTriangleIndices[j]);
}
}
}
connectedLsps.remove (0); // remove imaginary loudspeaker again
juce::Array gainVector;
gainVector.resize (connectedLsps.size());
const float kappa = getKappa (gains (imagGainIdx, 0),
gains (realGainIndex[0], 0),
gains (realGainIndex[1], 0),
connectedLsps.size());
gainVector.fill (gains (imagGainIdx, 0) * (points[imaginaryLspIdx].gain)
* kappa);
for (int j = 0; j < 2; ++j)
{
const int idx = connectedLsps.indexOf (triangleIndices[realGainIndex[j]]);
gainVector.set (idx, gainVector[idx] + gains (realGainIndex[j], 0));
}
for (int n = 0; n < connectedLsps.size(); ++n)
juce::FloatVectorOperations::addWithMultiply (
&decoderMatrix (points[connectedLsps[n]].realLspNum, 0),
&sh[0],
gainVector[n],
nCoeffs);
}
else
{
juce::FloatVectorOperations::addWithMultiply (
&decoderMatrix (points[tri.a].realLspNum, 0),
&sh[0],
gains (0, 0),
nCoeffs);
juce::FloatVectorOperations::addWithMultiply (
&decoderMatrix (points[tri.b].realLspNum, 0),
&sh[0],
gains (1, 0),
nCoeffs);
juce::FloatVectorOperations::addWithMultiply (
&decoderMatrix (points[tri.c].realLspNum, 0),
&sh[0],
gains (2, 0),
nCoeffs);
}
break;
}
}
jassert (t < triangles.size());
}
// calculate max lsp gain
float maxGain = 0.0f;
for (int i = 0; i < nLsps; ++i)
{
const R3 point = points[i];
if (! point.isImaginary)
{
SHEval (N, point.x, point.y, point.z, &sh[0]); // encoding at loudspeaker position
float sumOfSquares = 0.0f;
for (int m = 0; m < nRealLsps; ++m)
{
float sum = 0.0f;
for (int n = 0; n < nCoeffs; ++n)
sum += sh[n] * decoderMatrix (m, n);
sumOfSquares += juce::square (sum);
}
sumOfSquares = sqrt (sumOfSquares);
if (sumOfSquares > maxGain)
maxGain = sumOfSquares;
}
}
decoderMatrix = decoderMatrix * (1.0f / maxGain);
// calculate energy and rE vector
juce::Array> realLspsCoordinates;
realLspsCoordinates.resize (nRealLsps);
for (int i = 0; i < nLsps; ++i)
{
if (! points[i].isImaginary)
realLspsCoordinates.set (points[i].realLspNum,
juce::Vector3D (points[i].x, points[i].y, points[i].z)
.normalised()); // zero count
}
const int w = energyDistribution.getWidth();
const float wHalf = w / 2;
const int h = energyDistribution.getHeight();
const float hHalf = h / 2;
float minLvl = 0.0f;
float maxLvl = 0.0f;
float sumLvl = 0.0f;
auto levelValues = juce::dsp::Matrix (w, h);
for (int y = 0; y < h; ++y)
for (int x = 0; x < w; ++x)
{
juce::Vector3D spher (1.0f, 0.0f, 0.0f);
HammerAitov::XYToSpherical ((x - wHalf) / wHalf, (hHalf - y) / hHalf, spher.y, spher.z);
juce::Vector3D cart = sphericalInRadiansToCartesian (spher);
SHEval (N, cart.x, cart.y, cart.z, &sh[0]); // encoding a source
if (ambisonicWeights == ReferenceCountedDecoder::Weights::maxrE)
multiplyMaxRE (N, sh.data());
else if (ambisonicWeights == ReferenceCountedDecoder::Weights::inPhase)
multiplyInPhase (N, sh.data());
juce::Vector3D rE (0.0f, 0.0f, 0.0f);
float sumOfSquares = 0.0f;
for (int m = 0; m < nRealLsps; ++m)
{
float sum = 0.0f;
for (int n = 0; n < nCoeffs; ++n)
sum += (sh[n] * decoderMatrix (m, n));
const float sumSquared = juce::square (sum);
rE += realLspsCoordinates[m] * sumSquared;
sumOfSquares += sumSquared;
}
const float lvl = 0.5f * juce::Decibels::gainToDecibels (sumOfSquares);
levelValues (x, y) = lvl;
sumLvl += lvl;
if (lvl > maxLvl)
maxLvl = lvl;
if (lvl < minLvl)
minLvl = lvl;
rE /= sumOfSquares + FLT_EPSILON;
const float width = 2.0f * std::acos (juce::jmin (1.0f, rE.length()));
const float reMap = juce::jlimit (0.0f, 1.0f, width / juce::MathConstants::pi);
const juce::Colour rEPixelColour = juce::Colours::limegreen.withMultipliedAlpha (reMap);
rEVector.setPixelAt (x, y, rEPixelColour);
}
const float meanLvl = sumLvl / (w * h);
for (int y = 0; y < h; ++y)
for (int x = 0; x < w; ++x)
{
constexpr float plusMinusRange = 1.5f;
const float map =
(juce::jlimit (-plusMinusRange, plusMinusRange, levelValues (x, y) - meanLvl)
+ plusMinusRange)
/ (2 * plusMinusRange);
const juce::Colour pixelColour = juce::Colours::red.withMultipliedAlpha (map);
energyDistribution.setPixelAt (x, y, pixelColour);
}
DBG ("min: " << minLvl << " max: " << maxLvl);
updateLoudspeakerVisualization = true;
ReferenceCountedDecoder::Ptr newDecoder = new ReferenceCountedDecoder (
"Decoder",
"A " + getOrderString (N) + " order Ambisonics decoder using the AllRAD approach.",
(int) decoderMatrix.getSize()[0],
(int) decoderMatrix.getSize()[1]);
newDecoder->getMatrix() = decoderMatrix;
ReferenceCountedDecoder::Settings newSettings;
newSettings.expectedNormalization = ReferenceCountedDecoder::Normalization::n3d;
newSettings.weights = ambisonicWeights;
newSettings.weightsAlreadyApplied = false;
newDecoder->setSettings (newSettings);
juce::Array& routing = newDecoder->getRoutingArrayReference();
routing.resize (nRealLsps);
for (int i = 0; i < nLsps; ++i)
{
if (! points[i].isImaginary)
routing.set (points[i].realLspNum, points[i].channel - 1); // zero count
}
decoder.setDecoder (newDecoder);
decoderConfig = newDecoder;
updateChannelCount = true;
DBG ("finished");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::green;
newMessage.headline = "Decoder created";
newMessage.text = "The decoder was calculated successfully.";
messageToEditor = newMessage;
updateMessage = true;
return juce::Result::ok();
}
float AllRADecoderAudioProcessor::getKappa (float gIm, float gRe1, float gRe2, int N)
{
const float p = gIm * (gRe1 + gRe2) / (N * juce::square (gIm));
const float q = (juce::square (gRe1) + juce::square (gRe2) - 1.0f) / (N * juce::square (gIm));
return -p + std::sqrt (juce::jmax (juce::square (p) - q, 0.0f));
}
juce::dsp::Matrix AllRADecoderAudioProcessor::getInverse (juce::dsp::Matrix A)
{
const float det = A (0, 0) * (A (1, 1) * A (2, 2) - A (1, 2) * A (2, 1))
+ A (0, 1) * (A (1, 2) * A (2, 0) - A (1, 0) * A (2, 2))
+ A (0, 2) * (A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0));
const float factor = 1.0 / det;
juce::dsp::Matrix inverse (3, 3);
inverse (0, 0) = (A (1, 1) * A (2, 2) - A (1, 2) * A (2, 1)) * factor;
inverse (0, 1) = (-A (0, 1) * A (2, 2) + A (0, 2) * A (2, 1)) * factor;
inverse (0, 2) = (A (0, 1) * A (1, 2) - A (0, 2) * A (1, 1)) * factor;
inverse (1, 0) = (-A (1, 0) * A (2, 2) + A (1, 2) * A (2, 0)) * factor;
inverse (1, 1) = (A (0, 0) * A (2, 2) - A (0, 2) * A (2, 0)) * factor;
inverse (1, 2) = (-A (0, 0) * A (1, 2) + A (0, 2) * A (1, 0)) * factor;
inverse (2, 0) = (A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0)) * factor;
inverse (2, 1) = (-A (0, 0) * A (2, 1) + A (0, 1) * A (2, 0)) * factor;
inverse (2, 2) = (A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0)) * factor;
return inverse;
}
void AllRADecoderAudioProcessor::saveConfigurationToFile (juce::File destination)
{
if (*exportDecoder < 0.5f && *exportLayout < 0.5f)
{
DBG ("nothing to export");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::red;
newMessage.headline = "Nothing to export.";
newMessage.text = "Please select at least one of the export options.";
messageToEditor = newMessage;
updateMessage = true;
return;
}
auto* jsonObj = new juce::DynamicObject();
jsonObj->setProperty (
"Name",
juce::var ("All-Round Ambisonic decoder (AllRAD) and loudspeaker layout"));
juce::String versionString = juce::String ("v") + juce::String (JucePlugin_VersionString);
jsonObj->setProperty (
"Description",
juce::var ("This configuration file was created with the IEM AllRADecoder " + versionString
+ " plug-in. " + juce::Time::getCurrentTime().toString (true, true)));
if (*exportDecoder >= 0.5f)
{
if (decoderConfig != nullptr)
jsonObj->setProperty ("Decoder",
ConfigurationHelper::convertDecoderToVar (decoderConfig));
else
{
DBG ("No decoder available");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::red;
newMessage.headline = "No decoder available for export.";
newMessage.text = "Please calculate a decoder first.";
messageToEditor = newMessage;
updateMessage = true;
return;
}
}
if (*exportLayout >= 0.5f)
jsonObj->setProperty (
"LoudspeakerLayout",
ConfigurationHelper::convertLoudspeakersToVar (loudspeakers, "A loudspeaker layout"));
juce::Result result =
ConfigurationHelper::writeConfigurationToFile (destination, juce::var (jsonObj));
if (result.wasOk())
{
DBG ("Configuration successfully written to file.");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::green;
newMessage.headline = "Configuration exported successfully";
newMessage.text =
"The decoder was successfully written to " + destination.getFileName() + ".";
messageToEditor = newMessage;
updateMessage = true;
}
else
DBG ("Could not write configuration file.");
}
void AllRADecoderAudioProcessor::setLastDir (juce::File newLastDir)
{
lastDir = newLastDir;
const juce::var v (lastDir.getFullPathName());
properties->setValue ("presetFolder", v);
}
void AllRADecoderAudioProcessor::valueTreePropertyChanged (
juce::ValueTree& treeWhosePropertyHasChanged,
const juce::Identifier& property)
{
DBG ("valueTreePropertyChanged");
prepareLayout();
updateTable = true;
}
void AllRADecoderAudioProcessor::valueTreeChildAdded (juce::ValueTree& parentTree,
juce::ValueTree& childWhichHasBeenAdded)
{
DBG ("valueTreeChildAdded");
prepareLayout();
updateTable = true;
}
void AllRADecoderAudioProcessor::valueTreeChildRemoved (juce::ValueTree& parentTree,
juce::ValueTree& childWhichHasBeenRemoved,
int indexFromWhichChildWasRemoved)
{
DBG ("valueTreeChildRemoved");
prepareLayout();
updateTable = true;
}
void AllRADecoderAudioProcessor::valueTreeChildOrderChanged (
juce::ValueTree& parentTreeWhoseChildrenHaveMoved,
int oldIndex,
int newIndex)
{
DBG ("valueTreeChildOrderChanged");
prepareLayout();
}
void AllRADecoderAudioProcessor::valueTreeParentChanged (juce::ValueTree& treeWhoseParentHasChanged)
{
DBG ("valueTreeParentChanged");
}
void AllRADecoderAudioProcessor::loadConfiguration (const juce::File& configFile)
{
undoManager.beginNewTransaction();
loudspeakers.removeAllChildren (&undoManager);
juce::Result result =
ConfigurationHelper::parseFileForLoudspeakerLayout (configFile, loudspeakers, &undoManager);
if (! result.wasOk())
{
DBG ("Configuration could not be loaded.");
MailBox::Message newMessage;
newMessage.messageColour = juce::Colours::red;
newMessage.headline = "Error loading configuration";
newMessage.text = result.getErrorMessage();
messageToEditor = newMessage;
updateMessage = true;
}
}
void AllRADecoderAudioProcessor::rotate (const float degreesAddedToAzimuth)
{
loudspeakers.removeListener (this);
undoManager.beginNewTransaction();
bool amountIsPositive = degreesAddedToAzimuth > 0;
const int nLsps = loudspeakers.getNumChildren();
for (int i = 0; i < nLsps; ++i)
{
auto lsp = loudspeakers.getChild (i);
float val = lsp.getProperty ("Azimuth");
val += degreesAddedToAzimuth;
if (amountIsPositive && val > 360.0f)
val -= 360.0f;
else if (! amountIsPositive && val < -360.0f)
val += 360.0f;
lsp.setProperty ("Azimuth", val, &undoManager);
}
loudspeakers.addListener (this);
prepareLayout();
updateTable = true;
}
void AllRADecoderAudioProcessor::wrapSphericalCoordinates()
{
loudspeakers.removeListener (this);
for (juce::ValueTree::Iterator it = loudspeakers.begin(); it != loudspeakers.end(); ++it)
{
bool updateElevation = false;
float az = (*it).getProperty ("Azimuth");
float el = (*it).getProperty ("Elevation");
// Wrap elevation
if (el > 180.0f || el < -180.0f)
{
float fullRotations = std::copysign (std::ceil (std::abs (el) / 360.0f), el);
el -= fullRotations * 360.0f;
updateElevation = true;
}
if (el > 90.0f || el < -90.0f)
{
el = std::copysign (180.0 - std::abs (el), el);
az += 180.0f;
updateElevation = true;
(*it).setProperty ("Azimuth", az, &undoManager);
}
// Wrap azimuth
if (az > 180.0f || az < -180.0f)
{
float fullRotations = std::copysign (std::ceil (std::abs (az) / 360.0f), az);
az -= fullRotations * 360.0f;
(*it).setProperty ("Azimuth", az, nullptr);
}
// Update coordinates
if (updateElevation)
(*it).setProperty ("Elevation", el, nullptr);
}
loudspeakers.addListener (this);
}
//==============================================================================
const bool AllRADecoderAudioProcessor::interceptOSCMessage (juce::OSCMessage& message)
{
if (message.getAddressPattern().toString().equalsIgnoreCase (
"/" + juce::String (JucePlugin_Name) + "/decoderOrder")
&& message.size() >= 1)
{
if (message[0].isInt32())
{
auto value = message[0].getInt32() - 1;
message.clear();
message.addInt32 (value);
}
else if (message[0].isFloat32())
{
auto value = message[0].getFloat32() - 1;
message.clear();
message.addFloat32 (value);
}
}
return false;
}
const bool
AllRADecoderAudioProcessor::processNotYetConsumedOSCMessage (const juce::OSCMessage& message)
{
const juce::String prefix ("/" + juce::String (JucePlugin_Name));
if (message.getAddressPattern().toString().startsWith (prefix))
{
juce::OSCMessage msg (message);
msg.setAddressPattern (message.getAddressPattern().toString().substring (
juce::String (JucePlugin_Name).length() + 1));
if (msg.getAddressPattern().toString().equalsIgnoreCase ("/loadFile") && msg.size() >= 1)
{
if (msg[0].isString())
{
juce::File fileToLoad (msg[0].getString());
loadConfiguration (fileToLoad);
return true;
}
}
else if (msg.getAddressPattern().toString().equalsIgnoreCase ("/calculate")
|| msg.getAddressPattern().toString().equalsIgnoreCase ("/calculateDecoder"))
{
calculateDecoder();
return true;
}
else if (msg.getAddressPattern().toString().equalsIgnoreCase ("/export") && msg.size() >= 1)
{
if (msg[0].isString())
{
juce::File file (msg[0].getString());
saveConfigurationToFile (file);
return true;
}
}
else if (msg.getAddressPattern().toString().equalsIgnoreCase ("/playNoise")
&& msg.size() >= 1)
{
if (msg[0].isInt32())
{
const int channel = msg[0].getInt32();
if (channel <= 64)
{
playNoiseBurst (channel);
return true;
}
}
}
else if (msg.getAddressPattern().toString().equalsIgnoreCase ("/playEncodedNoise")
&& msg.size() >= 2)
{
float azimuth = 0.0f;
float elevation = 0.0f;
if (msg[0].isInt32())
azimuth = msg[0].getInt32();
else if (msg[0].isFloat32())
azimuth = msg[0].getFloat32();
else
return false;
if (msg[1].isInt32())
elevation = msg[1].getInt32();
else if (msg[1].isFloat32())
elevation = msg[1].getFloat32();
else
return false;
playAmbisonicNoiseBurst (azimuth, elevation);
return true;
}
}
return false;
}
// ==============================================================================
std::vector>
AllRADecoderAudioProcessor::createParameterLayout()
{
// add your audio parameters here
std::vector> params;
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"inputOrderSetting",
"Input Ambisonic Order",
"",
juce::NormalisableRange (0.0f, 8.0f, 1.0f),
0.0f,
[] (float value)
{
if (value >= 0.5f && value < 1.5f)
return "0th";
else if (value >= 1.5f && value < 2.5f)
return "1st";
else if (value >= 2.5f && value < 3.5f)
return "2nd";
else if (value >= 3.5f && value < 4.5f)
return "3rd";
else if (value >= 4.5f && value < 5.5f)
return "4th";
else if (value >= 5.5f && value < 6.5f)
return "5th";
else if (value >= 6.5f && value < 7.5f)
return "6th";
else if (value >= 7.5f)
return "7th";
else
return "Auto";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"useSN3D",
"Input Normalization",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
1.0f,
[] (float value)
{
if (value >= 0.5f)
return "SN3D";
else
return "N3D";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"decoderOrder",
"Decoder Order",
"",
juce::NormalisableRange (0.0f, 6.0f, 1.0f),
0.0f,
[] (float value)
{
if (value >= 0.5f && value < 1.5f)
return "2nd";
else if (value >= 1.5f && value < 2.5f)
return "3rd";
else if (value >= 2.5f && value < 3.5f)
return "4th";
else if (value >= 3.5f && value < 4.5f)
return "5th";
else if (value >= 4.5f && value < 5.5f)
return "6th";
else if (value >= 5.5f)
return "7th";
else
return "1st";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"exportDecoder",
"Export Decoder",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
1.0f,
[] (float value)
{
if (value >= 0.5f)
return "Yes";
else
return "No";
},
nullptr));
params.push_back (OSCParameterInterface::createParameterTheOldWay (
"exportLayout",
"Export Layout",
"",
juce::NormalisableRange (0.0f, 1.0f, 1.0f),
1.0f,
[] (float value)
{
if (value >= 0.5f)
return "Yes";
else
return "No";
},
nullptr));
params.push_back (std::make_unique ("weights",
"Ambisonic Weights",
weightsStrings,
1));
return params;
}
IEMPluginSuite-v1.16.0-d9e0a9aac011ab1a1edfea1d5920918f159a5851/AllRADecoder/Source/PluginEditor.cpp0000664 0025372 0025372 00000034306 15257173642 031427 0 ustar zmoelnig zmoelnig /*
==============================================================================
This file is part of the IEM plug-in suite.
Authors: Daniel Rudrich, Franz Zotter
Copyright (c) 2017 - Institute of Electronic Music and Acoustics (IEM)
https://iem.at
The IEM plug-in suite is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The IEM plug-in suite is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this software. If not, see .
==============================================================================
*/
#include "PluginEditor.h"
#include "PluginProcessor.h"
//==============================================================================
AllRADecoderAudioProcessorEditor::AllRADecoderAudioProcessorEditor (
AllRADecoderAudioProcessor& p,
juce::AudioProcessorValueTreeState& vts) :
juce::AudioProcessorEditor (&p),
processor (p),
valueTreeState (vts),
footer (p.getOSCParameterInterface()),
lv (processor.points, processor.triangles, processor.normals, processor.imaginaryFlags),
lspList (processor.getLoudspeakersValueTree(), lv, grid, processor.undoManager, processor),
grid (processor.points,
processor.imaginaryFlags,
processor.energyDistribution,
processor.rEVector)
{
// ============== BEGIN: essentials ======================
// set GUI size and lookAndFeel
//setSize(500, 300); // use this to create a fixed-size GUI
setResizeLimits (1000, 600, 1200, 900); // use this to create a resizable GUI
setResizable (true, true);
setLookAndFeel (&globalLaF);
// make title and footer visible, and set the PluginName
addAndMakeVisible (&title);
title.setTitle (juce::String ("AllRA"), juce::String ("Decoder"));
title.setFont (globalLaF.robotoBold, globalLaF.robotoLight);
addAndMakeVisible (&footer);
// ============= END: essentials ========================
// create the connection between title component's comboBoxes and parameters
cbNormalizationSettingAttachment.reset (
new ComboBoxAttachment (valueTreeState,
"useSN3D",
*title.getInputWidgetPtr()->getNormCbPointer()));
cbOrderSettingAttachment.reset (
new ComboBoxAttachment (valueTreeState,
"inputOrderSetting",
*title.getInputWidgetPtr()->getOrderCbPointer()));
addAndMakeVisible (cbDecoderOrder);
cbDecoderOrder.setJustificationType (juce::Justification::centred);
cbDecoderOrder.addSectionHeading ("Decoder order");
for (int n = 1; n <= 7; ++n)
cbDecoderOrder.addItem (getOrderString (n), n);
cbDecoderOrderAttachment.reset (
new ComboBoxAttachment (valueTreeState, "decoderOrder", cbDecoderOrder));
addAndMakeVisible (lbDecoderOrder);
lbDecoderOrder.setText ("Decoder Order", juce::Justification::left);
addAndMakeVisible (cbDecoderWeights);
cbDecoderWeights.setJustificationType (juce::Justification::centred);
cbDecoderWeights.addItemList (p.weightsStrings, 1);
cbDecoderWeightsAttachment.reset (
new ComboBoxAttachment (valueTreeState, "weights", cbDecoderWeights));
addAndMakeVisible (lbDecoderWeights);
lbDecoderWeights.setText ("Weights", juce::Justification::left);
addAndMakeVisible (gcLayout);
gcLayout.setText ("Loudspeaker Layout");
addAndMakeVisible (gcDecoder);
gcDecoder.setText ("Calculate Decoder");
addAndMakeVisible (gcExport);
gcExport.setText ("Export Decoder/Layout");
addAndMakeVisible (tbExportDecoder);
tbExportDecoderAttachment.reset (
new ButtonAttachment (valueTreeState, "exportDecoder", tbExportDecoder));
tbExportDecoder.setButtonText ("Export Decoder");
tbExportDecoder.setColour (juce::ToggleButton::tickColourId, juce::Colours::orange);
addAndMakeVisible (tbExportLayout);
tbExportLayoutAttachment.reset (
new ButtonAttachment (valueTreeState, "exportLayout", tbExportLayout));
tbExportLayout.setButtonText ("Export Layout");
tbExportLayout.setColour (juce::ToggleButton::tickColourId, juce::Colours::limegreen);
addAndMakeVisible (messageDisplay);
messageDisplay.setMessage (processor.messageToEditor);
addAndMakeVisible (grid);
addAndMakeVisible (tbCalculateDecoder);
tbCalculateDecoder.setButtonText ("CALCULATE DECODER");
tbCalculateDecoder.setColour (juce::TextButton::buttonColourId, juce::Colours::cornflowerblue);
tbCalculateDecoder.addListener (this);
addAndMakeVisible (tbAddSpeakers);
tbAddSpeakers.setButtonText ("ADD LOUDSPEAKER");
tbAddSpeakers.setColour (juce::TextButton::buttonColourId, juce::Colours::limegreen);
tbAddSpeakers.setTooltip (
"Adds a new loudspeaker with random position. \n Alt+click: adds an imaginary loudspeaker to the nadir position.");
tbAddSpeakers.addListener (this);
addAndMakeVisible (tbClearSpeakers);
tbClearSpeakers.setButtonText ("CLEAR");
tbClearSpeakers.setColour (juce::TextButton::buttonColourId, juce::Colours::red);
tbClearSpeakers.setTooltip ("Delete all loudspeakers.");
tbClearSpeakers.addListener (this);
addAndMakeVisible (tbJson);
tbJson.setButtonText ("EXPORT");
tbJson.setColour (juce::TextButton::buttonColourId, juce::Colours::orange);
tbJson.setTooltip ("Stores the decoder and/or loudspeaker layout to a configuration file.");
tbJson.addListener (this);
addAndMakeVisible (tbImport);
tbImport.setButtonText ("IMPORT");
tbImport.setColour (juce::TextButton::buttonColourId, juce::Colours::orange);
tbImport.setTooltip ("Imports loudspeakers from a configuration file.");
tbImport.addListener (this);
addAndMakeVisible (tbUndo);
tbUndo.setButtonText ("UNDO");
tbUndo.setColour (juce::TextButton::buttonColourId, juce::Colours::orangered);
tbUndo.onClick = [this]() { processor.undo(); };
addAndMakeVisible (tbRedo);
tbRedo.setButtonText ("REDO");
tbRedo.setColour (juce::TextButton::buttonColourId, juce::Colours::orangered);
tbRedo.onClick = [this]() { processor.redo(); };
addAndMakeVisible (tbRotate);
tbRotate.setButtonText ("ROTATE");
tbRotate.setColour (juce::TextButton::buttonColourId, juce::Colours::cornflowerblue);
tbRotate.setTooltip ("Rotates all loudspeakers by a desired amount around the z-axis.");
tbRotate.onClick = [this]() { openRotateWindow(); };
addAndMakeVisible (lv);
addAndMakeVisible (lspList);
updateChannelCount();
// start timer after everything is set up properly
startTimer (50);
tooltipWin.setLookAndFeel (&globalLaF);
tooltipWin.setMillisecondsBeforeTipAppears (500);
tooltipWin.setOpaque (false);
}
AllRADecoderAudioProcessorEditor::~AllRADecoderAudioProcessorEditor()
{
setLookAndFeel (nullptr);
}
//==============================================================================
void AllRADecoderAudioProcessorEditor::paint (juce::Graphics& g)
{
g.fillAll (globalLaF.ClBackground);
}
void AllRADecoderAudioProcessorEditor::resized()
{
// ============ BEGIN: header and footer ============
const int leftRightMargin = 30;
const int headerHeight = 60;
const int footerHeight = 25;
juce::Rectangle area (getLocalBounds());
juce::Rectangle footerArea (area.removeFromBottom (footerHeight));
footer.setBounds (footerArea);
area.removeFromLeft (leftRightMargin);
area.removeFromRight (leftRightMargin);
juce::Rectangle headerArea = area.removeFromTop (headerHeight);
title.setBounds (headerArea);
area.removeFromTop (10);
area.removeFromBottom (5);
// =========== END: header and footer =================
// try to not use explicit coordinates to position your GUI components
// the removeFrom...() methods are quite handy to create scalable areas
// best practice would be the use of flexBoxes...
// the following is medium level practice ;-)
juce::Rectangle