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.

673 lines
22KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include <juce_audio_processors/juce_audio_processors.h>
  18. #define createPlugin createStaticPlugin
  19. #include "src/DistrhoPluginInternal.hpp"
  20. #include "src/DistrhoUIInternal.hpp"
  21. START_NAMESPACE_DISTRHO
  22. // --------------------------------------------------------------------------------------------------------------------
  23. class ParameterFromDPF : public juce::AudioProcessorParameter
  24. {
  25. PluginExporter& plugin;
  26. const ParameterEnumerationValues& enumValues;
  27. const ParameterRanges& ranges;
  28. const uint32_t hints;
  29. const uint index;
  30. bool* const updatedPtr;
  31. mutable juce::StringArray dpfValueStrings;
  32. public:
  33. ParameterFromDPF(PluginExporter& plugin_, const uint index_, bool* const updatedPtr_)
  34. : plugin(plugin_),
  35. enumValues(plugin_.getParameterEnumValues(index_)),
  36. ranges(plugin_.getParameterRanges(index_)),
  37. hints(plugin_.getParameterHints(index_)),
  38. index(index_),
  39. updatedPtr(updatedPtr_) {}
  40. void setValueNotifyingHostFromDPF(const float newValue)
  41. {
  42. setValueNotifyingHost(ranges.getNormalizedValue(newValue));
  43. *updatedPtr = false;
  44. }
  45. protected:
  46. float getValue() const override
  47. {
  48. return ranges.getNormalizedValue(plugin.getParameterValue(index));
  49. }
  50. void setValue(const float newValue) override
  51. {
  52. *updatedPtr = true;
  53. plugin.setParameterValue(index, ranges.getUnnormalizedValue(newValue));
  54. }
  55. float getDefaultValue() const override
  56. {
  57. return ranges.getNormalizedValue(plugin.getParameterDefault(index));
  58. }
  59. juce::String getName(const int maximumStringLength) const override
  60. {
  61. DISTRHO_SAFE_ASSERT_RETURN(maximumStringLength > 0, {});
  62. return juce::String(plugin.getParameterName(index).buffer(), static_cast<size_t>(maximumStringLength));
  63. }
  64. juce::String getLabel() const override
  65. {
  66. return plugin.getParameterUnit(index).buffer();
  67. }
  68. int getNumSteps() const override
  69. {
  70. if (hints & kParameterIsBoolean)
  71. return 2;
  72. if (enumValues.restrictedMode)
  73. return enumValues.count;
  74. if (hints & kParameterIsInteger)
  75. return ranges.max - ranges.min;
  76. return juce::AudioProcessorParameter::getNumSteps();
  77. }
  78. bool isDiscrete() const override
  79. {
  80. if (hints & (kParameterIsBoolean|kParameterIsInteger))
  81. return true;
  82. if (enumValues.restrictedMode)
  83. return true;
  84. return false;
  85. }
  86. bool isBoolean() const override
  87. {
  88. return (hints & kParameterIsBoolean) != 0x0;
  89. }
  90. juce::String getText(const float normalizedValue, const int maximumStringLength) const override
  91. {
  92. DISTRHO_SAFE_ASSERT_RETURN(maximumStringLength > 0, {});
  93. float value = ranges.getUnnormalizedValue(normalizedValue);
  94. if (hints & kParameterIsBoolean)
  95. {
  96. const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
  97. value = value > midRange ? ranges.max : ranges.min;
  98. }
  99. else if (hints & kParameterIsInteger)
  100. {
  101. value = std::round(value);
  102. }
  103. if (enumValues.restrictedMode)
  104. {
  105. for (uint32_t i=0; i < enumValues.count; ++i)
  106. {
  107. if (d_isEqual(enumValues.values[i].value, value))
  108. return juce::String(enumValues.values[i].label, static_cast<size_t>(maximumStringLength));
  109. }
  110. }
  111. juce::String text;
  112. if (hints & kParameterIsInteger)
  113. text = juce::String(static_cast<int>(value));
  114. else
  115. text = juce::String(value);
  116. return juce::String(text.toRawUTF8(), static_cast<size_t>(maximumStringLength));
  117. }
  118. float getValueForText(const juce::String& text) const override
  119. {
  120. if (enumValues.restrictedMode)
  121. {
  122. for (uint32_t i=0; i < enumValues.count; ++i)
  123. {
  124. if (text == enumValues.values[i].label.buffer())
  125. return ranges.getNormalizedValue(enumValues.values[i].value);
  126. }
  127. }
  128. float value;
  129. if (hints & kParameterIsInteger)
  130. value = std::atoi(text.toRawUTF8());
  131. else
  132. value = std::atof(text.toRawUTF8());
  133. return ranges.getFixedAndNormalizedValue(value);
  134. }
  135. bool isAutomatable() const override
  136. {
  137. return (hints & kParameterIsAutomatable) != 0x0;
  138. }
  139. juce::String getCurrentValueAsText() const override
  140. {
  141. const float value = plugin.getParameterValue(index);
  142. if (enumValues.restrictedMode)
  143. {
  144. for (uint32_t i=0; i < enumValues.count; ++i)
  145. {
  146. if (d_isEqual(enumValues.values[i].value, value))
  147. return juce::String(enumValues.values[i].label);
  148. }
  149. }
  150. if (hints & kParameterIsInteger)
  151. return juce::String(static_cast<int>(value));
  152. return juce::String(value);
  153. }
  154. juce::StringArray getAllValueStrings() const override
  155. {
  156. if (dpfValueStrings.size() != 0)
  157. return dpfValueStrings;
  158. if (enumValues.restrictedMode)
  159. {
  160. for (uint32_t i=0; i < enumValues.count; ++i)
  161. dpfValueStrings.add(enumValues.values[i].label.buffer());
  162. return dpfValueStrings;
  163. }
  164. if (hints & kParameterIsBoolean)
  165. {
  166. if (hints & kParameterIsInteger)
  167. {
  168. dpfValueStrings.add(juce::String(static_cast<int>(ranges.min)));
  169. dpfValueStrings.add(juce::String(static_cast<int>(ranges.max)));
  170. }
  171. else
  172. {
  173. dpfValueStrings.add(juce::String(ranges.min));
  174. dpfValueStrings.add(juce::String(ranges.max));
  175. }
  176. }
  177. else if (hints & kParameterIsInteger)
  178. {
  179. const int imin = static_cast<int>(ranges.min);
  180. const int imax = static_cast<int>(ranges.max);
  181. for (int i=imin; i<=imax; ++i)
  182. dpfValueStrings.add(juce::String(i));
  183. }
  184. return dpfValueStrings;
  185. }
  186. };
  187. // --------------------------------------------------------------------------------------------------------------------
  188. // unused in cardinal
  189. static constexpr const requestParameterValueChangeFunc nullRequestParameterValueChangeFunc = nullptr;
  190. // only needed for headless builds, which this wrapper never builds for
  191. static constexpr const updateStateValueFunc nullUpdateStateValueFunc = nullptr;
  192. // DSP/processor implementation
  193. class CardinalWrapperProcessor : public juce::AudioProcessor
  194. {
  195. friend class CardinalWrapperEditor;
  196. PluginExporter plugin;
  197. MidiEvent midiEvents[kMaxMidiEvents];
  198. TimePosition timePosition;
  199. const uint32_t parameterCount;
  200. juce::AudioProcessorParameter* bypassParameter;
  201. juce::MidiBuffer* currentMidiMessages;
  202. bool* updatedParameters;
  203. public:
  204. CardinalWrapperProcessor()
  205. : plugin(this, writeMidiFunc, nullRequestParameterValueChangeFunc, nullUpdateStateValueFunc),
  206. parameterCount(plugin.getParameterCount()),
  207. bypassParameter(nullptr),
  208. currentMidiMessages(nullptr),
  209. updatedParameters(nullptr)
  210. {
  211. if (const double sampleRate = getSampleRate())
  212. plugin.setSampleRate(sampleRate);
  213. if (const int samplesPerBlock = getBlockSize())
  214. if (samplesPerBlock > 0)
  215. plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));
  216. getBypassParameter();
  217. if (parameterCount != 0)
  218. {
  219. updatedParameters = new bool[parameterCount];
  220. std::memset(updatedParameters, 0, sizeof(bool)*parameterCount);
  221. for (uint i=0; i<parameterCount; ++i)
  222. {
  223. ParameterFromDPF* const param = new ParameterFromDPF(plugin, i, updatedParameters + i);
  224. addParameter(param);
  225. if (plugin.getParameterDesignation(i) == kParameterDesignationBypass)
  226. bypassParameter = param;
  227. }
  228. }
  229. }
  230. ~CardinalWrapperProcessor() override
  231. {
  232. delete[] updatedParameters;
  233. }
  234. protected:
  235. const juce::String getName() const override
  236. {
  237. return plugin.getName();
  238. }
  239. juce::StringArray getAlternateDisplayNames() const override
  240. {
  241. return juce::StringArray(plugin.getLabel());
  242. }
  243. void prepareToPlay(const double sampleRate, const int samplesPerBlock) override
  244. {
  245. DISTRHO_SAFE_ASSERT_RETURN(samplesPerBlock > 0,);
  246. plugin.deactivateIfNeeded();
  247. plugin.setSampleRate(sampleRate);
  248. plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));
  249. plugin.activate();
  250. }
  251. void releaseResources() override
  252. {
  253. plugin.deactivateIfNeeded();
  254. }
  255. void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override
  256. {
  257. const int numSamples = buffer.getNumSamples();
  258. DISTRHO_SAFE_ASSERT_INT_RETURN(numSamples > 0, numSamples, midiMessages.clear());
  259. uint32_t midiEventCount = 0;
  260. for (const juce::MidiMessageMetadata midiMessage : midiMessages)
  261. {
  262. DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.numBytes > 0);
  263. DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.samplePosition >= 0);
  264. if (midiMessage.numBytes > static_cast<int>(MidiEvent::kDataSize))
  265. continue;
  266. MidiEvent& midiEvent(midiEvents[midiEventCount++]);
  267. midiEvent.frame = static_cast<uint32_t>(midiMessage.samplePosition);
  268. midiEvent.size = (static_cast<uint8_t>(midiMessage.numBytes));
  269. std::memcpy(midiEvent.data, midiMessage.data, midiEvent.size);
  270. if (midiEventCount == kMaxMidiEvents)
  271. break;
  272. }
  273. midiMessages.clear();
  274. const juce::ScopedValueSetter<juce::MidiBuffer*> cvs(currentMidiMessages, &midiMessages, nullptr);
  275. juce::AudioPlayHead* const playhead = getPlayHead();
  276. juce::AudioPlayHead::CurrentPositionInfo posInfo;
  277. if (playhead != nullptr && playhead->getCurrentPosition(posInfo))
  278. {
  279. timePosition.playing = posInfo.isPlaying;
  280. timePosition.bbt.valid = true;
  281. // ticksPerBeat is not possible with JUCE
  282. timePosition.bbt.ticksPerBeat = 1920.0;
  283. if (posInfo.timeInSamples >= 0)
  284. timePosition.frame = static_cast<uint64_t>(posInfo.timeInSamples);
  285. else
  286. timePosition.frame = 0;
  287. timePosition.bbt.beatsPerMinute = posInfo.bpm;
  288. const double ppqPos = std::abs(posInfo.ppqPosition);
  289. const int ppqPerBar = posInfo.timeSigNumerator * 4 / posInfo.timeSigDenominator;
  290. const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * posInfo.timeSigNumerator;
  291. const double rest = std::fmod(barBeats, 1.0);
  292. timePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
  293. timePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
  294. timePosition.bbt.tick = rest * timePosition.bbt.ticksPerBeat;
  295. timePosition.bbt.beatsPerBar = posInfo.timeSigNumerator;
  296. timePosition.bbt.beatType = posInfo.timeSigDenominator;
  297. if (posInfo.ppqPosition < 0.0)
  298. {
  299. --timePosition.bbt.bar;
  300. timePosition.bbt.beat = posInfo.timeSigNumerator - timePosition.bbt.beat + 1;
  301. timePosition.bbt.tick = timePosition.bbt.ticksPerBeat - timePosition.bbt.tick - 1;
  302. }
  303. timePosition.bbt.barStartTick = timePosition.bbt.ticksPerBeat*
  304. timePosition.bbt.beatsPerBar*
  305. (timePosition.bbt.bar-1);
  306. }
  307. else
  308. {
  309. timePosition.frame = 0;
  310. timePosition.playing = false;
  311. timePosition.bbt.valid = false;
  312. }
  313. plugin.setTimePosition(timePosition);
  314. DISTRHO_SAFE_ASSERT_RETURN(buffer.getNumChannels() == 2,);
  315. const float* audioBufferIn[2];
  316. float* audioBufferOut[2];
  317. audioBufferIn[0] = buffer.getReadPointer(0);
  318. audioBufferIn[1] = buffer.getReadPointer(1);
  319. audioBufferOut[0] = buffer.getWritePointer(0);
  320. audioBufferOut[1] = buffer.getWritePointer(1);
  321. plugin.run(audioBufferIn, audioBufferOut, static_cast<uint32_t>(numSamples), midiEvents, midiEventCount);
  322. }
  323. // fix compiler warning
  324. void processBlock(juce::AudioBuffer<double>&, juce::MidiBuffer&) override {}
  325. double getTailLengthSeconds() const override
  326. {
  327. return 0.0;
  328. }
  329. bool acceptsMidi() const override
  330. {
  331. return true;
  332. }
  333. bool producesMidi() const override
  334. {
  335. return true;
  336. }
  337. juce::AudioProcessorParameter* getBypassParameter() const override
  338. {
  339. return nullptr;
  340. }
  341. juce::AudioProcessorEditor* createEditor() override;
  342. bool hasEditor() const override
  343. {
  344. return true;
  345. }
  346. int getNumPrograms() override
  347. {
  348. return 1;
  349. }
  350. int getCurrentProgram() override
  351. {
  352. return 0;
  353. }
  354. void setCurrentProgram(int) override
  355. {
  356. }
  357. const juce::String getProgramName(int) override
  358. {
  359. return "Default";
  360. }
  361. void changeProgramName(int, const juce::String&) override
  362. {
  363. }
  364. void getStateInformation(juce::MemoryBlock& destData) override
  365. {
  366. juce::XmlElement xmlState("CardinalState");
  367. for (uint32_t i=0; i<parameterCount; ++i)
  368. xmlState.setAttribute(plugin.getParameterSymbol(i).buffer(), plugin.getParameterValue(i));
  369. for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
  370. {
  371. const String& key(plugin.getStateKey(i));
  372. xmlState.setAttribute(key.buffer(), plugin.getStateValue(key).buffer());
  373. }
  374. copyXmlToBinary(xmlState, destData);
  375. }
  376. void setStateInformation(const void* const data, const int sizeInBytes) override
  377. {
  378. std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
  379. DISTRHO_SAFE_ASSERT_RETURN(xmlState.get() != nullptr,);
  380. const juce::Array<juce::AudioProcessorParameter*>& parameters(getParameters());
  381. for (uint32_t i=0; i<parameterCount; ++i)
  382. {
  383. const double value = xmlState->getDoubleAttribute(plugin.getParameterSymbol(i).buffer(),
  384. plugin.getParameterDefault(i));
  385. const float normalizedValue = plugin.getParameterRanges(i).getFixedAndNormalizedValue(value);
  386. parameters.getUnchecked(static_cast<int>(i))->setValueNotifyingHost(normalizedValue);
  387. }
  388. for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
  389. {
  390. const String& key(plugin.getStateKey(i));
  391. const juce::String value = xmlState->getStringAttribute(key.buffer(),
  392. plugin.getStateDefaultValue(i).buffer());
  393. plugin.setState(key, value.toRawUTF8());
  394. }
  395. }
  396. private:
  397. static bool writeMidiFunc(void* const ptr, const MidiEvent& midiEvent)
  398. {
  399. CardinalWrapperProcessor* const processor = static_cast<CardinalWrapperProcessor*>(ptr);
  400. DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, false);
  401. const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
  402. return processor->currentMidiMessages->addEvent(data,
  403. static_cast<int>(midiEvent.size),
  404. static_cast<int>(midiEvent.frame));
  405. }
  406. };
  407. // --------------------------------------------------------------------------------------------------------------------
  408. // unused in cardinal
  409. static constexpr const sendNoteFunc nullSendNoteFunc = nullptr;
  410. // unwanted, juce file dialogs are ugly
  411. static constexpr const fileRequestFunc nullFileRequestFunc = nullptr;
  412. // UI/editor implementation
  413. class CardinalWrapperEditor : public juce::AudioProcessorEditor,
  414. private juce::Timer
  415. {
  416. CardinalWrapperProcessor& cardinalProcessor;
  417. UIExporter* ui;
  418. void* const dspPtr;
  419. public:
  420. CardinalWrapperEditor(CardinalWrapperProcessor& cardinalProc)
  421. : juce::AudioProcessorEditor(cardinalProc),
  422. cardinalProcessor(cardinalProc),
  423. ui(nullptr),
  424. dspPtr(cardinalProc.plugin.getInstancePointer())
  425. {
  426. setOpaque(true);
  427. setResizable(true, false);
  428. // setResizeLimits(648, 538, -1, -1);
  429. setSize(1228, 666);
  430. startTimer(1000.0 / 60.0);
  431. }
  432. ~CardinalWrapperEditor() override
  433. {
  434. stopTimer();
  435. delete ui;
  436. }
  437. protected:
  438. void timerCallback() override
  439. {
  440. if (ui == nullptr)
  441. return;
  442. for (uint32_t i=0; i<cardinalProcessor.parameterCount; ++i)
  443. {
  444. if (cardinalProcessor.updatedParameters[i])
  445. {
  446. cardinalProcessor.updatedParameters[i] = false;
  447. ui->parameterChanged(i, cardinalProcessor.plugin.getParameterValue(i));
  448. }
  449. }
  450. repaint();
  451. }
  452. void paint(juce::Graphics&) override
  453. {
  454. if (ui == nullptr)
  455. {
  456. juce::ComponentPeer* const peer = getPeer();
  457. DISTRHO_SAFE_ASSERT_RETURN(peer != nullptr,);
  458. void* const nativeHandle = peer->getNativeHandle();
  459. DISTRHO_SAFE_ASSERT_RETURN(nativeHandle != nullptr,);
  460. ui = new UIExporter(this,
  461. (uintptr_t)nativeHandle,
  462. cardinalProcessor.getSampleRate(),
  463. editParamFunc,
  464. setParamFunc,
  465. setStateFunc,
  466. nullSendNoteFunc,
  467. setSizeFunc,
  468. nullFileRequestFunc,
  469. nullptr, // bundlePath
  470. dspPtr,
  471. 0.0 // scaleFactor
  472. );
  473. if (cardinalProcessor.wrapperType == juce::AudioProcessor::wrapperType_Standalone)
  474. {
  475. const double scaleFactor = ui->getScaleFactor();
  476. ui->setWindowOffset(4 * scaleFactor, 30 * scaleFactor);
  477. }
  478. }
  479. ui->plugin_idle();
  480. }
  481. private:
  482. static void editParamFunc(void* const ptr, const uint32_t index, const bool started)
  483. {
  484. CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
  485. DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
  486. CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
  487. if (started)
  488. cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->beginChangeGesture();
  489. else
  490. cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->endChangeGesture();
  491. }
  492. static void setParamFunc(void* const ptr, const uint32_t index, const float value)
  493. {
  494. CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
  495. DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
  496. CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
  497. const juce::Array<juce::AudioProcessorParameter*>& parameters(cardinalProcessor.getParameters());
  498. juce::AudioProcessorParameter* const parameter = parameters.getUnchecked(static_cast<int>(index));
  499. static_cast<ParameterFromDPF*>(parameter)->setValueNotifyingHostFromDPF(value);
  500. }
  501. static void setStateFunc(void* const ptr, const char* const key, const char* const value)
  502. {
  503. CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
  504. DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
  505. CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
  506. cardinalProcessor.plugin.setState(key, value);
  507. }
  508. static void setSizeFunc(void* const ptr, uint width, uint height)
  509. {
  510. CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
  511. DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
  512. #ifdef DISTRHO_OS_MAC
  513. UIExporter* const ui = editor->ui;
  514. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  515. const double scaleFactor = ui->getScaleFactor();
  516. width /= scaleFactor;
  517. height /= scaleFactor;
  518. #endif
  519. editor->setSize(static_cast<int>(width), static_cast<int>(height));
  520. }
  521. };
  522. juce::AudioProcessorEditor* CardinalWrapperProcessor::createEditor()
  523. {
  524. return new CardinalWrapperEditor(*this);
  525. }
  526. // --------------------------------------------------------------------------------------------------------------------
  527. END_NAMESPACE_DISTRHO
  528. // --------------------------------------------------------------------------------------------------------------------
  529. juce::AudioProcessor* createPluginFilter()
  530. {
  531. // set valid but dummy values
  532. d_nextBufferSize = 512;
  533. d_nextSampleRate = 48000.0;
  534. return new DISTRHO_NAMESPACE::CardinalWrapperProcessor;
  535. }
  536. // --------------------------------------------------------------------------------------------------------------------