From c06f844934509a6cfb14d69b8aaf7fc1d5aee349 Mon Sep 17 00:00:00 2001 From: Qingyu Sui Date: Sun, 15 Jan 2023 19:51:40 +0800 Subject: [PATCH] Closes #2 --- GenshinMusicPlayer/MainWindow.xaml | 7 +- GenshinMusicPlayer/MainWindow.xaml.cs | 237 +++++++++++++----- GenshinMusicPlayer/Properties/AssemblyInfo.cs | 2 +- 3 files changed, 180 insertions(+), 66 deletions(-) diff --git a/GenshinMusicPlayer/MainWindow.xaml b/GenshinMusicPlayer/MainWindow.xaml index f29fe60..428da37 100644 --- a/GenshinMusicPlayer/MainWindow.xaml +++ b/GenshinMusicPlayer/MainWindow.xaml @@ -81,7 +81,10 @@ 演奏音乐 - + + + 快捷键:Ctrl + Shift + F12 + @@ -91,7 +94,7 @@ 历史音符: - + diff --git a/GenshinMusicPlayer/MainWindow.xaml.cs b/GenshinMusicPlayer/MainWindow.xaml.cs index 5db094e..52efb94 100644 --- a/GenshinMusicPlayer/MainWindow.xaml.cs +++ b/GenshinMusicPlayer/MainWindow.xaml.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Windows; using System.Windows.Controls; +using System.Windows.Interop; using System.Windows.Threading; using WindowsInput; using WindowsInput.Native; @@ -125,7 +126,7 @@ public override string ToString() { } #endregion - + #region Instruments public class InstrumentCheckNotesResult @@ -291,9 +292,81 @@ public partial class MainWindow : Window private bool? isHigherFirst; // Null means ignore; true means higher semitone first; false means lower first. private long noteMergingTime; + private bool isPlaying = false; + [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern bool SwitchToThisWindow(IntPtr hWnd, bool fAltTab); + [DllImport("User32.dll")] + public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); + + [DllImport("User32.dll")] + public static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + #region Hotkey + + private HwndSource hwndSource; + private const int HOTKEY_ID = 1236841456; + private const int WM_HOTKEY = 0x0312; + private const uint VK_F12 = 0x7B; + private const uint MOD_CTRL_SHIFT = 0x0006; + + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + var helper = new WindowInteropHelper(this); + hwndSource = HwndSource.FromHwnd(helper.Handle); + hwndSource.AddHook(HwndHook); + RegisterHotKey(); + } + + protected override void OnClosed(EventArgs e) + { + hwndSource.RemoveHook(HwndHook); + hwndSource = null; + UnregisterHotKey(); + base.OnClosed(e); + } + + private void RegisterHotKey() + { + var helper = new WindowInteropHelper(this); + if (!RegisterHotKey(helper.Handle, HOTKEY_ID, MOD_CTRL_SHIFT, VK_F12)) + { + MessageBox.Show("热键注册不成功,请检查是否有冲突!"); + } + } + + private void UnregisterHotKey() + { + var helper = new WindowInteropHelper(this); + UnregisterHotKey(helper.Handle, HOTKEY_ID); + } + + private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + switch (msg) + { + case WM_HOTKEY: + switch (wParam.ToInt32()) + { + case HOTKEY_ID: + OnHotKeyPressed(); + handled = true; + break; + } + break; + } + return IntPtr.Zero; + } + + private void OnHotKeyPressed() + { + SwitchPlayingStatus(); + } + + #endregion + private void LoadMidiFileInfo(MidiFile file) { Note minNote = null; @@ -405,8 +478,90 @@ private void UpdateComboBoxTone() } } + private void SwitchPlayingStatus() + { + if (!isPlaying) + { + ButtonStart.IsEnabled = false; + + if (notes == null) + { + MessageBox.Show("请先加载 MIDI 文件!"); + ButtonStart.IsEnabled = true; + return; + } + if (ComboBoxInstrument.SelectedIndex == -1) + { + MessageBox.Show("请选择乐器!"); + ButtonStart.IsEnabled = true; + return; + } + if (ComboBoxTone.SelectedIndex == -1) + { + MessageBox.Show("请选择调性!"); + ButtonStart.IsEnabled = true; + return; + } + if (RadioButtonNoteSemitoneNotAllowed.IsChecked == true) + { + var result = instrument.CheckNotes(ComboBoxTone.SelectedIndex, notes); + if (result.MissedCount > 0) + { + MessageBox.Show("有不允许的半音音符存在!"); + ButtonStart.IsEnabled = true; + return; + } + } + if (RadioButtonNoteOutOfRangeNotAllowed.IsChecked == true) + { + var result = instrument.CheckNotes(ComboBoxTone.SelectedIndex, notes); + if (result.OutOfRangeCount > 0) + { + MessageBox.Show("有不允许的乐器范围外音符存在!"); + ButtonStart.IsEnabled = true; + return; + } + } + if (!ulong.TryParse(TextBoxNoteMerging.Text, out ulong parseResult)) + { + MessageBox.Show("音符合并演奏选项不是合法的正整数!"); + ButtonStart.IsEnabled = true; + return; + } + + // Disable load button here to avoid that we need to enable it again and again at the returns above. + ButtonLoadMidiFile.IsEnabled = false; + + WrapPanelHistoryNotes.Children.Clear(); + ProgressBarPlay.Value = 0; + + tone = ComboBoxTone.SelectedIndex; + isHigherFirst = null; + if (RadioButtonNoteSemitoneIgnored.IsChecked != true) + { + isHigherFirst = RadioButtonNoteSemitoneHigher.IsChecked == true; + } + noteMergingTime = (long)parseResult; + + Thread thread = new Thread(PlayMusic); + thread.IsBackground = true; + thread.Start(); + } + else + { + isPlaying = false; + } + } + private void PlayMusic() { + Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () + { + ButtonStart.Content = "停止演奏"; + ButtonStart.IsEnabled = true; + isPlaying = true; + }); + // Switch to Genshin window and wait... Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { @@ -420,21 +575,25 @@ private void PlayMusic() break; } } + if (!isPlaying) goto STOP_PLAYING; Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { TextBoxCurrentNote.Text = "3 秒后开始……"; }); Thread.Sleep(1000); + if (!isPlaying) goto STOP_PLAYING; Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { TextBoxCurrentNote.Text = "2 秒后开始……"; }); Thread.Sleep(1000); + if (!isPlaying) goto STOP_PLAYING; Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { TextBoxCurrentNote.Text = "1 秒后开始……"; }); Thread.Sleep(1000); + if (!isPlaying) goto STOP_PLAYING; Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { TextBoxCurrentNote.Text = ""; @@ -445,6 +604,8 @@ private void PlayMusic() int noteIdx = 0; while (noteIdx < notes.Count) { + if (!isPlaying) goto STOP_PLAYING; + var nextTimeToBePlayed = startTime + TimeSpan.FromMilliseconds(notes[noteIdx].Time); // Merge notes @@ -466,6 +627,7 @@ private void PlayMusic() keysToPress.Add(noteToPlay.VirtualKeyCodePress.Value); } } + if (!isPlaying) goto STOP_PLAYING; // Wait and press var timeToSleep = nextTimeToBePlayed - DateTime.Now; @@ -473,6 +635,7 @@ private void PlayMusic() { Thread.Sleep(timeToSleep); } + if (!isPlaying) goto STOP_PLAYING; if (keysToPress.Count > 0) { sim.Keyboard.KeyPress(keysToPress.ToArray()); @@ -484,6 +647,7 @@ private void PlayMusic() ProgressBarPlay.Value = (nextTimeToBePlayed - startTime).TotalMilliseconds / maxNoteOffTime * 100; TextBoxCurrentNote.Text = notesToPlay.Aggregate("", (text, noteToPlay) => text + " " + noteToPlay.ToString()).Substring(1); WrapPanelHistoryNotes.Children.Add(new TextBox() { Text = TextBoxCurrentNote.Text, Margin = new Thickness(2, 2, 2, 2) }); + ScrollViewerHistoryNotes.ScrollToBottom(); }); noteIdx = nextNoteIdx; @@ -492,8 +656,15 @@ private void PlayMusic() Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { ProgressBarPlay.Value = 100; - ButtonLoadMidiFile.IsEnabled = true; + }); + isPlaying = false; + + STOP_PLAYING:; + Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () + { + ButtonStart.Content = "开始演奏"; ButtonStart.IsEnabled = true; + ButtonLoadMidiFile.IsEnabled = true; }); } @@ -531,67 +702,7 @@ private void ComboBoxInstrument_SelectionChanged(object sender, SelectionChanged private void ButtonStart_Click(object sender, RoutedEventArgs e) { - ButtonStart.IsEnabled = false; - if (notes == null) - { - MessageBox.Show("请先加载 MIDI 文件!"); - ButtonStart.IsEnabled = true; - return; - } - if (ComboBoxInstrument.SelectedIndex == -1) - { - MessageBox.Show("请选择乐器!"); - ButtonStart.IsEnabled = true; - return; - } - if (ComboBoxTone.SelectedIndex == -1) - { - MessageBox.Show("请选择调性!"); - ButtonStart.IsEnabled = true; - return; - } - if (RadioButtonNoteSemitoneNotAllowed.IsChecked == true) - { - var result = instrument.CheckNotes(ComboBoxTone.SelectedIndex, notes); - if (result.MissedCount > 0) - { - MessageBox.Show("有不允许的半音音符存在!"); - ButtonStart.IsEnabled = true; - return; - } - } - if (RadioButtonNoteOutOfRangeNotAllowed.IsChecked == true) - { - var result = instrument.CheckNotes(ComboBoxTone.SelectedIndex, notes); - if (result.OutOfRangeCount > 0) - { - MessageBox.Show("有不允许的乐器范围外音符存在!"); - ButtonStart.IsEnabled = true; - return; - } - } - if (!ulong.TryParse(TextBoxNoteMerging.Text, out ulong parseResult)) - { - MessageBox.Show("音符合并演奏选项不是合法的正整数!"); - ButtonStart.IsEnabled = true; - return; - } - - ButtonLoadMidiFile.IsEnabled = false; - WrapPanelHistoryNotes.Children.Clear(); - ProgressBarPlay.Value = 0; - - tone = ComboBoxTone.SelectedIndex; - isHigherFirst = null; - if (RadioButtonNoteSemitoneIgnored.IsChecked != true) - { - isHigherFirst = RadioButtonNoteSemitoneHigher.IsChecked == true; - } - noteMergingTime = (long)parseResult; - - Thread thread = new Thread(PlayMusic); - thread.IsBackground = true; - thread.Start(); + SwitchPlayingStatus(); } public MainWindow() diff --git a/GenshinMusicPlayer/Properties/AssemblyInfo.cs b/GenshinMusicPlayer/Properties/AssemblyInfo.cs index ebb30d1..e040857 100644 --- a/GenshinMusicPlayer/Properties/AssemblyInfo.cs +++ b/GenshinMusicPlayer/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.*")] +[assembly: AssemblyVersion("1.3.*")] //[assembly: AssemblyFileVersion("1.0.0.0")]