2 ZynAddSubFX - a software synthesizer
4 main.cpp - Main file of the synthesizer
5 Copyright (C) 2002-2005 Nasca Octavian Paul
6 Copyright (C) 2012-2016 Mark McCurry
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
30 #include <rtosc/rtosc.h>
31 #include <rtosc/ports.h>
32 #include <rtosc/thread-link.h>
33 #include "Params/PADnoteParameters.h"
35 #include "DSP/FFTwrapper.h"
36 #include "Misc/PresetExtractor.h"
37 #include "Misc/Master.h"
38 #include "Misc/Part.h"
39 #include "Misc/Util.h"
40 #include "zyn-version.h"
44 #include "Nio/InMgr.h"
47 #include "UI/Connection.h"
51 #include "Misc/MiddleWare.h"
52 MiddleWare
*middleware
;
57 int swaplr
= 0; //1 for left-right swapping
59 extern int Pexitprogram
; //if the UI set this to 1, the program will exit
62 #include "Misc/LASHClient.h"
63 LASHClient
*lash
= NULL
;
72 char *instance_name
= 0;
74 void exitprogram(const Config
&config
);
77 //cleanup on signaled exit
78 void sigterm_exit(int /*sig*/)
86 * Program initialisation
88 void initprogram(SYNTH_T synth
, Config
* config
, int prefered_port
)
90 middleware
= new MiddleWare(std::move(synth
), config
, prefered_port
);
91 master
= middleware
->spawnMaster();
92 master
->swaplr
= swaplr
;
94 signal(SIGINT
, sigterm_exit
);
95 signal(SIGTERM
, sigterm_exit
);
96 Nio::init(master
->synth
, config
->cfg
.oss_devs
, master
);
102 void exitprogram(const Config
& config
)
106 middleware
->removeAutoSave();
122 //Windows MIDI OH WHAT A HACK...
125 #include <mmsystem.h>
127 HMIDIIN winmidiinhandle
= 0;
129 void CALLBACK
WinMidiInProc(HMIDIIN hMidiIn
,UINT wMsg
,DWORD dwInstance
,
130 DWORD dwParam1
,DWORD dwParam2
)
133 if (wMsg
==MIM_DATA
) {
136 if (cmd
==0xfe) return;
137 par1
=(dwParam1
>>8)&0xff;
139 int cmdchan
=cmd
&0x0f;
140 int cmdtype
=(cmd
>>4)&0x0f;
148 ev
.channel
= cmdchan
;
155 ev
.channel
= cmdchan
;
156 ev
.value
= par2
&0xff;
159 case(0xb)://controller
162 ev
.channel
= cmdchan
;
163 ev
.value
= par2
&0xff;
166 case(0xe)://pitch wheel
167 //tmp=(par1+par2*(long int) 128)-8192;
168 //winmaster->SetController(cmdchan,C_pitchwheel,tmp);
177 void InitWinMidi(int midi
)
180 for(int i
=0; i
<10; ++i
) {
181 long int res
=midiInOpen(&winmidiinhandle
,i
,(DWORD_PTR
)(void*)WinMidiInProc
,0,CALLBACK_FUNCTION
);
182 if(res
== MMSYSERR_NOERROR
) {
183 res
=midiInStart(winmidiinhandle
);
184 printf("[INFO] Starting Windows MIDI At %d with code %d(noerror=%d)\n", i
, res
, MMSYSERR_NOERROR
);
188 printf("[INFO] No Windows MIDI Device At id %d\n", i
);
194 // midiInStop(winmidiinhandle);
195 // midiInClose(winmidiinhandle);
198 void InitWinMidi(int) {}
202 int main(int argc
, char *argv
[])
209 << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others"
212 << " Copyright (c) 2009-2016 Mark McCurry [active maintainer]"
214 cerr
<< "Compiled: " << __DATE__
<< " " << __TIME__
<< endl
;
215 cerr
<< "This program is free software (GNU GPL v2 or later) and \n";
216 cerr
<< "it comes with ABSOLUTELY NO WARRANTY.\n" << endl
;
218 cerr
<< "Try 'zynaddsubfx --help' for command-line options." << endl
;
220 /* Get the settings from the Config*/
221 synth
.samplerate
= config
.cfg
.SampleRate
;
222 synth
.buffersize
= config
.cfg
.SoundBufferSize
;
223 synth
.oscilsize
= config
.cfg
.OscilSize
;
224 swaplr
= config
.cfg
.SwapStereo
;
226 Nio::preferedSampleRate(synth
.samplerate
);
228 synth
.alias(); //build aliases
232 /* Parse command-line options */
233 struct option opts
[] = {
238 "load-instrument", 2, NULL
, 'L'
241 "midi-learn", 2, NULL
, 'M'
244 "sample-rate", 2, NULL
, 'r'
247 "buffer-size", 2, NULL
, 'b'
250 "oscil-size", 2, NULL
, 'o'
256 "no-gui", 0, NULL
, 'U'
259 "dummy", 2, NULL
, 'Y'
265 "version", 2, NULL
, 'v'
268 "named", 1, NULL
, 'N'
271 "auto-connect", 0, NULL
, 'a'
274 "auto-save", 0, NULL
, 'A'
277 "pid-in-client-name", 0, NULL
, 'p'
280 "prefered-port", 1, NULL
, 'P',
283 "output", 1, NULL
, 'O'
286 "input", 1, NULL
, 'I'
289 "exec-after-init", 1, NULL
, 'e'
292 "dump-oscdoc", 2, NULL
, 'd'
295 "dump-json-schema", 2, NULL
, 'D'
302 int option_index
= 0, opt
, exitwithhelp
= 0, exitwithversion
= 0;
303 int prefered_port
= -1;
304 int auto_save_interval
= 60;
307 string loadfile
, loadinstrument
, execAfterInit
, loadmidilearn
;
312 /**\todo check this process for a small memory leak*/
313 opt
= getopt_long(argc
,
315 "l:L:M:r:b:o:I:O:N:e:P:A:D:hvapSDUYZ",
318 char *optarguments
= optarg
;
320 #define GETOP(x) if(optarguments) \
322 #define GETOPNUM(x) if(optarguments) \
323 x = atoi(optarguments)
336 case 'Y': /* this command a dummy command (has NO effect)
337 and is used because I need for NSIS installer
338 (NSIS sometimes forces a command line for a
339 program, even if I don't need that; eg. when
340 I want to add a icon to a shortcut.
350 GETOP(loadinstrument
);
353 GETOP(loadmidilearn
);
356 GETOPNUM(synth
.samplerate
);
357 if(synth
.samplerate
< 4000) {
358 cerr
<< "ERROR:Incorrect sample rate: " << optarguments
364 GETOPNUM(synth
.buffersize
);
365 if(synth
.buffersize
< 2) {
366 cerr
<< "ERROR:Incorrect buffer size: " << optarguments
373 synth
.oscilsize
= tmp
= atoi(optarguments
);
374 if(synth
.oscilsize
< MAX_AD_HARMONICS
* 2)
375 synth
.oscilsize
= MAX_AD_HARMONICS
* 2;
378 ceil(logf(synth
.oscilsize
- 1.0f
) / logf(2.0f
)));
379 if(tmp
!= synth
.oscilsize
)
382 "synth.oscilsize is wrong (must be 2^n) or too small. Adjusting to "
383 << synth
.oscilsize
<< "." << endl
;
389 Nio::setPostfix(optarguments
);
393 Nio::setDefaultSource(optarguments
);
397 Nio::setDefaultSink(optarguments
);
400 Nio::autoConnect
= true;
403 Nio::pidInClientName
= true;
407 prefered_port
= atoi(optarguments
);
411 auto_save_interval
= atoi(optarguments
);
414 GETOP(execAfterInit
);
419 rtosc::OscDocFormatter s
;
420 ofstream
outfile(optarguments
);
421 s
.prog_name
= "ZynAddSubFX";
422 s
.p
= &Master::ports
;
423 s
.uri
= "http://example.com/fake/";
424 s
.doc_origin
= "http://example.com/fake/url.xml";
425 s
.author_first
= "Mark";
426 s
.author_last
= "McCurry";
433 ofstream
outfile(optarguments
);
434 void dump_json(std::ostream
&o
,
435 const rtosc::Ports
&p
);
436 dump_json(outfile
, Master::ports
);
441 wmidi
= atoi(optarguments
);
444 cerr
<< "ERROR:Bad option or parameter.\n" << endl
;
452 if(exitwithversion
) {
453 cout
<< "Version: " << version
<< endl
;
456 if(exitwithhelp
!= 0) {
457 cout
<< "Usage: zynaddsubfx [OPTION]\n\n"
458 << " -h , --help \t\t\t\t Display command-line help and exit\n"
459 << " -v , --version \t\t\t Display version and exit\n"
460 << " -l file, --load=FILE\t\t\t Loads a .xmz file\n"
461 << " -L file, --load-instrument=FILE\t Loads a .xiz file\n"
462 << " -M file, --midi-learn=FILE\t\t Loads a .xlz file\n"
463 << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n"
465 " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n"
466 << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n"
467 << " -S , --swap\t\t\t\t Swap Left <--> Right\n"
469 " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n"
470 << " -N , --named\t\t\t\t Postfix IO Name when possible\n"
471 << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n"
472 << " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled with 0 interval)\n"
473 << " -p , --pid-in-client-name\t\t Append PID to (JACK) "
475 << " -P , --preferred-port\t\t\t Preferred OSC Port\n"
476 << " -O , --output\t\t\t\t Set Output Engine\n"
477 << " -I , --input\t\t\t\t Set Input Engine\n"
478 << " -e , --exec-after-init\t\t Run post-initialization script\n"
479 << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n"
487 cerr
<< "\nSample Rate = \t\t" << synth
.samplerate
<< endl
;
488 cerr
<< "Sound Buffer Size = \t" << synth
.buffersize
<< " samples" << endl
;
489 cerr
<< "Internal latency = \t" << synth
.dt() * 1000.0f
<< " ms" << endl
;
490 cerr
<< "ADsynth Oscil.Size = \t" << synth
.oscilsize
<< " samples" << endl
;
492 initprogram(std::move(synth
), &config
, prefered_port
);
494 bool altered_master
= false;
495 if(!loadfile
.empty()) {
496 altered_master
= true;
497 const char *filename
= loadfile
.c_str();
498 int tmp
= master
->loadXML(filename
);
500 cerr
<< "ERROR: Could not load master file " << loadfile
505 strncpy(master
->last_xmz
, filename
, XMZ_PATH_MAX
);
506 master
->last_xmz
[XMZ_PATH_MAX
-1] = 0;
507 master
->applyparameters();
508 cout
<< "Master file loaded." << endl
;
512 if(!loadinstrument
.empty()) {
513 altered_master
= true;
515 int tmp
= master
->part
[loadtopart
]->loadXMLinstrument(
516 loadinstrument
.c_str());
518 cerr
<< "ERROR: Could not load instrument file "
519 << loadinstrument
<< '.' << endl
;
523 master
->part
[loadtopart
]->applyparameters();
524 master
->part
[loadtopart
]->initialize_rt();
525 cout
<< "Instrument file loaded." << endl
;
529 if(!loadmidilearn
.empty()) {
531 rtosc_message(msg
, sizeof(msg
), "/load_xlz",
532 "s", loadmidilearn
.c_str());
533 middleware
->transmitMsg(msg
);
537 middleware
->updateResources(master
);
540 printf("[INFO] Nio::start()\n");
541 bool ioGood
= Nio::start();
543 printf("[INFO] exec-after-init\n");
544 if(!execAfterInit
.empty()) {
545 cout
<< "Executing user supplied command: " << execAfterInit
<< endl
;
546 if(system(execAfterInit
.c_str()) == -1)
547 cerr
<< "Command Failed..." << endl
;
555 //Capture Startup Responses
556 printf("[INFO] startup OSC\n");
557 typedef std::vector
<const char *> wait_t
;
559 middleware
->setUiCallback([](void*v
,const char*msg
) {
560 wait_t
&wait
= *(wait_t
*)v
;
561 size_t len
= rtosc_message_length(msg
, -1);
562 char *copy
= new char[len
];
563 memcpy(copy
, msg
, len
);
564 wait
.push_back(copy
);
567 printf("[INFO] UI calbacks\n");
569 gui
= GUI::createUi(middleware
->spawnUiApi(), &Pexitprogram
);
570 middleware
->setUiCallback(GUI::raiseUi
, gui
);
571 middleware
->setIdleCallback([](void*){GUI::tickUi(gui
);}, NULL
);
573 //Replay Startup Responses
574 printf("[INFO] OSC replay\n");
575 for(auto msg
:msg_waitlist
) {
576 GUI::raiseUi(gui
, msg
);
582 GUI::raiseUi(gui
, "/show", "i", config
.cfg
.UserInterfaceMode
);
584 GUI::raiseUi(gui
, "/alert", "s",
585 "Default IO did not initialize.\nDefaulting to NULL backend.");
588 printf("[INFO] auto_save setup\n");
589 if(auto_save_interval
> 0 && false) {
590 int old_save
= middleware
->checkAutoSave();
592 GUI::raiseUi(gui
, "/alert-reload", "i", old_save
);
593 middleware
->enableAutoSave(auto_save_interval
);
595 printf("[INFO] NSM Stuff\n");
597 //TODO move this stuff into Cmake
598 #if USE_NSM && defined(WIN32)
603 #if LASH && defined(WIN32)
609 char *nsm_url
= getenv("NSM_URL");
612 nsm
= new NSM_Client(middleware
);
614 if(!nsm
->init(nsm_url
))
615 nsm
->announce("ZynAddSubFX", ":switch:", argv
[0]);
623 printf("[INFO] LASH Stuff\n");
629 lash
= new LASHClient(&argc
, &argv
);
630 GUI::raiseUi(gui
, "/session-type", "s", "LASH");
636 printf("[INFO] Launching Zyn-Fusion...\n");
637 const char *addr
= middleware
->getServerAddress();
639 execlp("zyn-fusion", "zyn-fusion", addr
, "--builtin", "--no-hotload", 0);
640 execlp("./zyn-fusion", "zyn-fusion", addr
, "--builtin", "--no-hotload", 0);
642 err(1,"Failed to launch Zyn-Fusion");
647 printf("[INFO] Main Loop...\n");
648 while(Pexitprogram
== 0) {
659 switch(lash
->checkevents(filename
)) {
660 case LASHClient::Save
:
661 GUI::raiseUi(gui
, "/save-master", "s", filename
.c_str());
662 lash
->confirmevent(LASHClient::Save
);
664 case LASHClient::Restore
:
665 GUI::raiseUi(gui
, "/load-master", "s", filename
.c_str());
666 lash
->confirmevent(LASHClient::Restore
);
668 case LASHClient::Quit
: