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.

885 lines
22KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2013 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 GPL.txt file
  16. */
  17. // for UINT32_MAX
  18. #define __STDC_LIMIT_MACROS
  19. #include <cstdint>
  20. #include "CarlaNative.hpp"
  21. #include "CarlaMIDI.h"
  22. #include "CarlaString.hpp"
  23. #include "RtList.hpp"
  24. #include <QtCore/QThread>
  25. #include "zynaddsubfx/Misc/Master.h"
  26. #include "zynaddsubfx/Misc/Part.h"
  27. #include "zynaddsubfx/Misc/Util.h"
  28. #ifdef WANT_ZYNADDSUBFX_UI
  29. // FIXME
  30. # ifdef override
  31. # define override_hack
  32. # undef override
  33. # endif
  34. # include "zynaddsubfx/UI/common.H"
  35. # include "zynaddsubfx/UI/MasterUI.h"
  36. # include <FL/Fl_Shared_Image.H>
  37. # include <FL/Fl_Tiled_Image.H>
  38. # include <FL/Fl_Dial.H>
  39. # include <FL/Fl_Theme.H>
  40. # ifdef override_hack
  41. # define override
  42. # undef override_hack
  43. # endif
  44. #endif
  45. #include <ctime>
  46. #include <set>
  47. #include <string>
  48. // Dummy variables and functions for linking purposes
  49. const char* instance_name = nullptr;
  50. class WavFile;
  51. namespace Nio {
  52. bool start(void){return 1;}
  53. void stop(void){}
  54. bool setSource(std::string){return true;}
  55. bool setSink(std::string){return true;}
  56. std::set<std::string> getSources(void){return std::set<std::string>();}
  57. std::set<std::string> getSinks(void){return std::set<std::string>();}
  58. std::string getSource(void){return "";}
  59. std::string getSink(void){return "";}
  60. void waveNew(WavFile*){}
  61. void waveStart(void){}
  62. void waveStop(void){}
  63. void waveEnd(void){}
  64. }
  65. SYNTH_T* synth = nullptr;
  66. #ifdef WANT_ZYNADDSUBFX_UI
  67. #define PIXMAP_PATH "/resources/zynaddsubfx/"
  68. static Fl_Tiled_Image* gModuleBackdrop = nullptr;
  69. static CarlaString gPixmapPath;
  70. extern CarlaString gUiPixmapPath;
  71. void set_module_parameters(Fl_Widget* o)
  72. {
  73. CARLA_ASSERT(gModuleBackdrop != nullptr);
  74. o->box(FL_DOWN_FRAME);
  75. o->align(o->align() | FL_ALIGN_IMAGE_BACKDROP);
  76. o->color(FL_BLACK);
  77. o->labeltype(FL_SHADOW_LABEL);
  78. if (gModuleBackdrop != nullptr)
  79. o->image(gModuleBackdrop);
  80. }
  81. #endif
  82. // -----------------------------------------------------------------------
  83. class ZynAddSubFxPrograms
  84. {
  85. public:
  86. ZynAddSubFxPrograms()
  87. : fInitiated(false)
  88. {
  89. }
  90. ~ZynAddSubFxPrograms()
  91. {
  92. if (! fInitiated)
  93. return;
  94. for (auto it = fPrograms.begin(); it.valid(); it.next())
  95. {
  96. const ProgramInfo*& pInfo(*it);
  97. delete pInfo;
  98. }
  99. fPrograms.clear();
  100. }
  101. void init(Master& master)
  102. {
  103. if (fInitiated)
  104. return;
  105. fInitiated = true;
  106. fPrograms.append(new ProgramInfo(0, 0, "default"));
  107. pthread_mutex_lock(&master.mutex);
  108. // refresh banks
  109. master.bank.rescanforbanks();
  110. for (uint32_t i=0, size = master.bank.banks.size(); i < size; ++i)
  111. {
  112. if (master.bank.banks[i].dir.empty())
  113. continue;
  114. master.bank.loadbank(master.bank.banks[i].dir);
  115. for (unsigned int instrument = 0; instrument < BANK_SIZE; ++instrument)
  116. {
  117. const std::string insName(master.bank.getname(instrument));
  118. if (insName.empty() || insName[0] == '\0' || insName[0] == ' ')
  119. continue;
  120. fPrograms.append(new ProgramInfo(i+1, instrument, insName.c_str()));
  121. }
  122. }
  123. pthread_mutex_unlock(&master.mutex);
  124. }
  125. void load(Master* const master, const uint8_t channel, const uint32_t bank, const uint32_t program)
  126. {
  127. if (bank == 0)
  128. {
  129. pthread_mutex_lock(&master->mutex);
  130. master->partonoff(channel, 1);
  131. master->part[channel]->defaults();
  132. master->part[channel]->applyparameters(false);
  133. pthread_mutex_unlock(&master->mutex);
  134. return;
  135. }
  136. const std::string& bankdir(master->bank.banks[bank-1].dir);
  137. if (! bankdir.empty())
  138. {
  139. pthread_mutex_lock(&master->mutex);
  140. master->partonoff(channel, 1);
  141. master->bank.loadbank(bankdir);
  142. master->bank.loadfromslot(program, master->part[channel]);
  143. master->part[channel]->applyparameters(false);
  144. pthread_mutex_unlock(&master->mutex);
  145. }
  146. }
  147. uint32_t count()
  148. {
  149. return fPrograms.count();
  150. }
  151. const MidiProgram* getInfo(const uint32_t index)
  152. {
  153. if (index >= fPrograms.count())
  154. return nullptr;
  155. const ProgramInfo*& pInfo(fPrograms.getAt(index));
  156. fRetProgram.bank = pInfo->bank;
  157. fRetProgram.program = pInfo->prog;
  158. fRetProgram.name = pInfo->name;
  159. return &fRetProgram;
  160. }
  161. private:
  162. struct ProgramInfo {
  163. uint32_t bank;
  164. uint32_t prog;
  165. const char* name;
  166. ProgramInfo(uint32_t bank_, uint32_t prog_, const char* name_)
  167. : bank(bank_),
  168. prog(prog_),
  169. name(carla_strdup(name_)) {}
  170. ~ProgramInfo()
  171. {
  172. if (name != nullptr)
  173. {
  174. delete[] name;
  175. name = nullptr;
  176. }
  177. }
  178. #ifdef CARLA_PROPER_CPP11_SUPPORT
  179. ProgramInfo() = delete;
  180. ProgramInfo(ProgramInfo&) = delete;
  181. ProgramInfo(const ProgramInfo&) = delete;
  182. #endif
  183. };
  184. bool fInitiated;
  185. MidiProgram fRetProgram;
  186. NonRtList<const ProgramInfo*> fPrograms;
  187. CARLA_DECLARE_NON_COPYABLE(ZynAddSubFxPrograms)
  188. };
  189. static ZynAddSubFxPrograms sPrograms;
  190. // -----------------------------------------------------------------------
  191. class ZynAddSubFxInstanceCount
  192. {
  193. public:
  194. ZynAddSubFxInstanceCount()
  195. : fCount(0)
  196. {
  197. }
  198. ~ZynAddSubFxInstanceCount()
  199. {
  200. CARLA_ASSERT(fCount == 0);
  201. }
  202. void addOne(HostDescriptor* host)
  203. {
  204. if (fCount++ == 0)
  205. {
  206. CARLA_ASSERT(synth == nullptr);
  207. CARLA_ASSERT(denormalkillbuf == nullptr);
  208. reinit(host);
  209. #ifdef WANT_ZYNADDSUBFX_UI
  210. if (gPixmapPath.isEmpty())
  211. {
  212. gPixmapPath = host->resource_dir;
  213. gPixmapPath += PIXMAP_PATH;
  214. gUiPixmapPath = gPixmapPath;
  215. }
  216. #endif
  217. }
  218. }
  219. void removeOne()
  220. {
  221. if (--fCount == 0)
  222. {
  223. CARLA_ASSERT(synth != nullptr);
  224. CARLA_ASSERT(denormalkillbuf != nullptr);
  225. Master::deleteInstance();
  226. delete[] denormalkillbuf;
  227. denormalkillbuf = nullptr;
  228. delete synth;
  229. synth = nullptr;
  230. }
  231. }
  232. void reinit(HostDescriptor* host)
  233. {
  234. Master::deleteInstance();
  235. if (denormalkillbuf != nullptr)
  236. {
  237. delete[] denormalkillbuf;
  238. denormalkillbuf = nullptr;
  239. }
  240. if (synth != nullptr)
  241. {
  242. delete synth;
  243. synth = nullptr;
  244. }
  245. synth = new SYNTH_T();
  246. synth->buffersize = host->get_buffer_size(host->handle);
  247. synth->samplerate = host->get_sample_rate(host->handle);
  248. synth->alias();
  249. config.init();
  250. config.cfg.SoundBufferSize = synth->buffersize;
  251. config.cfg.SampleRate = synth->samplerate;
  252. config.cfg.GzipCompression = 0;
  253. sprng(std::time(nullptr));
  254. denormalkillbuf = new float[synth->buffersize];
  255. for (int i=0; i < synth->buffersize; ++i)
  256. denormalkillbuf[i] = (RND - 0.5f) * 1e-16;
  257. sPrograms.init(Master::getInstance());
  258. }
  259. private:
  260. int fCount;
  261. CARLA_DECLARE_NON_COPYABLE(ZynAddSubFxInstanceCount)
  262. };
  263. static ZynAddSubFxInstanceCount sInstanceCount;
  264. // -----------------------------------------------------------------------
  265. class ZynAddSubFxThread : public QThread
  266. {
  267. public:
  268. ZynAddSubFxThread(Master* const master, const HostDescriptor* const host)
  269. : fMaster(master),
  270. kHost(host),
  271. #ifdef WANT_ZYNADDSUBFX_UI
  272. fUi(nullptr),
  273. fUiClosed(0),
  274. fNextUiAction(-1),
  275. #endif
  276. fQuit(false),
  277. fChangeProgram(false),
  278. fNextChannel(0),
  279. fNextBank(0),
  280. fNextProgram(0)
  281. {
  282. }
  283. ~ZynAddSubFxThread()
  284. {
  285. // must be closed by now
  286. #ifdef WANT_ZYNADDSUBFX_UI
  287. CARLA_ASSERT(fUi == nullptr);
  288. #endif
  289. CARLA_ASSERT(fQuit);
  290. }
  291. void loadProgramLater(const uint8_t channel, const uint32_t bank, const uint32_t program)
  292. {
  293. fNextChannel = channel;
  294. fNextBank = bank;
  295. fNextProgram = program;
  296. fChangeProgram = true;
  297. }
  298. void stopLoadProgramLater()
  299. {
  300. fChangeProgram = false;
  301. fNextChannel = 0;
  302. fNextBank = 0;
  303. fNextProgram = 0;
  304. }
  305. void setMaster(Master* const master)
  306. {
  307. fMaster = master;
  308. }
  309. void stop()
  310. {
  311. fQuit = true;
  312. quit();
  313. }
  314. #ifdef WANT_ZYNADDSUBFX_UI
  315. void uiHide()
  316. {
  317. fNextUiAction = 0;
  318. }
  319. void uiShow()
  320. {
  321. fNextUiAction = 1;
  322. }
  323. void uiRepaint()
  324. {
  325. if (fUi != nullptr)
  326. fNextUiAction = 2;
  327. }
  328. #endif
  329. protected:
  330. void run() override
  331. {
  332. while (! fQuit)
  333. {
  334. #ifdef WANT_ZYNADDSUBFX_UI
  335. Fl::lock();
  336. if (fNextUiAction == 2) // repaint
  337. {
  338. CARLA_ASSERT(fUi != nullptr);
  339. if (fUi != nullptr)
  340. fUi->refresh_master_ui();
  341. }
  342. else if (fNextUiAction == 1) // init/show
  343. {
  344. static bool initialized = false;
  345. if (! initialized)
  346. {
  347. initialized = true;
  348. fl_register_images();
  349. Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL);
  350. if (Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "knob.png"))
  351. Fl_Dial::default_image(img);
  352. if (Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "window_backdrop.png"))
  353. Fl::scheme_bg(new Fl_Tiled_Image(img));
  354. if(Fl_Shared_Image* const img = Fl_Shared_Image::get(gPixmapPath + "module_backdrop.png"))
  355. gModuleBackdrop = new Fl_Tiled_Image(img);
  356. Fl::background(50, 50, 50);
  357. Fl::background2(70, 70, 70);
  358. Fl::foreground(255, 255, 255);
  359. Fl_Theme::set("Cairo");
  360. }
  361. CARLA_ASSERT(fUi == nullptr);
  362. if (fUi == nullptr)
  363. {
  364. fUiClosed = 0;
  365. fUi = new MasterUI(fMaster, &fUiClosed);
  366. fUi->showUI();
  367. }
  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 (fQuit && 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 HostDescriptor* const kHost;
  424. #ifdef WANT_ZYNADDSUBFX_UI
  425. MasterUI* fUi;
  426. int fUiClosed;
  427. int fNextUiAction;
  428. #endif
  429. bool fQuit;
  430. bool fChangeProgram;
  431. uint8_t fNextChannel;
  432. uint32_t fNextBank;
  433. uint32_t fNextProgram;
  434. };
  435. // -----------------------------------------------------------------------
  436. class ZynAddSubFxPlugin : public PluginDescriptorClass
  437. {
  438. public:
  439. enum Parameters {
  440. PARAMETER_COUNT = 0
  441. };
  442. ZynAddSubFxPlugin(const HostDescriptor* const host)
  443. : PluginDescriptorClass(host),
  444. fMaster(new Master()),
  445. fSampleRate(getSampleRate()),
  446. fIsActive(false),
  447. fThread(fMaster, host)
  448. {
  449. fThread.start();
  450. for (int i = 0; i < NUM_MIDI_PARTS; ++i)
  451. fMaster->partonoff(i, 1);
  452. }
  453. ~ZynAddSubFxPlugin() override
  454. {
  455. //ensure that everything has stopped
  456. pthread_mutex_lock(&fMaster->mutex);
  457. pthread_mutex_unlock(&fMaster->mutex);
  458. fThread.stop();
  459. fThread.wait();
  460. delete fMaster;
  461. fMaster = nullptr;
  462. }
  463. protected:
  464. // -------------------------------------------------------------------
  465. // Plugin parameter calls
  466. uint32_t getParameterCount() override
  467. {
  468. return PARAMETER_COUNT;
  469. }
  470. const Parameter* getParameterInfo(const uint32_t index) override
  471. {
  472. CARLA_ASSERT(index < getParameterCount());
  473. //if (index >= PARAMETER_COUNT)
  474. return nullptr;
  475. static Parameter param;
  476. param.ranges.step = PARAMETER_RANGES_DEFAULT_STEP;
  477. param.ranges.stepSmall = PARAMETER_RANGES_DEFAULT_STEP_SMALL;
  478. param.ranges.stepLarge = PARAMETER_RANGES_DEFAULT_STEP_LARGE;
  479. param.scalePointCount = 0;
  480. param.scalePoints = nullptr;
  481. switch (index)
  482. {
  483. #if 0
  484. case PARAMETER_MASTER:
  485. param.hints = PARAMETER_IS_ENABLED | PARAMETER_IS_AUTOMABLE;
  486. param.name = "Master Volume";
  487. param.unit = nullptr;
  488. param.ranges.min = 0.0f;
  489. param.ranges.max = 100.0f;
  490. param.ranges.def = 100.0f;
  491. break;
  492. #endif
  493. }
  494. return &param;
  495. }
  496. float getParameterValue(const uint32_t index) override
  497. {
  498. CARLA_ASSERT(index < getParameterCount());
  499. switch (index)
  500. {
  501. #if 0
  502. case PARAMETER_MASTER:
  503. return fMaster->Pvolume;
  504. #endif
  505. default:
  506. return 0.0f;
  507. }
  508. }
  509. // -------------------------------------------------------------------
  510. // Plugin midi-program calls
  511. uint32_t getMidiProgramCount() override
  512. {
  513. return sPrograms.count();
  514. }
  515. const MidiProgram* getMidiProgramInfo(const uint32_t index) override
  516. {
  517. return sPrograms.getInfo(index);
  518. }
  519. // -------------------------------------------------------------------
  520. // Plugin state calls
  521. void setParameterValue(const uint32_t index, const float value) override
  522. {
  523. CARLA_ASSERT(index < getParameterCount());
  524. switch (index)
  525. {
  526. }
  527. return;
  528. // unused, TODO
  529. (void)value;
  530. }
  531. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  532. {
  533. if (bank >= fMaster->bank.banks.size())
  534. return;
  535. if (program >= BANK_SIZE)
  536. return;
  537. if (isOffline() || ! fIsActive)
  538. {
  539. sPrograms.load(fMaster, channel, bank, program);
  540. #ifdef WANT_ZYNADDSUBFX_UI
  541. fThread.uiRepaint();
  542. #endif
  543. }
  544. else
  545. fThread.loadProgramLater(channel, bank, program);
  546. }
  547. void setCustomData(const char* const key, const char* const value) override
  548. {
  549. CARLA_ASSERT(key != nullptr);
  550. CARLA_ASSERT(value != nullptr);
  551. if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  552. fMaster->loadXML(value);
  553. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  554. fMaster->part[0]->loadXMLinstrument(value);
  555. }
  556. // -------------------------------------------------------------------
  557. // Plugin process calls
  558. void activate() override
  559. {
  560. fIsActive = true;
  561. }
  562. void deactivate() override
  563. {
  564. fIsActive = false;
  565. }
  566. void process(float**, float** const outBuffer, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents) override
  567. {
  568. if (pthread_mutex_trylock(&fMaster->mutex) != 0)
  569. {
  570. carla_zeroFloat(outBuffer[0], frames);
  571. carla_zeroFloat(outBuffer[1], frames);
  572. return;
  573. }
  574. for (uint32_t i=0; i < midiEventCount; ++i)
  575. {
  576. const MidiEvent* const midiEvent(&midiEvents[i]);
  577. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  578. const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  579. if (MIDI_IS_STATUS_NOTE_OFF(status))
  580. {
  581. const uint8_t note = midiEvent->data[1];
  582. fMaster->noteOff(channel, note);
  583. }
  584. else if (MIDI_IS_STATUS_NOTE_ON(status))
  585. {
  586. const uint8_t note = midiEvent->data[1];
  587. const uint8_t velo = midiEvent->data[2];
  588. fMaster->noteOn(channel, note, velo);
  589. }
  590. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  591. {
  592. const uint8_t note = midiEvent->data[1];
  593. const uint8_t pressure = midiEvent->data[2];
  594. fMaster->polyphonicAftertouch(channel, note, pressure);
  595. }
  596. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  597. {
  598. const uint8_t control = midiEvent->data[1];
  599. const uint8_t value = midiEvent->data[2];
  600. fMaster->setController(channel, control, value);
  601. }
  602. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  603. {
  604. const uint8_t lsb = midiEvent->data[1];
  605. const uint8_t msb = midiEvent->data[2];
  606. const int value = ((msb << 7) | lsb) - 8192;
  607. fMaster->setController(channel, C_pitchwheel, value);
  608. }
  609. }
  610. fMaster->GetAudioOutSamples(frames, fSampleRate, outBuffer[0], outBuffer[1]);
  611. pthread_mutex_unlock(&fMaster->mutex);
  612. }
  613. #ifdef WANT_ZYNADDSUBFX_UI
  614. // -------------------------------------------------------------------
  615. // Plugin UI calls
  616. void uiShow(const bool show) override
  617. {
  618. if (show)
  619. fThread.uiShow();
  620. else
  621. fThread.uiHide();
  622. }
  623. #endif
  624. // -------------------------------------------------------------------
  625. // Plugin state calls
  626. char* getState() override
  627. {
  628. config.save();
  629. char* data = nullptr;
  630. fMaster->getalldata(&data);
  631. return data;
  632. }
  633. void setState(const char* const data) override
  634. {
  635. fThread.stopLoadProgramLater();
  636. fMaster->putalldata((char*)data, 0);
  637. fMaster->applyparameters(true);
  638. }
  639. // -------------------------------------------------------------------
  640. // Plugin dispatcher
  641. intptr_t pluginDispatcher(const PluginDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr) override
  642. {
  643. switch (opcode)
  644. {
  645. case PLUGIN_OPCODE_NULL:
  646. break;
  647. case PLUGIN_OPCODE_BUFFER_SIZE_CHANGED:
  648. // TODO
  649. break;
  650. case PLUGIN_OPCODE_SAMPLE_RATE_CHANGED:
  651. // TODO
  652. break;
  653. case PLUGIN_OPCODE_OFFLINE_CHANGED:
  654. break;
  655. case PLUGIN_OPCODE_UI_NAME_CHANGED:
  656. #ifdef WANT_ZYNADDSUBFX_UI
  657. // TODO
  658. #endif
  659. break;
  660. }
  661. return 0;
  662. // unused
  663. (void)index;
  664. (void)value;
  665. (void)ptr;
  666. }
  667. // -------------------------------------------------------------------
  668. private:
  669. Master* fMaster;
  670. unsigned fSampleRate;
  671. bool fIsActive;
  672. ZynAddSubFxThread fThread;
  673. public:
  674. static PluginHandle _instantiate(const PluginDescriptor*, HostDescriptor* host)
  675. {
  676. sInstanceCount.addOne(host);
  677. return new ZynAddSubFxPlugin(host);
  678. }
  679. static void _cleanup(PluginHandle handle)
  680. {
  681. delete (ZynAddSubFxPlugin*)handle;
  682. sInstanceCount.removeOne();
  683. }
  684. private:
  685. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  686. };
  687. // -----------------------------------------------------------------------
  688. static const PluginDescriptor zynaddsubfxDesc = {
  689. /* category */ PLUGIN_CATEGORY_SYNTH,
  690. #ifdef WANT_ZYNADDSUBFX_UI
  691. /* hints */ static_cast<PluginHints>(PLUGIN_IS_SYNTH|PLUGIN_HAS_GUI|PLUGIN_USES_STATE),
  692. #else
  693. /* hints */ static_cast<PluginHints>(PLUGIN_IS_SYNTH|PLUGIN_USES_STATE),
  694. #endif
  695. /* audioIns */ 0,
  696. /* audioOuts */ 2,
  697. /* midiIns */ 1,
  698. /* midiOuts */ 0,
  699. /* paramIns */ ZynAddSubFxPlugin::PARAMETER_COUNT,
  700. /* paramOuts */ 0,
  701. /* name */ "ZynAddSubFX",
  702. /* label */ "zynaddsubfx",
  703. /* maker */ "falkTX",
  704. /* copyright */ "GNU GPL v2+",
  705. PluginDescriptorFILL(ZynAddSubFxPlugin)
  706. };
  707. // -----------------------------------------------------------------------
  708. void carla_register_native_plugin_zynaddsubfx()
  709. {
  710. carla_register_native_plugin(&zynaddsubfxDesc);
  711. }
  712. // -----------------------------------------------------------------------