mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
feat: float and glow effect now can be adapted to auto word-by-word effect
This commit is contained in:
@@ -19,9 +19,10 @@ namespace BetterLyrics.WinUI3.Extensions
|
|||||||
new LyricsLine
|
new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = 0,
|
StartMs = 0,
|
||||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
EndMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds,
|
||||||
PrimaryText = "● ● ●",
|
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",
|
LanguageCode = "N/A",
|
||||||
|
|||||||
@@ -68,11 +68,17 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
for (int i = safeStart; i <= safeEnd; i++)
|
for (int i = safeStart; i <= safeEnd; i++)
|
||||||
{
|
{
|
||||||
var line = lines[i];
|
var line = lines[i];
|
||||||
|
|
||||||
var lineHeight = line.PrimaryLineHeight;
|
var lineHeight = line.PrimaryLineHeight;
|
||||||
|
|
||||||
if (lineHeight == null || lineHeight <= 0) continue;
|
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
|
double targetCharFloat = lyricsEffect.IsLyricsFloatAnimationAmountAutoAdjust
|
||||||
? lineHeight.Value * 0.1
|
? lineHeight.Value * 0.1
|
||||||
: lyricsEffect.LyricsFloatAnimationAmount;
|
: lyricsEffect.LyricsFloatAnimationAmount;
|
||||||
@@ -187,98 +193,100 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
line.YOffsetTransition.Start(targetYScrollOffset);
|
line.YOffsetTransition.Start(targetYScrollOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSecondaryLinePlayingChanged)
|
if (isWordAnimationEnabled)
|
||||||
{
|
{
|
||||||
// 辉光动画
|
if (isSecondaryLinePlayingChanged)
|
||||||
if (isGlowEnabled && lyricsEffect.LyricsGlowEffectScope == Enums.LyricsEffectScope.LineStartToCurrentChar
|
|
||||||
&& isSecondaryLinePlaying)
|
|
||||||
{
|
{
|
||||||
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;
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
|
|
||||||
renderChar.GlowTransition.Start(
|
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepInOutDuration),
|
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepLastingDuration),
|
|
||||||
new Models.Keyframe<double>(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)
|
|
||||||
{
|
{
|
||||||
if (syllable.DurationMs >= lyricsEffect.LyricsScaleEffectLongSyllableDuration)
|
var stepInOutDuration = Math.Min(Time.AnimationDuration.TotalMilliseconds, maxAnimationDurationMs) / 2.0 / 1000.0;
|
||||||
{
|
var stepLastingDuration = Math.Max(maxAnimationDurationMs / 1000.0 - stepInOutDuration * 2, 0);
|
||||||
var stepDuration = Math.Min(syllable.DurationMs, maxAnimationDurationMs) / 2.0 / 1000.0;
|
|
||||||
renderChar.ScaleTransition.Start(
|
|
||||||
new Models.Keyframe<double>(targetCharScale, stepDuration),
|
|
||||||
new Models.Keyframe<double>(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(
|
renderChar.GlowTransition.Start(
|
||||||
new Models.Keyframe<double>(targetCharGlow, stepDuration),
|
new Models.Keyframe<double>(targetCharGlow, stepInOutDuration),
|
||||||
new Models.Keyframe<double>(0, stepDuration)
|
new Models.Keyframe<double>(targetCharGlow, stepLastingDuration),
|
||||||
|
new Models.Keyframe<double>(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)
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
{
|
{
|
||||||
renderChar.Update(elapsedTime);
|
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<double>(targetCharScale, stepDuration),
|
||||||
|
new Models.Keyframe<double>(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<double>(targetCharGlow, stepDuration),
|
||||||
|
new Models.Keyframe<double>(0, stepDuration)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syllable.IsPlayingLastFrame = isSyllablePlaying;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var renderChar in line.PrimaryRenderChars)
|
||||||
|
{
|
||||||
|
renderChar.Update(elapsedTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line.Update(elapsedTime);
|
line.Update(elapsedTime);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
switch (wordByWordEffectMode)
|
switch (wordByWordEffectMode)
|
||||||
{
|
{
|
||||||
case WordByWordEffectMode.Auto:
|
case WordByWordEffectMode.Auto:
|
||||||
if (line.PrimaryRenderSyllables.Count > 1)
|
if (line.IsPrimaryHasRealSyllableInfo)
|
||||||
{
|
{
|
||||||
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ namespace BetterLyrics.WinUI3.Logic
|
|||||||
state.SyllableProgress = 1f;
|
state.SyllableProgress = 1f;
|
||||||
return state;
|
return state;
|
||||||
case WordByWordEffectMode.Always:
|
case WordByWordEffectMode.Always:
|
||||||
if (line.PrimaryRenderSyllables.Count > 1)
|
if (line.IsPrimaryHasRealSyllableInfo)
|
||||||
{
|
{
|
||||||
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
return CalculateSyllableProgress(currentTimeMs, line, lineEndMs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
public new string Text => PrimaryText;
|
public new string Text => PrimaryText;
|
||||||
public new int StartIndex = 0;
|
public new int StartIndex = 0;
|
||||||
|
|
||||||
|
public bool IsPrimaryHasRealSyllableInfo { get; set; } = false;
|
||||||
|
|
||||||
public LyricsLine()
|
public LyricsLine()
|
||||||
{
|
{
|
||||||
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)
|
for (int charStartIndex = 0; charStartIndex < PrimaryText.Length; charStartIndex++)
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
|
|
||||||
public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height;
|
public double? PrimaryLineHeight => PrimaryRenderChars.FirstOrDefault()?.LayoutRect.Height;
|
||||||
|
|
||||||
|
public bool IsPrimaryHasRealSyllableInfo { get; set; }
|
||||||
|
|
||||||
public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine)
|
public RenderLyricsLine(LyricsLine lyricsLine) : base(lyricsLine)
|
||||||
{
|
{
|
||||||
AngleTransition = new(
|
AngleTransition = new(
|
||||||
@@ -130,6 +132,7 @@ namespace BetterLyrics.WinUI3.Models.Lyrics
|
|||||||
PrimaryText = lyricsLine.PrimaryText;
|
PrimaryText = lyricsLine.PrimaryText;
|
||||||
SecondaryText = lyricsLine.SecondaryText;
|
SecondaryText = lyricsLine.SecondaryText;
|
||||||
PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList();
|
PrimaryRenderSyllables = lyricsLine.PrimarySyllables.Select(x => new RenderLyricsSyllable(x)).ToList();
|
||||||
|
IsPrimaryHasRealSyllableInfo = lyricsLine.IsPrimaryHasRealSyllableInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = syllables[0].StartMs,
|
StartMs = syllables[0].StartMs,
|
||||||
PrimaryText = string.Concat(syllables.Select(s => s.Text)),
|
PrimaryText = string.Concat(syllables.Select(s => s.Text)),
|
||||||
PrimarySyllables = syllables
|
PrimarySyllables = syllables,
|
||||||
|
IsPrimaryHasRealSyllableInfo = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -68,19 +69,13 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
content = bracketRegex!.Replace(line, "").Trim();
|
content = bracketRegex!.Replace(line, "").Trim();
|
||||||
if (content == "//") content = "";
|
if (content == "//") content = "";
|
||||||
|
|
||||||
lrcLines.Add(new LyricsLine
|
var lyricsLine = new LyricsLine
|
||||||
{
|
{
|
||||||
StartMs = lineStartMs,
|
StartMs = lineStartMs,
|
||||||
PrimarySyllables = [
|
PrimaryText = content,
|
||||||
new BaseLyrics
|
IsPrimaryHasRealSyllableInfo = false
|
||||||
{
|
};
|
||||||
StartIndex = 0,
|
lrcLines.Add(lyricsLine);
|
||||||
StartMs = lineStartMs,
|
|
||||||
Text = content
|
|
||||||
}
|
|
||||||
],
|
|
||||||
PrimaryText = content
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = lineRead.StartTime ?? 0,
|
StartMs = lineRead.StartTime ?? 0,
|
||||||
PrimaryText = lineRead.Text,
|
PrimaryText = lineRead.Text,
|
||||||
PrimarySyllables = [],
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
StartMs = containerStartMs,
|
StartMs = containerStartMs,
|
||||||
EndMs = containerEndMs,
|
EndMs = containerEndMs,
|
||||||
PrimaryText = fullOriginalText,
|
PrimaryText = fullOriginalText,
|
||||||
PrimarySyllables = syllables
|
PrimarySyllables = syllables,
|
||||||
|
IsPrimaryHasRealSyllableInfo = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var transSpan = container.Elements()
|
var transSpan = container.Elements()
|
||||||
@@ -151,7 +152,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = startMs,
|
StartMs = startMs,
|
||||||
EndMs = endMs,
|
EndMs = endMs,
|
||||||
PrimaryText = text
|
PrimaryText = text,
|
||||||
|
IsPrimaryHasRealSyllableInfo = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -160,7 +162,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
{
|
{
|
||||||
StartMs = startMs,
|
StartMs = startMs,
|
||||||
EndMs = endMs,
|
EndMs = endMs,
|
||||||
PrimaryText = ""
|
PrimaryText = "",
|
||||||
|
IsPrimaryHasRealSyllableInfo = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
GenerateTransliterationLyricsData();
|
GenerateTransliterationLyricsData();
|
||||||
|
|
||||||
EnsureEndMs(lyricsSearchResult?.Duration);
|
EnsureEndMs(lyricsSearchResult?.Duration);
|
||||||
|
EnsureSyllables();
|
||||||
|
|
||||||
return _lyricsDataArr;
|
return _lyricsDataArr;
|
||||||
}
|
}
|
||||||
@@ -313,5 +314,44 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke this after <see cref="EnsureEndMs"/>
|
||||||
|
/// </summary>
|
||||||
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user