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.

1061 lines
31KB

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