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.

558 lines
15KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. main.cpp - Main file of the synthesizer
  4. Copyright (C) 2002-2005 Nasca Octavian Paul
  5. Copyright (C) 2012-2014 Mark McCurry
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of version 2 of the GNU General Public License
  8. as published by the Free Software Foundation.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License (version 2 or later) for more details.
  13. You should have received a copy of the GNU General Public License (version 2)
  14. along with this program; if not, write to the Free Software Foundation,
  15. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. */
  17. #include <iostream>
  18. #include <fstream>
  19. #include <map>
  20. #include <cmath>
  21. #include <cctype>
  22. #include <algorithm>
  23. #include <signal.h>
  24. #include <unistd.h>
  25. #include <pthread.h>
  26. #include <getopt.h>
  27. #include <lo/lo.h>
  28. #include <rtosc/ports.h>
  29. #include <rtosc/thread-link.h>
  30. #include "Params/PADnoteParameters.h"
  31. #include "DSP/FFTwrapper.h"
  32. #include "Misc/PresetExtractor.h"
  33. #include "Misc/Master.h"
  34. #include "Misc/Part.h"
  35. #include "Misc/Util.h"
  36. //Nio System
  37. #include "Nio/Nio.h"
  38. //GUI System
  39. #include "UI/Connection.h"
  40. GUI::ui_handle_t gui;
  41. //Glue Layer
  42. #include "Misc/MiddleWare.h"
  43. MiddleWare *middleware;
  44. using namespace std;
  45. Master *master;
  46. int swaplr = 0; //1 for left-right swapping
  47. int Pexitprogram = 0; //if the UI set this to 1, the program will exit
  48. #if LASH
  49. #include "Misc/LASHClient.h"
  50. LASHClient *lash = NULL;
  51. #endif
  52. #if USE_NSM
  53. #include "UI/NSM.H"
  54. NSM_Client *nsm = 0;
  55. #endif
  56. char *instance_name = 0;
  57. void exitprogram(const Config &config);
  58. extern pthread_t main_thread;
  59. //cleanup on signaled exit
  60. void sigterm_exit(int /*sig*/)
  61. {
  62. if(Pexitprogram)
  63. exit(1);
  64. Pexitprogram = 1;
  65. }
  66. /*
  67. * Program initialisation
  68. */
  69. void initprogram(SYNTH_T synth, Config* config, int prefered_port)
  70. {
  71. middleware = new MiddleWare(std::move(synth), config, prefered_port);
  72. master = middleware->spawnMaster();
  73. master->swaplr = swaplr;
  74. signal(SIGINT, sigterm_exit);
  75. signal(SIGTERM, sigterm_exit);
  76. Nio::init(master->synth, config->cfg.oss_devs, master);
  77. }
  78. /*
  79. * Program exit
  80. */
  81. void exitprogram(const Config& config)
  82. {
  83. Nio::stop();
  84. config.save();
  85. middleware->removeAutoSave();
  86. GUI::destroyUi(gui);
  87. delete middleware;
  88. #if LASH
  89. if(lash)
  90. delete lash;
  91. #endif
  92. #if USE_NSM
  93. if(nsm)
  94. delete nsm;
  95. #endif
  96. FFT_cleanup();
  97. }
  98. int main(int argc, char *argv[])
  99. {
  100. main_thread = pthread_self();
  101. SYNTH_T synth;
  102. Config config;
  103. config.init();
  104. int noui = 0;
  105. cerr
  106. << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others"
  107. << endl;
  108. cerr
  109. << " Copyright (c) 2009-2015 Mark McCurry [active maintainer]"
  110. << endl;
  111. cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl;
  112. cerr << "This program is free software (GNU GPL v2 or later) and \n";
  113. cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl;
  114. if(argc == 1)
  115. cerr << "Try 'zynaddsubfx --help' for command-line options." << endl;
  116. /* Get the settings from the Config*/
  117. synth.samplerate = config.cfg.SampleRate;
  118. synth.buffersize = config.cfg.SoundBufferSize;
  119. synth.oscilsize = config.cfg.OscilSize;
  120. swaplr = config.cfg.SwapStereo;
  121. Nio::preferedSampleRate(synth.samplerate);
  122. synth.alias(); //build aliases
  123. sprng(time(NULL));
  124. /* Parse command-line options */
  125. struct option opts[] = {
  126. {
  127. "load", 2, NULL, 'l'
  128. },
  129. {
  130. "load-instrument", 2, NULL, 'L'
  131. },
  132. {
  133. "sample-rate", 2, NULL, 'r'
  134. },
  135. {
  136. "buffer-size", 2, NULL, 'b'
  137. },
  138. {
  139. "oscil-size", 2, NULL, 'o'
  140. },
  141. {
  142. "swap", 2, NULL, 'S'
  143. },
  144. {
  145. "no-gui", 2, NULL, 'U'
  146. },
  147. {
  148. "dummy", 2, NULL, 'Y'
  149. },
  150. {
  151. "help", 2, NULL, 'h'
  152. },
  153. {
  154. "version", 2, NULL, 'v'
  155. },
  156. {
  157. "named", 1, NULL, 'N'
  158. },
  159. {
  160. "auto-connect", 0, NULL, 'a'
  161. },
  162. {
  163. "auto-save", 0, NULL, 'A'
  164. },
  165. {
  166. "pid-in-client-name", 0, NULL, 'p'
  167. },
  168. {
  169. "prefered-port", 1, NULL, 'P',
  170. },
  171. {
  172. "output", 1, NULL, 'O'
  173. },
  174. {
  175. "input", 1, NULL, 'I'
  176. },
  177. {
  178. "exec-after-init", 1, NULL, 'e'
  179. },
  180. {
  181. "dump-oscdoc", 2, NULL, 'd'
  182. },
  183. {
  184. "ui-title", 1, NULL, 'u'
  185. },
  186. {
  187. 0, 0, 0, 0
  188. }
  189. };
  190. opterr = 0;
  191. int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0;
  192. int prefered_port = -1;
  193. int auto_save_interval = 60;
  194. string loadfile, loadinstrument, execAfterInit, ui_title;
  195. while(1) {
  196. int tmp = 0;
  197. /**\todo check this process for a small memory leak*/
  198. opt = getopt_long(argc,
  199. argv,
  200. "l:L:r:b:o:I:O:N:e:P:A:u:hvapSDUY",
  201. opts,
  202. &option_index);
  203. char *optarguments = optarg;
  204. #define GETOP(x) if(optarguments) \
  205. x = optarguments
  206. #define GETOPNUM(x) if(optarguments) \
  207. x = atoi(optarguments)
  208. if(opt == -1)
  209. break;
  210. switch(opt) {
  211. case 'h':
  212. exitwithhelp = 1;
  213. break;
  214. case 'v':
  215. exitwithversion = 1;
  216. break;
  217. case 'Y': /* this command a dummy command (has NO effect)
  218. and is used because I need for NSIS installer
  219. (NSIS sometimes forces a command line for a
  220. program, even if I don't need that; eg. when
  221. I want to add a icon to a shortcut.
  222. */
  223. break;
  224. case 'U':
  225. noui = 1;
  226. break;
  227. case 'l':
  228. GETOP(loadfile);
  229. break;
  230. case 'L':
  231. GETOP(loadinstrument);
  232. break;
  233. case 'r':
  234. GETOPNUM(synth.samplerate);
  235. if(synth.samplerate < 4000) {
  236. cerr << "ERROR:Incorrect sample rate: " << optarguments
  237. << endl;
  238. exit(1);
  239. }
  240. break;
  241. case 'b':
  242. GETOPNUM(synth.buffersize);
  243. if(synth.buffersize < 2) {
  244. cerr << "ERROR:Incorrect buffer size: " << optarguments
  245. << endl;
  246. exit(1);
  247. }
  248. break;
  249. case 'o':
  250. if(optarguments)
  251. synth.oscilsize = tmp = atoi(optarguments);
  252. if(synth.oscilsize < MAX_AD_HARMONICS * 2)
  253. synth.oscilsize = MAX_AD_HARMONICS * 2;
  254. synth.oscilsize =
  255. (int) powf(2,
  256. ceil(logf(synth.oscilsize - 1.0f) / logf(2.0f)));
  257. if(tmp != synth.oscilsize)
  258. cerr
  259. <<
  260. "synth.oscilsize is wrong (must be 2^n) or too small. Adjusting to "
  261. << synth.oscilsize << "." << endl;
  262. break;
  263. case 'S':
  264. swaplr = 1;
  265. break;
  266. case 'N':
  267. Nio::setPostfix(optarguments);
  268. break;
  269. case 'I':
  270. if(optarguments)
  271. Nio::setDefaultSource(optarguments);
  272. break;
  273. case 'O':
  274. if(optarguments)
  275. Nio::setDefaultSink(optarguments);
  276. break;
  277. case 'a':
  278. Nio::autoConnect = true;
  279. break;
  280. case 'p':
  281. Nio::pidInClientName = true;
  282. break;
  283. case 'P':
  284. if(optarguments)
  285. prefered_port = atoi(optarguments);
  286. break;
  287. case 'A':
  288. if(optarguments)
  289. auto_save_interval = atoi(optarguments);
  290. break;
  291. case 'e':
  292. GETOP(execAfterInit);
  293. break;
  294. case 'd':
  295. if(optarguments)
  296. {
  297. rtosc::OscDocFormatter s;
  298. ofstream outfile(optarguments);
  299. s.prog_name = "ZynAddSubFX";
  300. s.p = &Master::ports;
  301. s.uri = "http://example.com/fake/";
  302. s.doc_origin = "http://example.com/fake/url.xml";
  303. s.author_first = "Mark";
  304. s.author_last = "McCurry";
  305. outfile << s;
  306. }
  307. break;
  308. case 'u':
  309. if(optarguments)
  310. ui_title = optarguments;
  311. break;
  312. case '?':
  313. cerr << "ERROR:Bad option or parameter.\n" << endl;
  314. exitwithhelp = 1;
  315. break;
  316. }
  317. }
  318. synth.alias();
  319. if(exitwithversion) {
  320. cout << "Version: " << VERSION << endl;
  321. return 0;
  322. }
  323. if(exitwithhelp != 0) {
  324. cout << "Usage: zynaddsubfx [OPTION]\n\n"
  325. << " -h , --help \t\t\t\t Display command-line help and exit\n"
  326. << " -v , --version \t\t\t Display version and exit\n"
  327. << " -l file, --load=FILE\t\t\t Loads a .xmz file\n"
  328. << " -L file, --load-instrument=FILE\t Loads a .xiz file\n"
  329. << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n"
  330. <<
  331. " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n"
  332. << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n"
  333. << " -S , --swap\t\t\t\t Swap Left <--> Right\n"
  334. <<
  335. " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n"
  336. << " -N , --named\t\t\t\t Postfix IO Name when possible\n"
  337. << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n"
  338. << " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled for negative intervals)\n"
  339. << " -p , --pid-in-client-name\t\t Append PID to (JACK) "
  340. "client name\n"
  341. << " -P , --preferred-port\t\t\t Preferred OSC Port\n"
  342. << " -O , --output\t\t\t\t Set Output Engine\n"
  343. << " -I , --input\t\t\t\t Set Input Engine\n"
  344. << " -e , --exec-after-init\t\t Run post-initialization script\n"
  345. << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n"
  346. << " -u , --ui-title=TITLE\t\t Extend UI Window Titles\n"
  347. << endl;
  348. return 0;
  349. }
  350. cerr.precision(1);
  351. cerr << std::fixed;
  352. cerr << "\nSample Rate = \t\t" << synth.samplerate << endl;
  353. cerr << "Sound Buffer Size = \t" << synth.buffersize << " samples" << endl;
  354. cerr << "Internal latency = \t" << synth.dt() * 1000.0f << " ms" << endl;
  355. cerr << "ADsynth Oscil.Size = \t" << synth.oscilsize << " samples" << endl;
  356. initprogram(std::move(synth), &config, prefered_port);
  357. bool altered_master = false;
  358. if(!loadfile.empty()) {
  359. altered_master = true;
  360. const char *filename = loadfile.c_str();
  361. int tmp = master->loadXML(filename);
  362. if(tmp < 0) {
  363. cerr << "ERROR: Could not load master file " << loadfile
  364. << "." << endl;
  365. exit(1);
  366. }
  367. else {
  368. strncpy(master->last_xmz, filename, XMZ_PATH_MAX);
  369. master->last_xmz[XMZ_PATH_MAX-1] = 0;
  370. master->applyparameters();
  371. cout << "Master file loaded." << endl;
  372. }
  373. }
  374. if(!loadinstrument.empty()) {
  375. altered_master = true;
  376. int loadtopart = 0;
  377. int tmp = master->part[loadtopart]->loadXMLinstrument(
  378. loadinstrument.c_str());
  379. if(tmp < 0) {
  380. cerr << "ERROR: Could not load instrument file "
  381. << loadinstrument << '.' << endl;
  382. exit(1);
  383. }
  384. else {
  385. master->part[loadtopart]->applyparameters();
  386. master->part[loadtopart]->initialize_rt();
  387. cout << "Instrument file loaded." << endl;
  388. }
  389. }
  390. if(altered_master)
  391. middleware->updateResources(master);
  392. //Run the Nio system
  393. bool ioGood = Nio::start();
  394. if(!execAfterInit.empty()) {
  395. cout << "Executing user supplied command: " << execAfterInit << endl;
  396. if(system(execAfterInit.c_str()) == -1)
  397. cerr << "Command Failed..." << endl;
  398. }
  399. gui = NULL;
  400. //Capture Startup Responses
  401. typedef std::vector<const char *> wait_t;
  402. wait_t msg_waitlist;
  403. middleware->setUiCallback([](void*v,const char*msg) {
  404. wait_t &wait = *(wait_t*)v;
  405. size_t len = rtosc_message_length(msg, -1);
  406. char *copy = new char[len];
  407. memcpy(copy, msg, len);
  408. wait.push_back(copy);
  409. }, &msg_waitlist);
  410. if(!noui)
  411. gui = GUI::createUi(middleware->spawnUiApi(), &Pexitprogram);
  412. middleware->setUiCallback(GUI::raiseUi, gui);
  413. middleware->setIdleCallback([](void*){GUI::tickUi(gui);}, NULL);
  414. //Replay Startup Responses
  415. for(auto msg:msg_waitlist) {
  416. GUI::raiseUi(gui, msg);
  417. delete [] msg;
  418. }
  419. //set titles
  420. if(!ui_title.empty())
  421. GUI::raiseUi(gui, "/ui/title", "s", ui_title.c_str());
  422. if(!noui)
  423. {
  424. GUI::raiseUi(gui, "/show", "i", config.cfg.UserInterfaceMode);
  425. if(!ioGood)
  426. GUI::raiseUi(gui, "/alert", "s",
  427. "Default IO did not initialize.\nDefaulting to NULL backend.");
  428. }
  429. if(auto_save_interval >= 0) {
  430. int old_save = middleware->checkAutoSave();
  431. if(old_save > 0)
  432. GUI::raiseUi(gui, "/alert-reload", "i", old_save);
  433. middleware->enableAutoSave(auto_save_interval);
  434. }
  435. #if USE_NSM
  436. char *nsm_url = getenv("NSM_URL");
  437. if(nsm_url) {
  438. nsm = new NSM_Client;
  439. if(!nsm->init(nsm_url))
  440. nsm->announce("ZynAddSubFX", ":switch:", argv[0]);
  441. else {
  442. delete nsm;
  443. nsm = NULL;
  444. }
  445. }
  446. #endif
  447. #if USE_NSM
  448. if(!nsm)
  449. #endif
  450. {
  451. #if LASH
  452. lash = new LASHClient(&argc, &argv);
  453. GUI::raiseUi(gui, "/session-type", "s", "LASH");
  454. #endif
  455. }
  456. while(Pexitprogram == 0) {
  457. #if USE_NSM
  458. if(nsm) {
  459. nsm->check();
  460. goto done;
  461. }
  462. #endif
  463. #if LASH
  464. {
  465. string filename;
  466. switch(lash->checkevents(filename)) {
  467. case LASHClient::Save:
  468. GUI::raiseUi(gui, "/save-master", "s", filename.c_str());
  469. lash->confirmevent(LASHClient::Save);
  470. break;
  471. case LASHClient::Restore:
  472. GUI::raiseUi(gui, "/load-master", "s", filename.c_str());
  473. lash->confirmevent(LASHClient::Restore);
  474. break;
  475. case LASHClient::Quit:
  476. Pexitprogram = 1;
  477. default:
  478. break;
  479. }
  480. }
  481. #endif //LASH
  482. #if USE_NSM
  483. done:
  484. #endif
  485. GUI::tickUi(gui);
  486. middleware->tick();
  487. }
  488. exitprogram(config);
  489. return 0;
  490. }