The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2185 lines
97KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: DSPModulePluginDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: An audio plugin using the DSP module.
  24. dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
  25. juce_audio_plugin_client, juce_audio_processors,
  26. juce_audio_utils, juce_core, juce_data_structures, juce_dsp,
  27. juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
  28. exporters: xcode_mac, vs2019, linux_make
  29. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  30. type: AudioProcessor
  31. mainClass: DspModulePluginDemoAudioProcessor
  32. useLocalCopy: 1
  33. END_JUCE_PIP_METADATA
  34. *******************************************************************************/
  35. #pragma once
  36. #include "../Assets/DemoUtilities.h"
  37. namespace ID
  38. {
  39. #define PARAMETER_ID(str) constexpr const char* str { #str };
  40. PARAMETER_ID (inputGain)
  41. PARAMETER_ID (outputGain)
  42. PARAMETER_ID (pan)
  43. PARAMETER_ID (distortionEnabled)
  44. PARAMETER_ID (distortionType)
  45. PARAMETER_ID (distortionOversampler)
  46. PARAMETER_ID (distortionLowpass)
  47. PARAMETER_ID (distortionHighpass)
  48. PARAMETER_ID (distortionInGain)
  49. PARAMETER_ID (distortionCompGain)
  50. PARAMETER_ID (distortionMix)
  51. PARAMETER_ID (convolutionCabEnabled)
  52. PARAMETER_ID (convolutionReverbEnabled)
  53. PARAMETER_ID (convolutionReverbMix)
  54. PARAMETER_ID (multiBandEnabled)
  55. PARAMETER_ID (multiBandFreq)
  56. PARAMETER_ID (multiBandLowVolume)
  57. PARAMETER_ID (multiBandHighVolume)
  58. PARAMETER_ID (compressorEnabled)
  59. PARAMETER_ID (compressorThreshold)
  60. PARAMETER_ID (compressorRatio)
  61. PARAMETER_ID (compressorAttack)
  62. PARAMETER_ID (compressorRelease)
  63. PARAMETER_ID (noiseGateEnabled)
  64. PARAMETER_ID (noiseGateThreshold)
  65. PARAMETER_ID (noiseGateRatio)
  66. PARAMETER_ID (noiseGateAttack)
  67. PARAMETER_ID (noiseGateRelease)
  68. PARAMETER_ID (limiterEnabled)
  69. PARAMETER_ID (limiterThreshold)
  70. PARAMETER_ID (limiterRelease)
  71. PARAMETER_ID (directDelayEnabled)
  72. PARAMETER_ID (directDelayType)
  73. PARAMETER_ID (directDelayValue)
  74. PARAMETER_ID (directDelaySmoothing)
  75. PARAMETER_ID (directDelayMix)
  76. PARAMETER_ID (delayEffectEnabled)
  77. PARAMETER_ID (delayEffectType)
  78. PARAMETER_ID (delayEffectValue)
  79. PARAMETER_ID (delayEffectSmoothing)
  80. PARAMETER_ID (delayEffectLowpass)
  81. PARAMETER_ID (delayEffectFeedback)
  82. PARAMETER_ID (delayEffectMix)
  83. PARAMETER_ID (phaserEnabled)
  84. PARAMETER_ID (phaserRate)
  85. PARAMETER_ID (phaserDepth)
  86. PARAMETER_ID (phaserCentreFrequency)
  87. PARAMETER_ID (phaserFeedback)
  88. PARAMETER_ID (phaserMix)
  89. PARAMETER_ID (chorusEnabled)
  90. PARAMETER_ID (chorusRate)
  91. PARAMETER_ID (chorusDepth)
  92. PARAMETER_ID (chorusCentreDelay)
  93. PARAMETER_ID (chorusFeedback)
  94. PARAMETER_ID (chorusMix)
  95. PARAMETER_ID (ladderEnabled)
  96. PARAMETER_ID (ladderCutoff)
  97. PARAMETER_ID (ladderResonance)
  98. PARAMETER_ID (ladderDrive)
  99. PARAMETER_ID (ladderMode)
  100. #undef PARAMETER_ID
  101. }
  102. template <typename Func, typename... Items>
  103. constexpr void forEach (Func&& func, Items&&... items)
  104. noexcept (noexcept (std::initializer_list<int> { (func (std::forward<Items> (items)), 0)... }))
  105. {
  106. (void) std::initializer_list<int> { ((void) func (std::forward<Items> (items)), 0)... };
  107. }
  108. template <typename... Components>
  109. void addAllAndMakeVisible (Component& target, Components&... children)
  110. {
  111. forEach ([&] (Component& child) { target.addAndMakeVisible (child); }, children...);
  112. }
  113. template <typename... Processors>
  114. void prepareAll (const dsp::ProcessSpec& spec, Processors&... processors)
  115. {
  116. forEach ([&] (auto& proc) { proc.prepare (spec); }, processors...);
  117. }
  118. template <typename... Processors>
  119. void resetAll (Processors&... processors)
  120. {
  121. forEach ([] (auto& proc) { proc.reset(); }, processors...);
  122. }
  123. //==============================================================================
  124. class DspModulePluginDemo : public AudioProcessor,
  125. private ValueTree::Listener
  126. {
  127. public:
  128. DspModulePluginDemo()
  129. : DspModulePluginDemo (AudioProcessorValueTreeState::ParameterLayout{}) {}
  130. //==============================================================================
  131. void prepareToPlay (double sampleRate, int samplesPerBlock) override
  132. {
  133. const auto channels = jmax (getTotalNumInputChannels(), getTotalNumOutputChannels());
  134. if (channels == 0)
  135. return;
  136. chain.prepare ({ sampleRate, (uint32) samplesPerBlock, (uint32) channels });
  137. reset();
  138. }
  139. void reset() override
  140. {
  141. chain.reset();
  142. update();
  143. }
  144. void releaseResources() override {}
  145. void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
  146. {
  147. if (jmax (getTotalNumInputChannels(), getTotalNumOutputChannels()) == 0)
  148. return;
  149. ScopedNoDenormals noDenormals;
  150. if (requiresUpdate.load())
  151. update();
  152. irSize = dsp::get<convolutionIndex> (chain).reverb.getCurrentIRSize();
  153. const auto totalNumInputChannels = getTotalNumInputChannels();
  154. const auto totalNumOutputChannels = getTotalNumOutputChannels();
  155. setLatencySamples (dsp::get<convolutionIndex> (chain).getLatency()
  156. + (dsp::isBypassed<distortionIndex> (chain) ? 0 : roundToInt (dsp::get<distortionIndex> (chain).getLatency())));
  157. const auto numChannels = jmax (totalNumInputChannels, totalNumOutputChannels);
  158. auto inoutBlock = dsp::AudioBlock<float> (buffer).getSubsetChannelBlock (0, (size_t) numChannels);
  159. chain.process (dsp::ProcessContextReplacing<float> (inoutBlock));
  160. }
  161. void processBlock (AudioBuffer<double>&, MidiBuffer&) override {}
  162. //==============================================================================
  163. AudioProcessorEditor* createEditor() override { return nullptr; }
  164. bool hasEditor() const override { return false; }
  165. //==============================================================================
  166. const String getName() const override { return "DSPModulePluginDemo"; }
  167. bool acceptsMidi() const override { return false; }
  168. bool producesMidi() const override { return false; }
  169. bool isMidiEffect() const override { return false; }
  170. double getTailLengthSeconds() const override { return 0.0; }
  171. //==============================================================================
  172. int getNumPrograms() override { return 1; }
  173. int getCurrentProgram() override { return 0; }
  174. void setCurrentProgram (int) override {}
  175. const String getProgramName (int) override { return "None"; }
  176. void changeProgramName (int, const String&) override {}
  177. //==============================================================================
  178. bool isBusesLayoutSupported (const BusesLayout& layout) const override
  179. {
  180. return layout == BusesLayout { { AudioChannelSet::stereo() },
  181. { AudioChannelSet::stereo() } };
  182. }
  183. //==============================================================================
  184. void getStateInformation (MemoryBlock& destData) override
  185. {
  186. copyXmlToBinary (*apvts.copyState().createXml(), destData);
  187. }
  188. void setStateInformation (const void* data, int sizeInBytes) override
  189. {
  190. apvts.replaceState (ValueTree::fromXml (*getXmlFromBinary (data, sizeInBytes)));
  191. }
  192. int getCurrentIRSize() const { return irSize; }
  193. using Parameter = AudioProcessorValueTreeState::Parameter;
  194. // This struct holds references to the raw parameters, so that we don't have to search
  195. // the APVTS (involving string comparisons and map lookups!) every time a parameter
  196. // changes.
  197. struct ParameterReferences
  198. {
  199. template <typename Param>
  200. static void add (AudioProcessorParameterGroup& group, std::unique_ptr<Param> param)
  201. {
  202. group.addChild (std::move (param));
  203. }
  204. template <typename Param>
  205. static void add (AudioProcessorValueTreeState::ParameterLayout& group, std::unique_ptr<Param> param)
  206. {
  207. group.add (std::move (param));
  208. }
  209. template <typename Param, typename Group, typename... Ts>
  210. static Param& addToLayout (Group& layout, Ts&&... ts)
  211. {
  212. auto param = std::make_unique<Param> (std::forward<Ts> (ts)...);
  213. auto& ref = *param;
  214. add (layout, std::move (param));
  215. return ref;
  216. }
  217. static String valueToTextFunction (float x) { return String (x, 2); }
  218. static float textToValueFunction (const String& str) { return str.getFloatValue(); }
  219. static String valueToTextPanFunction (float x) { return getPanningTextForValue ((x + 100.0f) / 200.0f); }
  220. static float textToValuePanFunction (const String& str) { return getPanningValueForText (str) * 200.0f - 100.0f; }
  221. struct MainGroup
  222. {
  223. explicit MainGroup (AudioProcessorParameterGroup& layout)
  224. : inputGain (addToLayout<Parameter> (layout,
  225. ID::inputGain,
  226. "Input",
  227. "dB",
  228. NormalisableRange<float> (-40.0f, 40.0f),
  229. 0.0f,
  230. valueToTextFunction,
  231. textToValueFunction)),
  232. outputGain (addToLayout<Parameter> (layout,
  233. ID::outputGain,
  234. "Output",
  235. "dB",
  236. NormalisableRange<float> (-40.0f, 40.0f),
  237. 0.0f,
  238. valueToTextFunction,
  239. textToValueFunction)),
  240. pan (addToLayout<Parameter> (layout,
  241. ID::pan,
  242. "Panning",
  243. "",
  244. NormalisableRange<float> (-100.0f, 100.0f),
  245. 0.0f,
  246. valueToTextPanFunction,
  247. textToValuePanFunction)) {}
  248. Parameter& inputGain;
  249. Parameter& outputGain;
  250. Parameter& pan;
  251. };
  252. struct DistortionGroup
  253. {
  254. explicit DistortionGroup (AudioProcessorParameterGroup& layout)
  255. : enabled (addToLayout<AudioParameterBool> (layout,
  256. ID::distortionEnabled,
  257. "Distortion",
  258. true,
  259. "")),
  260. type (addToLayout<AudioParameterChoice> (layout,
  261. ID::distortionType,
  262. "Waveshaper",
  263. StringArray { "std::tanh", "Approx. tanh" },
  264. 0)),
  265. inGain (addToLayout<Parameter> (layout,
  266. ID::distortionInGain,
  267. "Gain",
  268. "dB",
  269. NormalisableRange<float> (-40.0f, 40.0f),
  270. 0.0f,
  271. valueToTextFunction,
  272. textToValueFunction)),
  273. lowpass (addToLayout<Parameter> (layout,
  274. ID::distortionLowpass,
  275. "Post Low-pass",
  276. "Hz",
  277. NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
  278. 22000.0f,
  279. valueToTextFunction,
  280. textToValueFunction)),
  281. highpass (addToLayout<Parameter> (layout,
  282. ID::distortionHighpass,
  283. "Pre High-pass",
  284. "Hz",
  285. NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
  286. 20.0f,
  287. valueToTextFunction,
  288. textToValueFunction)),
  289. compGain (addToLayout<Parameter> (layout,
  290. ID::distortionCompGain,
  291. "Compensat.",
  292. "dB",
  293. NormalisableRange<float> (-40.0f, 40.0f),
  294. 0.0f,
  295. valueToTextFunction,
  296. textToValueFunction)),
  297. mix (addToLayout<Parameter> (layout,
  298. ID::distortionMix,
  299. "Mix",
  300. "%",
  301. NormalisableRange<float> (0.0f, 100.0f),
  302. 100.0f,
  303. valueToTextFunction,
  304. textToValueFunction)),
  305. oversampler (addToLayout<AudioParameterChoice> (layout,
  306. ID::distortionOversampler,
  307. "Oversampling",
  308. StringArray { "2X",
  309. "4X",
  310. "8X",
  311. "2X compensated",
  312. "4X compensated",
  313. "8X compensated" },
  314. 1)) {}
  315. AudioParameterBool& enabled;
  316. AudioParameterChoice& type;
  317. Parameter& inGain;
  318. Parameter& lowpass;
  319. Parameter& highpass;
  320. Parameter& compGain;
  321. Parameter& mix;
  322. AudioParameterChoice& oversampler;
  323. };
  324. struct MultiBandGroup
  325. {
  326. explicit MultiBandGroup (AudioProcessorParameterGroup& layout)
  327. : enabled (addToLayout<AudioParameterBool> (layout,
  328. ID::multiBandEnabled,
  329. "Multi-band",
  330. false,
  331. "")),
  332. freq (addToLayout<Parameter> (layout,
  333. ID::multiBandFreq,
  334. "Sep. Freq.",
  335. "Hz",
  336. NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
  337. 2000.0f,
  338. valueToTextFunction,
  339. textToValueFunction)),
  340. lowVolume (addToLayout<Parameter> (layout,
  341. ID::multiBandLowVolume,
  342. "Low volume",
  343. "dB",
  344. NormalisableRange<float> (-40.0f, 40.0f),
  345. 0.0f,
  346. valueToTextFunction,
  347. textToValueFunction)),
  348. highVolume (addToLayout<Parameter> (layout,
  349. ID::multiBandHighVolume,
  350. "High volume",
  351. "dB",
  352. NormalisableRange<float> (-40.0f, 40.0f),
  353. 0.0f,
  354. valueToTextFunction,
  355. textToValueFunction)) {}
  356. AudioParameterBool& enabled;
  357. Parameter& freq;
  358. Parameter& lowVolume;
  359. Parameter& highVolume;
  360. };
  361. struct ConvolutionGroup
  362. {
  363. explicit ConvolutionGroup (AudioProcessorParameterGroup& layout)
  364. : cabEnabled (addToLayout<AudioParameterBool> (layout,
  365. ID::convolutionCabEnabled,
  366. "Cabinet",
  367. false,
  368. "")),
  369. reverbEnabled (addToLayout<AudioParameterBool> (layout,
  370. ID::convolutionReverbEnabled,
  371. "Reverb",
  372. false,
  373. "")),
  374. reverbMix (addToLayout<Parameter> (layout,
  375. ID::convolutionReverbMix,
  376. "Reverb Mix",
  377. "%",
  378. NormalisableRange<float> (0.0f, 100.0f),
  379. 50.0f,
  380. valueToTextFunction,
  381. textToValueFunction)) {}
  382. AudioParameterBool& cabEnabled;
  383. AudioParameterBool& reverbEnabled;
  384. Parameter& reverbMix;
  385. };
  386. struct CompressorGroup
  387. {
  388. explicit CompressorGroup (AudioProcessorParameterGroup& layout)
  389. : enabled (addToLayout<AudioParameterBool> (layout,
  390. ID::compressorEnabled,
  391. "Comp.",
  392. false,
  393. "")),
  394. threshold (addToLayout<Parameter> (layout,
  395. ID::compressorThreshold,
  396. "Threshold",
  397. "dB",
  398. NormalisableRange<float> (-100.0f, 0.0f),
  399. 0.0f,
  400. valueToTextFunction,
  401. textToValueFunction)),
  402. ratio (addToLayout<Parameter> (layout,
  403. ID::compressorRatio,
  404. "Ratio",
  405. ":1",
  406. NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
  407. 1.0f,
  408. valueToTextFunction,
  409. textToValueFunction)),
  410. attack (addToLayout<Parameter> (layout,
  411. ID::compressorAttack,
  412. "Attack",
  413. "ms",
  414. NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
  415. 1.0f,
  416. valueToTextFunction,
  417. textToValueFunction)),
  418. release (addToLayout<Parameter> (layout,
  419. ID::compressorRelease,
  420. "Release",
  421. "ms",
  422. NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
  423. 100.0f,
  424. valueToTextFunction,
  425. textToValueFunction)) {}
  426. AudioParameterBool& enabled;
  427. Parameter& threshold;
  428. Parameter& ratio;
  429. Parameter& attack;
  430. Parameter& release;
  431. };
  432. struct NoiseGateGroup
  433. {
  434. explicit NoiseGateGroup (AudioProcessorParameterGroup& layout)
  435. : enabled (addToLayout<AudioParameterBool> (layout,
  436. ID::noiseGateEnabled,
  437. "Gate",
  438. false,
  439. "")),
  440. threshold (addToLayout<Parameter> (layout,
  441. ID::noiseGateThreshold,
  442. "Threshold",
  443. "dB",
  444. NormalisableRange<float> (-100.0f, 0.0f),
  445. -100.0f,
  446. valueToTextFunction,
  447. textToValueFunction)),
  448. ratio (addToLayout<Parameter> (layout,
  449. ID::noiseGateRatio,
  450. "Ratio",
  451. ":1",
  452. NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
  453. 10.0f,
  454. valueToTextFunction,
  455. textToValueFunction)),
  456. attack (addToLayout<Parameter> (layout,
  457. ID::noiseGateAttack,
  458. "Attack",
  459. "ms",
  460. NormalisableRange<float> (0.01f, 1000.0f, 0.0f, 0.25f),
  461. 1.0f,
  462. valueToTextFunction,
  463. textToValueFunction)),
  464. release (addToLayout<Parameter> (layout,
  465. ID::noiseGateRelease,
  466. "Release",
  467. "ms",
  468. NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
  469. 100.0f,
  470. valueToTextFunction,
  471. textToValueFunction)) {}
  472. AudioParameterBool& enabled;
  473. Parameter& threshold;
  474. Parameter& ratio;
  475. Parameter& attack;
  476. Parameter& release;
  477. };
  478. struct LimiterGroup
  479. {
  480. explicit LimiterGroup (AudioProcessorParameterGroup& layout)
  481. : enabled (addToLayout<AudioParameterBool> (layout,
  482. ID::limiterEnabled,
  483. "Limiter",
  484. false,
  485. "")),
  486. threshold (addToLayout<Parameter> (layout,
  487. ID::limiterThreshold,
  488. "Threshold",
  489. "dB",
  490. NormalisableRange<float> (-40.0f, 0.0f),
  491. 0.0f,
  492. valueToTextFunction,
  493. textToValueFunction)),
  494. release (addToLayout<Parameter> (layout,
  495. ID::limiterRelease,
  496. "Release",
  497. "ms",
  498. NormalisableRange<float> (10.0f, 10000.0f, 0.0f, 0.25f),
  499. 100.0f,
  500. valueToTextFunction,
  501. textToValueFunction)) {}
  502. AudioParameterBool& enabled;
  503. Parameter& threshold;
  504. Parameter& release;
  505. };
  506. struct DirectDelayGroup
  507. {
  508. explicit DirectDelayGroup (AudioProcessorParameterGroup& layout)
  509. : enabled (addToLayout<AudioParameterBool> (layout,
  510. ID::directDelayEnabled,
  511. "DL Dir.",
  512. false,
  513. "")),
  514. type (addToLayout<AudioParameterChoice> (layout,
  515. ID::directDelayType,
  516. "DL Type",
  517. StringArray { "None", "Linear", "Lagrange", "Thiran" },
  518. 1)),
  519. value (addToLayout<Parameter> (layout,
  520. ID::directDelayValue,
  521. "Delay",
  522. "smps",
  523. NormalisableRange<float> (0.0f, 44100.0f),
  524. 0.0f,
  525. valueToTextFunction,
  526. textToValueFunction)),
  527. smoothing (addToLayout<Parameter> (layout,
  528. ID::directDelaySmoothing,
  529. "Smooth",
  530. "ms",
  531. NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
  532. 200.0f,
  533. valueToTextFunction,
  534. textToValueFunction)),
  535. mix (addToLayout<Parameter> (layout,
  536. ID::directDelayMix,
  537. "Delay Mix",
  538. "%",
  539. NormalisableRange<float> (0.0f, 100.0f),
  540. 50.0f,
  541. valueToTextFunction,
  542. textToValueFunction)) {}
  543. AudioParameterBool& enabled;
  544. AudioParameterChoice& type;
  545. Parameter& value;
  546. Parameter& smoothing;
  547. Parameter& mix;
  548. };
  549. struct DelayEffectGroup
  550. {
  551. explicit DelayEffectGroup (AudioProcessorParameterGroup& layout)
  552. : enabled (addToLayout<AudioParameterBool> (layout,
  553. ID::delayEffectEnabled,
  554. "DL Effect",
  555. false,
  556. "")),
  557. type (addToLayout<AudioParameterChoice> (layout,
  558. ID::delayEffectType,
  559. "DL Type",
  560. StringArray { "None", "Linear", "Lagrange", "Thiran" },
  561. 1)),
  562. value (addToLayout<Parameter> (layout,
  563. ID::delayEffectValue,
  564. "Delay",
  565. "ms",
  566. NormalisableRange<float> (0.01f, 1000.0f),
  567. 100.0f,
  568. valueToTextFunction,
  569. textToValueFunction)),
  570. smoothing (addToLayout<Parameter> (layout,
  571. ID::delayEffectSmoothing,
  572. "Smooth",
  573. "ms",
  574. NormalisableRange<float> (20.0f, 10000.0f, 0.0f, 0.25f),
  575. 400.0f,
  576. valueToTextFunction,
  577. textToValueFunction)),
  578. lowpass (addToLayout<Parameter> (layout,
  579. ID::delayEffectLowpass,
  580. "Low-pass",
  581. "Hz",
  582. NormalisableRange<float> (20.0f, 22000.0f, 0.0f, 0.25f),
  583. 22000.0f,
  584. valueToTextFunction,
  585. textToValueFunction)),
  586. mix (addToLayout<Parameter> (layout,
  587. ID::delayEffectMix,
  588. "Delay Mix",
  589. "%",
  590. NormalisableRange<float> (0.0f, 100.0f),
  591. 50.0f,
  592. valueToTextFunction,
  593. textToValueFunction)),
  594. feedback (addToLayout<Parameter> (layout,
  595. ID::delayEffectFeedback,
  596. "Feedback",
  597. "dB",
  598. NormalisableRange<float> (-100.0f, 0.0f),
  599. -100.0f,
  600. valueToTextFunction,
  601. textToValueFunction)) {}
  602. AudioParameterBool& enabled;
  603. AudioParameterChoice& type;
  604. Parameter& value;
  605. Parameter& smoothing;
  606. Parameter& lowpass;
  607. Parameter& mix;
  608. Parameter& feedback;
  609. };
  610. struct PhaserGroup
  611. {
  612. explicit PhaserGroup (AudioProcessorParameterGroup& layout)
  613. : enabled (addToLayout<AudioParameterBool> (layout,
  614. ID::phaserEnabled,
  615. "Phaser",
  616. false,
  617. "")),
  618. rate (addToLayout<Parameter> (layout,
  619. ID::phaserRate,
  620. "Rate",
  621. "Hz",
  622. NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
  623. 1.0f,
  624. valueToTextFunction,
  625. textToValueFunction)),
  626. depth (addToLayout<Parameter> (layout,
  627. ID::phaserDepth,
  628. "Depth",
  629. "%",
  630. NormalisableRange<float> (0.0f, 100.0f),
  631. 50.0f,
  632. valueToTextFunction,
  633. textToValueFunction)),
  634. centreFrequency (addToLayout<Parameter> (layout,
  635. ID::phaserCentreFrequency,
  636. "Center",
  637. "Hz",
  638. NormalisableRange<float> (20.0f, 20000.0f, 0.0f, 0.25f),
  639. 600.0f,
  640. valueToTextFunction,
  641. textToValueFunction)),
  642. feedback (addToLayout<Parameter> (layout,
  643. ID::phaserFeedback,
  644. "Feedback",
  645. "%",
  646. NormalisableRange<float> (0.0f, 100.0f),
  647. 50.0f,
  648. valueToTextFunction,
  649. textToValueFunction)),
  650. mix (addToLayout<Parameter> (layout,
  651. ID::phaserMix,
  652. "Mix",
  653. "%",
  654. NormalisableRange<float> (0.0f, 100.0f),
  655. 50.0f,
  656. valueToTextFunction,
  657. textToValueFunction)) {}
  658. AudioParameterBool& enabled;
  659. Parameter& rate;
  660. Parameter& depth;
  661. Parameter& centreFrequency;
  662. Parameter& feedback;
  663. Parameter& mix;
  664. };
  665. struct ChorusGroup
  666. {
  667. explicit ChorusGroup (AudioProcessorParameterGroup& layout)
  668. : enabled (addToLayout<AudioParameterBool> (layout,
  669. ID::chorusEnabled,
  670. "Chorus",
  671. false,
  672. "")),
  673. rate (addToLayout<Parameter> (layout,
  674. ID::chorusRate,
  675. "Rate",
  676. "Hz",
  677. NormalisableRange<float> (0.05f, 20.0f, 0.0f, 0.25f),
  678. 1.0f,
  679. valueToTextFunction,
  680. textToValueFunction)),
  681. depth (addToLayout<Parameter> (layout,
  682. ID::chorusDepth,
  683. "Depth",
  684. "%",
  685. NormalisableRange<float> (0.0f, 100.0f),
  686. 50.0f,
  687. valueToTextFunction,
  688. textToValueFunction)),
  689. centreDelay (addToLayout<Parameter> (layout,
  690. ID::chorusCentreDelay,
  691. "Center",
  692. "ms",
  693. NormalisableRange<float> (1.0f, 100.0f, 0.0f, 0.25f),
  694. 7.0f,
  695. valueToTextFunction,
  696. textToValueFunction)),
  697. feedback (addToLayout<Parameter> (layout,
  698. ID::chorusFeedback,
  699. "Feedback",
  700. "%",
  701. NormalisableRange<float> (0.0f, 100.0f),
  702. 50.0f,
  703. valueToTextFunction,
  704. textToValueFunction)),
  705. mix (addToLayout<Parameter> (layout,
  706. ID::chorusMix,
  707. "Mix",
  708. "%",
  709. NormalisableRange<float> (0.0f, 100.0f),
  710. 50.0f,
  711. valueToTextFunction,
  712. textToValueFunction)) {}
  713. AudioParameterBool& enabled;
  714. Parameter& rate;
  715. Parameter& depth;
  716. Parameter& centreDelay;
  717. Parameter& feedback;
  718. Parameter& mix;
  719. };
  720. struct LadderGroup
  721. {
  722. explicit LadderGroup (AudioProcessorParameterGroup& layout)
  723. : enabled (addToLayout<AudioParameterBool> (layout,
  724. ID::ladderEnabled,
  725. "Ladder",
  726. false,
  727. "")),
  728. mode (addToLayout<AudioParameterChoice> (layout,
  729. ID::ladderMode,
  730. "Mode",
  731. StringArray { "LP12", "LP24", "HP12", "HP24", "BP12", "BP24" },
  732. 1)),
  733. cutoff (addToLayout<Parameter> (layout,
  734. ID::ladderCutoff,
  735. "Frequency",
  736. "Hz",
  737. NormalisableRange<float> (10.0f, 22000.0f, 0.0f, 0.25f),
  738. 1000.0f,
  739. valueToTextFunction,
  740. textToValueFunction)),
  741. resonance (addToLayout<Parameter> (layout,
  742. ID::ladderResonance,
  743. "Resonance",
  744. "%",
  745. NormalisableRange<float> (0.0f, 100.0f),
  746. 0.0f,
  747. valueToTextFunction,
  748. textToValueFunction)),
  749. drive (addToLayout<Parameter> (layout,
  750. ID::ladderDrive,
  751. "Drive",
  752. "dB",
  753. NormalisableRange<float> (0.0f, 40.0f),
  754. 0.0f,
  755. valueToTextFunction,
  756. textToValueFunction)) {}
  757. AudioParameterBool& enabled;
  758. AudioParameterChoice& mode;
  759. Parameter& cutoff;
  760. Parameter& resonance;
  761. Parameter& drive;
  762. };
  763. explicit ParameterReferences (AudioProcessorValueTreeState::ParameterLayout& layout)
  764. : main (addToLayout<AudioProcessorParameterGroup> (layout, "main", "Main", "|")),
  765. distortion (addToLayout<AudioProcessorParameterGroup> (layout, "distortion", "Distortion", "|")),
  766. multiBand (addToLayout<AudioProcessorParameterGroup> (layout, "multiband", "Multi Band", "|")),
  767. convolution (addToLayout<AudioProcessorParameterGroup> (layout, "convolution", "Convolution", "|")),
  768. compressor (addToLayout<AudioProcessorParameterGroup> (layout, "compressor", "Compressor", "|")),
  769. noiseGate (addToLayout<AudioProcessorParameterGroup> (layout, "noisegate", "Noise Gate", "|")),
  770. limiter (addToLayout<AudioProcessorParameterGroup> (layout, "limiter", "Limiter", "|")),
  771. directDelay (addToLayout<AudioProcessorParameterGroup> (layout, "directdelay", "Direct Delay", "|")),
  772. delayEffect (addToLayout<AudioProcessorParameterGroup> (layout, "delayeffect", "Delay Effect", "|")),
  773. phaser (addToLayout<AudioProcessorParameterGroup> (layout, "phaser", "Phaser", "|")),
  774. chorus (addToLayout<AudioProcessorParameterGroup> (layout, "chorus", "Chorus", "|")),
  775. ladder (addToLayout<AudioProcessorParameterGroup> (layout, "ladder", "Ladder", "|")) {}
  776. MainGroup main;
  777. DistortionGroup distortion;
  778. MultiBandGroup multiBand;
  779. ConvolutionGroup convolution;
  780. CompressorGroup compressor;
  781. NoiseGateGroup noiseGate;
  782. LimiterGroup limiter;
  783. DirectDelayGroup directDelay;
  784. DelayEffectGroup delayEffect;
  785. PhaserGroup phaser;
  786. ChorusGroup chorus;
  787. LadderGroup ladder;
  788. };
  789. const ParameterReferences& getParameterValues() const noexcept { return parameters; }
  790. //==============================================================================
  791. // We store this here so that the editor retains its state if it is closed and reopened
  792. int indexTab = 0;
  793. private:
  794. struct LayoutAndReferences
  795. {
  796. AudioProcessorValueTreeState::ParameterLayout layout;
  797. ParameterReferences references;
  798. };
  799. explicit DspModulePluginDemo (AudioProcessorValueTreeState::ParameterLayout layout)
  800. : AudioProcessor (BusesProperties().withInput ("In", AudioChannelSet::stereo())
  801. .withOutput ("Out", AudioChannelSet::stereo())),
  802. parameters { layout },
  803. apvts { *this, nullptr, "state", std::move (layout) }
  804. {
  805. apvts.state.addListener (this);
  806. forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
  807. dsp::get<inputGainIndex> (chain),
  808. dsp::get<outputGainIndex> (chain));
  809. dsp::get<pannerIndex> (chain).setRule (dsp::PannerRule::linear);
  810. }
  811. //==============================================================================
  812. void valueTreePropertyChanged (ValueTree&, const Identifier&) override
  813. {
  814. requiresUpdate.store (true);
  815. }
  816. //==============================================================================
  817. void update()
  818. {
  819. {
  820. DistortionProcessor& distortion = dsp::get<distortionIndex> (chain);
  821. if (distortion.currentIndexOversampling != parameters.distortion.oversampler.getIndex())
  822. {
  823. distortion.currentIndexOversampling = parameters.distortion.oversampler.getIndex();
  824. prepareToPlay (getSampleRate(), getBlockSize());
  825. return;
  826. }
  827. distortion.currentIndexWaveshaper = parameters.distortion.type.getIndex();
  828. distortion.lowpass .setCutoffFrequency (parameters.distortion.lowpass.get());
  829. distortion.highpass.setCutoffFrequency (parameters.distortion.highpass.get());
  830. distortion.distGain.setGainDecibels (parameters.distortion.inGain.get());
  831. distortion.compGain.setGainDecibels (parameters.distortion.compGain.get());
  832. distortion.mixer.setWetMixProportion (parameters.distortion.mix.get() / 100.0f);
  833. dsp::setBypassed<distortionIndex> (chain, ! parameters.distortion.enabled);
  834. }
  835. {
  836. ConvolutionProcessor& convolution = dsp::get<convolutionIndex> (chain);
  837. convolution.cabEnabled = parameters.convolution.cabEnabled;
  838. convolution.reverbEnabled = parameters.convolution.reverbEnabled;
  839. convolution.mixer.setWetMixProportion (parameters.convolution.reverbMix.get() / 100.0f);
  840. }
  841. dsp::get<inputGainIndex> (chain).setGainDecibels (parameters.main.inputGain.get());
  842. dsp::get<outputGainIndex> (chain).setGainDecibels (parameters.main.outputGain.get());
  843. dsp::get<pannerIndex> (chain).setPan (parameters.main.pan.get() / 100.0f);
  844. {
  845. MultiBandProcessor& multiband = dsp::get<multiBandIndex> (chain);
  846. const auto multibandFreq = parameters.multiBand.freq.get();
  847. multiband.lowpass .setCutoffFrequency (multibandFreq);
  848. multiband.highpass.setCutoffFrequency (multibandFreq);
  849. const bool enabled = parameters.multiBand.enabled;
  850. multiband.lowVolume .setGainDecibels (enabled ? parameters.multiBand.lowVolume .get() : 0.0f);
  851. multiband.highVolume.setGainDecibels (enabled ? parameters.multiBand.highVolume.get() : 0.0f);
  852. dsp::setBypassed<multiBandIndex> (chain, ! enabled);
  853. }
  854. {
  855. dsp::Compressor<float>& compressor = dsp::get<compressorIndex> (chain);
  856. compressor.setThreshold (parameters.compressor.threshold.get());
  857. compressor.setRatio (parameters.compressor.ratio.get());
  858. compressor.setAttack (parameters.compressor.attack.get());
  859. compressor.setRelease (parameters.compressor.release.get());
  860. dsp::setBypassed<compressorIndex> (chain, ! parameters.compressor.enabled);
  861. }
  862. {
  863. dsp::NoiseGate<float>& noiseGate = dsp::get<noiseGateIndex> (chain);
  864. noiseGate.setThreshold (parameters.noiseGate.threshold.get());
  865. noiseGate.setRatio (parameters.noiseGate.ratio.get());
  866. noiseGate.setAttack (parameters.noiseGate.attack.get());
  867. noiseGate.setRelease (parameters.noiseGate.release.get());
  868. dsp::setBypassed<noiseGateIndex> (chain, ! parameters.noiseGate.enabled);
  869. }
  870. {
  871. dsp::Limiter<float>& limiter = dsp::get<limiterIndex> (chain);
  872. limiter.setThreshold (parameters.limiter.threshold.get());
  873. limiter.setRelease (parameters.limiter.release.get());
  874. dsp::setBypassed<limiterIndex> (chain, ! parameters.limiter.enabled);
  875. }
  876. {
  877. DirectDelayProcessor& delay = dsp::get<directDelayIndex> (chain);
  878. delay.delayLineDirectType = parameters.directDelay.type.getIndex();
  879. std::fill (delay.delayDirectValue.begin(),
  880. delay.delayDirectValue.end(),
  881. (double) parameters.directDelay.value.get());
  882. delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.directDelay.smoothing.get());
  883. delay.mixer.setWetMixProportion (parameters.directDelay.mix.get() / 100.0f);
  884. dsp::setBypassed<directDelayIndex> (chain, ! parameters.directDelay.enabled);
  885. }
  886. {
  887. DelayEffectProcessor& delay = dsp::get<delayEffectIndex> (chain);
  888. delay.delayEffectType = parameters.delayEffect.type.getIndex();
  889. std::fill (delay.delayEffectValue.begin(),
  890. delay.delayEffectValue.end(),
  891. (double) parameters.delayEffect.value.get() / 1000.0 * getSampleRate());
  892. const auto feedbackGain = Decibels::decibelsToGain (parameters.delayEffect.feedback.get(), -100.0f);
  893. for (auto& volume : delay.delayFeedbackVolume)
  894. volume.setTargetValue (feedbackGain);
  895. delay.smoothFilter.setCutoffFrequency (1000.0 / parameters.delayEffect.smoothing.get());
  896. delay.lowpass.setCutoffFrequency (parameters.delayEffect.lowpass.get());
  897. delay.mixer.setWetMixProportion (parameters.delayEffect.mix.get() / 100.0f);
  898. dsp::setBypassed<delayEffectIndex> (chain, ! parameters.delayEffect.enabled);
  899. }
  900. {
  901. dsp::Phaser<float>& phaser = dsp::get<phaserIndex> (chain);
  902. phaser.setRate (parameters.phaser.rate.get());
  903. phaser.setDepth (parameters.phaser.depth.get() / 100.0f);
  904. phaser.setCentreFrequency (parameters.phaser.centreFrequency.get());
  905. phaser.setFeedback (parameters.phaser.feedback.get() / 100.0f * 0.95f);
  906. phaser.setMix (parameters.phaser.mix.get() / 100.0f);
  907. dsp::setBypassed<phaserIndex> (chain, ! parameters.phaser.enabled);
  908. }
  909. {
  910. dsp::Chorus<float>& chorus = dsp::get<chorusIndex> (chain);
  911. chorus.setRate (parameters.chorus.rate.get());
  912. chorus.setDepth (parameters.chorus.depth.get() / 100.0f);
  913. chorus.setCentreDelay (parameters.chorus.centreDelay.get());
  914. chorus.setFeedback (parameters.chorus.feedback.get() / 100.0f * 0.95f);
  915. chorus.setMix (parameters.chorus.mix.get() / 100.0f);
  916. dsp::setBypassed<chorusIndex> (chain, ! parameters.chorus.enabled);
  917. }
  918. {
  919. dsp::LadderFilter<float>& ladder = dsp::get<ladderIndex> (chain);
  920. ladder.setCutoffFrequencyHz (parameters.ladder.cutoff.get());
  921. ladder.setResonance (parameters.ladder.resonance.get() / 100.0f);
  922. ladder.setDrive (Decibels::decibelsToGain (parameters.ladder.drive.get()));
  923. ladder.setMode ([&]
  924. {
  925. switch (parameters.ladder.mode.getIndex())
  926. {
  927. case 0: return dsp::LadderFilterMode::LPF12;
  928. case 1: return dsp::LadderFilterMode::LPF24;
  929. case 2: return dsp::LadderFilterMode::HPF12;
  930. case 3: return dsp::LadderFilterMode::HPF24;
  931. case 4: return dsp::LadderFilterMode::BPF12;
  932. default: break;
  933. }
  934. return dsp::LadderFilterMode::BPF24;
  935. }());
  936. dsp::setBypassed<ladderIndex> (chain, ! parameters.ladder.enabled);
  937. }
  938. requiresUpdate.store (false);
  939. }
  940. //==============================================================================
  941. static String getPanningTextForValue (float value)
  942. {
  943. if (value == 0.5f)
  944. return "center";
  945. if (value < 0.5f)
  946. return String (roundToInt ((0.5f - value) * 200.0f)) + "%L";
  947. return String (roundToInt ((value - 0.5f) * 200.0f)) + "%R";
  948. }
  949. static float getPanningValueForText (String strText)
  950. {
  951. if (strText.compareIgnoreCase ("center") == 0 || strText.compareIgnoreCase ("c") == 0)
  952. return 0.5f;
  953. strText = strText.trim();
  954. if (strText.indexOfIgnoreCase ("%L") != -1)
  955. {
  956. auto percentage = (float) strText.substring (0, strText.indexOf ("%")).getDoubleValue();
  957. return (100.0f - percentage) / 100.0f * 0.5f;
  958. }
  959. if (strText.indexOfIgnoreCase ("%R") != -1)
  960. {
  961. auto percentage = (float) strText.substring (0, strText.indexOf ("%")).getDoubleValue();
  962. return percentage / 100.0f * 0.5f + 0.5f;
  963. }
  964. return 0.5f;
  965. }
  966. //==============================================================================
  967. struct DistortionProcessor
  968. {
  969. DistortionProcessor()
  970. {
  971. forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
  972. distGain,
  973. compGain);
  974. lowpass.setType (dsp::FirstOrderTPTFilterType::lowpass);
  975. highpass.setType (dsp::FirstOrderTPTFilterType::highpass);
  976. mixer.setMixingRule (dsp::DryWetMixingRule::linear);
  977. }
  978. void prepare (const dsp::ProcessSpec& spec)
  979. {
  980. for (auto& oversampler : oversamplers)
  981. oversampler.initProcessing (spec.maximumBlockSize);
  982. prepareAll (spec, lowpass, highpass, distGain, compGain, mixer);
  983. }
  984. void reset()
  985. {
  986. for (auto& oversampler : oversamplers)
  987. oversampler.reset();
  988. resetAll (lowpass, highpass, distGain, compGain, mixer);
  989. }
  990. float getLatency() const
  991. {
  992. return oversamplers[size_t (currentIndexOversampling)].getLatencyInSamples();
  993. }
  994. template <typename Context>
  995. void process (Context& context)
  996. {
  997. if (context.isBypassed)
  998. return;
  999. const auto& inputBlock = context.getInputBlock();
  1000. mixer.setWetLatency (getLatency());
  1001. mixer.pushDrySamples (inputBlock);
  1002. distGain.process (context);
  1003. highpass.process (context);
  1004. auto ovBlock = oversamplers[size_t (currentIndexOversampling)].processSamplesUp (inputBlock);
  1005. dsp::ProcessContextReplacing<float> waveshaperContext (ovBlock);
  1006. if (isPositiveAndBelow (currentIndexWaveshaper, waveShapers.size()))
  1007. {
  1008. waveShapers[size_t (currentIndexWaveshaper)].process (waveshaperContext);
  1009. if (currentIndexWaveshaper == 1)
  1010. clipping.process (waveshaperContext);
  1011. waveshaperContext.getOutputBlock() *= 0.7f;
  1012. }
  1013. auto& outputBlock = context.getOutputBlock();
  1014. oversamplers[size_t (currentIndexOversampling)].processSamplesDown (outputBlock);
  1015. lowpass.process (context);
  1016. compGain.process (context);
  1017. mixer.mixWetSamples (outputBlock);
  1018. }
  1019. std::array<dsp::Oversampling<float>, 6> oversamplers
  1020. { {
  1021. { 2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
  1022. { 2, 2, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
  1023. { 2, 3, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, false },
  1024. { 2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
  1025. { 2, 2, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
  1026. { 2, 3, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, true, true },
  1027. } };
  1028. static float clip (float in) { return juce::jlimit (-1.0f, 1.0f, in); }
  1029. dsp::FirstOrderTPTFilter<float> lowpass, highpass;
  1030. dsp::Gain<float> distGain, compGain;
  1031. dsp::DryWetMixer<float> mixer { 10 };
  1032. std::array<dsp::WaveShaper<float>, 2> waveShapers { { { std::tanh },
  1033. { dsp::FastMathApproximations::tanh } } };
  1034. dsp::WaveShaper<float> clipping { clip };
  1035. int currentIndexOversampling = 0;
  1036. int currentIndexWaveshaper = 0;
  1037. };
  1038. struct ConvolutionProcessor
  1039. {
  1040. ConvolutionProcessor()
  1041. {
  1042. loadImpulseResponse (cabinet, "guitar_amp.wav");
  1043. loadImpulseResponse (reverb, "reverb_ir.wav");
  1044. mixer.setMixingRule (dsp::DryWetMixingRule::balanced);
  1045. }
  1046. void prepare (const dsp::ProcessSpec& spec)
  1047. {
  1048. prepareAll (spec, cabinet, reverb, mixer);
  1049. }
  1050. void reset()
  1051. {
  1052. resetAll (cabinet, reverb, mixer);
  1053. }
  1054. template <typename Context>
  1055. void process (Context& context)
  1056. {
  1057. auto contextConv = context;
  1058. contextConv.isBypassed = (! cabEnabled) || context.isBypassed;
  1059. cabinet.process (contextConv);
  1060. if (cabEnabled)
  1061. context.getOutputBlock().multiplyBy (4.0f);
  1062. if (reverbEnabled)
  1063. mixer.pushDrySamples (context.getInputBlock());
  1064. contextConv.isBypassed = (! reverbEnabled) || context.isBypassed;
  1065. reverb.process (contextConv);
  1066. if (reverbEnabled)
  1067. {
  1068. const auto& outputBlock = context.getOutputBlock();
  1069. outputBlock.multiplyBy (4.0f);
  1070. mixer.mixWetSamples (outputBlock);
  1071. }
  1072. }
  1073. int getLatency() const
  1074. {
  1075. auto latency = 0;
  1076. if (cabEnabled)
  1077. latency += cabinet.getLatency();
  1078. if (reverbEnabled)
  1079. latency += reverb.getLatency();
  1080. return latency;
  1081. }
  1082. dsp::ConvolutionMessageQueue queue;
  1083. dsp::Convolution cabinet { dsp::Convolution::NonUniform { 512 }, queue };
  1084. dsp::Convolution reverb { dsp::Convolution::NonUniform { 512 }, queue };
  1085. dsp::DryWetMixer<float> mixer;
  1086. bool cabEnabled = false, reverbEnabled = false;
  1087. private:
  1088. static void loadImpulseResponse (dsp::Convolution& convolution, const char* filename)
  1089. {
  1090. auto stream = createAssetInputStream (filename);
  1091. if (stream == nullptr)
  1092. {
  1093. jassertfalse;
  1094. return;
  1095. }
  1096. AudioFormatManager manager;
  1097. manager.registerBasicFormats();
  1098. std::unique_ptr<AudioFormatReader> reader { manager.createReaderFor (std::move (stream)) };
  1099. if (reader == nullptr)
  1100. {
  1101. jassertfalse;
  1102. return;
  1103. }
  1104. AudioBuffer<float> buffer (static_cast<int> (reader->numChannels),
  1105. static_cast<int> (reader->lengthInSamples));
  1106. reader->read (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), 0, buffer.getNumSamples());
  1107. convolution.loadImpulseResponse (std::move (buffer),
  1108. reader->sampleRate,
  1109. dsp::Convolution::Stereo::yes,
  1110. dsp::Convolution::Trim::yes,
  1111. dsp::Convolution::Normalise::yes);
  1112. }
  1113. };
  1114. struct MultiBandProcessor
  1115. {
  1116. MultiBandProcessor()
  1117. {
  1118. forEach ([] (dsp::Gain<float>& gain) { gain.setRampDurationSeconds (0.05); },
  1119. lowVolume,
  1120. highVolume);
  1121. lowpass .setType (dsp::LinkwitzRileyFilterType::lowpass);
  1122. highpass.setType (dsp::LinkwitzRileyFilterType::highpass);
  1123. }
  1124. void prepare (const dsp::ProcessSpec& spec)
  1125. {
  1126. prepareAll (spec, lowpass, highpass, lowVolume, highVolume);
  1127. bufferSeparation.setSize (4, int (spec.maximumBlockSize), false, false, true);
  1128. }
  1129. void reset()
  1130. {
  1131. resetAll (lowpass, highpass, lowVolume, highVolume);
  1132. }
  1133. template <typename Context>
  1134. void process (Context& context)
  1135. {
  1136. const auto& inputBlock = context.getInputBlock();
  1137. const auto numSamples = inputBlock.getNumSamples();
  1138. const auto numChannels = inputBlock.getNumChannels();
  1139. auto sepBlock = dsp::AudioBlock<float> (bufferSeparation).getSubBlock (0, (size_t) numSamples);
  1140. auto sepLowBlock = sepBlock.getSubsetChannelBlock (0, (size_t) numChannels);
  1141. auto sepHighBlock = sepBlock.getSubsetChannelBlock (2, (size_t) numChannels);
  1142. sepLowBlock .copyFrom (inputBlock);
  1143. sepHighBlock.copyFrom (inputBlock);
  1144. auto contextLow = dsp::ProcessContextReplacing<float> (sepLowBlock);
  1145. contextLow.isBypassed = context.isBypassed;
  1146. lowpass .process (contextLow);
  1147. lowVolume.process (contextLow);
  1148. auto contextHigh = dsp::ProcessContextReplacing<float> (sepHighBlock);
  1149. contextHigh.isBypassed = context.isBypassed;
  1150. highpass .process (contextHigh);
  1151. highVolume.process (contextHigh);
  1152. if (! context.isBypassed)
  1153. {
  1154. sepLowBlock.add (sepHighBlock);
  1155. context.getOutputBlock().copyFrom (sepLowBlock);
  1156. }
  1157. }
  1158. dsp::LinkwitzRileyFilter<float> lowpass, highpass;
  1159. dsp::Gain<float> lowVolume, highVolume;
  1160. AudioBuffer<float> bufferSeparation;
  1161. };
  1162. struct DirectDelayProcessor
  1163. {
  1164. DirectDelayProcessor()
  1165. {
  1166. smoothFilter.setType (dsp::FirstOrderTPTFilterType::lowpass);
  1167. mixer.setMixingRule (dsp::DryWetMixingRule::linear);
  1168. }
  1169. void prepare (const dsp::ProcessSpec& spec)
  1170. {
  1171. prepareAll (spec, noInterpolation, linear, lagrange, thiran, smoothFilter, mixer);
  1172. }
  1173. void reset()
  1174. {
  1175. resetAll (noInterpolation, linear, lagrange, thiran, smoothFilter, mixer);
  1176. }
  1177. template <typename Context>
  1178. void process (Context& context)
  1179. {
  1180. if (context.isBypassed)
  1181. return;
  1182. const auto& inputBlock = context.getInputBlock();
  1183. const auto& outputBlock = context.getOutputBlock();
  1184. mixer.pushDrySamples (inputBlock);
  1185. const auto numChannels = inputBlock.getNumChannels();
  1186. const auto numSamples = inputBlock.getNumSamples();
  1187. for (size_t channel = 0; channel < numChannels; ++channel)
  1188. {
  1189. auto* samplesIn = inputBlock .getChannelPointer (channel);
  1190. auto* samplesOut = outputBlock.getChannelPointer (channel);
  1191. for (size_t i = 0; i < numSamples; ++i)
  1192. {
  1193. const auto delay = smoothFilter.processSample (int (channel), delayDirectValue[channel]);
  1194. samplesOut[i] = [&]
  1195. {
  1196. switch (delayLineDirectType)
  1197. {
  1198. case 0:
  1199. noInterpolation.pushSample (int (channel), samplesIn[i]);
  1200. noInterpolation.setDelay ((float) delay);
  1201. return noInterpolation.popSample (int (channel));
  1202. case 1:
  1203. linear.pushSample (int (channel), samplesIn[i]);
  1204. linear.setDelay ((float) delay);
  1205. return linear.popSample (int (channel));
  1206. case 2:
  1207. lagrange.pushSample (int (channel), samplesIn[i]);
  1208. lagrange.setDelay ((float) delay);
  1209. return lagrange.popSample (int (channel));
  1210. case 3:
  1211. thiran.pushSample (int (channel), samplesIn[i]);
  1212. thiran.setDelay ((float) delay);
  1213. return thiran.popSample (int (channel));
  1214. default:
  1215. break;
  1216. }
  1217. jassertfalse;
  1218. return 0.0f;
  1219. }();
  1220. }
  1221. }
  1222. mixer.mixWetSamples (outputBlock);
  1223. }
  1224. static constexpr auto directDelayBufferSize = 44100;
  1225. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::None> noInterpolation { directDelayBufferSize };
  1226. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Linear> linear { directDelayBufferSize };
  1227. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Lagrange3rd> lagrange { directDelayBufferSize };
  1228. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Thiran> thiran { directDelayBufferSize };
  1229. // Double precision to avoid some approximation issues
  1230. dsp::FirstOrderTPTFilter<double> smoothFilter;
  1231. dsp::DryWetMixer<float> mixer;
  1232. std::array<double, 2> delayDirectValue { {} };
  1233. int delayLineDirectType = 1;
  1234. };
  1235. struct DelayEffectProcessor
  1236. {
  1237. DelayEffectProcessor()
  1238. {
  1239. smoothFilter.setType (dsp::FirstOrderTPTFilterType::lowpass);
  1240. lowpass.setType (dsp::FirstOrderTPTFilterType::lowpass);
  1241. mixer.setMixingRule (dsp::DryWetMixingRule::linear);
  1242. }
  1243. void prepare (const dsp::ProcessSpec& spec)
  1244. {
  1245. prepareAll (spec, noInterpolation, linear, lagrange, thiran, smoothFilter, lowpass, mixer);
  1246. for (auto& volume : delayFeedbackVolume)
  1247. volume.reset (spec.sampleRate, 0.05);
  1248. }
  1249. void reset()
  1250. {
  1251. resetAll (noInterpolation, linear, lagrange, thiran, smoothFilter, lowpass, mixer);
  1252. std::fill (lastDelayEffectOutput.begin(), lastDelayEffectOutput.end(), 0.0f);
  1253. }
  1254. template <typename Context>
  1255. void process (Context& context)
  1256. {
  1257. if (context.isBypassed)
  1258. return;
  1259. const auto& inputBlock = context.getInputBlock();
  1260. const auto& outputBlock = context.getOutputBlock();
  1261. const auto numSamples = inputBlock.getNumSamples();
  1262. const auto numChannels = inputBlock.getNumChannels();
  1263. mixer.pushDrySamples (inputBlock);
  1264. for (size_t channel = 0; channel < numChannels; ++channel)
  1265. {
  1266. auto* samplesIn = inputBlock .getChannelPointer (channel);
  1267. auto* samplesOut = outputBlock.getChannelPointer (channel);
  1268. for (size_t i = 0; i < numSamples; ++i)
  1269. {
  1270. auto input = samplesIn[i] - lastDelayEffectOutput[channel];
  1271. auto delay = smoothFilter.processSample (int (channel), delayEffectValue[channel]);
  1272. const auto output = [&]
  1273. {
  1274. switch (delayEffectType)
  1275. {
  1276. case 0:
  1277. noInterpolation.pushSample (int (channel), input);
  1278. noInterpolation.setDelay ((float) delay);
  1279. return noInterpolation.popSample (int (channel));
  1280. case 1:
  1281. linear.pushSample (int (channel), input);
  1282. linear.setDelay ((float) delay);
  1283. return linear.popSample (int (channel));
  1284. case 2:
  1285. lagrange.pushSample (int (channel), input);
  1286. lagrange.setDelay ((float) delay);
  1287. return lagrange.popSample (int (channel));
  1288. case 3:
  1289. thiran.pushSample (int (channel), input);
  1290. thiran.setDelay ((float) delay);
  1291. return thiran.popSample (int (channel));
  1292. default:
  1293. break;
  1294. }
  1295. jassertfalse;
  1296. return 0.0f;
  1297. }();
  1298. const auto processed = lowpass.processSample (int (channel), output);
  1299. samplesOut[i] = processed;
  1300. lastDelayEffectOutput[channel] = processed * delayFeedbackVolume[channel].getNextValue();
  1301. }
  1302. }
  1303. mixer.mixWetSamples (outputBlock);
  1304. }
  1305. static constexpr auto effectDelaySamples = 192000;
  1306. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::None> noInterpolation { effectDelaySamples };
  1307. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Linear> linear { effectDelaySamples };
  1308. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Lagrange3rd> lagrange { effectDelaySamples };
  1309. dsp::DelayLine<float, dsp::DelayLineInterpolationTypes::Thiran> thiran { effectDelaySamples };
  1310. // Double precision to avoid some approximation issues
  1311. dsp::FirstOrderTPTFilter<double> smoothFilter;
  1312. std::array<double, 2> delayEffectValue;
  1313. std::array<LinearSmoothedValue<float>, 2> delayFeedbackVolume;
  1314. dsp::FirstOrderTPTFilter<float> lowpass;
  1315. dsp::DryWetMixer<float> mixer;
  1316. std::array<float, 2> lastDelayEffectOutput;
  1317. int delayEffectType = 1;
  1318. };
  1319. ParameterReferences parameters;
  1320. AudioProcessorValueTreeState apvts;
  1321. using Chain = dsp::ProcessorChain<dsp::NoiseGate<float>,
  1322. dsp::Gain<float>,
  1323. DirectDelayProcessor,
  1324. MultiBandProcessor,
  1325. dsp::Compressor<float>,
  1326. dsp::Phaser<float>,
  1327. dsp::Chorus<float>,
  1328. DistortionProcessor,
  1329. dsp::LadderFilter<float>,
  1330. DelayEffectProcessor,
  1331. ConvolutionProcessor,
  1332. dsp::Limiter<float>,
  1333. dsp::Gain<float>,
  1334. dsp::Panner<float>>;
  1335. Chain chain;
  1336. // We use this enum to index into the chain above
  1337. enum ProcessorIndices
  1338. {
  1339. noiseGateIndex,
  1340. inputGainIndex,
  1341. directDelayIndex,
  1342. multiBandIndex,
  1343. compressorIndex,
  1344. phaserIndex,
  1345. chorusIndex,
  1346. distortionIndex,
  1347. ladderIndex,
  1348. delayEffectIndex,
  1349. convolutionIndex,
  1350. limiterIndex,
  1351. outputGainIndex,
  1352. pannerIndex
  1353. };
  1354. //==============================================================================
  1355. std::atomic<bool> requiresUpdate { true };
  1356. std::atomic<int> irSize { 0 };
  1357. //==============================================================================
  1358. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemo)
  1359. };
  1360. //==============================================================================
  1361. class DspModulePluginDemoEditor : public AudioProcessorEditor
  1362. {
  1363. public:
  1364. explicit DspModulePluginDemoEditor (DspModulePluginDemo& p)
  1365. : AudioProcessorEditor (&p),
  1366. proc (p)
  1367. {
  1368. comboEffect.addSectionHeading ("Main");
  1369. comboEffect.addItem ("Distortion", TabDistortion);
  1370. comboEffect.addItem ("Convolution", TabConvolution);
  1371. comboEffect.addItem ("Multi-band", TabMultiBand);
  1372. comboEffect.addSectionHeading ("Dynamics");
  1373. comboEffect.addItem ("Compressor", TabCompressor);
  1374. comboEffect.addItem ("Noise gate", TabNoiseGate);
  1375. comboEffect.addItem ("Limiter", TabLimiter);
  1376. comboEffect.addSectionHeading ("Delay");
  1377. comboEffect.addItem ("Delay line direct", TabDelayLineDirect);
  1378. comboEffect.addItem ("Delay line effect", TabDelayLineEffect);
  1379. comboEffect.addSectionHeading ("Others");
  1380. comboEffect.addItem ("Phaser", TabPhaser);
  1381. comboEffect.addItem ("Chorus", TabChorus);
  1382. comboEffect.addItem ("Ladder filter", TabLadder);
  1383. comboEffect.setSelectedId (proc.indexTab + 1, dontSendNotification);
  1384. comboEffect.onChange = [this]
  1385. {
  1386. proc.indexTab = comboEffect.getSelectedId() - 1;
  1387. updateVisibility();
  1388. };
  1389. addAllAndMakeVisible (*this,
  1390. comboEffect,
  1391. labelEffect,
  1392. basicControls,
  1393. distortionControls,
  1394. convolutionControls,
  1395. multibandControls,
  1396. compressorControls,
  1397. noiseGateControls,
  1398. limiterControls,
  1399. directDelayControls,
  1400. delayEffectControls,
  1401. phaserControls,
  1402. chorusControls,
  1403. ladderControls);
  1404. labelEffect.setJustificationType (Justification::centredRight);
  1405. labelEffect.attachToComponent (&comboEffect, true);
  1406. updateVisibility();
  1407. setSize (800, 430);
  1408. setResizable (false, false);
  1409. }
  1410. //==============================================================================
  1411. void paint (Graphics& g) override
  1412. {
  1413. auto rect = getLocalBounds();
  1414. auto rectTop = rect.removeFromTop (topSize);
  1415. auto rectBottom = rect.removeFromBottom (bottomSize);
  1416. auto rectEffects = rect.removeFromBottom (tabSize);
  1417. auto rectChoice = rect.removeFromBottom (midSize);
  1418. g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
  1419. g.fillRect (rect);
  1420. g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId).brighter (0.2f));
  1421. g.fillRect (rectEffects);
  1422. g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker (0.2f));
  1423. g.fillRect (rectTop);
  1424. g.fillRect (rectBottom);
  1425. g.fillRect (rectChoice);
  1426. g.setColour (Colours::white);
  1427. g.setFont (Font (20.0f).italicised().withExtraKerningFactor (0.1f));
  1428. g.drawFittedText ("DSP MODULE DEMO", rectTop.reduced (10, 0), Justification::centredLeft, 1);
  1429. g.setFont (Font (14.0f));
  1430. String strText = "IR length (reverb): " + String (proc.getCurrentIRSize()) + " samples";
  1431. g.drawFittedText (strText, rectBottom.reduced (10, 0), Justification::centredRight, 1);
  1432. }
  1433. void resized() override
  1434. {
  1435. auto rect = getLocalBounds();
  1436. rect.removeFromTop (topSize);
  1437. rect.removeFromBottom (bottomSize);
  1438. auto rectEffects = rect.removeFromBottom (tabSize);
  1439. auto rectChoice = rect.removeFromBottom (midSize);
  1440. comboEffect.setBounds (rectChoice.withSizeKeepingCentre (200, 24));
  1441. rect.reduce (80, 0);
  1442. rectEffects.reduce (20, 0);
  1443. basicControls.setBounds (rect);
  1444. forEach ([&] (Component& comp) { comp.setBounds (rectEffects); },
  1445. distortionControls,
  1446. convolutionControls,
  1447. multibandControls,
  1448. compressorControls,
  1449. noiseGateControls,
  1450. limiterControls,
  1451. directDelayControls,
  1452. delayEffectControls,
  1453. phaserControls,
  1454. chorusControls,
  1455. ladderControls);
  1456. }
  1457. private:
  1458. class ComponentWithParamMenu : public Component
  1459. {
  1460. public:
  1461. ComponentWithParamMenu (AudioProcessorEditor& editorIn, RangedAudioParameter& paramIn)
  1462. : editor (editorIn), param (paramIn) {}
  1463. void mouseUp (const MouseEvent& e) override
  1464. {
  1465. if (e.mods.isRightButtonDown())
  1466. if (auto* c = editor.getHostContext())
  1467. if (auto menuInfo = c->getContextMenuForParameterIndex (&param))
  1468. menuInfo->getEquivalentPopupMenu().showMenuAsync (PopupMenu::Options{}.withTargetComponent (this)
  1469. .withMousePosition());
  1470. }
  1471. private:
  1472. AudioProcessorEditor& editor;
  1473. RangedAudioParameter& param;
  1474. };
  1475. class AttachedSlider : public ComponentWithParamMenu
  1476. {
  1477. public:
  1478. AttachedSlider (AudioProcessorEditor& editorIn, RangedAudioParameter& paramIn)
  1479. : ComponentWithParamMenu (editorIn, paramIn),
  1480. label ("", paramIn.name),
  1481. attachment (paramIn, slider)
  1482. {
  1483. slider.addMouseListener (this, true);
  1484. addAllAndMakeVisible (*this, slider, label);
  1485. slider.setTextValueSuffix (" " + paramIn.label);
  1486. label.attachToComponent (&slider, false);
  1487. label.setJustificationType (Justification::centred);
  1488. }
  1489. void resized() override { slider.setBounds (getLocalBounds().reduced (0, 40)); }
  1490. private:
  1491. Slider slider { Slider::RotaryVerticalDrag, Slider::TextBoxBelow };
  1492. Label label;
  1493. SliderParameterAttachment attachment;
  1494. };
  1495. class AttachedToggle : public ComponentWithParamMenu
  1496. {
  1497. public:
  1498. AttachedToggle (AudioProcessorEditor& editorIn, RangedAudioParameter& paramIn)
  1499. : ComponentWithParamMenu (editorIn, paramIn),
  1500. toggle (paramIn.name),
  1501. attachment (paramIn, toggle)
  1502. {
  1503. toggle.addMouseListener (this, true);
  1504. addAndMakeVisible (toggle);
  1505. }
  1506. void resized() override { toggle.setBounds (getLocalBounds()); }
  1507. private:
  1508. ToggleButton toggle;
  1509. ButtonParameterAttachment attachment;
  1510. };
  1511. class AttachedCombo : public ComponentWithParamMenu
  1512. {
  1513. public:
  1514. AttachedCombo (AudioProcessorEditor& editorIn, RangedAudioParameter& paramIn)
  1515. : ComponentWithParamMenu (editorIn, paramIn),
  1516. combo (paramIn),
  1517. label ("", paramIn.name),
  1518. attachment (paramIn, combo)
  1519. {
  1520. combo.addMouseListener (this, true);
  1521. addAllAndMakeVisible (*this, combo, label);
  1522. label.attachToComponent (&combo, false);
  1523. label.setJustificationType (Justification::centred);
  1524. }
  1525. void resized() override
  1526. {
  1527. combo.setBounds (getLocalBounds().withSizeKeepingCentre (jmin (getWidth(), 150), 24));
  1528. }
  1529. private:
  1530. struct ComboWithItems : public ComboBox
  1531. {
  1532. explicit ComboWithItems (RangedAudioParameter& param)
  1533. {
  1534. // Adding the list here in the constructor means that the combo
  1535. // is already populated when we construct the attachment below
  1536. addItemList (dynamic_cast<AudioParameterChoice&> (param).choices, 1);
  1537. }
  1538. };
  1539. ComboWithItems combo;
  1540. Label label;
  1541. ComboBoxParameterAttachment attachment;
  1542. };
  1543. //==============================================================================
  1544. void updateVisibility()
  1545. {
  1546. const auto indexEffect = comboEffect.getSelectedId();
  1547. const auto op = [&] (const std::tuple<Component&, int>& tup)
  1548. {
  1549. Component& comp = std::get<0> (tup);
  1550. const int tabIndex = std::get<1> (tup);
  1551. comp.setVisible (tabIndex == indexEffect);
  1552. };
  1553. forEach (op,
  1554. std::forward_as_tuple (distortionControls, TabDistortion),
  1555. std::forward_as_tuple (convolutionControls, TabConvolution),
  1556. std::forward_as_tuple (multibandControls, TabMultiBand),
  1557. std::forward_as_tuple (compressorControls, TabCompressor),
  1558. std::forward_as_tuple (noiseGateControls, TabNoiseGate),
  1559. std::forward_as_tuple (limiterControls, TabLimiter),
  1560. std::forward_as_tuple (directDelayControls, TabDelayLineDirect),
  1561. std::forward_as_tuple (delayEffectControls, TabDelayLineEffect),
  1562. std::forward_as_tuple (phaserControls, TabPhaser),
  1563. std::forward_as_tuple (chorusControls, TabChorus),
  1564. std::forward_as_tuple (ladderControls, TabLadder));
  1565. }
  1566. enum EffectsTabs
  1567. {
  1568. TabDistortion = 1,
  1569. TabConvolution,
  1570. TabMultiBand,
  1571. TabCompressor,
  1572. TabNoiseGate,
  1573. TabLimiter,
  1574. TabDelayLineDirect,
  1575. TabDelayLineEffect,
  1576. TabPhaser,
  1577. TabChorus,
  1578. TabLadder
  1579. };
  1580. //==============================================================================
  1581. ComboBox comboEffect;
  1582. Label labelEffect { {}, "Audio effect: " };
  1583. struct GetTrackInfo
  1584. {
  1585. // Combo boxes need a lot of room
  1586. Grid::TrackInfo operator() (AttachedCombo&) const { return 120_px; }
  1587. // Toggles are a bit smaller
  1588. Grid::TrackInfo operator() (AttachedToggle&) const { return 80_px; }
  1589. // Sliders take up as much room as they can
  1590. Grid::TrackInfo operator() (AttachedSlider&) const { return 1_fr; }
  1591. };
  1592. template <typename... Components>
  1593. static void performLayout (const Rectangle<int>& bounds, Components&... components)
  1594. {
  1595. Grid grid;
  1596. using Track = Grid::TrackInfo;
  1597. grid.autoColumns = Track (1_fr);
  1598. grid.autoRows = Track (1_fr);
  1599. grid.columnGap = Grid::Px (10);
  1600. grid.rowGap = Grid::Px (0);
  1601. grid.autoFlow = Grid::AutoFlow::column;
  1602. grid.templateColumns = { GetTrackInfo{} (components)... };
  1603. grid.items = { GridItem (components)... };
  1604. grid.performLayout (bounds);
  1605. }
  1606. struct BasicControls : public Component
  1607. {
  1608. explicit BasicControls (AudioProcessorEditor& editor,
  1609. const DspModulePluginDemo::ParameterReferences::MainGroup& state)
  1610. : pan (editor, state.pan),
  1611. input (editor, state.inputGain),
  1612. output (editor, state.outputGain)
  1613. {
  1614. addAllAndMakeVisible (*this, pan, input, output);
  1615. }
  1616. void resized() override
  1617. {
  1618. performLayout (getLocalBounds(), input, output, pan);
  1619. }
  1620. AttachedSlider pan, input, output;
  1621. };
  1622. struct DistortionControls : public Component
  1623. {
  1624. explicit DistortionControls (AudioProcessorEditor& editor,
  1625. const DspModulePluginDemo::ParameterReferences::DistortionGroup& state)
  1626. : toggle (editor, state.enabled),
  1627. lowpass (editor, state.lowpass),
  1628. highpass (editor, state.highpass),
  1629. mix (editor, state.mix),
  1630. gain (editor, state.inGain),
  1631. compv (editor, state.compGain),
  1632. type (editor, state.type),
  1633. oversampling (editor, state.oversampler)
  1634. {
  1635. addAllAndMakeVisible (*this, toggle, type, lowpass, highpass, mix, gain, compv, oversampling);
  1636. }
  1637. void resized() override
  1638. {
  1639. performLayout (getLocalBounds(), toggle, type, gain, highpass, lowpass, compv, mix, oversampling);
  1640. }
  1641. AttachedToggle toggle;
  1642. AttachedSlider lowpass, highpass, mix, gain, compv;
  1643. AttachedCombo type, oversampling;
  1644. };
  1645. struct ConvolutionControls : public Component
  1646. {
  1647. explicit ConvolutionControls (AudioProcessorEditor& editor,
  1648. const DspModulePluginDemo::ParameterReferences::ConvolutionGroup& state)
  1649. : cab (editor, state.cabEnabled),
  1650. reverb (editor, state.reverbEnabled),
  1651. mix (editor, state.reverbMix)
  1652. {
  1653. addAllAndMakeVisible (*this, cab, reverb, mix);
  1654. }
  1655. void resized() override
  1656. {
  1657. performLayout (getLocalBounds(), cab, reverb, mix);
  1658. }
  1659. AttachedToggle cab, reverb;
  1660. AttachedSlider mix;
  1661. };
  1662. struct MultiBandControls : public Component
  1663. {
  1664. explicit MultiBandControls (AudioProcessorEditor& editor,
  1665. const DspModulePluginDemo::ParameterReferences::MultiBandGroup& state)
  1666. : toggle (editor, state.enabled),
  1667. low (editor, state.lowVolume),
  1668. high (editor, state.highVolume),
  1669. lRFreq (editor, state.freq)
  1670. {
  1671. addAllAndMakeVisible (*this, toggle, low, high, lRFreq);
  1672. }
  1673. void resized() override
  1674. {
  1675. performLayout (getLocalBounds(), toggle, lRFreq, low, high);
  1676. }
  1677. AttachedToggle toggle;
  1678. AttachedSlider low, high, lRFreq;
  1679. };
  1680. struct CompressorControls : public Component
  1681. {
  1682. explicit CompressorControls (AudioProcessorEditor& editor,
  1683. const DspModulePluginDemo::ParameterReferences::CompressorGroup& state)
  1684. : toggle (editor, state.enabled),
  1685. threshold (editor, state.threshold),
  1686. ratio (editor, state.ratio),
  1687. attack (editor, state.attack),
  1688. release (editor, state.release)
  1689. {
  1690. addAllAndMakeVisible (*this, toggle, threshold, ratio, attack, release);
  1691. }
  1692. void resized() override
  1693. {
  1694. performLayout (getLocalBounds(), toggle, threshold, ratio, attack, release);
  1695. }
  1696. AttachedToggle toggle;
  1697. AttachedSlider threshold, ratio, attack, release;
  1698. };
  1699. struct NoiseGateControls : public Component
  1700. {
  1701. explicit NoiseGateControls (AudioProcessorEditor& editor,
  1702. const DspModulePluginDemo::ParameterReferences::NoiseGateGroup& state)
  1703. : toggle (editor, state.enabled),
  1704. threshold (editor, state.threshold),
  1705. ratio (editor, state.ratio),
  1706. attack (editor, state.attack),
  1707. release (editor, state.release)
  1708. {
  1709. addAllAndMakeVisible (*this, toggle, threshold, ratio, attack, release);
  1710. }
  1711. void resized() override
  1712. {
  1713. performLayout (getLocalBounds(), toggle, threshold, ratio, attack, release);
  1714. }
  1715. AttachedToggle toggle;
  1716. AttachedSlider threshold, ratio, attack, release;
  1717. };
  1718. struct LimiterControls : public Component
  1719. {
  1720. explicit LimiterControls (AudioProcessorEditor& editor,
  1721. const DspModulePluginDemo::ParameterReferences::LimiterGroup& state)
  1722. : toggle (editor, state.enabled),
  1723. threshold (editor, state.threshold),
  1724. release (editor, state.release)
  1725. {
  1726. addAllAndMakeVisible (*this, toggle, threshold, release);
  1727. }
  1728. void resized() override
  1729. {
  1730. performLayout (getLocalBounds(), toggle, threshold, release);
  1731. }
  1732. AttachedToggle toggle;
  1733. AttachedSlider threshold, release;
  1734. };
  1735. struct DirectDelayControls : public Component
  1736. {
  1737. explicit DirectDelayControls (AudioProcessorEditor& editor,
  1738. const DspModulePluginDemo::ParameterReferences::DirectDelayGroup& state)
  1739. : toggle (editor, state.enabled),
  1740. type (editor, state.type),
  1741. delay (editor, state.value),
  1742. smooth (editor, state.smoothing),
  1743. mix (editor, state.mix)
  1744. {
  1745. addAllAndMakeVisible (*this, toggle, type, delay, smooth, mix);
  1746. }
  1747. void resized() override
  1748. {
  1749. performLayout (getLocalBounds(), toggle, type, delay, smooth, mix);
  1750. }
  1751. AttachedToggle toggle;
  1752. AttachedCombo type;
  1753. AttachedSlider delay, smooth, mix;
  1754. };
  1755. struct DelayEffectControls : public Component
  1756. {
  1757. explicit DelayEffectControls (AudioProcessorEditor& editor,
  1758. const DspModulePluginDemo::ParameterReferences::DelayEffectGroup& state)
  1759. : toggle (editor, state.enabled),
  1760. type (editor, state.type),
  1761. value (editor, state.value),
  1762. smooth (editor, state.smoothing),
  1763. lowpass (editor, state.lowpass),
  1764. feedback (editor, state.feedback),
  1765. mix (editor, state.mix)
  1766. {
  1767. addAllAndMakeVisible (*this, toggle, type, value, smooth, lowpass, feedback, mix);
  1768. }
  1769. void resized() override
  1770. {
  1771. performLayout (getLocalBounds(), toggle, type, value, smooth, lowpass, feedback, mix);
  1772. }
  1773. AttachedToggle toggle;
  1774. AttachedCombo type;
  1775. AttachedSlider value, smooth, lowpass, feedback, mix;
  1776. };
  1777. struct PhaserControls : public Component
  1778. {
  1779. explicit PhaserControls (AudioProcessorEditor& editor,
  1780. const DspModulePluginDemo::ParameterReferences::PhaserGroup& state)
  1781. : toggle (editor, state.enabled),
  1782. rate (editor, state.rate),
  1783. depth (editor, state.depth),
  1784. centre (editor, state.centreFrequency),
  1785. feedback (editor, state.feedback),
  1786. mix (editor, state.mix)
  1787. {
  1788. addAllAndMakeVisible (*this, toggle, rate, depth, centre, feedback, mix);
  1789. }
  1790. void resized() override
  1791. {
  1792. performLayout (getLocalBounds(), toggle, rate, depth, centre, feedback, mix);
  1793. }
  1794. AttachedToggle toggle;
  1795. AttachedSlider rate, depth, centre, feedback, mix;
  1796. };
  1797. struct ChorusControls : public Component
  1798. {
  1799. explicit ChorusControls (AudioProcessorEditor& editor,
  1800. const DspModulePluginDemo::ParameterReferences::ChorusGroup& state)
  1801. : toggle (editor, state.enabled),
  1802. rate (editor, state.rate),
  1803. depth (editor, state.depth),
  1804. centre (editor, state.centreDelay),
  1805. feedback (editor, state.feedback),
  1806. mix (editor, state.mix)
  1807. {
  1808. addAllAndMakeVisible (*this, toggle, rate, depth, centre, feedback, mix);
  1809. }
  1810. void resized() override
  1811. {
  1812. performLayout (getLocalBounds(), toggle, rate, depth, centre, feedback, mix);
  1813. }
  1814. AttachedToggle toggle;
  1815. AttachedSlider rate, depth, centre, feedback, mix;
  1816. };
  1817. struct LadderControls : public Component
  1818. {
  1819. explicit LadderControls (AudioProcessorEditor& editor,
  1820. const DspModulePluginDemo::ParameterReferences::LadderGroup& state)
  1821. : toggle (editor, state.enabled),
  1822. mode (editor, state.mode),
  1823. freq (editor, state.cutoff),
  1824. resonance (editor, state.resonance),
  1825. drive (editor, state.drive)
  1826. {
  1827. addAllAndMakeVisible (*this, toggle, mode, freq, resonance, drive);
  1828. }
  1829. void resized() override
  1830. {
  1831. performLayout (getLocalBounds(), toggle, mode, freq, resonance, drive);
  1832. }
  1833. AttachedToggle toggle;
  1834. AttachedCombo mode;
  1835. AttachedSlider freq, resonance, drive;
  1836. };
  1837. //==============================================================================
  1838. static constexpr auto topSize = 40,
  1839. bottomSize = 40,
  1840. midSize = 40,
  1841. tabSize = 155;
  1842. //==============================================================================
  1843. DspModulePluginDemo& proc;
  1844. BasicControls basicControls { *this, proc.getParameterValues().main };
  1845. DistortionControls distortionControls { *this, proc.getParameterValues().distortion };
  1846. ConvolutionControls convolutionControls { *this, proc.getParameterValues().convolution };
  1847. MultiBandControls multibandControls { *this, proc.getParameterValues().multiBand };
  1848. CompressorControls compressorControls { *this, proc.getParameterValues().compressor };
  1849. NoiseGateControls noiseGateControls { *this, proc.getParameterValues().noiseGate };
  1850. LimiterControls limiterControls { *this, proc.getParameterValues().limiter };
  1851. DirectDelayControls directDelayControls { *this, proc.getParameterValues().directDelay };
  1852. DelayEffectControls delayEffectControls { *this, proc.getParameterValues().delayEffect };
  1853. PhaserControls phaserControls { *this, proc.getParameterValues().phaser };
  1854. ChorusControls chorusControls { *this, proc.getParameterValues().chorus };
  1855. LadderControls ladderControls { *this, proc.getParameterValues().ladder };
  1856. //==============================================================================
  1857. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DspModulePluginDemoEditor)
  1858. };
  1859. struct DspModulePluginDemoAudioProcessor : public DspModulePluginDemo
  1860. {
  1861. AudioProcessorEditor* createEditor() override
  1862. {
  1863. return new DspModulePluginDemoEditor (*this);
  1864. }
  1865. bool hasEditor() const override { return true; }
  1866. };