From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- .../openmpt-trunk/mptrack/TrackerSettings.cpp | 1605 ++++++++++++++++++++ 1 file changed, 1605 insertions(+) create mode 100644 Src/external_dependencies/openmpt-trunk/mptrack/TrackerSettings.cpp (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/TrackerSettings.cpp') diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/TrackerSettings.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/TrackerSettings.cpp new file mode 100644 index 00000000..dbf081a1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/TrackerSettings.cpp @@ -0,0 +1,1605 @@ +/* + * TrackerSettings.cpp + * ------------------- + * Purpose: Code for managing, loading and saving all applcation settings. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Mptrack.h" +#include "Moddoc.h" +#include "Mainfrm.h" +#include "mpt/environment/environment.hpp" +#include "mpt/uuid/uuid.hpp" +#include "openmpt/sounddevice/SoundDevice.hpp" +#include "openmpt/sounddevice/SoundDeviceManager.hpp" +#include "../common/version.h" +#include "UpdateCheck.h" +#include "Mpdlgs.h" +#include "../common/mptStringBuffer.h" +#include "TrackerSettings.h" +#include "../common/misc_util.h" +#include "PatternClipboard.h" +#include "../common/ComponentManager.h" +#include "ExceptionHandler.h" +#include "../soundlib/mod_specifications.h" +#include "../soundlib/Tables.h" +#include "../common/mptFileIO.h" +#include "../soundlib/tuningcollection.h" +#include "TuningDialog.h" + + +#include + + +OPENMPT_NAMESPACE_BEGIN + + +#define OLD_SOUNDSETUP_REVERSESTEREO 0x20 +#define OLD_SOUNDSETUP_SECONDARY 0x40 +#define OLD_SOUNDSETUP_NOBOOSTTHREADPRIORITY 0x80 + +#ifndef NO_EQ + +constexpr EQPreset FlatEQPreset = {"Flat", {16, 16, 16, 16, 16, 16}, {125, 300, 600, 1250, 4000, 8000}}; + +#endif // !NO_EQ + + +TrackerSettings &TrackerSettings::Instance() +{ + return theApp.GetTrackerSettings(); +} + + +static Version GetPreviousSettingsVersion(const mpt::ustring &iniVersion) +{ + if(!iniVersion.empty()) + { + return Version::Parse(iniVersion); + } else + { + // No version stored. + // This is the first run, thus set the previous version to our current + // version which will avoid running all settings upgrade code. + return Version::Current(); + } +} + + +mpt::ustring SettingsModTypeToString(MODTYPE modtype) +{ + return mpt::ToUnicode(mpt::Charset::UTF8, CSoundFile::GetModSpecifications(modtype).fileExtension); +} + +MODTYPE SettingsStringToModType(const mpt::ustring &str) +{ + return CModSpecifications::ExtensionToType(mpt::ToCharset(mpt::Charset::UTF8, str)); +} + + +static uint32 GetDefaultPatternSetup() +{ + return PATTERN_PLAYNEWNOTE | PATTERN_EFFECTHILIGHT + | PATTERN_CENTERROW | PATTERN_DRAGNDROPEDIT + | PATTERN_FLATBUTTONS | PATTERN_NOEXTRALOUD | PATTERN_2NDHIGHLIGHT + | PATTERN_STDHIGHLIGHT | PATTERN_SHOWPREVIOUS | PATTERN_CONTSCROLL + | PATTERN_SYNCMUTE | PATTERN_AUTODELAY | PATTERN_NOTEFADE + | PATTERN_SHOWDEFAULTVOLUME | PATTERN_LIVEUPDATETREE | PATTERN_SYNCSAMPLEPOS; +} + + +void SampleUndoBufferSize::CalculateSize() +{ + if(sizePercent < 0) + sizePercent = 0; + MEMORYSTATUSEX memStatus; + memStatus.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memStatus); + // The setting is a percentage of the memory that's actually *available* to OpenMPT, which is a max of 4GB in 32-bit mode. + sizeByte = mpt::saturate_cast(std::min(memStatus.ullTotalPhys, DWORDLONG(SIZE_T_MAX)) * sizePercent / 100); + + // Pretend there's at least one MiB of memory (haha) + if(sizePercent != 0 && sizeByte < 1 * 1024 * 1024) + { + sizeByte = 1 * 1024 * 1024; + } +} + + +DebugSettings::DebugSettings(SettingsContainer &conf) + : conf(conf) + // Debug +#if !defined(MPT_LOG_IS_DISABLED) + , DebugLogLevel(conf, U_("Debug"), U_("LogLevel"), static_cast(mpt::log::GlobalLogLevel)) + , DebugLogFacilitySolo(conf, U_("Debug"), U_("LogFacilitySolo"), std::string()) + , DebugLogFacilityBlocked(conf, U_("Debug"), U_("LogFacilityBlocked"), std::string()) + , DebugLogFileEnable(conf, U_("Debug"), U_("LogFileEnable"), mpt::log::FileEnabled) + , DebugLogDebuggerEnable(conf, U_("Debug"), U_("LogDebuggerEnable"), mpt::log::DebuggerEnabled) + , DebugLogConsoleEnable(conf, U_("Debug"), U_("LogConsoleEnable"), mpt::log::ConsoleEnabled) +#endif + , DebugTraceEnable(conf, U_("Debug"), U_("TraceEnable"), false) + , DebugTraceSize(conf, U_("Debug"), U_("TraceSize"), 1000000) + , DebugTraceAlwaysDump(conf, U_("Debug"), U_("TraceAlwaysDump"), false) + , DebugStopSoundDeviceOnCrash(conf, U_("Debug"), U_("StopSoundDeviceOnCrash"), true) + , DebugStopSoundDeviceBeforeDump(conf, U_("Debug"), U_("StopSoundDeviceBeforeDump"), false) + , DebugDelegateToWindowsHandler(conf, U_("Debug"), U_("DelegateToWindowsHandler"), false) +{ + + // Duplicate state for debug stuff in order to avoid calling into settings framework from crash context. + ExceptionHandler::stopSoundDeviceOnCrash = DebugStopSoundDeviceOnCrash; + ExceptionHandler::stopSoundDeviceBeforeDump = DebugStopSoundDeviceBeforeDump; + ExceptionHandler::delegateToWindowsHandler = DebugDelegateToWindowsHandler; + + // enable debug features (as early as possible after reading the settings) + #if !defined(MPT_LOG_IS_DISABLED) + #if !defined(MPT_LOG_GLOBAL_LEVEL_STATIC) + mpt::log::GlobalLogLevel = DebugLogLevel; + #endif + mpt::log::SetFacilities(DebugLogFacilitySolo, DebugLogFacilityBlocked); + mpt::log::FileEnabled = DebugLogFileEnable; + mpt::log::DebuggerEnabled = DebugLogDebuggerEnable; + mpt::log::ConsoleEnabled = DebugLogConsoleEnable; + #endif + if(DebugTraceEnable) + { + mpt::log::Trace::Enable(DebugTraceSize); + } +} + + +DebugSettings::~DebugSettings() +{ + if(DebugTraceAlwaysDump) + { + DebugTraceDump(); + } +} + + +TrackerSettings::TrackerSettings(SettingsContainer &conf) + : conf(conf) + // Version + , IniVersion(conf, U_("Version"), U_("Version"), mpt::ustring()) + , FirstRun(IniVersion.Get() == mpt::ustring()) + , PreviousSettingsVersion(GetPreviousSettingsVersion(IniVersion)) + , VersionInstallGUID(conf, U_("Version"), U_("InstallGUID"), mpt::UUID()) + // Display + , m_ShowSplashScreen(conf, U_("Display"), U_("ShowSplashScreen"), true) + , gbMdiMaximize(conf, U_("Display"), U_("MDIMaximize"), true) + , highResUI(conf, U_("Display"), U_("HighResUI"), false) + , glTreeSplitRatio(conf, U_("Display"), U_("MDITreeRatio"), 128) + , glTreeWindowWidth(conf, U_("Display"), U_("MDITreeWidth"), 160) + , glGeneralWindowHeight(conf, U_("Display"), U_("MDIGeneralHeight"), 222) + , glPatternWindowHeight(conf, U_("Display"), U_("MDIPatternHeight"), 152) + , glSampleWindowHeight(conf, U_("Display"), U_("MDISampleHeight"), 190) + , glInstrumentWindowHeight(conf, U_("Display"), U_("MDIInstrumentHeight"), 300) + , glCommentsWindowHeight(conf, U_("Display"), U_("MDICommentsHeight"), 288) + , glGraphWindowHeight(conf, U_("Display"), U_("MDIGraphHeight"), 288) + , gnPlugWindowX(conf, U_("Display"), U_("PlugSelectWindowX"), 243) + , gnPlugWindowY(conf, U_("Display"), U_("PlugSelectWindowY"), 273) + , gnPlugWindowWidth(conf, U_("Display"), U_("PlugSelectWindowWidth"), 450) + , gnPlugWindowHeight(conf, U_("Display"), U_("PlugSelectWindowHeight"), 540) + , gnPlugWindowLast(conf, U_("Display"), U_("PlugSelectWindowLast"), 0) + , gnMsgBoxVisiblityFlags(conf, U_("Display"), U_("MsgBoxVisibilityFlags"), uint32_max) + , GUIUpdateInterval(conf, U_("Display"), U_("GUIUpdateInterval"), 0) + , FSUpdateInterval(conf, U_("Display"), U_("FSUpdateInterval"), 500) + , VuMeterUpdateInterval(conf, U_("Display"), U_("VuMeterUpdateInterval"), 15) + , VuMeterDecaySpeedDecibelPerSecond(conf, U_("Display"), U_("VuMeterDecaySpeedDecibelPerSecond"), 88.0f) + , accidentalFlats(conf, U_("Display"), U_("AccidentalFlats"), false) + , rememberSongWindows(conf, U_("Display"), U_("RememberSongWindows"), true) + , showDirsInSampleBrowser(conf, U_("Display"), U_("ShowDirsInSampleBrowser"), false) + , commentsFont(conf, U_("Display"), U_("Comments Font"), FontSetting(U_("Courier New"), 120)) + , defaultRainbowChannelColors(conf, U_("Display"), U_("DefaultChannelColors"), DefaultChannelColors::Random) + // Misc + , defaultModType(conf, U_("Misc"), U_("DefaultModType"), MOD_TYPE_IT) + , defaultNewFileAction(conf, U_("Misc"), U_("DefaultNewFileAction"), nfDefaultFormat) + , DefaultPlugVolumeHandling(conf, U_("Misc"), U_("DefaultPlugVolumeHandling"), PLUGIN_VOLUMEHANDLING_IGNORE) + , autoApplySmoothFT2Ramping(conf, U_("Misc"), U_("SmoothFT2Ramping"), false) + , MiscITCompressionStereo(conf, U_("Misc"), U_("ITCompressionStereo"), 4) + , MiscITCompressionMono(conf, U_("Misc"), U_("ITCompressionMono"), 4) + , MiscSaveChannelMuteStatus(conf, U_("Misc"), U_("SaveChannelMuteStatus"), true) + , MiscAllowMultipleCommandsPerKey(conf, U_("Misc"), U_("AllowMultipleCommandsPerKey"), false) + , MiscDistinguishModifiers(conf, U_("Misc"), U_("DistinguishModifiers"), false) + , MiscProcessPriorityClass(conf, U_("Misc"), U_("ProcessPriorityClass"), ProcessPriorityClassNORMAL) + , MiscFlushFileBuffersOnSave(conf, U_("Misc"), U_("FlushFileBuffersOnSave"), true) + , MiscCacheCompleteFileBeforeLoading(conf, U_("Misc"), U_("CacheCompleteFileBeforeLoading"), false) + , MiscUseSingleInstance(conf, U_("Misc"), U_("UseSingleInstance"), false) + // Sound Settings + , m_SoundShowRecordingSettings(false) + , m_SoundShowDeprecatedDevices(conf, U_("Sound Settings"), U_("ShowDeprecatedDevices"), false) + , m_SoundDeprecatedDeviceWarningShown(conf, U_("Sound Settings"), U_("DeprecatedDeviceWarningShown"), false) + , m_SoundSampleRates(conf, U_("Sound Settings"), U_("SampleRates"), GetDefaultSampleRates()) + , m_SoundSettingsOpenDeviceAtStartup(conf, U_("Sound Settings"), U_("OpenDeviceAtStartup"), false) + , m_SoundSettingsStopMode(conf, U_("Sound Settings"), U_("StopMode"), SoundDeviceStopModeClosed) + , m_SoundDeviceSettingsUseOldDefaults(false) + , m_SoundDeviceID_DEPRECATED(SoundDevice::Legacy::ID()) + , m_SoundDeviceIdentifier(conf, U_("Sound Settings"), U_("Device"), SoundDevice::Identifier()) + , MixerMaxChannels(conf, U_("Sound Settings"), U_("MixChannels"), MixerSettings().m_nMaxMixChannels) + , MixerDSPMask(conf, U_("Sound Settings"), U_("Quality"), MixerSettings().DSPMask) + , MixerFlags(conf, U_("Sound Settings"), U_("SoundSetup"), MixerSettings().MixerFlags) + , MixerSamplerate(conf, U_("Sound Settings"), U_("Mixing_Rate"), MixerSettings().gdwMixingFreq) + , MixerOutputChannels(conf, U_("Sound Settings"), U_("ChannelMode"), MixerSettings().gnChannels) + , MixerPreAmp(conf, U_("Sound Settings"), U_("PreAmp"), MixerSettings().m_nPreAmp) + , MixerStereoSeparation(conf, U_("Sound Settings"), U_("StereoSeparation"), MixerSettings().m_nStereoSeparation) + , MixerVolumeRampUpMicroseconds(conf, U_("Sound Settings"), U_("VolumeRampUpMicroseconds"), MixerSettings().GetVolumeRampUpMicroseconds()) + , MixerVolumeRampDownMicroseconds(conf, U_("Sound Settings"), U_("VolumeRampDownMicroseconds"), MixerSettings().GetVolumeRampDownMicroseconds()) + , MixerNumInputChannels(conf, U_("Sound Settings"), U_("NumInputChannels"), static_cast(MixerSettings().NumInputChannels)) + , ResamplerMode(conf, U_("Sound Settings"), U_("SrcMode"), CResamplerSettings().SrcMode) + , ResamplerSubMode(conf, U_("Sound Settings"), U_("XMMSModplugResamplerWFIRType"), CResamplerSettings().gbWFIRType) + , ResamplerCutoffPercent(conf, U_("Sound Settings"), U_("ResamplerWFIRCutoff"), mpt::saturate_round(CResamplerSettings().gdWFIRCutoff * 100.0)) + , ResamplerEmulateAmiga(conf, U_("Sound Settings"), U_("ResamplerEmulateAmiga"), Resampling::AmigaFilter::A1200) + , SoundBoostedThreadPriority(conf, U_("Sound Settings"), U_("BoostedThreadPriority"), SoundDevice::AppInfo().BoostedThreadPriorityXP) + , SoundBoostedThreadMMCSSClass(conf, U_("Sound Settings"), U_("BoostedThreadMMCSSClass"), SoundDevice::AppInfo().BoostedThreadMMCSSClassVista) + , SoundBoostedThreadRealtimePosix(conf, U_("Sound Settings"), U_("BoostedThreadRealtimeLinux"), SoundDevice::AppInfo().BoostedThreadRealtimePosix) + , SoundBoostedThreadNicenessPosix(conf, U_("Sound Settings"), U_("BoostedThreadNicenessPosix"), SoundDevice::AppInfo().BoostedThreadNicenessPosix) + , SoundBoostedThreadRtprioPosix(conf, U_("Sound Settings"), U_("BoostedThreadRtprioLinux"), SoundDevice::AppInfo().BoostedThreadRtprioPosix) + , SoundMaskDriverCrashes(conf, U_("Sound Settings"), U_("MaskDriverCrashes"), SoundDevice::AppInfo().MaskDriverCrashes) + , SoundAllowDeferredProcessing(conf, U_("Sound Settings"), U_("AllowDeferredProcessing"), SoundDevice::AppInfo().AllowDeferredProcessing) + // MIDI Settings + , m_nMidiDevice(conf, U_("MIDI Settings"), U_("MidiDevice"), 0) + , midiDeviceName(conf, U_("MIDI Settings"), U_("MidiDeviceName"), _T("")) + , m_dwMidiSetup(conf, U_("MIDI Settings"), U_("MidiSetup"), MIDISETUP_RECORDVELOCITY | MIDISETUP_RECORDNOTEOFF | MIDISETUP_TRANSPOSEKEYBOARD | MIDISETUP_MIDITOPLUG) + , aftertouchBehaviour(conf, U_("MIDI Settings"), U_("AftertouchBehaviour"), atDoNotRecord) + , midiVelocityAmp(conf, U_("MIDI Settings"), U_("MidiVelocityAmp"), 100) + , midiIgnoreCCs(conf, U_("MIDI Settings"), U_("IgnoredCCs"), std::bitset<128>()) + , midiImportPatternLen(conf, U_("MIDI Settings"), U_("MidiImportPatLen"), 128) + , midiImportQuantize(conf, U_("MIDI Settings"), U_("MidiImportQuantize"), 32) + , midiImportTicks(conf, U_("MIDI Settings"), U_("MidiImportTicks"), 6) + // Pattern Editor + , gbLoopSong(conf, U_("Pattern Editor"), U_("LoopSong"), true) + , gnPatternSpacing(conf, U_("Pattern Editor"), U_("Spacing"), 0) + , gbPatternVUMeters(conf, U_("Pattern Editor"), U_("VU-Meters"), true) + , gbPatternPluginNames(conf, U_("Pattern Editor"), U_("Plugin-Names"), true) + , gbPatternRecord(conf, U_("Pattern Editor"), U_("Record"), true) + , patternNoEditPopup(conf, U_("Pattern Editor"), U_("NoEditPopup"), false) + , patternStepCommands(conf, U_("Pattern Editor"), U_("EditStepAppliesToCommands"), false) + , m_dwPatternSetup(conf, U_("Pattern Editor"), U_("PatternSetup"), GetDefaultPatternSetup()) + , m_nRowHighlightMeasures(conf, U_("Pattern Editor"), U_("RowSpacing"), 16) + , m_nRowHighlightBeats(conf, U_("Pattern Editor"), U_("RowSpacing2"), 4) + , recordQuantizeRows(conf, U_("Pattern Editor"), U_("RecordQuantize"), 0) + , gnAutoChordWaitTime(conf, U_("Pattern Editor"), U_("AutoChordWaitTime"), 60) + , orderlistMargins(conf, U_("Pattern Editor"), U_("DefaultSequenceMargins"), 0) + , rowDisplayOffset(conf, U_("Pattern Editor"), U_("RowDisplayOffset"), 0) + , patternFont(conf, U_("Pattern Editor"), U_("Font"), FontSetting(PATTERNFONT_SMALL, 0)) + , patternFontDot(conf, U_("Pattern Editor"), U_("FontDot"), U_(".")) + , effectVisWidth(conf, U_("Pattern Editor"), U_("EffectVisWidth"), -1) + , effectVisHeight(conf, U_("Pattern Editor"), U_("EffectVisHeight"), -1) + , effectVisX(conf, U_("Pattern Editor"), U_("EffectVisX"), int32_min) + , effectVisY(conf, U_("Pattern Editor"), U_("EffectVisY"), int32_min) + , patternAccessibilityFormat(conf, U_("Pattern Editor"), U_("AccessibilityFormat"), _T("Row %row%, Channel %channel%, %column_type%: %column_description%")) + , patternAlwaysDrawWholePatternOnScrollSlow(conf, U_("Pattern Editor"), U_("AlwaysDrawWholePatternOnScrollSlow"), false) + , orderListOldDropBehaviour(conf, U_("Pattern Editor"), U_("OrderListOldDropBehaviour"), false) + // Sample Editor + , m_SampleUndoBufferSize(conf, U_("Sample Editor"), U_("UndoBufferSize"), SampleUndoBufferSize()) + , sampleEditorKeyBehaviour(conf, U_("Sample Editor"), U_("KeyBehaviour"), seNoteOffOnNewKey) + , m_defaultSampleFormat(conf, U_("Sample Editor"), U_("DefaultFormat"), dfFLAC) + , sampleEditorTimelineFormat(conf, U_("Sample Editor"), U_("TimelineFormat"), TimelineFormat::Seconds) + , sampleEditorDefaultResampler(conf, U_("Sample Editor"), U_("DefaultResampler"), SRCMODE_DEFAULT) + , m_nFinetuneStep(conf, U_("Sample Editor"), U_("FinetuneStep"), 10) + , m_FLACCompressionLevel(conf, U_("Sample Editor"), U_("FLACCompressionLevel"), 5) + , compressITI(conf, U_("Sample Editor"), U_("CompressITI"), true) + , m_MayNormalizeSamplesOnLoad(conf, U_("Sample Editor"), U_("MayNormalizeSamplesOnLoad"), true) + , previewInFileDialogs(conf, U_("Sample Editor"), U_("PreviewInFileDialogs"), false) + , cursorPositionInHex(conf, U_("Sample Editor"), U_("CursorPositionInHex"), false) + // Export + , ExportDefaultToSoundcardSamplerate(conf, U_("Export"), U_("DefaultToSoundcardSamplerate"), true) + , ExportStreamEncoderSettings(conf, U_("Export")) + // Components + , ComponentsLoadOnStartup(conf, U_("Components"), U_("LoadOnStartup"), ComponentManagerSettingsDefault().LoadOnStartup()) + , ComponentsKeepLoaded(conf, U_("Components"), U_("KeepLoaded"), ComponentManagerSettingsDefault().KeepLoaded()) + // AutoSave + , CreateBackupFiles(conf, U_("AutoSave"), U_("CreateBackupFiles"), true) + , AutosaveEnabled(conf, U_("AutoSave"), U_("Enabled"), true) + , AutosaveIntervalMinutes(conf, U_("AutoSave"), U_("IntervalMinutes"), 10) + , AutosaveHistoryDepth(conf, U_("AutoSave"), U_("BackupHistory"), 3) + , AutosaveUseOriginalPath(conf, U_("AutoSave"), U_("UseOriginalPath"), true) + , AutosavePath(conf, U_("AutoSave"), U_("Path"), mpt::GetTempDirectory()) + // Paths + , PathSongs(conf, U_("Paths"), U_("Songs_Directory"), mpt::PathString()) + , PathSamples(conf, U_("Paths"), U_("Samples_Directory"), mpt::PathString()) + , PathInstruments(conf, U_("Paths"), U_("Instruments_Directory"), mpt::PathString()) + , PathPlugins(conf, U_("Paths"), U_("Plugins_Directory"), mpt::PathString()) + , PathPluginPresets(conf, U_("Paths"), U_("Plugin_Presets_Directory"), mpt::PathString()) + , PathExport(conf, U_("Paths"), U_("Export_Directory"), mpt::PathString()) + , PathTunings(theApp.GetConfigPath() + P_("tunings\\")) + , PathUserTemplates(theApp.GetConfigPath() + P_("TemplateModules\\")) + // Default template + , defaultTemplateFile(conf, U_("Paths"), U_("DefaultTemplate"), mpt::PathString()) + , defaultArtist(conf, U_("Misc"), U_("DefaultArtist"), mpt::getenv(U_("USERNAME")).value_or(U_(""))) + // MRU List + , mruListLength(conf, U_("Misc"), U_("MRUListLength"), 10) + // Plugins + , bridgeAllPlugins(conf, U_("VST Plugins"), U_("BridgeAllPlugins"), false) + , enableAutoSuspend(conf, U_("VST Plugins"), U_("EnableAutoSuspend"), false) + , midiMappingInPluginEditor(conf, U_("VST Plugins"), U_("EnableMidiMappingInEditor"), true) + , pluginProjectPath(conf, U_("VST Plugins"), U_("ProjectPath"), mpt::ustring()) + , vstHostProductString(conf, U_("VST Plugins"), U_("HostProductString"), "OpenMPT") + , vstHostVendorString(conf, U_("VST Plugins"), U_("HostVendorString"), "OpenMPT project") + , vstHostVendorVersion(conf, U_("VST Plugins"), U_("HostVendorVersion"), Version::Current().GetRawVersion()) + // Broken Plugins Workarounds + , BrokenPluginsWorkaroundVSTMaskAllCrashes(conf, U_("Broken Plugins Workarounds"), U_("VSTMaskAllCrashes"), true) // TODO: really should be false + , BrokenPluginsWorkaroundVSTNeverUnloadAnyPlugin(conf, U_("Broken Plugins Workarounds"), U_("VSTNeverUnloadAnyPlugin"), false) +#if defined(MPT_ENABLE_UPDATE) + // Update + , UpdateEnabled(conf, U_("Update"), U_("Enabled"), true) + , UpdateInstallAutomatically(conf, U_("Update"), U_("InstallAutomatically"), false) + , UpdateLastUpdateCheck(conf, U_("Update"), U_("LastUpdateCheck"), mpt::Date::Unix(time_t())) + , UpdateUpdateCheckPeriod_DEPRECATED(conf, U_("Update"), U_("UpdateCheckPeriod"), 7) + , UpdateIntervalDays(conf, U_("Update"), U_("UpdateCheckIntervalDays"), 7) + , UpdateChannel(conf, U_("Update"), U_("Channel"), UpdateChannelRelease) + , UpdateUpdateURL_DEPRECATED(conf, U_("Update"), U_("UpdateURL"), U_("https://update.openmpt.org/check/$VERSION/$GUID")) + , UpdateAPIURL(conf, U_("Update"), U_("APIURL"), CUpdateCheck::GetDefaultAPIURL()) + , UpdateStatisticsConsentAsked(conf, U_("Update"), U_("StatistisConsentAsked"), false) + , UpdateStatistics(conf, U_("Update"), U_("Statistis"), false) + , UpdateSendGUID_DEPRECATED(conf, U_("Update"), U_("SendGUID"), false) + , UpdateShowUpdateHint(conf, U_("Update"), U_("ShowUpdateHint"), true) + , UpdateIgnoreVersion(conf, U_("Update"), U_("IgnoreVersion"), _T("")) + , UpdateSkipSignatureVerificationUNSECURE(conf, U_("Update"), U_("SkipSignatureVerification"), false) + , UpdateSigningKeysRootAnchors(conf, U_("Update"), U_("SigningKeysRootAnchors"), CUpdateCheck::GetDefaultUpdateSigningKeysRootAnchors()) +#endif // MPT_ENABLE_UPDATE + // Wine suppport + , WineSupportEnabled(conf, U_("WineSupport"), U_("Enabled"), false) + , WineSupportAlwaysRecompile(conf, U_("WineSupport"), U_("AlwaysRecompile"), false) + , WineSupportAskCompile(conf, U_("WineSupport"), U_("AskCompile"), false) + , WineSupportCompileVerbosity(conf, U_("WineSupport"), U_("CompileVerbosity"), 2) // 0=silent 1=silentmake 2=progresswindow 3=standard 4=verbosemake 5=veryverbosemake 6=msgboxes + , WineSupportForeignOpenMPT(conf, U_("WineSupport"), U_("ForeignOpenMPT"), false) + , WineSupportAllowUnknownHost(conf, U_("WineSupport"), U_("AllowUnknownHost"), false) + , WineSupportEnablePulseAudio(conf, U_("WineSupport"), U_("EnablePulseAudio"), 1) + , WineSupportEnablePortAudio(conf, U_("WineSupport"), U_("EnablePortAudio"), 1) + , WineSupportEnableRtAudio(conf, U_("WineSupport"), U_("EnableRtAudio"), 1) +{ + + // Effects +#ifndef NO_DSP + m_MegaBassSettings.m_nXBassDepth = conf.Read(U_("Effects"), U_("XBassDepth"), m_MegaBassSettings.m_nXBassDepth); + m_MegaBassSettings.m_nXBassRange = conf.Read(U_("Effects"), U_("XBassRange"), m_MegaBassSettings.m_nXBassRange); +#endif +#ifndef NO_REVERB + m_ReverbSettings.m_nReverbDepth = conf.Read(U_("Effects"), U_("ReverbDepth"), m_ReverbSettings.m_nReverbDepth); + m_ReverbSettings.m_nReverbType = conf.Read(U_("Effects"), U_("ReverbType"), m_ReverbSettings.m_nReverbType); +#endif +#ifndef NO_DSP + m_SurroundSettings.m_nProLogicDepth = conf.Read(U_("Effects"), U_("ProLogicDepth"), m_SurroundSettings.m_nProLogicDepth); + m_SurroundSettings.m_nProLogicDelay = conf.Read(U_("Effects"), U_("ProLogicDelay"), m_SurroundSettings.m_nProLogicDelay); +#endif +#ifndef NO_EQ + m_EqSettings = conf.Read(U_("Effects"), U_("EQ_Settings"), FlatEQPreset); + const EQPreset userPresets[] = + { + FlatEQPreset, + { "User 1", {16,16,16,16,16,16}, { 150, 350, 700, 1500, 4500, 8000 } }, + { "User 2", {16,16,16,16,16,16}, { 200, 400, 800, 1750, 5000, 9000 } }, + { "User 3", {16,16,16,16,16,16}, { 250, 450, 900, 2000, 5000, 10000 } } + }; + + m_EqUserPresets[0] = conf.Read(U_("Effects"), U_("EQ_User1"), userPresets[0]); + m_EqUserPresets[1] = conf.Read(U_("Effects"), U_("EQ_User2"), userPresets[1]); + m_EqUserPresets[2] = conf.Read(U_("Effects"), U_("EQ_User3"), userPresets[2]); + m_EqUserPresets[3] = conf.Read(U_("Effects"), U_("EQ_User4"), userPresets[3]); +#endif +#ifndef NO_DSP + m_BitCrushSettings.m_Bits = conf.Read(U_("Effects"), U_("BitCrushBits"), m_BitCrushSettings.m_Bits); +#endif + // Display (Colors) + GetDefaultColourScheme(rgbCustomColors); + for(int ncol = 0; ncol < MAX_MODCOLORS; ncol++) + { + const mpt::ustring colorName = MPT_UFORMAT("Color{}")(mpt::ufmt::dec0<2>(ncol)); + rgbCustomColors[ncol] = conf.Read(U_("Display"), colorName, rgbCustomColors[ncol]); + } + // Paths + m_szKbdFile = conf.Read(U_("Paths"), U_("Key_Config_File"), mpt::PathString()); + conf.Forget(U_("Paths"), U_("Key_Config_File")); + + // init old and messy stuff: + + // Default chords + MemsetZero(Chords); + for(UINT ichord = 0; ichord < 3 * 12; ichord++) + { + Chords[ichord].key = (uint8)ichord; + Chords[ichord].notes[0] = MPTChord::noNote; + Chords[ichord].notes[1] = MPTChord::noNote; + Chords[ichord].notes[2] = MPTChord::noNote; + + if(ichord < 12) + { + // Major Chords + Chords[ichord].notes[0] = (int8)(ichord + 4); + Chords[ichord].notes[1] = (int8)(ichord + 7); + Chords[ichord].notes[2] = (int8)(ichord + 10); + } else if(ichord < 24) + { + // Minor Chords + Chords[ichord].notes[0] = (int8)(ichord - 9); + Chords[ichord].notes[1] = (int8)(ichord - 5); + Chords[ichord].notes[2] = (int8)(ichord - 2); + } + } + + + // load old and messy stuff: + + PatternClipboard::SetClipboardSize(conf.Read(U_("Pattern Editor"), U_("NumClipboards"), mpt::saturate_cast(PatternClipboard::GetClipboardSize()))); + + // Chords + LoadChords(Chords); + + // Zxx Macros + MIDIMacroConfig macros; + theApp.GetDefaultMidiMacro(macros); + for(int i = 0; i < kSFxMacros; i++) + { + macros.SFx[i] = conf.Read(U_("Zxx Macros"), MPT_UFORMAT("SF{}")(mpt::ufmt::HEX(i)), macros.SFx[i]); + } + for(int i = 0; i < kZxxMacros; i++) + { + macros.Zxx[i] = conf.Read(U_("Zxx Macros"), MPT_UFORMAT("Z{}")(mpt::ufmt::HEX0<2>(i | 0x80)), macros.Zxx[i]); + } + + + // MRU list + Limit(mruListLength, 0u, 32u); + mruFiles.reserve(mruListLength); + for(uint32 i = 0; i < mruListLength; i++) + { + mpt::ustring key = MPT_UFORMAT("File{}")(i); + + mpt::PathString path = theApp.PathInstallRelativeToAbsolute(conf.Read(U_("Recent File List"), key, mpt::PathString())); + if(!path.empty()) + { + mruFiles.push_back(path); + } + } + + // Fixups: + // ------- + + const Version storedVersion = PreviousSettingsVersion; + + // Version + if(!VersionInstallGUID.Get().IsValid()) + { + // No UUID found - generate one. + VersionInstallGUID = mpt::UUID::Generate(mpt::global_prng()); + } + + // Plugins + if(storedVersion < MPT_V("1.19.03.01") && vstHostProductString.Get() == "OpenMPT") + { + vstHostVendorVersion = Version::Current().GetRawVersion(); + } + if(storedVersion < MPT_V("1.30.00.24")) + { + BrokenPluginsWorkaroundVSTNeverUnloadAnyPlugin = !conf.Read(U_("VST Plugins"), U_("FullyUnloadPlugins"), true); + conf.Remove(U_("VST Plugins"), U_("FullyUnloadPlugins")); + } + + // Sound Settings + if(storedVersion < MPT_V("1.22.07.30")) + { + if(conf.Read(U_("Sound Settings"), U_("KeepDeviceOpen"), false)) + { + m_SoundSettingsStopMode = SoundDeviceStopModePlaying; + } else + { + m_SoundSettingsStopMode = SoundDeviceStopModeStopped; + } + } + if(storedVersion < MPT_V("1.22.07.04")) + { + std::vector sampleRates = m_SoundSampleRates; + if(std::count(sampleRates.begin(), sampleRates.end(), MixerSamplerate) == 0) + { + sampleRates.push_back(MixerSamplerate); + std::sort(sampleRates.begin(), sampleRates.end()); + std::reverse(sampleRates.begin(), sampleRates.end()); + m_SoundSampleRates = sampleRates; + } + } + if(storedVersion < MPT_V("1.22.07.04")) + { + m_SoundDeviceID_DEPRECATED = conf.Read(U_("Sound Settings"), U_("WaveDevice"), SoundDevice::Legacy::ID()); + Setting m_BufferLength_DEPRECATED(conf, U_("Sound Settings"), U_("BufferLength"), 50); + Setting m_LatencyMS(conf, U_("Sound Settings"), U_("Latency"), mpt::saturate_round(SoundDevice::Settings().Latency * 1000.0)); + Setting m_UpdateIntervalMS(conf, U_("Sound Settings"), U_("UpdateInterval"), mpt::saturate_round(SoundDevice::Settings().UpdateInterval * 1000.0)); + Setting m_SampleFormat(conf, U_("Sound Settings"), U_("BitsPerSample"), SoundDevice::Settings().sampleFormat); + Setting m_SoundDeviceExclusiveMode(conf, U_("Sound Settings"), U_("ExclusiveMode"), SoundDevice::Settings().ExclusiveMode); + Setting m_SoundDeviceBoostThreadPriority(conf, U_("Sound Settings"), U_("BoostThreadPriority"), SoundDevice::Settings().BoostThreadPriority); + Setting m_SoundDeviceUseHardwareTiming(conf, U_("Sound Settings"), U_("UseHardwareTiming"), SoundDevice::Settings().UseHardwareTiming); + Setting m_SoundDeviceChannelMapping(conf, U_("Sound Settings"), U_("ChannelMapping"), SoundDevice::Settings().Channels); + if(storedVersion < MPT_V("1.21.01.26")) + { + if(m_BufferLength_DEPRECATED != 0) + { + if(m_BufferLength_DEPRECATED < 1) m_BufferLength_DEPRECATED = 1; // 1ms + if(m_BufferLength_DEPRECATED > 1000) m_BufferLength_DEPRECATED = 1000; // 1sec + if((m_SoundDeviceID_DEPRECATED & SoundDevice::Legacy::MaskType) == SoundDevice::Legacy::TypeASIO) + { + m_LatencyMS = m_BufferLength_DEPRECATED * 1; + m_UpdateIntervalMS = m_BufferLength_DEPRECATED / 8; + } else + { + m_LatencyMS = m_BufferLength_DEPRECATED * 3; + m_UpdateIntervalMS = m_BufferLength_DEPRECATED / 8; + } + if(!m_UpdateIntervalMS) m_UpdateIntervalMS = static_cast(SoundDevice::Settings().UpdateInterval * 1000.0); + } + conf.Remove(m_BufferLength_DEPRECATED.GetPath()); + } + if(storedVersion < MPT_V("1.22.01.03")) + { + m_SoundDeviceExclusiveMode = ((MixerFlags & OLD_SOUNDSETUP_SECONDARY) == 0); + } + if(storedVersion < MPT_V("1.22.01.03")) + { + m_SoundDeviceBoostThreadPriority = ((MixerFlags & OLD_SOUNDSETUP_NOBOOSTTHREADPRIORITY) == 0); + } + if(storedVersion < MPT_V("1.22.07.03")) + { + m_SoundDeviceChannelMapping = SoundDevice::ChannelMapping::BaseChannel(MixerOutputChannels, conf.Read(U_("Sound Settings"), U_("ASIOBaseChannel"), 0)); + } + m_SoundDeviceSettingsDefaults.Latency = m_LatencyMS / 1000.0; + m_SoundDeviceSettingsDefaults.UpdateInterval = m_UpdateIntervalMS / 1000.0; + m_SoundDeviceSettingsDefaults.Samplerate = MixerSamplerate; + if(m_SoundDeviceSettingsDefaults.Channels.GetNumHostChannels() != MixerOutputChannels) + { + // reset invalid channel mapping to default + m_SoundDeviceSettingsDefaults.Channels = SoundDevice::ChannelMapping(MixerOutputChannels); + } + m_SoundDeviceSettingsDefaults.InputChannels = 0; + m_SoundDeviceSettingsDefaults.sampleFormat = m_SampleFormat; + m_SoundDeviceSettingsDefaults.ExclusiveMode = m_SoundDeviceExclusiveMode; + m_SoundDeviceSettingsDefaults.BoostThreadPriority = m_SoundDeviceBoostThreadPriority; + m_SoundDeviceSettingsDefaults.UseHardwareTiming = m_SoundDeviceUseHardwareTiming; + m_SoundDeviceSettingsDefaults.InputSourceID = 0; + m_SoundDeviceSettingsUseOldDefaults = true; + } + if(storedVersion < MPT_V("1.28.00.41")) + { + // reset this setting to the default when updating, + // because we do not provide a GUI any more, + // and in general, it should not get changed anyway + ResamplerCutoffPercent = mpt::saturate_round(CResamplerSettings().gdWFIRCutoff * 100.0); + } + if(MixerSamplerate == 0) + { + MixerSamplerate = MixerSettings().gdwMixingFreq; + } + if(storedVersion < MPT_V("1.21.01.26")) + { + MixerFlags &= ~OLD_SOUNDSETUP_REVERSESTEREO; + } + if(storedVersion < MPT_V("1.22.01.03")) + { + MixerFlags &= ~OLD_SOUNDSETUP_SECONDARY; + } + if(storedVersion < MPT_V("1.22.01.03")) + { + MixerFlags &= ~OLD_SOUNDSETUP_NOBOOSTTHREADPRIORITY; + } + if(storedVersion < MPT_V("1.20.00.22")) + { + MixerSettings settings = GetMixerSettings(); + settings.SetVolumeRampUpSamples(conf.Read(U_("Sound Settings"), U_("VolumeRampSamples"), 42)); + settings.SetVolumeRampDownSamples(conf.Read(U_("Sound Settings"), U_("VolumeRampSamples"), 42)); + SetMixerSettings(settings); + conf.Remove(U_("Sound Settings"), U_("VolumeRampSamples")); + } else if(storedVersion < MPT_V("1.22.07.18")) + { + MixerSettings settings = GetMixerSettings(); + settings.SetVolumeRampUpSamples(conf.Read(U_("Sound Settings"), U_("VolumeRampUpSamples"), MixerSettings().GetVolumeRampUpSamples())); + settings.SetVolumeRampDownSamples(conf.Read(U_("Sound Settings"), U_("VolumeRampDownSamples"), MixerSettings().GetVolumeRampDownSamples())); + SetMixerSettings(settings); + } + Limit(ResamplerCutoffPercent, 0, 100); + if(storedVersion < MPT_V("1.29.00.11")) + { + MixerMaxChannels = MixerSettings().m_nMaxMixChannels; // reset to default on update because we removed the setting in the GUI + } + if(storedVersion < MPT_V("1.29.00.20")) + { + MixerDSPMask = MixerDSPMask & ~SNDDSP_BITCRUSH; + } + + // Misc + if(defaultModType == MOD_TYPE_NONE) + { + defaultModType = MOD_TYPE_IT; + } + + // MIDI Settings + if((m_dwMidiSetup & 0x40) != 0 && storedVersion < MPT_V("1.20.00.86")) + { + // This flag used to be "amplify MIDI Note Velocity" - with a fixed amplification factor of 2. + midiVelocityAmp = 200; + m_dwMidiSetup &= ~0x40; + } + + // Pattern Editor + if(storedVersion < MPT_V("1.17.02.50")) + { + m_dwPatternSetup |= PATTERN_NOTEFADE; + } + if(storedVersion < MPT_V("1.17.03.01")) + { + m_dwPatternSetup |= PATTERN_RESETCHANNELS; + } + if(storedVersion < MPT_V("1.19.00.07")) + { + m_dwPatternSetup &= ~0x800; // this was previously deprecated and is now used for something else + } + if(storedVersion < MPT_V("1.20.00.04")) + { + m_dwPatternSetup &= ~0x200000; // ditto + } + if(storedVersion < MPT_V("1.20.00.07")) + { + m_dwPatternSetup &= ~0x400000; // ditto + } + if(storedVersion < MPT_V("1.20.00.39")) + { + m_dwPatternSetup &= ~0x10000000; // ditto + } + if(storedVersion < MPT_V("1.24.01.04")) + { + commentsFont = FontSetting(U_("Courier New"), (m_dwPatternSetup & 0x02) ? 120 : 90); + patternFont = FontSetting((m_dwPatternSetup & 0x08) ? PATTERNFONT_SMALL : PATTERNFONT_LARGE, 0); + m_dwPatternSetup &= ~(0x08 | 0x02); + } + if(storedVersion < MPT_V("1.25.00.08") && glGeneralWindowHeight < 222) + { + glGeneralWindowHeight += 44; + } + if(storedVersion < MPT_V("1.25.00.16") && (m_dwPatternSetup & 0x100000)) + { + // Move MIDI recording to MIDI setup + m_dwPatternSetup &= ~0x100000; + m_dwMidiSetup |= MIDISETUP_ENABLE_RECORD_DEFAULT; + } + if(storedVersion < MPT_V("1.27.00.51")) + { + // Moving option out of pattern config + CreateBackupFiles = (m_dwPatternSetup & 0x200) != 0; + m_dwPatternSetup &= ~0x200; + } + + // Export + if(storedVersion < MPT_V("1.30.00.38")) + { + { + conf.Write(U_("Export"), U_("FLAC_Mode"), Encoder::ModeLossless); + const int oldformat = conf.Read(U_("Export"), U_("FLAC_Format"), 1); + Encoder::Format newformat = { Encoder::Format::Encoding::Integer, 24, mpt::get_endian() }; + if (oldformat >= 0) + { + switch (oldformat % 3) + { + case 0: + newformat = { Encoder::Format::Encoding::Integer, 24, mpt::get_endian() }; + break; + case 1: + newformat = { Encoder::Format::Encoding::Integer, 16, mpt::get_endian() }; + break; + case 2: + newformat = { Encoder::Format::Encoding::Integer, 8, mpt::get_endian() }; + break; + } + } + conf.Write(U_("Export"), U_("FLAC_Format2"), newformat); + conf.Forget(U_("Export"), U_("FLAC_Format")); + } + { + conf.Write(U_("Export"), U_("Wave_Mode"), Encoder::ModeLossless); + const int oldformat = conf.Read(U_("Export"), U_("Wave_Format"), 1); + Encoder::Format newformat = { Encoder::Format::Encoding::Float, 32, mpt::endian::little }; + if (oldformat >= 0) + { + switch (oldformat % 6) + { + case 0: + newformat = { Encoder::Format::Encoding::Float, 64, mpt::endian::little }; + break; + case 1: + newformat = { Encoder::Format::Encoding::Float, 32, mpt::endian::little }; + break; + case 2: + newformat = { Encoder::Format::Encoding::Integer, 32, mpt::endian::little }; + break; + case 3: + newformat = { Encoder::Format::Encoding::Integer, 24, mpt::endian::little }; + break; + case 4: + newformat = { Encoder::Format::Encoding::Integer, 16, mpt::endian::little }; + break; + case 5: + newformat = { Encoder::Format::Encoding::Unsigned, 8, mpt::endian::little }; + break; + } + } + conf.Write(U_("Export"), U_("Wave_Format2"), newformat); + conf.Forget(U_("Export"), U_("Wave_Format")); + } + { + conf.Write(U_("Export"), U_("AU_Mode"), Encoder::ModeLossless); + const int oldformat = conf.Read(U_("Export"), U_("AU_Format"), 1); + Encoder::Format newformat = { Encoder::Format::Encoding::Float, 32, mpt::endian::big }; + if(oldformat >= 0) + { + switch(oldformat % 6) + { + case 0: + newformat = { Encoder::Format::Encoding::Float, 64, mpt::endian::big }; + break; + case 1: + newformat = { Encoder::Format::Encoding::Float, 32, mpt::endian::big }; + break; + case 2: + newformat = { Encoder::Format::Encoding::Integer, 32, mpt::endian::big }; + break; + case 3: + newformat = { Encoder::Format::Encoding::Integer, 24, mpt::endian::big }; + break; + case 4: + newformat = { Encoder::Format::Encoding::Integer, 16, mpt::endian::big }; + break; + case 5: + newformat = { Encoder::Format::Encoding::Integer, 8, mpt::endian::big }; + break; + } + } + conf.Write(U_("Export"), U_("AU_Format2"), newformat); + conf.Forget(U_("Export"), U_("AU_Format")); + } + { + conf.Write(U_("Export"), U_("RAW_Mode"), Encoder::ModeLossless); + const int oldformat = conf.Read(U_("Export"), U_("RAW_Format"), 1); + Encoder::Format newformat = { Encoder::Format::Encoding::Float, 32, mpt::get_endian() }; + if(oldformat >= 0) + { + switch(oldformat % 7) + { + case 0: + newformat = { Encoder::Format::Encoding::Float, 64, mpt::get_endian() }; + break; + case 1: + newformat = { Encoder::Format::Encoding::Float, 32, mpt::get_endian() }; + break; + case 2: + newformat = { Encoder::Format::Encoding::Integer, 32, mpt::get_endian() }; + break; + case 3: + newformat = { Encoder::Format::Encoding::Integer, 24, mpt::get_endian() }; + break; + case 4: + newformat = { Encoder::Format::Encoding::Integer, 16, mpt::get_endian() }; + break; + case 5: + newformat = { Encoder::Format::Encoding::Integer, 8, mpt::get_endian() }; + break; + case 6: + newformat = { Encoder::Format::Encoding::Unsigned, 8, mpt::get_endian() }; + break; + } + } + conf.Write(U_("Export"), U_("RAW_Format2"), newformat); + conf.Forget(U_("Export"), U_("RAW_Format")); + } + } + +#if defined(MPT_ENABLE_UPDATE) + // Update + if(storedVersion < MPT_V("1.28.00.39")) + { + if(UpdateUpdateCheckPeriod_DEPRECATED <= 0) + { + UpdateEnabled = true; + UpdateIntervalDays = -1; + } else + { + UpdateEnabled = true; + UpdateIntervalDays = UpdateUpdateCheckPeriod_DEPRECATED.Get(); + } + const auto url = UpdateUpdateURL_DEPRECATED.Get(); + if(url.empty() || + url == UL_("http://update.openmpt.org/check/$VERSION/$GUID") || + url == UL_("https://update.openmpt.org/check/$VERSION/$GUID")) + { + UpdateChannel = UpdateChannelRelease; + } else if(url == UL_("http://update.openmpt.org/check/testing/$VERSION/$GUID") || + url == UL_("https://update.openmpt.org/check/testing/$VERSION/$GUID")) + { + UpdateChannel = UpdateChannelDevelopment; + } else + { + UpdateChannel = UpdateChannelDevelopment; + } + UpdateStatistics = UpdateSendGUID_DEPRECATED.Get(); + conf.Forget(UpdateUpdateCheckPeriod_DEPRECATED.GetPath()); + conf.Forget(UpdateUpdateURL_DEPRECATED.GetPath()); + conf.Forget(UpdateSendGUID_DEPRECATED.GetPath()); + } +#endif // MPT_ENABLE_UPDATE + + if(storedVersion < MPT_V("1.29.00.39")) + { + // ASIO device IDs are now normalized to upper-case in the device enumeration code. + // Previous device IDs could be mixed-case (as retrieved from the registry), which would make direct ID comparison fail now. + auto device = m_SoundDeviceIdentifier.Get(); + if(device.substr(0, 5) == UL_("ASIO_")) + { + device = mpt::ToUpperCase(device); + m_SoundDeviceIdentifier = device; + } + } + + // Effects +#ifndef NO_EQ + FixupEQ(m_EqSettings); + FixupEQ(m_EqUserPresets[0]); + FixupEQ(m_EqUserPresets[1]); + FixupEQ(m_EqUserPresets[2]); + FixupEQ(m_EqUserPresets[3]); +#endif // !NO_EQ + + // Zxx Macros + if((MPT_V("1.17.00.00") <= storedVersion) && (storedVersion < MPT_V("1.20.00.00"))) + { + // Fix old nasty broken (non-standard) MIDI configs in INI file. + macros.UpgradeMacros(); + } + theApp.SetDefaultMidiMacro(macros); + + // Paths + m_szKbdFile = theApp.PathInstallRelativeToAbsolute(m_szKbdFile); + + // Sample undo buffer size (used to be a hidden, absolute setting in MiB) + int64 oldUndoSize = m_SampleUndoBufferSize.Get().GetSizeInPercent(); + if(storedVersion < MPT_V("1.22.07.25") && oldUndoSize != SampleUndoBufferSize::defaultSize && oldUndoSize != 0) + { + m_SampleUndoBufferSize = SampleUndoBufferSize(static_cast(100 * (oldUndoSize << 20) / SampleUndoBufferSize(100).GetSizeInBytes())); + } + + // More controls in the plugin selection dialog + if(storedVersion < MPT_V("1.26.00.26")) + { + gnPlugWindowHeight += 40; + } + + // Sanitize resampling mode for sample editor + if(!Resampling::IsKnownMode(sampleEditorDefaultResampler) && sampleEditorDefaultResampler != SRCMODE_DEFAULT) + { + sampleEditorDefaultResampler = SRCMODE_DEFAULT; + } + + // Migrate Tuning data + MigrateTunings(storedVersion); + + // Sanitize MIDI import data + if(midiImportPatternLen < 1 || midiImportPatternLen > MAX_PATTERN_ROWS) + midiImportPatternLen = 128; + if(midiImportQuantize < 4 || midiImportQuantize > 256) + midiImportQuantize = 32; + if(midiImportTicks < 2 || midiImportTicks > 16) + midiImportTicks = 16; + + // Last fixup: update config version + IniVersion = mpt::ufmt::val(Version::Current()); + + // Write updated settings + conf.Flush(); +} + + +TrackerSettings::~TrackerSettings() +{ + return; +} + + +namespace SoundDevice +{ +namespace Legacy +{ +SoundDevice::Info FindDeviceInfo(SoundDevice::Manager &manager, SoundDevice::Legacy::ID id) +{ + if(manager.GetDeviceInfos().empty()) + { + return SoundDevice::Info(); + } + SoundDevice::Type type = SoundDevice::Type(); + switch((id & SoundDevice::Legacy::MaskType) >> SoundDevice::Legacy::ShiftType) + { + case SoundDevice::Legacy::TypeWAVEOUT: + type = SoundDevice::TypeWAVEOUT; + break; + case SoundDevice::Legacy::TypeDSOUND: + type = SoundDevice::TypeDSOUND; + break; + case SoundDevice::Legacy::TypeASIO: + type = SoundDevice::TypeASIO; + break; + case SoundDevice::Legacy::TypePORTAUDIO_WASAPI: + type = SoundDevice::TypePORTAUDIO_WASAPI; + break; + case SoundDevice::Legacy::TypePORTAUDIO_WDMKS: + type = SoundDevice::TypePORTAUDIO_WDMKS; + break; + case SoundDevice::Legacy::TypePORTAUDIO_WMME: + type = SoundDevice::TypePORTAUDIO_WMME; + break; + case SoundDevice::Legacy::TypePORTAUDIO_DS: + type = SoundDevice::TypePORTAUDIO_DS; + break; + } + if(type.empty()) + { // fallback to first device + return *manager.begin(); + } + std::size_t index = static_cast((id & SoundDevice::Legacy::MaskIndex) >> SoundDevice::Legacy::ShiftIndex); + std::size_t seenDevicesOfDesiredType = 0; + for(const auto &info : manager) + { + if(info.type == type) + { + if(seenDevicesOfDesiredType == index) + { + if(!info.IsValid()) + { // fallback to first device + return *manager.begin(); + } + return info; + } + seenDevicesOfDesiredType++; + } + } + // default to first device + return *manager.begin(); +} +} // namespace Legacy +} // namespace SoundDevice + + +void TrackerSettings::MigrateOldSoundDeviceSettings(SoundDevice::Manager &manager) +{ + if(m_SoundDeviceSettingsUseOldDefaults) + { + // get the old default device + SetSoundDeviceIdentifier(SoundDevice::Legacy::FindDeviceInfo(manager, m_SoundDeviceID_DEPRECATED).GetIdentifier()); + // apply old global sound device settings to each found device + for(const auto &it : manager) + { + SetSoundDeviceSettings(it.GetIdentifier(), GetSoundDeviceSettingsDefaults()); + } + } +} + + +void TrackerSettings::MigrateTunings(const Version storedVersion) +{ + if(!PathTunings.GetDefaultDir().IsDirectory()) + { + CreateDirectory(PathTunings.GetDefaultDir().AsNative().c_str(), 0); + } + if(!(PathTunings.GetDefaultDir() + P_("Built-in\\")).IsDirectory()) + { + CreateDirectory((PathTunings.GetDefaultDir() + P_("Built-in\\")).AsNative().c_str(), 0); + } + if(!(PathTunings.GetDefaultDir() + P_("Locale\\")).IsDirectory()) + { + CreateDirectory((PathTunings.GetDefaultDir() + P_("Local\\")).AsNative().c_str(), 0); + } + { + mpt::PathString fn = PathTunings.GetDefaultDir() + P_("Built-in\\12TET.tun"); + if(!fn.FileOrDirectoryExists()) + { + std::unique_ptr pT = CSoundFile::CreateTuning12TET(U_("12TET")); + mpt::SafeOutputFile sf(fn, std::ios::binary, mpt::FlushMode::Full); + pT->Serialize(sf); + } + } + { + mpt::PathString fn = PathTunings.GetDefaultDir() + P_("Built-in\\12TET [[fs15 1.17.02.49]].tun"); + if(!fn.FileOrDirectoryExists()) + { + std::unique_ptr pT = CSoundFile::CreateTuning12TET(U_("12TET [[fs15 1.17.02.49]]")); + mpt::SafeOutputFile sf(fn, std::ios::binary, mpt::FlushMode::Full); + pT->Serialize(sf); + } + } + oldLocalTunings = LoadLocalTunings(); + if(storedVersion < MPT_V("1.27.00.56")) + { + UnpackTuningCollection(*oldLocalTunings, PathTunings.GetDefaultDir() + P_("Local\\")); + } +} + + +std::unique_ptr TrackerSettings::LoadLocalTunings() +{ + std::unique_ptr s_pTuningsSharedLocal = std::make_unique(); + mpt::ifstream f( + PathTunings.GetDefaultDir() + + P_("local_tunings") + + mpt::PathString::FromUTF8(CTuningCollection::s_FileExtension) + , std::ios::binary); + if(f.good()) + { + mpt::ustring dummyName; + s_pTuningsSharedLocal->Deserialize(f, dummyName, TuningCharsetFallback); + } + return s_pTuningsSharedLocal; +} + + +struct StoredSoundDeviceSettings +{ + +private: + SettingsContainer &conf; + const SoundDevice::Info deviceInfo; + +private: + Setting LatencyUS; + Setting UpdateIntervalUS; + Setting Samplerate; + Setting ChannelsOld; // compatibility with older versions + Setting ChannelMapping; + Setting InputChannels; + Setting sampleFormat; + Setting ExclusiveMode; + Setting BoostThreadPriority; + Setting KeepDeviceRunning; + Setting UseHardwareTiming; + Setting DitherType; + Setting InputSourceID; + +public: + + StoredSoundDeviceSettings(SettingsContainer &conf, const SoundDevice::Info & deviceInfo, const SoundDevice::Settings & defaults) + : conf(conf) + , deviceInfo(deviceInfo) + , LatencyUS(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("Latency"), mpt::saturate_round(defaults.Latency * 1000000.0)) + , UpdateIntervalUS(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("UpdateInterval"), mpt::saturate_round(defaults.UpdateInterval * 1000000.0)) + , Samplerate(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("SampleRate"), defaults.Samplerate) + , ChannelsOld(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("Channels"), mpt::saturate_cast((int)defaults.Channels)) + , ChannelMapping(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("ChannelMapping"), defaults.Channels) + , InputChannels(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("InputChannels"), defaults.InputChannels) + , sampleFormat(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("SampleFormat"), defaults.sampleFormat) + , ExclusiveMode(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("ExclusiveMode"), defaults.ExclusiveMode) + , BoostThreadPriority(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("BoostThreadPriority"), defaults.BoostThreadPriority) + , KeepDeviceRunning(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("KeepDeviceRunning"), defaults.KeepDeviceRunning) + , UseHardwareTiming(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("UseHardwareTiming"), defaults.UseHardwareTiming) + , DitherType(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("DitherType"), static_cast(defaults.DitherType)) + , InputSourceID(conf, U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("InputSourceID"), defaults.InputSourceID) + { + if(ChannelMapping.Get().GetNumHostChannels() != ChannelsOld) + { + // If the stored channel count and the count of channels used in the channel mapping do not match, + // construct a default mapping from the channel count. + ChannelMapping = SoundDevice::ChannelMapping(ChannelsOld); + } + // store informational data (not read back, just to allow the user to mock with the raw ini file) + conf.Write(U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("Type"), deviceInfo.type); + conf.Write(U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("InternalID"), deviceInfo.internalID); + conf.Write(U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("API"), deviceInfo.apiName); + conf.Write(U_("Sound Settings"), deviceInfo.GetIdentifier() + U_("_") + U_("Name"), deviceInfo.name); + } + + StoredSoundDeviceSettings & operator = (const SoundDevice::Settings &settings) + { + LatencyUS = mpt::saturate_round(settings.Latency * 1000000.0); + UpdateIntervalUS = mpt::saturate_round(settings.UpdateInterval * 1000000.0); + Samplerate = settings.Samplerate; + ChannelsOld = mpt::saturate_cast((int)settings.Channels); + ChannelMapping = settings.Channels; + InputChannels = settings.InputChannels; + sampleFormat = settings.sampleFormat; + ExclusiveMode = settings.ExclusiveMode; + BoostThreadPriority = settings.BoostThreadPriority; + KeepDeviceRunning = settings.KeepDeviceRunning; + UseHardwareTiming = settings.UseHardwareTiming; + DitherType = static_cast(settings.DitherType); + InputSourceID = settings.InputSourceID; + return *this; + } + + operator SoundDevice::Settings () const + { + SoundDevice::Settings settings; + settings.Latency = LatencyUS / 1000000.0; + settings.UpdateInterval = UpdateIntervalUS / 1000000.0; + settings.Samplerate = Samplerate; + settings.Channels = ChannelMapping; + settings.InputChannels = InputChannels; + settings.sampleFormat = sampleFormat; + settings.ExclusiveMode = ExclusiveMode; + settings.BoostThreadPriority = BoostThreadPriority; + settings.KeepDeviceRunning = KeepDeviceRunning; + settings.UseHardwareTiming = UseHardwareTiming; + settings.DitherType = DitherType; + settings.InputSourceID = InputSourceID; + return settings; + } +}; + +SoundDevice::Settings TrackerSettings::GetSoundDeviceSettingsDefaults() const +{ + return m_SoundDeviceSettingsDefaults; +} + +SoundDevice::Identifier TrackerSettings::GetSoundDeviceIdentifier() const +{ + return m_SoundDeviceIdentifier; +} + +void TrackerSettings::SetSoundDeviceIdentifier(const SoundDevice::Identifier &identifier) +{ + m_SoundDeviceIdentifier = identifier; +} + +SoundDevice::Settings TrackerSettings::GetSoundDeviceSettings(const SoundDevice::Identifier &device) const +{ + const SoundDevice::Info deviceInfo = theApp.GetSoundDevicesManager()->FindDeviceInfo(device); + if(!deviceInfo.IsValid()) + { + return SoundDevice::Settings(); + } + const SoundDevice::Caps deviceCaps = theApp.GetSoundDevicesManager()->GetDeviceCaps(device, CMainFrame::GetMainFrame()->gpSoundDevice); + SoundDevice::Settings settings = StoredSoundDeviceSettings(conf, deviceInfo, deviceCaps.DefaultSettings); + return settings; +} + +void TrackerSettings::SetSoundDeviceSettings(const SoundDevice::Identifier &device, const SoundDevice::Settings &settings) +{ + const SoundDevice::Info deviceInfo = theApp.GetSoundDevicesManager()->FindDeviceInfo(device); + if(!deviceInfo.IsValid()) + { + return; + } + const SoundDevice::Caps deviceCaps = theApp.GetSoundDevicesManager()->GetDeviceCaps(device, CMainFrame::GetMainFrame()->gpSoundDevice); + StoredSoundDeviceSettings(conf, deviceInfo, deviceCaps.DefaultSettings) = settings; +} + + +MixerSettings TrackerSettings::GetMixerSettings() const +{ + MixerSettings settings; + settings.m_nMaxMixChannels = MixerMaxChannels; + settings.DSPMask = MixerDSPMask; + settings.MixerFlags = MixerFlags; + settings.gdwMixingFreq = MixerSamplerate; + settings.gnChannels = MixerOutputChannels; + settings.m_nPreAmp = MixerPreAmp; + settings.m_nStereoSeparation = MixerStereoSeparation; + settings.VolumeRampUpMicroseconds = MixerVolumeRampUpMicroseconds; + settings.VolumeRampDownMicroseconds = MixerVolumeRampDownMicroseconds; + settings.NumInputChannels = MixerNumInputChannels; + return settings; +} + +void TrackerSettings::SetMixerSettings(const MixerSettings &settings) +{ + MixerMaxChannels = settings.m_nMaxMixChannels; + MixerDSPMask = settings.DSPMask; + MixerFlags = settings.MixerFlags; + MixerSamplerate = settings.gdwMixingFreq; + MixerOutputChannels = settings.gnChannels; + MixerPreAmp = settings.m_nPreAmp; + MixerStereoSeparation = settings.m_nStereoSeparation; + MixerVolumeRampUpMicroseconds = settings.VolumeRampUpMicroseconds; + MixerVolumeRampDownMicroseconds = settings.VolumeRampDownMicroseconds; + MixerNumInputChannels = static_cast(settings.NumInputChannels); +} + + +CResamplerSettings TrackerSettings::GetResamplerSettings() const +{ + CResamplerSettings settings; + settings.SrcMode = ResamplerMode; + settings.gbWFIRType = ResamplerSubMode; + settings.gdWFIRCutoff = ResamplerCutoffPercent * 0.01; + settings.emulateAmiga = ResamplerEmulateAmiga; + return settings; +} + +void TrackerSettings::SetResamplerSettings(const CResamplerSettings &settings) +{ + ResamplerMode = settings.SrcMode; + ResamplerSubMode = settings.gbWFIRType; + ResamplerCutoffPercent = mpt::saturate_round(settings.gdWFIRCutoff * 100.0); + ResamplerEmulateAmiga = settings.emulateAmiga; +} + + +void TrackerSettings::GetDefaultColourScheme(std::array &colours) +{ + colours[MODCOLOR_BACKNORMAL] = RGB(0xFF, 0xFF, 0xFF); + colours[MODCOLOR_TEXTNORMAL] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_BACKCURROW] = RGB(0xC0, 0xC0, 0xC0); + colours[MODCOLOR_TEXTCURROW] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_BACKSELECTED] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_TEXTSELECTED] = RGB(0xFF, 0xFF, 0xFF); + colours[MODCOLOR_SAMPLE] = RGB(0xFF, 0x00, 0x00); + colours[MODCOLOR_BACKPLAYCURSOR] = RGB(0xFF, 0xFF, 0x80); + colours[MODCOLOR_TEXTPLAYCURSOR] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_BACKHILIGHT] = RGB(0xE0, 0xE8, 0xE0); + // Effect Colors + colours[MODCOLOR_NOTE] = RGB(0x00, 0x00, 0x80); + colours[MODCOLOR_INSTRUMENT] = RGB(0x00, 0x80, 0x80); + colours[MODCOLOR_VOLUME] = RGB(0x00, 0x80, 0x00); + colours[MODCOLOR_PANNING] = RGB(0x00, 0x80, 0x80); + colours[MODCOLOR_PITCH] = RGB(0x80, 0x80, 0x00); + colours[MODCOLOR_GLOBALS] = RGB(0x80, 0x00, 0x00); + // VU-Meters + colours[MODCOLOR_VUMETER_LO] = RGB(0x00, 0xC8, 0x00); + colours[MODCOLOR_VUMETER_MED] = RGB(0xFF, 0xC8, 0x00); + colours[MODCOLOR_VUMETER_HI] = RGB(0xE1, 0x00, 0x00); + colours[MODCOLOR_VUMETER_LO_VST] = RGB(0x18, 0x96, 0xE1); + colours[MODCOLOR_VUMETER_MED_VST] = RGB(0xFF, 0xC8, 0x00); + colours[MODCOLOR_VUMETER_HI_VST] = RGB(0xE1, 0x00, 0x00); + // Channel separators + colours[MODCOLOR_SEPSHADOW] = GetSysColor(COLOR_BTNSHADOW); + colours[MODCOLOR_SEPFACE] = GetSysColor(COLOR_BTNFACE); + colours[MODCOLOR_SEPHILITE] = GetSysColor(COLOR_BTNHIGHLIGHT); + // Pattern blend colour + colours[MODCOLOR_BLENDCOLOR] = GetSysColor(COLOR_BTNFACE); + // Dodgy commands + colours[MODCOLOR_DODGY_COMMANDS] = RGB(0xC0, 0x00, 0x00); + // Sample / instrument editor + colours[MODCOLOR_BACKSAMPLE] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_SAMPLESELECTED] = RGB(0xFF, 0xFF, 0xFF); + colours[MODCOLOR_BACKENV] = RGB(0x00, 0x00, 0x00); + colours[MODCOLOR_ENVELOPES] = RGB(0x00, 0x00, 0xFF); + colours[MODCOLOR_ENVELOPE_RELEASE] = RGB(0xFF, 0xFF, 0x00); + colours[MODCOLOR_SAMPLE_LOOPMARKER] = RGB(0x30, 0xCC, 0x30); + colours[MODCOLOR_SAMPLE_SUSTAINMARKER] = RGB(50, 0xCC, 0xCC); + colours[MODCOLOR_SAMPLE_CUEPOINT] = RGB(0xFF, 0xCC, 0x30); +} + + +#ifndef NO_EQ + +void TrackerSettings::FixupEQ(EQPreset &eqSettings) +{ + for(UINT i = 0; i < MAX_EQ_BANDS; i++) + { + if(eqSettings.Gains[i] > 32) + eqSettings.Gains[i] = 16; + if((eqSettings.Freqs[i] < 100) || (eqSettings.Freqs[i] > 10000)) + eqSettings.Freqs[i] = FlatEQPreset.Freqs[i]; + } + mpt::String::SetNullTerminator(eqSettings.szName); +} + +#endif // !NO_EQ + + +void TrackerSettings::SaveSettings() +{ + + WINDOWPLACEMENT wpl; + wpl.length = sizeof(WINDOWPLACEMENT); + CMainFrame::GetMainFrame()->GetWindowPlacement(&wpl); + conf.Write(U_("Display"), U_("WindowPlacement"), wpl); + + conf.Write(U_("Pattern Editor"), U_("NumClipboards"), mpt::saturate_cast(PatternClipboard::GetClipboardSize())); + + // Effects +#ifndef NO_DSP + conf.Write(U_("Effects"), U_("XBassDepth"), m_MegaBassSettings.m_nXBassDepth); + conf.Write(U_("Effects"), U_("XBassRange"), m_MegaBassSettings.m_nXBassRange); +#endif +#ifndef NO_REVERB + conf.Write(U_("Effects"), U_("ReverbDepth"), m_ReverbSettings.m_nReverbDepth); + conf.Write(U_("Effects"), U_("ReverbType"), m_ReverbSettings.m_nReverbType); +#endif +#ifndef NO_DSP + conf.Write(U_("Effects"), U_("ProLogicDepth"), m_SurroundSettings.m_nProLogicDepth); + conf.Write(U_("Effects"), U_("ProLogicDelay"), m_SurroundSettings.m_nProLogicDelay); +#endif +#ifndef NO_EQ + conf.Write(U_("Effects"), U_("EQ_Settings"), m_EqSettings); + conf.Write(U_("Effects"), U_("EQ_User1"), m_EqUserPresets[0]); + conf.Write(U_("Effects"), U_("EQ_User2"), m_EqUserPresets[1]); + conf.Write(U_("Effects"), U_("EQ_User3"), m_EqUserPresets[2]); + conf.Write(U_("Effects"), U_("EQ_User4"), m_EqUserPresets[3]); +#endif +#ifndef NO_DSP + conf.Write(U_("Effects"), U_("BitCrushBits"), m_BitCrushSettings.m_Bits); +#endif + + // Display (Colors) + for(int ncol = 0; ncol < MAX_MODCOLORS; ncol++) + { + conf.Write(U_("Display"), MPT_UFORMAT("Color{}")(mpt::ufmt::dec0<2>(ncol)), rgbCustomColors[ncol]); + } + + // Paths + // Obsolete, since we always write to Keybindings.mkb now. + // Older versions of OpenMPT 1.18+ will look for this file if this entry is missing, so removing this entry after having read it is kind of backwards compatible. + conf.Remove(U_("Paths"), U_("Key_Config_File")); + + // Chords + SaveChords(Chords); + + // Save default macro configuration + MIDIMacroConfig macros; + theApp.GetDefaultMidiMacro(macros); + for(int isfx = 0; isfx < kSFxMacros; isfx++) + { + conf.Write(U_("Zxx Macros"), MPT_UFORMAT("SF{}")(mpt::ufmt::HEX(isfx)), macros.SFx[isfx]); + } + for(int izxx = 0; izxx < kZxxMacros; izxx++) + { + conf.Write(U_("Zxx Macros"), MPT_UFORMAT("Z{}")(mpt::ufmt::HEX0<2>(izxx | 0x80)), macros.Zxx[izxx]); + } + + // MRU list + for(uint32 i = 0; i < (ID_MRU_LIST_LAST - ID_MRU_LIST_FIRST + 1); i++) + { + mpt::ustring key = MPT_UFORMAT("File{}")(i); + + if(i < mruFiles.size()) + { + mpt::PathString path = mruFiles[i]; + if(theApp.IsPortableMode()) + { + path = theApp.PathAbsoluteToInstallRelative(path); + } + conf.Write(U_("Recent File List"), key, path); + } else + { + conf.Remove(U_("Recent File List"), key); + } + } +} + + +bool TrackerSettings::IsComponentBlocked(const std::string &key) +{ + return Setting(conf, U_("Components"), U_("Block") + mpt::ToUnicode(mpt::Charset::ASCII, key), ComponentManagerSettingsDefault().IsBlocked(key)); +} + + +std::vector TrackerSettings::GetSampleRates() const +{ + return m_SoundSampleRates; +} + + +std::vector TrackerSettings::GetDefaultSampleRates() +{ + return std::vector{ + 192000, + 176400, + 96000, + 88200, + 48000, + 44100, + 32000, + 24000, + 22050, + 16000, + 11025, + 8000 + }; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Chords + +void TrackerSettings::LoadChords(MPTChords &chords) +{ + for(std::size_t i = 0; i < std::size(chords); i++) + { + uint32 chord; + mpt::ustring noteName = MPT_UFORMAT("{}{}")(mpt::ustring(NoteNamesSharp[i % 12]), i / 12); + if((chord = conf.Read(U_("Chords"), noteName, -1)) != uint32(-1)) + { + if((chord & 0xFFFFFFC0) || chords[i].notes[0] == MPTChord::noNote) + { + chords[i].key = (uint8)(chord & 0x3F); + int shift = 6; + for(auto ¬e : chords[i].notes) + { + // Extract 6 bits and sign-extend to 8 bits + const int signBit = ((chord >> (shift + 5)) & 1); + note = static_cast(((chord >> shift) & 0x3F) | (0xC0 * signBit)); + shift += 6; + if(note == 0) + note = MPTChord::noNote; + else if(note > 0) + note--; + } + } + } + } +} + + +void TrackerSettings::SaveChords(MPTChords &chords) +{ + for(std::size_t i = 0; i < std::size(chords); i++) + { + auto notes = chords[i].notes; + for(auto ¬e : notes) + { + if(note == MPTChord::noNote) + note = 0; + else if(note >= 0) + note++; + note &= 0x3F; + } + int32 s = (chords[i].key) | (notes[0] << 6) | (notes[1] << 12) | (notes[2] << 18); + mpt::ustring noteName = MPT_UFORMAT("{}{}")(mpt::ustring(NoteNamesSharp[i % 12]), i / 12); + conf.Write(U_("Chords"), noteName, s); + } +} + + +void TrackerSettings::SetMIDIDevice(UINT id) +{ + m_nMidiDevice = id; + MIDIINCAPS mic; + mic.szPname[0] = 0; + if(midiInGetDevCaps(id, &mic, sizeof(mic)) == MMSYSERR_NOERROR) + { + midiDeviceName = mic.szPname; + } +} + + +UINT TrackerSettings::GetCurrentMIDIDevice() +{ + if(midiDeviceName.Get().IsEmpty()) + return m_nMidiDevice; + + CString deviceName = midiDeviceName; + deviceName.TrimRight(); + + MIDIINCAPS mic; + UINT candidate = m_nMidiDevice, numDevs = midiInGetNumDevs(); + for(UINT i = 0; i < numDevs; i++) + { + mic.szPname[0] = 0; + if(midiInGetDevCaps(i, &mic, sizeof(mic)) != MMSYSERR_NOERROR) + continue; + + // Some device names have trailing spaces (e.g. "USB MIDI Interface "), but those may get lost in our settings framework. + mpt::String::SetNullTerminator(mic.szPname); + size_t strLen = _tcslen(mic.szPname); + while(strLen-- > 0) + { + if(mic.szPname[strLen] == _T(' ')) + mic.szPname[strLen] = 0; + else + break; + } + if(CString(mic.szPname) == deviceName) + { + candidate = i; + numDevs = m_nMidiDevice + 1; + // If the same device name exists twice, try to match both device number and name + if(candidate == m_nMidiDevice) + return candidate; + } + } + // If the device changed its ID, update it now. + m_nMidiDevice = candidate; + return candidate; +} + + +mpt::ustring IgnoredCCsToString(const std::bitset<128> &midiIgnoreCCs) +{ + mpt::ustring cc; + bool first = true; + for(int i = 0; i < 128; i++) + { + if(midiIgnoreCCs[i]) + { + if(!first) + { + cc += U_(","); + } + cc += mpt::ufmt::val(i); + first = false; + } + } + return cc; +} + + +std::bitset<128> StringToIgnoredCCs(const mpt::ustring &in) +{ + CString cc = mpt::ToCString(in); + std::bitset<128> midiIgnoreCCs; + midiIgnoreCCs.reset(); + int curPos = 0; + CString ccToken = cc.Tokenize(_T(", "), curPos); + while(ccToken != _T("")) + { + int ccNumber = ConvertStrTo(ccToken); + if(ccNumber >= 0 && ccNumber <= 127) + midiIgnoreCCs.set(ccNumber); + ccToken = cc.Tokenize(_T(", "), curPos); + } + return midiIgnoreCCs; +} + + +DefaultAndWorkingDirectory::DefaultAndWorkingDirectory() +{ + return; +} + +DefaultAndWorkingDirectory::DefaultAndWorkingDirectory(const mpt::PathString &def) + : m_Default(def) + , m_Working(def) +{ + return; +} + +DefaultAndWorkingDirectory::~DefaultAndWorkingDirectory() +{ + return; +} + +void DefaultAndWorkingDirectory::SetDefaultDir(const mpt::PathString &filenameFrom, bool stripFilename) +{ + if(InternalSet(m_Default, filenameFrom, stripFilename) && !m_Default.empty()) + { + // When updating default directory, also update the working directory. + InternalSet(m_Working, filenameFrom, stripFilename); + } +} + +void DefaultAndWorkingDirectory::SetWorkingDir(const mpt::PathString &filenameFrom, bool stripFilename) +{ + InternalSet(m_Working, filenameFrom, stripFilename); +} + +mpt::PathString DefaultAndWorkingDirectory::GetDefaultDir() const +{ + return m_Default; +} + +mpt::PathString DefaultAndWorkingDirectory::GetWorkingDir() const +{ + return m_Working; +} + +// Retrieve / set default directory from given string and store it our setup variables +// If stripFilename is true, the filenameFrom parameter is assumed to be a full path including a filename. +// Return true if the value changed. +bool DefaultAndWorkingDirectory::InternalSet(mpt::PathString &dest, const mpt::PathString &filenameFrom, bool stripFilename) +{ + mpt::PathString newPath = (stripFilename ? filenameFrom.GetPath() : filenameFrom); + newPath.EnsureTrailingSlash(); + mpt::PathString oldPath = dest; + dest = newPath; + return newPath != oldPath; +} + +ConfigurableDirectory::ConfigurableDirectory(SettingsContainer &conf, const AnyStringLocale §ion, const AnyStringLocale &key, const mpt::PathString &def) + : conf(conf) + , m_Setting(conf, section, key, def) +{ + SetDefaultDir(theApp.PathInstallRelativeToAbsolute(m_Setting), false); +} + +ConfigurableDirectory::~ConfigurableDirectory() +{ + m_Setting = theApp.IsPortableMode() ? theApp.PathAbsoluteToInstallRelative(m_Default) : m_Default; +} + + + +OPENMPT_NAMESPACE_END -- cgit