1 /*
2     Config.cpp - Configuration file functions
3 
4     Original ZynAddSubFX author Nasca Octavian Paul
5     Copyright (C) 2002-2005 Nasca Octavian Paul
6     Copyright 2009-2011, Alan Calvert
7     Copyright 2013, Nikita Zlobin
8     Copyright 2014-2021, Will Godfrey & others
9 
10     This file is part of yoshimi, which is free software: you can redistribute
11     it and/or modify it under the terms of the GNU Library General Public
12     License as published by the Free Software Foundation; either version 2 of
13     the License, or (at your option) any later version.
14 
15     yoshimi is distributed in the hope that it will be useful, but WITHOUT ANY
16     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17     FOR A PARTICULAR PURPOSE.   See the GNU General Public License (version 2 or
18     later) for more details.
19 
20     You should have received a copy of the GNU General Public License along with
21     yoshimi; if not, write to the Free Software Foundation, Inc., 51 Franklin
22     Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 
24     This file is derivative of ZynAddSubFX original code.
25 */
26 
27 #include <sys/types.h>
28 #include <iostream>
29 #include <fenv.h>
30 #include <errno.h>
31 #include <cmath>
32 #include <string>
33 #include <libgen.h>
34 #include <limits.h>
35 
36 #if defined(__SSE__)
37 #include <xmmintrin.h>
38 #endif
39 
40 #if defined(JACK_SESSION)
41 #include <jack/session.h>
42 #endif
43 
44 #include "Misc/XMLwrapper.h"
45 #include "Misc/SynthEngine.h"
46 #include "Misc/Config.h"
47 #include "Misc/FileMgrFuncs.h"
48 #include "Misc/NumericFuncs.h"
49 #include "Misc/FormatFuncs.h"
50 #include "Misc/TextMsgBuffer.h"
51 #ifdef GUI_FLTK
52     #include "MasterUI.h"
53 #endif
54 #include "ConfBuild.h"
55 
56 using std::cout;
57 using std::endl;
58 
59 using file::isRegularFile;
60 using file::createDir;
61 using file::copyDir;
62 using file::isDirectory;
63 using file::extendLocalPath;
64 using file::setExtension;
65 using file::renameFile;
66 
67 using func::nearestPowerOf2;
68 using func::asString;
69 using func::string2int;
70 
71 unsigned char panLaw = 1;
72 
73 bool         Config::showSplash = true;
74 bool         Config::autoInstance = false;
75 unsigned int Config::activeInstance = 0;
76 int          Config::showCLIcontext = 1;
77 
78 string jUuid = "";
79 
Config(SynthEngine * _synth,std::list<string> & allArgs,bool isLV2Plugin)80 Config::Config(SynthEngine *_synth, std::list<string>& allArgs, bool isLV2Plugin) :
81     stateChanged(false),
82     restoreJackSession(false),
83     oldConfig(false),
84     runSynth(true),
85     finishedCLI(true),
86     VirKeybLayout(0),
87     audioEngine(DEFAULT_AUDIO),
88     engineChanged(false),
89     midiEngine(DEFAULT_MIDI),
90     midiChanged(false),
91     alsaMidiType(1), // search
92     audioDevice("default"),
93     midiDevice("default"),
94     jackServer("default"),
95     jackMidiDevice("default"),
96     startJack(false),
97     connectJackaudio(true),
98     connectJackChanged(false),
99     alsaAudioDevice("default"),
100     alsaMidiDevice("default"),
101     loadDefaultState(false),
102     sessionStage(_SYS_::type::Normal),
103     Interpolation(0),
104     xmlType(0),
105     instrumentFormat(1),
106     EnableProgChange(1), // default will be inverted
107     toConsole(0),
108     consoleTextSize(12),
109     hideErrors(0),
110     showTimes(0),
111     logXMLheaders(0),
112     xmlmax(0),
113     GzipCompression(3),
114     Samplerate(48000),
115     rateChanged(false),
116     Buffersize(256),
117     bufferChanged(false),
118     Oscilsize(512),
119     oscilChanged(false),
120     showGui(true),
121     guiChanged(false),
122     showCli(true),
123     cliChanged(false),
124     singlePath(false),
125     banksChecked(false),
126     panLaw(1),
127     configChanged(false),
128     rtprio(40),
129     midi_bank_root(0), // 128 is used as 'disabled'
130     midi_bank_C(32),   // 128 is used as 'disabled'
131     midi_upper_voice_C(128),
132     enable_NRPN(true),
133     ignoreResetCCs(false),
134     monitorCCin(false),
135     showLearnedCC(true),
136     single_row_panel(1),
137     NumAvailableParts(NUM_MIDI_CHANNELS),
138     currentPart(0),
139     currentBank(0),
140     currentRoot(0),
141     bankHighlight(false),
142     lastBankPart(UNUSED),
143     currentPreset(0),
144     tempBank(0),
145     tempRoot(0),
146     VUcount(0),
147     channelSwitchType(0),
148     channelSwitchCC(128),
149     channelSwitchValue(0),
150     nrpnL(127),
151     nrpnH(127),
152     nrpnActive(false),
153     sigIntActive(0),
154     ladi1IntActive(0),
155     sse_level(0),
156     programcommand("yoshimi"),
157     synth(_synth),
158     bRuntimeSetupCompleted(false),
159     exitType(EXIT_SUCCESS)
160 {
161     std::cerr.precision(4);
162 
163     if (isLV2Plugin)
164     {
165         //Log("LV2 only");
166         if (!loadConfig())
167             Log("\n\nCould not load config. Using default values.\n");
168         bRuntimeSetupCompleted = true;
169         //skip further setup, which is irrelevant for lv2 plugin instance.
170         return;
171     }
172 
173     //Log("Standalone Only");
174     static bool torun = true;
175     if (torun) // only the first stand-alone synth can read args
176     {
177         applyOptions(this, allArgs);
178         torun = false;
179     }
180     if (!loadConfig())
181     {
182         string message = "Could not load config. Using default values.";
183         TextMsgBuffer::instance().push(message); // needed for CLI
184         Log("\n\n" + message + "\n");
185     }
186     bRuntimeSetupCompleted = Setup();
187 }
188 
189 
Setup(void)190 bool Config::Setup(void)
191 {
192     switch (audioEngine)
193     {
194         case alsa_audio:
195             audioDevice = string(alsaAudioDevice);
196             break;
197 
198         case jack_audio:
199             audioDevice = string(jackServer);
200             break;
201         case no_audio:
202         default:
203             audioDevice.clear();
204             break;
205     }
206     if (!audioDevice.size())
207         audioDevice = "default";
208     switch (midiEngine)
209     {
210         case jack_midi:
211             midiDevice = string(jackMidiDevice);
212             break;
213 
214         case alsa_midi:
215             midiDevice = string(alsaMidiDevice);
216             break;
217 
218         case no_midi:
219         default:
220             midiDevice.clear();
221             break;
222     }
223     if (!midiDevice.size())
224         midiDevice = "";
225     Oscilsize = nearestPowerOf2(Oscilsize, MIN_OSCIL_SIZE, MAX_OSCIL_SIZE);
226     Buffersize = nearestPowerOf2(Buffersize, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
227 
228     if (!jUuid.empty())
229         jackSessionUuid = jUuid;
230     return true;
231 }
232 
233 
~Config()234 Config::~Config()
235 {;}
236 
237 
flushLog(void)238 void Config::flushLog(void)
239 {
240     if (LogList.size())
241     {
242         while (LogList.size())
243         {
244             cout << LogList.front() << endl;
245             LogList.pop_front();
246         }
247     }
248 }
249 
250 
clearPresetsDirlist(void)251 void Config::clearPresetsDirlist(void)
252 {
253     for (int i = 0; i < MAX_PRESET_DIRS; ++i)
254         presetsDirlist[i].clear();
255 }
256 
257 
loadConfig(void)258 bool Config::loadConfig(void)
259 {
260     if (file::userHome() == "/tmp")
261         Log ("Failed to find 'Home' directory - using tmp.\nSettings will be lost on computer shutdown.");
262     if (file::localDir().empty())
263     {
264         Log("Failed to create local yoshimi directory.");
265         return false;
266     }
267     string foundConfig = file::configDir();
268     defaultStateName = foundConfig + "/yoshimi";
269 
270     if (file::configDir().empty())
271     {
272         Log("Failed to create config directory '" + file::userHome() + "'");
273         return false;
274     }
275     string yoshimi = "/" + string(YOSHIMI);
276 
277     baseConfig = foundConfig + yoshimi + string(EXTEN::config);
278 
279     int thisInstance = synth->getUniqueId();
280     defaultSession = defaultStateName + "-" + asString(thisInstance) + EXTEN::state;
281     yoshimi += ("-" + asString(thisInstance));
282     //cout << "\nsession >" << defaultSession << "<\n" << endl;
283 
284     ConfigFile = foundConfig + yoshimi + EXTEN::instance;
285 
286     if (thisInstance == 0 && sessionStage != _SYS_::type::RestoreConf)
287     {
288         TextMsgBuffer::instance().init(); // sneaked it in here so it's early
289 
290         presetDir = file::localDir() + "/presets";
291         if (!isDirectory(presetDir))
292         { // only ever want to do this once
293             if (createDir(presetDir))
294             {
295                 Log("Failed to create presets directory '" + presetDir + "'");
296             }
297             else
298             {
299                 defaultPresets();
300                 int i = 1;
301                 while (!presetsDirlist[i].empty())
302                 {
303                     copyDir(presetsDirlist[i], presetDir, 1);
304                     ++i;
305                 }
306             }
307         }
308         if (!isDirectory(file::localDir() + "/found/"))
309         { // only ever want to do this once
310             if (createDir(file::localDir() + "/found/"))
311                 Log("Failed to create root directory for local banks");
312         }
313 
314         // conversion for old location
315         string newInstance0 = ConfigFile;
316         if (isRegularFile(baseConfig) && !isRegularFile(newInstance0), 0)
317         {
318             file::copyFile(baseConfig, newInstance0, 0);
319             Log("Reorganising config files.");
320             if (isRegularFile(defaultStateName + EXTEN::state))
321             {
322                 if (!isRegularFile(defaultSession))
323                 {
324                     renameFile(defaultStateName + EXTEN::state, defaultSession);
325                     Log("Moving default state file.");
326                 }
327             }
328         }
329     }
330 
331     if (!isRegularFile(baseConfig))
332     {
333         Log("Basic configuration " + baseConfig + " not found, will use default settings");
334             defaultPresets();
335     }
336 
337     bool isok = true;
338     if (!isRegularFile(ConfigFile))
339     {
340         Log("Configuration " + ConfigFile + " not found, will use default settings");
341         configChanged = true; // give the user the choice
342     }
343     else
344     {
345         XMLwrapper *xml = new XMLwrapper(synth, true);
346         if (!xml)
347             Log("loadConfig failed XMLwrapper allocation");
348         else
349         {
350             // get base first
351             isok = xml->loadXMLfile(baseConfig);
352             if (isok)
353                 isok = extractBaseParameters(xml);
354             else
355                 Log("loadConfig load base failed");
356             delete xml;
357 
358             // now the instance data
359             if (isok)
360             {
361                 XMLwrapper *xml = new XMLwrapper(synth, true);
362                 if (!xml)
363                     Log("loadConfig failed XMLwrapper allocation");
364                 else
365                 {
366                     isok = xml->loadXMLfile(ConfigFile);
367                     if (isok)
368                         isok = extractConfigData(xml);
369                     else
370                         Log("loadConfig load instance failed");
371                     delete xml;
372                 }
373             }
374             if (thisInstance == 0 && sessionStage != _SYS_::type::RestoreConf)
375             {
376                 int currentVersion = lastXMLmajor * 10 + lastXMLminor;
377                 int storedVersion = MIN_CONFIG_MAJOR * 10 + MIN_CONFIG_MINOR;
378                 if (currentVersion < storedVersion)
379                     oldConfig = true;
380                 else
381                     oldConfig = false;
382             }
383         }
384     }
385 
386     //cout << "Session Stage " << sessionStage << endl;
387 
388     if (sessionStage == _SYS_::type::RestoreConf)
389         return true;
390 
391     if (sessionStage != _SYS_::type::Normal)
392     {
393         XMLwrapper *xml = new XMLwrapper(synth, true);
394         if (!xml)
395             Log("loadConfig failed XMLwrapper allocation");
396         else
397         {
398             isok = xml->loadXMLfile(StateFile);
399             if (isok)
400             {
401                 if (sessionStage == _SYS_::type::StartupFirst)
402                     sessionStage = _SYS_::type::StartupSecond;
403                 else if (sessionStage == _SYS_::type::JackFirst)
404                     sessionStage = _SYS_::type::JackSecond;
405                 isok = extractConfigData(xml);
406             }
407             else
408                 Log("loadConfig load instance failed");
409             delete xml;
410         }
411     }
412     return isok;
413 }
414 
restoreConfig(SynthEngine * _synth)415 void Config::restoreConfig(SynthEngine *_synth)
416 {
417     size_t tmpRoot = _synth->ReadBankRoot();
418     size_t tmpBank = _synth->ReadBank();
419     int tmpChanged = configChanged;
420     sessionStage = _SYS_::type::RestoreConf;
421 
422     // restore old settings
423     loadConfig();
424 
425     // but keep current root and bank
426     _synth->setRootBank(tmpRoot, tmpBank);
427     // and ESPECIALLY 'load as default' status!
428     configChanged = tmpChanged;
429 }
430 
431 
defaultPresets(void)432 void Config::defaultPresets(void)
433 {
434     string presetdirs[]  = {
435         //string(getenv("HOME")) + "/.local/yoshimi/presets",
436         presetDir,
437         extendLocalPath("/presets"),
438         // The following is not a default one.
439         //string(getenv("HOME")) + "/" + string(EXTEN::config) + "/yoshimi/presets",
440         "/usr/share/yoshimi/presets",
441         "/usr/local/share/yoshimi/presets",
442         /*
443          * We no longer include zyn presets as they changed the filenames.
444         "/usr/share/zynaddsubfx/presets",
445         "/usr/local/share/zynaddsubfx/presets",
446         */
447         "@end"
448     };
449     int i = 0;
450     int actual = 0;
451     while (presetdirs[i] != "@end")
452     {
453         if (isDirectory(presetdirs[i]))
454         {
455             Log(presetdirs[i], _SYS_::LogNotSerious);
456             presetsDirlist[actual] = presetdirs[i];
457             ++actual;
458         }
459         ++i;
460     }
461 }
462 
463 
extractBaseParameters(XMLwrapper * xml)464 bool Config::extractBaseParameters(XMLwrapper *xml)
465 {
466     if (synth->getUniqueId() != 0)
467         return true;
468 
469     if (!xml)
470     {
471         Log("extractConfigData on NULL");
472         return false;
473     }
474     if (!xml->enterbranch("BASE_PARAMETERS"))
475     {
476         Log("extractConfigData, no BASE_PARAMETERS branch");
477         return false;
478     }
479 
480     if (!guiChanged)
481         showGui = xml->getparbool("enable_gui", showGui);
482     showSplash = xml->getparbool("enable_splash", showSplash);
483     if (!cliChanged)
484         showCli = xml->getparbool("enable_CLI", showCli);
485     singlePath = xml->getparbool("enable_single_master", singlePath);
486     banksChecked = xml->getparbool("banks_checked", banksChecked);
487     autoInstance = xml->getparbool("enable_auto_instance", autoInstance);
488     if (autoInstance)
489         activeInstance = xml->getparU("active_instances", 0);
490     else
491         activeInstance = 1;
492     showCLIcontext = xml->getpar("show_CLI_context", 1, 0, 2);
493     GzipCompression = xml->getpar("gzip_compression", GzipCompression, 0, 9);
494 
495     // get preset dirs
496     int count = 0;
497     bool found = false;
498     for (int i = 0; i < MAX_PRESET_DIRS; ++i)
499     {
500         if (xml->enterbranch("PRESETSROOT", i))
501         {
502             string dir = xml->getparstr("presets_root");
503             if (isDirectory(dir))
504             {
505                 presetsDirlist[count] = dir;
506                 found = true;
507                 ++count;
508             }
509             xml->exitbranch();
510         }
511     }
512     if (!found)
513     {
514         defaultPresets();
515         currentPreset = 0;
516         configChanged = true; // give the user the choice
517     }
518 
519     // the following three retained here for compatibility with old config type
520     if (!rateChanged)
521         Samplerate = xml->getpar("sample_rate", Samplerate, 44100, 192000);
522     if (!bufferChanged)
523         Buffersize = xml->getpar("sound_buffer_size", Buffersize, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
524     if (!oscilChanged)
525         Oscilsize = xml->getpar("oscil_size", Oscilsize, MIN_OSCIL_SIZE, MAX_OSCIL_SIZE);
526 
527 
528     xml->exitbranch(); // BaseParameters
529     return true;
530 }
531 
extractConfigData(XMLwrapper * xml)532 bool Config::extractConfigData(XMLwrapper *xml)
533 {
534     if (!xml)
535     {
536         Log("extractConfigData on NULL");
537         return false;
538     }
539     if (!xml->enterbranch("CONFIGURATION"))
540     {
541         Log("extractConfigData, no CONFIGURATION branch");
542         Log("Running with defaults");
543         return true;
544     }
545     /*
546      * default state must be first test as we need to abort
547      * and fetch this instead
548      */
549     if (sessionStage == _SYS_::type::Normal)
550     {
551         loadDefaultState = xml->getpar("defaultState", loadDefaultState, 0, 1);
552         if (loadDefaultState)
553         {
554             xml->exitbranch(); // CONFIGURATION
555             configChanged = true;
556             sessionStage = _SYS_::type::Default;
557             StateFile = defaultSession;
558             Log("Loading default state");
559             return true;
560         }
561     }
562 
563     if (sessionStage != _SYS_::type::InProgram)
564     {
565 
566         if (!rateChanged)
567             Samplerate = xml->getpar("sample_rate", Samplerate, 44100, 192000);
568         if (!bufferChanged)
569             Buffersize = xml->getpar("sound_buffer_size", Buffersize, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE);
570         if (!oscilChanged)
571             Oscilsize = xml->getpar("oscil_size", Oscilsize, MIN_OSCIL_SIZE, MAX_OSCIL_SIZE);
572         single_row_panel = xml->getpar("single_row_panel", single_row_panel, 0, 1);
573         toConsole = xml->getpar("reports_destination", toConsole, 0, 1);
574         consoleTextSize = xml->getpar("console_text_size", consoleTextSize, 11, 100);
575         hideErrors = xml->getpar("hide_system_errors", hideErrors, 0, 1);
576         showTimes = xml->getpar("report_load_times", showTimes, 0, 1);
577         logXMLheaders = xml->getpar("report_XMLheaders", logXMLheaders, 0, 1);
578         VirKeybLayout = xml->getpar("virtual_keyboard_layout", VirKeybLayout, 1, 6) - 1;
579         xmlmax = xml->getpar("full_parameters", xmlmax, 0, 1);
580 
581         // get legacy preset dirs
582         int count = 0;
583         for (int i = 0; i < MAX_PRESET_DIRS; ++i)
584         {
585             if (xml->enterbranch("PRESETSROOT", i))
586             {
587                 string dir = xml->getparstr("presets_root");
588                 if (isDirectory(dir))
589                 {
590                     presetsDirlist[count] = dir;
591                     ++count;
592                 }
593                 xml->exitbranch();
594             }
595         }
596 
597         bankHighlight = xml->getparbool("bank_highlight", bankHighlight);
598 
599         currentPreset = xml->getpar("presetsCurrentRootID", currentPreset, 0, MAX_PRESETS);
600 
601         Interpolation = xml->getpar("interpolation", Interpolation, 0, 1);
602 
603         // engines
604         if (!engineChanged)
605             audioEngine = (audio_drivers)xml->getpar("audio_engine", audioEngine, no_audio, alsa_audio);
606         if (!midiChanged)
607             midiEngine = (midi_drivers)xml->getpar("midi_engine", midiEngine, no_midi, alsa_midi);
608         alsaMidiType = xml->getpar("alsa_midi_type", 0, 0, 2);
609 
610         // alsa settings
611         alsaAudioDevice = xml->getparstr("linux_alsa_audio_dev");
612         alsaMidiDevice = xml->getparstr("linux_alsa_midi_dev");
613 
614         // jack settings
615         jackServer = xml->getparstr("linux_jack_server");
616         jackMidiDevice = xml->getparstr("linux_jack_midi_dev");
617         if (!connectJackChanged)
618             connectJackaudio = xml->getpar("connect_jack_audio", connectJackaudio, 0, 1);
619 
620         // midi options
621         midi_bank_root = xml->getpar("midi_bank_root", midi_bank_root, 0, 128);
622         midi_bank_C = xml->getpar("midi_bank_C", midi_bank_C, 0, 128);
623         midi_upper_voice_C = xml->getpar("midi_upper_voice_C", midi_upper_voice_C, 0, 128);
624         EnableProgChange = 1 - xml->getpar("ignore_program_change", EnableProgChange, 0, 1); // inverted for Zyn compatibility
625         instrumentFormat = xml->getpar("saved_instrument_format",instrumentFormat, 1, 3);
626         enable_NRPN = xml->getparbool("enable_incoming_NRPNs", enable_NRPN);
627         ignoreResetCCs = xml->getpar("ignore_reset_all_CCs",ignoreResetCCs,0, 1);
628         monitorCCin = xml->getparbool("monitor-incoming_CCs", monitorCCin);
629         showLearnedCC = xml->getparbool("open_editor_on_learned_CC", showLearnedCC);
630     }
631     if (tempRoot == 0)
632         tempRoot = xml->getpar("root_current_ID", 0, 0, 127);
633 
634     if (tempBank == 0)
635     tempBank = xml->getpar("bank_current_ID", 0, 0, 127);
636     xml->exitbranch(); // CONFIGURATION
637     return true;
638 }
639 
640 
saveConfig(bool master)641 bool Config::saveConfig(bool master)
642 {
643     bool result = false;
644     if (master)
645     {
646         //cout << "saving master" << endl;
647         xmlType = TOPLEVEL::XML::MasterConfig;
648         XMLwrapper *xml = new XMLwrapper(synth, true);
649         if (!xml)
650         {
651             Log("saveConfig failed xml allocation", _SYS_::LogNotSerious);
652             return result;
653         }
654         string resConfigFile = baseConfig;
655 
656         if (xml->saveXMLfile(resConfigFile, false))
657         {
658             configChanged = false;
659             result = true;
660         }
661         else
662             Log("Failed to save master config to " + resConfigFile, _SYS_::LogNotSerious);
663 
664         delete xml;
665     }
666     xmlType = TOPLEVEL::XML::Config;
667     XMLwrapper *xml = new XMLwrapper(synth, true);
668     if (!xml)
669     {
670         Log("saveConfig failed xml allocation", _SYS_::LogNotSerious);
671         return result;
672     }
673     addConfigXML(xml);
674     string resConfigFile = ConfigFile;
675 
676     if (xml->saveXMLfile(resConfigFile))
677     {
678         configChanged = false;
679         result = true;
680     }
681     else
682         Log("Failed to save instance to " + resConfigFile, _SYS_::LogNotSerious);
683 
684     delete xml;
685     return result;
686 }
687 
688 
addConfigXML(XMLwrapper * xml)689 void Config::addConfigXML(XMLwrapper *xml)
690 {
691     xml->beginbranch("CONFIGURATION");
692     xml->addpar("defaultState", loadDefaultState);
693 
694     xml->addpar("sample_rate", synth->getRuntime().Samplerate);
695     xml->addpar("sound_buffer_size", synth->getRuntime().Buffersize);
696     xml->addpar("oscil_size", synth->getRuntime().Oscilsize);
697 
698     xml->addpar("single_row_panel", single_row_panel);
699     xml->addpar("reports_destination", toConsole);
700     xml->addpar("console_text_size", consoleTextSize);
701     xml->addpar("hide_system_errors", hideErrors);
702     xml->addpar("report_load_times", showTimes);
703     xml->addpar("report_XMLheaders", logXMLheaders);
704     xml->addpar("virtual_keyboard_layout", VirKeybLayout + 1);
705     xml->addpar("full_parameters", xmlmax);
706 
707     xml->addparbool("bank_highlight", bankHighlight);
708 
709     xml->addpar("presetsCurrentRootID", currentPreset);
710 
711     xml->addpar("interpolation", Interpolation);
712 
713     xml->addpar("audio_engine", synth->getRuntime().audioEngine);
714     xml->addpar("midi_engine", synth->getRuntime().midiEngine);
715     xml->addpar("alsa_midi_type", synth->getRuntime().alsaMidiType);
716 
717     xml->addparstr("linux_alsa_audio_dev", alsaAudioDevice);
718     xml->addparstr("linux_alsa_midi_dev", alsaMidiDevice);
719 
720     xml->addparstr("linux_jack_server", jackServer);
721     xml->addparstr("linux_jack_midi_dev", jackMidiDevice);
722     xml->addpar("connect_jack_audio", connectJackaudio);
723 
724     xml->addpar("midi_bank_root", midi_bank_root);
725     xml->addpar("midi_bank_C", midi_bank_C);
726     xml->addpar("midi_upper_voice_C", midi_upper_voice_C);
727     xml->addpar("ignore_program_change", (1 - EnableProgChange));
728     xml->addpar("enable_part_on_voice_load", 1); // for backward compatibility
729     xml->addpar("saved_instrument_format", instrumentFormat);
730     xml->addparbool("enable_incoming_NRPNs", enable_NRPN);
731     xml->addpar("ignore_reset_all_CCs",ignoreResetCCs);
732     xml->addparbool("monitor-incoming_CCs", monitorCCin);
733     xml->addparbool("open_editor_on_learned_CC",showLearnedCC);
734     xml->addpar("check_pad_synth", 1); // for backward compatibility
735     xml->addpar("root_current_ID", synth->ReadBankRoot());
736     xml->addpar("bank_current_ID", synth->ReadBank());
737     xml->endbranch(); // CONFIGURATION
738 }
739 
740 
saveSessionData(string savefile)741 bool Config::saveSessionData(string savefile)
742 {
743     savefile = setExtension(savefile, EXTEN::state);
744     synth->getRuntime().xmlType = TOPLEVEL::XML::State;
745     XMLwrapper *xml = new XMLwrapper(synth, true);
746     if (!xml)
747     {
748         Log("saveSessionData failed xml allocation", _SYS_::LogNotSerious | _SYS_::LogError);
749         return false;
750     }
751     bool ok = true;
752     addConfigXML(xml);
753     synth->add2XML(xml);
754     synth->midilearn.insertMidiListData(xml);
755     if (xml->saveXMLfile(savefile))
756         Log("Session data saved to " + savefile, _SYS_::LogNotSerious);
757     else
758     {
759         ok = false;
760         Log("Failed to save session data to " + savefile, _SYS_::LogNotSerious);
761     }
762     if (xml)
763         delete xml;
764     return ok;
765 }
766 
767 
restoreSessionData(string sessionfile)768 bool Config::restoreSessionData(string sessionfile)
769 {
770     XMLwrapper *xml = NULL;
771     bool ok = false;
772 
773     if (sessionfile.size() && !isRegularFile(sessionfile))
774         sessionfile = setExtension(sessionfile, EXTEN::state);
775     if (!sessionfile.size() || !isRegularFile(sessionfile))
776     {
777         Log("Session file " + sessionfile + " not available", _SYS_::LogNotSerious);
778         goto end_game;
779     }
780     if (!(xml = new XMLwrapper(synth, true)))
781     {
782         Log("Failed to init xml for restoreState", _SYS_::LogNotSerious | _SYS_::LogError);
783         goto end_game;
784     }
785     if (!xml->loadXMLfile(sessionfile))
786     {
787         Log("Failed to load xml file " + sessionfile, _SYS_::LogNotSerious);
788         goto end_game;
789     }
790 
791 
792     ok = extractConfigData(xml);
793     if (ok)
794     {
795         // mark as soon as anything changes
796         synth->getRuntime().stateChanged = true;
797         for (int npart = 0; npart < NUM_MIDI_PARTS; ++ npart)
798         {
799             synth->part[npart]->defaults();
800             synth->part[npart]->Prcvchn = npart % NUM_MIDI_CHANNELS;
801         }
802         ok = synth->getfromXML(xml);
803         if (ok)
804             synth->setAllPartMaps();
805         bool oklearn = synth->midilearn.extractMidiListData(false, xml);
806         if (oklearn)
807             synth->midilearn.updateGui(MIDILEARN::control::hideGUI);
808             // handles possibly undefined window
809         }
810 
811 end_game:
812     if (xml)
813         delete xml;
814     return ok;
815 }
816 
817 
Log(const string & msg,char tostderr)818 void Config::Log(const string& msg, char tostderr)
819 {
820     if ((tostderr & _SYS_::LogNotSerious) && hideErrors)
821         return;
822     else if(!(tostderr & _SYS_::LogError))
823     {
824         if (showGui && toConsole)
825             LogList.push_back(msg);
826         else
827             cout << msg << endl;
828     }
829     else
830         std::cerr << msg << endl; // error log
831 }
832 
833 
LogError(const string & msg)834 void Config::LogError(const string &msg)
835 {
836     std::cerr << "[ERROR] " << msg << endl;
837 }
838 
839 
StartupReport(const string & clientName)840 void Config::StartupReport(const string& clientName)
841 {
842     bool fullInfo = (synth->getUniqueId() == 0);
843     if (fullInfo)
844         Log("Build Number " + std::to_string(BUILD_NUMBER));
845     Log("Clientname: " + clientName);
846     string report = "Audio: ";
847     switch (audioEngine)
848     {
849         case jack_audio:
850             report += "jack";
851             break;
852 
853         case alsa_audio:
854             report += "alsa";
855             break;
856 
857         default:
858             report += "nada";
859     }
860     report += (" -> '" + audioDevice + "'");
861     Log(report, _SYS_::LogNotSerious);
862     report = "Midi: ";
863     switch (midiEngine)
864     {
865         case jack_midi:
866             report += "jack";
867             break;
868 
869         case alsa_midi:
870             report += "alsa";
871             break;
872 
873         default:
874             report += "nada";
875     }
876     if (!midiDevice.size())
877         midiDevice = "default";
878     report += (" -> '" + midiDevice + "'");
879     Log(report, _SYS_::LogNotSerious);
880     if (fullInfo)
881     {
882         Log("Oscilsize: " + asString(synth->oscilsize), _SYS_::LogNotSerious);
883         Log("Samplerate: " + asString(synth->samplerate), _SYS_::LogNotSerious);
884         Log("Period size: " + asString(synth->buffersize), _SYS_::LogNotSerious);
885     }
886 }
887 
888 
setRtprio(int prio)889 void Config::setRtprio(int prio)
890 {
891     if (prio < rtprio)
892         rtprio = prio;
893 }
894 
895 
896 // general thread start service
startThread(pthread_t * pth,void * (* thread_fn)(void *),void * arg,bool schedfifo,char priodec,const string & name)897 bool Config::startThread(pthread_t *pth, void *(*thread_fn)(void*), void *arg,
898                          bool schedfifo, char priodec, const string& name)
899 {
900     pthread_attr_t attr;
901     int chk;
902     bool outcome = false;
903     bool retry = true;
904     while (retry)
905     {
906         if (!(chk = pthread_attr_init(&attr)))
907         {
908 
909             if (schedfifo)
910             {
911                 if ((chk = pthread_attr_setschedpolicy(&attr, SCHED_FIFO)))
912                 {
913                     Log("Failed to set SCHED_FIFO policy in thread attribute "
914                                 + string(strerror(errno))
915                                 + " (" + asString(chk) + ")", _SYS_::LogError);
916                     schedfifo = false;
917                     continue;
918                 }
919                 if ((chk = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)))
920                 {
921                     Log("Failed to set inherit scheduler thread attribute "
922                                 + string(strerror(errno)) + " ("
923                                 + asString(chk) + ")", _SYS_::LogError);
924                     schedfifo = false;
925                     continue;
926                 }
927                 sched_param prio_params;
928                 int prio = rtprio - priodec;
929                 if (prio < 1)
930                     prio = 1;
931                 Log(name + " priority is " + std::to_string(prio), _SYS_::LogError);
932                 prio_params.sched_priority = prio;
933                 if ((chk = pthread_attr_setschedparam(&attr, &prio_params)))
934                 {
935                     Log("Failed to set thread priority attribute ("
936                                 + asString(chk) + ")  ", _SYS_::LogNotSerious | _SYS_::LogError);
937                     schedfifo = false;
938                     continue;
939                 }
940             }
941             if (!(chk = pthread_create(pth, &attr, thread_fn, arg)))
942             {
943                 outcome = true;
944                 break;
945             }
946             else if (schedfifo)
947             {
948                 schedfifo = false;
949                 continue;
950             }
951             outcome = false;
952             break;
953         }
954         else
955             Log("Failed to initialise thread attributes " + asString(chk), _SYS_::LogError);
956 
957         if (schedfifo)
958         {
959             Log("Failed to start thread (sched_fifo) " + asString(chk)
960                 + "  " + string(strerror(errno)), _SYS_::LogError);
961             schedfifo = false;
962             continue;
963         }
964         Log("Failed to start thread (sched_other) " + asString(chk)
965             + "  " + string(strerror(errno)), _SYS_::LogError);
966         outcome = false;
967         break;
968     }
969     return outcome;
970 }
971 
972 
signalCheck(void)973 void Config::signalCheck(void)
974 {
975     #if defined(JACK_SESSION)
976         int jsev = __sync_fetch_and_add(&jsessionSave, 0);
977         if (jsev != 0)
978         {
979             __sync_and_and_fetch(&jsessionSave, 0);
980             switch (jsev)
981             {
982                 case JackSessionSave:
983                     saveJackSession();
984                     break;
985 
986                 case JackSessionSaveAndQuit:
987                     saveJackSession();
988                     runSynth = false;
989                     break;
990 
991                 case JackSessionSaveTemplate: // not implemented
992                     break;
993 
994                 default:
995                     break;
996             }
997         }
998     #endif
999 
1000     if (ladi1IntActive)
1001     {
1002         __sync_and_and_fetch(&ladi1IntActive, 0);
1003         saveSessionData(StateFile);
1004     }
1005 
1006     if (sigIntActive)
1007         runSynth = false;
1008 }
1009 
1010 
setInterruptActive(void)1011 void Config::setInterruptActive(void)
1012 {
1013     Log("Interrupt received", _SYS_::LogError);
1014     __sync_or_and_fetch(&sigIntActive, 0xFF);
1015 }
1016 
1017 
setLadi1Active(void)1018 void Config::setLadi1Active(void)
1019 {
1020     __sync_or_and_fetch(&ladi1IntActive, 0xFF);
1021 }
1022 
1023 
restoreJsession(void)1024 bool Config::restoreJsession(void)
1025 {
1026     #if defined(JACK_SESSION)
1027         return restoreSessionData(jackSessionFile);
1028     #else
1029         return false;
1030     #endif
1031 }
1032 
1033 
setJackSessionSave(int event_type,const string & session_file)1034 void Config::setJackSessionSave(int event_type, const string& session_file)
1035 {
1036     jackSessionFile = session_file;
1037     __sync_and_and_fetch(&jsessionSave, 0);
1038     __sync_or_and_fetch(&jsessionSave, event_type);
1039 }
1040 
1041 
testCCvalue(int cc)1042 string Config::testCCvalue(int cc)
1043 {
1044     string result = "";
1045     switch (cc)
1046     {
1047         case 1:
1048             result = "mod wheel";
1049             break;
1050 
1051         case 11:
1052             result = "expression";
1053             break;
1054 
1055         case 71:
1056             result = "filter Q";
1057             break;
1058 
1059         case 74:
1060             result = "filter cutoff";
1061             break;
1062 
1063         case 75:
1064             result = "bandwidth";
1065             break;
1066 
1067         case 76:
1068             result = "FM amplitude";
1069             break;
1070 
1071         case 77:
1072             result = "resonance center";
1073             break;
1074 
1075         case 78:
1076             result = "resonance bandwidth";
1077             break;
1078 
1079         default:
1080             result = masterCCtest(cc);
1081     }
1082     return result;
1083 }
1084 
1085 
masterCCtest(int cc)1086 string Config::masterCCtest(int cc)
1087 {
1088     string result = "";
1089     switch (cc)
1090     {
1091          case 6:
1092             result = "data msb";
1093             break;
1094 
1095         case 7:
1096             result = "volume";
1097             break;
1098 
1099         case 10:
1100             result = "panning";
1101             break;
1102 
1103         case 38:
1104             result = "data lsb";
1105             break;
1106 
1107         case 64:
1108             result = "sustain pedal";
1109             break;
1110 
1111         case 65:
1112             result = "portamento";
1113             break;
1114 
1115         case 96:
1116             result = "data increment";
1117             break;
1118 
1119         case 97:
1120             result = "data decrement";
1121             break;
1122 
1123         case 98:
1124             result = "NRPN lsb";
1125             break;
1126 
1127         case 99:
1128             result = "NRPN msb";
1129             break;
1130 
1131         case 120:
1132             result = "all sounds off";
1133             break;
1134 
1135         case 121:
1136             result = "reset all controllers";
1137             break;
1138 
1139         case 123:
1140             result = "all notes off";
1141             break;
1142 
1143         default:
1144         {
1145             if (cc < 128) // don't compare with 'disabled' state
1146             {
1147                 if (cc == midi_bank_C)
1148                     result = "bank change";
1149                 else if (cc == midi_bank_root)
1150                     result = "bank root change";
1151                 else if (cc == midi_upper_voice_C)
1152                     result = "extended program change";
1153                 else if (cc == channelSwitchCC)
1154                     result = "channel switcher";
1155             }
1156         }
1157     }
1158     return result;
1159 }
1160 
1161 
saveJackSession(void)1162 void Config::saveJackSession(void)
1163 {
1164     saveSessionData(jackSessionFile);
1165     jackSessionFile.clear();
1166 }
1167 
1168 
SSEcapability(void)1169 int Config::SSEcapability(void)
1170 {
1171     #if !defined(__SSE__)
1172         return 0;
1173     #else
1174         #if defined(__x86_64__)
1175             int64_t edx;
1176             __asm__ __volatile__ (
1177                 "mov %%rbx,%%rdi\n\t" // save PIC register
1178                 "movl $1,%%eax\n\t"
1179                 "cpuid\n\t"
1180                 "mov %%rdi,%%rbx\n\t" // restore PIC register
1181                 : "=d" (edx)
1182                 : : "%rax", "%rcx", "%rdi"
1183             );
1184         #else
1185             int32_t edx;
1186             __asm__ __volatile__ (
1187                 "movl %%ebx,%%edi\n\t" // save PIC register
1188                 "movl $1,%%eax\n\t"
1189                 "cpuid\n\t"
1190                 "movl %%edi,%%ebx\n\t" // restore PIC register
1191                 : "=d" (edx)
1192                 : : "%eax", "%ecx", "%edi"
1193             );
1194         #endif
1195         return ((edx & 0x02000000 /*SSE*/) | (edx & 0x04000000 /*SSE2*/)) >> 25;
1196     #endif
1197 }
1198 /*
1199 SSEcapability() draws gratefully on the work of others.
1200 */
1201 
1202 /*
1203  * The code below has been replaced with specific anti-denormal code where needed.
1204  * Although the new code is slightly less efficient it is compatible across platforms,
1205  * where as the 'daz' processor code is not available on platforms such as ARM.
1206  */
1207 
1208 /*void Config::AntiDenormals(bool set_daz_ftz)
1209 {
1210     return;
1211     if (synth->getIsLV2Plugin())
1212     {
1213         return;// no need to set floating point rules for lv2 - host should control it.
1214     }
1215     #if defined(__SSE__)
1216         if (set_daz_ftz)
1217         {
1218             sse_level = SSEcapability();
1219             if (sse_level & 0x01)
1220                 // SSE, turn on flush to zero (FTZ) and round towards zero (RZ)
1221                 _mm_setcsr(_mm_getcsr() | 0x8000|0x6000);
1222             if (sse_level & 0x02)
1223                 // SSE2, turn on denormals are zero (DAZ)
1224                _mm_setcsr(_mm_getcsr() | 0x0040);
1225         }
1226         else if (sse_level)
1227         {
1228             // Clear underflow and precision flags,
1229             // turn DAZ, FTZ off, restore round to nearest (RN)
1230             _mm_setcsr(_mm_getcsr() & ~(0x0030|0x8000|0x0040|0x6000));
1231         }
1232     #endif
1233 }*/
1234 
applyOptions(Config * settings,std::list<string> & allArgs)1235 void Config::applyOptions(Config* settings, std::list<string>& allArgs)
1236 {
1237     if (allArgs.empty())
1238         return;
1239     for (std::list<string>::iterator it = allArgs.begin(); it != allArgs.end(); ++it)
1240     {
1241         string line = *it;
1242         size_t pos = line.find(":");
1243         char cmd = line.at(0);
1244         line = line.substr(pos +1);
1245         switch (cmd)
1246         {
1247             case 'A':
1248                 settings->configChanged = true;
1249                 settings->engineChanged = true;
1250                 settings->audioEngine = alsa_audio;
1251                 if (!line.empty())
1252                     settings->audioDevice = line;
1253                 else
1254                     settings->audioDevice = settings->alsaAudioDevice;
1255             break;
1256 
1257         case 'a':
1258             settings->configChanged = true;
1259             settings->midiChanged = true;
1260             settings->midiEngine = alsa_midi;
1261             if (!line.empty())
1262                 settings->midiDevice = line;
1263             else
1264                 settings->midiDevice = string(settings->alsaMidiDevice);
1265             break;
1266 
1267         case 'b':
1268             settings->configChanged = true;
1269             settings->bufferChanged = true;
1270             settings->Buffersize = string2int(line);
1271             cout << "B "<< line << endl;
1272             break;
1273 
1274         case 'c':
1275             settings->configChanged = true;
1276             settings->cliChanged = true;
1277             settings->showCli = false;
1278             break;
1279 
1280         case 'C':
1281             settings->configChanged = true;
1282             settings->cliChanged = true;
1283             settings->showCli = true;
1284             break;
1285 
1286         case 'D':
1287             if (!line.empty())
1288                 settings->rootDefine = line;
1289             break;
1290 
1291         case 'i':
1292             settings->configChanged = true;
1293             settings->guiChanged = true;
1294             settings->showGui = false;
1295             break;
1296 
1297         case 'I':
1298             settings->configChanged = true;
1299             settings->guiChanged = true;
1300             settings->showGui = true;
1301             break;
1302 
1303         case 'J':
1304             settings->configChanged = true;
1305             settings->engineChanged = true;
1306             settings->audioEngine = jack_audio;
1307             if (!line.empty())
1308                 settings->audioDevice = line;
1309             break;
1310 
1311         case 'j':
1312             settings->configChanged = true;
1313             settings->midiChanged = true;
1314             settings->midiEngine = jack_midi;
1315             if (!line.empty())
1316                 settings->midiDevice = line;
1317             else
1318                 settings->midiDevice = string(settings->jackMidiDevice);
1319             break;
1320 
1321         case 'K':
1322             settings->configChanged = true;
1323             settings->connectJackChanged = true;
1324             settings->connectJackaudio = true;
1325             break;
1326 
1327         case 'k':
1328             settings->startJack = true;
1329             break;
1330 
1331         case 'l': settings->paramsLoad = line; break;
1332 
1333         case 'L':
1334         {
1335             unsigned int partLoad = 0;
1336             size_t pos = line.rfind("@");
1337             // this provides a way to specify which part to load to
1338             if (pos != string::npos)
1339             {
1340                 if (line.length() - pos <= 3)
1341                 {
1342                     partLoad = (stoi("0" + line.substr(pos + 1))) - 1;
1343                 }
1344                 if (partLoad >= 64)
1345                     partLoad = 0;
1346                 line = line.substr(0, pos);
1347             }
1348             settings->load2part = partLoad;
1349             settings->instrumentLoad = line;
1350             break;
1351         }
1352 
1353         case 'M':settings->midiLearnLoad = line; break;
1354 
1355         case 'N': settings->nameTag = line; break;
1356 
1357         case 'o':
1358             settings->configChanged = true;
1359             settings->oscilChanged = true;
1360             settings->Oscilsize = string2int(line);
1361             break;
1362 
1363         case 'R':
1364         {
1365             settings->configChanged = true;
1366             settings->rateChanged = true;
1367             int num = (string2int(line) / 48 ) * 48;
1368             if (num < 48000 || num > 192000)
1369                 num = 44100; // play safe
1370             settings->Samplerate = num;
1371             break;
1372         }
1373 
1374         case 'S':
1375             if (!line.empty())
1376             {
1377                 settings->sessionStage = _SYS_::type::StartupFirst;
1378                 settings->configChanged = true;
1379                 settings->StateFile = line;
1380             }
1381             break;
1382 
1383         case 'u':
1384             if (!line.empty())
1385             {
1386                 settings->sessionStage = _SYS_::type::JackFirst;
1387                 settings->configChanged = true;
1388                 settings->StateFile = setExtension(line, EXTEN::state);
1389             }
1390             break;
1391 
1392         case 'U':
1393             if (!line.empty())
1394                 jUuid = line;
1395             break;
1396 
1397         case '@':
1398             settings->configChanged = true;
1399             settings->engineChanged = true;
1400             settings->midiChanged = true;
1401             settings->audioEngine = no_audio;
1402             settings->midiEngine  = no_midi;
1403             break;
1404         }
1405 
1406         //cout << cmd << " line " << line << endl;
1407     }
1408     if (jackSessionUuid.size() && jackSessionFile.size())
1409         restoreJackSession = true;
1410 }
1411 
1412 
1413 #ifdef GUI_FLTK
processGuiMessages()1414 void GuiThreadMsg::processGuiMessages()
1415 {
1416     GuiThreadMsg *msg = (GuiThreadMsg *)Fl::thread_message();
1417     if (msg)
1418     {
1419         SynthEngine *synth = ((SynthEngine *)msg->data);
1420         MasterUI *guiMaster = synth->getGuiMaster((msg->type == GuiThreadMsg::NewSynthEngine));
1421         if (msg->type == GuiThreadMsg::NewSynthEngine)
1422         {
1423             // This *defines* guiMaster
1424             if (!guiMaster)
1425                 std::cerr << "Error starting Main UI!" << endl;
1426             else
1427             {
1428                 guiMaster->Init(guiMaster->getSynth()->getWindowTitle().c_str());
1429 
1430                 if (synth->getRuntime().audioEngine < 1)
1431                     alert(synth, "Yoshimi could not connect to any sound system. Running with no Audio.");
1432                 if (synth->getRuntime().midiEngine < 1)
1433                     alert(synth, "Yoshimi could not connect to any MIDI system. Running with no MIDI.");
1434             }
1435         }
1436         else if (guiMaster)
1437         {
1438             switch(msg->type)
1439             {
1440                 default:
1441                     break;
1442             }
1443         }
1444         delete msg;
1445     }
1446 }
1447 #endif
1448