hydrogen 1.2.3
Preferences.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <stdlib.h>
25
26#ifndef WIN32
27#include <pwd.h>
28#include <unistd.h>
29#endif
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <stdlib.h>
33#include <fstream>
34#include <iostream>
35#include <cstdio>
36#include <list>
37#include <algorithm>
38#include <memory>
39
40#include <core/MidiMap.h>
41#include <core/Version.h>
43#include <core/Helpers/Xml.h>
45
46#include <QDir>
47//#include <QApplication>
48
49namespace H2Core
50{
51
52Preferences* Preferences::__instance = nullptr;
53
55{
56 if ( __instance == nullptr ) {
58 }
59}
60
62{
63 __instance = this;
64 m_pTheme = std::make_shared<Theme>();
65
66 // switch to enable / disable lash, only on h2 startup
67 m_brestartLash = false;
68 m_bsetLash = false;
69
70 //rubberband bpm change queue
72
73 QString rubberBandCLIPath = getenv( "PATH" );
74 QStringList rubberBandCLIPathList = rubberBandCLIPath.split(":");//linux use ":" as separator. maybe windows and osx use other separators
75
76 //find the Rubberband-CLI in system env
77 //if this fails a second test will check individual user settings
78 for(int i = 0; i < rubberBandCLIPathList.size(); ++i){
79 m_rubberBandCLIexecutable = rubberBandCLIPathList[i] + "/rubberband";
80 if ( QFile( m_rubberBandCLIexecutable ).exists() == true ){
82 break;
83 }else
84 {
85 m_rubberBandCLIexecutable = "Path to Rubberband-CLI";
87 }
88 }
89
90 m_sPreferredLanguage = QString();
91 __playsamplesonclicking = false; // audio file browser
92 __playselectedinstrument = false; // midi keyboard and keyboard play only selected instrument
93
94 recordEvents = false; // not recording by default
95 punchInPos = 0;
96 punchOutPos = -1;
97
98 __expandSongItem = true; //SoundLibraryPanel
99 __expandPatternItem = true; //SoundLibraryPanel
100 __useTimelineBpm = false; // use timeline
101
102 m_sLastExportPatternAsDirectory = QDir::homePath();
103 m_sLastExportSongDirectory = QDir::homePath();
104 m_sLastSaveSongAsDirectory = QDir::homePath();
107 m_sLastExportLilypondDirectory = QDir::homePath();
108 m_sLastExportMidiDirectory = QDir::homePath();
109 m_sLastImportDrumkitDirectory = QDir::homePath();
110 m_sLastExportDrumkitDirectory = QDir::homePath();
111 m_sLastOpenLayerDirectory = QDir::homePath();
112 m_sLastOpenPlaybackTrackDirectory = QDir::homePath();
116
117 //export dialog
125
126 //export midi dialog
131 m_bFollowPlayhead = true;
132 // SEE ABOVE: m_brestartLash
133 // SEE ABOVE: m_bsetLash
134
135 m_bbc = false;
136 m_mmcsetplay = false;
137
138 m_countOffset = 0; // beatcounter
139 m_startOffset = 0; // beatcounter
140
141 sServerList.push_back( QString("http://hydrogen-music.org/feeds/drumkit_list.php") );
143 m_patternCategories.push_back( QString("not_categorized") );
144
145 //___ audio engine properties ___
146 m_sAudioDriver = QString("Auto");
147 m_bUseMetronome = false;
148 m_fMetronomeVolume = 0.5;
149 m_nMaxNotes = 256;
150 m_nBufferSize = 1024;
151 m_nSampleRate = 44100;
152
153 //___ oss driver properties ___
154 m_sOSSDevice = QString("/dev/dsp");
155
156 //___ MIDI Driver properties
157#if defined(H2CORE_HAVE_ALSA)
158 m_sMidiDriver = QString("ALSA");
159#elif defined(H2CORE_HAVE_PORTMIDI)
160 m_sMidiDriver = QString("PortMidi");
161#elif defined(H2CORE_HAVE_COREMIDI)
162 m_sMidiDriver = QString("CoreMIDI");
163#elif defined(H2CORE_HAVE_JACK)
164 m_sMidiDriver = QString("JACK-MIDI");
165#else
166 // Set ALSA as fallback if none of the above options are available
167 // (although MIDI won't work in this case).
168 m_sMidiDriver = QString( "ALSA" );
169#endif
173 m_bMidiNoteOffIgnore = false;
174 m_bMidiFixedMapping = false;
176
177 // PortAudio properties
178 m_sPortAudioDevice = QString();
179 m_sPortAudioHostAPI = QString();
181
182 // CoreAudio
183 m_sCoreAudioDevice = QString();
184
185 //___ alsa audio driver properties ___
186
187#ifdef H2CORE_HAVE_ALSA
188 // Ensure the device read from the local preferences does
189 // exist. If not, we try to replace it with a valid one.
190 QStringList alsaDevices = AlsaAudioDriver::getDevices();
191 if ( alsaDevices.size() == 0 ||
192 alsaDevices.contains( "hw:0" ) ) {
193 m_sAlsaAudioDevice = "hw:0";
194 } else {
195 // Fall back to a device found on the system (but not the
196 // "null" one).
197 if ( alsaDevices[ 0 ] != "null" ) {
198 m_sAlsaAudioDevice = alsaDevices[ 0 ];
199 } else if ( alsaDevices.size() > 1 ) {
200 m_sAlsaAudioDevice = alsaDevices[ 1 ];
201 } else {
202 m_sAlsaAudioDevice = "hw:0";
203 }
204 }
205#else
206 m_sAlsaAudioDevice = "hw:0";
207#endif
208
209 //___ jack driver properties ___
210 m_sJackPortName1 = QString("alsa_pcm:playback_1");
211 m_sJackPortName2 = QString("alsa_pcm:playback_2");
214 m_bJackTrackOuts = false;
219
220 // OSC configuration
221 m_bOscServerEnabled = false;
223 m_nOscServerPort = 9000;
225
226 //___ General properties ___
227 m_brestoreLastSong = true;
229 m_bUseLash = false;
230 m_bShowDevelWarning = false;
232 // NONE: lastSongFilename;
233 hearNewNotes = true;
234 // NONE: m_recentFiles;
235 // NONE: m_recentFX;
236 quantizeEvents = true;
237 recordEvents = false;
239 m_bHideKeyboardCursor = false;
240
241 //___ GUI properties ___
245 m_bIsFXTabVisible = true;
246 m_bShowAutomationArea = false;
247 m_bShowPlaybackTrack = false;
252 mainFormProperties.set(0, 0, 1000, 700, true);
253 mixerProperties.set(10, 350, 829, 276, true);
254 patternEditorProperties.set(280, 100, 706, 439, true);
255 songEditorProperties.set(10, 10, 600, 250, true);
256 instrumentRackProperties.set(500, 20, 526, 437, true);
257 audioEngineInfoProperties.set(720, 120, 0, 0, false);
258 m_playlistDialogProperties.set(200, 300, 961, 397, false);
259 m_directorProperties.set(200, 300, 423, 377, false);
260 m_ladspaProperties[0].set(2, 20, 0, 0, false);
261 m_ladspaProperties[1].set(2, 20, 0, 0, false);
262 m_ladspaProperties[2].set(2, 20, 0, 0, false);
263 m_ladspaProperties[3].set(2, 20, 0, 0, false);
264 m_nMaxBars = 400;
265 m_nMaxLayers = 16;
266
270
271 const bool bGlobalPrefLoaded = loadPreferences( true );
272 const bool bUserPrefLoaded = loadPreferences( false );
273 if ( bGlobalPrefLoaded || bUserPrefLoaded ) {
275 } else {
276 m_bLoadingSuccessful = false;
277 }
278
279}
280
281
282
284{
285 INFOLOG( "DESTROY" );
286 __instance = nullptr;
287}
288
289
290
291
292
293
298{
299 // We do not required the recently used variables to be
300 // accumulated throughout various configuration files.
301 m_recentFiles.clear();
302 m_recentFX.clear();
303
304 bool bRecreate = false; // configuration file must be recreated?
305
306 QString sPreferencesFilename;
307 const QString sPreferencesOverwritePath = Filesystem::getPreferencesOverwritePath();
308 if ( sPreferencesOverwritePath.isEmpty() ) {
309 sPreferencesFilename = ( bGlobal ? Filesystem::sys_config_path() : Filesystem::usr_config_path() );
310 INFOLOG( QString( "Loading preferences file (%1) [%2]" ).arg( bGlobal ? "SYS" : "USER" ).arg( sPreferencesFilename ) );
311 }
312 else {
313 sPreferencesFilename = sPreferencesOverwritePath;
314 INFOLOG( QString( "Loading preferences file [%1]" ).arg( sPreferencesFilename ) );
315 }
316
317 if ( ! Filesystem::file_readable( sPreferencesFilename, true ) ) {
318 if ( bGlobal ) {
319 ERRORLOG( QString( "Global preferences file [%1] is not readable!" )
320 .arg( sPreferencesFilename ) );
321 return false;
322 }
323 else {
324 WARNINGLOG( QString( "User-level preferences file [%1] is not readable! It will be recreated." )
325 .arg( sPreferencesFilename ) );
326 bRecreate = true;
327 }
328 }
329 else {
330 // Preferences is readable.
331
332 XMLDoc doc;
333 doc.read( sPreferencesFilename, nullptr, false );
334 XMLNode rootNode = doc.firstChildElement( "hydrogen_preferences" );
335
336 if ( !rootNode.isNull() ) {
337
338 // version
339 QString version = rootNode.read_string( "version", "", false, false );
340 if ( version.isEmpty() ) {
341 bRecreate = true;
342 }
343
345 m_sPreferredLanguage = rootNode.read_string( "preferredLanguage", m_sPreferredLanguage, false, "" );
346 __playselectedinstrument = rootNode.read_bool( "instrumentInputMode", __playselectedinstrument, false, false );
347 m_bShowDevelWarning = rootNode.read_bool( "showDevelWarning", m_bShowDevelWarning, false, false );
348 m_bShowNoteOverwriteWarning = rootNode.read_bool( "showNoteOverwriteWarning", m_bShowNoteOverwriteWarning, false, false );
349 m_brestoreLastSong = rootNode.read_bool( "restoreLastSong", m_brestoreLastSong, false, false );
350 m_brestoreLastPlaylist = rootNode.read_bool( "restoreLastPlaylist", m_brestoreLastPlaylist, false, false );
351 m_bUseLash = rootNode.read_bool( "useLash", false, false, false );
352 __useTimelineBpm = rootNode.read_bool( "useTimeLine", __useTimelineBpm, false, false );
353 m_nMaxBars = rootNode.read_int( "maxBars", 400, false, false );
354 m_nMaxLayers = rootNode.read_int( "maxLayers", 16, false, false );
356 rootNode.read_int( "defaultUILayout",
357 static_cast<int>(InterfaceTheme::Layout::SinglePane), false, false )) );
359 rootNode.read_int( "uiScalingPolicy",
360 static_cast<int>(InterfaceTheme::ScalingPolicy::Smaller), false, false )) );
361 m_nLastOpenTab = rootNode.read_int( "lastOpenTab", 0, false, false );
362 m_bUseRelativeFilenamesForPlaylists = rootNode.read_bool( "useRelativeFilenamesForPlaylists", false, false, false );
363 m_bHideKeyboardCursor = rootNode.read_bool( "hideKeyboardCursorWhenUnused", false, false, false );
364
365 //restore the right m_bsetlash value
367 m_useTheRubberbandBpmChangeEvent = rootNode.read_bool( "useTheRubberbandBpmChangeEvent", m_useTheRubberbandBpmChangeEvent, false, false );
368
369 hearNewNotes = rootNode.read_bool( "hearNewNotes", hearNewNotes, false, false );
370 quantizeEvents = rootNode.read_bool( "quantizeEvents", quantizeEvents, false, false );
371
372 //rubberband
374 //this scond test will check individual user settings
375 QString test = rootNode.read_string( "path_to_rubberband", "", false, false );
376 if ( QFile( test ).exists() == true ){
378 }else
379 {
380 m_rubberBandCLIexecutable = "Path to Rubberband-CLI";
381 }
382 }
383
384 XMLNode recentUsedSongsNode = rootNode.firstChildElement( "recentUsedSongs" );
385 if ( ! recentUsedSongsNode.isNull() ) {
386 QDomElement songElement = recentUsedSongsNode.firstChildElement( "song" );
387 while( !songElement.isNull() && ! songElement.text().isEmpty() ){
388 m_recentFiles.push_back( songElement.text() );
389 songElement = songElement.nextSiblingElement( "song" );
390 }
391
392 } else {
393 WARNINGLOG( "recentUsedSongs node not found" );
394 }
395
396 XMLNode recentFXNode = rootNode.firstChildElement( "recentlyUsedEffects" );
397 if ( ! recentFXNode.isNull() ) {
398 QDomElement fxElement = recentFXNode.firstChildElement( "FX" );
399 while ( !fxElement.isNull() && ! fxElement.text().isEmpty()) {
400 m_recentFX.push_back( fxElement.text() );
401 fxElement = fxElement.nextSiblingElement( "FX" );
402 }
403 } else {
404 WARNINGLOG( "recentlyUsedEffects node not found" );
405 }
406
407 sServerList.clear();
408 XMLNode serverListNode = rootNode.firstChildElement( "serverList" );
409 if ( ! serverListNode.isNull() ) {
410 QDomElement serverElement = serverListNode.firstChildElement( "server" );
411 while ( !serverElement.isNull() && !serverElement.text().isEmpty() ) {
412 sServerList.push_back( serverElement.text() );
413 serverElement = serverElement.nextSiblingElement( "server" );
414 }
415 } else {
416 WARNINGLOG( "serverList node not found" );
417 }
418
419 m_patternCategories.clear();
420 XMLNode patternCategoriesNode = rootNode.firstChildElement( "patternCategories" );
421 if ( ! patternCategoriesNode.isNull() ) {
422 QDomElement patternCategoriesElement = patternCategoriesNode.firstChildElement( "categories" );
423 while ( !patternCategoriesElement.isNull() && !patternCategoriesElement.text().isEmpty() ) {
424 m_patternCategories.push_back( patternCategoriesElement.text() );
425 patternCategoriesElement = patternCategoriesElement.nextSiblingElement( "categories" );
426 }
427 } else {
428 WARNINGLOG( "patternCategories node not found" );
429 }
430
431
433 XMLNode audioEngineNode = rootNode.firstChildElement( "audio_engine" );
434 if ( audioEngineNode.isNull() ) {
435 WARNINGLOG( "audio_engine node not found" );
436 bRecreate = true;
437 } else {
438 m_sAudioDriver = audioEngineNode.read_string( "audio_driver", m_sAudioDriver, false, false );
439 // Ensure compatibility will older versions of the
440 // files after capitalization in the GUI
441 // (2021-02-05). This can be dropped in releases >=
442 // 1.2
443 if ( m_sAudioDriver == "Jack" ) {
444 m_sAudioDriver = "JACK";
445 } else if ( m_sAudioDriver == "Oss" ) {
446 m_sAudioDriver = "OSS";
447 } else if ( m_sAudioDriver == "Alsa" ) {
448 m_sAudioDriver = "ALSA";
449 }
450 m_bUseMetronome = audioEngineNode.read_bool( "use_metronome", m_bUseMetronome, false, false );
451 m_fMetronomeVolume = audioEngineNode.read_float( "metronome_volume", 0.5f, false, false );
452 m_nMaxNotes = audioEngineNode.read_int( "maxNotes", m_nMaxNotes, false, false );
453 m_nBufferSize = audioEngineNode.read_int( "buffer_size", m_nBufferSize, false, false );
454 m_nSampleRate = audioEngineNode.read_int( "samplerate", m_nSampleRate, false, false );
455
457 XMLNode ossDriverNode = audioEngineNode.firstChildElement( "oss_driver" );
458 if ( ossDriverNode.isNull() ) {
459 WARNINGLOG( "oss_driver node not found" );
460 bRecreate = true;
461 } else {
462 m_sOSSDevice = ossDriverNode.read_string( "ossDevice", m_sOSSDevice, false, false );
463 }
464
466 XMLNode portAudioDriverNode = audioEngineNode.firstChildElement( "portaudio_driver" );
467 if ( portAudioDriverNode.isNull() ) {
468 WARNINGLOG( "portaudio_driver node not found" );
469 bRecreate = true;
470 } else {
471 m_sPortAudioDevice = portAudioDriverNode.read_string( "portAudioDevice", m_sPortAudioDevice, false, true );
472 m_sPortAudioHostAPI = portAudioDriverNode.read_string( "portAudioHostAPI", m_sPortAudioHostAPI, false, true );
473 m_nLatencyTarget = portAudioDriverNode.read_int( "latencyTarget", m_nLatencyTarget, false, false );
474 }
475
477 XMLNode coreAudioDriverNode = audioEngineNode.firstChildElement( "coreaudio_driver" );
478 if ( coreAudioDriverNode.isNull() ) {
479 WARNINGLOG( "coreaudio_driver node not found" );
480 bRecreate = true;
481 } else {
482 m_sCoreAudioDevice = coreAudioDriverNode.read_string( "coreAudioDevice", m_sCoreAudioDevice, false, true );
483 }
484
486 XMLNode jackDriverNode = audioEngineNode.firstChildElement( "jack_driver" );
487 if ( jackDriverNode.isNull() ) {
488 WARNINGLOG( "jack_driver node not found" );
489 bRecreate = true;
490 } else {
491 m_sJackPortName1 = jackDriverNode.read_string( "jack_port_name_1", m_sJackPortName1, false, false );
492 m_sJackPortName2 = jackDriverNode.read_string( "jack_port_name_2", m_sJackPortName2, false, false );
493 QString sMode = jackDriverNode.read_string( "jack_transport_mode", "NO_JACK_TRANSPORT", false, false );
494 if ( sMode == "NO_JACK_TRANSPORT" ) {
496 } else if ( sMode == "USE_JACK_TRANSPORT" ) {
498 }
499
500 //jack time master
501 m_bJackTimebaseEnabled = jackDriverNode.read_bool( "jack_timebase_enabled", false, false, false );
502 QString tmMode = jackDriverNode.read_string( "jack_transport_mode_master", "NO_JACK_TIME_MASTER", false, false );
503 if ( tmMode == "NO_JACK_TIME_MASTER" ) {
505 } else if ( tmMode == "USE_JACK_TIME_MASTER" ) {
507 }
508
509 int nBBTSync = jackDriverNode.read_int( "jack_bbt_sync", 0, false, false );
510 if ( nBBTSync == 0 ){
512 } else if ( nBBTSync == 1 ) {
514 } else {
515 WARNINGLOG( QString( "Unknown jack_bbt_sync value [%1]. Using JackBBTSyncMethod::constMeasure instead." )
516 .arg( nBBTSync ) );
518 }
519 // ~ jack time master
520
521 m_bJackTrackOuts = jackDriverNode.read_bool( "jack_track_outs", m_bJackTrackOuts, false, false );
522 m_bJackConnectDefaults = jackDriverNode.read_bool( "jack_connect_defaults", m_bJackConnectDefaults, false, false );
523
524 int nJackTrackOutputMode = jackDriverNode.read_int( "jack_track_output_mode", 0, false, false );
525 switch ( nJackTrackOutputMode ) {
526 case 0:
528 break;
529 case 1:
531 break;
532 default:
533 WARNINGLOG( QString( "Unknown jack_track_output_mode value [%1]. Using JackTrackOutputMode::postFader instead." )
534 .arg( nJackTrackOutputMode ) );
536 }
537 }
538
539
541 XMLNode alsaAudioDriverNode = audioEngineNode.firstChildElement( "alsa_audio_driver" );
542 if ( alsaAudioDriverNode.isNull() ) {
543 WARNINGLOG( "alsa_audio_driver node not found" );
544 bRecreate = true;
545 } else {
546 m_sAlsaAudioDevice = alsaAudioDriverNode.read_string( "alsa_audio_device", m_sAlsaAudioDevice, false, false );
547 }
548
550 XMLNode midiDriverNode = audioEngineNode.firstChildElement( "midi_driver" );
551 if ( midiDriverNode.isNull() ) {
552 WARNINGLOG( "midi_driver node not found" );
553 bRecreate = true;
554 } else {
555 m_sMidiDriver = midiDriverNode.read_string( "driverName", "ALSA", false, false );
556 // Ensure compatibility will older versions of the
557 // files after capitalization in the GUI
558 // (2021-02-05). This can be dropped in releases
559 // >= 1.2
560 if ( m_sAudioDriver == "JackMidi" ) {
561 m_sAudioDriver = "JACK-MIDI";
562 } else if ( m_sAudioDriver == "CoreMidi" ) {
563 m_sAudioDriver = "CoreMIDI";
564 }
565 m_sMidiPortName = midiDriverNode.read_string(
566 "port_name", Preferences::getNullMidiPort(), false, false );
567 m_sMidiOutputPortName = midiDriverNode.read_string(
568 "output_port_name", Preferences::getNullMidiPort(), false, false );
569 m_nMidiChannelFilter = midiDriverNode.read_int( "channel_filter", -1, false, false );
570 m_bMidiNoteOffIgnore = midiDriverNode.read_bool( "ignore_note_off", true, false, false );
571 m_bMidiDiscardNoteAfterAction = midiDriverNode.read_bool( "discard_note_after_action", true, false, false );
572 m_bMidiFixedMapping = midiDriverNode.read_bool( "fixed_mapping", false, false, true );
573 m_bEnableMidiFeedback = midiDriverNode.read_bool( "enable_midi_feedback", false, false, true );
574 }
575
577 XMLNode oscServerNode = audioEngineNode.firstChildElement( "osc_configuration" );
578 if ( oscServerNode.isNull() ) {
579 WARNINGLOG( "osc_configuration node not found" );
580 bRecreate = true;
581 } else {
582 m_bOscServerEnabled = oscServerNode.read_bool( "oscEnabled", false, false, false );
583 m_bOscFeedbackEnabled = oscServerNode.read_bool( "oscFeedbackEnabled", true, false, false );
584 m_nOscServerPort = oscServerNode.read_int( "oscServerPort", 9000, false, false );
585 }
586 }
587
589 XMLNode guiNode = rootNode.firstChildElement( "gui" );
590 if ( guiNode.isNull() ) {
591 WARNINGLOG( "gui node not found" );
592 bRecreate = true;
593 } else {
594 // QT Style
595 setQTStyle( guiNode.read_string( "QTStyle", "Fusion", false, true ) );
596
597 if ( getQTStyle() == "Plastique" ){
598 setQTStyle( "Fusion" );
599 }
600
601 // Font fun
602 setApplicationFontFamily( guiNode.read_string( "application_font_family", getApplicationFontFamily(), false, false ) );
603 // The value defaults to m_sApplicationFontFamily on
604 // purpose to provide backward compatibility.
605 setLevel2FontFamily( guiNode.read_string( "level2_font_family", getLevel2FontFamily(), false, false ) );
606 setLevel3FontFamily( guiNode.read_string( "level3_font_family", getLevel3FontFamily(), false, false ) );
607 setFontSize( static_cast<FontTheme::FontSize>(
608 guiNode.read_int( "font_size",
609 static_cast<int>(FontTheme::FontSize::Medium), false, false ) ) );
610
611 // Mixer falloff speed
612 setMixerFalloffSpeed( guiNode.read_float( "mixer_falloff_speed",
613 InterfaceTheme::FALLOFF_NORMAL, false, false ) );
614
615 // pattern editor grid resolution
616 m_nPatternEditorGridResolution = guiNode.read_int( "patternEditorGridResolution", m_nPatternEditorGridResolution, false, false );
617 m_bPatternEditorUsingTriplets = guiNode.read_bool( "patternEditorUsingTriplets", m_bPatternEditorUsingTriplets, false, false );
618
619 m_bShowInstrumentPeaks = guiNode.read_bool( "showInstrumentPeaks", m_bShowInstrumentPeaks, false, false );
620 m_bIsFXTabVisible = guiNode.read_bool( "isFXTabVisible", m_bIsFXTabVisible, false, false );
621 m_bShowAutomationArea = guiNode.read_bool( "showAutomationArea", m_bShowAutomationArea, false, false );
622 m_bShowPlaybackTrack = guiNode.read_bool( "showPlaybackTrack", m_bShowPlaybackTrack, false, false );
623
624
625 // pattern editor grid geometry
626 m_nPatternEditorGridHeight = guiNode.read_int( "patternEditorGridHeight", m_nPatternEditorGridHeight, false, false );
627 m_nPatternEditorGridWidth = guiNode.read_int( "patternEditorGridWidth", m_nPatternEditorGridWidth, false, false );
628
629 // song editor grid geometry
630 m_nSongEditorGridHeight = guiNode.read_int( "songEditorGridHeight", m_nSongEditorGridHeight, false, false );
631 m_nSongEditorGridWidth = guiNode.read_int( "songEditorGridWidth", m_nSongEditorGridWidth, false, false );
632
633 // mainForm window properties
634 setMainFormProperties( readWindowProperties( guiNode, "mainForm_properties", mainFormProperties ) );
635 setMixerProperties( readWindowProperties( guiNode, "mixer_properties", mixerProperties ) );
636 setPatternEditorProperties( readWindowProperties( guiNode, "patternEditor_properties", patternEditorProperties ) );
637 setSongEditorProperties( readWindowProperties( guiNode, "songEditor_properties", songEditorProperties ) );
638 setInstrumentRackProperties( readWindowProperties( guiNode, "instrumentRack_properties", instrumentRackProperties ) );
639 setAudioEngineInfoProperties( readWindowProperties( guiNode, "audioEngineInfo_properties", audioEngineInfoProperties ) );
640 setPlaylistDialogProperties( readWindowProperties( guiNode, "playlistDialog_properties", m_playlistDialogProperties ) );
641 setDirectorProperties( readWindowProperties( guiNode, "director_properties", m_directorProperties ) );
642
643 // last used file dialog folders
644 m_sLastExportPatternAsDirectory = guiNode.read_string( "lastExportPatternAsDirectory", QDir::homePath(), true, false, true );
645 m_sLastExportSongDirectory = guiNode.read_string( "lastExportSongDirectory", QDir::homePath(), true, false, true );
646 m_sLastSaveSongAsDirectory = guiNode.read_string( "lastSaveSongAsDirectory", QDir::homePath(), true, false, true );
647 m_sLastOpenSongDirectory = guiNode.read_string( "lastOpenSongDirectory", Filesystem::songs_dir(), true, false, true );
648 m_sLastOpenPatternDirectory = guiNode.read_string( "lastOpenPatternDirectory", Filesystem::patterns_dir(), true, false, true );
649 m_sLastExportLilypondDirectory = guiNode.read_string( "lastExportLilypondDirectory", QDir::homePath(), true, false, true );
650 m_sLastExportMidiDirectory = guiNode.read_string( "lastExportMidiDirectory", QDir::homePath(), true, false, true );
651 m_sLastImportDrumkitDirectory = guiNode.read_string( "lastImportDrumkitDirectory", QDir::homePath(), true, false, true );
652 m_sLastExportDrumkitDirectory = guiNode.read_string( "lastExportDrumkitDirectory", QDir::homePath(), true, false, true );
653 m_sLastOpenLayerDirectory = guiNode.read_string( "lastOpenLayerDirectory", QDir::homePath(), true, false, true );
654 m_sLastOpenPlaybackTrackDirectory = guiNode.read_string( "lastOpenPlaybackTrackDirectory", QDir::homePath(), true, false, true );
655 m_sLastAddSongToPlaylistDirectory = guiNode.read_string( "lastAddSongToPlaylistDirectory", Filesystem::songs_dir(), true, false, true );
656 m_sLastPlaylistDirectory = guiNode.read_string( "lastPlaylistDirectory", Filesystem::playlists_dir(), true, false, true );
657 m_sLastPlaylistScriptDirectory = guiNode.read_string( "lastPlaylistScriptDirectory", Filesystem::scripts_dir(), true, false, true );
658 m_sLastImportThemeDirectory = guiNode.read_string( "lastImportThemeDirectory", QDir::homePath(), true, false, true );
659 m_sLastExportThemeDirectory = guiNode.read_string( "lastExportThemeDirectory", QDir::homePath(), true, false, true );
660
661 //export dialog properties
662 m_nExportTemplateIdx = guiNode.read_int( "exportDialogTemplate", 0, false, false );
663 m_nExportModeIdx = guiNode.read_int( "exportDialogMode", 0, false, false );
664 m_nExportSampleRateIdx = guiNode.read_int( "exportDialogSampleRate", 0, false, false );
665 m_nExportSampleDepthIdx = guiNode.read_int( "exportDialogSampleDepth", 0, false, false );
667 guiNode.read_bool( "showExportSongLicenseWarning", true,
668 true, false );
669
671 guiNode.read_bool( "showExportDrumkitLicenseWarning", true,
672 true, false );
674 guiNode.read_bool( "showExportDrumkitCopyleftWarning", true,
675 true, false );
677 guiNode.read_bool( "showExportDrumkitAttributionWarning", true,
678 true, false );
679
680 m_bFollowPlayhead = guiNode.read_bool( "followPlayhead", true, false, false );
681
682 // midi export dialog properties
683 m_nMidiExportMode = guiNode.read_int( "midiExportDialogMode", 0, false, false );
684
685 //beatcounter
686 QString bcMode = guiNode.read_string( "bc", "BC_OFF", false, false );
687 if ( bcMode == "BC_OFF" ) {
688 m_bbc = BC_OFF;
689 } else if ( bcMode == "BC_ON" ) {
690 m_bbc = BC_ON;
691 }
692
693
694 QString setPlay = guiNode.read_string( "setplay", "SET_PLAY_OFF", false, false );
695 if ( setPlay == "SET_PLAY_OFF" ) {
697 } else if ( setPlay == "SET_PLAY_ON" ) {
699 }
700
701 m_countOffset = guiNode.read_int( "countoffset", 0, false, false );
702 m_startOffset = guiNode.read_int( "playoffset", 0, false, false );
703
704 // ~ beatcounter
705
706 m_nAutosavesPerHour = guiNode.read_int( "autosavesPerHour", 60, false, false );
707
708 //SoundLibraryPanel expand items
709 __expandSongItem = guiNode.read_bool( "expandSongItem", __expandSongItem, false, false );
710 __expandPatternItem = guiNode.read_bool( "expandPatternItem", __expandPatternItem, false, false );
711
712 for ( unsigned nFX = 0; nFX < MAX_FX; nFX++ ) {
713 QString sNodeName = QString("ladspaFX_properties%1").arg( nFX );
714 setLadspaProperties( nFX, readWindowProperties( guiNode, sNodeName, m_ladspaProperties[nFX] ) );
715 }
716
717 XMLNode pColorThemeNode = guiNode.firstChildElement( "colorTheme" );
718 if ( !pColorThemeNode.isNull() ) {
719 Theme::readColorTheme( pColorThemeNode, m_pTheme );
720 } else {
721 WARNINGLOG( "colorTheme node not found" );
722 bRecreate = true;
723 }
724
725 //SongEditor coloring
727 guiNode.read_int("SongEditor_ColoringMethod",
728 static_cast<int>(InterfaceTheme::ColoringMethod::Custom), false, false )) );
729 std::vector<QColor> colors( getMaxPatternColors() );
730 for ( int ii = 0; ii < getMaxPatternColors(); ii++ ) {
731 colors[ ii ] = guiNode.read_color( QString( "SongEditor_pattern_color_%1" ).arg( ii ),
732 m_pTheme->getColorTheme()->m_accentColor, false, false );
733 }
734 setPatternColors( colors );
735 setVisiblePatternColors( guiNode.read_int( "SongEditor_visible_pattern_colors", 1, false, false ) );
736 if ( getVisiblePatternColors() > 50 ) {
738 } else if ( getVisiblePatternColors() < 0 ) {
740 }
741 }
742
744 XMLNode filesNode = rootNode.firstChildElement( "files" );
745 if ( filesNode.isNull() ) {
746 WARNINGLOG( "files node not found" );
747 bRecreate = true;
748 } else {
749 // last used song
750 m_lastSongFilename = filesNode.read_string( "lastSongFilename", m_lastSongFilename, false, true );
751 m_lastPlaylistFilename = filesNode.read_string( "lastPlaylistFilename", m_lastPlaylistFilename, false, true );
752 m_sDefaultEditor = filesNode.read_string( "defaulteditor", m_sDefaultEditor, false, true );
753 }
754
757
758
759 XMLNode pMidiEventMapNode = rootNode.firstChildElement( "midiEventMap" );
760 if ( !pMidiEventMapNode.isNull() ) {
761 XMLNode pMidiEventNode = pMidiEventMapNode.firstChildElement( "midiEvent" );
762 while ( !pMidiEventNode.isNull() ) {
763 if( pMidiEventNode.firstChildElement().nodeName() == QString("mmcEvent")){
764 QString event = pMidiEventNode.firstChildElement("mmcEvent").text();
765 QString sAction = pMidiEventNode.firstChildElement("action").text();
766 QString sParam = pMidiEventNode.firstChildElement("parameter").text();
767 QString sParam2 = pMidiEventNode.firstChildElement("parameter2").text();
768 QString sParam3 = pMidiEventNode.firstChildElement("parameter3").text();
769
770 std::shared_ptr<Action> pAction = std::make_shared<Action>( sAction );
771 pAction->setParameter1( sParam );
772 pAction->setParameter2( sParam2 );
773 pAction->setParameter3( sParam3 );
774 mM->registerMMCEvent(event, pAction);
775 }
776
777 if( pMidiEventNode.firstChildElement().nodeName() == QString("noteEvent")){
778 QString event = pMidiEventNode.firstChildElement("noteEvent").text();
779 QString sAction = pMidiEventNode.firstChildElement("action").text();
780 QString sParam = pMidiEventNode.firstChildElement("parameter").text();
781 QString sParam2 = pMidiEventNode.firstChildElement("parameter2").text();
782 QString sParam3 = pMidiEventNode.firstChildElement("parameter3").text();
783 QString s_eventParameter = pMidiEventNode.firstChildElement("eventParameter").text();
784 std::shared_ptr<Action> pAction = std::make_shared<Action>( sAction );
785 pAction->setParameter1( sParam );
786 pAction->setParameter2( sParam2 );
787 pAction->setParameter3( sParam3 );
788 mM->registerNoteEvent(s_eventParameter.toInt(), pAction);
789 }
790
791 if( pMidiEventNode.firstChildElement().nodeName() == QString("ccEvent") ){
792 QString event = pMidiEventNode.firstChildElement("ccEvent").text();
793 QString sAction = pMidiEventNode.firstChildElement("action").text();
794 QString sParam = pMidiEventNode.firstChildElement("parameter").text();
795 QString sParam2 = pMidiEventNode.firstChildElement("parameter2").text();
796 QString sParam3 = pMidiEventNode.firstChildElement("parameter3").text();
797 QString s_eventParameter = pMidiEventNode.firstChildElement("eventParameter").text();
798 std::shared_ptr<Action> pAction = std::make_shared<Action>( sAction );
799 pAction->setParameter1( sParam );
800 pAction->setParameter2( sParam2 );
801 pAction->setParameter3( sParam3 );
802 mM->registerCCEvent( s_eventParameter.toInt(), pAction );
803 }
804
805 if( pMidiEventNode.firstChildElement().nodeName() == QString("pcEvent") ){
806 QString event = pMidiEventNode.firstChildElement("pcEvent").text();
807 QString sAction = pMidiEventNode.firstChildElement("action").text();
808 QString sParam = pMidiEventNode.firstChildElement("parameter").text();
809 QString sParam2 = pMidiEventNode.firstChildElement("parameter2").text();
810 QString sParam3 = pMidiEventNode.firstChildElement("parameter3").text();
811 std::shared_ptr<Action> pAction = std::make_shared<Action>( sAction );
812 pAction->setParameter1( sParam );
813 pAction->setParameter2( sParam2 );
814 pAction->setParameter3( sParam3 );
815 mM->registerPCEvent( pAction );
816 }
817
818 pMidiEventNode = pMidiEventNode.nextSiblingElement( "midiEvent" );
819 }
820
821 } else {
822 WARNINGLOG( "midiMap node not found" );
823 }
824 } // rootNode
825 else {
826 WARNINGLOG( "hydrogen_preferences node not found" );
827 bRecreate = true;
828 }
829 }
830
831 if ( m_nMaxLayers < 16 ) {
832 m_nMaxLayers = 16;
833 }
834
835 // The preferences file should be recreated?
836 if ( bRecreate && ! bGlobal ) {
837 WARNINGLOG( "Recreating configuration file." );
839 return false;
840 }
841
842 return true;
843}
844
845
846
851{
852 QString sPreferencesFilename;
853 const QString sPreferencesOverwritePath = Filesystem::getPreferencesOverwritePath();
854 if ( sPreferencesOverwritePath.isEmpty() ) {
855 sPreferencesFilename = Filesystem::usr_config_path();
856 } else {
857 sPreferencesFilename = sPreferencesOverwritePath;
858 }
859
860 INFOLOG( QString( "Saving preferences file %1" ).arg( sPreferencesFilename ) );
861
862 XMLDoc doc;
863 XMLNode rootNode = doc.set_root( "hydrogen_preferences" );
864
865 // hydrogen version
866 rootNode.write_string( "version", QString( get_version().c_str() ) );
867
869 rootNode.write_string( "preferredLanguage", m_sPreferredLanguage );
870 rootNode.write_bool( "restoreLastSong", m_brestoreLastSong );
871 rootNode.write_bool( "restoreLastPlaylist", m_brestoreLastPlaylist );
872
873 rootNode.write_bool( "useLash", m_bsetLash );
874 rootNode.write_bool( "useTimeLine", __useTimelineBpm );
875
876 rootNode.write_int( "maxBars", m_nMaxBars );
877 rootNode.write_int( "maxLayers", m_nMaxLayers );
878
879 rootNode.write_int( "defaultUILayout", static_cast<int>(getDefaultUILayout()) );
880 rootNode.write_int( "uiScalingPolicy", static_cast<int>(getUIScalingPolicy()) );
881 rootNode.write_int( "lastOpenTab", m_nLastOpenTab );
882
883 rootNode.write_bool( "useTheRubberbandBpmChangeEvent", m_useTheRubberbandBpmChangeEvent );
884
885 rootNode.write_bool( "useRelativeFilenamesForPlaylists", m_bUseRelativeFilenamesForPlaylists );
886 rootNode.write_bool( "hideKeyboardCursorWhenUnused", m_bHideKeyboardCursor );
887
888 // instrument input mode
889 rootNode.write_bool( "instrumentInputMode", __playselectedinstrument );
890
891 //show development version warning
892 rootNode.write_bool( "showDevelWarning", m_bShowDevelWarning );
893
894 // Warn about overwriting notes
895 rootNode.write_bool( "showNoteOverwriteWarning", m_bShowNoteOverwriteWarning );
896
897 // hear new notes in the pattern editor
898 rootNode.write_bool( "hearNewNotes", hearNewNotes );
899
900 // key/midi event prefs
901 rootNode.write_bool( "quantizeEvents", quantizeEvents );
902
903 //extern executables
904 if ( !Filesystem::file_executable( m_rubberBandCLIexecutable , true /* silent */) ) {
905 m_rubberBandCLIexecutable = "Path to Rubberband-CLI";
906 }
907 rootNode.write_string( "path_to_rubberband", m_rubberBandCLIexecutable );
908
909 // Recent used songs
910 XMLNode recentUsedSongsNode = rootNode.createNode( "recentUsedSongs" );
911 {
912 unsigned nSongs = 5;
913 if ( m_recentFiles.size() < 5 ) {
914 nSongs = m_recentFiles.size();
915 }
916 for ( unsigned i = 0; i < nSongs; i++ ) {
917 recentUsedSongsNode.write_string( "song", m_recentFiles[ i ] );
918 }
919 }
920
921 XMLNode recentFXNode = rootNode.createNode( "recentlyUsedEffects" );
922 {
923 int nFX = 0;
924 QString FXname;
925 foreach( FXname, m_recentFX ) {
926 recentFXNode.write_string( "FX", FXname );
927 if ( ++nFX > 10 ) break;
928 }
929 }
930
931
932 std::list<QString>::const_iterator cur_Server;
933
934 XMLNode serverListNode = rootNode.createNode( "serverList" );
935 for( cur_Server = sServerList.begin(); cur_Server != sServerList.end(); ++cur_Server ){
936 serverListNode.write_string( QString("server") , QString( *cur_Server ) );
937 }
938
939
940 std::list<QString>::const_iterator cur_patternCategories;
941
942 XMLNode patternCategoriesNode = rootNode.createNode( "patternCategories" );
943 for( cur_patternCategories = m_patternCategories.begin(); cur_patternCategories != m_patternCategories.end(); ++cur_patternCategories ){
944 patternCategoriesNode.write_string( QString("categories") , QString( *cur_patternCategories ) );
945 }
946
947 //---- AUDIO ENGINE ----
948 XMLNode audioEngineNode = rootNode.createNode( "audio_engine" );
949 {
950 // audio driver
951 audioEngineNode.write_string( "audio_driver", m_sAudioDriver );
952
953 // use metronome
954 audioEngineNode.write_bool( "use_metronome", m_bUseMetronome );
955 audioEngineNode.write_float( "metronome_volume", m_fMetronomeVolume );
956 audioEngineNode.write_int( "maxNotes", m_nMaxNotes );
957 audioEngineNode.write_int( "buffer_size", m_nBufferSize );
958 audioEngineNode.write_int( "samplerate", m_nSampleRate );
959
961 XMLNode ossDriverNode = audioEngineNode.createNode( "oss_driver" );
962 {
963 ossDriverNode.write_string( "ossDevice", m_sOSSDevice );
964 }
965
967 XMLNode portAudioDriverNode = audioEngineNode.createNode( "portaudio_driver" );
968 {
969 portAudioDriverNode.write_string( "portAudioDevice", m_sPortAudioDevice );
970 portAudioDriverNode.write_string( "portAudioHostAPI", m_sPortAudioHostAPI );
971 portAudioDriverNode.write_int( "latencyTarget", m_nLatencyTarget );
972 }
973
975 XMLNode coreAudioDriverNode = audioEngineNode.createNode( "coreaudio_driver" );
976 {
977 coreAudioDriverNode.write_string( "coreAudioDevice", m_sCoreAudioDevice );
978 }
979
981 XMLNode jackDriverNode = audioEngineNode.createNode( "jack_driver" );
982 {
983 jackDriverNode.write_string( "jack_port_name_1", m_sJackPortName1 ); // jack port name 1
984 jackDriverNode.write_string( "jack_port_name_2", m_sJackPortName2 ); // jack port name 2
985
986 // jack transport slave
987 QString sMode;
989 sMode = "NO_JACK_TRANSPORT";
991 sMode = "USE_JACK_TRANSPORT";
992 }
993 jackDriverNode.write_string( "jack_transport_mode", sMode );
994
995 //jack time master
996 jackDriverNode.write_bool( "jack_timebase_enabled", m_bJackTimebaseEnabled );
997 QString tmMode;
999 tmMode = "NO_JACK_TIME_MASTER";
1000 } else if ( m_bJackMasterMode == USE_JACK_TIME_MASTER ) {
1001 tmMode = "USE_JACK_TIME_MASTER";
1002 }
1003 jackDriverNode.write_string( "jack_transport_mode_master", tmMode );
1004
1005 int nBBTSync = 0;
1007 nBBTSync = 0;
1009 nBBTSync = 1;
1010 }
1011 jackDriverNode.write_int( "jack_bbt_sync", nBBTSync );
1012
1013 // ~ jack time master
1014
1015 // jack default connection
1016 jackDriverNode.write_bool( "jack_connect_defaults", m_bJackConnectDefaults );
1017
1018 int nJackTrackOutputMode;
1020 nJackTrackOutputMode = 0;
1022 nJackTrackOutputMode = 1;
1023 }
1024 jackDriverNode.write_int( "jack_track_output_mode", nJackTrackOutputMode );
1025
1026 // jack track outs
1027 jackDriverNode.write_bool( "jack_track_outs", m_bJackTrackOuts );
1028 }
1029
1031 XMLNode alsaAudioDriverNode = audioEngineNode.createNode( "alsa_audio_driver" );
1032 {
1033 alsaAudioDriverNode.write_string( "alsa_audio_device", m_sAlsaAudioDevice );
1034 }
1035
1037 XMLNode midiDriverNode = audioEngineNode.createNode( "midi_driver" );
1038 {
1039 midiDriverNode.write_string( "driverName", m_sMidiDriver );
1040 midiDriverNode.write_string( "port_name", m_sMidiPortName );
1041 midiDriverNode.write_string( "output_port_name", m_sMidiOutputPortName );
1042 midiDriverNode.write_int( "channel_filter", m_nMidiChannelFilter );
1043 midiDriverNode.write_bool( "ignore_note_off", m_bMidiNoteOffIgnore );
1044 midiDriverNode.write_bool( "enable_midi_feedback", m_bEnableMidiFeedback );
1045 midiDriverNode.write_bool( "discard_note_after_action", m_bMidiDiscardNoteAfterAction );
1046 midiDriverNode.write_bool( "fixed_mapping", m_bMidiFixedMapping );
1047 }
1048
1050 XMLNode oscNode = audioEngineNode.createNode( "osc_configuration" );
1051 {
1052 oscNode.write_int( "oscServerPort", m_nOscServerPort );
1053 oscNode.write_bool( "oscEnabled", m_bOscServerEnabled );
1054 oscNode.write_bool( "oscFeedbackEnabled", m_bOscFeedbackEnabled );
1055 }
1056
1057 }
1058
1059 //---- GUI ----
1060 XMLNode guiNode = rootNode.createNode( "gui" );
1061 {
1062 guiNode.write_string( "QTStyle", getQTStyle() );
1063 guiNode.write_string( "application_font_family", getApplicationFontFamily() );
1064 guiNode.write_string( "level2_font_family", getLevel2FontFamily() );
1065 guiNode.write_string( "level3_font_family", getLevel3FontFamily() );
1066 guiNode.write_int( "font_size", static_cast<int>(getFontSize()) );
1067 guiNode.write_float( "mixer_falloff_speed", getMixerFalloffSpeed() );
1068 guiNode.write_int( "patternEditorGridResolution", m_nPatternEditorGridResolution );
1069 guiNode.write_int( "patternEditorGridHeight", m_nPatternEditorGridHeight );
1070 guiNode.write_int( "patternEditorGridWidth", m_nPatternEditorGridWidth );
1071 guiNode.write_bool( "patternEditorUsingTriplets", m_bPatternEditorUsingTriplets );
1072 guiNode.write_int( "songEditorGridHeight", m_nSongEditorGridHeight );
1073 guiNode.write_int( "songEditorGridWidth", m_nSongEditorGridWidth );
1074 guiNode.write_bool( "showInstrumentPeaks", m_bShowInstrumentPeaks );
1075 guiNode.write_bool( "isFXTabVisible", m_bIsFXTabVisible );
1076 guiNode.write_bool( "showAutomationArea", m_bShowAutomationArea );
1077 guiNode.write_bool( "showPlaybackTrack", m_bShowPlaybackTrack );
1078
1079 // MainForm window properties
1080 writeWindowProperties( guiNode, "mainForm_properties", mainFormProperties );
1081 writeWindowProperties( guiNode, "mixer_properties", mixerProperties );
1082 writeWindowProperties( guiNode, "patternEditor_properties", patternEditorProperties );
1083 writeWindowProperties( guiNode, "songEditor_properties", songEditorProperties );
1084 writeWindowProperties( guiNode, "instrumentRack_properties", instrumentRackProperties );
1085 writeWindowProperties( guiNode, "audioEngineInfo_properties", audioEngineInfoProperties );
1086 writeWindowProperties( guiNode, "playlistDialog_properties", m_playlistDialogProperties );
1087 writeWindowProperties( guiNode, "director_properties", m_directorProperties );
1088 for ( unsigned nFX = 0; nFX < MAX_FX; nFX++ ) {
1089 QString sNode = QString("ladspaFX_properties%1").arg( nFX );
1090 writeWindowProperties( guiNode, sNode, m_ladspaProperties[nFX] );
1091 }
1092
1093 // last used file dialog folders
1094 guiNode.write_string( "lastExportPatternAsDirectory", m_sLastExportPatternAsDirectory );
1095 guiNode.write_string( "lastExportSongDirectory", m_sLastExportSongDirectory );
1096 guiNode.write_string( "lastSaveSongAsDirectory", m_sLastSaveSongAsDirectory );
1097 guiNode.write_string( "lastOpenSongDirectory", m_sLastOpenSongDirectory );
1098 guiNode.write_string( "lastOpenPatternDirectory", m_sLastOpenPatternDirectory );
1099 guiNode.write_string( "lastExportLilypondDirectory", m_sLastExportLilypondDirectory );
1100 guiNode.write_string( "lastExportMidiDirectory", m_sLastExportMidiDirectory );
1101 guiNode.write_string( "lastImportDrumkitDirectory", m_sLastImportDrumkitDirectory );
1102 guiNode.write_string( "lastExportDrumkitDirectory", m_sLastExportDrumkitDirectory );
1103 guiNode.write_string( "lastOpenLayerDirectory", m_sLastOpenLayerDirectory );
1104 guiNode.write_string( "lastOpenPlaybackTrackDirectory", m_sLastOpenPlaybackTrackDirectory );
1105 guiNode.write_string( "lastAddSongToPlaylistDirectory", m_sLastAddSongToPlaylistDirectory );
1106 guiNode.write_string( "lastPlaylistDirectory", m_sLastPlaylistDirectory );
1107 guiNode.write_string( "lastPlaylistScriptDirectory", m_sLastPlaylistScriptDirectory );
1108 guiNode.write_string( "lastImportThemeDirectory", m_sLastImportThemeDirectory );
1109 guiNode.write_string( "lastExportThemeDirectory", m_sLastExportThemeDirectory );
1110
1111 //ExportSongDialog
1112 guiNode.write_int( "exportDialogMode", m_nExportModeIdx );
1113 guiNode.write_int( "exportDialogTemplate", m_nExportTemplateIdx );
1114 guiNode.write_int( "exportDialogSampleRate", m_nExportSampleRateIdx );
1115 guiNode.write_int( "exportDialogSampleDepth", m_nExportSampleDepthIdx );
1116 guiNode.write_bool( "showExportSongLicenseWarning", m_bShowExportSongLicenseWarning );
1117 guiNode.write_bool( "showExportDrumkitLicenseWarning", m_bShowExportDrumkitLicenseWarning );
1118 guiNode.write_bool( "showExportDrumkitCopyleftWarning", m_bShowExportDrumkitCopyleftWarning );
1119 guiNode.write_bool( "showExportDrumkitAttributionWarning", m_bShowExportDrumkitAttributionWarning );
1120
1121 guiNode.write_bool( "followPlayhead", m_bFollowPlayhead );
1122
1123 //ExportMidiDialog
1124 guiNode.write_int( "midiExportDialogMode", m_nMidiExportMode );
1125
1126 //beatcounter
1127 QString bcMode;
1128
1129 if ( m_bbc == BC_OFF ) {
1130 bcMode = "BC_OFF";
1131 } else if ( m_bbc == BC_ON ) {
1132 bcMode = "BC_ON";
1133 }
1134 guiNode.write_string( "bc", bcMode );
1135
1136 QString setPlay;
1137 if ( m_mmcsetplay == SET_PLAY_OFF ) {
1138 setPlay = "SET_PLAY_OFF";
1139 } else if ( m_mmcsetplay == SET_PLAY_ON ) {
1140 setPlay = "SET_PLAY_ON";
1141 }
1142 guiNode.write_string( "setplay", setPlay );
1143
1144 guiNode.write_int( "countoffset", m_countOffset );
1145 guiNode.write_int( "playoffset", m_startOffset );
1146 // ~ beatcounter
1147
1148 guiNode.write_int( "autosavesPerHour", m_nAutosavesPerHour );
1149
1150 //SoundLibraryPanel expand items
1151 guiNode.write_bool( "expandSongItem", __expandSongItem );
1152 guiNode.write_bool( "expandPatternItem", __expandPatternItem );
1153
1154 // User interface style
1155 Theme::writeColorTheme( &guiNode, m_pTheme );
1156
1157 //SongEditor coloring method
1158 guiNode.write_int( "SongEditor_ColoringMethod", static_cast<int>(getColoringMethod()) );
1159 for ( int ii = 0; ii < getMaxPatternColors(); ii++ ) {
1160 guiNode.write_color( QString( "SongEditor_pattern_color_%1" ).arg( ii ), getPatternColors()[ ii ] );
1161 }
1162 guiNode.write_int( "SongEditor_visible_pattern_colors", getVisiblePatternColors() );
1163 }
1164
1165 //---- FILES ----
1166 XMLNode filesNode = rootNode.createNode( "files" );
1167 {
1168 // last used song
1169 filesNode.write_string( "lastSongFilename", m_lastSongFilename );
1170 filesNode.write_string( "lastPlaylistFilename", m_lastPlaylistFilename );
1171 filesNode.write_string( "defaulteditor", m_sDefaultEditor );
1172 }
1173
1174 // In case the Preferences in both system and user space are
1175 // bricked Hydrogen attempts to recreate the Preferences before
1176 // the core is properly initialized. That's why we assure for the
1177 // MidiMap to be initialized.
1180
1181 //---- MidiMap ----
1182 XMLNode midiEventMapNode = rootNode.createNode( "midiEventMap" );
1183
1184 for( const auto& [ssType, ppAction] : mM->getMMCActionMap() ){
1185 if ( ppAction != nullptr && ! ppAction->isNull() ){
1186 XMLNode midiEventNode = midiEventMapNode.createNode( "midiEvent" );
1187
1188 midiEventNode.write_string( "mmcEvent" , ssType );
1189 midiEventNode.write_string( "action" , ppAction->getType());
1190 midiEventNode.write_string( "parameter" , ppAction->getParameter1() );
1191 midiEventNode.write_string( "parameter2" , ppAction->getParameter2() );
1192 midiEventNode.write_string( "parameter3" , ppAction->getParameter3() );
1193 }
1194 }
1195
1196 for ( const auto& [nnPitch, ppAction] : mM->getNoteActionMap() ){
1197 if ( ppAction != nullptr && ! ppAction->isNull() ){
1198 XMLNode midiEventNode = midiEventMapNode.createNode( "midiEvent" );
1199
1200 midiEventNode.write_string(
1202 midiEventNode.write_int( "eventParameter" , nnPitch );
1203 midiEventNode.write_string( "action" , ppAction->getType() );
1204 midiEventNode.write_string( "parameter" , ppAction->getParameter1() );
1205 midiEventNode.write_string( "parameter2" , ppAction->getParameter2() );
1206 midiEventNode.write_string( "parameter3" , ppAction->getParameter3() );
1207 }
1208 }
1209
1210 for ( const auto& [nnParam, ppAction] : mM->getCCActionMap() ){
1211 if ( ppAction != nullptr && ! ppAction->isNull() ){
1212 XMLNode midiEventNode = midiEventMapNode.createNode( "midiEvent" );
1213
1214 midiEventNode.write_string(
1216 midiEventNode.write_int( "eventParameter" , nnParam );
1217 midiEventNode.write_string( "action" , ppAction->getType() );
1218 midiEventNode.write_string( "parameter" , ppAction->getParameter1() );
1219 midiEventNode.write_string( "parameter2" , ppAction->getParameter2() );
1220 midiEventNode.write_string( "parameter3" , ppAction->getParameter3() );
1221 }
1222 }
1223
1224 for ( const auto& ppAction : mM->getPCActions() ) {
1225 if ( ppAction != nullptr && ! ppAction->isNull() ){
1226 XMLNode midiEventNode = midiEventMapNode.createNode( "midiEvent" );
1227
1228 midiEventNode.write_string(
1230 midiEventNode.write_string( "action" , ppAction->getType() );
1231 midiEventNode.write_string( "parameter" , ppAction->getParameter1() );
1232 midiEventNode.write_string( "parameter2" , ppAction->getParameter2() );
1233 midiEventNode.write_string( "parameter3" , ppAction->getParameter3() );
1234 }
1235 }
1236
1237 return doc.write( sPreferencesFilename );
1238}
1239
1240void Preferences::setMostRecentFX( QString FX_name )
1241{
1242 int pos = m_recentFX.indexOf( FX_name );
1243
1244 if ( pos != -1 ) {
1245 m_recentFX.removeAt( pos );
1246 }
1247
1248 m_recentFX.push_front( FX_name );
1249}
1250
1252WindowProperties Preferences::readWindowProperties( XMLNode parent, const QString& windowName, WindowProperties defaultProp )
1253{
1254 WindowProperties prop = defaultProp;
1255
1256 XMLNode windowPropNode = parent.firstChildElement( windowName );
1257 if ( windowPropNode.isNull() ) {
1258 WARNINGLOG( "Error reading configuration file: " + windowName + " node not found" );
1259 } else {
1260 prop.visible = windowPropNode.read_bool( "visible", true, false, false );
1261 prop.x = windowPropNode.read_int( "x", prop.x, false, false );
1262 prop.y = windowPropNode.read_int( "y", prop.y, false, false );
1263 prop.width = windowPropNode.read_int( "width", prop.width, false, false );
1264 prop.height = windowPropNode.read_int( "height", prop.height, false, false );
1265 prop.m_geometry = QByteArray::fromBase64( windowPropNode.read_string( "geometry",
1266 prop.m_geometry.toBase64(), false, true )
1267 .toUtf8() );
1268 }
1269
1270 return prop;
1271}
1272
1273
1274
1276void Preferences::writeWindowProperties( XMLNode parent, const QString& windowName, const WindowProperties& prop )
1277{
1278 XMLNode windowPropNode = parent.createNode( windowName );
1279
1280 windowPropNode.write_bool( "visible", prop.visible );
1281 windowPropNode.write_int( "x", prop.x );
1282 windowPropNode.write_int( "y", prop.y );
1283 windowPropNode.write_int( "width", prop.width );
1284 windowPropNode.write_int( "height", prop.height );
1285 windowPropNode.write_string( "geometry", QString( prop.m_geometry.toBase64() ) );
1286}
1287
1288// -----------------------
1289
1290
1291
1293{
1294// infoLog( "INIT" );
1295 x = 0;
1296 y = 0;
1297 width = 0;
1298 height = 0;
1299 visible = true;
1300}
1301
1302
1304 : x(other.x),
1305 y(other.y),
1306 width(other.width),
1307 height(other.height),
1308 visible(other.visible)
1309{
1310// infoLog( "INIT" );
1311}
1312
1313
1314
1316{
1317// infoLog( "DESTROY" );
1318}
1319
1320};
#define INFOLOG(x)
Definition Object.h:237
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
static QStringList getDevices()
Use the name hints to build a list of potential device names.
static QString scripts_dir()
returns user scripts path
static bool file_executable(const QString &path, bool silent=false)
returns true if the given path is an existing executable regular file
static QString usr_config_path()
returns user config path
static QString songs_dir()
returns user songs path
static QString playlists_dir()
returns user playlist path
static const QString & getPreferencesOverwritePath()
Definition Filesystem.h:540
static QString sys_config_path()
returns system config path
static bool file_readable(const QString &path, bool silent=false)
returns true if the given path is an existing readable regular file
static QString patterns_dir()
returns user patterns path
FontSize
Enables custom scaling of the font size in the GUI.
Definition Theme.h:184
static float FALLOFF_NORMAL
Definition Theme.h:139
static QString EventToQString(Event event)
WindowProperties audioEngineInfoProperties
void writeWindowProperties(XMLNode parent, const QString &windowName, const WindowProperties &prop)
Write the xml nodes related to window properties.
QString m_sLastExportPatternAsDirectory
bool m_bShowExportDrumkitAttributionWarning
WindowProperties m_playlistDialogProperties
QString m_sDefaultEditor
Default text editor (used by Playlisteditor)
void setPatternColors(std::vector< QColor > patternColors)
bool __useTimelineBpm
Whether to use local speeds specified along the Timeline or a constant tempo for the whole Song in Hy...
JackTrackOutputMode m_JackTrackOutputMode
Specifies which audio settings will be applied to the sample supplied in the JACK per track output po...
bool m_bUseRelativeFilenamesForPlaylists
void setLadspaProperties(unsigned nFX, const WindowProperties &prop)
WindowProperties songEditorProperties
void setMixerProperties(const WindowProperties &prop)
void setPatternEditorProperties(const WindowProperties &prop)
bool m_bOscFeedbackEnabled
Whether to send the current state of Hydrogen to the OSC clients.
void setQTStyle(const QString &sStyle)
const QString & getApplicationFontFamily() const
void setMostRecentFX(QString)
void setSongEditorProperties(const WindowProperties &prop)
std::vector< QString > m_recentFiles
const QString & getQTStyle()
int m_bJackMasterMode
Specifies if Hydrogen should run as JACK time master.
WindowProperties mixerProperties
unsigned m_nPatternEditorGridWidth
QString m_sAudioDriver
Audio driver.
unsigned m_nSampleRate
Sample rate of the audio.
bool m_bShowNoteOverwriteWarning
Last song used.
WindowProperties instrumentRackProperties
QString m_sPortAudioHostAPI
bool savePreferences()
Save the preferences file.
bool m_bPatternEditorUsingTriplets
void setVisiblePatternColors(int nValue)
void setColoringMethod(InterfaceTheme::ColoringMethod coloringMethod)
const QString & getLevel3FontFamily() const
void setInstrumentRackProperties(const WindowProperties &prop)
InterfaceTheme::Layout getDefaultUILayout()
bool m_bOscServerEnabled
Whether to start the OscServer thread.
QString m_sMidiOutputPortName
unsigned m_nSongEditorGridHeight
void setApplicationFontFamily(const QString &family)
QString m_sLastImportDrumkitDirectory
QString m_sLastExportSongDirectory
QString m_sLastOpenPatternDirectory
static void create_instance()
If __instance equals 0, a new Preferences singleton will be created and stored in it.
int m_nMaxLayers
Maximum number of layers to be used in the Instrument editor.
unsigned m_nPatternEditorGridHeight
int getMaxPatternColors() const
WindowProperties m_directorProperties
void setLevel3FontFamily(const QString &family)
void setDirectorProperties(const WindowProperties &prop)
void setPlaylistDialogProperties(const WindowProperties &prop)
QString m_sMidiDriver
MIDI driver.
unsigned m_nSongEditorGridWidth
bool m_bShowExportDrumkitCopyleftWarning
QString m_lastPlaylistFilename
QString m_sLastExportThemeDirectory
void setMainFormProperties(const WindowProperties &prop)
void setAudioEngineInfoProperties(const WindowProperties &prop)
std::list< QString > sServerList
@ postFader
Applies layer, component, and instrument gain, note and instrument pan, note velocity,...
@ preFader
Only layer gain and note velocity will be applied to the samples.
bool loadPreferences(bool bGlobal)
Load the preferences file.
QString m_sLastExportDrumkitDirectory
QString m_sLastExportMidiDirectory
QString m_sLastImportThemeDirectory
std::list< QString > m_patternCategories
FontTheme::FontSize getFontSize() const
bool m_bJackConnectDefaults
Toggles auto-connecting of the main stereo output ports to the system's default ports when starting t...
unsigned m_nMaxNotes
max notes
bool m_bUseLash
Show development version warning?
void setFontSize(FontTheme::FontSize fontSize)
InterfaceTheme::ColoringMethod getColoringMethod() const
bool m_bMidiDiscardNoteAfterAction
bool m_useTheRubberbandBpmChangeEvent
rubberband bpm change queue
bool m_bUseMetronome
If set to true, samples of the metronome will be added to H2Core::AudioEngine::m_songNoteQueue and th...
QString m_sLastOpenLayerDirectory
WindowProperties mainFormProperties
QString m_sOSSDevice
Device used for output.
InterfaceTheme::ScalingPolicy getUIScalingPolicy()
std::vector< QColor > getPatternColors() const
QString m_sLastSaveSongAsDirectory
QString m_rubberBandCLIexecutable
Rubberband CLI.
QString m_sLastAddSongToPlaylistDirectory
JackBBTSyncMethod m_JackBBTSync
Since Hydrogen uses both fixed pattern lengths and recalculates the tick size each time it encounters...
WindowProperties patternEditorProperties
bool m_bJackTimebaseEnabled
External applications with a faulty JACK timebase master implementation can mess up the transport wit...
void setDefaultUILayout(InterfaceTheme::Layout layout)
@ constMeasure
The measure - could be any - does not change during the song.
@ identicalBars
The length of each pattern must match the measure of the corresponding bar in the timebase master.
const QString & getLevel2FontFamily() const
int m_nMaxBars
Maximum number of bars shown in the Song Editor at once.
QString m_sLastPlaylistScriptDirectory
bool m_bShowExportSongLicenseWarning
@ NO_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
@ USE_JACK_TIME_MASTER
Specifies that Hydrogen is using in the time master mode and will thus control specific aspects of th...
Definition Preferences.h:96
@ NO_JACK_TIME_MASTER
Specifies that Hydrogen is note using in the time master mode.
@ USE_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
Definition Preferences.h:89
int getVisiblePatternColors() const
float m_fMetronomeVolume
Metronome volume FIXME: remove this volume!!
QString m_sLastExportLilypondDirectory
void setMixerFalloffSpeed(float value)
int m_nOscTemporaryPort
In case m_nOscServerPort is already occupied by another client, the alternative - random - port numbe...
WindowProperties readWindowProperties(XMLNode parent, const QString &windowName, WindowProperties defaultProp)
Read the xml nodes related to window properties.
bool m_bShowExportDrumkitLicenseWarning
static QString getNullMidiPort()
Choice of m_sMidiPortName and m_sMidiOutputPortName in case no port/device was selected.
QStringList m_recentFX
static Preferences * __instance
Object holding the current Preferences singleton.
int m_nOscServerPort
Port number the OscServer will be started at.
bool m_bJackTrackOuts
If set to true, JackAudioDriver::makeTrackOutputs() will create two individual left and right output ...
void setLevel2FontFamily(const QString &family)
int m_bJackTransportMode
Specifies whether or not Hydrogen will use the JACK transport system.
QString m_sPreferredLanguage
QString m_sLastOpenSongDirectory
std::shared_ptr< Theme > m_pTheme
unsigned m_nBufferSize
Buffer size of the audio.
QString m_sLastPlaylistDirectory
void setUIScalingPolicy(InterfaceTheme::ScalingPolicy policy)
QString m_sLastOpenPlaybackTrackDirectory
WindowProperties m_ladspaProperties[MAX_FX]
static void readColorTheme(XMLNode parent, std::shared_ptr< Theme > pTheme)
Definition Theme.cpp:405
static void writeColorTheme(XMLNode *parent, std::shared_ptr< Theme > pTheme)
Definition Theme.cpp:312
void set(int _x, int _y, int _width, int _height, bool _visible, QByteArray geometry=QByteArray())
Definition Preferences.h:63
XMLDoc is a subclass of QDomDocument with read and write methods.
Definition Xml.h:182
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
Definition Xml.cpp:391
bool read(const QString &filepath, const QString &schemapath=nullptr, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:296
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:370
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
int read_int(const QString &node, int default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads an integer stored into a child node
Definition Xml.cpp:170
bool read_bool(const QString &node, bool default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a boolean stored into a child node
Definition Xml.cpp:184
QColor read_color(const QString &node, const QColor &defaultValue=QColor(97, 167, 251), bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
Definition Xml.cpp:107
QString read_string(const QString &node, const QString &default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:95
float read_float(const QString &node, float default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a float stored into a child node
Definition Xml.cpp:139
void write_color(const QString &node, const QColor &color)
Definition Xml.cpp:273
void write_float(const QString &node, const float value)
write a float into a child node
Definition Xml.cpp:280
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:63
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:269
void write_bool(const QString &node, const bool value)
write a boolean into a child node
Definition Xml.cpp:288
void write_int(const QString &node, const int value)
write an integer into a child node
Definition Xml.cpp:284
The MidiMap maps MidiActions to MidiEvents.
Definition MidiMap.h:38
void registerMMCEvent(QString, std::shared_ptr< Action >)
Sets up the relation between a mmc event and an action.
Definition MidiMap.cpp:96
void registerNoteEvent(int, std::shared_ptr< Action >)
Sets up the relation between a note event and an action.
Definition MidiMap.cpp:130
std::multimap< int, std::shared_ptr< Action > > getCCActionMap() const
Definition MidiMap.h:128
void registerPCEvent(std::shared_ptr< Action >)
Sets up the relation between a program change and an action.
Definition MidiMap.cpp:190
std::vector< std::shared_ptr< Action > > getPCActions() const
Returns the pc action which was linked to the given event.
Definition MidiMap.h:131
std::multimap< QString, std::shared_ptr< Action > > getMMCActionMap() const
Definition MidiMap.h:122
static void create_instance()
If __instance equals 0, a new MidiMap singleton will be created and stored in it.
Definition MidiMap.cpp:64
std::multimap< int, std::shared_ptr< Action > > getNoteActionMap() const
Definition MidiMap.h:125
static MidiMap * get_instance()
Returns a pointer to the current MidiMap singleton stored in __instance.
Definition MidiMap.h:65
static void reset_instance()
Convenience function calling reset() on the current MidiMap __instance.
Definition MidiMap.cpp:71
void registerCCEvent(int, std::shared_ptr< Action >)
Sets up the relation between a cc event and an action.
Definition MidiMap.cpp:161
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
std::string get_version()
Returns the current Hydrogen version string.
Definition Version.cpp:30
static const std::string version
Definition Version.cpp:28