diff --git a/BassBoom.Basolia/Lyrics/Lyric.cs b/BassBoom.Basolia/Lyrics/Lyric.cs index d7516b1..7ac0d86 100644 --- a/BassBoom.Basolia/Lyrics/Lyric.cs +++ b/BassBoom.Basolia/Lyrics/Lyric.cs @@ -44,6 +44,16 @@ public LyricLine[] GetLinesCurrent() return GetLinesToSpan(currentSpan); } + /// + /// Gets all the lines from the current music duration to the end + /// + /// Array of lyric lines from the current music duration to the end + public LyricLine[] GetLinesUpcoming() + { + var currentSpan = PlaybackPositioningTools.GetCurrentDurationSpan(); + return GetLinesFromSpan(currentSpan); + } + /// /// Gets the last lyric line from the current music duration /// @@ -76,6 +86,14 @@ public List GetLastLineWordsCurrent() public LyricLine[] GetLinesToSpan(TimeSpan span) => Lines.Where((line) => line.LineSpan <= span).ToArray(); + /// + /// Gets all the lines from the current span to the end + /// + /// Time span in which it usually represents the current music duration + /// Array of lyric lines from the current span to the end + public LyricLine[] GetLinesFromSpan(TimeSpan span) => + Lines.Where((line) => line.LineSpan > span).ToArray(); + /// /// Gets the last lyric line from the given time span /// diff --git a/BassBoom.Basolia/Playback/PlaybackPositioningTools.cs b/BassBoom.Basolia/Playback/PlaybackPositioningTools.cs index 8ccf7f5..90094d7 100644 --- a/BassBoom.Basolia/Playback/PlaybackPositioningTools.cs +++ b/BassBoom.Basolia/Playback/PlaybackPositioningTools.cs @@ -25,6 +25,7 @@ using BassBoom.Native.Runtime; using System; using System.Threading; +using BassBoom.Basolia.Lyrics; namespace BassBoom.Basolia.Playback { @@ -143,6 +144,23 @@ public static void SeekToFrame(int frame) } } + public static void SeekLyric(LyricLine lyricLine) + { + lock (PositionLock) + { + InitBasolia.CheckInited(); + + // Check to see if the file is open + if (!FileTools.IsOpened) + throw new BasoliaException("Can't seek a file that's not open", mpg123_errors.MPG123_BAD_FILE); + + // Get the length, convert it to frames, and seek + var length = lyricLine.LineSpan.TotalSeconds; + int frame = (int)(length * FormatTools.GetFormatInfo().rate); + SeekToFrame(frame); + } + } + /// /// Drops all MPEG frames to the device /// diff --git a/BassBoom.Cli/CliBase/Common.cs b/BassBoom.Cli/CliBase/Common.cs index cdda5f6..dc5a00e 100644 --- a/BassBoom.Cli/CliBase/Common.cs +++ b/BassBoom.Cli/CliBase/Common.cs @@ -174,6 +174,10 @@ [N] Next song [R] Remove current song [CTRL] + [R] Remove all songs [S] (when playing) Selectively seek + [F] (when playing) Seek to previous lyric + [G] (when playing) Seek to next lyric + [J] (when playing) Seek to current lyric + [K] (when playing) Seek to which lyric [E] Opens the equalizer [D] (when playing) Device and driver info [Z] System info diff --git a/BassBoom.Cli/CliBase/Player.cs b/BassBoom.Cli/CliBase/Player.cs index 8df7d32..91854e5 100644 --- a/BassBoom.Cli/CliBase/Player.cs +++ b/BassBoom.Cli/CliBase/Player.cs @@ -213,6 +213,19 @@ private static void HandleKeypressPlayMode(ConsoleKeyInfo keystroke, Screen play playerThread = new(HandlePlay); PlayerControls.Play(); break; + case ConsoleKey.F: + PlayerControls.SeekPreviousLyric(); + break; + case ConsoleKey.G: + PlayerControls.SeekNextLyric(); + break; + case ConsoleKey.J: + PlayerControls.SeekCurrentLyric(); + break; + case ConsoleKey.K: + PlayerControls.SeekWhichLyric(); + playerScreen.RequireRefresh(); + break; case ConsoleKey.N: PlayerControls.Stop(false); PlayerControls.SeekBeginning(); diff --git a/BassBoom.Cli/CliBase/PlayerControls.cs b/BassBoom.Cli/CliBase/PlayerControls.cs index 7e36f2d..8b22da2 100644 --- a/BassBoom.Cli/CliBase/PlayerControls.cs +++ b/BassBoom.Cli/CliBase/PlayerControls.cs @@ -31,6 +31,7 @@ using Terminaux.Base; using Terminaux.Base.Buffered; using Terminaux.Colors.Data; +using Terminaux.Inputs; using Terminaux.Inputs.Styles.Infobox; using Terminaux.Writer.ConsoleWriters; using Terminaux.Writer.FancyWriters; @@ -75,6 +76,71 @@ internal static void SeekBeginning() Player.position = 0; } + internal static void SeekPreviousLyric() + { + // In case we have no songs in the playlist, or we have no lyrics... + if (Common.cachedInfos.Count == 0) + return; + if (Common.CurrentCachedInfo.LyricInstance is null) + return; + + var lyrics = Common.CurrentCachedInfo.LyricInstance.GetLinesCurrent(); + if (lyrics.Length == 0) + return; + var lyric = lyrics.Length == 1 ? lyrics[0] : lyrics[lyrics.Length - 2]; + PlaybackPositioningTools.SeekLyric(lyric); + } + + internal static void SeekCurrentLyric() + { + // In case we have no songs in the playlist, or we have no lyrics... + if (Common.cachedInfos.Count == 0) + return; + if (Common.CurrentCachedInfo.LyricInstance is null) + return; + + var lyrics = Common.CurrentCachedInfo.LyricInstance.GetLinesCurrent(); + if (lyrics.Length == 0) + return; + var lyric = lyrics[lyrics.Length - 1]; + PlaybackPositioningTools.SeekLyric(lyric); + } + + internal static void SeekNextLyric() + { + // In case we have no songs in the playlist, or we have no lyrics... + if (Common.cachedInfos.Count == 0) + return; + if (Common.CurrentCachedInfo.LyricInstance is null) + return; + + var lyrics = Common.CurrentCachedInfo.LyricInstance.GetLinesUpcoming(); + if (lyrics.Length == 0) + { + SeekCurrentLyric(); + return; + } + var lyric = lyrics[0]; + PlaybackPositioningTools.SeekLyric(lyric); + } + + internal static void SeekWhichLyric() + { + // In case we have no songs in the playlist, or we have no lyrics... + if (Common.cachedInfos.Count == 0) + return; + if (Common.CurrentCachedInfo.LyricInstance is null) + return; + + var lyrics = Common.CurrentCachedInfo.LyricInstance.Lines; + var choices = lyrics.Select((line) => new InputChoiceInfo($"{line.LineSpan}", line.Line)).ToArray(); + int index = InfoBoxSelectionColor.WriteInfoBoxSelection(choices, "Select a lyric to seek to"); + if (index == -1) + return; + var lyric = lyrics[index]; + PlaybackPositioningTools.SeekLyric(lyric); + } + internal static void Play() { // In case we have no songs in the playlist...