Skip to content

Commit

Permalink
Show error when note cannot be played
Browse files Browse the repository at this point in the history
Adds an option to let the user pick to between moving the semitone higher, lower, or ignoring the note when it cannot be played even after considering transpose settings.
  • Loading branch information
sabihoshi committed May 11, 2021
1 parent 97dd880 commit d7f6c85
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 37 deletions.
38 changes: 31 additions & 7 deletions GenshinLyreMidiPlayer.WPF/Core/LyrePlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ namespace GenshinLyreMidiPlayer.WPF.Core
{
public static class LyrePlayer
{
public enum Tranpose
{
Ignore,
Up,
Down
}

private static readonly IInputSimulator Input = new InputSimulator();

private static readonly List<int> LyreNotes = new()
Expand Down Expand Up @@ -38,7 +45,8 @@ public static class LyrePlayer
83 // B5
};

public static int TransposeNote(int noteId)
public static int TransposeNote(int noteId,
Tranpose direction = Tranpose.Ignore)
{
while (true)
{
Expand All @@ -50,7 +58,14 @@ public static int TransposeNote(int noteId)
else if (noteId > LyreNotes.Last())
noteId -= 12;
else
noteId++;
{
return direction switch
{
Tranpose.Ignore => noteId,
Tranpose.Up => ++noteId,
Tranpose.Down => --noteId
};
}
}
}

Expand All @@ -72,13 +87,22 @@ public static void NoteUp(int noteId, Layout selectedLayout)
public static void InteractNote(int noteId, Layout selectedLayout,
Func<VirtualKeyCode, IKeyboardSimulator> action)
{
var layout = GetLayout(selectedLayout);
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<VirtualKeyCode> keys, int noteId, out VirtualKeyCode key)
{
var keyIndex = LyreNotes.IndexOf(noteId);
if (keyIndex < 0 || keyIndex > layout.Count)
return;
key = keys.ElementAtOrDefault(keyIndex);

var key = layout[keyIndex];
action.Invoke(key);
return keyIndex != -1;
}
}
}
2 changes: 1 addition & 1 deletion GenshinLyreMidiPlayer.WPF/GenshinLyreMidiPlayer.WPF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<UseWPF>true</UseWPF>
<StartupObject>GenshinLyreMidiPlayer.WPF.App</StartupObject>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>1.9.3</Version>
<Version>1.9.4</Version>
<ApplicationIcon>item_windsong_lyre.ico</ApplicationIcon>
<Nullable>enable</Nullable>
<RepositoryUrl>https://github.com/sabihoshi/GenshinLyreMidiPlayer</RepositoryUrl>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Humanizer;
Expand All @@ -8,15 +8,15 @@ namespace GenshinLyreMidiPlayer.WPF.ModernWPF.Errors
{
public class ErrorContentDialog : ContentDialog
{
public ErrorContentDialog(Exception e, IReadOnlyCollection<Enum>? options = null)
public ErrorContentDialog(Exception e, IReadOnlyCollection<Enum>? options = null, string? closeText = null)
{
Title = e.Message;
Content = e;

PrimaryButtonText = options?.ElementAtOrDefault(0)?.Humanize();
SecondaryButtonText = options?.ElementAtOrDefault(1)?.Humanize();

CloseButtonText = "Abort";
CloseButtonText = closeText ?? "Abort";
}
}
}
84 changes: 58 additions & 26 deletions GenshinLyreMidiPlayer.WPF/ViewModels/LyrePlayerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
using GenshinLyreMidiPlayer.Data.Notification;
using GenshinLyreMidiPlayer.Data.Properties;
using GenshinLyreMidiPlayer.WPF.Core;
using GenshinLyreMidiPlayer.WPF.ModernWPF.Errors;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Tools;
using ModernWpf.Controls;
using Stylet;
using StyletIoC;
using static Windows.Media.MediaPlaybackAutoRepeatMode;
using static GenshinLyreMidiPlayer.WPF.Core.LyrePlayer.Tranpose;
using MidiFile = GenshinLyreMidiPlayer.Data.Midi.MidiFile;

namespace GenshinLyreMidiPlayer.WPF.ViewModels
Expand Down Expand Up @@ -60,8 +63,8 @@ public LyrePlayerViewModel(IContainer ioc,
_player.CommandManager.NextReceived += (_, _) => Next();
_player.CommandManager.PreviousReceived += (_, _) => Previous();

_player.CommandManager.PlayReceived += (_, _) => PlayPause();
_player.CommandManager.PauseReceived += (_, _) => PlayPause();
_player.CommandManager.PlayReceived += async (_, _) => await PlayPause();
_player.CommandManager.PauseReceived += async (_, _) => await PlayPause();
}
}

