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.

842 lines
22KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2014 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 "CarlaNative.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/Part.h"
  25. #include "Misc/Util.h"
  26. #ifdef WANT_ZYNADDSUBFX_UI
  27. # ifdef override
  28. # define override_hack
  29. # undef override
  30. # endif
  31. # include "UI/common.H"
  32. # include "UI/MasterUI.h"
  33. # include <FL/Fl_Shared_Image.H>
  34. # include <FL/Fl_Tiled_Image.H>
  35. # include <FL/Fl_Theme.H>
  36. # ifdef override_hack
  37. # define override
  38. # undef override_hack
  39. # endif
  40. #endif
  41. #include <ctime>
  42. #include <set>
  43. #include <string>
  44. #include "juce_audio_basics.h"
  45. using juce::FloatVectorOperations;
  46. #ifdef WANT_ZYNADDSUBFX_UI
  47. static Fl_Tiled_Image* gModuleBackdrop = nullptr;
  48. static CarlaString gPixmapPath;
  49. extern CarlaString gUiPixmapPath;
  50. void set_module_parameters(Fl_Widget* o)
  51. {
  52. CARLA_ASSERT(gModuleBackdrop != nullptr);
  53. o->box(FL_DOWN_FRAME);
  54. o->align(o->align() | FL_ALIGN_IMAGE_BACKDROP);
  55. o->color(FL_BLACK);
  56. o->labeltype(FL_SHADOW_LABEL);
  57. if (gModuleBackdrop != nullptr)
  58. o->image(gModuleBackdrop);
  59. }
  60. #endif
  61. // -----------------------------------------------------------------------
  62. class ZynAddSubFxPrograms
  63. {
  64. public:
  65. ZynAddSubFxPrograms()
  66. : fInitiated(false),
  67. fRetProgram({0, 0, nullptr}),
  68. fPrograms() {}
  69. ~ZynAddSubFxPrograms()
  70. {
  71. if (! fInitiated)
  72. return;
  73. for (LinkedList<const ProgramInfo*>::Itenerator it = fPrograms.begin(); it.valid(); it.next())
  74. {
  75. const ProgramInfo* const& pInfo(it.getValue());
  76. delete pInfo;
  77. }
  78. fPrograms.clear();
  79. FFT_cleanup();
  80. }
  81. void init()
  82. {
  83. if (fInitiated)
  84. return;
  85. fInitiated = true;
  86. fPrograms.append(new ProgramInfo(0, 0, "default"));
  87. Master& master(Master::getInstance());
  88. pthread_mutex_lock(&master.mutex);
  89. // refresh banks
  90. master.bank.rescanforbanks();
  91. for (uint32_t i=0, size=static_cast<uint32_t>(master.bank.banks.size()); i<size; ++i)
  92. {
  93. if (master.bank.banks[i].dir.empty())
  94. continue;
  95. master.bank.loadbank(master.bank.banks[i].dir);
  96. for (unsigned int instrument = 0; instrument < BANK_SIZE; ++instrument)
  97. {
  98. const std::string insName(master.bank.getname(instrument));
  99. if (insName.empty() || insName[0] == '\0' || insName[0] == ' ')
  100. continue;
  101. fPrograms.append(new ProgramInfo(i+1, instrument, insName.c_str()));
  102. }
  103. }
  104. pthread_mutex_unlock(&master.mutex);
  105. }
  106. void load(Master* const master, const uint8_t channel, const uint32_t bank, const uint32_t program)
  107. {
  108. if (bank == 0)
  109. {
  110. pthread_mutex_lock(&master->mutex);
  111. master->partonoff(channel, 1);
  112. master->part[channel]->defaults();
  113. master->part[channel]->applyparameters(false);
  114. pthread_mutex_unlock(&master->mutex);
  115. return;
  116. }
  117. const std::string& bankdir(master->bank.banks[bank-1].dir);
  118. if (! bankdir.empty())
  119. {
  120. pthread_mutex_lock(&master->mutex);
  121. master->partonoff(channel, 1);
  122. master->bank.loadbank(bankdir);
  123. master->bank.loadfromslot(program, master->part[channel]);
  124. master->part[channel]->applyparameters(false);
  125. pthread_mutex_unlock(&master->mutex);
  126. }
  127. }
  128. uint32_t count() const noexcept
  129. {
  130. return static_cast<uint32_t>(fPrograms.count());
  131. }
  132. const NativeMidiProgram* getInfo(const uint32_t index) const noexcept
  133. {
  134. if (index >= fPrograms.count())
  135. return nullptr;
  136. const ProgramInfo* const pInfo(fPrograms.getAt(index, nullptr));
  137. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr, nullptr);
  138. fRetProgram.bank = pInfo->bank;
  139. fRetProgram.program = pInfo->prog;
  140. fRetProgram.name = pInfo->name;
  141. return &fRetProgram;
  142. }
  143. private:
  144. struct ProgramInfo {
  145. uint32_t bank;
  146. uint32_t prog;
  147. const char* name;
  148. ProgramInfo(uint32_t bank_, uint32_t prog_, const char* name_) noexcept
  149. : bank(bank_),
  150. prog(prog_),
  151. name(carla_strdup_safe(name_)) {}
  152. ~ProgramInfo() noexcept
  153. {
  154. if (name != nullptr)
  155. {
  156. delete[] name;
  157. name = nullptr;
  158. }
  159. }
  160. #ifdef CARLA_PROPER_CPP11_SUPPORT
  161. ProgramInfo() = delete;
  162. ProgramInfo(ProgramInfo&) = delete;
  163. ProgramInfo(const ProgramInfo&) = delete;
  164. ProgramInfo& operator=(ProgramInfo&);
  165. ProgramInfo& operator=(const ProgramInfo&);
  166. #endif
  167. };
  168. bool fInitiated;
  169. mutable NativeMidiProgram fRetProgram;
  170. LinkedList<const ProgramInfo*> fPrograms;
  171. CARLA_PREVENT_HEAP_ALLOCATION
  172. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
  173. };
  174. static ZynAddSubFxPrograms sPrograms;
  175. // -----------------------------------------------------------------------
  176. class ZynAddSubFxInstanceCount
  177. {
  178. public:
  179. ZynAddSubFxInstanceCount()
  180. : fCount(0)
  181. {
  182. }
  183. ~ZynAddSubFxInstanceCount()
  184. {
  185. CARLA_ASSERT(fCount == 0);
  186. }
  187. void addOne(const NativeHostDescriptor* const host)
  188. {
  189. if (fCount++ == 0)
  190. {
  191. CARLA_ASSERT(synth == nullptr);
  192. CARLA_ASSERT(denormalkillbuf == nullptr);
  193. reinit(host);
  194. #ifdef WANT_ZYNADDSUBFX_UI
  195. if (gPixmapPath.isEmpty())
  196. {
  197. gPixmapPath = host->resourceDir;
  198. gPixmapPath += "/zynaddsubfx/";
  199. gUiPixmapPath = gPixmapPath;
  200. }
  201. #endif
  202. }
  203. }
  204. void removeOne()
  205. {
  206. if (--fCount == 0)
  207. {
  208. CARLA_ASSERT(synth != nullptr);
  209. CARLA_ASSERT(denormalkillbuf != nullptr);
  210. Master::deleteInstance();
  211. delete[] denormalkillbuf;
  212. denormalkillbuf = nullptr;
  213. delete synth;
  214. synth = nullptr;
  215. }
  216. }
  217. void reinit(const NativeHostDescriptor* const host)
  218. {
  219. Master::deleteInstance();
  220. if (denormalkillbuf != nullptr)
  221. {
  222. delete[] denormalkillbuf;
  223. denormalkillbuf = nullptr;
  224. }
  225. if (synth != nullptr)
  226. {
  227. delete synth;
  228. synth = nullptr;
  229. }
  230. synth = new SYNTH_T();
  231. synth->buffersize = static_cast<int>(host->get_buffer_size(host->handle));
  232. synth->samplerate = static_cast<uint>(host->get_sample_rate(host->handle));
  233. if (synth->buffersize > 32)
  234. synth->buffersize = 32;
  235. synth->alias();
  236. config.init();
  237. config.cfg.SoundBufferSize = synth->buffersize;
  238. config.cfg.SampleRate = static_cast<int>(synth->samplerate);
  239. config.cfg.GzipCompression = 0;
  240. sprng(static_cast<prng_t>(std::time(nullptr)));
  241. denormalkillbuf = new float[synth->buffersize];
  242. for (int i=0; i < synth->buffersize; ++i)
  243. denormalkillbuf[i] = (RND - 0.5f) * 1e-16f;
  244. Master::getInstance();
  245. }
  246. void maybeReinit(const NativeHostDescriptor* const host)
  247. {
  248. if (host->get_buffer_size(host->handle) == static_cast<uint32_t>(synth->buffersize) &&
  249. host->get_sample_rate(host->handle) == static_cast<double>(synth->samplerate))
  250. return;
  251. reinit(host);
  252. }
  253. private:
  254. int fCount;
  255. CARLA_PREVENT_HEAP_ALLOCATION
  256. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxInstanceCount)
  257. };
  258. static ZynAddSubFxInstanceCount sInstanceCount;
  259. // -----------------------------------------------------------------------
  260. class ZynAddSubFxThread : public CarlaThread
  261. {
  262. public:
  263. ZynAddSubFxThread(Master* const master, const NativeHostDescriptor* const host)
  264. : CarlaThread("ZynAddSubFxThread"),
  265. fMaster(master),
  266. kHost(host),
  267. #ifdef WANT_ZYNADDSUBFX_UI
  268. fUi(nullptr),
  269. fUiClosed(0),
  270. fNextUiAction(-1),
  271. #endif
  272. fChangeProgram(false),
  273. fNextChannel(0),
  274. fNextBank(0),
  275. fNextProgram(0)
  276. {
  277. }
  278. ~ZynAddSubFxThread()
  279. {
  280. #ifdef WANT_ZYNADDSUBFX_UI
  281. // must be closed by now
  282. CARLA_ASSERT(fUi == nullptr);
  283. #endif
  284. }
  285. void loadProgramLater(const uint8_t channel, const uint32_t bank, const uint32_t program)
  286. {
  287. fNextChannel = channel;
  288. fNextBank = bank;
  289. fNextProgram = program;
  290. fChangeProgram = true;
  291. }
  292. void stopLoadProgramLater()
  293. {
  294. fChangeProgram = false;
  295. fNextChannel = 0;
  296. fNextBank = 0;
  297. fNextProgram = 0;
  298. }
  299. void setMaster(Master* const master)
  300. {
  301. fMaster = master;
  302. }
  303. #ifdef WANT_ZYNADDSUBFX_UI
  304. void uiHide()
  305. {
  306. fNextUiAction = 0;
  307. }
  308. void uiShow()
  309. {
  310. fNextUiAction = 1;
  311. }
  312. void uiRepaint()
  313. {
  314. if (fUi != nullptr)
  315. fNextUiAction = 2;
  316. }
  317. void uiChangeName(const char* const name)
  318. {
  319. if (fUi != nullptr)
  320. {
  321. Fl::lock();
  322. fUi->masterwindow->label(name);
  323. Fl::unlock();
  324. }
  325. }
  326. #endif
  327. protected:
  328. void run() override
  329. {
  330. while (! shouldThreadExit())
  331. {
  332. #ifdef WANT_ZYNADDSUBFX_UI
  333. Fl::lock();
  334. if (fNextUiAction == 2) // repaint
  335. {
  336. CARLA_ASSERT(fUi != nullptr);
  337. if (fUi != nullptr)
  338. fUi->refresh_master_ui();
  339. }
  340. else if (fNextUiAction == 1) // init/show
  341. {
  342. static bool initialized = false;
  343. if (! initialized)
  344. {
  345. initialized = true;
  346. fl_register_images();
  347. Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL);
  348. if (Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "knob.png"))
  349. Fl_Dial::default_image(img);
  350. if (Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "window_backdrop.png"))
  351. Fl::scheme_bg(new Fl_Tiled_Image(img));
  352. if(Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "module_backdrop.png"))
  353. gModuleBackdrop = new Fl_Tiled_Image(img);
  354. Fl::background(50, 50, 50);
  355. Fl::background2(70, 70, 70);
  356. Fl::foreground(255, 255, 255);
  357. Fl_Theme::set("Cairo");
  358. }
  359. if (fUi == nullptr)
  360. {
  361. fUiClosed = 0;
  362. fUi = new MasterUI(fMaster, &fUiClosed);
  363. fUi->masterwindow->label(kHost->uiName);
  364. fUi->showUI();
  365. }
  366. else
  367. fUi->showUI();
  368. }
  369. else if (fNextUiAction == 0) // close
  370. {
  371. CARLA_ASSERT(fUi != nullptr);
  372. if (fUi != nullptr)
  373. {
  374. delete fUi;
  375. fUi = nullptr;
  376. }
  377. }
  378. fNextUiAction = -1;
  379. if (fUiClosed != 0)
  380. {
  381. fUiClosed = 0;
  382. fNextUiAction = 0;
  383. kHost->ui_closed(kHost->handle);
  384. }
  385. Fl::check();
  386. Fl::unlock();
  387. #endif
  388. if (fChangeProgram)
  389. {
  390. fChangeProgram = false;
  391. sPrograms.load(fMaster, fNextChannel, fNextBank, fNextProgram);
  392. fNextChannel = 0;
  393. fNextBank = 0;
  394. fNextProgram = 0;
  395. #ifdef WANT_ZYNADDSUBFX_UI
  396. if (fUi != nullptr)
  397. {
  398. Fl::lock();
  399. fUi->refresh_master_ui();
  400. Fl::unlock();
  401. }
  402. #endif
  403. carla_msleep(15);
  404. }
  405. else
  406. {
  407. carla_msleep(30);
  408. }
  409. }
  410. #ifdef WANT_ZYNADDSUBFX_UI
  411. if (shouldThreadExit() || fUi != nullptr)
  412. {
  413. Fl::lock();
  414. delete fUi;
  415. fUi = nullptr;
  416. Fl::check();
  417. Fl::unlock();
  418. }
  419. #endif
  420. }
  421. private:
  422. Master* fMaster;
  423. const NativeHostDescriptor* const kHost;
  424. #ifdef WANT_ZYNADDSUBFX_UI
  425. MasterUI* fUi;
  426. int fUiClosed;
  427. volatile int fNextUiAction;
  428. #endif
  429. volatile bool fChangeProgram;
  430. volatile uint8_t fNextChannel;
  431. volatile uint32_t fNextBank;
  432. volatile uint32_t fNextProgram;
  433. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  434. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxThread)
  435. };
  436. // -----------------------------------------------------------------------
  437. class ZynAddSubFxPlugin : public NativePluginClass
  438. {
  439. public:
  440. ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
  441. : NativePluginClass(host),
  442. fMaster(new Master()),
  443. fSampleRate(static_cast<uint>(getSampleRate())),
  444. fIsActive(false),
  445. fThread(fMaster, host),
  446. leakDetector_ZynAddSubFxPlugin()
  447. {
  448. fThread.startThread();
  449. for (int i = 0; i < NUM_MIDI_PARTS; ++i)
  450. fMaster->partonoff(i, 1);
  451. sPrograms.init();
  452. }
  453. ~ZynAddSubFxPlugin() override
  454. {
  455. deleteMaster();
  456. }
  457. protected:
  458. // -------------------------------------------------------------------
  459. // Plugin midi-program calls
  460. uint32_t getMidiProgramCount() const override
  461. {
  462. return sPrograms.count();
  463. }
  464. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const override
  465. {
  466. return sPrograms.getInfo(index);
  467. }
  468. // -------------------------------------------------------------------
  469. // Plugin state calls
  470. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  471. {
  472. if (bank >= fMaster->bank.banks.size())
  473. return;
  474. if (program >= BANK_SIZE)
  475. return;
  476. if (isOffline() || ! fIsActive)
  477. {
  478. sPrograms.load(fMaster, channel, bank, program);
  479. #ifdef WANT_ZYNADDSUBFX_UI
  480. fThread.uiRepaint();
  481. #endif
  482. }
  483. else
  484. fThread.loadProgramLater(channel, bank, program);
  485. }
  486. void setCustomData(const char* const key, const char* const value) override
  487. {
  488. CARLA_SAFE_ASSERT_RETURN(key != nullptr,);
  489. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  490. pthread_mutex_lock(&fMaster->mutex);
  491. if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  492. {
  493. fMaster->defaults();
  494. fMaster->loadXML(value);
  495. }
  496. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  497. {
  498. fMaster->part[0]->defaultsinstrument();
  499. fMaster->part[0]->loadXMLinstrument(value);
  500. }
  501. pthread_mutex_unlock(&fMaster->mutex);
  502. fMaster->applyparameters();
  503. }
  504. // -------------------------------------------------------------------
  505. // Plugin process calls
  506. void activate() override
  507. {
  508. fIsActive = true;
  509. }
  510. void deactivate() override
  511. {
  512. fIsActive = false;
  513. }
  514. void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  515. {
  516. if (pthread_mutex_trylock(&fMaster->mutex) != 0)
  517. {
  518. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  519. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  520. return;
  521. }
  522. for (uint32_t i=0; i < midiEventCount; ++i)
  523. {
  524. const NativeMidiEvent* const midiEvent(&midiEvents[i]);
  525. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  526. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  527. if (MIDI_IS_STATUS_NOTE_OFF(status))
  528. {
  529. const char note = static_cast<char>(midiEvent->data[1]);
  530. fMaster->noteOff(channel, note);
  531. }
  532. else if (MIDI_IS_STATUS_NOTE_ON(status))
  533. {
  534. const char note = static_cast<char>(midiEvent->data[1]);
  535. const char velo = static_cast<char>(midiEvent->data[2]);
  536. fMaster->noteOn(channel, note, velo);
  537. }
  538. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  539. {
  540. const char note = static_cast<char>(midiEvent->data[1]);
  541. const char pressure = static_cast<char>(midiEvent->data[2]);
  542. fMaster->polyphonicAftertouch(channel, note, pressure);
  543. }
  544. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  545. {
  546. const int control = midiEvent->data[1];
  547. const int value = midiEvent->data[2];
  548. fMaster->setController(channel, control, value);
  549. }
  550. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  551. {
  552. const uint8_t lsb = midiEvent->data[1];
  553. const uint8_t msb = midiEvent->data[2];
  554. const int value = ((msb << 7) | lsb) - 8192;
  555. fMaster->setController(channel, C_pitchwheel, value);
  556. }
  557. }
  558. fMaster->GetAudioOutSamples(frames, fSampleRate, outBuffer[0], outBuffer[1]);
  559. pthread_mutex_unlock(&fMaster->mutex);
  560. }
  561. #ifdef WANT_ZYNADDSUBFX_UI
  562. // -------------------------------------------------------------------
  563. // Plugin UI calls
  564. void uiShow(const bool show) override
  565. {
  566. if (show)
  567. fThread.uiShow();
  568. else
  569. fThread.uiHide();
  570. }
  571. #endif
  572. // -------------------------------------------------------------------
  573. // Plugin state calls
  574. char* getState() const override
  575. {
  576. config.save();
  577. char* data = nullptr;
  578. fMaster->getalldata(&data);
  579. return data;
  580. }
  581. void setState(const char* const data) override
  582. {
  583. fThread.stopLoadProgramLater();
  584. fMaster->putalldata(const_cast<char*>(data), 0);
  585. fMaster->applyparameters(true);
  586. }
  587. // -------------------------------------------------------------------
  588. // Plugin dispatcher
  589. // TODO - save&load current state
  590. void bufferSizeChanged(const uint32_t) final
  591. {
  592. deleteMaster();
  593. sInstanceCount.maybeReinit(getHostHandle());
  594. initMaster();
  595. }
  596. void sampleRateChanged(const double sampleRate) final
  597. {
  598. fSampleRate = static_cast<uint>(sampleRate);
  599. deleteMaster();
  600. sInstanceCount.maybeReinit(getHostHandle());
  601. initMaster();
  602. }
  603. void initMaster()
  604. {
  605. fMaster = new Master();
  606. fThread.setMaster(fMaster);
  607. fThread.startThread();
  608. for (int i = 0; i < NUM_MIDI_PARTS; ++i)
  609. fMaster->partonoff(i, 1);
  610. }
  611. void deleteMaster()
  612. {
  613. //ensure that everything has stopped
  614. pthread_mutex_lock(&fMaster->mutex);
  615. pthread_mutex_unlock(&fMaster->mutex);
  616. fThread.stopThread(-1);
  617. delete fMaster;
  618. fMaster = nullptr;
  619. }
  620. #ifdef WANT_ZYNADDSUBFX_UI
  621. void uiNameChanged(const char* const uiName) override
  622. {
  623. fThread.uiChangeName(uiName);
  624. }
  625. #endif
  626. // -------------------------------------------------------------------
  627. private:
  628. Master* fMaster;
  629. uint fSampleRate;
  630. bool fIsActive;
  631. ZynAddSubFxThread fThread;
  632. // -------------------------------------------------------------------
  633. public:
  634. static NativePluginHandle _instantiate(const NativeHostDescriptor* host)
  635. {
  636. sInstanceCount.addOne(host);
  637. return new ZynAddSubFxPlugin(host);
  638. }
  639. static void _cleanup(NativePluginHandle handle)
  640. {
  641. delete (ZynAddSubFxPlugin*)handle;
  642. sInstanceCount.removeOne();
  643. }
  644. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  645. };
  646. // -----------------------------------------------------------------------
  647. static const NativePluginDescriptor zynaddsubfxDesc = {
  648. /* category */ PLUGIN_CATEGORY_SYNTH,
  649. #ifdef WANT_ZYNADDSUBFX_UI
  650. /* hints */ static_cast<NativePluginHints>(PLUGIN_IS_SYNTH|PLUGIN_HAS_UI|PLUGIN_USES_MULTI_PROGS|PLUGIN_USES_STATE),
  651. #else
  652. /* hints */ static_cast<NativePluginHints>(PLUGIN_IS_SYNTH|PLUGIN_USES_MULTI_PROGS|PLUGIN_USES_STATE),
  653. #endif
  654. /* supports */ static_cast<NativePluginSupports>(PLUGIN_SUPPORTS_CONTROL_CHANGES|PLUGIN_SUPPORTS_NOTE_AFTERTOUCH|PLUGIN_SUPPORTS_PITCHBEND|PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  655. /* audioIns */ 0,
  656. /* audioOuts */ 2,
  657. /* midiIns */ 1,
  658. /* midiOuts */ 0,
  659. /* paramIns */ 0,
  660. /* paramOuts */ 0,
  661. /* name */ "ZynAddSubFX",
  662. /* label */ "zynaddsubfx",
  663. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  664. /* copyright */ "GNU GPL v2+",
  665. PluginDescriptorFILL(ZynAddSubFxPlugin)
  666. };
  667. // -----------------------------------------------------------------------
  668. CARLA_EXPORT
  669. void carla_register_native_plugin_zynaddsubfx_synth();
  670. CARLA_EXPORT
  671. void carla_register_native_plugin_zynaddsubfx_synth()
  672. {
  673. carla_register_native_plugin(&zynaddsubfxDesc);
  674. }
  675. // -----------------------------------------------------------------------