karaoke-dev / karaoke

Will be the best karaoke system.
http://blog.karaoke.dev
GNU General Public License v3.0
204 stars 16 forks source link

Implement the processor for able to switch display the lyric text or romanization text. #2176

Closed andy840119 closed 9 months ago

andy840119 commented 9 months ago

What's the benefit of this change:

  1. All bindable event is wrapped in the processor and will be released if the processor is destroyed.
  2. This ruleset allows user to fill the romanization in the time-tag. I notice that some users create the karaoke song with romaji, should be better to create the Japanese song with romanization.
andy840119 commented 9 months ago

And note that the romanization conversion is not implemented because conversion logic is complex and hard to maintain. Will start implement if user request.

Here's the sample conversion made before:

private static IEnumerable<Tuple<RomanizationInfo, TimeTag?>> getRomanizationInfos(Lyric lyric)
{
    const string space = " ";

    var timeTags = lyric.TimeTags;

    int startCanterTextIndex = 0;

    for (int i = 0; i < timeTags.Count; i++)
    {
        var currentTimeTag = timeTags[i];
        var nextTimeTag = timeTags.GetNext(currentTimeTag);
        if (nextTimeTag == default)
            yield break;

        // get the center text index.
        bool hasEmptySpace = i != 0 && currentTimeTag.FirstSyllable;
        int startCenterTextIndex = startCanterTextIndex + (hasEmptySpace ? 1 : 0);

        var romanizationInfo = createRomanizationInfo(startCenterTextIndex, currentTimeTag, nextTimeTag);
        yield return new Tuple<RomanizationInfo, TimeTag?>(romanizationInfo, currentTimeTag);
        startCanterTextIndex += romanizationInfo.RomanizedSyllable.Length;

        if (!nextTimeTag.FirstSyllable)
            continue;

        // should add extra space if the next time tag is the first syllable.
        var newRomanizationInfo = new RomanizationInfo
        {
            RomanizedSyllableStartCharIndex = startCanterTextIndex,
            RomanizedSyllableEndCharIndex = startCanterTextIndex + space.Length,
            RomanizedSyllable = space,
        };
        yield return new Tuple<RomanizationInfo, TimeTag?>(newRomanizationInfo, null);
        startCanterTextIndex += newRomanizationInfo.RomanizedSyllable.Length;
    }

    yield break;

    static RomanizationInfo createRomanizationInfo(int startIndex, TimeTag currentTimeTag, TimeTag nextTimeTag) =>
        new()
        {
            RomanizedSyllableStartCharIndex = startIndex,
            RomanizedSyllableEndCharIndex = startIndex + currentTimeTag.RomanizedSyllable?.Length ?? 0,
            OriginLyricStartCharIndex = TextIndexUtils.ToCharIndex(currentTimeTag.Index),
            OriginLyricEndCharIndex = TextIndexUtils.ToCharIndex(nextTimeTag.Index),
            RomanizedSyllable = currentTimeTag.RomanizedSyllable ?? string.Empty,
        };
}

private struct RomanizationInfo
{
    public int RomanizedSyllableStartCharIndex { get; set; }

    public int RomanizedSyllableEndCharIndex { get; set; }

    public int OriginLyricStartCharIndex { get; set; }

    public int OriginLyricEndCharIndex { get; set; }

    public string RomanizedSyllable { get; set; }
}

or:

private static IEnumerable<TextIndexWithGapIndex> getGapIndexes(Lyric lyric)
{
    var timeTags = lyric.TimeTags;

    int startCanterTextIndex = 0;

    for (int i = 0; i < timeTags.Count; i++)
    {
        var timeTag = timeTags[i];
        var nextTimeTag = timeTags.GetNext(timeTag);
        if (nextTimeTag == default)
            yield break;

        // get the center text index.
        bool hasEmptySpace = i != 0 && timeTag.FirstSyllable;
        int startCenterTextIndex = startCanterTextIndex + (hasEmptySpace ? 1 : 0);
        int endCenterTextIndex = startCenterTextIndex + timeTag.RomanizedSyllable?.Length ?? 0;

        // get the gap index that
        int startGapIndex = TextIndexUtils.ToGapIndex(timeTag.Index);
        int endGapIndex = TextIndexUtils.ToGapIndex(nextTimeTag.Index);

        yield return new TextIndexWithGapIndex
        {
            TimeTag = timeTag,
            RomanizedSyllableStartCharIndex = startCenterTextIndex,
            RomanizedSyllableEndCharIndex = endCenterTextIndex,
            OriginLyricStartGapIndex = startGapIndex,
            OriginLyricEndGapIndex = endGapIndex,
        };

        startCanterTextIndex = endCenterTextIndex;
    }
}

private struct TextIndexWithGapIndex
{
    public TimeTag TimeTag { get; set; }

    public int? RomanizedSyllableStartCharIndex { get; set; }

    public int? RomanizedSyllableEndCharIndex { get; set; }

    public string RomanizedSyllable => TimeTag.RomanizedSyllable ?? string.Empty;

    public int OriginLyricStartGapIndex { get; set; }

    public int OriginLyricEndGapIndex { get; set; }
}
andy840119 commented 9 months ago

先這樣 Config 之後可能需要調整,或許根據不同的歌曲會有不同設定 或是轉換成 mod (mod 會比 config 容易設定,並且也可以從遊玩去猜出 user 是不是本地人)