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...