1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $
5 //
6 // (C) Copyright 1999-2011 Werner Schweer (ws@seh.de)
7 //
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; version 2 of
11 // the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 //
22 //=========================================================
23
24 #include <QApplication>
25 #include <QCommandLineParser>
26 #include <QCommandLineOption>
27 #include <QDir>
28 #include <QFile>
29 //#include <QFileInfo>
30 #include <QFileInfoList>
31 #include <QDirIterator>
32 #include <QKeyEvent>
33 #include <QMessageBox>
34 #include <QLocale>
35 #include <QSplashScreen>
36 #include <QTimer>
37 #include <QTranslator>
38 #include <QIcon>
39 #include <QString>
40 #include <QStringList>
41 //#include <QStyle>
42 #include <QStyleFactory>
43 #include <QStyleHints>
44 #include <QStandardPaths>
45 #include <QTime>
46 #include <QDebug>
47 #include <QElapsedTimer>
48
49 //#include <iostream>
50
51 #include <time.h>
52 #ifndef _WIN32
53 #include <sys/mman.h>
54 #endif
55
56 #include "config.h"
57
58 #ifdef ALSA_SUPPORT
59 #include <alsa/asoundlib.h>
60 #endif
61
62 #include "al/al.h"
63 #include "al/dsp.h"
64 #include "app.h"
65 #include "audio.h"
66 #include "audiodev.h"
67 #include "gconfig.h"
68 #include "globals.h"
69 #include "helper.h"
70 #include "sync.h"
71 #include "functions.h"
72 #include "appearance.h"
73 #include "midiseq.h"
74 #include "minstrument.h"
75 #include "midiport.h"
76 #include "mididev.h"
77 #include "plugin.h"
78 #include "wavepreview.h"
79 #include "plugin_cache_writer.h"
80 #include "pluglist.h"
81 #include "metronome_class.h"
82 #include "audio_convert/audio_converter_plugin.h"
83 #include "audio_convert/audio_converter_settings_group.h"
84 #include "wave.h"
85 #include "conf.h"
86
87 #ifdef HAVE_LASH
88 #include <lash/lash.h>
89 #endif
90
91 namespace MusECore {
92 extern bool initDummyAudio();
93 #ifdef HAVE_RTAUDIO
94 extern bool initRtAudio(bool forceDefault = false);
95 #endif
96 extern bool initJackAudio();
97 extern void initMidiController();
98 extern void initMetronome();
99 //extern void initOSC();
100 extern void initVST();
101 extern void initVST_Native();
102 //extern void initPlugins();
103 extern void initDSSI();
104 #ifdef LV2_SUPPORT
105 extern void initLV2();
106 extern void deinitLV2();
107 #endif
108 //extern bool readConfiguration();
109
110 //extern void initMidiSequencer();
111 //extern void exitMidiSequencer();
112 extern void initAudio();
113 extern void initAudioPrefetch();
114 extern void initMidiSynth();
115
116 #ifdef ALSA_SUPPORT
117 extern snd_seq_t * alsaSeq;
118 #endif
119
120 extern void setAlsaClientName(const char*);
121 }
122
123 namespace MusEGui {
124 void initIcons(int cursorSize, const QString& gpath, const QString& upath);
125 void initShortCuts();
126 #ifdef HAVE_LASH
127 extern lash_client_t * lash_client;
128 #endif
129 extern QStringList projectRecentList;
130 }
131
132 enum AudioDriverSelect {
133 DriverConfigSetting,
134 DummyAudioOverride,
135 JackAudioOverride,
136 RtAudioOverride,
137
138 };
139
140 static QString locale_override;
141
142 //---------------------------------------------------------
143 // MuseApplication
144 //---------------------------------------------------------
145
146 class MuseApplication : public QApplication {
147 MusEGui::MusE* muse;
148
149 public:
MuseApplication(int & argc,char ** argv)150 MuseApplication(int& argc, char** argv)
151 : QApplication(argc, argv)
152 {
153 muse = nullptr;
154 }
155
156
setMuse(MusEGui::MusE * m)157 void setMuse(MusEGui::MusE* m) {
158 muse = m;
159
160 connect(this,SIGNAL(focusChanged(QWidget*,QWidget*)),muse,SLOT(focusChanged(QWidget*,QWidget*)));
161 #ifdef HAVE_LASH
162 if(MusEGlobal::useLASH)
163 startTimer (300);
164 #endif
165 }
166
notify(QObject * receiver,QEvent * event)167 bool notify(QObject* receiver, QEvent* event) override {
168 const bool flag = QApplication::notify(receiver, event);
169 const QEvent::Type type = event->type();
170 if (type == QEvent::KeyPress) {
171 const QMetaObject * mo = receiver->metaObject();
172 if (mo){
173 if (strcmp(mo->className(), "QWidgetWindow") == 0)
174 return false;
175 }
176 QKeyEvent* ke = (QKeyEvent*)event;
177 MusEGlobal::globalKeyState = ke->modifiers();
178
179 bool accepted = ke->isAccepted();
180 if (!accepted) {
181 int key = ke->key();
182 if (((QInputEvent*)ke)->modifiers() & Qt::ShiftModifier)
183 key += Qt::SHIFT;
184 if (((QInputEvent*)ke)->modifiers() & Qt::AltModifier)
185 key += Qt::ALT;
186 if (((QInputEvent*)ke)->modifiers() & Qt::ControlModifier)
187 key+= Qt::CTRL;
188 if(muse)
189 muse->kbAccel(key);
190 return true;
191 }
192 }
193 else if (type == QEvent::KeyRelease) {
194 QKeyEvent* ke = (QKeyEvent*)event;
195 ///MusEGlobal::globalKeyState = ke->stateAfter();
196 MusEGlobal::globalKeyState = ke->modifiers();
197 }
198
199 return flag;
200 }
201
202 #ifdef HAVE_LASH
timerEvent(QTimerEvent *)203 virtual void timerEvent (QTimerEvent*) {
204 if(muse && MusEGlobal::useLASH)
205 muse->lash_idle_cb ();
206 }
207 #endif /* HAVE_LASH */
208
209 };
210
211 //---------------------------------------------------------
212 // localeList
213 //---------------------------------------------------------
214
localeList()215 static QString localeList()
216 {
217 // Find out what translations are available:
218 QStringList deliveredLocaleListFiltered;
219 QString distLocale = MusEGlobal::museGlobalShare + "/locale";
220 QFileInfo distLocaleFi(distLocale);
221 if (distLocaleFi.isDir()) {
222 QDir dir = QDir(distLocale);
223 QStringList deliveredLocaleList = dir.entryList();
224 for(QStringList::iterator it = deliveredLocaleList.begin(); it != deliveredLocaleList.end(); ++it) {
225 QString item = *it;
226 if (item.endsWith(".qm")) {
227 int inipos = item.indexOf("muse_") + 5;
228 int finpos = item.lastIndexOf(".qm");
229 deliveredLocaleListFiltered << item.mid(inipos, finpos - inipos);
230 }
231 }
232 return deliveredLocaleListFiltered.join(",");
233 }
234 return QString("No translations found!");
235 }
236
fallbackDummy()237 void fallbackDummy() {
238
239 fprintf(stderr, "Falling back to dummy audio driver\n");
240 QMessageBox::critical(nullptr, "MusE fatal error", "MusE <b>failed</b> to find selected <b>audio server</b>.<br><br>"
241 "<i>MusE will continue <b>without audio support</b> (-a switch)!</i>");
242 MusEGlobal::realTimeScheduling = true;
243 MusECore::initDummyAudio();
244 }
245
246 //---------------------------------------------------------
247 // printExtraHelpText
248 //---------------------------------------------------------
249
printExtraHelpText()250 static void printExtraHelpText()
251 {
252 printf("\n");
253 #ifdef HAVE_LASH
254 printf("LASH and ");
255 #endif
256 printf("Qt options are also accepted. Some common Qt options:\n");
257 printf(" -style [=] style Set application GUI style (Fusion, Windows etc.)\n"
258 " -stylesheet [=] stylesheet Set application styleSheet\n"
259 " -session [=] session Restore application from an earlier session\n"
260 " -widgetcount Print debug message at end, about undestroyed/maximum widgets\n"
261 " -reverse Set application's layout direction to Qt::RightToLeft\n"
262 " -graphicssystem Set backend used for on-screen widgets/QPixmaps: raster or opengl\n"
263 " -qmljsdebugger = port Activate QML/JS debugger with port, formatted port:1234[,block]\n"
264 );
265
266 printf("\n");
267
268 printf("Some useful environment variables:\n\n");
269 printf(" LANG: Help browser language suffix (en etc.)\n\n");
270 printf("These variables are read ONCE upon first-time run, to fill the Plugin Paths\n"
271 " in Global Settings. Afterwards the paths can be altered in Global Settings:\n\n");
272 printf(" LADSPA_PATH: Override where to look for ladspa plugins, or else\n"
273 " ~/ladspa:~/.ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\n\n");
274 #ifdef DSSI_SUPPORT
275 printf(" DSSI_PATH: Override where to look for dssi plugins, or else\n"
276 " ~/dssi:~/.dssi:/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi\n\n" );
277 printf(" VST_PATH: Override where dssi-vst (if installed) looks for Wine vst plugins, or else\n"
278 " ~/vst win 32bit:~/.vst win 32bit or ~/vst:~/.vst on windows\n\n");
279 #endif
280 #ifdef VST_NATIVE_SUPPORT
281 printf(" LXVST_PATH: Override where to look for Linux vst plugins, or else VST_PATH, or else\n"
282 " ~/lxvst:~/.lxvst:/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst\n"
283 " also on Linux ~/vst:~/.vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst\n\n");
284 #endif
285 #ifdef LV2_SUPPORT
286 printf(" LV2_PATH: Override where to look for LV2 plugins or else\n"
287 " ~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2\n\n");
288 #endif
289 }
290
291 enum CommandLineParseResult
292 {
293 CommandLineOk,
294 CommandLineError,
295 CommandLineVersionRequested,
296 CommandLineHelpRequested
297 };
298
parseCommandLine(QCommandLineParser & parser,QString * errorMessage,QString & open_filename,AudioDriverSelect & audioType,bool & force_plugin_rescan,bool & dont_plugin_rescan)299 CommandLineParseResult parseCommandLine(
300 QCommandLineParser &parser, QString *errorMessage,
301 QString& open_filename, AudioDriverSelect& audioType, bool& force_plugin_rescan, bool& dont_plugin_rescan)
302 {
303 parser.setApplicationDescription(APP_DESCRIPTION);
304 const QString version_string(VERSION);
305 const QString git_string(GITSTRING);
306 if(git_string.isEmpty())
307 QCoreApplication::setApplicationVersion(version_string);
308 else
309 QCoreApplication::setApplicationVersion(version_string + ", (" + git_string + ")");
310 const QCommandLineOption helpOption = parser.addHelpOption();
311 const QCommandLineOption versionOption = parser.addVersionOption();
312
313 parser.addPositionalArgument("filename", QCoreApplication::translate("main", "File to open"));
314
315 QCommandLineOption option_a("a", QCoreApplication::translate("main", "Alsa midi only (using dummy audio driver)"));
316 parser.addOption(option_a);
317
318 #ifdef HAVE_RTAUDIO
319 QCommandLineOption option_t("t", QCoreApplication::translate("main", "Use RtAudio driver"));
320 parser.addOption(option_t);
321 #endif
322 QCommandLineOption option_j("j", QCoreApplication::translate("main", "Use JAckAudio driver to connect to Jack audio server"));
323 parser.addOption(option_j);
324 QCommandLineOption option_J("J", QCoreApplication::translate("main", "Do not try to auto-start the Jack audio server"));
325 parser.addOption(option_J);
326 QCommandLineOption option_F("F", QCoreApplication::translate("main",
327 "Do not auto-populate midi ports with midi devices found, at startup"));
328 parser.addOption(option_F);
329 QCommandLineOption option_A("A", QCoreApplication::translate("main", "Force inclusion of ALSA midi even if using Jack"));
330 parser.addOption(option_A);
331 QCommandLineOption option_P("P", QCoreApplication::translate("main",
332 "Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.)"), "n");
333 parser.addOption(option_P);
334 QCommandLineOption option_Y("Y", QCoreApplication::translate("main",
335 "Force midi real time priority to n (default: audio driver prio -1)\n"), "n");
336 parser.addOption(option_Y);
337
338 QCommandLineOption option_R("R", QCoreApplication::translate("main",
339 "Force plugin cache re-creation. (Automatic if any plugin path directories changed.)"));
340 parser.addOption(option_R);
341 QCommandLineOption option_C("C", QCoreApplication::translate("main",
342 "Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care."));
343 parser.addOption(option_C);
344 QCommandLineOption option_p("p", QCoreApplication::translate("main", "Don't load LADSPA plugins"));
345 parser.addOption(option_p);
346 QCommandLineOption option_S("S", QCoreApplication::translate("main", "Don't load MESS plugins"));
347 parser.addOption(option_S);
348 #ifdef VST_SUPPORT
349 QCommandLineOption option_V("V", QCoreApplication::translate("main", "Don't load VST plugins"));
350 parser.addOption(option_V);
351 #endif
352 #ifdef VST_NATIVE_SUPPORT
353 QCommandLineOption option_N("N", QCoreApplication::translate("main", "Don't load LinuxVST plugins"));
354 parser.addOption(option_N);
355 #endif
356 #ifdef DSSI_SUPPORT
357 QCommandLineOption option_I("I", QCoreApplication::translate("main", "Don't load DSSI plugins"));
358 parser.addOption(option_I);
359 #endif
360 #ifdef LV2_SUPPORT
361 QCommandLineOption option_2("2", QCoreApplication::translate("main", "Don't load LV2 plugins"));
362 parser.addOption(option_2);
363 #endif
364 #ifdef HAVE_LASH
365 QCommandLineOption option_L("L", QCoreApplication::translate("main", "Don't use LASH"));
366 parser.addOption(option_L);
367 #endif
368
369 QCommandLineOption option_l(QCommandLineOption("l", QCoreApplication::translate("main",
370 "Force locale to the given language/country code (xx = ") + localeList() + ")", "xx"));
371 parser.addOption(option_l);
372 QCommandLineOption option_u("u", QCoreApplication::translate("main",
373 "Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins."));
374 parser.addOption(option_u);
375 QCommandLineOption option_d("d", QCoreApplication::translate("main", "Debug mode: no threads, no RT"));
376 parser.addOption(option_d);
377 QCommandLineOption option_D("D", QCoreApplication::translate("main",
378 "Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively!"));
379 parser.addOption(option_D);
380 QCommandLineOption option_m("m", QCoreApplication::translate("main", "Debug mode: trace midi Input"));
381 parser.addOption(option_m);
382 QCommandLineOption option_M("M", QCoreApplication::translate("main", "Debug mode: trace midi Output"));
383 parser.addOption(option_M);
384 QCommandLineOption option_s("s", QCoreApplication::translate("main", "Debug mode: trace sync\n"));
385 parser.addOption(option_s);
386
387 #ifdef PYTHON_SUPPORT
388 QCommandLineOption option_y("y", QCoreApplication::translate("main", "Enable Python control support"));
389 parser.addOption(option_y);
390 QCommandLineOption option_pyro_ns_host("pyro-ns-host",
391 QCoreApplication::translate("main", "Pyro nameserver host name"), "hostname");
392 parser.addOption(option_pyro_ns_host);
393 QCommandLineOption option_pyro_ns_port("pyro-ns-port",
394 QCoreApplication::translate("main", "Pyro nameserver host port"), "port");
395 parser.addOption(option_pyro_ns_port);
396 QCommandLineOption option_pyro_daemon_host("pyro-daemon-host",
397 QCoreApplication::translate("main", "Pyro daemon host name"), "hostname");
398 parser.addOption(option_pyro_daemon_host);
399 QCommandLineOption option_pyro_daemon_port("pyro-daemon-port",
400 QCoreApplication::translate("main", "Pyro daemon host port"), "port");
401 parser.addOption(option_pyro_daemon_port);
402 QCommandLineOption option_pyro_comm_timeout("pyro-comm-timeout",
403 QCoreApplication::translate("main", "Pyro communication timeout in seconds"), "timeout");
404 parser.addOption(option_pyro_comm_timeout);
405 #endif
406
407 if(!parser.parse(QCoreApplication::arguments()))
408 {
409 *errorMessage = parser.errorText();
410 return CommandLineError;
411 }
412
413 if(parser.isSet(versionOption))
414 return CommandLineVersionRequested;
415
416 if(parser.isSet(helpOption))
417 return CommandLineHelpRequested;
418
419 const QStringList used_positional_args = parser.positionalArguments();
420 const int used_positional_args_sz = used_positional_args.size();
421 if(used_positional_args_sz > 1)
422 {
423 *errorMessage = "Error: Expected only one positional argument";
424 return CommandLineError;
425 }
426 else if(used_positional_args_sz == 1)
427 {
428 open_filename = used_positional_args.first();
429 }
430
431 if(parser.isSet(option_a))
432 audioType = DummyAudioOverride;
433
434 if(parser.isSet(option_l))
435 locale_override = parser.value(option_l);
436
437 #ifdef HAVE_RTAUDIO
438 if(parser.isSet(option_t))
439 audioType = RtAudioOverride;
440 #endif
441
442 if(parser.isSet(option_J))
443 MusEGlobal::noAutoStartJack = true;
444
445 if(parser.isSet(option_j))
446 audioType = JackAudioOverride;
447
448 if(parser.isSet(option_F))
449 MusEGlobal::populateMidiPortsOnStart = false;
450
451 if(parser.isSet(option_A))
452 MusEGlobal::useAlsaWithJack = true;
453
454 if(parser.isSet(option_d))
455 {
456 MusEGlobal::debugMode = true;
457 MusEGlobal::realTimeScheduling = false;
458 }
459
460 if(parser.isSet(option_D))
461 {
462 if(!MusEGlobal::debugMsg)
463 MusEGlobal::debugMsg=true;
464 else
465 MusEGlobal::heavyDebugMsg=true;
466 }
467
468 if(parser.isSet(option_m))
469 MusEGlobal::midiInputTrace = true;
470
471 if(parser.isSet(option_M))
472 MusEGlobal::midiOutputTrace = true;
473
474 if(parser.isSet(option_s))
475 MusEGlobal::debugSync = true;
476
477 if(parser.isSet(option_u))
478 MusEGlobal::unityWorkaround = true;
479
480 if(parser.isSet(option_P))
481 MusEGlobal::realTimePriority = parser.value(option_P).toInt();
482
483 if(parser.isSet(option_Y))
484 MusEGlobal::midiRTPrioOverride = parser.value(option_Y).toInt();
485
486 if(parser.isSet(option_p))
487 MusEGlobal::loadPlugins = false;
488
489 if(parser.isSet(option_R))
490 force_plugin_rescan = true;
491
492 if(parser.isSet(option_C))
493 dont_plugin_rescan = true;
494
495 if(parser.isSet(option_S))
496 MusEGlobal::loadMESS = false;
497
498 #ifdef VST_SUPPORT
499 if(parser.isSet(option_V))
500 MusEGlobal::loadVST = false;
501 #endif
502
503 #ifdef VST_NATIVE_SUPPORT
504 if(parser.isSet(option_N))
505 MusEGlobal::loadNativeVST = false;
506 #endif
507
508 #ifdef DSSI_SUPPORT
509 if(parser.isSet(option_I))
510 MusEGlobal::loadDSSI = false;
511 #endif
512
513 #ifdef HAVE_LASH
514 if(parser.isSet(option_L))
515 MusEGlobal::useLASH = false;
516 #endif
517
518 #ifdef LV2_SUPPORT
519 if(parser.isSet(option_2))
520 MusEGlobal::loadLV2 = false;
521 #endif
522
523 #ifdef PYTHON_SUPPORT
524 if(parser.isSet(option_y))
525 {
526 MusEGlobal::usePythonBridge = true;
527
528 if(parser.isSet(option_pyro_ns_host))
529 MusEGlobal::pythonBridgePyroNSHostname = parser.value(option_pyro_ns_host);
530
531 if(parser.isSet(option_pyro_ns_port))
532 MusEGlobal::pythonBridgePyroNSPort = parser.value(option_pyro_ns_port);
533
534 if(parser.isSet(option_pyro_daemon_host))
535 MusEGlobal::pythonBridgePyroDaemonHostname = parser.value(option_pyro_daemon_host);
536
537 if(parser.isSet(option_pyro_daemon_port))
538 MusEGlobal::pythonBridgePyroDaemonPort = parser.value(option_pyro_daemon_port);
539
540 if(parser.isSet(option_pyro_comm_timeout))
541 MusEGlobal::pythonBridgePyroCommTimeout = parser.value(option_pyro_comm_timeout).toFloat();
542 }
543 #endif
544
545 return CommandLineOk;
546 }
547
548
549 //---------------------------------------------------------
550 // main
551 //---------------------------------------------------------
552
main(int argc,char * argv[])553 int main(int argc, char* argv[])
554 {
555 // Get the separator used for file paths.
556 const QChar list_separator = QDir::listSeparator();
557
558 // Get environment variables for various paths.
559 // "The Qt environment manipulation functions are thread-safe, but this requires that
560 // the C library equivalent functions like getenv and putenv are not directly called."
561 // "Note: on desktop Windows, qgetenv() may produce data loss if the original string
562 // contains Unicode characters not representable in the ANSI encoding.
563 // Use qEnvironmentVariable() instead. On Unix systems, this function is lossless."
564 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
565 const QString ladspa_path = qEnvironmentVariable("LADSPA_PATH");
566 const QString dssi_path = qEnvironmentVariable("DSSI_PATH");
567 const QString vst_path = qEnvironmentVariable("VST_PATH");
568 // This Linux VST path is known to be used by Ardour.
569 const QString lxvst_path = qEnvironmentVariable("LXVST_PATH");
570 const QString lv2_path = qEnvironmentVariable("LV2_PATH");
571 #else
572 // "To convert the data to a QString use QString::fromLocal8Bit()."
573 const QString ladspa_path = QString::fromLocal8Bit(qgetenv("LADSPA_PATH"));
574 const QString dssi_path = QString::fromLocal8Bit(qgetenv("DSSI_PATH"));
575 const QString vst_path = QString::fromLocal8Bit(qgetenv("VST_PATH"));
576 const QString lxvst_path = QString::fromLocal8Bit(qgetenv("LXVST_PATH"));
577 const QString lv2_path = QString::fromLocal8Bit(qgetenv("LV2_PATH"));
578 #endif
579
580
581 QString last_project_filename;
582 bool last_project_was_template = false;
583 bool last_project_loaded_config = false;
584 bool plugin_rescan_already_done = false;
585 int rv = 0;
586
587 //==============================================
588 // BEGIN Restart loop. For (re)starting the app.
589 //==============================================
590
591 bool is_restarting = true; // First-time init true.
592 while(is_restarting)
593 {
594 is_restarting = false;
595
596 //QTime timer; // Deprecated
597 QElapsedTimer timer;
598 timer.start();
599
600 // Make working copies of the arguments.
601 const int argument_count = argc;
602 int argc_copy = argc;
603 char** argv_copy = nullptr;
604 if(argument_count > 0)
605 {
606 argv_copy = (char**)malloc(argument_count * sizeof(char*));
607 int len = 0;
608 for(int i = 0; i < argument_count; ++i)
609 {
610 argv_copy[i] = nullptr;
611 if(argv[i])
612 {
613 len = strlen(argv[i]);
614 argv_copy[i] = (char*)malloc(len + 2);
615 strcpy(argv_copy[i], argv[i]);
616 }
617 }
618 }
619
620 // Let LASH remove its recognized arguments first (generally longer than Qt's).
621 // Tip: LADISH's LASH emulation (current 1.0) does not take any arguments.
622 #ifdef HAVE_LASH
623 lash_args_t * lash_args = 0;
624 lash_args = lash_extract_args (&argc_copy, &argv_copy);
625 #endif
626
627 // Now create the application, and let Qt remove recognized arguments.
628 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
629 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
630
631 // Qt style must be set before app object is created (->Qt docu)
632 // Should the standard Qt style (Fusion) get removed in future,
633 // we're OK to just keep the current system style here (kybos)
634 if (QStyleFactory::keys().contains(MusEGlobal::defaultStyle, Qt::CaseInsensitive))
635 QApplication::setStyle(MusEGlobal::defaultStyle);
636
637 //========================
638 // Application instance:
639 //========================
640
641 MuseApplication app(argc_copy, argv_copy);
642 // if (QStyle* def_style = app.style())
643 // {
644 // const QString appStyleObjName = def_style->objectName();
645 // MusEGui::Appearance::getSetDefaultStyle(&appStyleObjName);
646 // }
647
648 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
649 << "Read configuration...";
650
651 app.setOrganizationName(ORGANIZATION_NAME);
652 app.setOrganizationDomain(ORGANIZATION_DOMAIN);
653 app.setApplicationName(PACKAGE_NAME);
654 app.setApplicationDisplayName(APP_DISPLAY_NAME);
655
656 // NOTE: 'GenericConfigLocation' returned config dir (ie. ~./config).
657 // 'ConfigLocation' also returned config dir (ie. ~./config).
658 // 'AppConfigLocation' (Qt 5.5) returned config + organization name + application name dirs
659 // (ie. ~./config/MusE/MusE-qt).
660 // Beware, setting application name and organization name influence these locations.
661
662 // "Returns a directory location where user-specific configuration files should be written.
663 // This is an application-specific directory, and the returned path is never empty.
664 // This enum value was added in Qt 5.5."
665 MusEGlobal::configPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
666
667 // "Returns a directory location where user-specific non-essential (cached) data should be written.
668 // This is an application-specific directory. The returned path is never empty."
669 // NOTE: This returned cache + organization name + application name dirs (ie. ~./cache/MusE/MusE-qt).
670 MusEGlobal::cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
671
672 MusEGlobal::museUser = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
673 MusEGlobal::museGlobalLib = QString(LIBDIR);
674 MusEGlobal::museGlobalShare = QString(SHAREDIR);
675
676 const QByteArray appDir = qgetenv("APPDIR"); // running in AppImage
677 if (!appDir.isEmpty()) {
678 MusEGlobal::museGlobalLib = appDir + MusEGlobal::museGlobalLib;
679 MusEGlobal::museGlobalShare = appDir + MusEGlobal::museGlobalShare;
680 }
681
682 MusEGlobal::museProject = MusEGlobal::museProjectInitPath; //getcwd(0, 0);
683 MusEGlobal::museInstruments = MusEGlobal::museGlobalShare + "/instruments";
684
685 MusEGlobal::configName = MusEGlobal::configPath + "/MusE-seq.cfg";
686
687 const QString oldConfigPath(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
688 + "/" + PACKAGE_NAME);
689
690 const QString old_qtconfig_name(oldConfigPath + "/MusE-qt.conf");
691 const QString new_qtconfig_name(oldConfigPath + "/MusE.conf");
692
693 const QString new_plugin_cache_path(MusEGlobal::cachePath + "/scanner");
694
695 // If the new-style plugin cache directory location doesn't exist yet, and an
696 // old-style plugin cache directory exists, rename the old one to the new one.
697 if(!MusEGlobal::cachePath.isEmpty())
698 {
699 QDir new_plugin_cache_dir(new_plugin_cache_path);
700 if(!new_plugin_cache_dir.exists())
701 {
702 const QString old_plugin_cache_path(oldConfigPath + "/scanner");
703 QDir old_plugin_cache_dir(old_plugin_cache_path);
704 if(old_plugin_cache_dir.exists())
705 {
706 QDir(MusEGlobal::cachePath).mkpath(".");
707 if(!QDir().rename(old_plugin_cache_path, new_plugin_cache_path))
708 fprintf(stderr, "Error renaming plugin cache dir:<%s> to:<%s>\n",
709 old_plugin_cache_path.toLocal8Bit().constData(),
710 new_plugin_cache_path.toLocal8Bit().constData());
711 }
712 }
713 }
714
715 // If the new-style config directory location doesn't exist yet, and an
716 // old-style config directory exists, rename the old one to the new one.
717 if(!MusEGlobal::configPath.isEmpty())
718 {
719 QDir new_config_dir(MusEGlobal::configPath);
720 if(!new_config_dir.exists())
721 {
722 QFileInfoList fil;
723 QDir old_config_dir(oldConfigPath);
724 if(old_config_dir.exists())
725 fil = old_config_dir.entryInfoList(QDir::AllEntries| QDir::NoDotAndDotDot);
726
727 // Create the new directory AFTER grabbing the existing list so that
728 // if the new directory is a subdirectory of the existing one,
729 // it will not show up in the list.
730 new_config_dir.mkpath(".");
731
732 if(!fil.isEmpty())
733 {
734 QFileInfo fi;
735 foreach(fi, fil)
736 {
737 const QString afp(fi.absoluteFilePath());
738 // DO NOT move the old MusE-qt config file.
739 // Given an organization name and application name, that is where
740 // QSettings are stored it (ie. ~/.config/MusE not ~/.config/MusE/MusE).
741 if(afp == old_qtconfig_name)
742 continue;
743 const QString fn = fi.fileName();
744 const QString new_fn(MusEGlobal::configPath + "/" + fn);
745 if(fi.isDir())
746 {
747 if(!QDir().rename(afp, new_fn))
748 fprintf(stderr, "Error renaming config dir:<%s> to:<%s>\n",
749 afp.toLocal8Bit().constData(), new_fn.toLocal8Bit().constData());
750 }
751 else
752 {
753 QFile f(afp);
754 if(!f.rename(new_fn))
755 fprintf(stderr, "Error renaming config file:<%s> to:<%s>\n",
756 afp.toLocal8Bit().constData(), new_fn.toLocal8Bit().constData());
757 }
758 }
759 }
760 }
761 }
762
763 {
764 const QString old_config_name(MusEGlobal::configPath + "/MusE.cfg");
765 // Rename existing config file to new name.
766 QFile oldConfigFile(old_config_name);
767 if(oldConfigFile.exists())
768 oldConfigFile.rename(MusEGlobal::configName);
769 }
770
771 bool cConfExists = false;
772 {
773 QFile cConf (MusEGlobal::configName);
774 cConfExists = cConf.exists();
775 }
776
777 {
778 QFile oldQtConfigFile(old_qtconfig_name);
779 if(oldQtConfigFile.exists())
780 oldQtConfigFile.rename(new_qtconfig_name);
781 }
782
783 // User instruments dir:
784 MusEGlobal::museUserInstruments = MusEGlobal::configPath + "/instruments";
785 // Create user instruments dir if it doesn't exist
786 {
787 QDir uinstrDir = QDir(MusEGlobal::museUserInstruments);
788 if(!uinstrDir.exists())
789 {
790 fprintf(stderr, "User instrument directory does not exist. Creating it.\n");
791 uinstrDir.mkpath(".");
792 }
793 }
794
795 MusEGui::initShortCuts();
796
797 // Discover available MusE audio converters, before reading configuration
798 MusEGlobal::audioConverterPluginList.discover(MusEGlobal::museGlobalLib, MusEGlobal::debugMsg);
799 // Default, non-local settings.
800 MusEGlobal::defaultAudioConverterSettings = new MusECore::AudioConverterSettingsGroup(false);
801 MusEGlobal::defaultAudioConverterSettings->populate(&MusEGlobal::audioConverterPluginList, false);
802
803 MusECore::readConfiguration();
804
805 // Need to put a sane defaults here because we can't use '~' in the file name strings.
806 if(!cConfExists)
807 {
808 MusEGlobal::config.projectBaseFolder = MusEGlobal::museUser + QString("/MusE");
809 MusEGlobal::config.startSong = "";
810 }
811
812 app.instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !MusEGlobal::config.showIconsInMenus);
813 app.instance()->setAttribute(Qt::AA_DontUseNativeDialogs, !MusEGlobal::config.useNativeStandardDialogs);
814
815 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
816 app.styleHints()->setShowShortcutsInContextMenus(true);
817 #endif
818
819 //=================
820 // LADSPA paths:
821 //=================
822 bool found = false;
823 if(MusEGlobal::config.pluginLadspaPathList.isEmpty())
824 {
825 if(ladspa_path.isEmpty())
826 {
827 MusEGlobal::config.pluginLadspaPathList <<
828 (MusEGlobal::museUser + QString("/ladspa")) <<
829 (MusEGlobal::museUser + QString("/.ladspa")) <<
830 QString("/usr/local/lib64/ladspa") <<
831 QString("/usr/local/lib/ladspa") <<
832 QString("/usr/lib64/ladspa") <<
833 QString("/usr/lib/ladspa");
834 }
835 else
836 {
837 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
838 #if QT_VERSION >= 0x050e00
839 MusEGlobal::config.pluginLadspaPathList = ladspa_path.split(list_separator, Qt::SkipEmptyParts);
840 #else
841 MusEGlobal::config.pluginLadspaPathList = ladspa_path.split(list_separator, QString::SkipEmptyParts);
842 #endif
843 found = true;
844 }
845 }
846 if(!found && qputenv("LADSPA_PATH", MusEGlobal::config.pluginLadspaPathList.join(list_separator).toLocal8Bit()) == 0)
847 fprintf(stderr, "Error setting LADSPA_PATH\n");
848
849 //===============
850 // DSSI paths:
851 //===============
852 found = false;
853 if(MusEGlobal::config.pluginDssiPathList.isEmpty())
854 {
855 if(dssi_path.isEmpty())
856 {
857 MusEGlobal::config.pluginDssiPathList <<
858 (MusEGlobal::museUser + QString("/dssi")) <<
859 (MusEGlobal::museUser + QString("/.dssi")) <<
860 QString("/usr/local/lib64/dssi") <<
861 QString("/usr/local/lib/dssi") <<
862 QString("/usr/lib64/dssi") <<
863 QString("/usr/lib/dssi");
864 }
865 else
866 {
867 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
868 #if QT_VERSION >= 0x050e00
869 MusEGlobal::config.pluginDssiPathList = dssi_path.split(list_separator, Qt::SkipEmptyParts);
870 #else
871 MusEGlobal::config.pluginDssiPathList = dssi_path.split(list_separator, QString::SkipEmptyParts);
872 #endif
873 found = true;
874 }
875 }
876 if(!found && qputenv("DSSI_PATH", MusEGlobal::config.pluginDssiPathList.join(list_separator).toLocal8Bit()) == 0)
877 fprintf(stderr, "Error setting DSSI_PATH\n");
878
879 //=======================
880 // Win VST (*.dll) paths:
881 //=======================
882 found = false;
883 if(MusEGlobal::config.pluginVstPathList.isEmpty())
884 {
885 if(vst_path.isEmpty())
886 {
887 MusEGlobal::config.pluginVstPathList <<
888 // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files.
889 // Otherwise on Linux for example, vst is where Linux vst *.so files are found.
890 #ifdef Q_OS_WIN
891 // TODO: Refine this for Q_OS_WIN. Where exactly do we look though?
892 (MusEGlobal::museUser + QString("/vst")) <<
893 (MusEGlobal::museUser + QString("/.vst"));
894 #else
895 (MusEGlobal::museUser + QString("/vst win 32bit")) <<
896 (MusEGlobal::museUser + QString("/.vst win 32bit"));
897 #endif
898 }
899 else
900 {
901 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
902 #if QT_VERSION >= 0x050e00
903 MusEGlobal::config.pluginVstPathList = vst_path.split(list_separator, Qt::SkipEmptyParts);
904 #else
905 MusEGlobal::config.pluginVstPathList = vst_path.split(list_separator, QString::SkipEmptyParts);
906 #endif
907 found = true;
908 }
909 }
910 if(!found && qputenv("VST_PATH", MusEGlobal::config.pluginVstPathList.join(list_separator).toLocal8Bit()) == 0)
911 fprintf(stderr, "Error setting VST_PATH\n");
912
913 //=======================
914 // LinuxVST (*.so) paths:
915 //=======================
916 found = false;
917 if(MusEGlobal::config.pluginLinuxVstPathList.isEmpty())
918 {
919 if(lxvst_path.isEmpty())
920 {
921 if(vst_path.isEmpty())
922 {
923 MusEGlobal::config.pluginLinuxVstPathList <<
924
925 // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files.
926 // Otherwise on Linux for example, vst is where Linux vst *.so files are found.
927 // On win, lxvst should be safe, likely where Linux vst *.so files might be found (if that's even a thing!).
928 #ifndef Q_OS_WIN
929 (MusEGlobal::museUser + QString("/vst")) <<
930 #endif
931 (MusEGlobal::museUser + QString("/lxvst")) <<
932
933 #ifndef Q_OS_WIN
934 (MusEGlobal::museUser + QString("/.vst")) <<
935 #endif
936 (MusEGlobal::museUser + QString("/.lxvst")) <<
937
938 #ifndef Q_OS_WIN
939 QString("/usr/local/lib64/vst") <<
940 #endif
941 QString("/usr/local/lib64/lxvst") <<
942
943 #ifndef Q_OS_WIN
944 QString("/usr/local/lib/vst") <<
945 #endif
946 QString("/usr/local/lib/lxvst") <<
947
948 #ifndef Q_OS_WIN
949 QString("/usr/lib64/vst") <<
950 #endif
951 QString("/usr/lib64/lxvst") <<
952
953 #ifndef Q_OS_WIN
954 QString("/usr/lib/vst") <<
955 #endif
956 QString("/usr/lib/lxvst");
957
958 }
959 else
960 {
961 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
962 #if QT_VERSION >= 0x050e00
963 MusEGlobal::config.pluginLinuxVstPathList = vst_path.split(list_separator, Qt::SkipEmptyParts);
964 #else
965 MusEGlobal::config.pluginLinuxVstPathList = vst_path.split(list_separator, QString::SkipEmptyParts);
966 #endif
967 found = true;
968 }
969 }
970 else
971 {
972 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
973 #if QT_VERSION >= 0x050e00
974 MusEGlobal::config.pluginLinuxVstPathList = lxvst_path.split(list_separator, Qt::SkipEmptyParts);
975 #else
976 MusEGlobal::config.pluginLinuxVstPathList = lxvst_path.split(list_separator, QString::SkipEmptyParts);
977 #endif
978 found = true;
979 }
980 }
981 if(!found && qputenv("LXVST_PATH", MusEGlobal::config.pluginLinuxVstPathList.join(list_separator).toLocal8Bit()) == 0)
982 fprintf(stderr, "Error setting LXVST_PATH\n");
983
984 //==============
985 // LV2 paths:
986 //==============
987 // Special for LV2: Since we use the recommended lilv_world_load_all()
988 // not lilv_world_load_bundle(), LV2_PATH seems to be the only way to set paths.
989 found = false;
990 if(MusEGlobal::config.pluginLv2PathList.isEmpty())
991 {
992 if(lv2_path.isEmpty())
993 {
994 MusEGlobal::config.pluginLv2PathList <<
995 (MusEGlobal::museUser + QString("/lv2")) <<
996 (MusEGlobal::museUser + QString("/.lv2")) <<
997 QString("/usr/local/lib64/lv2") <<
998 QString("/usr/local/lib/lv2") <<
999 QString("/usr/lib64/lv2") <<
1000 QString("/usr/lib/lv2");
1001 }
1002 else
1003 {
1004 // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14.
1005 #if QT_VERSION >= 0x050e00
1006 MusEGlobal::config.pluginLv2PathList = lv2_path.split(list_separator, Qt::SkipEmptyParts);
1007 #else
1008 MusEGlobal::config.pluginLv2PathList = lv2_path.split(list_separator, QString::SkipEmptyParts);
1009 #endif
1010 found = true;
1011 }
1012 }
1013 if(!found && qputenv("LV2_PATH", MusEGlobal::config.pluginLv2PathList.join(list_separator).toLocal8Bit()) == 0)
1014 fprintf(stderr, "Error setting LV2_PATH\n");
1015
1016
1017 // BEGIN Parse command line options
1018 //----------------------------------
1019 QString open_filename;
1020 AudioDriverSelect audioType = DriverConfigSetting;
1021 bool force_plugin_rescan = false;
1022 bool dont_plugin_rescan = false;
1023 // A block because we don't want ths hanging around. Use it then lose it.
1024 {
1025 QCommandLineParser parser;
1026 QString errorMessage;
1027 switch (parseCommandLine(parser, &errorMessage, open_filename,
1028 audioType, force_plugin_rescan, dont_plugin_rescan))
1029 {
1030 case CommandLineOk:
1031 break;
1032 case CommandLineError:
1033 fputs(qPrintable(errorMessage), stderr);
1034 fputs("\n\n", stderr);
1035 fputs(qPrintable(parser.helpText()), stderr);
1036 printExtraHelpText();
1037 #ifdef HAVE_LASH
1038 if(lash_args) lash_args_destroy(lash_args);
1039 #endif
1040 return 1;
1041 case CommandLineVersionRequested:
1042 printf("%s %s\n", qPrintable(QCoreApplication::applicationName()),
1043 qPrintable(QCoreApplication::applicationVersion()));
1044 #ifdef HAVE_LASH
1045 if(lash_args) lash_args_destroy(lash_args);
1046 #endif
1047 return 0;
1048 case CommandLineHelpRequested:
1049 // Works OK, but we want extra help text. Also the lash args destroy thingy...
1050 //parser.showHelp();
1051 //Q_UNREACHABLE();
1052 fputs(qPrintable(parser.helpText()), stdout);
1053 printExtraHelpText();
1054 #ifdef HAVE_LASH
1055 if(lash_args) lash_args_destroy(lash_args);
1056 #endif
1057 return 0;
1058 }
1059 }
1060 // END Parse command line options
1061 //----------------------------------
1062
1063 // Set some AL library namespace debug flags as well.
1064 // Make sure the AL namespace variables mirror our variables.
1065 AL::debugMsg = MusEGlobal::debugMsg;
1066 AL::denormalBias = MusEGlobal::denormalBias;
1067 AL::division = MusEGlobal::config.division;
1068 AL::sampleRate = MusEGlobal::sampleRate;
1069 AL::mtcType = MusEGlobal::mtcType;
1070
1071 // REMOVE Tim. py. Removed. TEST Keep this? Think not. It was for getting the last option (the filename)
1072 // when we were using getopt() but now we use QCommandLineParser. Un-needed ?
1073 // argc_copy -= optind;
1074 // ++argc_copy;
1075
1076 srand(time(nullptr)); // initialize random number generator
1077 //signal(SIGCHLD, catchSignal); // interferes with initVST(). see also app.cpp, function catchSignal()
1078
1079 static QTranslator translator(nullptr);
1080 {
1081 QString locale(QLocale::system().name());
1082 if (locale_override.length() >0 )
1083 locale = locale_override;
1084 if (locale != "C") {
1085 QString loc("muse_");
1086 loc += locale;
1087 if (!translator.load(loc, QString("."))) {
1088 QString lp(MusEGlobal::museGlobalShare);
1089 lp += QString("/locale");
1090 if (!translator.load(loc, lp)) {
1091 fprintf(stderr, "no locale <%s>/<%s>\n", loc.toLatin1().constData(), lp.toLatin1().constData());
1092 }
1093 }
1094 app.installTranslator(&translator);
1095 }
1096
1097 QLocale def_loc(locale);
1098 QLocale::setDefault(def_loc);
1099 }
1100
1101 fprintf(stderr, "LOCALE %s\n",QLocale().name().toLatin1().data());
1102
1103 if (QLocale().name() == "de" || locale_override == "de") {
1104 fprintf(stderr, "locale de - setting 'note h is B' override parameter.\n");
1105 MusEGlobal::hIsB = false;
1106 }
1107
1108 QApplication::addLibraryPath(MusEGlobal::museGlobalLib + "/qtplugins");
1109 if (MusEGlobal::debugMsg) {
1110 QStringList list = app.libraryPaths();
1111 QStringList::Iterator it = list.begin();
1112 fprintf(stderr, "QtLibraryPath:\n");
1113 while(it != list.end()) {
1114 fprintf(stderr, " <%s>\n", (*it).toLatin1().constData());
1115 ++it;
1116 }
1117 }
1118
1119 // NOTE: Set the stylesheet and style as early as possible!
1120 // Any later invites trouble - typically the colours may be off,
1121 // but currently with Breeze or Oxygen, MDI sub windows may be frozen!
1122 // Working with Breeze maintainer to fix problem... 2017/06/06 Tim.
1123 // MusEGui::updateThemeAndStyle();
1124
1125
1126 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1127 << "Load theme...";
1128
1129 MusEGui::loadTheme(MusEGlobal::config.theme);
1130 // MusEGui::loadThemeColors(MusEGlobal::config.theme);
1131
1132 //-------------------------------------------------------
1133 // BEGIN SHOW MUSE SPLASH SCREEN
1134 //-------------------------------------------------------
1135
1136 QString splash_prefix;
1137 QSplashScreen* muse_splash = nullptr;
1138 if (MusEGlobal::config.showSplashScreen) {
1139 QPixmap splsh(MusEGlobal::museGlobalShare + "/splash.jpg");
1140
1141 if (!splsh.isNull()) {
1142 muse_splash = new QSplashScreen(splsh, Qt::WindowStaysOnTopHint);
1143 muse_splash->setAttribute(Qt::WA_DeleteOnClose); // Possibly also Qt::X11BypassWindowManagerHint
1144 splash_prefix = QString("MusE ") + QString(VERSION) + ":";
1145 muse_splash->show();
1146 }
1147 }
1148
1149 //-------------------------------------------------------
1150 // END SHOW MUSE SPLASH SCREEN
1151 //-------------------------------------------------------
1152
1153 //-------------------------------------------------------
1154 // BEGIN Plugin scanning
1155 //-------------------------------------------------------
1156
1157 if(muse_splash)
1158 {
1159 muse_splash->showMessage(splash_prefix + QString(" Creating plugin cache files..."),
1160 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1161 qApp->processEvents();
1162 }
1163
1164 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1165 << "Scan plugins...";
1166
1167 bool do_rescan = false;
1168 if(force_plugin_rescan)
1169 {
1170 force_plugin_rescan = false;
1171 if(!plugin_rescan_already_done)
1172 {
1173 do_rescan = true;
1174 plugin_rescan_already_done = true;
1175 }
1176 }
1177
1178 if(MusEGlobal::config.pluginCacheTriggerRescan)
1179 {
1180 do_rescan = true;
1181 // Done with rescan trigger. Reset it now.
1182 MusEGlobal::config.pluginCacheTriggerRescan = false;
1183 }
1184
1185 if (MusEGlobal::debugMsg)
1186 qDebug() << "Cache path for plugin scan:" << new_plugin_cache_path;
1187
1188 // Scan all known plugins from the cache file, or if it does not exist
1189 // create the cache file by reading plugins in a safe 'sandbox'.
1190 MusEPlugin::PluginScanInfoStruct::PluginType_t types = MusEPlugin::PluginScanInfoStruct::PluginTypeNone;
1191 if(MusEGlobal::loadPlugins)
1192 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA;
1193 if(MusEGlobal::loadMESS)
1194 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeMESS;
1195 if(MusEGlobal::loadVST)
1196 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeVST;
1197 if(MusEGlobal::loadNativeVST)
1198 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST;
1199 if(MusEGlobal::loadDSSI)
1200 types |= (MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI |
1201 MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST);
1202 if(MusEGlobal::loadLV2)
1203 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLV2;
1204
1205 types |= MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown;
1206
1207 MusEPlugin::checkPluginCacheFiles(new_plugin_cache_path,
1208 // List of plugins to scan into and write to cache files from.
1209 &MusEPlugin::pluginList,
1210 // Don't bother reading any port information that might exist in the cache.
1211 false,
1212 // Whether to force recreation.
1213 do_rescan,
1214 // Whether to NOT recreate.
1215 dont_plugin_rescan,
1216 // When creating, where to find the application's own plugins.
1217 MusEGlobal::museGlobalLib,
1218 // Plugin types to check.
1219 types,
1220 // Debug messages.
1221 MusEGlobal::debugMsg);
1222
1223 // Done with rescan trigger. Reset it now.
1224 if(do_rescan)
1225 MusEGlobal::config.pluginCacheTriggerRescan = false;
1226
1227 //-------------------------------------------------------
1228 // END Plugin scanning
1229 //-------------------------------------------------------
1230
1231 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1232 << "Init audio...";
1233
1234 AL::initDsp();
1235
1236 if(muse_splash)
1237 {
1238 muse_splash->showMessage(splash_prefix + QString(" Initializing audio system..."),
1239 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1240 qApp->processEvents();
1241 }
1242
1243 MusECore::initAudio();
1244
1245 MusEGui::initIcons(MusEGlobal::config.cursorSize,
1246 MusEGlobal::museGlobalShare + "/themes/" + MusEGlobal::config.theme,
1247 MusEGlobal::configPath + "/themes/" + MusEGlobal::config.theme);
1248
1249 if (MusEGlobal::loadMESS)
1250 MusECore::initMidiSynth(); // Need to do this now so that Add Track -> Synth menu is populated when MusE is created.
1251
1252 MusEGlobal::muse = new MusEGui::MusE();
1253 app.setMuse(MusEGlobal::muse);
1254
1255 MusEGui::init_function_dialogs();
1256 MusEGui::retranslate_function_dialogs();
1257
1258 if(muse_splash)
1259 {
1260 muse_splash->showMessage(splash_prefix + QString(" Initializing audio driver..."),
1261 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1262 qApp->processEvents();
1263 }
1264
1265 if (MusEGlobal::config.useDenormalBias) {
1266 fprintf(stderr, "Denormal protection enabled.\n");
1267 }
1268 if (MusEGlobal::debugMsg) {
1269 fprintf(stderr, "global lib: <%s>\n", MusEGlobal::museGlobalLib.toLatin1().constData());
1270 fprintf(stderr, "global share: <%s>\n", MusEGlobal::museGlobalShare.toLatin1().constData());
1271 fprintf(stderr, "muse home: <%s>\n", MusEGlobal::museUser.toLatin1().constData());
1272 fprintf(stderr, "project dir: <%s>\n", MusEGlobal::museProject.toLatin1().constData());
1273 fprintf(stderr, "user instruments: <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData());
1274 }
1275
1276 //rlimit lim; getrlimit(RLIMIT_RTPRIO, &lim);
1277 //fprintf(stderr, "RLIMIT_RTPRIO soft:%d hard:%d\n", lim.rlim_cur, lim.rlim_max); // Reported 80, 80 even with non-RT kernel.
1278 if (MusEGlobal::realTimePriority < sched_get_priority_min(SCHED_FIFO))
1279 MusEGlobal::realTimePriority = sched_get_priority_min(SCHED_FIFO);
1280 else if (MusEGlobal::realTimePriority > sched_get_priority_max(SCHED_FIFO))
1281 MusEGlobal::realTimePriority = sched_get_priority_max(SCHED_FIFO);
1282 // If we requested to force the midi thread priority...
1283 if(MusEGlobal::midiRTPrioOverride > 0)
1284 {
1285 if (MusEGlobal::midiRTPrioOverride < sched_get_priority_min(SCHED_FIFO))
1286 MusEGlobal::midiRTPrioOverride = sched_get_priority_min(SCHED_FIFO);
1287 else if (MusEGlobal::midiRTPrioOverride > sched_get_priority_max(SCHED_FIFO))
1288 MusEGlobal::midiRTPrioOverride = sched_get_priority_max(SCHED_FIFO);
1289 }
1290
1291 #ifdef HAVE_LASH
1292 bool using_jack = false;
1293 #endif
1294 if (MusEGlobal::debugMode) {
1295 MusEGlobal::realTimeScheduling = false;
1296 MusECore::initDummyAudio();
1297 }
1298 else if (audioType == DummyAudioOverride) {
1299 fprintf(stderr, "Force Dummy Audio driver\n");
1300 MusEGlobal::realTimeScheduling = true;
1301 MusECore::initDummyAudio();
1302 }
1303 #ifdef HAVE_RTAUDIO
1304 else if (audioType == RtAudioOverride) {
1305 fprintf(stderr, "Force RtAudio with Pulse Backend\n");
1306 MusEGlobal::realTimeScheduling = true;
1307 if(MusECore::initRtAudio(true))
1308 fallbackDummy();
1309 else
1310 fprintf(stderr, "Using rtAudio\n");
1311 }
1312 #endif
1313 else if (audioType == JackAudioOverride) {
1314 if(MusECore::initJackAudio())
1315 fallbackDummy();
1316 else
1317 {
1318 #ifdef HAVE_LASH
1319 using_jack = true;
1320 #endif
1321 fprintf(stderr, "...Using Jack\n");
1322 }
1323 }
1324 else if (audioType == DriverConfigSetting) {
1325 fprintf(stderr, "Select audio device from configuration : %d\n", MusEGlobal::config.deviceAudioBackend);
1326 switch (MusEGlobal::config.deviceAudioBackend) {
1327 case MusEGlobal::DummyAudio:
1328 {
1329 fprintf(stderr, "User DummyAudio backend - selected through configuration\n");
1330 MusEGlobal::realTimeScheduling = true;
1331 MusECore::initDummyAudio();
1332 break;
1333 }
1334 case MusEGlobal::RtAudioAlsa:
1335 case MusEGlobal::RtAudioOss:
1336 // case MusEGlobal::RtAudioJack:
1337 case MusEGlobal::RtAudioChoice:
1338 case MusEGlobal::RtAudioPulse:
1339 {
1340 fprintf(stderr, "User RtAudio backend - backend selected through configuration: ");
1341 if(MusEGlobal::config.deviceAudioBackend >= MusEGlobal::numRtAudioDevices)
1342 fprintf(stderr, "Unknown");
1343 else
1344 fprintf(stderr, "%s",
1345 MusEGlobal::selectableAudioBackendDevices[MusEGlobal::config.deviceAudioBackend].
1346 toLatin1().constData());
1347 fprintf(stderr, "\n");
1348
1349 MusEGlobal::realTimeScheduling = true;
1350 #ifdef HAVE_RTAUDIO
1351 if(MusECore::initRtAudio())
1352 fallbackDummy();
1353 else
1354 fprintf(stderr, "Using rtAudio\n");
1355 #else
1356 fallbackDummy();
1357 #endif
1358 break;
1359 }
1360 case MusEGlobal::JackAudio:
1361 {
1362 fprintf(stderr, "User JackAudio backend - backend selected through configuration\n");
1363 if (MusECore::initJackAudio())
1364 {
1365 MusEGlobal::realTimeScheduling = true;
1366 // Force default Pulse.
1367 #ifdef HAVE_RTAUDIO
1368 if(MusECore::initRtAudio(true))
1369 fallbackDummy();
1370 else
1371 fprintf(stderr, "Using rtAudio Pulse\n");
1372 #else
1373 fallbackDummy();
1374 #endif
1375 }
1376 else
1377 {
1378 #ifdef HAVE_LASH
1379 using_jack = true;
1380 #endif
1381 fprintf(stderr, "Using Jack\n");
1382 }
1383
1384 break;
1385 }
1386 }
1387 }
1388
1389 MusEGlobal::realTimeScheduling = MusEGlobal::audioDevice->isRealtime();
1390
1391 // ??? With Jack2 this reports true even if it is not running realtime.
1392 // Jack says: "Cannot use real-time scheduling (RR/10)(1: Operation not permitted)". The kernel is non-RT.
1393 // I cannot seem to find a reliable answer to the question, even with dummy audio and system calls.
1394
1395 // setup the prefetch fifo length now that the segmentSize is known
1396 MusEGlobal::fifoLength = 131072 / MusEGlobal::segmentSize;
1397 MusECore::initAudioPrefetch();
1398
1399 // Set up the wave module now that sampleRate and segmentSize are known.
1400 MusECore::SndFile::initWaveModule(
1401 &MusEGlobal::sndFiles,
1402 &MusEGlobal::audioConverterPluginList,
1403 &MusEGlobal::defaultAudioConverterSettings,
1404 MusEGlobal::sampleRate,
1405 MusEGlobal::segmentSize);
1406
1407 if(muse_splash)
1408 {
1409 muse_splash->showMessage(splash_prefix + QString(" Initializing midi devices..."),
1410 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1411 qApp->processEvents();
1412 }
1413
1414 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1415 << "Init MIDI...";
1416
1417 // REMOVE Tim. startup. Removed 2019/02/21. It's been six years since 1.9.9.5 release.
1418 // Remove this waiting part at some point if we're all good...
1419 //
1420 // // WARNING Must do it this way. Call registerClient long AFTER Jack client
1421 // // is created and MusE ALSA client is created (in initMidiDevices),
1422 // // otherwise random crashes can occur within Jack <= 1.9.8.
1423 // // Fixed in Jack 1.9.9. Tim.
1424 // This initMidiDevices will automatically initialize the midiSeq sequencer thread,
1425 // but not start it - that's a bit later on.
1426 MusECore::initMidiDevices();
1427 // // Wait until things have settled. One second seems OK so far.
1428 // for(int t = 0; t < 100; ++t)
1429 // usleep(10000);
1430 // Now it is safe to call registerClient.
1431 MusEGlobal::audioDevice->registerClient();
1432
1433 MusECore::initMidiController();
1434 MusECore::initMidiInstruments();
1435 MusECore::initMidiPorts();
1436
1437 if(muse_splash)
1438 {
1439 muse_splash->showMessage(splash_prefix + QString(" Initializing plugins..."),
1440 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1441 qApp->processEvents();
1442 }
1443
1444 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1445 << "Init plugins...";
1446
1447 if (MusEGlobal::loadPlugins)
1448 MusECore::initPlugins();
1449
1450 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1451 << "Init VST plugins...";
1452
1453 if (MusEGlobal::loadVST)
1454 MusECore::initVST();
1455
1456 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1457 << "Init native VST plugins...";
1458
1459 if (MusEGlobal::loadNativeVST)
1460 MusECore::initVST_Native();
1461
1462 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1463 << "Init DSSI plugins...";
1464
1465 if(MusEGlobal::loadDSSI)
1466 MusECore::initDSSI();
1467
1468 #ifdef LV2_SUPPORT
1469 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1470 << "Init LV2 plugins...";
1471
1472 if(MusEGlobal::loadLV2)
1473 MusECore::initLV2();
1474 #endif
1475
1476 // Now that all the plugins are done loading from the global plugin cache list,
1477 // we are done with it. Clear it to free up memory.
1478 // TODO Future: Will need to keep it around if we ever switch to using the list all the time
1479 // instead of separate global plugin and synth lists.
1480 MusEPlugin::pluginList.clear();
1481
1482 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1483 << "Init OSC / metronome...";
1484
1485 MusECore::initOSC();
1486
1487 MusECore::initMetronome();
1488
1489 const QString metro_presets = MusEGlobal::museGlobalShare + QString("/metronome");
1490 MusECore::initMetronomePresets(metro_presets, &MusEGlobal::metroAccentPresets, MusEGlobal::debugMsg);
1491 // If the global metronome accent settings are empty, it is unlikely the user did that, or wants that.
1492 // More likely it indicates this is a first-time init of the global settings.
1493 // In any case, if empty fill the global metronome accent settings with factory presets.
1494 if(MusEGlobal::metroGlobalSettings.metroAccentsMap &&
1495 MusEGlobal::metroGlobalSettings.metroAccentsMap->empty())
1496 {
1497 // Fill with defaults.
1498 MusEGlobal::metroAccentPresets.defaultAccents(
1499 MusEGlobal::metroGlobalSettings.metroAccentsMap,
1500 MusECore::MetroAccentsStruct::FactoryPreset);
1501 }
1502
1503 MusECore::initWavePreview(MusEGlobal::segmentSize);
1504
1505 MusECore::enumerateJackMidiDevices();
1506
1507 #ifdef HAVE_LASH
1508 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1509 << "Init LASH...";
1510
1511 {
1512 MusEGui::lash_client = 0;
1513 if(MusEGlobal::useLASH)
1514 {
1515 if(muse_splash)
1516 {
1517 muse_splash->showMessage(splash_prefix + QString(" Initializing LASH support..."),
1518 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1519 qApp->processEvents();
1520 }
1521
1522 int lash_flags = LASH_Config_File;
1523 const char *muse_name = PACKAGE_NAME;
1524 MusEGui::lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0));
1525 #ifdef ALSA_SUPPORT
1526 if(MusECore::alsaSeq)
1527 lash_alsa_client_id (MusEGui::lash_client, snd_seq_client_id (MusECore::alsaSeq));
1528 #endif
1529 //if (audioType != DummyAudio) {
1530 if (using_jack) {
1531 const char *jack_name = MusEGlobal::audioDevice->clientName();
1532 lash_jack_client_name (MusEGui::lash_client, jack_name);
1533 }
1534 }
1535 if(lash_args)
1536 lash_args_destroy(lash_args);
1537 }
1538 #endif /* HAVE_LASH */
1539
1540 #ifndef _WIN32
1541 if (!MusEGlobal::debugMode) {
1542 if (mlockall(MCL_CURRENT | MCL_FUTURE))
1543 perror("WARNING: Cannot lock memory:");
1544 }
1545 #endif
1546
1547 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1548 << "Populating Track context menu...";
1549
1550 if(muse_splash)
1551 {
1552 muse_splash->showMessage(splash_prefix + QString(" Populating Track context menu..."),
1553 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1554 qApp->processEvents();
1555 }
1556
1557 MusEGlobal::muse->populateAddTrack(); // could possibly be done in a thread.
1558
1559 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1560 << "Show GUI...";
1561
1562 MusEGlobal::muse->show();
1563
1564 // Let the configuration settings take effect. Do not save.
1565 MusEGlobal::muse->changeConfig(false);
1566 // Set style and stylesheet, and do not force the style
1567 //MusEGui::updateThemeAndStyle(); // Works better if called just after app created, above.
1568
1569 MusEGlobal::muse->seqStart();
1570 MusEGlobal::muse->initStatusBar();
1571
1572 // If the sequencer object was created, report timing.
1573 if(MusEGlobal::midiSeq)
1574 MusEGlobal::midiSeq->checkAndReportTimingResolution();
1575
1576 //--------------------------------------------------
1577 // Set the audio device sync timeout value.
1578 //--------------------------------------------------
1579 // Enforce a 30 second timeout.
1580 // TODO: Split this up and have user adjustable normal (2 or 10 second default) value,
1581 // plus a contribution from the total required precount time.
1582 // Too bad we likely can't set it dynamically in the audio sync callback.
1583 // NOTE: This is also enforced casually in Song:seqSignal after a stop, start, or seek.
1584 MusEGlobal::audioDevice->setSyncTimeout(30000000);
1585
1586 //--------------------------------------------------
1587 // Auto-fill the midi ports, if appropriate.
1588 // Only if NOT actually opening an existing file.
1589 // FIXME: Maybe check if it's a .med file (song may populate)
1590 // or .mid file (always populate) or .wav file etc.
1591 //--------------------------------------------------
1592 if(MusEGlobal::populateMidiPortsOnStart &&
1593 ((!open_filename.isEmpty() && !QFile(open_filename).exists()) ||
1594 (open_filename.isEmpty() &&
1595 (MusEGlobal::config.startMode == 1 || MusEGlobal::config.startMode == 2) &&
1596 !MusEGlobal::config.startSongLoadConfig)))
1597 MusECore::populateMidiPorts();
1598
1599 if(muse_splash)
1600 {
1601 muse_splash->showMessage(splash_prefix + QString(" Click to close splash screen..."),
1602 Qt::AlignLeft|Qt::AlignBottom, Qt::yellow);
1603
1604 // From this point on, slap a timer on it so that it stays up for few seconds,
1605 // since closing it now might be too short display time.
1606 QTimer::singleShot(3000, muse_splash, SLOT(close()));
1607 }
1608
1609 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1610 << "Load default project";
1611
1612 //--------------------------------------------------
1613 // Load the default song.
1614 //--------------------------------------------------
1615 // When restarting, override with the last project file name used.
1616 if(last_project_filename.isEmpty())
1617 {
1618 MusEGlobal::muse->loadDefaultSong(open_filename, false, false);
1619 }
1620 else
1621 {
1622 MusEGlobal::muse->loadDefaultSong(
1623 last_project_filename, last_project_was_template, last_project_loaded_config);
1624 }
1625
1626 QTimer::singleShot(100, MusEGlobal::muse, SLOT(showDidYouKnowDialogIfEnabled()));
1627
1628 //--------------------------------------------------
1629 // Start the application...
1630 //--------------------------------------------------
1631
1632 qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1633 << "Start application loop...";
1634
1635 qDebug() << "Total start-up time:" << timer.elapsed() << "ms";
1636
1637 rv = app.exec();
1638
1639 //--------------------------------------------------
1640 // ... Application finished.
1641 //--------------------------------------------------
1642
1643 if(MusEGlobal::debugMsg)
1644 fprintf(stderr, "app.exec() returned:%d\nDeleting main MusE object\n", rv);
1645
1646 if (MusEGlobal::loadPlugins)
1647 {
1648 for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i)
1649 delete (*i);
1650 MusEGlobal::plugins.clear();
1651 }
1652
1653 MusECore::exitWavePreview();
1654
1655 #ifdef LV2_SUPPORT
1656 if(MusEGlobal::loadLV2)
1657 MusECore::deinitLV2();
1658 #endif
1659
1660 // In case the sequencer object is still alive, make sure to destroy it now.
1661 MusECore::exitMidiSequencer();
1662
1663 // Grab the restart flag before deleting muse.
1664 is_restarting = MusEGlobal::muse->restartingApp();
1665
1666 if (is_restarting)
1667 qDebug() << "\n->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz"))
1668 << "Restarting application...";
1669
1670 {
1671 // If the current project file name exists, set the last_project_filename
1672 // variable so that if restarting, it starts with that file.
1673 // This should be OK since fresh untitled unsaved songs will not have a
1674 // file yet, they will have a unique file name that does not exist until
1675 // saved, so it will either use the existing open_filename, or default
1676 // to normal operation (template, last song, or blank).
1677 const QString s = MusEGlobal::muse->lastProjectFilePath();
1678 const QFileInfo fi(s);
1679 if(fi.exists())
1680 {
1681 last_project_filename = s;
1682 last_project_was_template = MusEGlobal::muse->lastProjectWasTemplate();
1683 last_project_loaded_config = MusEGlobal::muse->lastProjectLoadedConfig();
1684 }
1685 else
1686 {
1687 last_project_filename.clear();
1688 last_project_was_template = false;
1689 last_project_loaded_config = false;
1690 }
1691 }
1692
1693 // Now delete the application.
1694 delete MusEGlobal::muse;
1695 MusEGlobal::muse = nullptr;
1696
1697 // These are owned by muse and deleted above. Reset to zero now.
1698 MusEGlobal::undoRedo = nullptr;
1699 MusEGlobal::undoAction = nullptr;
1700 MusEGlobal::redoAction = nullptr;
1701
1702 // Reset the option index.
1703 // NOTE: See optind manual for special resetting values.
1704 // Traditionally 1 is set, but here we may need GNU specific 0.
1705 //optind = 0;
1706
1707 // Free the working copies of the arguments.
1708 if(argv_copy)
1709 {
1710 for(int i = 0; i < argument_count; ++i)
1711 {
1712 if(argv_copy[i])
1713 free(argv_copy[i]);
1714 }
1715 free(argv_copy);
1716 }
1717
1718 // Reset these before restarting, seems to work better,
1719 // makes a difference with the MDI freezing problem, above.
1720 app.setStyleSheet("");
1721 // app.setStyle(MusEGlobal::config.style);
1722
1723 // Reset the recently opened list.
1724 MusEGui::projectRecentList.clear();
1725
1726 // Clear and delete these.
1727 if(MusEGlobal::defaultAudioConverterSettings)
1728 delete MusEGlobal::defaultAudioConverterSettings;
1729 MusEGlobal::defaultAudioConverterSettings = nullptr;
1730 MusEGlobal::audioConverterPluginList.clearDelete();
1731
1732 // Clear the mixer configurations.
1733 MusEGlobal::config.mixer1.stripOrder.clear();
1734 MusEGlobal::config.mixer1.stripVisibility.clear();
1735 MusEGlobal::config.mixer1.stripConfigList.clear();
1736 MusEGlobal::config.mixer2.stripOrder.clear();
1737 MusEGlobal::config.mixer2.stripVisibility.clear();
1738 MusEGlobal::config.mixer2.stripConfigList.clear();
1739 }
1740
1741 //============================================
1742 // END Restart loop. For (re)starting the app.
1743 //============================================
1744
1745 if(MusEGlobal::debugMsg)
1746 fprintf(stderr, "Finished! Exiting main, return value:%d\n", rv);
1747 return rv;
1748
1749 }
1750