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.

942 lines
28KB

  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 "Misc/Master.h"
  23. #include "Misc/MiddleWare.h"
  24. #include "Misc/Part.h"
  25. #include "Misc/Util.h"
  26. #include <ctime>
  27. #include <set>
  28. #include <string>
  29. #include "juce_audio_basics.h"
  30. using juce::roundToIntAccurate;
  31. using juce::FloatVectorOperations;
  32. // -----------------------------------------------------------------------
  33. class ZynAddSubFxPrograms
  34. {
  35. public:
  36. ZynAddSubFxPrograms() noexcept
  37. : fInitiated(false),
  38. #ifdef CARLA_PROPER_CPP11_SUPPORT
  39. fRetProgram({0, 0, nullptr}),
  40. #endif
  41. fPrograms() {}
  42. ~ZynAddSubFxPrograms() noexcept
  43. {
  44. if (! fInitiated)
  45. return;
  46. for (LinkedList<const ProgramInfo*>::Itenerator it = fPrograms.begin2(); it.valid(); it.next())
  47. {
  48. const ProgramInfo* const& pInfo(it.getValue(nullptr));
  49. delete pInfo;
  50. }
  51. fPrograms.clear();
  52. }
  53. void initIfNeeded()
  54. {
  55. if (fInitiated)
  56. return;
  57. fInitiated = true;
  58. fPrograms.append(new ProgramInfo(0, 0, "default", ""));
  59. Config config;
  60. config.init();
  61. SYNTH_T synth;
  62. Master master(synth, &config);
  63. // refresh banks
  64. master.bank.rescanforbanks();
  65. for (std::size_t i=0, size=master.bank.banks.size(); i<size; ++i)
  66. {
  67. const std::string dir(master.bank.banks[i].dir);
  68. if (dir.empty())
  69. continue;
  70. master.bank.loadbank(dir);
  71. for (uint ninstrument = 0; ninstrument < BANK_SIZE; ++ninstrument)
  72. {
  73. const Bank::ins_t& instrument(master.bank.ins[ninstrument]);
  74. if (instrument.name.empty() || instrument.name[0] == ' ')
  75. continue;
  76. fPrograms.append(new ProgramInfo(i+1, ninstrument, instrument.name.c_str(), instrument.filename.c_str()));
  77. }
  78. }
  79. }
  80. uint32_t getNativeMidiProgramCount() const noexcept
  81. {
  82. return static_cast<uint32_t>(fPrograms.count());
  83. }
  84. const NativeMidiProgram* getNativeMidiProgramInfo(const uint32_t index) const noexcept
  85. {
  86. if (index >= fPrograms.count())
  87. return nullptr;
  88. const ProgramInfo* const pInfo(fPrograms.getAt(index, nullptr));
  89. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr, nullptr);
  90. fRetProgram.bank = pInfo->bank;
  91. fRetProgram.program = pInfo->prog;
  92. fRetProgram.name = pInfo->name;
  93. return &fRetProgram;
  94. }
  95. const char* getZynProgramFilename(const uint32_t bank, const uint32_t program) const noexcept
  96. {
  97. for (LinkedList<const ProgramInfo*>::Itenerator it = fPrograms.begin2(); it.valid(); it.next())
  98. {
  99. const ProgramInfo* const& pInfo(it.getValue(nullptr));
  100. if (pInfo->bank != bank)
  101. continue;
  102. if (pInfo->prog != program)
  103. continue;
  104. return pInfo->filename;
  105. }
  106. return nullptr;
  107. }
  108. private:
  109. struct ProgramInfo {
  110. uint32_t bank;
  111. uint32_t prog;
  112. const char* name;
  113. const char* filename;
  114. ProgramInfo(uint32_t b, uint32_t p, const char* n, const char* fn) noexcept
  115. : bank(b),
  116. prog(p),
  117. name(carla_strdup_safe(n)),
  118. filename(carla_strdup_safe(fn)) {}
  119. ~ProgramInfo() noexcept
  120. {
  121. if (name != nullptr)
  122. {
  123. delete[] name;
  124. name = nullptr;
  125. }
  126. if (filename != nullptr)
  127. {
  128. delete[] filename;
  129. filename = nullptr;
  130. }
  131. }
  132. #ifdef CARLA_PROPER_CPP11_SUPPORT
  133. ProgramInfo() = delete;
  134. ProgramInfo(ProgramInfo&) = delete;
  135. ProgramInfo(const ProgramInfo&) = delete;
  136. ProgramInfo& operator=(ProgramInfo&);
  137. ProgramInfo& operator=(const ProgramInfo&);
  138. #endif
  139. };
  140. bool fInitiated;
  141. mutable NativeMidiProgram fRetProgram;
  142. LinkedList<const ProgramInfo*> fPrograms;
  143. CARLA_PREVENT_HEAP_ALLOCATION
  144. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
  145. };
  146. static ZynAddSubFxPrograms sPrograms;
  147. // -----------------------------------------------------------------------
  148. class ZynAddSubFxPlugin : public NativePluginAndUiClass
  149. {
  150. public:
  151. enum Parameters {
  152. kParamPart01Enabled ,
  153. kParamPart16Enabled = kParamPart01Enabled + 15,
  154. kParamPart01Volume,
  155. kParamPart16Volume = kParamPart01Volume + 15,
  156. kParamPart01Panning,
  157. kParamPart16Panning = kParamPart01Panning + 15,
  158. kParamFilterCutoff, // Filter Frequency
  159. kParamFilterQ, // Filter Resonance
  160. kParamBandwidth, // Bandwidth
  161. kParamModAmp, // FM Gain
  162. kParamResCenter, // Resonance center frequency
  163. kParamResBandwidth, // Resonance bandwidth
  164. kParamCount
  165. };
  166. ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
  167. : NativePluginAndUiClass(host, "zynaddsubfx-ui"),
  168. fMiddleWare(nullptr),
  169. fMaster(nullptr),
  170. fSynth(),
  171. fIsActive(false),
  172. fMutex()
  173. {
  174. sPrograms.initIfNeeded();
  175. fConfig.init();
  176. // init parameters to default
  177. fParameters[kParamPart01Enabled] = 1.0f;
  178. for (int i=kParamPart16Enabled+1; --i>kParamPart01Enabled;)
  179. fParameters[i] = 0.0f;
  180. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  181. fParameters[i] = 100.0f;
  182. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  183. fParameters[i] = 64.0f;
  184. fParameters[kParamFilterCutoff] = 64.0f;
  185. fParameters[kParamFilterQ] = 64.0f;
  186. fParameters[kParamBandwidth] = 64.0f;
  187. fParameters[kParamModAmp] = 127.0f;
  188. fParameters[kParamResCenter] = 64.0f;
  189. fParameters[kParamResBandwidth] = 64.0f;
  190. fSynth.buffersize = static_cast<int>(getBufferSize());
  191. fSynth.samplerate = static_cast<uint>(getSampleRate());
  192. //if (fSynth.buffersize > 32)
  193. // fSynth.buffersize = 32;
  194. fSynth.alias();
  195. _initMaster();
  196. _setMasterParameters();
  197. }
  198. ~ZynAddSubFxPlugin() override
  199. {
  200. _deleteMaster();
  201. }
  202. protected:
  203. // -------------------------------------------------------------------
  204. // Plugin parameter calls
  205. uint32_t getParameterCount() const final
  206. {
  207. return kParamCount;
  208. }
  209. const NativeParameter* getParameterInfo(const uint32_t index) const override
  210. {
  211. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  212. static NativeParameter param;
  213. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  214. param.name = nullptr;
  215. param.unit = nullptr;
  216. param.ranges.def = 64.0f;
  217. param.ranges.min = 0.0f;
  218. param.ranges.max = 127.0f;
  219. param.ranges.step = 1.0f;
  220. param.ranges.stepSmall = 1.0f;
  221. param.ranges.stepLarge = 20.0f;
  222. param.scalePointCount = 0;
  223. param.scalePoints = nullptr;
  224. if (index <= kParamPart16Enabled)
  225. {
  226. hints |= NATIVE_PARAMETER_IS_BOOLEAN;
  227. param.ranges.def = 0.0f;
  228. param.ranges.min = 0.0f;
  229. param.ranges.max = 1.0f;
  230. param.ranges.step = 1.0f;
  231. param.ranges.stepSmall = 1.0f;
  232. param.ranges.stepLarge = 1.0f;
  233. #define PARAM_PART_ENABLE_DESC(N) \
  234. case kParamPart01Enabled + N - 1: \
  235. param.name = "Part " #N " Enabled"; break;
  236. switch (index)
  237. {
  238. case kParamPart01Enabled:
  239. param.name = "Part 01 Enabled";
  240. param.ranges.def = 1.0f;
  241. break;
  242. PARAM_PART_ENABLE_DESC( 2)
  243. PARAM_PART_ENABLE_DESC( 3)
  244. PARAM_PART_ENABLE_DESC( 4)
  245. PARAM_PART_ENABLE_DESC( 5)
  246. PARAM_PART_ENABLE_DESC( 6)
  247. PARAM_PART_ENABLE_DESC( 7)
  248. PARAM_PART_ENABLE_DESC( 8)
  249. PARAM_PART_ENABLE_DESC( 9)
  250. PARAM_PART_ENABLE_DESC(10)
  251. PARAM_PART_ENABLE_DESC(11)
  252. PARAM_PART_ENABLE_DESC(12)
  253. PARAM_PART_ENABLE_DESC(13)
  254. PARAM_PART_ENABLE_DESC(14)
  255. PARAM_PART_ENABLE_DESC(15)
  256. PARAM_PART_ENABLE_DESC(16)
  257. }
  258. #undef PARAM_PART_ENABLE_DESC
  259. }
  260. else if (index <= kParamPart16Volume)
  261. {
  262. hints |= NATIVE_PARAMETER_IS_INTEGER;
  263. param.ranges.def = 100.0f;
  264. #define PARAM_PART_ENABLE_DESC(N) \
  265. case kParamPart01Volume + N - 1: \
  266. param.name = "Part " #N " Volume"; break;
  267. switch (index)
  268. {
  269. PARAM_PART_ENABLE_DESC( 1)
  270. PARAM_PART_ENABLE_DESC( 2)
  271. PARAM_PART_ENABLE_DESC( 3)
  272. PARAM_PART_ENABLE_DESC( 4)
  273. PARAM_PART_ENABLE_DESC( 5)
  274. PARAM_PART_ENABLE_DESC( 6)
  275. PARAM_PART_ENABLE_DESC( 7)
  276. PARAM_PART_ENABLE_DESC( 8)
  277. PARAM_PART_ENABLE_DESC( 9)
  278. PARAM_PART_ENABLE_DESC(10)
  279. PARAM_PART_ENABLE_DESC(11)
  280. PARAM_PART_ENABLE_DESC(12)
  281. PARAM_PART_ENABLE_DESC(13)
  282. PARAM_PART_ENABLE_DESC(14)
  283. PARAM_PART_ENABLE_DESC(15)
  284. PARAM_PART_ENABLE_DESC(16)
  285. }
  286. #undef PARAM_PART_ENABLE_DESC
  287. }
  288. else if (index <= kParamPart16Panning)
  289. {
  290. hints |= NATIVE_PARAMETER_IS_INTEGER;
  291. #define PARAM_PART_ENABLE_DESC(N) \
  292. case kParamPart01Panning + N - 1: \
  293. param.name = "Part " #N " Panning"; break;
  294. switch (index)
  295. {
  296. PARAM_PART_ENABLE_DESC( 1)
  297. PARAM_PART_ENABLE_DESC( 2)
  298. PARAM_PART_ENABLE_DESC( 3)
  299. PARAM_PART_ENABLE_DESC( 4)
  300. PARAM_PART_ENABLE_DESC( 5)
  301. PARAM_PART_ENABLE_DESC( 6)
  302. PARAM_PART_ENABLE_DESC( 7)
  303. PARAM_PART_ENABLE_DESC( 8)
  304. PARAM_PART_ENABLE_DESC( 9)
  305. PARAM_PART_ENABLE_DESC(10)
  306. PARAM_PART_ENABLE_DESC(11)
  307. PARAM_PART_ENABLE_DESC(12)
  308. PARAM_PART_ENABLE_DESC(13)
  309. PARAM_PART_ENABLE_DESC(14)
  310. PARAM_PART_ENABLE_DESC(15)
  311. PARAM_PART_ENABLE_DESC(16)
  312. }
  313. #undef PARAM_PART_ENABLE_DESC
  314. }
  315. else if (index <= kParamResBandwidth)
  316. {
  317. hints |= NATIVE_PARAMETER_IS_INTEGER;
  318. switch (index)
  319. {
  320. case kParamFilterCutoff:
  321. param.name = "Filter Cutoff";
  322. break;
  323. case kParamFilterQ:
  324. param.name = "Filter Q";
  325. break;
  326. case kParamBandwidth:
  327. param.name = "Bandwidth";
  328. break;
  329. case kParamModAmp:
  330. param.name = "FM Gain";
  331. param.ranges.def = 127.0f;
  332. break;
  333. case kParamResCenter:
  334. param.name = "Res Center Freq";
  335. break;
  336. case kParamResBandwidth:
  337. param.name = "Res Bandwidth";
  338. break;
  339. }
  340. }
  341. param.hints = static_cast<NativeParameterHints>(hints);
  342. return &param;
  343. }
  344. float getParameterValue(const uint32_t index) const final
  345. {
  346. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  347. return fParameters[index];
  348. }
  349. // -------------------------------------------------------------------
  350. // Plugin midi-program calls
  351. uint32_t getMidiProgramCount() const noexcept override
  352. {
  353. return sPrograms.getNativeMidiProgramCount();
  354. }
  355. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
  356. {
  357. return sPrograms.getNativeMidiProgramInfo(index);
  358. }
  359. // -------------------------------------------------------------------
  360. // Plugin state calls
  361. void setParameterValue(const uint32_t index, const float value) final
  362. {
  363. CARLA_SAFE_ASSERT_RETURN(index < kParamCount,);
  364. if (index <= kParamPart16Enabled)
  365. {
  366. fParameters[index] = (value >= 0.5f) ? 1.0f : 0.0f;
  367. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  368. fMiddleWare->activeUrl("");
  369. char msg[24];
  370. std::sprintf(msg, "/part%i/Penabled", index-kParamPart01Enabled);
  371. fMiddleWare->transmitMsg(msg, (value >= 0.5f) ? "T" : "F");
  372. }
  373. else if (index <= kParamPart16Volume)
  374. {
  375. if (carla_isEqual(fParameters[index], value))
  376. return;
  377. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  378. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  379. fMiddleWare->activeUrl("");
  380. char msg[24];
  381. std::sprintf(msg, "/part%i/Pvolume", index-kParamPart01Volume);
  382. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  383. }
  384. else if (index <= kParamPart16Panning)
  385. {
  386. if (carla_isEqual(fParameters[index], value))
  387. return;
  388. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  389. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  390. fMiddleWare->activeUrl("");
  391. char msg[24];
  392. std::sprintf(msg, "/part%i/Ppanning", index-kParamPart01Panning);
  393. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  394. }
  395. else if (index <= kParamResBandwidth)
  396. {
  397. const MidiControllers zynControl(getZynControlFromIndex(index));
  398. CARLA_SAFE_ASSERT_RETURN(zynControl != C_NULL,);
  399. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  400. for (int npart=0; npart<NUM_MIDI_PARTS; ++npart)
  401. {
  402. if (fMaster->part[npart] != nullptr)
  403. fMaster->part[npart]->SetController(zynControl, static_cast<int>(value));
  404. }
  405. }
  406. }
  407. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  408. {
  409. CARLA_SAFE_ASSERT_RETURN(program < BANK_SIZE,);
  410. if (bank == 0)
  411. {
  412. // reset part to default
  413. // TODO
  414. return;
  415. }
  416. const char* const filename(sPrograms.getZynProgramFilename(bank, program));
  417. CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0',);
  418. fMiddleWare->transmitMsg("/load-part", "is", channel, filename);
  419. }
  420. void setCustomData(const char* const key, const char* const value) override
  421. {
  422. CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
  423. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  424. /**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  425. {
  426. fMiddleWare->transmitMsg("/load_xmz", "s", value);
  427. }
  428. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  429. {
  430. fMiddleWare->transmitMsg("/load_xiz", "is", 0, value);
  431. }
  432. }
  433. // -------------------------------------------------------------------
  434. // Plugin process calls
  435. void activate() override
  436. {
  437. fIsActive = true;
  438. }
  439. void deactivate() override
  440. {
  441. fIsActive = false;
  442. }
  443. void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  444. {
  445. if (! fMutex.tryLock())
  446. {
  447. if (! isOffline())
  448. {
  449. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  450. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  451. return;
  452. }
  453. fMutex.lock();
  454. }
  455. for (uint32_t i=0; i < midiEventCount; ++i)
  456. {
  457. const NativeMidiEvent* const midiEvent(&midiEvents[i]);
  458. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  459. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  460. if (MIDI_IS_STATUS_NOTE_OFF(status))
  461. {
  462. const char note = static_cast<char>(midiEvent->data[1]);
  463. fMaster->noteOff(channel, note);
  464. }
  465. else if (MIDI_IS_STATUS_NOTE_ON(status))
  466. {
  467. const char note = static_cast<char>(midiEvent->data[1]);
  468. const char velo = static_cast<char>(midiEvent->data[2]);
  469. fMaster->noteOn(channel, note, velo);
  470. }
  471. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  472. {
  473. const char note = static_cast<char>(midiEvent->data[1]);
  474. const char pressure = static_cast<char>(midiEvent->data[2]);
  475. fMaster->polyphonicAftertouch(channel, note, pressure);
  476. }
  477. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  478. {
  479. // skip controls which we map to parameters
  480. if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount)
  481. continue;
  482. const int control = midiEvent->data[1];
  483. const int value = midiEvent->data[2];
  484. fMaster->setController(channel, control, value);
  485. }
  486. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  487. {
  488. const uint8_t lsb = midiEvent->data[1];
  489. const uint8_t msb = midiEvent->data[2];
  490. const int value = ((msb << 7) | lsb) - 8192;
  491. fMaster->setController(channel, C_pitchwheel, value);
  492. }
  493. }
  494. fMaster->GetAudioOutSamples(frames, fSynth.samplerate, outBuffer[0], outBuffer[1]);
  495. fMutex.unlock();
  496. }
  497. // -------------------------------------------------------------------
  498. // Plugin UI calls
  499. #ifdef HAVE_ZYN_UI_DEPS
  500. void uiShow(const bool show) override
  501. {
  502. if (show)
  503. {
  504. if (isPipeRunning())
  505. {
  506. const CarlaMutexLocker cml(getPipeLock());
  507. writeMessage("focus\n", 6);
  508. flushMessages();
  509. return;
  510. }
  511. carla_stdout("Trying to start UI using \"%s\"", getExtUiPath());
  512. CarlaExternalUI::setData(getExtUiPath(), fMiddleWare->getServerAddress(), getUiName());
  513. if (! CarlaExternalUI::startPipeServer(true))
  514. {
  515. uiClosed();
  516. hostUiUnavailable();
  517. }
  518. }
  519. else
  520. {
  521. CarlaExternalUI::stopPipeServer(2000);
  522. }
  523. }
  524. void uiIdle() override
  525. {
  526. NativePluginAndUiClass::uiIdle();
  527. if (isPipeRunning())
  528. fMiddleWare->tick();
  529. }
  530. #endif
  531. // -------------------------------------------------------------------
  532. // Plugin state calls
  533. char* getState() const override
  534. {
  535. char* data = nullptr;
  536. if (fIsActive)
  537. {
  538. fMiddleWare->doReadOnlyOp([this, &data]{
  539. fMaster->getalldata(&data);
  540. });
  541. }
  542. else
  543. {
  544. fMaster->getalldata(&data);
  545. }
  546. return data;
  547. }
  548. void setState(const char* const data) override
  549. {
  550. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  551. const CarlaMutexLocker cml(fMutex);
  552. fMaster->putalldata(data);
  553. fMaster->applyparameters();
  554. fMiddleWare->updateResources(fMaster);
  555. _setMasterParameters();
  556. }
  557. // -------------------------------------------------------------------
  558. // Plugin dispatcher
  559. void bufferSizeChanged(const uint32_t bufferSize) final
  560. {
  561. char* const state(getState());
  562. _deleteMaster();
  563. fSynth.buffersize = static_cast<int>(bufferSize);
  564. fSynth.alias();
  565. _initMaster();
  566. setState(state);
  567. std::free(state);
  568. }
  569. void sampleRateChanged(const double sampleRate) final
  570. {
  571. char* const state(getState());
  572. _deleteMaster();
  573. fSynth.samplerate = static_cast<uint>(sampleRate);
  574. fSynth.alias();
  575. _initMaster();
  576. setState(state);
  577. std::free(state);
  578. }
  579. // -------------------------------------------------------------------
  580. private:
  581. MiddleWare* fMiddleWare;
  582. Master* fMaster;
  583. SYNTH_T fSynth;
  584. Config fConfig;
  585. bool fIsActive;
  586. float fParameters[kParamCount];
  587. CarlaMutex fMutex;
  588. static MidiControllers getZynControlFromIndex(const uint index)
  589. {
  590. switch (index)
  591. {
  592. case kParamFilterCutoff:
  593. return C_filtercutoff;
  594. case kParamFilterQ:
  595. return C_filterq;
  596. case kParamBandwidth:
  597. return C_bandwidth;
  598. case kParamModAmp:
  599. return C_fmamp;
  600. case kParamResCenter:
  601. return C_resonance_center;
  602. case kParamResBandwidth:
  603. return C_resonance_bandwidth;
  604. default:
  605. return C_NULL;
  606. }
  607. }
  608. static Parameters getIndexFromZynControl(const uint8_t control)
  609. {
  610. switch (control)
  611. {
  612. case C_filtercutoff:
  613. return kParamFilterCutoff;
  614. case C_filterq:
  615. return kParamFilterQ;
  616. case C_bandwidth:
  617. return kParamBandwidth;
  618. case C_fmamp:
  619. return kParamModAmp;
  620. case C_resonance_center:
  621. return kParamResCenter;
  622. case C_resonance_bandwidth:
  623. return kParamResBandwidth;
  624. default:
  625. return kParamCount;
  626. }
  627. }
  628. // -------------------------------------------------------------------
  629. void _initMaster()
  630. {
  631. fMiddleWare = new MiddleWare(std::move(fSynth), &fConfig);
  632. fMiddleWare->setUiCallback(__uiCallback, this);
  633. fMiddleWare->setIdleCallback(_idleCallback, this);
  634. _masterChangedCallback(fMiddleWare->spawnMaster());
  635. }
  636. void _setMasterParameters()
  637. {
  638. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  639. fMiddleWare->activeUrl("");
  640. for (int i=kParamPart16Enabled+1; --i>=kParamPart01Enabled;)
  641. {
  642. char msg[24];
  643. std::sprintf(msg, "/part%i/Penabled", i-kParamPart01Enabled);
  644. fMiddleWare->transmitMsg(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  645. }
  646. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  647. {
  648. char msg[24];
  649. std::sprintf(msg, "/part%i/Pvolume", i-kParamPart01Volume);
  650. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  651. }
  652. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  653. {
  654. char msg[24];
  655. std::sprintf(msg, "/part%i/Ppanning", i-kParamPart01Panning);
  656. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  657. }
  658. for (int i=0; i<NUM_MIDI_PARTS; ++i)
  659. {
  660. fMaster->part[i]->SetController(C_filtercutoff, static_cast<int>(fParameters[kParamFilterCutoff]));
  661. fMaster->part[i]->SetController(C_filterq, static_cast<int>(fParameters[kParamFilterQ]));
  662. fMaster->part[i]->SetController(C_bandwidth, static_cast<int>(fParameters[kParamBandwidth]));
  663. fMaster->part[i]->SetController(C_fmamp, static_cast<int>(fParameters[kParamModAmp]));
  664. fMaster->part[i]->SetController(C_resonance_center, static_cast<int>(fParameters[kParamResCenter]));
  665. fMaster->part[i]->SetController(C_resonance_bandwidth, static_cast<int>(fParameters[kParamResBandwidth]));
  666. }
  667. }
  668. void _deleteMaster()
  669. {
  670. fMaster = nullptr;
  671. delete fMiddleWare;
  672. fMiddleWare = nullptr;
  673. }
  674. void _masterChangedCallback(Master* m)
  675. {
  676. fMaster = m;
  677. fMaster->setMasterChangedCallback(__masterChangedCallback, this);
  678. }
  679. static void __masterChangedCallback(void* ptr, Master* m)
  680. {
  681. ((ZynAddSubFxPlugin*)ptr)->_masterChangedCallback(m);
  682. }
  683. void _uiCallback(const char* const msg)
  684. {
  685. if (std::strncmp(msg, "/part", 5) != 0)
  686. return;
  687. const char* msgtmp = msg;
  688. msgtmp += 5;
  689. CARLA_SAFE_ASSERT_RETURN( msgtmp[0] >= '0' && msgtmp[0] <= '9',);
  690. CARLA_SAFE_ASSERT_RETURN((msgtmp[1] >= '0' && msgtmp[1] <= '9') || msgtmp[1] == '/',);
  691. char partnstr[3] = { '\0', '\0', '\0' };
  692. partnstr[0] = msgtmp[0];
  693. ++msgtmp;
  694. if (msgtmp[0] >= '0' && msgtmp[0] <= '9')
  695. {
  696. partnstr[1] = msgtmp[0];
  697. ++msgtmp;
  698. }
  699. const int partn = std::atoi(partnstr);
  700. ++msgtmp;
  701. /**/ if (std::strcmp(msgtmp, "Penabled") == 0)
  702. {
  703. const int index = kParamPart01Enabled+partn;
  704. const bool enbl = rtosc_argument(msg,0).T;
  705. fParameters[index] = enbl ? 1.0f : 0.0f;
  706. uiParameterChanged(kParamPart01Enabled+partn, enbl ? 1.0f : 0.0f);
  707. }
  708. else if (std::strcmp(msgtmp, "Pvolume") == 0)
  709. {
  710. const int index = kParamPart01Volume+partn;
  711. const int value = rtosc_argument(msg,0).i;
  712. fParameters[index] = value;
  713. uiParameterChanged(kParamPart01Volume+partn, value);
  714. }
  715. else if (std::strcmp(msgtmp, "Ppanning") == 0)
  716. {
  717. const int index = kParamPart01Panning+partn;
  718. const int value = rtosc_argument(msg,0).i;
  719. fParameters[index] = value;
  720. uiParameterChanged(kParamPart01Panning+partn, value);
  721. }
  722. }
  723. static void __uiCallback(void* ptr, const char* msg)
  724. {
  725. ((ZynAddSubFxPlugin*)ptr)->_uiCallback(msg);
  726. }
  727. static void _idleCallback(void* ptr)
  728. {
  729. ((ZynAddSubFxPlugin*)ptr)->hostGiveIdle();
  730. }
  731. // -------------------------------------------------------------------
  732. PluginClassEND(ZynAddSubFxPlugin)
  733. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  734. };
  735. // -----------------------------------------------------------------------
  736. static const NativePluginDescriptor zynaddsubfxDesc = {
  737. /* category */ NATIVE_PLUGIN_CATEGORY_SYNTH,
  738. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
  739. #ifdef HAVE_ZYN_UI_DEPS
  740. |NATIVE_PLUGIN_HAS_UI
  741. #endif
  742. |NATIVE_PLUGIN_USES_MULTI_PROGS
  743. |NATIVE_PLUGIN_USES_STATE),
  744. /* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_CONTROL_CHANGES
  745. |NATIVE_PLUGIN_SUPPORTS_NOTE_AFTERTOUCH
  746. |NATIVE_PLUGIN_SUPPORTS_PITCHBEND
  747. |NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  748. /* audioIns */ 0,
  749. /* audioOuts */ 2,
  750. /* midiIns */ 1,
  751. /* midiOuts */ 0,
  752. /* paramIns */ ZynAddSubFxPlugin::kParamCount,
  753. /* paramOuts */ 0,
  754. /* name */ "ZynAddSubFX",
  755. /* label */ "zynaddsubfx",
  756. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  757. /* copyright */ "GNU GPL v2+",
  758. PluginDescriptorFILL(ZynAddSubFxPlugin)
  759. };
  760. // -----------------------------------------------------------------------
  761. CARLA_EXPORT
  762. void carla_register_native_plugin_zynaddsubfx_synth();
  763. CARLA_EXPORT
  764. void carla_register_native_plugin_zynaddsubfx_synth()
  765. {
  766. carla_register_native_plugin(&zynaddsubfxDesc);
  767. }
  768. // -----------------------------------------------------------------------