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.

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