Audio plugin host https://kx.studio/carla
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.

804 lines
22KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2015 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 2 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 doc/GPL.txt file.
  16. */
  17. #include "CarlaNativeExtUI.hpp"
  18. #include "CarlaMIDI.h"
  19. #include "CarlaThread.hpp"
  20. #include "LinkedList.hpp"
  21. #include "CarlaMathUtils.hpp"
  22. #include "DSP/FFTwrapper.h"
  23. #include "Misc/Master.h"
  24. #include "Misc/MiddleWare.h"
  25. #include "Misc/Part.h"
  26. #include "Misc/Util.h"
  27. #ifdef HAVE_ZYN_UI_DEPS
  28. # include "UI/Connection.h"
  29. #endif
  30. #include <ctime>
  31. #include <set>
  32. #include <string>
  33. #include "juce_audio_basics.h"
  34. using juce::roundToIntAccurate;
  35. using juce::FloatVectorOperations;
  36. // -----------------------------------------------------------------------
  37. #ifdef HAVE_ZYN_UI_DEPS
  38. namespace GUI {
  39. Fl_Osc_Interface* genOscInterface(MiddleWare*)
  40. {
  41. return nullptr;
  42. }
  43. void raiseUi(ui_handle_t, const char *)
  44. {
  45. }
  46. #if 0
  47. ui_handle_t createUi(Fl_Osc_Interface*, void *exit)
  48. {
  49. return nullptr;
  50. }
  51. void destroyUi(ui_handle_t)
  52. {
  53. }
  54. void raiseUi(ui_handle_t, const char *, const char *, ...)
  55. {
  56. }
  57. void tickUi(ui_handle_t)
  58. {
  59. //usleep(100000);
  60. }
  61. #endif
  62. };
  63. #endif
  64. // -----------------------------------------------------------------------
  65. class ZynAddSubFxPrograms
  66. {
  67. public:
  68. ZynAddSubFxPrograms() noexcept
  69. : fInitiated(false),
  70. #ifdef CARLA_PROPER_CPP11_SUPPORT
  71. fRetProgram({0, 0, nullptr}),
  72. #endif
  73. fPrograms() {}
  74. ~ZynAddSubFxPrograms() noexcept
  75. {
  76. if (! fInitiated)
  77. return;
  78. for (LinkedList<const ProgramInfo*>::Itenerator it = fPrograms.begin(); it.valid(); it.next())
  79. {
  80. const ProgramInfo* const& pInfo(it.getValue(nullptr));
  81. delete pInfo;
  82. }
  83. fPrograms.clear();
  84. }
  85. void initIfNeeded()
  86. {
  87. if (fInitiated)
  88. return;
  89. fInitiated = true;
  90. fPrograms.append(new ProgramInfo(0, 0, "default"));
  91. Master& master(getMasterInstance());
  92. // refresh banks
  93. master.bank.rescanforbanks();
  94. for (uint32_t i=0, size=static_cast<uint32_t>(master.bank.banks.size()); i<size; ++i)
  95. {
  96. if (master.bank.banks[i].dir.empty())
  97. continue;
  98. master.bank.loadbank(master.bank.banks[i].dir);
  99. for (uint instrument = 0; instrument < BANK_SIZE; ++instrument)
  100. {
  101. const std::string insName(master.bank.getname(instrument));
  102. if (insName.empty() || insName[0] == '\0' || insName[0] == ' ')
  103. continue;
  104. fPrograms.append(new ProgramInfo(i+1, instrument, insName.c_str()));
  105. }
  106. }
  107. }
  108. void load(Master* const master, CarlaMutex& mutex, const uint8_t channel, const uint32_t bank, const uint32_t program)
  109. {
  110. if (bank == 0)
  111. {
  112. if (program != 0)
  113. return;
  114. const CarlaMutexLocker cml(mutex);
  115. master->partonoff(channel, 1);
  116. master->part[channel]->defaults();
  117. master->part[channel]->applyparameters(false);
  118. return;
  119. }
  120. const Master& gmaster(getMasterInstance());
  121. const std::string& bankdir(gmaster.bank.banks[bank-1].dir);
  122. if (bankdir.empty())
  123. return;
  124. const CarlaMutexLocker cml(mutex);
  125. master->partonoff(channel, 1);
  126. master->bank.loadbank(bankdir);
  127. master->bank.loadfromslot(program, master->part[channel]);
  128. master->part[channel]->applyparameters(false);
  129. }
  130. uint32_t getNativeMidiProgramCount() const noexcept
  131. {
  132. return static_cast<uint32_t>(fPrograms.count());
  133. }
  134. const NativeMidiProgram* getNativeMidiProgramInfo(const uint32_t index) const noexcept
  135. {
  136. if (index >= fPrograms.count())
  137. return nullptr;
  138. const ProgramInfo* const pInfo(fPrograms.getAt(index, nullptr));
  139. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr, nullptr);
  140. fRetProgram.bank = pInfo->bank;
  141. fRetProgram.program = pInfo->prog;
  142. fRetProgram.name = pInfo->name;
  143. return &fRetProgram;
  144. }
  145. uint32_t getZynBankCount() const
  146. {
  147. const Master& master(getMasterInstance());
  148. return master.bank.banks.size();
  149. }
  150. private:
  151. struct ProgramInfo {
  152. uint32_t bank;
  153. uint32_t prog;
  154. const char* name;
  155. ProgramInfo(uint32_t b, uint32_t p, const char* n) noexcept
  156. : bank(b),
  157. prog(p),
  158. name(carla_strdup_safe(n)) {}
  159. ~ProgramInfo() noexcept
  160. {
  161. if (name != nullptr)
  162. {
  163. delete[] name;
  164. name = nullptr;
  165. }
  166. }
  167. #ifdef CARLA_PROPER_CPP11_SUPPORT
  168. ProgramInfo() = delete;
  169. ProgramInfo(ProgramInfo&) = delete;
  170. ProgramInfo(const ProgramInfo&) = delete;
  171. ProgramInfo& operator=(ProgramInfo&);
  172. ProgramInfo& operator=(const ProgramInfo&);
  173. #endif
  174. };
  175. bool fInitiated;
  176. mutable NativeMidiProgram fRetProgram;
  177. LinkedList<const ProgramInfo*> fPrograms;
  178. static Master& getMasterInstance()
  179. {
  180. static SYNTH_T synth;
  181. static Master master(synth);
  182. return master;
  183. }
  184. CARLA_PREVENT_HEAP_ALLOCATION
  185. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
  186. };
  187. static ZynAddSubFxPrograms sPrograms;
  188. // -----------------------------------------------------------------------
  189. class ZynAddSubFxThread : public CarlaThread
  190. {
  191. public:
  192. ZynAddSubFxThread(Master* const master, CarlaMutex& mutex) noexcept
  193. : CarlaThread("ZynAddSubFxThread"),
  194. fMaster(master),
  195. fMutex(mutex),
  196. fChangeProgram(false),
  197. fNextChannel(0),
  198. fNextBank(0),
  199. fNextProgram(0) {}
  200. void loadProgramLater(const uint8_t channel, const uint32_t bank, const uint32_t program) noexcept
  201. {
  202. fNextChannel = channel;
  203. fNextBank = bank;
  204. fNextProgram = program;
  205. fChangeProgram = true;
  206. }
  207. void stopLoadProgramLater() noexcept
  208. {
  209. fChangeProgram = false;
  210. fNextChannel = 0;
  211. fNextBank = 0;
  212. fNextProgram = 0;
  213. }
  214. void setMaster(Master* const master) noexcept
  215. {
  216. fMaster = master;
  217. }
  218. protected:
  219. void run() override
  220. {
  221. while (! shouldThreadExit())
  222. {
  223. if (fChangeProgram)
  224. {
  225. fChangeProgram = false;
  226. sPrograms.load(fMaster, fMutex, fNextChannel, fNextBank, fNextProgram);
  227. fNextChannel = 0;
  228. fNextBank = 0;
  229. fNextProgram = 0;
  230. carla_msleep(15);
  231. }
  232. else
  233. {
  234. carla_msleep(30);
  235. }
  236. }
  237. }
  238. private:
  239. Master* fMaster;
  240. CarlaMutex& fMutex;
  241. volatile bool fChangeProgram;
  242. volatile uint8_t fNextChannel;
  243. volatile uint32_t fNextBank;
  244. volatile uint32_t fNextProgram;
  245. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  246. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxThread)
  247. };
  248. // -----------------------------------------------------------------------
  249. class ZynAddSubFxPlugin : public NativePluginAndUiClass
  250. {
  251. public:
  252. enum Parameters {
  253. kParamFilterCutoff = 0, // Filter Frequency
  254. kParamFilterQ, // Filter Resonance
  255. kParamBandwidth, // Bandwidth
  256. kParamModAmp, // FM Gain
  257. kParamResCenter, // Resonance center frequency
  258. kParamResBandwidth, // Resonance bandwidth
  259. kParamCount
  260. };
  261. ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
  262. : NativePluginAndUiClass(host, "zynaddsubfx-ui"),
  263. fMiddleWare(nullptr),
  264. fMaster(nullptr),
  265. fSynth(),
  266. fIsActive(false),
  267. fMutex(),
  268. fThread(nullptr, fMutex),
  269. leakDetector_ZynAddSubFxPlugin()
  270. {
  271. // init parameters to default
  272. fParameters[kParamFilterCutoff] = 64.0f;
  273. fParameters[kParamFilterQ] = 64.0f;
  274. fParameters[kParamBandwidth] = 64.0f;
  275. fParameters[kParamModAmp] = 127.0f;
  276. fParameters[kParamResCenter] = 64.0f;
  277. fParameters[kParamResBandwidth] = 64.0f;
  278. fSynth.buffersize = static_cast<int>(getBufferSize());
  279. fSynth.samplerate = static_cast<uint>(getSampleRate());
  280. //if (fSynth.buffersize > 32)
  281. // fSynth.buffersize = 32;
  282. fSynth.alias();
  283. _initMaster();
  284. sPrograms.initIfNeeded();
  285. }
  286. ~ZynAddSubFxPlugin() override
  287. {
  288. _deleteMaster();
  289. }
  290. protected:
  291. // -------------------------------------------------------------------
  292. // Plugin parameter calls
  293. uint32_t getParameterCount() const final
  294. {
  295. return kParamCount;
  296. }
  297. const NativeParameter* getParameterInfo(const uint32_t index) const override
  298. {
  299. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  300. static NativeParameter param;
  301. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_INTEGER|NATIVE_PARAMETER_IS_AUTOMABLE;
  302. param.name = nullptr;
  303. param.unit = nullptr;
  304. param.ranges.def = 64.0f;
  305. param.ranges.min = 0.0f;
  306. param.ranges.max = 127.0f;
  307. param.ranges.step = 1.0f;
  308. param.ranges.stepSmall = 1.0f;
  309. param.ranges.stepLarge = 20.0f;
  310. param.scalePointCount = 0;
  311. param.scalePoints = nullptr;
  312. switch (index)
  313. {
  314. case kParamFilterCutoff:
  315. param.name = "Filter Cutoff";
  316. break;
  317. case kParamFilterQ:
  318. param.name = "Filter Q";
  319. break;
  320. case kParamBandwidth:
  321. param.name = "Bandwidth";
  322. break;
  323. case kParamModAmp:
  324. param.name = "FM Gain";
  325. param.ranges.def = 127.0f;
  326. break;
  327. case kParamResCenter:
  328. param.name = "Res Center Freq";
  329. break;
  330. case kParamResBandwidth:
  331. param.name = "Res Bandwidth";
  332. break;
  333. }
  334. param.hints = static_cast<NativeParameterHints>(hints);
  335. return &param;
  336. }
  337. float getParameterValue(const uint32_t index) const final
  338. {
  339. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  340. return fParameters[index];
  341. }
  342. // -------------------------------------------------------------------
  343. // Plugin midi-program calls
  344. uint32_t getMidiProgramCount() const noexcept override
  345. {
  346. return sPrograms.getNativeMidiProgramCount();
  347. }
  348. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
  349. {
  350. return sPrograms.getNativeMidiProgramInfo(index);
  351. }
  352. // -------------------------------------------------------------------
  353. // Plugin state calls
  354. void setParameterValue(const uint32_t index, const float value) final
  355. {
  356. CARLA_SAFE_ASSERT_RETURN(index < kParamCount,);
  357. const uint zynIndex(getZynParameterFromIndex(index));
  358. CARLA_SAFE_ASSERT_RETURN(zynIndex != C_NULL,);
  359. fParameters[index] = std::round(carla_fixValue(0.0f, 127.0f, value));
  360. for (int npart=0; npart<NUM_MIDI_PARTS; ++npart)
  361. {
  362. if (fMaster->part[npart] != nullptr && fMaster->part[npart]->Penabled != 0)
  363. fMaster->part[npart]->SetController(zynIndex, static_cast<int>(value));
  364. }
  365. }
  366. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  367. {
  368. if (bank >= sPrograms.getZynBankCount())
  369. return;
  370. if (program >= BANK_SIZE)
  371. return;
  372. if (isOffline() || ! fIsActive)
  373. {
  374. sPrograms.load(fMaster, fMutex, channel, bank, program);
  375. return;
  376. }
  377. fThread.loadProgramLater(channel, bank, program);
  378. }
  379. void setCustomData(const char* const key, const char* const value) override
  380. {
  381. CARLA_SAFE_ASSERT_RETURN(key != nullptr,);
  382. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  383. const CarlaMutexLocker cml(fMutex);
  384. /**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  385. {
  386. fMaster->defaults();
  387. fMaster->loadXML(value);
  388. }
  389. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  390. {
  391. fMaster->part[0]->defaultsinstrument();
  392. fMaster->part[0]->loadXMLinstrument(value);
  393. }
  394. fMaster->applyparameters();
  395. }
  396. // -------------------------------------------------------------------
  397. // Plugin process calls
  398. void activate() override
  399. {
  400. fIsActive = true;
  401. }
  402. void deactivate() override
  403. {
  404. fIsActive = false;
  405. }
  406. void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  407. {
  408. if (! fMutex.tryLock())
  409. {
  410. if (! isOffline())
  411. {
  412. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  413. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  414. return;
  415. }
  416. fMutex.lock();
  417. }
  418. for (uint32_t i=0; i < midiEventCount; ++i)
  419. {
  420. const NativeMidiEvent* const midiEvent(&midiEvents[i]);
  421. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  422. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  423. if (MIDI_IS_STATUS_NOTE_OFF(status))
  424. {
  425. const char note = static_cast<char>(midiEvent->data[1]);
  426. fMaster->noteOff(channel, note);
  427. }
  428. else if (MIDI_IS_STATUS_NOTE_ON(status))
  429. {
  430. const char note = static_cast<char>(midiEvent->data[1]);
  431. const char velo = static_cast<char>(midiEvent->data[2]);
  432. fMaster->noteOn(channel, note, velo);
  433. }
  434. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  435. {
  436. const char note = static_cast<char>(midiEvent->data[1]);
  437. const char pressure = static_cast<char>(midiEvent->data[2]);
  438. fMaster->polyphonicAftertouch(channel, note, pressure);
  439. }
  440. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  441. {
  442. // skip controls which we map to parameters
  443. if (getZynParameterFromIndex(midiEvent->data[1]) != C_NULL)
  444. continue;
  445. const int control = midiEvent->data[1];
  446. const int value = midiEvent->data[2];
  447. fMaster->setController(channel, control, value);
  448. }
  449. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  450. {
  451. const uint8_t lsb = midiEvent->data[1];
  452. const uint8_t msb = midiEvent->data[2];
  453. const int value = ((msb << 7) | lsb) - 8192;
  454. fMaster->setController(channel, C_pitchwheel, value);
  455. }
  456. }
  457. fMaster->GetAudioOutSamples(frames, fSynth.samplerate, outBuffer[0], outBuffer[1]);
  458. fMutex.unlock();
  459. }
  460. // -------------------------------------------------------------------
  461. // Plugin UI calls
  462. #ifdef HAVE_ZYN_UI_DEPS
  463. void uiShow(const bool show) override
  464. {
  465. if (show)
  466. {
  467. if (isPipeRunning())
  468. {
  469. const CarlaMutexLocker cml(getPipeLock());
  470. writeMessage("focus\n", 6);
  471. flushMessages();
  472. return;
  473. }
  474. carla_stdout("Trying to start UI using \"%s\"", getExtUiPath());
  475. CarlaExternalUI::setData(getExtUiPath(), fMiddleWare->getServerAddress(), getUiName());
  476. if (! CarlaExternalUI::startPipeServer(true))
  477. {
  478. uiClosed();
  479. hostUiUnavailable();
  480. }
  481. }
  482. else
  483. {
  484. CarlaExternalUI::stopPipeServer(2000);
  485. }
  486. }
  487. void uiIdle() override
  488. {
  489. NativePluginAndUiClass::uiIdle();
  490. if (isPipeRunning())
  491. fMiddleWare->tick();
  492. }
  493. #endif
  494. // -------------------------------------------------------------------
  495. // Plugin state calls
  496. char* getState() const override
  497. {
  498. char* data = nullptr;
  499. fMaster->getalldata(&data);
  500. return data;
  501. }
  502. void setState(const char* const data) override
  503. {
  504. fThread.stopLoadProgramLater();
  505. const CarlaMutexLocker cml(fMutex);
  506. fMaster->putalldata(const_cast<char*>(data), 0);
  507. fMaster->applyparameters();
  508. }
  509. // -------------------------------------------------------------------
  510. // Plugin dispatcher
  511. void bufferSizeChanged(const uint32_t bufferSize) final
  512. {
  513. char* const state(getState());
  514. _deleteMaster();
  515. fSynth.buffersize = static_cast<int>(bufferSize);
  516. fSynth.alias();
  517. _initMaster();
  518. if (state != nullptr)
  519. {
  520. fMaster->putalldata(state, 0);
  521. fMaster->applyparameters();
  522. std::free(state);
  523. }
  524. }
  525. void sampleRateChanged(const double sampleRate) final
  526. {
  527. char* const state(getState());
  528. _deleteMaster();
  529. fSynth.samplerate = static_cast<uint>(sampleRate);
  530. fSynth.alias();
  531. _initMaster();
  532. if (state != nullptr)
  533. {
  534. fMaster->putalldata(state, 0);
  535. fMaster->applyparameters();
  536. std::free(state);
  537. }
  538. }
  539. // -------------------------------------------------------------------
  540. private:
  541. MiddleWare* fMiddleWare;
  542. Master* fMaster;
  543. SYNTH_T fSynth;
  544. bool fIsActive;
  545. float fParameters[kParamCount];
  546. CarlaMutex fMutex;
  547. ZynAddSubFxThread fThread;
  548. static uint getZynParameterFromIndex(const uint index)
  549. {
  550. switch (index)
  551. {
  552. case kParamFilterCutoff:
  553. return C_filtercutoff;
  554. case kParamFilterQ:
  555. return C_filterq;
  556. case kParamBandwidth:
  557. return C_bandwidth;
  558. case kParamModAmp:
  559. return C_fmamp;
  560. case kParamResCenter:
  561. return C_resonance_center;
  562. case kParamResBandwidth:
  563. return C_resonance_bandwidth;
  564. case kParamCount:
  565. return C_NULL;
  566. }
  567. return C_NULL;
  568. }
  569. // -------------------------------------------------------------------
  570. void _initMaster()
  571. {
  572. fMiddleWare = new MiddleWare(fSynth);
  573. fMaster = fMiddleWare->spawnMaster();
  574. fThread.setMaster(fMaster);
  575. fThread.startThread();
  576. for (int i=0; i<NUM_MIDI_PARTS; ++i)
  577. {
  578. fMaster->partonoff(i, 1);
  579. fMaster->part[i]->SetController(C_filtercutoff, static_cast<int>(fParameters[kParamFilterCutoff]));
  580. fMaster->part[i]->SetController(C_filterq, static_cast<int>(fParameters[kParamFilterQ]));
  581. fMaster->part[i]->SetController(C_bandwidth, static_cast<int>(fParameters[kParamBandwidth]));
  582. fMaster->part[i]->SetController(C_fmamp, static_cast<int>(fParameters[kParamModAmp]));
  583. fMaster->part[i]->SetController(C_resonance_center, static_cast<int>(fParameters[kParamResCenter]));
  584. fMaster->part[i]->SetController(C_resonance_bandwidth, static_cast<int>(fParameters[kParamResBandwidth]));
  585. }
  586. }
  587. void _deleteMaster()
  588. {
  589. //ensure that everything has stopped
  590. fThread.stopThread(-1);
  591. fMaster = nullptr;
  592. delete fMiddleWare;
  593. fMiddleWare = nullptr;
  594. }
  595. // -------------------------------------------------------------------
  596. public:
  597. static NativePluginHandle _instantiate(const NativeHostDescriptor* host)
  598. {
  599. static bool needsInit = true;
  600. if (needsInit)
  601. {
  602. needsInit = false;
  603. config.init();
  604. sprng(static_cast<prng_t>(std::time(nullptr)));
  605. // FIXME - kill this
  606. denormalkillbuf = new float[8192];
  607. for (int i=0; i < 8192; ++i)
  608. denormalkillbuf[i] = (RND - 0.5f) * 1e-16f;
  609. }
  610. return new ZynAddSubFxPlugin(host);
  611. }
  612. static void _cleanup(NativePluginHandle handle)
  613. {
  614. delete (ZynAddSubFxPlugin*)handle;
  615. }
  616. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  617. };
  618. // -----------------------------------------------------------------------
  619. static const NativePluginDescriptor zynaddsubfxDesc = {
  620. /* category */ NATIVE_PLUGIN_CATEGORY_SYNTH,
  621. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
  622. #ifdef HAVE_ZYN_UI_DEPS
  623. |NATIVE_PLUGIN_HAS_UI
  624. #endif
  625. |NATIVE_PLUGIN_USES_MULTI_PROGS
  626. |NATIVE_PLUGIN_USES_STATE),
  627. /* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_CONTROL_CHANGES
  628. |NATIVE_PLUGIN_SUPPORTS_NOTE_AFTERTOUCH
  629. |NATIVE_PLUGIN_SUPPORTS_PITCHBEND
  630. |NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  631. /* audioIns */ 0,
  632. /* audioOuts */ 2,
  633. /* midiIns */ 1,
  634. /* midiOuts */ 0,
  635. /* paramIns */ ZynAddSubFxPlugin::kParamCount,
  636. /* paramOuts */ 0,
  637. /* name */ "ZynAddSubFX",
  638. /* label */ "zynaddsubfx",
  639. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  640. /* copyright */ "GNU GPL v2+",
  641. PluginDescriptorFILL(ZynAddSubFxPlugin)
  642. };
  643. // -----------------------------------------------------------------------
  644. CARLA_EXPORT
  645. void carla_register_native_plugin_zynaddsubfx_synth();
  646. CARLA_EXPORT
  647. void carla_register_native_plugin_zynaddsubfx_synth()
  648. {
  649. carla_register_native_plugin(&zynaddsubfxDesc);
  650. }
  651. // -----------------------------------------------------------------------