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.

985 lines
30KB

  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. private CarlaThread
  150. {
  151. public:
  152. enum Parameters {
  153. kParamPart01Enabled ,
  154. kParamPart16Enabled = kParamPart01Enabled + 15,
  155. kParamPart01Volume,
  156. kParamPart16Volume = kParamPart01Volume + 15,
  157. kParamPart01Panning,
  158. kParamPart16Panning = kParamPart01Panning + 15,
  159. kParamFilterCutoff, // Filter Frequency
  160. kParamFilterQ, // Filter Resonance
  161. kParamBandwidth, // Bandwidth
  162. kParamModAmp, // FM Gain
  163. kParamResCenter, // Resonance center frequency
  164. kParamResBandwidth, // Resonance bandwidth
  165. kParamCount
  166. };
  167. ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
  168. : NativePluginAndUiClass(host, "zynaddsubfx-ui"),
  169. CarlaThread("ZynAddSubFxPlugin"),
  170. fMiddleWare(nullptr),
  171. fMaster(nullptr),
  172. fSynth(),
  173. fIsActive(false),
  174. fMutex()
  175. {
  176. sPrograms.initIfNeeded();
  177. fConfig.init();
  178. // init parameters to default
  179. fParameters[kParamPart01Enabled] = 1.0f;
  180. for (int i=kParamPart16Enabled+1; --i>kParamPart01Enabled;)
  181. fParameters[i] = 0.0f;
  182. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  183. fParameters[i] = 100.0f;
  184. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  185. fParameters[i] = 64.0f;
  186. fParameters[kParamFilterCutoff] = 64.0f;
  187. fParameters[kParamFilterQ] = 64.0f;
  188. fParameters[kParamBandwidth] = 64.0f;
  189. fParameters[kParamModAmp] = 127.0f;
  190. fParameters[kParamResCenter] = 64.0f;
  191. fParameters[kParamResBandwidth] = 64.0f;
  192. fSynth.buffersize = static_cast<int>(getBufferSize());
  193. fSynth.samplerate = static_cast<uint>(getSampleRate());
  194. if (fSynth.buffersize > 32)
  195. fSynth.buffersize = 32;
  196. fSynth.alias();
  197. _initMaster();
  198. _setMasterParameters();
  199. }
  200. ~ZynAddSubFxPlugin() override
  201. {
  202. _deleteMaster();
  203. }
  204. protected:
  205. // -------------------------------------------------------------------
  206. // Plugin parameter calls
  207. uint32_t getParameterCount() const final
  208. {
  209. return kParamCount;
  210. }
  211. const NativeParameter* getParameterInfo(const uint32_t index) const override
  212. {
  213. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  214. static NativeParameter param;
  215. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  216. param.name = nullptr;
  217. param.unit = nullptr;
  218. param.ranges.def = 64.0f;
  219. param.ranges.min = 0.0f;
  220. param.ranges.max = 127.0f;
  221. param.ranges.step = 1.0f;
  222. param.ranges.stepSmall = 1.0f;
  223. param.ranges.stepLarge = 20.0f;
  224. param.scalePointCount = 0;
  225. param.scalePoints = nullptr;
  226. if (index <= kParamPart16Enabled)
  227. {
  228. hints |= NATIVE_PARAMETER_IS_BOOLEAN;
  229. param.ranges.def = 0.0f;
  230. param.ranges.min = 0.0f;
  231. param.ranges.max = 1.0f;
  232. param.ranges.step = 1.0f;
  233. param.ranges.stepSmall = 1.0f;
  234. param.ranges.stepLarge = 1.0f;
  235. #define PARAM_PART_ENABLE_DESC(N) \
  236. case kParamPart01Enabled + N - 1: \
  237. param.name = "Part " #N " Enabled"; break;
  238. switch (index)
  239. {
  240. case kParamPart01Enabled:
  241. param.name = "Part 01 Enabled";
  242. param.ranges.def = 1.0f;
  243. break;
  244. PARAM_PART_ENABLE_DESC( 2)
  245. PARAM_PART_ENABLE_DESC( 3)
  246. PARAM_PART_ENABLE_DESC( 4)
  247. PARAM_PART_ENABLE_DESC( 5)
  248. PARAM_PART_ENABLE_DESC( 6)
  249. PARAM_PART_ENABLE_DESC( 7)
  250. PARAM_PART_ENABLE_DESC( 8)
  251. PARAM_PART_ENABLE_DESC( 9)
  252. PARAM_PART_ENABLE_DESC(10)
  253. PARAM_PART_ENABLE_DESC(11)
  254. PARAM_PART_ENABLE_DESC(12)
  255. PARAM_PART_ENABLE_DESC(13)
  256. PARAM_PART_ENABLE_DESC(14)
  257. PARAM_PART_ENABLE_DESC(15)
  258. PARAM_PART_ENABLE_DESC(16)
  259. }
  260. #undef PARAM_PART_ENABLE_DESC
  261. }
  262. else if (index <= kParamPart16Volume)
  263. {
  264. hints |= NATIVE_PARAMETER_IS_INTEGER;
  265. param.ranges.def = 100.0f;
  266. #define PARAM_PART_ENABLE_DESC(N) \
  267. case kParamPart01Volume + N - 1: \
  268. param.name = "Part " #N " Volume"; break;
  269. switch (index)
  270. {
  271. PARAM_PART_ENABLE_DESC( 1)
  272. PARAM_PART_ENABLE_DESC( 2)
  273. PARAM_PART_ENABLE_DESC( 3)
  274. PARAM_PART_ENABLE_DESC( 4)
  275. PARAM_PART_ENABLE_DESC( 5)
  276. PARAM_PART_ENABLE_DESC( 6)
  277. PARAM_PART_ENABLE_DESC( 7)
  278. PARAM_PART_ENABLE_DESC( 8)
  279. PARAM_PART_ENABLE_DESC( 9)
  280. PARAM_PART_ENABLE_DESC(10)
  281. PARAM_PART_ENABLE_DESC(11)
  282. PARAM_PART_ENABLE_DESC(12)
  283. PARAM_PART_ENABLE_DESC(13)
  284. PARAM_PART_ENABLE_DESC(14)
  285. PARAM_PART_ENABLE_DESC(15)
  286. PARAM_PART_ENABLE_DESC(16)
  287. }
  288. #undef PARAM_PART_ENABLE_DESC
  289. }
  290. else if (index <= kParamPart16Panning)
  291. {
  292. hints |= NATIVE_PARAMETER_IS_INTEGER;
  293. #define PARAM_PART_ENABLE_DESC(N) \
  294. case kParamPart01Panning + N - 1: \
  295. param.name = "Part " #N " Panning"; break;
  296. switch (index)
  297. {
  298. PARAM_PART_ENABLE_DESC( 1)
  299. PARAM_PART_ENABLE_DESC( 2)
  300. PARAM_PART_ENABLE_DESC( 3)
  301. PARAM_PART_ENABLE_DESC( 4)
  302. PARAM_PART_ENABLE_DESC( 5)
  303. PARAM_PART_ENABLE_DESC( 6)
  304. PARAM_PART_ENABLE_DESC( 7)
  305. PARAM_PART_ENABLE_DESC( 8)
  306. PARAM_PART_ENABLE_DESC( 9)
  307. PARAM_PART_ENABLE_DESC(10)
  308. PARAM_PART_ENABLE_DESC(11)
  309. PARAM_PART_ENABLE_DESC(12)
  310. PARAM_PART_ENABLE_DESC(13)
  311. PARAM_PART_ENABLE_DESC(14)
  312. PARAM_PART_ENABLE_DESC(15)
  313. PARAM_PART_ENABLE_DESC(16)
  314. }
  315. #undef PARAM_PART_ENABLE_DESC
  316. }
  317. else if (index <= kParamResBandwidth)
  318. {
  319. hints |= NATIVE_PARAMETER_IS_INTEGER;
  320. switch (index)
  321. {
  322. case kParamFilterCutoff:
  323. param.name = "Filter Cutoff";
  324. break;
  325. case kParamFilterQ:
  326. param.name = "Filter Q";
  327. break;
  328. case kParamBandwidth:
  329. param.name = "Bandwidth";
  330. break;
  331. case kParamModAmp:
  332. param.name = "FM Gain";
  333. param.ranges.def = 127.0f;
  334. break;
  335. case kParamResCenter:
  336. param.name = "Res Center Freq";
  337. break;
  338. case kParamResBandwidth:
  339. param.name = "Res Bandwidth";
  340. break;
  341. }
  342. }
  343. param.hints = static_cast<NativeParameterHints>(hints);
  344. return &param;
  345. }
  346. float getParameterValue(const uint32_t index) const final
  347. {
  348. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  349. return fParameters[index];
  350. }
  351. // -------------------------------------------------------------------
  352. // Plugin midi-program calls
  353. uint32_t getMidiProgramCount() const noexcept override
  354. {
  355. return sPrograms.getNativeMidiProgramCount();
  356. }
  357. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
  358. {
  359. return sPrograms.getNativeMidiProgramInfo(index);
  360. }
  361. // -------------------------------------------------------------------
  362. // Plugin state calls
  363. void setParameterValue(const uint32_t index, const float value) final
  364. {
  365. CARLA_SAFE_ASSERT_RETURN(index < kParamCount,);
  366. if (index <= kParamPart16Enabled)
  367. {
  368. fParameters[index] = (value >= 0.5f) ? 1.0f : 0.0f;
  369. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  370. fMiddleWare->activeUrl("");
  371. char msg[24];
  372. std::sprintf(msg, "/part%i/Penabled", index-kParamPart01Enabled);
  373. fMiddleWare->transmitMsg(msg, (value >= 0.5f) ? "T" : "F");
  374. }
  375. else if (index <= kParamPart16Volume)
  376. {
  377. if (carla_isEqual(fParameters[index], value))
  378. return;
  379. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  380. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  381. fMiddleWare->activeUrl("");
  382. char msg[24];
  383. std::sprintf(msg, "/part%i/Pvolume", index-kParamPart01Volume);
  384. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  385. }
  386. else if (index <= kParamPart16Panning)
  387. {
  388. if (carla_isEqual(fParameters[index], value))
  389. return;
  390. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  391. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  392. fMiddleWare->activeUrl("");
  393. char msg[24];
  394. std::sprintf(msg, "/part%i/Ppanning", index-kParamPart01Panning);
  395. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  396. }
  397. else if (index <= kParamResBandwidth)
  398. {
  399. const MidiControllers zynControl(getZynControlFromIndex(index));
  400. CARLA_SAFE_ASSERT_RETURN(zynControl != C_NULL,);
  401. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  402. for (int npart=0; npart<NUM_MIDI_PARTS; ++npart)
  403. {
  404. if (fMaster->part[npart] != nullptr)
  405. fMaster->part[npart]->SetController(zynControl, static_cast<int>(value));
  406. }
  407. }
  408. }
  409. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  410. {
  411. CARLA_SAFE_ASSERT_RETURN(program < BANK_SIZE,);
  412. if (bank == 0)
  413. {
  414. // reset part to default
  415. // TODO
  416. return;
  417. }
  418. const char* const filename(sPrograms.getZynProgramFilename(bank, program));
  419. CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0',);
  420. fMiddleWare->transmitMsg("/load-part", "is", channel, filename);
  421. }
  422. void setCustomData(const char* const key, const char* const value) override
  423. {
  424. CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
  425. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  426. /**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  427. {
  428. fMiddleWare->transmitMsg("/load_xmz", "s", value);
  429. }
  430. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  431. {
  432. fMiddleWare->transmitMsg("/load_xiz", "is", 0, value);
  433. }
  434. }
  435. // -------------------------------------------------------------------
  436. // Plugin process calls
  437. void activate() override
  438. {
  439. fIsActive = true;
  440. }
  441. void deactivate() override
  442. {
  443. fIsActive = false;
  444. }
  445. void process(float**, float** const outBuffer, const uint32_t frames,
  446. const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  447. {
  448. if (! fMutex.tryLock())
  449. {
  450. if (! isOffline())
  451. {
  452. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  453. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  454. return;
  455. }
  456. fMutex.lock();
  457. }
  458. // the following code is based on ZynAddSubFX DSSI plugin code by Stephen G. Parry
  459. uint32_t fromFrame = 0;
  460. uint32_t toFrame = 0;
  461. uint32_t midiEventIndex = 0;
  462. uint32_t nextEventFrame = 0;
  463. do {
  464. /* Find the time of the next event, if any */
  465. if (midiEvents == nullptr || midiEventIndex >= midiEventCount)
  466. nextEventFrame = (uint32_t)-1;
  467. else
  468. nextEventFrame = midiEvents[midiEventIndex].time;
  469. /* find the end of the sub-sample to be processed this time round... */
  470. /* if the next event falls within the desired sample interval... */
  471. if (nextEventFrame < frames && nextEventFrame >= toFrame)
  472. /* set the end to be at that event */
  473. toFrame = nextEventFrame;
  474. else
  475. /* ...else go for the whole remaining sample */
  476. toFrame = frames;
  477. if (fromFrame < toFrame)
  478. {
  479. // call master to fill from `fromFrame` to `toFrame`:
  480. fMaster->GetAudioOutSamples(toFrame - fromFrame, fSynth.samplerate, outBuffer[0], outBuffer[1]);
  481. // next sub-sample please...
  482. fromFrame = toFrame;
  483. }
  484. // Now process any event(s) at the current timing point
  485. for (; midiEventIndex < midiEventCount && midiEvents[midiEventIndex].time == toFrame;)
  486. {
  487. const NativeMidiEvent* const midiEvent(&midiEvents[midiEventIndex++]);
  488. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  489. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  490. if (MIDI_IS_STATUS_NOTE_OFF(status))
  491. {
  492. const char note = static_cast<char>(midiEvent->data[1]);
  493. fMaster->noteOff(channel, note);
  494. }
  495. else if (MIDI_IS_STATUS_NOTE_ON(status))
  496. {
  497. const char note = static_cast<char>(midiEvent->data[1]);
  498. const char velo = static_cast<char>(midiEvent->data[2]);
  499. fMaster->noteOn(channel, note, velo);
  500. }
  501. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  502. {
  503. const char note = static_cast<char>(midiEvent->data[1]);
  504. const char pressure = static_cast<char>(midiEvent->data[2]);
  505. fMaster->polyphonicAftertouch(channel, note, pressure);
  506. }
  507. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  508. {
  509. // skip controls which we map to parameters
  510. if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount)
  511. continue;
  512. const int control = midiEvent->data[1];
  513. const int value = midiEvent->data[2];
  514. fMaster->setController(channel, control, value);
  515. }
  516. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  517. {
  518. const uint8_t lsb = midiEvent->data[1];
  519. const uint8_t msb = midiEvent->data[2];
  520. const int value = ((msb << 7) | lsb) - 8192;
  521. fMaster->setController(channel, C_pitchwheel, value);
  522. }
  523. }
  524. // Keep going until we have the desired total length of sample...
  525. } while (toFrame < frames);
  526. fMutex.unlock();
  527. }
  528. // -------------------------------------------------------------------
  529. // Plugin UI calls
  530. #ifdef HAVE_ZYN_UI_DEPS
  531. void uiShow(const bool show) override
  532. {
  533. if (show)
  534. {
  535. if (isPipeRunning())
  536. {
  537. const CarlaMutexLocker cml(getPipeLock());
  538. writeMessage("focus\n", 6);
  539. flushMessages();
  540. return;
  541. }
  542. carla_stdout("Trying to start UI using \"%s\"", getExtUiPath());
  543. CarlaExternalUI::setData(getExtUiPath(), fMiddleWare->getServerAddress(), getUiName());
  544. if (! CarlaExternalUI::startPipeServer(true))
  545. {
  546. uiClosed();
  547. hostUiUnavailable();
  548. }
  549. }
  550. else
  551. {
  552. CarlaExternalUI::stopPipeServer(2000);
  553. }
  554. }
  555. #endif
  556. // -------------------------------------------------------------------
  557. // Plugin state calls
  558. char* getState() const override
  559. {
  560. char* data = nullptr;
  561. if (fIsActive)
  562. {
  563. fMiddleWare->doReadOnlyOp([this, &data]{
  564. fMaster->getalldata(&data);
  565. });
  566. }
  567. else
  568. {
  569. fMaster->getalldata(&data);
  570. }
  571. return data;
  572. }
  573. void setState(const char* const data) override
  574. {
  575. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  576. const CarlaMutexLocker cml(fMutex);
  577. fMaster->putalldata(data);
  578. fMaster->applyparameters();
  579. fMiddleWare->updateResources(fMaster);
  580. _setMasterParameters();
  581. }
  582. // -------------------------------------------------------------------
  583. // Plugin dispatcher
  584. void bufferSizeChanged(const uint32_t bufferSize) final
  585. {
  586. char* const state(getState());
  587. _deleteMaster();
  588. fSynth.buffersize = static_cast<int>(bufferSize);
  589. fSynth.alias();
  590. _initMaster();
  591. setState(state);
  592. std::free(state);
  593. }
  594. void sampleRateChanged(const double sampleRate) final
  595. {
  596. char* const state(getState());
  597. _deleteMaster();
  598. fSynth.samplerate = static_cast<uint>(sampleRate);
  599. fSynth.alias();
  600. _initMaster();
  601. setState(state);
  602. std::free(state);
  603. }
  604. // -------------------------------------------------------------------
  605. private:
  606. MiddleWare* fMiddleWare;
  607. Master* fMaster;
  608. SYNTH_T fSynth;
  609. Config fConfig;
  610. bool fIsActive;
  611. float fParameters[kParamCount];
  612. CarlaMutex fMutex;
  613. static MidiControllers getZynControlFromIndex(const uint index)
  614. {
  615. switch (index)
  616. {
  617. case kParamFilterCutoff:
  618. return C_filtercutoff;
  619. case kParamFilterQ:
  620. return C_filterq;
  621. case kParamBandwidth:
  622. return C_bandwidth;
  623. case kParamModAmp:
  624. return C_fmamp;
  625. case kParamResCenter:
  626. return C_resonance_center;
  627. case kParamResBandwidth:
  628. return C_resonance_bandwidth;
  629. default:
  630. return C_NULL;
  631. }
  632. }
  633. static Parameters getIndexFromZynControl(const uint8_t control)
  634. {
  635. switch (control)
  636. {
  637. case C_filtercutoff:
  638. return kParamFilterCutoff;
  639. case C_filterq:
  640. return kParamFilterQ;
  641. case C_bandwidth:
  642. return kParamBandwidth;
  643. case C_fmamp:
  644. return kParamModAmp;
  645. case C_resonance_center:
  646. return kParamResCenter;
  647. case C_resonance_bandwidth:
  648. return kParamResBandwidth;
  649. default:
  650. return kParamCount;
  651. }
  652. }
  653. void run() noexcept override
  654. {
  655. for (; ! shouldThreadExit();)
  656. {
  657. try {
  658. fMiddleWare->tick();
  659. } CARLA_SAFE_EXCEPTION("ZynAddSubFX MiddleWare tick");
  660. carla_msleep(1);
  661. }
  662. }
  663. // -------------------------------------------------------------------
  664. void _initMaster()
  665. {
  666. fMiddleWare = new MiddleWare(std::move(fSynth), &fConfig);
  667. fMiddleWare->setUiCallback(__uiCallback, this);
  668. fMiddleWare->setIdleCallback(_idleCallback, this);
  669. _masterChangedCallback(fMiddleWare->spawnMaster());
  670. startThread();
  671. }
  672. void _setMasterParameters()
  673. {
  674. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  675. fMiddleWare->activeUrl("");
  676. for (int i=kParamPart16Enabled+1; --i>=kParamPart01Enabled;)
  677. {
  678. char msg[24];
  679. std::sprintf(msg, "/part%i/Penabled", i-kParamPart01Enabled);
  680. fMiddleWare->transmitMsg(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  681. }
  682. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  683. {
  684. char msg[24];
  685. std::sprintf(msg, "/part%i/Pvolume", i-kParamPart01Volume);
  686. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  687. }
  688. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  689. {
  690. char msg[24];
  691. std::sprintf(msg, "/part%i/Ppanning", i-kParamPart01Panning);
  692. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  693. }
  694. for (int i=0; i<NUM_MIDI_PARTS; ++i)
  695. {
  696. fMaster->part[i]->SetController(C_filtercutoff, static_cast<int>(fParameters[kParamFilterCutoff]));
  697. fMaster->part[i]->SetController(C_filterq, static_cast<int>(fParameters[kParamFilterQ]));
  698. fMaster->part[i]->SetController(C_bandwidth, static_cast<int>(fParameters[kParamBandwidth]));
  699. fMaster->part[i]->SetController(C_fmamp, static_cast<int>(fParameters[kParamModAmp]));
  700. fMaster->part[i]->SetController(C_resonance_center, static_cast<int>(fParameters[kParamResCenter]));
  701. fMaster->part[i]->SetController(C_resonance_bandwidth, static_cast<int>(fParameters[kParamResBandwidth]));
  702. }
  703. }
  704. void _deleteMaster()
  705. {
  706. stopThread(1000);
  707. fMaster = nullptr;
  708. delete fMiddleWare;
  709. fMiddleWare = nullptr;
  710. }
  711. void _masterChangedCallback(Master* m)
  712. {
  713. fMaster = m;
  714. fMaster->setMasterChangedCallback(__masterChangedCallback, this);
  715. }
  716. static void __masterChangedCallback(void* ptr, Master* m)
  717. {
  718. ((ZynAddSubFxPlugin*)ptr)->_masterChangedCallback(m);
  719. }
  720. void _uiCallback(const char* const msg)
  721. {
  722. if (std::strncmp(msg, "/part", 5) != 0)
  723. return;
  724. const char* msgtmp = msg;
  725. msgtmp += 5;
  726. CARLA_SAFE_ASSERT_RETURN( msgtmp[0] >= '0' && msgtmp[0] <= '9',);
  727. CARLA_SAFE_ASSERT_RETURN((msgtmp[1] >= '0' && msgtmp[1] <= '9') || msgtmp[1] == '/',);
  728. char partnstr[3] = { '\0', '\0', '\0' };
  729. partnstr[0] = msgtmp[0];
  730. ++msgtmp;
  731. if (msgtmp[0] >= '0' && msgtmp[0] <= '9')
  732. {
  733. partnstr[1] = msgtmp[0];
  734. ++msgtmp;
  735. }
  736. const int partn = std::atoi(partnstr);
  737. ++msgtmp;
  738. /**/ if (std::strcmp(msgtmp, "Penabled") == 0)
  739. {
  740. const int index = kParamPart01Enabled+partn;
  741. const bool enbl = rtosc_argument(msg,0).T;
  742. fParameters[index] = enbl ? 1.0f : 0.0f;
  743. uiParameterChanged(kParamPart01Enabled+partn, enbl ? 1.0f : 0.0f);
  744. }
  745. else if (std::strcmp(msgtmp, "Pvolume") == 0)
  746. {
  747. const int index = kParamPart01Volume+partn;
  748. const int value = rtosc_argument(msg,0).i;
  749. fParameters[index] = value;
  750. uiParameterChanged(kParamPart01Volume+partn, value);
  751. }
  752. else if (std::strcmp(msgtmp, "Ppanning") == 0)
  753. {
  754. const int index = kParamPart01Panning+partn;
  755. const int value = rtosc_argument(msg,0).i;
  756. fParameters[index] = value;
  757. uiParameterChanged(kParamPart01Panning+partn, value);
  758. }
  759. }
  760. static void __uiCallback(void* ptr, const char* msg)
  761. {
  762. ((ZynAddSubFxPlugin*)ptr)->_uiCallback(msg);
  763. }
  764. static void _idleCallback(void* ptr)
  765. {
  766. ((ZynAddSubFxPlugin*)ptr)->hostGiveIdle();
  767. }
  768. // -------------------------------------------------------------------
  769. PluginClassEND(ZynAddSubFxPlugin)
  770. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  771. };
  772. // -----------------------------------------------------------------------
  773. static const NativePluginDescriptor zynaddsubfxDesc = {
  774. /* category */ NATIVE_PLUGIN_CATEGORY_SYNTH,
  775. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
  776. #ifdef HAVE_ZYN_UI_DEPS
  777. |NATIVE_PLUGIN_HAS_UI
  778. #endif
  779. |NATIVE_PLUGIN_USES_MULTI_PROGS
  780. |NATIVE_PLUGIN_USES_STATE),
  781. /* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_CONTROL_CHANGES
  782. |NATIVE_PLUGIN_SUPPORTS_NOTE_AFTERTOUCH
  783. |NATIVE_PLUGIN_SUPPORTS_PITCHBEND
  784. |NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  785. /* audioIns */ 0,
  786. /* audioOuts */ 2,
  787. /* midiIns */ 1,
  788. /* midiOuts */ 0,
  789. /* paramIns */ ZynAddSubFxPlugin::kParamCount,
  790. /* paramOuts */ 0,
  791. /* name */ "ZynAddSubFX",
  792. /* label */ "zynaddsubfx",
  793. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  794. /* copyright */ "GNU GPL v2+",
  795. PluginDescriptorFILL(ZynAddSubFxPlugin)
  796. };
  797. // -----------------------------------------------------------------------
  798. CARLA_EXPORT
  799. void carla_register_native_plugin_zynaddsubfx_synth();
  800. CARLA_EXPORT
  801. void carla_register_native_plugin_zynaddsubfx_synth()
  802. {
  803. carla_register_native_plugin(&zynaddsubfxDesc);
  804. }
  805. // -----------------------------------------------------------------------