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.

1097 lines
32KB

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