PluginProcessor.cpp (13152B)
1 /* 2 ============================================================================== 3 4 This file was auto-generated! 5 6 It contains the basic framework code for a JUCE plugin processor. 7 8 ============================================================================== 9 */ 10 11 #include "PluginProcessor.h" 12 #include "GUI/ModulatableSlider.h" 13 #include "GUI/OnOff/PowerButton.h" 14 #include "GUI/OversamplingMenu.h" 15 #include "GUI/SettingsButton.h" 16 #include "GUI/TitleComp.h" 17 #include "GUI/TooltipComp.h" 18 #include "GUI/Visualizers/MixGroupViz.h" 19 #include "GUI/WowFlutterMenu.h" 20 #include "Presets/PresetManager.h" 21 22 #if JUCE_IOS 23 #include "GUI/IOSOnly/ScrollView.h" 24 #include "GUI/IOSOnly/TipJar.h" 25 #endif 26 27 namespace 28 { 29 const String settingsFilePath = "ChowdhuryDSP/ChowTape/.plugin_settings.json"; 30 31 const String isStereoTag = "plugin:is_stereo"; 32 33 const String inGainTag = "ingain"; 34 const String outGainTag = "outgain"; 35 const String dryWetTag = "drywet"; 36 } // namespace 37 38 //============================================================================== 39 ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor() : inputFilters (vts), 40 midSideController (vts), 41 toneControl (vts), 42 compressionProcessor (vts), 43 hysteresis (vts), 44 degrade (vts), 45 chewer (vts), 46 lossFilter (vts), 47 flutter (vts), 48 onOffManager (vts, this), 49 mixGroupsController (vts, this) 50 { 51 pluginSettings->initialise (settingsFilePath); 52 53 chowdsp::ParamUtils::loadParameterPointer (inGainDBParam, vts, inGainTag); 54 chowdsp::ParamUtils::loadParameterPointer (outGainDBParam, vts, outGainTag); 55 chowdsp::ParamUtils::loadParameterPointer (dryWetParam, vts, dryWetTag); 56 57 presetManager = std::make_unique<PresetManager> (vts); 58 59 positionInfo.bpm = 120.0; 60 positionInfo.timeSigNumerator = 4; 61 62 scope = magicState.createAndAddObject<TapeScope> ("scope"); 63 flutter.initialisePlots (magicState); 64 65 LookAndFeel::setDefaultLookAndFeel (&myLNF); 66 67 PluginHostType hostType; 68 if (hostType.isRenoise()) // Renoise has different gain staging, so we handle that here 69 toneControl.setDBScale (12.0f); 70 else 71 toneControl.setDBScale (18.0f); 72 } 73 74 void ChowtapeModelAudioProcessor::addParameters (Parameters& params) 75 { 76 using namespace chowdsp::ParamUtils; 77 createGainDBParameter (params, inGainTag, "Input Gain", -30.0f, 6.0f, 0.0f); 78 createGainDBParameter (params, outGainTag, "Output Gain", -30.0f, 30.0f, 0.0f); 79 createPercentParameter (params, dryWetTag, "Dry/Wet", 1.0f); 80 81 InputFilters::createParameterLayout (params); 82 ToneControl::createParameterLayout (params); 83 CompressionProcessor::createParameterLayout (params); 84 HysteresisProcessor::createParameterLayout (params); 85 LossFilter::createParameterLayout (params); 86 WowFlutterProcessor::createParameterLayout (params); 87 DegradeProcessor::createParameterLayout (params); 88 ChewProcessor::createParameterLayout (params); 89 MidSideProcessor::createParameterLayout (params); 90 MixGroupsController::createParameterLayout (params); 91 } 92 93 void ChowtapeModelAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) 94 { 95 const auto numChannels = getTotalNumInputChannels(); 96 setRateAndBufferSizeDetails (sampleRate, samplesPerBlock); 97 98 inGain.prepareToPlay (sampleRate, samplesPerBlock); 99 inputFilters.prepareToPlay (sampleRate, samplesPerBlock, numChannels); 100 midSideController.prepare (sampleRate, samplesPerBlock); 101 toneControl.prepare (sampleRate, numChannels); 102 compressionProcessor.prepare (sampleRate, samplesPerBlock, numChannels); 103 hysteresis.prepareToPlay (sampleRate, samplesPerBlock, numChannels); 104 degrade.prepareToPlay (sampleRate, samplesPerBlock, numChannels); 105 chewer.prepare (sampleRate, samplesPerBlock, numChannels); 106 lossFilter.prepare ((float) sampleRate, samplesPerBlock, numChannels); 107 108 dryDelay.prepare ({ sampleRate, (uint32) samplesPerBlock, (uint32) numChannels }); 109 dryDelay.setDelay (calcLatencySamples()); 110 111 flutter.prepareToPlay (sampleRate, samplesPerBlock, numChannels); 112 outGain.prepareToPlay (sampleRate, samplesPerBlock); 113 114 scope->setNumChannels (numChannels); 115 scope->prepareToPlay (sampleRate, samplesPerBlock); 116 117 dryWet.setDryWet (*vts.getRawParameterValue ("drywet") / 100.0f); 118 dryWet.reset(); 119 dryBuffer.setSize (numChannels, samplesPerBlock); 120 121 setLatencySamples (roundToInt (calcLatencySamples())); 122 magicState.getPropertyAsValue (isStereoTag).setValue (numChannels == 2); 123 } 124 125 void ChowtapeModelAudioProcessor::releaseResources() 126 { 127 hysteresis.releaseResources(); 128 } 129 130 float ChowtapeModelAudioProcessor::calcLatencySamples() const noexcept 131 { 132 return lossFilter.getLatencySamples() + hysteresis.getLatencySamples() + compressionProcessor.getLatencySamples(); 133 } 134 135 bool ChowtapeModelAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const 136 { 137 if (wrapperType == juce::AudioProcessor::WrapperType::wrapperType_LV2) 138 { 139 return ((layouts.getMainOutputChannelSet() == AudioChannelSet::stereo()) 140 && (layouts.getMainInputChannelSet() == AudioChannelSet::stereo())); 141 } 142 else 143 { 144 return ((! layouts.getMainInputChannelSet().isDiscreteLayout()) 145 && (! layouts.getMainOutputChannelSet().isDiscreteLayout()) 146 && (layouts.getMainInputChannelSet() == layouts.getMainOutputChannelSet()) 147 && (! layouts.getMainInputChannelSet().isDisabled())); 148 } 149 } 150 151 void ChowtapeModelAudioProcessor::processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer&) 152 { 153 ScopedNoDenormals noDenormals; 154 155 dryBuffer.makeCopyOf (buffer, true); 156 latencyCompensation(); 157 buffer.makeCopyOf (dryBuffer, true); 158 } 159 160 void ChowtapeModelAudioProcessor::processAudioBlock (AudioBuffer<float>& buffer) 161 { 162 if (auto* playhead = getPlayHead()) 163 playhead->getCurrentPosition (positionInfo); 164 165 inGain.setGain (Decibels::decibelsToGain (inGainDBParam->getCurrentValue())); 166 outGain.setGain (Decibels::decibelsToGain (outGainDBParam->getCurrentValue())); 167 dryWet.setDryWet (dryWetParam->getCurrentValue()); 168 169 dryBuffer.makeCopyOf (buffer, true); 170 inGain.processBlock (buffer); 171 inputFilters.processBlock (buffer); 172 173 scope->pushSamplesIO (buffer, TapeScope::AudioType::Input); 174 175 midSideController.processInput (buffer); 176 toneControl.processBlockIn (buffer); 177 compressionProcessor.processBlock (buffer); 178 hysteresis.processBlock (buffer); 179 toneControl.processBlockOut (buffer); 180 chewer.processBlock (buffer); 181 degrade.processBlock (buffer); 182 flutter.processBlock (buffer); 183 lossFilter.processBlock (buffer); 184 185 latencyCompensation(); 186 187 midSideController.processOutput (buffer); 188 inputFilters.processBlockMakeup (buffer); 189 outGain.processBlock (buffer); 190 dryWet.processBlock (dryBuffer, buffer); 191 192 chowdsp::BufferMath::sanitizeBuffer (buffer); 193 194 scope->pushSamplesIO (buffer, TapeScope::AudioType::Output); 195 } 196 197 void ChowtapeModelAudioProcessor::latencyCompensation() 198 { 199 // delay dry buffer to avoid phase issues 200 const auto latencySampFloat = calcLatencySamples(); 201 const auto latencySamp = roundToInt (latencySampFloat); 202 setLatencySamples (latencySamp); 203 204 // delay makeup block from input filters 205 inputFilters.setMakeupDelay (latencySampFloat); 206 207 // For "true bypass" use integer sample delay to avoid delay 208 // line interpolation freq. response issues 209 if (dryWet.getDryWet() < 0.15f) 210 { 211 dryDelay.setDelay ((float) latencySamp); 212 } 213 else 214 { 215 dryDelay.setDelay (latencySampFloat); 216 } 217 218 dsp::AudioBlock<float> block { dryBuffer }; 219 dryDelay.process (dsp::ProcessContextReplacing<float> { block }); 220 } 221 222 AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor() 223 { 224 if (openGLHelper == nullptr) 225 openGLHelper = std::make_unique<chowdsp::OpenGLHelper>(); 226 227 auto builder = std::make_unique<foleys::MagicGUIBuilder> (magicState); 228 builder->registerJUCEFactories(); 229 builder->registerFactory ("presets", &chowdsp::PresetsItem<ChowtapeModelAudioProcessor>::factory); 230 builder->registerFactory ("TooltipComp", &TooltipItem::factory); 231 builder->registerFactory ("ModSlider", &ModSliderItem::factory); 232 builder->registerFactory ("TitleComp", &TitleItem::factory); 233 builder->registerFactory ("MixGroupViz", &MixGroupVizItem::factory); 234 builder->registerFactory ("PowerButton", &PowerButtonItem::factory); 235 builder->registerFactory ("OversamplingMenu", &chowdsp::OversamplingMenuItem<ChowtapeModelAudioProcessor, OversamplingMenu>::factory); 236 builder->registerFactory ("SettingsButton", &SettingsButtonItem::factory); 237 builder->registerFactory ("InfoComp", &chowdsp::InfoItem<ChowtapeModelAudioProcessor>::factory); 238 239 builder->registerFactory ("FlutterMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> 240 { return std::make_unique<WowFlutterMenuItem> (b, node, "Flutter"); }); 241 242 builder->registerFactory ("WowMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> 243 { return std::make_unique<WowFlutterMenuItem> (b, node, "Wow"); }); 244 245 builder->registerJUCELookAndFeels(); 246 builder->registerLookAndFeel ("MyLNF", std::make_unique<MyLNF>()); 247 builder->registerLookAndFeel ("ComboBoxLNF", std::make_unique<ComboBoxLNF>()); 248 builder->registerLookAndFeel ("PresetsLNF", std::make_unique<PresetsLNF>()); 249 builder->registerLookAndFeel ("SpeedButtonLNF", std::make_unique<SpeedButtonLNF>()); 250 251 if (auto* speedHandle = dynamic_cast<AudioParameterFloat*> (vts.getParameter ("speed"))) 252 { 253 for (auto speed : { 3.75f, 7.5f, 15.0f, 30.0f }) 254 { 255 magicState.addTrigger ("set_speed_" + String (speed, 2, false), [speedHandle, speed] 256 { 257 speedHandle->beginChangeGesture(); 258 speedHandle->setValueNotifyingHost (speedHandle->convertTo0to1 (speed)); 259 speedHandle->endChangeGesture(); }); 260 } 261 } 262 else 263 { 264 // speedHandle was nullptr! 265 jassertfalse; 266 } 267 268 #if JUCE_IOS 269 builder->registerFactory ("ScrollView", &ScrollView::factory); 270 builder->registerFactory ("TipJar", &TipJarItem::factory); 271 auto* editor = new foleys::MagicPluginEditor (magicState, BinaryData::gui_ios_xml, BinaryData::gui_ios_xmlSize, std::move (builder)); 272 #else 273 auto* editor = new foleys::MagicPluginEditor (magicState, BinaryData::gui_xml, BinaryData::gui_xmlSize, std::move (builder)); 274 #endif 275 276 onOffManager.setOnOffForNewEditor (editor); 277 278 #if CHOWDSP_AUTO_UPDATE 279 updater.showUpdaterScreen (editor); 280 #endif 281 282 // we need to set resize limits for StandalonePluginHolder 283 editor->setResizeLimits (10, 10, 2000, 2000); 284 285 openGLHelper->setComponent (editor); 286 287 return editor; 288 } 289 290 void ChowtapeModelAudioProcessor::getStateInformation (MemoryBlock& destData) 291 { 292 auto xml = std::make_unique<XmlElement> ("state"); 293 xml->setAttribute ("version", chowdsp::VersionUtils::Version (JucePlugin_VersionString).getVersionString()); 294 295 auto state = vts.copyState(); 296 xml->addChildElement (state.createXml().release()); 297 xml->addChildElement (presetManager->saveXmlState().release()); 298 299 copyXmlToBinary (*xml, destData); 300 } 301 302 void ChowtapeModelAudioProcessor::setStateInformation (const void* data, int sizeInBytes) 303 { 304 auto xmlState = getXmlFromBinary (data, sizeInBytes); 305 if (xmlState == nullptr) 306 { 307 // let's check if state was saved with Foley's methods 308 auto tree = ValueTree::readFromData (data, size_t (sizeInBytes)); 309 if (tree.isValid()) 310 vts.replaceState (tree); 311 312 return; 313 } 314 315 if (xmlState->hasAttribute ("version")) 316 { 317 auto* vtsXml = xmlState->getChildByName (vts.state.getType()); 318 if (vtsXml == nullptr) // invalid ValueTreeState 319 return; 320 321 presetManager->loadXmlState (xmlState->getChildByName (chowdsp::PresetManager::presetStateTag)); 322 vts.replaceState (ValueTree::fromXml (*vtsXml)); 323 } 324 else 325 { 326 // state was saved before we started tracking the version with the state, 327 // so let's load state the old way... 328 if (xmlState->hasTagName (vts.state.getType())) 329 vts.replaceState (juce::ValueTree::fromXml (*xmlState)); 330 } 331 } 332 333 // This creates new instances of the plugin.. 334 AudioProcessor* JUCE_CALLTYPE createPluginFilter() 335 { 336 return new ChowtapeModelAudioProcessor(); 337 }