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