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.

zynaddsubfx-synth.cpp 32KB

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