diff --git a/GenshinLyreMidiPlayer.Data/Entities/History.cs b/GenshinLyreMidiPlayer.Data/Entities/History.cs index 9b70b76..e154010 100644 --- a/GenshinLyreMidiPlayer.Data/Entities/History.cs +++ b/GenshinLyreMidiPlayer.Data/Entities/History.cs @@ -4,11 +4,19 @@ namespace GenshinLyreMidiPlayer.Data.Entities; public class History { - public History() { } + protected History() { } - public History(string path) { Path = path; } + public History(string path, int key) + { + Key = key; + Path = path; + } public Guid Id { get; set; } + public int Key { get; set; } + public string Path { get; set; } = null!; + + public Transpose? Transpose { get; set; } } \ No newline at end of file diff --git a/GenshinLyreMidiPlayer.Data/Entities/Transpose.cs b/GenshinLyreMidiPlayer.Data/Entities/Transpose.cs new file mode 100644 index 0000000..4aab082 --- /dev/null +++ b/GenshinLyreMidiPlayer.Data/Entities/Transpose.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; + +namespace GenshinLyreMidiPlayer.Data.Entities; + +public enum Transpose +{ + [Description("Ignore missing notes")] Ignore, + [Description("Transpose up")] Up, + [Description("Transpose down")] Down +} \ No newline at end of file diff --git a/GenshinLyreMidiPlayer.Data/Midi/MidiFile.cs b/GenshinLyreMidiPlayer.Data/Midi/MidiFile.cs index a594e0c..907c9b8 100644 --- a/GenshinLyreMidiPlayer.Data/Midi/MidiFile.cs +++ b/GenshinLyreMidiPlayer.Data/Midi/MidiFile.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using GenshinLyreMidiPlayer.Data.Entities; using Melanchall.DryWetMidi.Core; using Melanchall.DryWetMidi.Interaction; using Melanchall.DryWetMidi.Tools; @@ -13,14 +14,16 @@ public class MidiFile : Screen private readonly ReadingSettings? _settings; private int _position; - public MidiFile(string path, ReadingSettings? settings = null) + public MidiFile(History history, ReadingSettings? settings = null) { _settings = settings; - Path = path; + History = history; InitializeMidi(); } + public History History { get; } + public int Position { get => _position + 1; @@ -29,7 +32,7 @@ public int Position public Melanchall.DryWetMidi.Core.MidiFile Midi { get; private set; } = null!; - public string Path { get; } + public string Path => History.Path; public string Title => GetFileNameWithoutExtension(Path); diff --git a/GenshinLyreMidiPlayer.Data/Properties/Settings.Designer.cs b/GenshinLyreMidiPlayer.Data/Properties/Settings.Designer.cs index ccd1866..c58545b 100644 --- a/GenshinLyreMidiPlayer.Data/Properties/Settings.Designer.cs +++ b/GenshinLyreMidiPlayer.Data/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace GenshinLyreMidiPlayer.Data.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -46,8 +46,8 @@ public string LicenseUri { [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)] - [global::System.Configuration.DefaultSettingValueAttribute("https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/THIRD-PARTY-NOTICES." + - "md")] + [global::System.Configuration.DefaultSettingValueAttribute("\r\n https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/TH" + + "IRD-PARTY-NOTICES.md\r\n ")] public string ThirdPartyLicenseUri { get { return ((string)(this["ThirdPartyLicenseUri"])); @@ -126,18 +126,6 @@ public bool UseSpeakers { } } - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int KeyOffset { - get { - return ((int)(this["KeyOffset"])); - } - set { - this["KeyOffset"] = value; - } - } - [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("-1")] diff --git a/GenshinLyreMidiPlayer.Data/Properties/Settings.settings b/GenshinLyreMidiPlayer.Data/Properties/Settings.settings index 16f9919..69069c1 100644 --- a/GenshinLyreMidiPlayer.Data/Properties/Settings.settings +++ b/GenshinLyreMidiPlayer.Data/Properties/Settings.settings @@ -39,9 +39,6 @@ False - - 0 - -1 diff --git a/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs b/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs index f451773..ac0282e 100644 --- a/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs +++ b/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using GenshinLyreMidiPlayer.Data.Entities; using WindowsInput; using WindowsInput.Native; using static GenshinLyreMidiPlayer.WPF.Core.Keyboard; @@ -9,13 +10,6 @@ namespace GenshinLyreMidiPlayer.WPF.Core; public static class LyrePlayer { - public enum Transpose - { - Ignore, - Up, - Down - } - private static readonly IInputSimulator Input = new InputSimulator(); private static readonly List LyreNotes = new() diff --git a/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj b/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj index 2f236ea..8b7510f 100644 --- a/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj +++ b/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -6,7 +6,7 @@ true GenshinLyreMidiPlayer.WPF.App app.manifest - 3.0.0 + 3.1.0 item_windsong_lyre.ico enable https://github.com/sabihoshi/GenshinLyreMidiPlayer diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs index 1b653ce..b8727bc 100644 --- a/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs +++ b/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs @@ -4,6 +4,7 @@ using System.Windows; using Windows.Media; using Windows.Media.Playback; +using GenshinLyreMidiPlayer.Data.Entities; using GenshinLyreMidiPlayer.Data.Midi; using GenshinLyreMidiPlayer.Data.Notification; using GenshinLyreMidiPlayer.Data.Properties; @@ -16,7 +17,7 @@ using ModernWpf.Controls; using Stylet; using StyletIoC; -using static GenshinLyreMidiPlayer.WPF.Core.LyrePlayer.Transpose; +using static GenshinLyreMidiPlayer.WPF.ViewModels.SettingsPageViewModel; using MidiFile = GenshinLyreMidiPlayer.Data.Midi.MidiFile; namespace GenshinLyreMidiPlayer.WPF.ViewModels; @@ -29,25 +30,22 @@ public class LyrePlayerViewModel : Screen, { private static readonly Settings Settings = Settings.Default; private readonly IEventAggregator _events; + private readonly MainWindowViewModel _main; private readonly MediaPlayer? _player; private readonly OutputDevice? _speakers; private readonly PlaybackCurrentTimeWatcher _timeWatcher; - private readonly SettingsPageViewModel _settings; private bool _ignoreSliderChange; private InputDevice? _inputDevice; private TimeSpan _songPosition; - public LyrePlayerViewModel(IContainer ioc, - SettingsPageViewModel settings, PlaylistViewModel playlist) + public LyrePlayerViewModel(IContainer ioc, MainWindowViewModel main) { + _main = main; _timeWatcher = PlaybackCurrentTimeWatcher.Instance; _events = ioc.Get(); _events.Subscribe(this); - _settings = settings; - Playlist = playlist; - SelectedMidiInput = MidiInputs[0]; _timeWatcher.CurrentTimeChanged += OnSongTick; @@ -74,8 +72,8 @@ public LyrePlayerViewModel(IContainer ioc, { new ErrorContentDialog(e, closeText: "Ignore").ShowAsync(); - _settings.CanUseSpeakers = false; - Settings.UseSpeakers = false; + SettingsView.CanUseSpeakers = false; + Settings.UseSpeakers = false; } } @@ -124,7 +122,7 @@ public double SongPosition public Playback? Playback { get; private set; } - public PlaylistViewModel Playlist { get; } + public PlaylistViewModel Playlist => _main.PlaylistView; public string PlayPauseIcon => Playback?.IsRunning ?? false ? PauseIcon : PlayIcon; @@ -135,6 +133,8 @@ public double SongPosition private MusicDisplayProperties? Display => _player?.SystemMediaTransportControls.DisplayUpdater.MusicProperties; + private SettingsPageViewModel SettingsView => _main.SettingsView; + private static string PauseIcon => "\xEDB4"; private static string PlayIcon => "\xF5B0"; @@ -355,10 +355,10 @@ public void UpdateButtons() private int ApplyNoteSettings(int noteId) { - noteId -= Settings.KeyOffset; + noteId -= Playlist.OpenedFile?.History.Key ?? 0; if (Settings.TransposeNotes) - noteId = LyrePlayer.TransposeNote(noteId, _settings.Transpose ?? Ignore); + noteId = LyrePlayer.TransposeNote(noteId, SettingsView.Transpose.Key); return noteId; } @@ -388,13 +388,13 @@ private async Task InitializePlayback() // Check for notes that cannot be played even after transposing. var outOfRange = midi.GetNotes().Where(note => - !_settings.SelectedLayout.Key.TryGetKey(ApplyNoteSettings(note.NoteNumber), out _)); + !SettingsView.SelectedLayout.Key.TryGetKey(ApplyNoteSettings(note.NoteNumber), out _)); - if (_settings.Transpose is null && outOfRange.Any()) + if (Playlist.OpenedFile.History.Transpose is null && outOfRange.Any()) { await Application.Current.Dispatcher.Invoke(async () => { - var options = new Enum[] { Up, Down }; + var options = new Enum[] { Transpose.Up, Transpose.Down }; var exceptionDialog = new ErrorContentDialog( new IndexOutOfRangeException( "Some notes cannot be played by the Lyre because it is missing Sharps & Flats. " + @@ -403,17 +403,17 @@ await Application.Current.Dispatcher.Invoke(async () => var result = await exceptionDialog.ShowAsync(); - _settings.Transpose = result switch + SettingsView.Transpose = result switch { - ContentDialogResult.None => Ignore, - ContentDialogResult.Primary => Up, - ContentDialogResult.Secondary => Down + ContentDialogResult.None => TransposeNames.ElementAt(0), + ContentDialogResult.Primary => TransposeNames.ElementAt(1), + ContentDialogResult.Secondary => TransposeNames.ElementAt(2) }; }); } Playback = midi.GetPlayback(); - Playback.Speed = _settings.SelectedSpeed.Speed; + Playback.Speed = SettingsView.SelectedSpeed.Speed; Playback.InterruptNotesOnStop = true; @@ -486,7 +486,7 @@ private void PlayNote(NoteEvent noteEvent) return; } - var layout = _settings.SelectedLayout.Key; + var layout = SettingsView.SelectedLayout.Key; var note = ApplyNoteSettings(noteEvent.NoteNumber); switch (noteEvent.EventType) diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs index f20390f..67fd62c 100644 --- a/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs +++ b/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs @@ -12,16 +12,16 @@ public class MainWindowViewModel : Conductor.StackNavigation { private readonly IContainer _ioc; private readonly Stack _history = new(); - private NavigationView _navView; + private NavigationView _navView = null!; public MainWindowViewModel(IContainer ioc, IEventAggregator events) { _ioc = ioc; - SettingsView = ioc.Get(); - PlaylistView = ioc.Get(); - PlayerView = new(ioc, SettingsView, PlaylistView); - PianoSheetView = new(SettingsView, PlaylistView); + PlaylistView = new(ioc, this); + SettingsView = new(ioc, this); + PlayerView = new(ioc, this); + PianoSheetView = new(this); } public bool ShowUpdate => SettingsView.NeedsUpdate && ActiveItem != SettingsView; @@ -58,7 +58,7 @@ protected override async void OnViewLoaded() } await using var db = _ioc.Get(); - await PlaylistView.AddFiles(db.History.Select(midi => midi.Path)); + await PlaylistView.AddFiles(db.History); } private void AutoSuggestBoxOnTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e) diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs index 05b6a12..7d18a07 100644 --- a/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs +++ b/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs @@ -6,22 +6,17 @@ using Melanchall.DryWetMidi.Interaction; using PropertyChanged; using Stylet; -using static GenshinLyreMidiPlayer.WPF.Core.LyrePlayer.Transpose; namespace GenshinLyreMidiPlayer.WPF.ViewModels; public class PianoSheetViewModel : Screen { + private readonly MainWindowViewModel _main; private uint _bars = 1; private uint _beats; private uint _shorten = 1; - public PianoSheetViewModel(SettingsPageViewModel settingsPage, - PlaylistViewModel playlistView) - { - SettingsPage = settingsPage; - PlaylistView = playlistView; - } + public PianoSheetViewModel(MainWindowViewModel main) { _main = main; } [OnChangedMethod(nameof(Update))] public char Delimiter { get; set; } = '.'; @@ -32,9 +27,9 @@ public PianoSheetViewModel(SettingsPageViewModel settingsPage, set => SettingsPage.SelectedLayout = value; } - public PlaylistViewModel PlaylistView { get; } + public PlaylistViewModel PlaylistView => _main.PlaylistView; - public SettingsPageViewModel SettingsPage { get; } + public SettingsPageViewModel SettingsPage => _main.SettingsView; public string Result { get; private set; } = string.Empty; @@ -84,7 +79,7 @@ public void Update() foreach (var note in notes) { var offset = note.NoteNumber - SettingsPage.KeyOffset; - var transpose = SettingsPage.Transpose ?? Ignore; + var transpose = SettingsPage.Transpose.Key; var id = LyrePlayer.TransposeNote(offset, transpose); if (!layout.TryGetKey(id, out var key)) continue; diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs index 7187085..1113799 100644 --- a/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs +++ b/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs @@ -27,13 +27,14 @@ public enum LoopMode } private readonly IContainer _ioc; - private readonly IEventAggregator _events; + private readonly MainWindowViewModel _main; - public PlaylistViewModel(IContainer ioc, IEventAggregator events) + public PlaylistViewModel(IContainer ioc, MainWindowViewModel main) { _ioc = ioc; - _events = events; + _events = ioc.Get(); + _main = main; } public BindableCollection FilteredTracks => string.IsNullOrWhiteSpace(FilterText) @@ -44,7 +45,7 @@ public PlaylistViewModel(IContainer ioc, IEventAggregator events) public bool Shuffle { get; set; } - public LoopMode Loop { get; set; } + public LoopMode Loop { get; set; } = LoopMode.All; public MidiFile? OpenedFile { get; set; } @@ -56,8 +57,6 @@ public PlaylistViewModel(IContainer ioc, IEventAggregator events) public Stack History { get; } = new(); - public string FilterText { get; set; } - public string LoopStateString => Loop switch { @@ -67,6 +66,8 @@ public PlaylistViewModel(IContainer ioc, IEventAggregator events) LoopMode.All => "\xE8EE" }; + public string? FilterText { get; set; } + private BindableCollection ShuffledTracks { get; set; } = new(); public BindableCollection GetPlaylist() => Shuffle ? ShuffledTracks : Tracks; @@ -100,17 +101,31 @@ public async Task AddFiles(IEnumerable files) ShuffledTracks = new(Tracks.OrderBy(_ => Guid.NewGuid())); RefreshPlaylist(); - await UpdateHistory(); var next = Next(); if (OpenedFile is null && Tracks.Count > 0 && next is not null) _events.Publish(Next()); } + public async Task AddFiles(IEnumerable files) + { + foreach (var file in files) + { + await AddFile(file); + } + + RefreshPlaylist(); + } + public async Task ClearPlaylist() { + await using var db = _ioc.Get(); + db.History.RemoveRange(db.History); + await db.SaveChangesAsync(); + Tracks.Clear(); - await UpdateHistory(); + OpenedFile = null; + SelectedFile = null; } public async Task OpenFile() @@ -127,11 +142,25 @@ public async Task OpenFile() await AddFiles(openFileDialog.FileNames); } + public async Task RemoveTrack() + { + if (SelectedFile is not null) + { + await using var db = _ioc.Get(); + db.History.Remove(SelectedFile.History); + await db.SaveChangesAsync(); + + OpenedFile = OpenedFile == SelectedFile ? null : OpenedFile; + Tracks.Remove(SelectedFile); + + RefreshPlaylist(); + } + } + public async Task UpdateHistory() { await using var db = _ioc.Get(); - db.History.RemoveRange(db.History); - db.History.AddRange(Tracks.Select(t => new History(t.Path))); + db.UpdateRange(Tracks.Select(t => t.History)); await db.SaveChangesAsync(); } @@ -144,19 +173,21 @@ public void OnFileChanged(object sender, EventArgs e) public void OnFilterTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e) => FilterText = sender.Text; - public void Previous() + public void OnOpenedFileChanged() { - History.Pop(); - _events.Publish(History.Pop()); + if (OpenedFile is null) return; + + var transpose = SettingsPageViewModel.TransposeNames + .FirstOrDefault(e => e.Key == OpenedFile.History.Transpose); + + _main.SettingsView.Transpose = transpose; + _main.SettingsView.KeyOffset = OpenedFile.History.Key; } - public void RemoveTrack() + public void Previous() { - if (SelectedFile is not null) - { - Tracks.Remove(SelectedFile); - RefreshPlaylist(); - } + History.Pop(); + _events.Publish(History.Pop()); } public void ToggleLoop() @@ -178,21 +209,31 @@ public void ToggleShuffle() RefreshPlaylist(); } - private async Task AddFile(string fileName, ReadingSettings? settings = null) + private async Task AddFile(History history, ReadingSettings? settings = null) { try { - var file = new MidiFile(fileName, settings); - Tracks.Add(file); + Tracks.Add(new(history, settings)); } catch (Exception e) { settings ??= new(); if (await ExceptionHandler.TryHandleException(e, settings)) - await AddFile(fileName, settings); + await AddFile(history, settings); } } + private async Task AddFile(string fileName, ReadingSettings? settings = null) + { + var history = new History(fileName, _main.SettingsView.KeyOffset); + + await AddFile(history); + + await using var db = _ioc.Get(); + db.History.Add(history); + await db.SaveChangesAsync(); + } + private void RefreshPlaylist() { var playlist = GetPlaylist(); diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs index 70e19c1..4f4aa95 100644 --- a/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs +++ b/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs @@ -8,6 +8,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using GenshinLyreMidiPlayer.Data; +using GenshinLyreMidiPlayer.Data.Entities; using GenshinLyreMidiPlayer.Data.Git; using GenshinLyreMidiPlayer.Data.Midi; using GenshinLyreMidiPlayer.Data.Notification; @@ -21,18 +23,33 @@ using ModernWpf.Controls; using Stylet; using StyletIoC; +using static GenshinLyreMidiPlayer.Data.Entities.Transpose; namespace GenshinLyreMidiPlayer.WPF.ViewModels; public class SettingsPageViewModel : Screen { private static readonly Settings Settings = Settings.Default; + + public static readonly Dictionary TransposeNames = new() + { + [Ignore] = "Ignore notes", + [Up] = "Shift one semitone up", + [Down] = "Shift one semitone down" + }; + + private readonly IContainer _ioc; private readonly IEventAggregator _events; - private int _keyOffset = Settings.KeyOffset; + private readonly MainWindowViewModel _main; + private int _keyOffset; - public SettingsPageViewModel(IContainer ioc) + public SettingsPageViewModel(IContainer ioc, MainWindowViewModel main) { + _ioc = ioc; _events = ioc.Get(); + _main = main; + + _keyOffset = Playlist.OpenedFile?.History.Key ?? 0; ThemeManager.Current.ApplicationTheme = Settings.AppTheme switch { @@ -135,6 +152,8 @@ public int KeyOffset public KeyValuePair SelectedLayout { get; set; } + public KeyValuePair Transpose { get; set; } = TransposeNames.First(); + public static List MidiSpeeds { get; } = new() { new("0.25x", 0.25), @@ -161,12 +180,12 @@ public string GenshinLocation public string UpdateString { get; set; } = string.Empty; - public LyrePlayer.Transpose? Transpose { get; set; } - public uint MergeMilliseconds { get; set; } = Settings.MergeMilliseconds; public static Version ProgramVersion => Assembly.GetExecutingAssembly().GetName().Version!; + private PlaylistViewModel Playlist => _main.PlaylistView; + public bool TryGetLocation() { var locations = new[] @@ -327,11 +346,24 @@ private void OnAutoCheckUpdatesChanged() { if (AutoCheckUpdates) _ = CheckForUpdate(); + + Settings.Modify(s => s.AutoCheckUpdates = AutoCheckUpdates); } private void OnIncludeBetaUpdatesChanged() => _ = CheckForUpdate(); - private void OnKeyOffsetChanged() => Settings.Modify(s => s.KeyOffset = KeyOffset); + private async void OnKeyOffsetChanged() + { + if (Playlist.OpenedFile is null) + return; + + await using var db = _ioc.Get(); + + Playlist.OpenedFile.History.Key = KeyOffset; + db.Update(Playlist.OpenedFile.History); + + await db.SaveChangesAsync(); + } private void OnMergeMillisecondsChanged() { @@ -352,4 +384,17 @@ private void OnSelectedLayoutIndexChanged() } private void OnSelectedSpeedChanged() => _events.Publish(this); + + private async void OnTransposeChanged() + { + if (Playlist.OpenedFile is null) + return; + + await using var db = _ioc.Get(); + + Playlist.OpenedFile.History.Transpose = Transpose.Key; + db.Update(Playlist.OpenedFile.History); + + await db.SaveChangesAsync(); + } } \ No newline at end of file diff --git a/GenshinLyreMidiPlayer.WPF/Views/SettingsPageView.xaml b/GenshinLyreMidiPlayer.WPF/Views/SettingsPageView.xaml index 2c9f450..0451199 100644 --- a/GenshinLyreMidiPlayer.WPF/Views/SettingsPageView.xaml +++ b/GenshinLyreMidiPlayer.WPF/Views/SettingsPageView.xaml @@ -69,6 +69,15 @@ + + + + + +