From 8fc67711b6f093b96e47a418905f3cf73e86e552 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Fri, 9 Jan 2026 19:26:16 -0500 Subject: [PATCH 1/7] Simplify language selection in README.CN.md Removed redundant link from Chinese README. --- README.CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.CN.md b/README.CN.md index 9496bc8..e426854 100644 --- a/README.CN.md +++ b/README.CN.md @@ -1,4 +1,4 @@ -[**中文**](README.CN.md) | [**English**](README.md) +**中文** | [**English**](README.md)
Logo From 4e33444d8e4cceef4121958e9a6703345f6e7341 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Fri, 9 Jan 2026 19:26:31 -0500 Subject: [PATCH 2/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a5fc79..7f159aa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[**中文**](README.CN.md) | [**English**](README.md) +[**中文**](README.CN.md) | **English**
Logo From 047e53b83037fe1dbf8411598d83bf06cf887b37 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sat, 10 Jan 2026 07:38:17 -0500 Subject: [PATCH 3/7] fix: lyrics parser --- BetterLyrics.Core/BetterLyrics.Core.csproj | 4 +- .../Logic/LyricsAnimator.cs | 2 +- .../Logic/LyricsLayoutManager.cs | 2 +- .../Logic/LyricsSynchronizer.cs | 5 +-- .../Models/Lyrics/BaseLyrics.cs | 4 +- .../Parsers/LyricsParser/LyricsParser.Lrc.cs | 28 ++++++------- .../LyricsParser/LyricsParser.QrcKrc.cs | 2 +- .../Parsers/LyricsParser/LyricsParser.cs | 42 +++++++++++++++++++ 8 files changed, 66 insertions(+), 23 deletions(-) diff --git a/BetterLyrics.Core/BetterLyrics.Core.csproj b/BetterLyrics.Core/BetterLyrics.Core.csproj index 968ac4e..9d780f1 100644 --- a/BetterLyrics.Core/BetterLyrics.Core.csproj +++ b/BetterLyrics.Core/BetterLyrics.Core.csproj @@ -7,7 +7,9 @@ - + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs index 14fdd0b..9d8a36e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs @@ -83,7 +83,7 @@ namespace BetterLyrics.WinUI3.Logic ? 1.15 : lyricsEffect.LyricsScaleEffectAmount / 100.0; - var maxAnimationDurationMs = Math.Max(line.EndMs - currentPositionMs, 0); + var maxAnimationDurationMs = Math.Max(line.EndMs ?? 0 - currentPositionMs, 0); bool isSecondaryLinePlaying = line.GetIsPlaying(currentPositionMs); bool isSecondaryLinePlayingChanged = line.IsPlayingLastFrame != isSecondaryLinePlaying; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs index 786778d..7411bf4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsLayoutManager.cs @@ -215,7 +215,7 @@ namespace BetterLyrics.WinUI3.Logic lanesEndMs.Add(0); } - lanesEndMs[assignedLane] = end; + lanesEndMs[assignedLane] = end ?? 0; line.LaneIndex = assignedLane; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs index 6619507..2c4cedb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs @@ -74,7 +74,7 @@ namespace BetterLyrics.WinUI3.Logic if (line == null) return state; - double lineEndMs = line.EndMs; + double lineEndMs = line.EndMs ?? 0; // 还没到 if (currentTimeMs < line.StartMs) return state; @@ -129,8 +129,7 @@ namespace BetterLyrics.WinUI3.Logic var timing = line.PrimaryRenderSyllables[i]; var nextTiming = (i + 1 < count) ? line.PrimaryRenderSyllables[i + 1] : null; - //double timingEndMs = timing.EndMs ?? nextTiming?.StartMs ?? lineEndMs; - double timingEndMs = timing.EndMs; + double timingEndMs = timing.EndMs ?? 0; // 在当前字范围内 if (time >= timing.StartMs && time <= timingEndMs) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs index fb4be92..9e8b001 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/BaseLyrics.cs @@ -7,8 +7,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public class BaseLyrics { public int StartMs { get; set; } - public int EndMs { get; set; } - public int DurationMs => EndMs - StartMs; + public int? EndMs { get; set; } = null; + public int DurationMs => Math.Max((EndMs ?? 0) - StartMs, 0); public string Text { get; set; } = ""; public int Length => Text.Length; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs index 758efc6..0af32e5 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs @@ -40,24 +40,11 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser startIndex += text.Length; } - int lineEndMs = 0; - - if (syllables.Count > 0) - { - var lastSyllable = syllables[syllables.Count - 1]; - if (string.IsNullOrWhiteSpace(lastSyllable.Text)) - { - lineEndMs = lastSyllable.StartMs; - syllables.RemoveAt(syllables.Count - 1); - } - } - if (syllables.Count > 1) { lrcLines.Add(new LyricsLine { StartMs = syllables[0].StartMs, - EndMs = lineEndMs, PrimaryText = string.Concat(syllables.Select(s => s.Text)), PrimarySyllables = syllables }); @@ -81,7 +68,19 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser content = bracketRegex!.Replace(line, "").Trim(); if (content == "//") content = ""; - lrcLines.Add(new LyricsLine { StartMs = lineStartMs, PrimaryText = content }); + lrcLines.Add(new LyricsLine + { + StartMs = lineStartMs, + PrimarySyllables = [ + new BaseLyrics + { + StartIndex = 0, + StartMs = lineStartMs, + Text = content + } + ], + PrimaryText = content + }); } } } @@ -125,5 +124,6 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser } } } + } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs index 7087ac6..55e7b68 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs @@ -21,7 +21,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser var lineWrite = new LyricsLine { StartMs = lineRead.StartTime ?? 0, - EndMs = lineRead.EndTime ?? (nextLineRead?.StartTime ?? 0), + EndMs = lineRead.EndTime, PrimaryText = lineRead.Text, PrimarySyllables = [], }; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs index 4748be6..5bc61af 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs @@ -69,6 +69,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser LoadTransliteration(lyricsSearchResult); GenerateTransliterationLyricsData(); + EnsureEndMs(lyricsSearchResult?.Duration); + return _lyricsDataArr; } @@ -271,5 +273,45 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser } } + private void EnsureEndMs(double? duration) + { + foreach (var lyricsData in _lyricsDataArr) + { + var lines = lyricsData.LyricsLines; + // 计算结束时间 + for (int i = 0; i < lines.Count; i++) + { + // 计算行结束时间 + if (lines[i].EndMs == null) + { + if (i + 1 < lines.Count) + { + lines[i].EndMs = lines[i + 1].StartMs; + } + else + { + lines[i].EndMs = (int)(duration ?? 0) * 1000; + } + } + // 计算音节结束时间 + for (int j = 0; j < lines[i].PrimarySyllables.Count; j++) + { + var syllable = lines[i].PrimarySyllables[j]; + if (syllable.EndMs == null) + { + if (j < lines[i].PrimarySyllables.Count - 1) + { + syllable.EndMs = lines[i].PrimarySyllables[j + 1].StartMs; + } + else + { + syllable.EndMs = lines[i].EndMs; + } + } + } + } + } + } + } } From 74eeffc8a6ce9fa823649291b3a93558822643a1 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sat, 10 Jan 2026 09:45:23 -0500 Subject: [PATCH 4/7] fix: fan shape lyrics effect --- .../BetterLyrics.WinUI3 (Package)/Package.appxmanifest | 2 +- .../BetterLyrics.WinUI3/Logic/LyricsAnimator.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index 9e7ecfe..b6dd072 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.2.254.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs index 9d8a36e..3dcb793 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs @@ -176,7 +176,7 @@ namespace BetterLyrics.WinUI3.Logic line.AngleTransition.SetDelay(yScrollDelay); line.AngleTransition.Start( (isFanEnabled && !isMouseScrolling) ? - Math.PI * (fanAngleRad / 180.0) * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) : + fanAngleRad * distanceFactor * (i > primaryPlayingLineIndex ? 1 : -1) : 0); line.YOffsetTransition.SetEasingType(canvasYScrollTransition.EasingType); @@ -187,7 +187,7 @@ namespace BetterLyrics.WinUI3.Logic line.YOffsetTransition.Start(targetYScrollOffset); } - if (isLayoutChanged || isSecondaryLinePlayingChanged) + if (isSecondaryLinePlayingChanged) { // 辉光动画 if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar From 3e1907ad8cc2744d980ebc2d46e9354897ec4647 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sat, 10 Jan 2026 10:06:11 -0500 Subject: [PATCH 5/7] fix: lyrics parser (line endtime) --- .../BetterLyrics.WinUI3 (Package)/Package.appxmanifest | 2 +- .../Parsers/LyricsParser/LyricsParser.QrcKrc.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index b6dd072..951e89b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.2.255.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs index 55e7b68..06a8050 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs @@ -21,7 +21,6 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser var lineWrite = new LyricsLine { StartMs = lineRead.StartTime ?? 0, - EndMs = lineRead.EndTime, PrimaryText = lineRead.Text, PrimarySyllables = [], }; From e43461d62471733131d88d9cc0cfb67b2b2794d5 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sat, 10 Jan 2026 11:29:22 -0500 Subject: [PATCH 6/7] feat: float and glow effect now can be adapted to auto word-by-word effect --- .../Extensions/LyricsDataExtensions.cs | 5 +- .../Logic/LyricsAnimator.cs | 174 +++++++++--------- .../Logic/LyricsSynchronizer.cs | 4 +- .../Models/Lyrics/LyricsLine.cs | 2 + .../Models/Lyrics/RenderLyricsLine.cs | 3 + .../Parsers/LyricsParser/LyricsParser.Lrc.cs | 19 +- .../LyricsParser/LyricsParser.QrcKrc.cs | 2 +- .../Parsers/LyricsParser/LyricsParser.Ttml.cs | 9 +- .../Parsers/LyricsParser/LyricsParser.cs | 40 ++++ 9 files changed, 155 insertions(+), 103 deletions(-) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs index 8fb4fc0..b66d14b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Extensions/LyricsDataExtensions.cs @@ -19,9 +19,10 @@ namespace BetterLyrics.WinUI3.Extensions new LyricsLine { StartMs = 0, - EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds, + EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds, PrimaryText = "● ● ●", - PrimarySyllables = [new BaseLyrics { Text = "● ● ●", StartMs = 0, EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds }], + PrimarySyllables = [new BaseLyrics { Text = "● ● ●", StartMs = 0, EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds }], + IsPrimaryHasRealSyllableInfo = true, }, ], LanguageCode = "N/A", diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs index 3dcb793..7d0cfce 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsAnimator.cs @@ -68,11 +68,17 @@ namespace BetterLyrics.WinUI3.Logic for (int i = safeStart; i <= safeEnd; i++) { var line = lines[i]; - var lineHeight = line.PrimaryLineHeight; - if (lineHeight == null || lineHeight <= 0) continue; + bool isWordAnimationEnabled = lyricsEffect.WordByWordEffectMode switch + { + Enums.WordByWordEffectMode.Auto => line.IsPrimaryHasRealSyllableInfo, + Enums.WordByWordEffectMode.Always => true, + Enums.WordByWordEffectMode.Never => false, + _ => line.IsPrimaryHasRealSyllableInfo + }; + double targetCharFloat = lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust ? lineHeight.Value * 0.1 : lyricsEffect.LyricsFloatAnimationAmount; @@ -187,98 +193,100 @@ namespace BetterLyrics.WinUI3.Logic line.YOffsetTransition.Start(targetYScrollOffset); } - if (isSecondaryLinePlayingChanged) + if (isWordAnimationEnabled) { - // 辉光动画 - if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar - && isSecondaryLinePlaying) + if (isSecondaryLinePlayingChanged) { - foreach (var renderChar in line.PrimaryRenderChars) + // 辉光动画 + if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar + && isSecondaryLinePlaying) { - var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0; - var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0); - renderChar.GlowTransition.Start( - new Models.Keyframe(targetCharGlow, stepInOutDuration), - new Models.Keyframe(targetCharGlow, stepLastingDuration), - new Models.Keyframe(0, stepInOutDuration) - ); - } - } - - // 浮动动画 - if (isFloatEnabled) - { - foreach (var renderChar in line.PrimaryRenderChars) - { - renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetCharFloat : 0); - } - } - } - - // 字符动画 - foreach (var renderChar in line.PrimaryRenderChars) - { - renderChar.ProgressPlayed = renderChar.GetPlayProgress(currentPositionMs); - - bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs); - bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying; - - if (isCharPlayingChanged) - { - if (isFloatEnabled) - { - renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs)); - renderChar.FloatTransition.Start(0); - } - - renderChar.IsPlayingLastFrame = isCharPlaying; - } - } - - // 音节动画 - foreach (var syllable in line.PrimaryRenderSyllables) - { - bool isSyllablePlaying = syllable.GetIsPlaying(currentPositionMs); - bool isSyllablePlayingChanged = syllable.IsPlayingLastFrame != isSyllablePlaying; - - if (isSyllablePlayingChanged) - { - if (isScaleEnabled && isSyllablePlaying) - { - foreach (var renderChar in syllable.ChildrenRenderLyricsChars) + foreach (var renderChar in line.PrimaryRenderChars) { - if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration) - { - var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0; - renderChar.ScaleTransition.Start( - new Models.Keyframe(targetCharScale, stepDuration), - new Models.Keyframe(1.0, stepDuration) - ); - } - } - } - - if (isGlowEnabled && isSyllablePlaying && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LongDurationSyllable - && syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration) - { - foreach (var renderChar in syllable.ChildrenRenderLyricsChars) - { - var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0; + var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0; + var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0); renderChar.GlowTransition.Start( - new Models.Keyframe(targetCharGlow, stepDuration), - new Models.Keyframe(0, stepDuration) + new Models.Keyframe(targetCharGlow, stepInOutDuration), + new Models.Keyframe(targetCharGlow, stepLastingDuration), + new Models.Keyframe(0, stepInOutDuration) ); } } - syllable.IsPlayingLastFrame = isSyllablePlaying; + // 浮动动画 + if (isFloatEnabled) + { + foreach (var renderChar in line.PrimaryRenderChars) + { + renderChar.FloatTransition.Start(isSecondaryLinePlaying ? targetCharFloat : 0); + } + } } - } - // 使动画步进一帧 - foreach (var renderChar in line.PrimaryRenderChars) - { - renderChar.Update(elapsedTime); + // 字符动画 + foreach (var renderChar in line.PrimaryRenderChars) + { + renderChar.ProgressPlayed = renderChar.GetPlayProgress(currentPositionMs); + + bool isCharPlaying = renderChar.GetIsPlaying(currentPositionMs); + bool isCharPlayingChanged = renderChar.IsPlayingLastFrame != isCharPlaying; + + if (isCharPlayingChanged) + { + if (isFloatEnabled) + { + renderChar.FloatTransition.SetDurationMs(Math.Min(lyricsEffect.LyricsFloatAnimationDuration, maxAnimationDurationMs)); + renderChar.FloatTransition.Start(0); + } + + renderChar.IsPlayingLastFrame = isCharPlaying; + } + } + + // 音节动画 + foreach (var syllable in line.PrimaryRenderSyllables) + { + bool isSyllablePlaying = syllable.GetIsPlaying(currentPositionMs); + bool isSyllablePlayingChanged = syllable.IsPlayingLastFrame != isSyllablePlaying; + + if (isSyllablePlayingChanged) + { + if (isScaleEnabled && isSyllablePlaying) + { + foreach (var renderChar in syllable.ChildrenRenderLyricsChars) + { + if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration) + { + var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0; + renderChar.ScaleTransition.Start( + new Models.Keyframe(targetCharScale, stepDuration), + new Models.Keyframe(1.0, stepDuration) + ); + } + } + } + + if (isGlowEnabled && isSyllablePlaying && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LongDurationSyllable + && syllable.DurationMs >= lyricsEffect.LyricsGlowEffectLongSyllableDuration) + { + foreach (var renderChar in syllable.ChildrenRenderLyricsChars) + { + var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0; + renderChar.GlowTransition.Start( + new Models.Keyframe(targetCharGlow, stepDuration), + new Models.Keyframe(0, stepDuration) + ); + } + } + + syllable.IsPlayingLastFrame = isSyllablePlaying; + } + } + + foreach (var renderChar in line.PrimaryRenderChars) + { + renderChar.Update(elapsedTime); + } } line.Update(elapsedTime); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs index 2c4cedb..e9c1579 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Logic/LyricsSynchronizer.cs @@ -91,7 +91,7 @@ namespace BetterLyrics.WinUI3.Logic switch (wordByWordEffectMode) { case WordByWordEffectMode.Auto: - if (line.PrimaryRenderSyllables.Count > 1) + if (line.IsPrimaryHasRealSyllableInfo) { return CalculateSyllableProgress(currentTimeMs, line, lineEndMs); } @@ -106,7 +106,7 @@ namespace BetterLyrics.WinUI3.Logic state.SyllableProgress = 1f; return state; case WordByWordEffectMode.Always: - if (line.PrimaryRenderSyllables.Count > 1) + if (line.IsPrimaryHasRealSyllableInfo) { return CalculateSyllableProgress(currentTimeMs, line, lineEndMs); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs index b54a993..81f0a52 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/LyricsLine.cs @@ -22,6 +22,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public new string Text => PrimaryText; public new int StartIndex = 0; + public bool IsPrimaryHasRealSyllableInfo { get; set; } = false; + public LyricsLine() { for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs index 7d0d137..ec858a4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/Lyrics/RenderLyricsLine.cs @@ -76,6 +76,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height; + public bool IsPrimaryHasRealSyllableInfo { get; set; } + public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine) { AngleTransition = new( @@ -130,6 +132,7 @@ namespace BetterLyrics.WinUI3.Models.Lyrics PrimaryText = lyricsLine.PrimaryText; SecondaryText = lyricsLine.SecondaryText; PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList(); + IsPrimaryHasRealSyllableInfo = lyricsLine.IsPrimaryHasRealSyllableInfo; } public void UpdateCenterPosition(double maxWidth, TextAlignmentType type) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs index 0af32e5..53a1d32 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Lrc.cs @@ -46,7 +46,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = syllables[0].StartMs, PrimaryText = string.Concat(syllables.Select(s => s.Text)), - PrimarySyllables = syllables + PrimarySyllables = syllables, + IsPrimaryHasRealSyllableInfo = true }); } else @@ -68,19 +69,13 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser content = bracketRegex!.Replace(line, "").Trim(); if (content == "//") content = ""; - lrcLines.Add(new LyricsLine + var lyricsLine = new LyricsLine { StartMs = lineStartMs, - PrimarySyllables = [ - new BaseLyrics - { - StartIndex = 0, - StartMs = lineStartMs, - Text = content - } - ], - PrimaryText = content - }); + PrimaryText = content, + IsPrimaryHasRealSyllableInfo = false + }; + lrcLines.Add(lyricsLine); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs index 06a8050..b59046c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.QrcKrc.cs @@ -22,7 +22,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = lineRead.StartTime ?? 0, PrimaryText = lineRead.Text, - PrimarySyllables = [], + IsPrimaryHasRealSyllableInfo = true, }; var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs index b9ff6f5..f0c6b76 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.Ttml.cs @@ -127,7 +127,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser StartMs = containerStartMs, EndMs = containerEndMs, PrimaryText = fullOriginalText, - PrimarySyllables = syllables + PrimarySyllables = syllables, + IsPrimaryHasRealSyllableInfo = true, }); var transSpan = container.Elements() @@ -151,7 +152,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = startMs, EndMs = endMs, - PrimaryText = text + PrimaryText = text, + IsPrimaryHasRealSyllableInfo = false, }); } else @@ -160,7 +162,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser { StartMs = startMs, EndMs = endMs, - PrimaryText = "" + PrimaryText = "", + IsPrimaryHasRealSyllableInfo = false, }); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs index 5bc61af..42e379c 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Parsers/LyricsParser/LyricsParser.cs @@ -70,6 +70,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser GenerateTransliterationLyricsData(); EnsureEndMs(lyricsSearchResult?.Duration); + EnsureSyllables(); return _lyricsDataArr; } @@ -313,5 +314,44 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser } } + /// + /// Invoke this after + /// + private void EnsureSyllables() + { + foreach (var lyricsData in _lyricsDataArr) + { + if (lyricsData == null) continue; + + var lines = lyricsData.LyricsLines; + if (lines == null) continue; + + foreach (var line in lines) + { + if (line == null) continue; + if (line.IsPrimaryHasRealSyllableInfo) continue; + if (line.PrimarySyllables.Count > 0) continue; + + var content = line.PrimaryText; + var length = content.Length; + if (length == 0) continue; + + var avgSyllableDuration = line.DurationMs / length; + if (avgSyllableDuration == 0) continue; + + for (int j = 0; j < length; j++) + { + line.PrimarySyllables.Add(new BaseLyrics + { + Text = content[j].ToString(), + StartIndex = j, + StartMs = line.StartMs + avgSyllableDuration * j, + EndMs = line.StartMs + avgSyllableDuration * (j + 1), + }); + } + } + } + } + } } From 3357e7aaf424c49dc4c2c894821827df1740d776 Mon Sep 17 00:00:00 2001 From: Zhe Fang Date: Sat, 10 Jan 2026 11:46:26 -0500 Subject: [PATCH 7/7] chores: bump to 256 --- .../BetterLyrics.WinUI3 (Package)/Package.appxmanifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index 951e89b..f995fdb 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.2.256.0" />