/* ZynAddSubFX - a software synthesizer main.cpp - Main file of the synthesizer Copyright (C) 2002-2005 Nasca Octavian Paul Copyright (C) 2012-2014 Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "UI/common.H" #include #include #include #include #include #include #include #include #include "DSP/FFTwrapper.h" #include "Misc/Master.h" #include "Misc/Part.h" #include "Misc/Util.h" #include "Misc/Dump.h" extern Dump dump; //Nio System #include "Nio/Nio.h" #ifndef DISABLE_GUI #ifdef QT_GUI #include #include "masterui.h" QApplication *app; #elif defined FLTK_GUI #include "UI/MasterUI.h" #elif defined NTK_GUI #include "UI/MasterUI.h" #include #include #include #include #endif // FLTK_GUI MasterUI *ui; #endif //DISABLE_GUI using namespace std; pthread_t thr4; Master *master; SYNTH_T *synth; int swaplr = 0; //1 for left-right swapping int Pexitprogram = 0; //if the UI set this to 1, the program will exit #if LASH #include "Misc/LASHClient.h" LASHClient *lash = NULL; #endif #if USE_NSM #include "UI/NSM.H" NSM_Client *nsm = 0; #endif char *instance_name = 0; void exitprogram(); //cleanup on signaled exit void sigterm_exit(int /*sig*/) { Pexitprogram = 1; } #ifndef DISABLE_GUI #ifdef NTK_GUI static Fl_Tiled_Image *module_backdrop; #endif void set_module_parameters ( Fl_Widget *o ) { #ifdef NTK_GUI o->box( FL_DOWN_FRAME ); o->align( o->align() | FL_ALIGN_IMAGE_BACKDROP ); o->color( FL_BLACK ); o->image( module_backdrop ); o->labeltype( FL_SHADOW_LABEL ); #else o->box( FL_PLASTIC_UP_BOX ); o->color( FL_CYAN ); o->labeltype( FL_EMBOSSED_LABEL ); #endif } #endif /* * Program initialisation */ void initprogram(void) { cerr.precision(1); cerr << std::fixed; cerr << "\nSample Rate = \t\t" << synth->samplerate << endl; cerr << "Sound Buffer Size = \t" << synth->buffersize << " samples" << endl; cerr << "Internal latency = \t" << synth->buffersize_f * 1000.0f / synth->samplerate_f << " ms" << endl; cerr << "ADsynth Oscil.Size = \t" << synth->oscilsize << " samples" << endl; master = &Master::getInstance(); master->swaplr = swaplr; signal(SIGINT, sigterm_exit); signal(SIGTERM, sigterm_exit); } /* * Program exit */ void exitprogram() { //ensure that everything has stopped with the mutex wait pthread_mutex_lock(&master->mutex); pthread_mutex_unlock(&master->mutex); Nio::stop(); #ifndef DISABLE_GUI delete ui; #endif #if LASH if(lash) delete lash; #endif #if USE_NSM if(nsm) delete nsm; #endif delete [] denormalkillbuf; FFT_cleanup(); Master::deleteInstance(); } int main(int argc, char *argv[]) { synth = new SYNTH_T; config.init(); dump.startnow(); int noui = 0; cerr << "\nZynAddSubFX - Copyright (c) 2002-2011 Nasca Octavian Paul and others" << endl; cerr << " Copyright (c) 2009-2014 Mark McCurry [active maintainer]" << endl; cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; cerr << "This program is free software (GNU GPL v.2 or later) and \n"; cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; if(argc == 1) cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; /* Get the settings from the Config*/ synth->samplerate = config.cfg.SampleRate; synth->buffersize = config.cfg.SoundBufferSize; synth->oscilsize = config.cfg.OscilSize; swaplr = config.cfg.SwapStereo; Nio::preferedSampleRate(synth->samplerate); synth->alias(); //build aliases sprng(time(NULL)); /* Parse command-line options */ struct option opts[] = { { "load", 2, NULL, 'l' }, { "load-instrument", 2, NULL, 'L' }, { "sample-rate", 2, NULL, 'r' }, { "buffer-size", 2, NULL, 'b' }, { "oscil-size", 2, NULL, 'o' }, { "dump", 2, NULL, 'D' }, { "swap", 2, NULL, 'S' }, { "no-gui", 2, NULL, 'U' }, { "dummy", 2, NULL, 'Y' }, { "help", 2, NULL, 'h' }, { "version", 2, NULL, 'v' }, { "named", 1, NULL, 'N' }, { "auto-connect", 0, NULL, 'a' }, { "output", 1, NULL, 'O' }, { "input", 1, NULL, 'I' }, { "exec-after-init", 1, NULL, 'e' }, { 0, 0, 0, 0 } }; opterr = 0; int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; string loadfile, loadinstrument, execAfterInit; while(1) { int tmp = 0; /**\todo check this process for a small memory leak*/ opt = getopt_long(argc, argv, "l:L:r:b:o:I:O:N:e:hvaSDUY", opts, &option_index); char *optarguments = optarg; #define GETOP(x) if(optarguments) \ x = optarguments #define GETOPNUM(x) if(optarguments) \ x = atoi(optarguments) if(opt == -1) break; switch(opt) { case 'h': exitwithhelp = 1; break; case 'v': exitwithversion = 1; break; case 'Y': /* this command a dummy command (has NO effect) and is used because I need for NSIS installer (NSIS sometimes forces a command line for a program, even if I don't need that; eg. when I want to add a icon to a shortcut. */ break; case 'U': noui = 1; break; case 'l': GETOP(loadfile); break; case 'L': GETOP(loadinstrument); break; case 'r': GETOPNUM(synth->samplerate); if(synth->samplerate < 4000) { cerr << "ERROR:Incorrect sample rate: " << optarguments << endl; exit(1); } break; case 'b': GETOPNUM(synth->buffersize); if(synth->buffersize < 2) { cerr << "ERROR:Incorrect buffer size: " << optarguments << endl; exit(1); } break; case 'o': if(optarguments) synth->oscilsize = tmp = atoi(optarguments); if(synth->oscilsize < MAX_AD_HARMONICS * 2) synth->oscilsize = MAX_AD_HARMONICS * 2; synth->oscilsize = (int) powf(2, ceil(logf(synth->oscilsize - 1.0f) / logf(2.0f))); if(tmp != synth->oscilsize) cerr << "synth->oscilsize is wrong (must be 2^n) or too small. Adjusting to " << synth->oscilsize << "." << endl; break; case 'S': swaplr = 1; break; case 'D': dump.startnow(); break; case 'N': Nio::setPostfix(optarguments); break; case 'I': if(optarguments) Nio::setDefaultSource(optarguments); break; case 'O': if(optarguments) Nio::setDefaultSink(optarguments); break; case 'a': Nio::autoConnect = true; break; case 'e': GETOP(execAfterInit); break; case '?': cerr << "ERROR:Bad option or parameter.\n" << endl; exitwithhelp = 1; break; } } synth->alias(); if(exitwithversion) { cout << "Version: " << VERSION << endl; return 0; } if(exitwithhelp != 0) { cout << "Usage: zynaddsubfx [OPTION]\n\n" << " -h , --help \t\t\t\t Display command-line help and exit\n" << " -v , --version \t\t\t Display version and exit\n" << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" << " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" << " -S , --swap\t\t\t\t Swap Left <--> Right\n" << " -D , --dump\t\t\t\t Dumps midi note ON/OFF commands\n" << " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" << " -N , --named\t\t\t\t Postfix IO Name when possible\n" << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" << " -O , --output\t\t\t\t Set Output Engine\n" << " -I , --input\t\t\t\t Set Input Engine\n" << " -e , --exec-after-init\t\t Run post-initialization script\n" << endl; return 0; } //produce denormal buf denormalkillbuf = new float [synth->buffersize]; for(int i = 0; i < synth->buffersize; ++i) denormalkillbuf[i] = (RND - 0.5f) * 1e-16; initprogram(); if(!loadfile.empty()) { int tmp = master->loadXML(loadfile.c_str()); if(tmp < 0) { cerr << "ERROR: Could not load master file " << loadfile << "." << endl; exit(1); } else { master->applyparameters(); cout << "Master file loaded." << endl; } } if(!loadinstrument.empty()) { int loadtopart = 0; int tmp = master->part[loadtopart]->loadXMLinstrument( loadinstrument.c_str()); if(tmp < 0) { cerr << "ERROR: Could not load instrument file " << loadinstrument << '.' << endl; exit(1); } else { master->part[loadtopart]->applyparameters(); cout << "Instrument file loaded." << endl; } } //Run the Nio system bool ioGood = Nio::start(); if(!execAfterInit.empty()) { cout << "Executing user supplied command: " << execAfterInit << endl; if(system(execAfterInit.c_str()) == -1) cerr << "Command Failed..." << endl; } #ifndef DISABLE_GUI #ifdef NTK_GUI fl_register_images(); Fl_Tooltip::textcolor(0x0); Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL); if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/knob.png")) Fl_Dial::default_image(img); else Fl_Dial::default_image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/knob.png")); if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/window_backdrop.png")) Fl::scheme_bg(new Fl_Tiled_Image(img)); else Fl::scheme_bg(new Fl_Tiled_Image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/window_backdrop.png"))); if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/module_backdrop.png")) module_backdrop = new Fl_Tiled_Image(img); else module_backdrop = new Fl_Tiled_Image(Fl_Shared_Image::get(SOURCE_DIR "/../pixmaps/module_backdrop.png")); Fl::background( 50, 50, 50 ); Fl::background2( 70, 70, 70 ); Fl::foreground( 255,255,255 ); #endif ui = new MasterUI(master, &Pexitprogram); if ( !noui) { ui->showUI(); if(!ioGood) fl_alert( "Default IO did not initialize.\nDefaulting to NULL backend."); } #endif #ifndef DISABLE_GUI #if USE_NSM char *nsm_url = getenv("NSM_URL"); if(nsm_url) { nsm = new NSM_Client; if(!nsm->init(nsm_url)) nsm->announce("ZynAddSubFX", ":switch:", argv[0]); else { delete nsm; nsm = NULL; } } #endif #endif #if USE_NSM if(!nsm) #endif { #if LASH lash = new LASHClient(&argc, &argv); #ifndef DISABLE_GUI ui->sm_indicator1->value(1); ui->sm_indicator2->value(1); ui->sm_indicator1->tooltip("LASH"); ui->sm_indicator2->tooltip("LASH"); #endif #endif } while(Pexitprogram == 0) { #ifndef DISABLE_GUI #if USE_NSM if(nsm) { nsm->check(); goto done; } #endif #if LASH { string filename; switch(lash->checkevents(filename)) { case LASHClient::Save: ui->do_save_master(filename.c_str()); lash->confirmevent(LASHClient::Save); break; case LASHClient::Restore: ui->do_load_master(filename.c_str()); lash->confirmevent(LASHClient::Restore); break; case LASHClient::Quit: Pexitprogram = 1; default: break; } } #endif //LASH #if USE_NSM done: #endif Fl::wait(0.02f); #else usleep(100000); #endif } exitprogram(); return 0; }