diff --git a/GenshinLyreMidiPlayer.Data/GenshinLyreMidiPlayer.Data.csproj b/GenshinLyreMidiPlayer.Data/GenshinLyreMidiPlayer.Data.csproj
index 8c1c738..0c34d7d 100644
--- a/GenshinLyreMidiPlayer.Data/GenshinLyreMidiPlayer.Data.csproj
+++ b/GenshinLyreMidiPlayer.Data/GenshinLyreMidiPlayer.Data.csproj
@@ -1,32 +1,32 @@
-
- net5.0-windows10.0.19041.0
- latest
- enable
-
+
+ net5.0-windows10.0.19041.0
+ latest
+ enable
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
- True
- Settings.settings
- True
-
-
+
+
+ True
+ Settings.settings
+ True
+
+
-
-
- Settings.Designer.cs
- PublicSettingsSingleFileGenerator
-
-
+
+
+ Settings.Designer.cs
+ PublicSettingsSingleFileGenerator
+
+
diff --git a/GenshinLyreMidiPlayer.Data/Notification/PlayTimerNotification.cs b/GenshinLyreMidiPlayer.Data/Notification/PlayTimerNotification.cs
index 865d387..c68113b 100644
--- a/GenshinLyreMidiPlayer.Data/Notification/PlayTimerNotification.cs
+++ b/GenshinLyreMidiPlayer.Data/Notification/PlayTimerNotification.cs
@@ -1,6 +1,4 @@
namespace GenshinLyreMidiPlayer.Data.Notification
{
- public class PlayTimerNotification
- {
- }
+ public class PlayTimerNotification { }
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.Data/Properties/Settings.cs b/GenshinLyreMidiPlayer.Data/Properties/Settings.cs
index 18365a3..7e62c5f 100644
--- a/GenshinLyreMidiPlayer.Data/Properties/Settings.cs
+++ b/GenshinLyreMidiPlayer.Data/Properties/Settings.cs
@@ -10,6 +10,8 @@ namespace GenshinLyreMidiPlayer.Data.Properties
// The SettingsSaving event is raised before the setting values are saved.
public sealed partial class Settings
{
+ protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e) => Save();
+
protected override void OnSettingsLoaded(object sender, SettingsLoadedEventArgs e)
{
if (Default.UpgradeRequired)
@@ -19,7 +21,5 @@ protected override void OnSettingsLoaded(object sender, SettingsLoadedEventArgs
Default.Save();
}
}
-
- protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e) => Save();
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.Data/Properties/Settings.settings b/GenshinLyreMidiPlayer.Data/Properties/Settings.settings
index 873fcfb..16f9919 100644
--- a/GenshinLyreMidiPlayer.Data/Properties/Settings.settings
+++ b/GenshinLyreMidiPlayer.Data/Properties/Settings.settings
@@ -1,62 +1,67 @@
-
-
-
-
- <?xml version="1.0" encoding="utf-16"?>
-<SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <ConnectionString>history.db</ConnectionString>
- <ProviderName />
-</SerializableConnectionString>
- history.db
-
-
- https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/LICENSE.md
-
-
- https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/THIRD-PARTY-NOTICES.md
-
-
- True
-
-
- True
-
-
- False
-
-
- True
-
-
- False
-
-
- False
-
-
- 0
-
-
- -1
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- True
-
-
- GenshinImpact.exe
-
-
+
+
+
+
+ <?xml version="1.0" encoding="utf-16"?>
+ <SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <ConnectionString>history.db</ConnectionString>
+ <ProviderName />
+ </SerializableConnectionString>
+
+ history.db
+
+
+ https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/LICENSE.md
+
+
+
+ https://github.com/sabihoshi/GenshinLyreMidiPlayer/blob/main/THIRD-PARTY-NOTICES.md
+
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ 0
+
+
+ -1
+
+
+ 0
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ True
+
+
+ GenshinImpact.exe
+
+
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs b/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs
index 222053e..6bbaf1b 100644
--- a/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs
+++ b/GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs
@@ -45,6 +45,20 @@ public enum Transpose
83 // B5
};
+ public static bool TryGetKey(this Layout layout, int noteId, out VirtualKeyCode key)
+ {
+ var keys = GetLayout(layout);
+ return TryGetKey(keys, noteId, out key);
+ }
+
+ public static bool TryGetKey(this IEnumerable keys, int noteId, out VirtualKeyCode key)
+ {
+ var keyIndex = LyreNotes.IndexOf(noteId);
+ key = keys.ElementAtOrDefault(keyIndex);
+
+ return keyIndex != -1;
+ }
+
public static int TransposeNote(int noteId,
Transpose direction = Transpose.Ignore)
{
@@ -69,9 +83,11 @@ public static int TransposeNote(int noteId,
}
}
- public static void PlayNote(int noteId, Layout selectedLayout)
+ public static void InteractNote(int noteId, Layout selectedLayout,
+ Func action)
{
- InteractNote(noteId, selectedLayout, Input.Keyboard.KeyPress);
+ if (selectedLayout.TryGetKey(noteId, out var key))
+ action.Invoke(key);
}
public static void NoteDown(int noteId, Layout selectedLayout)
@@ -84,25 +100,9 @@ public static void NoteUp(int noteId, Layout selectedLayout)
InteractNote(noteId, selectedLayout, Input.Keyboard.KeyUp);
}
- public static void InteractNote(int noteId, Layout selectedLayout,
- Func action)
- {
- if (selectedLayout.TryGetKey(noteId, out var key))
- action.Invoke(key);
- }
-
- public static bool TryGetKey(this Layout layout, int noteId, out VirtualKeyCode key)
- {
- var keys = GetLayout(layout);
- return TryGetKey(keys, noteId, out key);
- }
-
- public static bool TryGetKey(this IEnumerable keys, int noteId, out VirtualKeyCode key)
+ public static void PlayNote(int noteId, Layout selectedLayout)
{
- var keyIndex = LyreNotes.IndexOf(noteId);
- key = keys.ElementAtOrDefault(keyIndex);
-
- return keyIndex != -1;
+ InteractNote(noteId, selectedLayout, Input.Keyboard.KeyPress);
}
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/Core/WindowHelper.cs b/GenshinLyreMidiPlayer.WPF/Core/WindowHelper.cs
index 68c9b63..0ba4a4c 100644
--- a/GenshinLyreMidiPlayer.WPF/Core/WindowHelper.cs
+++ b/GenshinLyreMidiPlayer.WPF/Core/WindowHelper.cs
@@ -17,18 +17,6 @@ public static class WindowHelper
private static string GenshinProcessName
=> Path.GetFileNameWithoutExtension(Settings.Default.GenshinLocation)!;
- private static IntPtr? FindWindowByProcessName(string processName)
- {
- var process = Process.GetProcessesByName(processName);
- return process.FirstOrDefault(p => p.MainWindowHandle != IntPtr.Zero)?.MainWindowHandle;
- }
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
- private static extern IntPtr GetForegroundWindow();
-
- [DllImport("user32.dll")]
- private static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
-
public static bool EnsureGameOnTop()
{
var genshinWindow = FindWindowByProcessName(GenshinProcessName);
@@ -41,17 +29,29 @@ public static bool EnsureGameOnTop()
return GetForegroundWindow().Equals(genshinWindow);
}
+ public static bool IsGameFocused()
+ {
+ var genshinWindow = FindWindowByProcessName(GenshinProcessName);
+ return genshinWindow != null &&
+ IsWindowFocused((IntPtr) genshinWindow);
+ }
+
private static bool IsWindowFocused(IntPtr windowPtr)
{
var hWnd = GetForegroundWindow();
return hWnd.Equals(windowPtr);
}
- public static bool IsGameFocused()
+ [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
+ private static extern IntPtr GetForegroundWindow();
+
+ private static IntPtr? FindWindowByProcessName(string processName)
{
- var genshinWindow = FindWindowByProcessName(GenshinProcessName);
- return genshinWindow != null &&
- IsWindowFocused((IntPtr) genshinWindow);
+ var process = Process.GetProcessesByName(processName);
+ return process.FirstOrDefault(p => p.MainWindowHandle != IntPtr.Zero)?.MainWindowHandle;
}
+
+ [DllImport("user32.dll")]
+ private static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/FodyWeavers.xml b/GenshinLyreMidiPlayer.WPF/FodyWeavers.xml
index d5abfed..22d54c1 100644
--- a/GenshinLyreMidiPlayer.WPF/FodyWeavers.xml
+++ b/GenshinLyreMidiPlayer.WPF/FodyWeavers.xml
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj b/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj
index 82849e0..2d08abe 100644
--- a/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj
+++ b/GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj
@@ -1,49 +1,49 @@
-
- WinExe
- net5.0-windows10.0.19041.0
- true
- GenshinLyreMidiPlayer.WPF.App
- app.manifest
- 2.3.3
- item_windsong_lyre.ico
- enable
- https://github.com/sabihoshi/GenshinLyreMidiPlayer
- sabihoshi
- Genshin Lyre MIDI Player
-
- sabihoshi
- A music player that plays MIDI files into Genshin Impact's Windsong Lyre.
- latest
- LICENSE.md
- true
-
+
+ WinExe
+ net5.0-windows10.0.19041.0
+ true
+ GenshinLyreMidiPlayer.WPF.App
+ app.manifest
+ 2.3.3
+ item_windsong_lyre.ico
+ enable
+ https://github.com/sabihoshi/GenshinLyreMidiPlayer
+ sabihoshi
+ Genshin Lyre MIDI Player
+
+ sabihoshi
+ A music player that plays MIDI files into Genshin Impact's Windsong Lyre.
+ latest
+ LICENSE.md
+ true
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
- True
-
-
-
+
+
+ True
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
diff --git a/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Animation.cs b/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Animation.cs
index 707fd64..dfd67b1 100644
--- a/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Animation.cs
+++ b/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Animation.cs
@@ -42,13 +42,6 @@ public void Stop()
_element.InvalidateProperty(UIElement.RenderTransformOriginProperty);
}
- private void OnCurrentStateInvalidated(object? sender, EventArgs e)
- {
- if (sender is Clock clock) _currentState = clock.CurrentState;
- }
-
- private void OnCompleted(object? sender, EventArgs e) { Completed?.Invoke(this, EventArgs.Empty); }
-
private BitmapCache GetBitmapCache()
{
#if NETCOREAPP || NET462
@@ -57,5 +50,12 @@ private BitmapCache GetBitmapCache()
return _defaultBitmapCache;
#endif
}
+
+ private void OnCompleted(object? sender, EventArgs e) { Completed?.Invoke(this, EventArgs.Empty); }
+
+ private void OnCurrentStateInvalidated(object? sender, EventArgs e)
+ {
+ if (sender is Clock clock) _currentState = clock.CurrentState;
+ }
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Transition.cs b/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Transition.cs
index 8de268b..516c678 100644
--- a/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Transition.cs
+++ b/GenshinLyreMidiPlayer.WPF/ModernWPF/Animation/Transition.cs
@@ -39,13 +39,6 @@ static Transition()
DecelerateKeySpline.Freeze();
}
- //protected virtual string GetNavigationStateCore();
- //protected virtual void SetNavigationStateCore(string navigationState);
-
- protected abstract Animation? GetEnterAnimation(FrameworkElement element, bool movingBackwards);
-
- protected abstract Animation? GetExitAnimation(FrameworkElement element, bool movingBackwards);
-
public Animation? GetEnterAnimation(object element, bool movingBackwards) =>
GetEnterAnimation(
(FrameworkElement) element, movingBackwards);
@@ -53,5 +46,12 @@ static Transition()
public Animation? GetExitAnimation(object element, bool movingBackwards) =>
GetExitAnimation(
(FrameworkElement) element, movingBackwards);
+
+ //protected virtual string GetNavigationStateCore();
+ //protected virtual void SetNavigationStateCore(string navigationState);
+
+ protected abstract Animation? GetEnterAnimation(FrameworkElement element, bool movingBackwards);
+
+ protected abstract Animation? GetExitAnimation(FrameworkElement element, bool movingBackwards);
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ModernWPF/CaptionedObject.cs b/GenshinLyreMidiPlayer.WPF/ModernWPF/CaptionedObject.cs
index 691c8c0..787a2ca 100644
--- a/GenshinLyreMidiPlayer.WPF/ModernWPF/CaptionedObject.cs
+++ b/GenshinLyreMidiPlayer.WPF/ModernWPF/CaptionedObject.cs
@@ -11,10 +11,10 @@ public CaptionedObject(T o, string? caption = null)
Caption = caption;
}
- protected string? Caption { get; }
-
public T Object { get; }
+ protected string? Caption { get; }
+
public override string ToString() => Caption ?? base.ToString() ?? string.Empty;
}
diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs
index 30319b9..eb1c517 100644
--- a/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs
+++ b/GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs
@@ -30,9 +30,9 @@ public class LyrePlayerViewModel : Screen,
private static readonly Settings Settings = Settings.Default;
private readonly IEventAggregator _events;
private readonly MediaPlayer? _player;
- private readonly SettingsPageViewModel _settings;
private readonly OutputDevice? _speakers;
private readonly PlaybackCurrentTimeWatcher _timeWatcher;
+ private readonly SettingsPageViewModel _settings;
private bool _ignoreSliderChange;
private InputDevice? _inputDevice;
private TimeSpan _songPosition;
@@ -122,26 +122,26 @@ public double SongPosition
public MidiInput? SelectedMidiInput { get; set; }
- private MusicDisplayProperties? Display =>
- _player?.SystemMediaTransportControls.DisplayUpdater.MusicProperties;
-
public Playback? Playback { get; private set; }
public PlaylistViewModel Playlist { get; }
+ public string PlayPauseIcon => Playback?.IsRunning ?? false ? PauseIcon : PlayIcon;
+
+ public TimeSpan CurrentTime => _songPosition;
+
+ public TimeSpan MaximumTime => Playlist.OpenedFile?.Duration ?? TimeSpan.Zero;
+
+ private MusicDisplayProperties? Display =>
+ _player?.SystemMediaTransportControls.DisplayUpdater.MusicProperties;
+
private static string PauseIcon => "\xEDB4";
private static string PlayIcon => "\xF5B0";
- public string PlayPauseIcon => Playback?.IsRunning ?? false ? PauseIcon : PlayIcon;
-
private SystemMediaTransportControls? Controls =>
_player?.SystemMediaTransportControls;
- public TimeSpan CurrentTime => _songPosition;
-
- public TimeSpan MaximumTime => Playlist.OpenedFile?.Duration ?? TimeSpan.Zero;
-
public async void Handle(MergeNotesNotification message)
{
if (!message.Merge)
@@ -172,6 +172,93 @@ public async void Handle(PlayTimerNotification message)
public async void Handle(SettingsPageViewModel message) { await InitializePlayback(); }
+ public async Task OpenFile()
+ {
+ await Playlist.OpenFile();
+ UpdateButtons();
+ }
+
+ public async Task PlayPause()
+ {
+ if (Playback is null)
+ await InitializePlayback();
+
+ if (Playback!.IsRunning)
+ Playback.Stop();
+ else
+ {
+ var time = new MetricTimeSpan(CurrentTime);
+ Playback.PlaybackStart = time;
+ Playback.MoveToTime(time);
+
+ if (Settings.UseSpeakers)
+ Playback.Start();
+ else
+ {
+ WindowHelper.EnsureGameOnTop();
+ await Task.Delay(100);
+
+ if (WindowHelper.IsGameFocused())
+ {
+ Playback.PlaybackStart = Playback.GetCurrentTime(TimeSpanType.Midi);
+ Playback.Start();
+ }
+ }
+ }
+ }
+
+ public void CloseFile()
+ {
+ if (Playback != null)
+ {
+ _timeWatcher.RemovePlayback(Playback);
+
+ Playback.Stop();
+ Playback.Dispose();
+ }
+
+ MidiTracks.Clear();
+ MoveSlider(TimeSpan.Zero);
+
+ Playback = null;
+ Playlist.OpenedFile = null;
+ }
+
+ public async void Next()
+ {
+ var next = Playlist.Next();
+ if (next is null)
+ {
+ if (Playback is not null)
+ {
+ Playback.PlaybackStart = null;
+ Playback.MoveToStart();
+ }
+
+ MoveSlider(TimeSpan.Zero);
+ return;
+ }
+
+ Handle(next);
+
+ if (Playback is not null)
+ await PlayPause();
+ }
+
+ public void OnSelectedMidiInputChanged()
+ {
+ _inputDevice?.Dispose();
+
+ if (SelectedMidiInput?.DeviceName is not null
+ && SelectedMidiInput.DeviceName != "None")
+ {
+ _inputDevice = InputDevice.GetByName(SelectedMidiInput.DeviceName);
+
+ _inputDevice!.EventReceived += OnNoteEvent;
+ _inputDevice!.StartEventsListening();
+ }
+ }
+
public void OnSongPositionChanged()
{
NotifyOfPropertyChange(() => CurrentTime);
@@ -191,18 +278,42 @@ public void OnSongPositionChanged()
_ignoreSliderChange = false;
}
- public void OnSelectedMidiInputChanged()
+ public void OnSongTick(object? sender, PlaybackCurrentTimeChangedEventArgs e)
{
- _inputDevice?.Dispose();
+ foreach (var playbackTime in e.Times)
+ {
+ TimeSpan time = (MetricTimeSpan) playbackTime.Time;
+ MoveSlider(time);
- if (SelectedMidiInput?.DeviceName is not null
- && SelectedMidiInput.DeviceName != "None")
+ UpdateButtons();
+ }
+ }
+
+ public void Previous()
+ {
+ if (CurrentTime > TimeSpan.FromSeconds(3))
{
- _inputDevice = InputDevice.GetByName(SelectedMidiInput.DeviceName);
+ Playback?.Stop();
+ Playback?.MoveToStart();
- _inputDevice!.EventReceived += OnNoteEvent;
- _inputDevice!.StartEventsListening();
+ MoveSlider(TimeSpan.Zero);
+ Playback?.Start();
+ }
+ else
+ Playlist.Previous();
+ }
+
+ public void RefreshDevices()
+ {
+ MidiInputs.Clear();
+ MidiInputs.Add(new("None"));
+
+ foreach (var device in InputDevice.GetAll())
+ {
+ MidiInputs.Add(new(device.Name));
}
+
+ SelectedMidiInput = MidiInputs[0];
}
public void UpdateButtons()
@@ -242,38 +353,14 @@ public void UpdateButtons()
}
}
- private void InitializeTracks()
- {
- if (Playlist.OpenedFile?.Midi is null)
- return;
-
- MidiTracks.Clear();
- MidiTracks.AddRange(Playlist.OpenedFile
- .Midi.GetTrackChunks()
- .Select(t => new MidiTrack(_events, t)));
- }
-
- public async Task OpenFile()
- {
- await Playlist.OpenFile();
- UpdateButtons();
- }
-
- public void CloseFile()
+ private int ApplyNoteSettings(int noteId)
{
- if (Playback != null)
- {
- _timeWatcher.RemovePlayback(Playback);
-
- Playback.Stop();
- Playback.Dispose();
- }
+ noteId -= Settings.KeyOffset;
- MidiTracks.Clear();
- MoveSlider(TimeSpan.Zero);
+ if (Settings.TransposeNotes)
+ noteId = LyrePlayer.TransposeNote(noteId, _settings.Transpose ?? Ignore);
- Playback = null;
- Playlist.OpenedFile = null;
+ return noteId;
}
private async Task InitializePlayback()
@@ -352,89 +439,21 @@ await Application.Current.Dispatcher.Invoke(async () =>
UpdateButtons();
}
- private int ApplyNoteSettings(int noteId)
- {
- noteId -= Settings.KeyOffset;
-
- if (Settings.TransposeNotes)
- noteId = LyrePlayer.TransposeNote(noteId, _settings.Transpose ?? Ignore);
-
- return noteId;
- }
-
- public void Previous()
- {
- if (CurrentTime > TimeSpan.FromSeconds(3))
- {
- Playback?.Stop();
- Playback?.MoveToStart();
-
- MoveSlider(TimeSpan.Zero);
- Playback?.Start();
- }
- else
- Playlist.Previous();
- }
-
- public async void Next()
+ private void InitializeTracks()
{
- var next = Playlist.Next();
- if (next is null)
- {
- if (Playback is not null)
- {
- Playback.PlaybackStart = null;
- Playback.MoveToStart();
- }
-
- MoveSlider(TimeSpan.Zero);
+ if (Playlist.OpenedFile?.Midi is null)
return;
- }
-
- Handle(next);
-
- if (Playback is not null)
- await PlayPause();
- }
-
- public async Task PlayPause()
- {
- if (Playback is null)
- await InitializePlayback();
-
- if (Playback!.IsRunning)
- Playback.Stop();
- else
- {
- var time = new MetricTimeSpan(CurrentTime);
- Playback.PlaybackStart = time;
- Playback.MoveToTime(time);
-
- if (Settings.UseSpeakers)
- Playback.Start();
- else
- {
- WindowHelper.EnsureGameOnTop();
- await Task.Delay(100);
- if (WindowHelper.IsGameFocused())
- {
- Playback.PlaybackStart = Playback.GetCurrentTime(TimeSpanType.Midi);
- Playback.Start();
- }
- }
- }
+ MidiTracks.Clear();
+ MidiTracks.AddRange(Playlist.OpenedFile
+ .Midi.GetTrackChunks()
+ .Select(t => new MidiTrack(_events, t)));
}
- public void OnSongTick(object? sender, PlaybackCurrentTimeChangedEventArgs e)
+ private void MoveSlider(TimeSpan value)
{
- foreach (var playbackTime in e.Times)
- {
- TimeSpan time = (MetricTimeSpan) playbackTime.Time;
- MoveSlider(time);
-
- UpdateButtons();
- }
+ _ignoreSliderChange = true;
+ SongPosition = value.TotalSeconds;
}
private void OnNoteEvent(object? sender, MidiEventPlayedEventArgs e)
@@ -485,24 +504,5 @@ private void PlayNote(NoteEvent noteEvent)
break;
}
}
-
- private void MoveSlider(TimeSpan value)
- {
- _ignoreSliderChange = true;
- SongPosition = value.TotalSeconds;
- }
-
- public void RefreshDevices()
- {
- MidiInputs.Clear();
- MidiInputs.Add(new("None"));
-
- foreach (var device in InputDevice.GetAll())
- {
- MidiInputs.Add(new(device.Name));
- }
-
- SelectedMidiInput = MidiInputs[0];
- }
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs
index 30ebbc2..7d73841 100644
--- a/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs
+++ b/GenshinLyreMidiPlayer.WPF/ViewModels/MainWindowViewModel.cs
@@ -10,8 +10,8 @@ namespace GenshinLyreMidiPlayer.WPF.ViewModels
{
public class MainWindowViewModel : Conductor.StackNavigation
{
- private readonly Stack _history = new();
private readonly IContainer _ioc;
+ private readonly Stack _history = new();
private NavigationView _navView;
public MainWindowViewModel(IContainer ioc, IEventAggregator events)
@@ -74,13 +74,6 @@ private void AutoSuggestBoxOnTextChanged(AutoSuggestBox sender, AutoSuggestBoxTe
}
}
- private void NavigateBack(NavigationView sender, NavigationViewBackRequestedEventArgs args)
- {
- _history.Pop();
- sender.SelectedItem = _history.Pop();
- sender.IsBackEnabled = _history.Count > 1;
- }
-
private void Navigate(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.IsSettingsSelected)
@@ -97,5 +90,12 @@ void Activate(IScreen viewModel)
_history.Push((NavigationViewItem) sender.SelectedItem);
}
}
+
+ private void NavigateBack(NavigationView sender, NavigationViewBackRequestedEventArgs args)
+ {
+ _history.Pop();
+ sender.SelectedItem = _history.Pop();
+ sender.IsBackEnabled = _history.Count > 1;
+ }
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs
index 2b8979b..326c8f0 100644
--- a/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs
+++ b/GenshinLyreMidiPlayer.WPF/ViewModels/PianoSheetViewModel.cs
@@ -24,6 +24,13 @@ public PianoSheetViewModel(SettingsPageViewModel settingsPage,
[OnChangedMethod(nameof(Update))] public char Delimiter { get; set; } = '.';
+ [OnChangedMethod(nameof(Update))]
+ public KeyValuePair SelectedLayout
+ {
+ get => SettingsPage.SelectedLayout;
+ set => SettingsPage.SelectedLayout = value;
+ }
+
public PlaylistViewModel PlaylistView { get; }
public SettingsPageViewModel SettingsPage { get; }
@@ -51,15 +58,6 @@ public uint Shorten
set => SetAndNotify(ref _shorten, Math.Max(value, 1));
}
- [OnChangedMethod(nameof(Update))]
- public KeyValuePair SelectedLayout
- {
- get => SettingsPage.SelectedLayout;
- set => SettingsPage.SelectedLayout = value;
- }
-
- protected override void OnActivate() { Update(); }
-
public void Update()
{
if (PlaylistView.OpenedFile is null)
@@ -102,5 +100,7 @@ public void Update()
Result = sb.ToString();
}
+
+ protected override void OnActivate() { Update(); }
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs
index 625ea0b..371de9f 100644
--- a/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs
+++ b/GenshinLyreMidiPlayer.WPF/ViewModels/PlaylistViewModel.cs
@@ -26,9 +26,10 @@ public enum LoopMode
All
}
- private readonly IEventAggregator _events;
private readonly IContainer _ioc;
+ private readonly IEventAggregator _events;
+
public PlaylistViewModel(IContainer ioc, IEventAggregator events)
{
_ioc = ioc;
@@ -39,8 +40,6 @@ public PlaylistViewModel(IContainer ioc, IEventAggregator events)
? Tracks
: new(Tracks.Where(t => t.Title.Contains(FilterText, StringComparison.OrdinalIgnoreCase)));
- private BindableCollection ShuffledTracks { get; set; } = new();
-
public BindableCollection Tracks { get; } = new();
public bool Shuffle { get; set; }
@@ -68,29 +67,9 @@ public PlaylistViewModel(IContainer ioc, IEventAggregator events)
LoopMode.All => "\xE8EE"
};
- public void OnFilterTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e)
- {
- FilterText = sender.Text;
- }
-
- public void ToggleShuffle()
- {
- Shuffle = !Shuffle;
-
- if (Shuffle)
- ShuffledTracks = new(Tracks.OrderBy(_ => Guid.NewGuid()));
-
- RefreshPlaylist();
- }
-
- public void ToggleLoop()
- {
- var loopState = (int) Loop;
- var loopStates = Enum.GetValues(typeof(LoopMode)).Length;
+ private BindableCollection ShuffledTracks { get; set; } = new();
- var newState = (loopState + 1) % loopStates;
- Loop = (LoopMode) newState;
- }
+ public BindableCollection GetPlaylist() => Shuffle ? ShuffledTracks : Tracks;
public MidiFile? Next()
{
@@ -112,7 +91,27 @@ public void ToggleLoop()
return playlist.ElementAtOrDefault(next);
}
- public BindableCollection GetPlaylist() => Shuffle ? ShuffledTracks : Tracks;
+ public async Task AddFiles(IEnumerable files)
+ {
+ foreach (var file in files)
+ {
+ await AddFile(file);
+ }
+
+ 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 ClearPlaylist()
+ {
+ Tracks.Clear();
+ await UpdateHistory();
+ }
public async Task OpenFile()
{
@@ -128,35 +127,29 @@ public async Task OpenFile()
await AddFiles(openFileDialog.FileNames);
}
- public async Task AddFiles(IEnumerable files)
+ public async Task UpdateHistory()
{
- foreach (var file in files)
- {
- await AddFile(file);
- }
+ await using var db = _ioc.Get();
+ db.History.RemoveRange(db.History);
+ db.History.AddRange(Tracks.Select(t => new History(t.Path)));
+ await db.SaveChangesAsync();
+ }
- ShuffledTracks = new(Tracks.OrderBy(_ => Guid.NewGuid()));
- RefreshPlaylist();
- await UpdateHistory();
+ public void OnFileChanged(object sender, EventArgs e)
+ {
+ if (SelectedFile is not null)
+ _events.Publish(SelectedFile);
+ }
- var next = Next();
- if (OpenedFile is null && Tracks.Count > 0 && next is not null)
- _events.Publish(Next());
+ public void OnFilterTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs e)
+ {
+ FilterText = sender.Text;
}
- private async Task AddFile(string fileName, ReadingSettings? settings = null)
+ public void Previous()
{
- try
- {
- var file = new MidiFile(fileName, settings);
- Tracks.Add(file);
- }
- catch (Exception e)
- {
- settings ??= new();
- if (await ExceptionHandler.TryHandleException(e, settings))
- await AddFile(fileName, settings);
- }
+ History.Pop();
+ _events.Publish(History.Pop());
}
public void RemoveTrack()
@@ -168,18 +161,38 @@ public void RemoveTrack()
}
}
- public async Task ClearPlaylist()
+ public void ToggleLoop()
{
- Tracks.Clear();
- await UpdateHistory();
+ var loopState = (int) Loop;
+ var loopStates = Enum.GetValues(typeof(LoopMode)).Length;
+
+ var newState = (loopState + 1) % loopStates;
+ Loop = (LoopMode) newState;
}
- public async Task UpdateHistory()
+ public void ToggleShuffle()
{
- await using var db = _ioc.Get();
- db.History.RemoveRange(db.History);
- db.History.AddRange(Tracks.Select(t => new History(t.Path)));
- await db.SaveChangesAsync();
+ Shuffle = !Shuffle;
+
+ if (Shuffle)
+ ShuffledTracks = new(Tracks.OrderBy(_ => Guid.NewGuid()));
+
+ RefreshPlaylist();
+ }
+
+ private async Task AddFile(string fileName, ReadingSettings? settings = null)
+ {
+ try
+ {
+ var file = new MidiFile(fileName, settings);
+ Tracks.Add(file);
+ }
+ catch (Exception e)
+ {
+ settings ??= new();
+ if (await ExceptionHandler.TryHandleException(e, settings))
+ await AddFile(fileName, settings);
+ }
}
private void RefreshPlaylist()
@@ -190,17 +203,5 @@ private void RefreshPlaylist()
file.Position = playlist.IndexOf(file);
}
}
-
- public void OnFileChanged(object sender, EventArgs e)
- {
- if (SelectedFile is not null)
- _events.Publish(SelectedFile);
- }
-
- public void Previous()
- {
- History.Pop();
- _events.Publish(History.Pop());
- }
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs b/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs
index 80322b7..5bccb7b 100644
--- a/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs
+++ b/GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs
@@ -149,6 +149,12 @@ public int KeyOffset
public MidiSpeed SelectedSpeed { get; set; } = MidiSpeeds[Settings.SelectedSpeed];
+ public string GenshinLocation
+ {
+ get => Settings.GenshinLocation;
+ set => Settings.GenshinLocation = value;
+ }
+
public string Key => $"Key: {KeyOffsets[KeyOffset]}";
public string TimerText => CanChangeTime ? "Start" : "Stop";
@@ -161,12 +167,6 @@ public int KeyOffset
public static Version ProgramVersion => Assembly.GetExecutingAssembly().GetName().Version!;
- public string GenshinLocation
- {
- get => Settings.GenshinLocation;
- set => Settings.GenshinLocation = value;
- }
-
public bool TryGetLocation()
{
var locations = new[]
@@ -190,32 +190,30 @@ public bool TryGetLocation()
return locations.Any(TrySetLocation);
}
- private bool TrySetLocation(string? location)
+ public async Task CheckForUpdate()
{
- if (File.Exists(location) && location is not null)
- {
- Settings.GenshinLocation = location;
- NotifyOfPropertyChange(() => Settings.GenshinLocation);
- return true;
- }
+ if (IsCheckingUpdate)
+ return;
- return false;
- }
+ UpdateString = "Checking for updates...";
+ IsCheckingUpdate = true;
- public async Task SetLocation()
- {
- var openFileDialog = new OpenFileDialog
+ try
{
- Filter = "Executable|*.exe|All files (*.*)|*.*",
- InitialDirectory = WindowHelper.InstallLocation is null
- ? string.Empty
- : Path.Combine(WindowHelper.InstallLocation, "Genshin Impact Game")
- };
-
- var success = openFileDialog.ShowDialog() == true;
- var set = TrySetLocation(openFileDialog.FileName);
-
- if (!(success && set)) await LocationMissing();
+ LatestVersion = await GetLatestVersion();
+ UpdateString = LatestVersion.Version > ProgramVersion
+ ? "(Update available!)"
+ : string.Empty;
+ }
+ catch (Exception)
+ {
+ UpdateString = "Failed to check updates";
+ }
+ finally
+ {
+ IsCheckingUpdate = false;
+ NotifyOfPropertyChange(() => NeedsUpdate);
+ }
}
public async Task LocationMissing()
@@ -245,6 +243,22 @@ public async Task LocationMissing()
}
}
+ public async Task SetLocation()
+ {
+ var openFileDialog = new OpenFileDialog
+ {
+ Filter = "Executable|*.exe|All files (*.*)|*.*",
+ InitialDirectory = WindowHelper.InstallLocation is null
+ ? string.Empty
+ : Path.Combine(WindowHelper.InstallLocation, "Genshin Impact Game")
+ };
+
+ var success = openFileDialog.ShowDialog() == true;
+ var set = TrySetLocation(openFileDialog.FileName);
+
+ if (!(success && set)) await LocationMissing();
+ }
+
public async Task StartStopTimer()
{
if (PlayTimerToken is not null)
@@ -265,44 +279,6 @@ await Task.Delay(start, PlayTimerToken.Token)
PlayTimerToken = null;
}
- public void SetTimeToNow() => DateTime = DateTime.Now;
-
- private void OnAutoCheckUpdatesChanged()
- {
- if (AutoCheckUpdates)
- _ = CheckForUpdate();
- }
-
- private void OnIncludeBetaUpdatesChanged() => _ = CheckForUpdate();
-
- private void OnKeyOffsetChanged() => Settings.Modify(s => s.KeyOffset = KeyOffset);
-
- public async Task CheckForUpdate()
- {
- if (IsCheckingUpdate)
- return;
-
- UpdateString = "Checking for updates...";
- IsCheckingUpdate = true;
-
- try
- {
- LatestVersion = await GetLatestVersion();
- UpdateString = LatestVersion.Version > ProgramVersion
- ? "(Update available!)"
- : string.Empty;
- }
- catch (Exception)
- {
- UpdateString = "Failed to check updates";
- }
- finally
- {
- IsCheckingUpdate = false;
- NotifyOfPropertyChange(() => NeedsUpdate);
- }
- }
-
public async Task GetLatestVersion()
{
var client = new HttpClient();
@@ -321,12 +297,42 @@ public async Task GetLatestVersion()
.First(v => !v.Draft && !v.Prerelease || IncludeBetaUpdates);
}
- private void OnSelectedLayoutIndexChanged()
+ public void OnThemeChanged()
{
- var layout = (int) SelectedLayout.Key;
- Settings.Modify(s => s.SelectedLayout = layout);
+ var theme = (int?) ThemeManager.Current.ApplicationTheme ?? -1;
+ Settings.Modify(s => s.AppTheme = theme);
}
+ public void SetTimeToNow() => DateTime = DateTime.Now;
+
+ protected override void OnActivate()
+ {
+ if (AutoCheckUpdates)
+ _ = CheckForUpdate();
+ }
+
+ private bool TrySetLocation(string? location)
+ {
+ if (File.Exists(location) && location is not null)
+ {
+ Settings.GenshinLocation = location;
+ NotifyOfPropertyChange(() => Settings.GenshinLocation);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void OnAutoCheckUpdatesChanged()
+ {
+ if (AutoCheckUpdates)
+ _ = CheckForUpdate();
+ }
+
+ private void OnIncludeBetaUpdatesChanged() => _ = CheckForUpdate();
+
+ private void OnKeyOffsetChanged() => Settings.Modify(s => s.KeyOffset = KeyOffset);
+
private void OnMergeMillisecondsChanged()
{
Settings.Modify(s => s.MergeMilliseconds = MergeMilliseconds);
@@ -339,18 +345,12 @@ private void OnMergeNotesChanged()
_events.Publish(new MergeNotesNotification(MergeNotes));
}
- private void OnSelectedSpeedChanged() => _events.Publish(this);
-
- public void OnThemeChanged()
+ private void OnSelectedLayoutIndexChanged()
{
- var theme = (int?) ThemeManager.Current.ApplicationTheme ?? -1;
- Settings.Modify(s => s.AppTheme = theme);
+ var layout = (int) SelectedLayout.Key;
+ Settings.Modify(s => s.SelectedLayout = layout);
}
- protected override void OnActivate()
- {
- if (AutoCheckUpdates)
- _ = CheckForUpdate();
- }
+ private void OnSelectedSpeedChanged() => _events.Publish(this);
}
}
\ No newline at end of file
diff --git a/GenshinLyreMidiPlayer.WPF/Views/LyrePlayerView.xaml b/GenshinLyreMidiPlayer.WPF/Views/LyrePlayerView.xaml
index 2660a01..f0217cb 100644
--- a/GenshinLyreMidiPlayer.WPF/Views/LyrePlayerView.xaml
+++ b/GenshinLyreMidiPlayer.WPF/Views/LyrePlayerView.xaml
@@ -98,7 +98,8 @@