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.

1063 lines
32KB

  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. isPlugin = true;
  236. sPrograms.initIfNeeded();
  237. fConfig.init();
  238. // init parameters to default
  239. fParameters[kParamPart01Enabled] = 1.0f;
  240. for (int i=kParamPart16Enabled+1; --i>kParamPart01Enabled;)
  241. fParameters[i] = 0.0f;
  242. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  243. fParameters[i] = 100.0f;
  244. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  245. fParameters[i] = 64.0f;
  246. fParameters[kParamFilterCutoff] = 64.0f;
  247. fParameters[kParamFilterQ] = 64.0f;
  248. fParameters[kParamBandwidth] = 64.0f;
  249. fParameters[kParamModAmp] = 127.0f;
  250. fParameters[kParamResCenter] = 64.0f;
  251. fParameters[kParamResBandwidth] = 64.0f;
  252. fSynth.buffersize = static_cast<int>(getBufferSize());
  253. fSynth.samplerate = static_cast<uint>(getSampleRate());
  254. if (fSynth.buffersize > 32)
  255. fSynth.buffersize = 32;
  256. fSynth.alias();
  257. _initMaster();
  258. _setMasterParameters();
  259. fMiddleWareThread->start(fMiddleWare);
  260. }
  261. ~ZynAddSubFxPlugin() override
  262. {
  263. fMiddleWareThread->stop();
  264. _deleteMaster();
  265. }
  266. protected:
  267. // -------------------------------------------------------------------
  268. // Plugin parameter calls
  269. uint32_t getParameterCount() const final
  270. {
  271. return kParamCount;
  272. }
  273. const NativeParameter* getParameterInfo(const uint32_t index) const override
  274. {
  275. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  276. static NativeParameter param;
  277. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  278. param.name = nullptr;
  279. param.unit = nullptr;
  280. param.ranges.def = 64.0f;
  281. param.ranges.min = 0.0f;
  282. param.ranges.max = 127.0f;
  283. param.ranges.step = 1.0f;
  284. param.ranges.stepSmall = 1.0f;
  285. param.ranges.stepLarge = 20.0f;
  286. param.scalePointCount = 0;
  287. param.scalePoints = nullptr;
  288. if (index <= kParamPart16Enabled)
  289. {
  290. hints |= NATIVE_PARAMETER_IS_BOOLEAN;
  291. param.ranges.def = 0.0f;
  292. param.ranges.min = 0.0f;
  293. param.ranges.max = 1.0f;
  294. param.ranges.step = 1.0f;
  295. param.ranges.stepSmall = 1.0f;
  296. param.ranges.stepLarge = 1.0f;
  297. #define PARAM_PART_ENABLE_DESC(N) \
  298. case kParamPart01Enabled + N - 1: \
  299. param.name = "Part " #N " Enabled"; break;
  300. switch (index)
  301. {
  302. case kParamPart01Enabled:
  303. param.name = "Part 01 Enabled";
  304. param.ranges.def = 1.0f;
  305. break;
  306. PARAM_PART_ENABLE_DESC( 2)
  307. PARAM_PART_ENABLE_DESC( 3)
  308. PARAM_PART_ENABLE_DESC( 4)
  309. PARAM_PART_ENABLE_DESC( 5)
  310. PARAM_PART_ENABLE_DESC( 6)
  311. PARAM_PART_ENABLE_DESC( 7)
  312. PARAM_PART_ENABLE_DESC( 8)
  313. PARAM_PART_ENABLE_DESC( 9)
  314. PARAM_PART_ENABLE_DESC(10)
  315. PARAM_PART_ENABLE_DESC(11)
  316. PARAM_PART_ENABLE_DESC(12)
  317. PARAM_PART_ENABLE_DESC(13)
  318. PARAM_PART_ENABLE_DESC(14)
  319. PARAM_PART_ENABLE_DESC(15)
  320. PARAM_PART_ENABLE_DESC(16)
  321. }
  322. #undef PARAM_PART_ENABLE_DESC
  323. }
  324. else if (index <= kParamPart16Volume)
  325. {
  326. hints |= NATIVE_PARAMETER_IS_INTEGER;
  327. param.ranges.def = 100.0f;
  328. #define PARAM_PART_ENABLE_DESC(N) \
  329. case kParamPart01Volume + N - 1: \
  330. param.name = "Part " #N " Volume"; break;
  331. switch (index)
  332. {
  333. PARAM_PART_ENABLE_DESC( 1)
  334. PARAM_PART_ENABLE_DESC( 2)
  335. PARAM_PART_ENABLE_DESC( 3)
  336. PARAM_PART_ENABLE_DESC( 4)
  337. PARAM_PART_ENABLE_DESC( 5)
  338. PARAM_PART_ENABLE_DESC( 6)
  339. PARAM_PART_ENABLE_DESC( 7)
  340. PARAM_PART_ENABLE_DESC( 8)
  341. PARAM_PART_ENABLE_DESC( 9)
  342. PARAM_PART_ENABLE_DESC(10)
  343. PARAM_PART_ENABLE_DESC(11)
  344. PARAM_PART_ENABLE_DESC(12)
  345. PARAM_PART_ENABLE_DESC(13)
  346. PARAM_PART_ENABLE_DESC(14)
  347. PARAM_PART_ENABLE_DESC(15)
  348. PARAM_PART_ENABLE_DESC(16)
  349. }
  350. #undef PARAM_PART_ENABLE_DESC
  351. }
  352. else if (index <= kParamPart16Panning)
  353. {
  354. hints |= NATIVE_PARAMETER_IS_INTEGER;
  355. #define PARAM_PART_ENABLE_DESC(N) \
  356. case kParamPart01Panning + N - 1: \
  357. param.name = "Part " #N " Panning"; break;
  358. switch (index)
  359. {
  360. PARAM_PART_ENABLE_DESC( 1)
  361. PARAM_PART_ENABLE_DESC( 2)
  362. PARAM_PART_ENABLE_DESC( 3)
  363. PARAM_PART_ENABLE_DESC( 4)
  364. PARAM_PART_ENABLE_DESC( 5)
  365. PARAM_PART_ENABLE_DESC( 6)
  366. PARAM_PART_ENABLE_DESC( 7)
  367. PARAM_PART_ENABLE_DESC( 8)
  368. PARAM_PART_ENABLE_DESC( 9)
  369. PARAM_PART_ENABLE_DESC(10)
  370. PARAM_PART_ENABLE_DESC(11)
  371. PARAM_PART_ENABLE_DESC(12)
  372. PARAM_PART_ENABLE_DESC(13)
  373. PARAM_PART_ENABLE_DESC(14)
  374. PARAM_PART_ENABLE_DESC(15)
  375. PARAM_PART_ENABLE_DESC(16)
  376. }
  377. #undef PARAM_PART_ENABLE_DESC
  378. }
  379. else if (index <= kParamResBandwidth)
  380. {
  381. hints |= NATIVE_PARAMETER_IS_INTEGER;
  382. switch (index)
  383. {
  384. case kParamFilterCutoff:
  385. param.name = "Filter Cutoff";
  386. break;
  387. case kParamFilterQ:
  388. param.name = "Filter Q";
  389. break;
  390. case kParamBandwidth:
  391. param.name = "Bandwidth";
  392. break;
  393. case kParamModAmp:
  394. param.name = "FM Gain";
  395. param.ranges.def = 127.0f;
  396. break;
  397. case kParamResCenter:
  398. param.name = "Res Center Freq";
  399. break;
  400. case kParamResBandwidth:
  401. param.name = "Res Bandwidth";
  402. break;
  403. }
  404. }
  405. param.hints = static_cast<NativeParameterHints>(hints);
  406. return &param;
  407. }
  408. float getParameterValue(const uint32_t index) const final
  409. {
  410. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  411. return fParameters[index];
  412. }
  413. // -------------------------------------------------------------------
  414. // Plugin midi-program calls
  415. uint32_t getMidiProgramCount() const noexcept override
  416. {
  417. return sPrograms.getNativeMidiProgramCount();
  418. }
  419. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
  420. {
  421. return sPrograms.getNativeMidiProgramInfo(index);
  422. }
  423. // -------------------------------------------------------------------
  424. // Plugin state calls
  425. void setParameterValue(const uint32_t index, const float value) final
  426. {
  427. CARLA_SAFE_ASSERT_RETURN(index < kParamCount,);
  428. if (index <= kParamPart16Enabled)
  429. {
  430. fParameters[index] = (value >= 0.5f) ? 1.0f : 0.0f;
  431. char msg[24];
  432. std::sprintf(msg, "/part%i/Penabled", index-kParamPart01Enabled);
  433. #ifdef ZYN_MSG_ANYWHERE
  434. fMiddleWare->messageAnywhere(msg, (value >= 0.5f) ? "T" : "F");
  435. #else
  436. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  437. fMiddleWare->activeUrl("");
  438. fMiddleWare->transmitMsg(msg, (value >= 0.5f) ? "T" : "F");
  439. #endif
  440. }
  441. else if (index <= kParamPart16Volume)
  442. {
  443. if (carla_isEqual(fParameters[index], value))
  444. return;
  445. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  446. char msg[24];
  447. std::sprintf(msg, "/part%i/Pvolume", index-kParamPart01Volume);
  448. #ifdef ZYN_MSG_ANYWHERE
  449. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[index]));
  450. #else
  451. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  452. fMiddleWare->activeUrl("");
  453. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  454. #endif
  455. }
  456. else if (index <= kParamPart16Panning)
  457. {
  458. if (carla_isEqual(fParameters[index], value))
  459. return;
  460. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  461. char msg[24];
  462. std::sprintf(msg, "/part%i/Ppanning", index-kParamPart01Panning);
  463. #ifdef ZYN_MSG_ANYWHERE
  464. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[index]));
  465. #else
  466. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  467. fMiddleWare->activeUrl("");
  468. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  469. #endif
  470. }
  471. else if (index <= kParamResBandwidth)
  472. {
  473. const MidiControllers zynControl(getZynControlFromIndex(index));
  474. CARLA_SAFE_ASSERT_RETURN(zynControl != C_NULL,);
  475. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  476. for (int npart=0; npart<NUM_MIDI_PARTS; ++npart)
  477. {
  478. if (fMaster->part[npart] != nullptr)
  479. fMaster->part[npart]->SetController(zynControl, static_cast<int>(value));
  480. }
  481. }
  482. }
  483. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  484. {
  485. CARLA_SAFE_ASSERT_RETURN(program < BANK_SIZE,);
  486. if (bank == 0)
  487. {
  488. // reset part to default
  489. // TODO
  490. return;
  491. }
  492. const char* const filename(sPrograms.getZynProgramFilename(bank, program));
  493. CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0',);
  494. #ifdef ZYN_MSG_ANYWHERE
  495. fMiddleWare->messageAnywhere("/load-part", "is", channel, filename);
  496. #else
  497. fMiddleWare->transmitMsg("/load-part", "is", channel, filename);
  498. #endif
  499. }
  500. void setCustomData(const char* const key, const char* const value) override
  501. {
  502. CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
  503. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  504. /**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  505. {
  506. #ifdef ZYN_MSG_ANYWHERE
  507. fMiddleWare->messageAnywhere("/load_xmz", "s", value);
  508. #else
  509. fMiddleWare->transmitMsg("/load_xmz", "s", value);
  510. #endif
  511. }
  512. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  513. {
  514. #ifdef ZYN_MSG_ANYWHERE
  515. fMiddleWare->messageAnywhere("/load_xiz", "is", 0, value);
  516. #else
  517. fMiddleWare->transmitMsg("/load_xiz", "is", 0, value);
  518. #endif
  519. }
  520. }
  521. // -------------------------------------------------------------------
  522. // Plugin process calls
  523. void process(float**, float** const outBuffer, const uint32_t frames,
  524. const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  525. {
  526. if (! fMutex.tryLock())
  527. {
  528. if (! isOffline())
  529. {
  530. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  531. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  532. return;
  533. }
  534. fMutex.lock();
  535. }
  536. uint32_t framesOffset = 0;
  537. for (uint32_t i=0; i < midiEventCount; ++i)
  538. {
  539. const NativeMidiEvent* const midiEvent(&midiEvents[i]);
  540. if (midiEvent->time >= frames)
  541. continue;
  542. if (midiEvent->time > framesOffset)
  543. {
  544. fMaster->GetAudioOutSamples(midiEvent->time-framesOffset, fSynth.samplerate, outBuffer[0]+framesOffset,
  545. outBuffer[1]+framesOffset);
  546. framesOffset = midiEvent->time;
  547. }
  548. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  549. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  550. if (MIDI_IS_STATUS_NOTE_OFF(status))
  551. {
  552. const char note = static_cast<char>(midiEvent->data[1]);
  553. fMaster->noteOff(channel, note);
  554. }
  555. else if (MIDI_IS_STATUS_NOTE_ON(status))
  556. {
  557. const char note = static_cast<char>(midiEvent->data[1]);
  558. const char velo = static_cast<char>(midiEvent->data[2]);
  559. fMaster->noteOn(channel, note, velo);
  560. }
  561. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  562. {
  563. const char note = static_cast<char>(midiEvent->data[1]);
  564. const char pressure = static_cast<char>(midiEvent->data[2]);
  565. fMaster->polyphonicAftertouch(channel, note, pressure);
  566. }
  567. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  568. {
  569. // skip controls which we map to parameters
  570. if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount)
  571. continue;
  572. const int control = midiEvent->data[1];
  573. const int value = midiEvent->data[2];
  574. fMaster->setController(channel, control, value);
  575. }
  576. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  577. {
  578. const uint8_t lsb = midiEvent->data[1];
  579. const uint8_t msb = midiEvent->data[2];
  580. const int value = ((msb << 7) | lsb) - 8192;
  581. fMaster->setController(channel, C_pitchwheel, value);
  582. }
  583. }
  584. if (frames > framesOffset)
  585. fMaster->GetAudioOutSamples(frames-framesOffset, fSynth.samplerate, outBuffer[0]+framesOffset,
  586. outBuffer[1]+framesOffset);
  587. fMutex.unlock();
  588. }
  589. // -------------------------------------------------------------------
  590. // Plugin UI calls
  591. #ifdef HAVE_ZYN_UI_DEPS
  592. void uiShow(const bool show) override
  593. {
  594. if (show)
  595. {
  596. if (isPipeRunning())
  597. {
  598. const CarlaMutexLocker cml(getPipeLock());
  599. writeMessage("focus\n", 6);
  600. flushMessages();
  601. return;
  602. }
  603. carla_stdout("Trying to start UI using \"%s\"", getExtUiPath());
  604. CarlaExternalUI::setData(getExtUiPath(), fMiddleWare->getServerAddress(), getUiName());
  605. if (! CarlaExternalUI::startPipeServer(true))
  606. {
  607. uiClosed();
  608. hostUiUnavailable();
  609. }
  610. }
  611. else
  612. {
  613. CarlaExternalUI::stopPipeServer(2000);
  614. }
  615. }
  616. #endif
  617. // -------------------------------------------------------------------
  618. // Plugin state calls
  619. char* getState() const override
  620. {
  621. const MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  622. char* data = nullptr;
  623. fMaster->getalldata(&data);
  624. return data;
  625. }
  626. void setState(const char* const data) override
  627. {
  628. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  629. const MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  630. const CarlaMutexLocker cml(fMutex);
  631. fMaster->putalldata(data);
  632. fMaster->applyparameters();
  633. fMaster->initialize_rt();
  634. fMiddleWare->updateResources(fMaster);
  635. _setMasterParameters();
  636. }
  637. // -------------------------------------------------------------------
  638. // Plugin dispatcher
  639. void bufferSizeChanged(const uint32_t bufferSize) final
  640. {
  641. MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  642. char* const state(getState());
  643. _deleteMaster();
  644. fSynth.buffersize = static_cast<int>(bufferSize);
  645. if (fSynth.buffersize > 32)
  646. fSynth.buffersize = 32;
  647. fSynth.alias();
  648. _initMaster();
  649. mwss.updateMiddleWare(fMiddleWare);
  650. setState(state);
  651. std::free(state);
  652. }
  653. void sampleRateChanged(const double sampleRate) final
  654. {
  655. MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  656. char* const state(getState());
  657. _deleteMaster();
  658. fSynth.samplerate = static_cast<uint>(sampleRate);
  659. fSynth.alias();
  660. _initMaster();
  661. mwss.updateMiddleWare(fMiddleWare);
  662. setState(state);
  663. std::free(state);
  664. }
  665. // -------------------------------------------------------------------
  666. private:
  667. MiddleWare* fMiddleWare;
  668. Master* fMaster;
  669. SYNTH_T fSynth;
  670. Config fConfig;
  671. float fParameters[kParamCount];
  672. CarlaMutex fMutex;
  673. ScopedPointer<MiddleWareThread> fMiddleWareThread;
  674. static MidiControllers getZynControlFromIndex(const uint index)
  675. {
  676. switch (index)
  677. {
  678. case kParamFilterCutoff:
  679. return C_filtercutoff;
  680. case kParamFilterQ:
  681. return C_filterq;
  682. case kParamBandwidth:
  683. return C_bandwidth;
  684. case kParamModAmp:
  685. return C_fmamp;
  686. case kParamResCenter:
  687. return C_resonance_center;
  688. case kParamResBandwidth:
  689. return C_resonance_bandwidth;
  690. default:
  691. return C_NULL;
  692. }
  693. }
  694. static Parameters getIndexFromZynControl(const uint8_t control)
  695. {
  696. switch (control)
  697. {
  698. case C_filtercutoff:
  699. return kParamFilterCutoff;
  700. case C_filterq:
  701. return kParamFilterQ;
  702. case C_bandwidth:
  703. return kParamBandwidth;
  704. case C_fmamp:
  705. return kParamModAmp;
  706. case C_resonance_center:
  707. return kParamResCenter;
  708. case C_resonance_bandwidth:
  709. return kParamResBandwidth;
  710. default:
  711. return kParamCount;
  712. }
  713. }
  714. // -------------------------------------------------------------------
  715. void _initMaster()
  716. {
  717. fMiddleWare = new MiddleWare(std::move(fSynth), &fConfig);
  718. fMiddleWare->setUiCallback(__uiCallback, this);
  719. fMiddleWare->setIdleCallback(_idleCallback, this);
  720. _masterChangedCallback(fMiddleWare->spawnMaster());
  721. }
  722. void _setMasterParameters()
  723. {
  724. #ifndef ZYN_MSG_ANYWHERE
  725. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  726. fMiddleWare->activeUrl("");
  727. #endif
  728. char msg[24];
  729. for (int i=kParamPart16Enabled+1; --i>=kParamPart01Enabled;)
  730. {
  731. std::sprintf(msg, "/part%i/Penabled", i-kParamPart01Enabled);
  732. #ifdef ZYN_MSG_ANYWHERE
  733. fMiddleWare->messageAnywhere(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  734. #else
  735. fMiddleWare->transmitMsg(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  736. #endif
  737. }
  738. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  739. {
  740. std::sprintf(msg, "/part%i/Pvolume", i-kParamPart01Volume);
  741. #ifdef ZYN_MSG_ANYWHERE
  742. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[i]));
  743. #else
  744. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  745. #endif
  746. }
  747. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  748. {
  749. std::sprintf(msg, "/part%i/Ppanning", i-kParamPart01Panning);
  750. #ifdef ZYN_MSG_ANYWHERE
  751. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[i]));
  752. #else
  753. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  754. #endif
  755. }
  756. for (int i=0; i<NUM_MIDI_PARTS; ++i)
  757. {
  758. fMaster->part[i]->SetController(C_filtercutoff, static_cast<int>(fParameters[kParamFilterCutoff]));
  759. fMaster->part[i]->SetController(C_filterq, static_cast<int>(fParameters[kParamFilterQ]));
  760. fMaster->part[i]->SetController(C_bandwidth, static_cast<int>(fParameters[kParamBandwidth]));
  761. fMaster->part[i]->SetController(C_fmamp, static_cast<int>(fParameters[kParamModAmp]));
  762. fMaster->part[i]->SetController(C_resonance_center, static_cast<int>(fParameters[kParamResCenter]));
  763. fMaster->part[i]->SetController(C_resonance_bandwidth, static_cast<int>(fParameters[kParamResBandwidth]));
  764. }
  765. }
  766. void _deleteMaster()
  767. {
  768. fMaster = nullptr;
  769. delete fMiddleWare;
  770. fMiddleWare = nullptr;
  771. }
  772. void _masterChangedCallback(Master* m)
  773. {
  774. fMaster = m;
  775. fMaster->setMasterChangedCallback(__masterChangedCallback, this);
  776. }
  777. static void __masterChangedCallback(void* ptr, Master* m)
  778. {
  779. ((ZynAddSubFxPlugin*)ptr)->_masterChangedCallback(m);
  780. }
  781. void _uiCallback(const char* const msg)
  782. {
  783. if (std::strncmp(msg, "/part", 5) != 0)
  784. return;
  785. const char* msgtmp = msg;
  786. msgtmp += 5;
  787. CARLA_SAFE_ASSERT_RETURN( msgtmp[0] >= '0' && msgtmp[0] <= '9',);
  788. CARLA_SAFE_ASSERT_RETURN((msgtmp[1] >= '0' && msgtmp[1] <= '9') || msgtmp[1] == '/',);
  789. char partnstr[3] = { '\0', '\0', '\0' };
  790. partnstr[0] = msgtmp[0];
  791. ++msgtmp;
  792. if (msgtmp[0] >= '0' && msgtmp[0] <= '9')
  793. {
  794. partnstr[1] = msgtmp[0];
  795. ++msgtmp;
  796. }
  797. const int partn = std::atoi(partnstr);
  798. ++msgtmp;
  799. /**/ if (std::strcmp(msgtmp, "Penabled") == 0)
  800. {
  801. const int index = kParamPart01Enabled+partn;
  802. const bool enbl = rtosc_argument(msg,0).T;
  803. fParameters[index] = enbl ? 1.0f : 0.0f;
  804. uiParameterChanged(kParamPart01Enabled+partn, enbl ? 1.0f : 0.0f);
  805. }
  806. else if (std::strcmp(msgtmp, "Pvolume") == 0)
  807. {
  808. const int index = kParamPart01Volume+partn;
  809. const int value = rtosc_argument(msg,0).i;
  810. fParameters[index] = value;
  811. uiParameterChanged(kParamPart01Volume+partn, value);
  812. }
  813. else if (std::strcmp(msgtmp, "Ppanning") == 0)
  814. {
  815. const int index = kParamPart01Panning+partn;
  816. const int value = rtosc_argument(msg,0).i;
  817. fParameters[index] = value;
  818. uiParameterChanged(kParamPart01Panning+partn, value);
  819. }
  820. }
  821. static void __uiCallback(void* ptr, const char* msg)
  822. {
  823. ((ZynAddSubFxPlugin*)ptr)->_uiCallback(msg);
  824. }
  825. static void _idleCallback(void* ptr)
  826. {
  827. ((ZynAddSubFxPlugin*)ptr)->hostGiveIdle();
  828. }
  829. // -------------------------------------------------------------------
  830. PluginClassEND(ZynAddSubFxPlugin)
  831. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  832. };
  833. // -----------------------------------------------------------------------
  834. static const NativePluginDescriptor zynaddsubfxDesc = {
  835. /* category */ NATIVE_PLUGIN_CATEGORY_SYNTH,
  836. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
  837. #ifdef HAVE_ZYN_UI_DEPS
  838. |NATIVE_PLUGIN_HAS_UI
  839. #endif
  840. |NATIVE_PLUGIN_USES_MULTI_PROGS
  841. |NATIVE_PLUGIN_USES_STATE),
  842. /* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_CONTROL_CHANGES
  843. |NATIVE_PLUGIN_SUPPORTS_NOTE_AFTERTOUCH
  844. |NATIVE_PLUGIN_SUPPORTS_PITCHBEND
  845. |NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  846. /* audioIns */ 0,
  847. /* audioOuts */ 2,
  848. /* midiIns */ 1,
  849. /* midiOuts */ 0,
  850. /* paramIns */ ZynAddSubFxPlugin::kParamCount,
  851. /* paramOuts */ 0,
  852. /* name */ "ZynAddSubFX",
  853. /* label */ "zynaddsubfx",
  854. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  855. /* copyright */ "GNU GPL v2+",
  856. PluginDescriptorFILL(ZynAddSubFxPlugin)
  857. };
  858. // -----------------------------------------------------------------------
  859. CARLA_EXPORT
  860. void carla_register_native_plugin_zynaddsubfx_synth();
  861. CARLA_EXPORT
  862. void carla_register_native_plugin_zynaddsubfx_synth()
  863. {
  864. carla_register_native_plugin(&zynaddsubfxDesc);
  865. }
  866. // -----------------------------------------------------------------------