Follow the song in the lyrics
Synced LRC highlights the playing line, keeps it centred and seeks on click. Plain text scrolls continuously with a nudge knob instead — a highlight on guessed timings turns guaranteed drift into what looks like a bug. Seuraa kappaletta turns following off without losing the highlight, and scrolling by hand turns it off too. Fixes the scroll landing in the wrong place (offsetTop measured from a different coordinate space than the box it was applied to) and the fader shifting the deck sideways at score 100 (auto-sized grid columns plus a readout spanning both).
This commit is contained in:
@@ -9,6 +9,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -49,6 +51,51 @@ var lyricsClient = &http.Client{Timeout: lyricsTimout}
|
||||
// nobody wants to read [00:11.74] at the start of every line.
|
||||
var lrcStamp = regexp.MustCompile(`^(\[\d{1,2}:\d{2}(?:[.:]\d{1,3})?\]\s*)+`)
|
||||
|
||||
// A line of synced lyrics: the seconds it starts at, and the words.
|
||||
type lyricLine struct {
|
||||
At float64
|
||||
Text string
|
||||
}
|
||||
|
||||
var lrcOne = regexp.MustCompile(`\[(\d{1,2}):(\d{2})(?:[.:](\d{1,3}))?\]`)
|
||||
|
||||
// parseLRC returns nil for plain text, which is the signal to scroll continuously instead of
|
||||
// highlighting: a line-by-line highlight on guessed timings makes every second of drift read as a
|
||||
// bug.
|
||||
func parseLRC(s string) []lyricLine {
|
||||
if !strings.HasPrefix(strings.TrimSpace(s), "[") {
|
||||
return nil
|
||||
}
|
||||
var out []lyricLine
|
||||
for _, raw := range strings.Split(s, "\n") {
|
||||
stamps := lrcOne.FindAllStringSubmatch(raw, -1)
|
||||
if len(stamps) == 0 {
|
||||
continue
|
||||
}
|
||||
text := strings.TrimSpace(lrcStamp.ReplaceAllString(raw, ""))
|
||||
// One line can carry several timestamps when a refrain repeats.
|
||||
for _, m := range stamps {
|
||||
min, _ := strconv.Atoi(m[1])
|
||||
sec, _ := strconv.Atoi(m[2])
|
||||
at := float64(min*60 + sec)
|
||||
if m[3] != "" {
|
||||
frac, _ := strconv.Atoi(m[3])
|
||||
switch len(m[3]) {
|
||||
case 1:
|
||||
at += float64(frac) / 10
|
||||
case 2:
|
||||
at += float64(frac) / 100
|
||||
default:
|
||||
at += float64(frac) / 1000
|
||||
}
|
||||
}
|
||||
out = append(out, lyricLine{At: at, Text: text})
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].At < out[j].At })
|
||||
return out
|
||||
}
|
||||
|
||||
func stripLRC(s string) string {
|
||||
if !strings.HasPrefix(strings.TrimSpace(s), "[") {
|
||||
return s
|
||||
|
||||
Reference in New Issue
Block a user