Expand Down Expand Up @@ -153,27 +156,27 @@ public MidiInput? SelectedMidiInput

public TimeSpan MaximumTime => Playlist.OpenedFile?.Duration ?? TimeSpan.Zero;

public void Handle(MergeNotesNotification message)
public async void Handle(MergeNotesNotification message)
{
if (!message.Merge)
InitializeTracks();

InitializePlayback();
await InitializePlayback();
}

public void Handle(MidiFile file)
public async void Handle(MidiFile file)
{
CloseFile();
Playlist.OpenedFile = file;
Playlist.History.Push(file);

InitializeTracks();
InitializePlayback();
await InitializePlayback();
}

public void Handle(MidiTrack track) { InitializePlayback(); }
public async void Handle(MidiTrack track) { await InitializePlayback(); }

public void Handle(SettingsPageViewModel message) { InitializePlayback(); }
public async void Handle(SettingsPageViewModel message) { await InitializePlayback(); }

public void UpdateButtons()
{
Expand Down Expand Up @@ -242,7 +245,7 @@ public void CloseFile()
Playlist.OpenedFile = null;
}

private void InitializePlayback()
private async Task InitializePlayback()
{
Playback?.Stop();
Playback?.Dispose();
Expand All @@ -265,6 +268,29 @@ private void 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 _));

if (outOfRange.Any())
{
var options = new Enum[] { Up, Down };
var exceptionDialog = new ErrorContentDialog(
new IndexOutOfRangeException(
"Some notes cannot be played by the Lyre because it is missing Sharps & Flats. " +
"This can be solved by snapping to the nearest semitone."),
options, "Ignore");

var result = await exceptionDialog.ShowAsync();

_settings.Transpose = result switch
{
ContentDialogResult.None => Ignore,
ContentDialogResult.Primary => Up,
ContentDialogResult.Secondary => Down
};
}

Playback = midi.GetPlayback();
Playback.Speed = _settings.SelectedSpeed.Speed;

Expand Down Expand Up @@ -292,6 +318,16 @@ private void InitializePlayback()
UpdateButtons();
}

private int ApplyNoteSettings(int noteId)
{
noteId -= Settings.KeyOffset;

if (Settings.TransposeNotes)
noteId = LyrePlayer.TransposeNote(noteId, _settings.Transpose);

return noteId;
}

public void Previous()
{
if (CurrentTime > TimeSpan.FromSeconds(3))
Expand All @@ -303,7 +339,7 @@ public void Previous()
Playlist.Previous();
}

public void Next()
public async void Next()
{
var next = Playlist.Next();
if (next is null)
Expand All @@ -315,12 +351,13 @@ public void Next()
Handle(next);

if (Playback is not null)
PlayPause();
await PlayPause();
}

public void PlayPause()
public async Task PlayPause()
{
if (Playback is null) InitializePlayback();
if (Playback is null)
await InitializePlayback();

if (Playback!.IsRunning)
Playback.Stop();
Expand All @@ -336,17 +373,14 @@ public void PlayPause()
Playback.Start();
else
{
Task.Run(async () =>
WindowHelper.EnsureGameOnTop();
await Task.Delay(100);

if (WindowHelper.IsGameFocused())
{
WindowHelper.EnsureGameOnTop();
await Task.Delay(100);
if (WindowHelper.IsGameFocused())
{
Playback.PlaybackStart = Playback.GetCurrentTime(TimeSpanType.Midi);
Playback.Start();
}
});
Playback.PlaybackStart = Playback.GetCurrentTime(TimeSpanType.Midi);
Playback.Start();
}
}
}
}
Expand Down Expand Up @@ -393,9 +427,7 @@ private void PlayNote(NoteEvent noteEvent)
}

var layout = _settings.SelectedLayout.Key;
var note = noteEvent.NoteNumber - Settings.KeyOffset;
if (Settings.TransposeNotes)
note = LyrePlayer.TransposeNote(note);
var note = ApplyNoteSettings(noteEvent.NoteNumber);

switch (noteEvent.EventType)
{
Expand Down
2 changes: 2 additions & 0 deletions GenshinLyreMidiPlayer.WPF/ViewModels/SettingsPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ public int KeyOffset

public string UpdateString { get; set; } = string.Empty;

public LyrePlayer.Tranpose Transpose { get; set; } = LyrePlayer.Tranpose.Ignore;

public uint MergeMilliseconds { get; set; } = Settings.MergeMilliseconds;

public static Version ProgramVersion => Assembly.GetExecutingAssembly().GetName().Version!;
Expand Down

0 comments on commit d7f6c85

Please sign in to comment.