From 9b4c9ad093900d8a7c8fc60d07428798563eb8f7 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 9 Aug 2024 22:03:04 +0800 Subject: [PATCH 1/9] Fix the typo. --- .../Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs index 59bf2f47b..216f29944 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs @@ -82,7 +82,7 @@ protected override void ParseLine(Beatmap beatmap, Section section, string line) writer.Flush(); stream.Position = 0; - // Create lec decoder + // Create lrc decoder var decoder = new LrcDecoder(); var lrcBeatmap = decoder.Decode(reader); From a16cd7acd3155fc1571624320ba1a3e5c60cdf36 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 9 Aug 2024 22:02:55 +0800 Subject: [PATCH 2/9] Implement the utils to convert the song into the beatmap. --- .../Beatmaps/Formats/LrcEncoderTest.cs | 20 +-- .../Integration/Formats/LrcParserUtilsTest.cs | 30 +++++ .../Beatmaps/Formats/LrcDecoder.cs | 55 +------- .../Beatmaps/Formats/LrcEncoder.cs | 73 +---------- .../Integration/Formats/LrcParserUtils.cs | 124 ++++++++++++++++++ 5 files changed, 163 insertions(+), 139 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcParserUtilsTest.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/LrcParserUtils.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs index 6e0fd8583..460d3e133 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs @@ -10,8 +10,8 @@ using osu.Game.IO; using osu.Game.IO.Serialization; using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; -using osu.Game.Rulesets.Karaoke.Tests.Helper; using osu.Game.Rulesets.Karaoke.Tests.Resources; +using LrcDecoder = osu.Game.Rulesets.Karaoke.Beatmaps.Formats.LrcDecoder; namespace osu.Game.Rulesets.Karaoke.Tests.Beatmaps.Formats; @@ -53,22 +53,4 @@ private static Beatmap decode(string filename, out Beatmap encoded) encoded = new LrcDecoder().Decode(sr2); return legacyDecoded; } - - [TestCase(new[] { "[0,start]:1100", "[0,end]:2000", "[1,start]:2100", "[1,end]:3000" }, new double[] { 1100, 2000, 2100, 3000 })] - [TestCase(new[] { "[1,end]:3000", "[1,start]:2100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000, 2100, 3000 })] - [TestCase(new[] { "[0,start]", "[0,start]", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000 })] - [TestCase(new[] { "[0,start]:1000", "[0,start]:1100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1000, 2000 })] - [TestCase(new[] { "[0,start]", "[0,end]", "[0,start]", "[1,start]", "[1,end]" }, new double[] { })] - [TestCase(new[] { "[0,start]:2000", "[0,end]:1000" }, new double[] { 2000, 2000 })] - [TestCase(new[] { "[0,start]:1100", "[0,end]:2100", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1100, 2100, 2100, 3000 })] - [TestCase(new[] { "[0,start]:1000", "[0,end]:5000", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1000, 5000, 5000, 5000 })] - [TestCase(new[] { "[0,start]:1000", "[0,end]:2000", "[1,start]:0", "[1,end]:3000" }, new double[] { 1000, 2000, 2000, 3000 })] - //[TestCase(new[] { "[0,start]:4000", "[0,end]:3000", "[1,start]:2000", "[1,end]:1000" }, new double[] { 4000, 4000, 4000, 4000 })] - public void TestToDictionary(string[] timeTagTexts, double[] expected) - { - var timeTags = TestCaseTagHelper.ParseTimeTags(timeTagTexts); - - double[] actual = LrcEncoder.ToDictionary(timeTags).Values.ToArray(); - Assert.AreEqual(expected, actual); - } } diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcParserUtilsTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcParserUtilsTest.cs new file mode 100644 index 000000000..d6445329c --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcParserUtilsTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Tests.Helper; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class LrcParserUtilsTest +{ + [TestCase(new[] { "[0,start]:1100", "[0,end]:2000", "[1,start]:2100", "[1,end]:3000" }, new double[] { 1100, 2000, 2100, 3000 })] + [TestCase(new[] { "[1,end]:3000", "[1,start]:2100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000, 2100, 3000 })] + [TestCase(new[] { "[0,start]", "[0,start]", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1100, 2000 })] + [TestCase(new[] { "[0,start]:1000", "[0,start]:1100", "[0,end]:2000", "[0,start]:1100" }, new double[] { 1000, 2000 })] + [TestCase(new[] { "[0,start]", "[0,end]", "[0,start]", "[1,start]", "[1,end]" }, new double[] { })] + [TestCase(new[] { "[0,start]:2000", "[0,end]:1000" }, new double[] { 2000, 2000 })] + [TestCase(new[] { "[0,start]:1100", "[0,end]:2100", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1100, 2100, 2100, 3000 })] + [TestCase(new[] { "[0,start]:1000", "[0,end]:5000", "[1,start]:2000", "[1,end]:3000" }, new double[] { 1000, 5000, 5000, 5000 })] + [TestCase(new[] { "[0,start]:1000", "[0,end]:2000", "[1,start]:0", "[1,end]:3000" }, new double[] { 1000, 2000, 2000, 3000 })] + //[TestCase(new[] { "[0,start]:4000", "[0,end]:3000", "[1,start]:2000", "[1,end]:1000" }, new double[] { 4000, 4000, 4000, 4000 })] + public void TestToDictionary(string[] timeTagTexts, double[] expected) + { + var timeTags = TestCaseTagHelper.ParseTimeTags(timeTagTexts); + + double[] actual = LrcParserUtils.ToDictionary(timeTags).Values.ToArray(); + Assert.AreEqual(expected, actual); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs index 4a9fa35b8..d30ae957e 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs @@ -1,16 +1,10 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; -using LrcParser.Model; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; -using osu.Game.Rulesets.Karaoke.Objects; -using osu.Game.Rulesets.Karaoke.Objects.Utils; -using Lyric = osu.Game.Rulesets.Karaoke.Objects.Lyric; -using RubyTag = osu.Game.Rulesets.Karaoke.Objects.RubyTag; +using osu.Game.Rulesets.Karaoke.Integration.Formats; namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; @@ -24,52 +18,13 @@ public static void Register() protected override void ParseStreamInto(LineBufferedReader stream, Beatmap output) { - // Clear all hitobjects - output.HitObjects.Clear(); - string lyricText = stream.ReadToEnd(); - var result = new LrcParser.Parser.Lrc.LrcParser().Decode(lyricText); + var song = new LrcParser.Parser.Lrc.LrcParser().Decode(lyricText); - var newLyrics = result.Lyrics.Select(lrcLyric => - { - var lrcTimeTags = lrcLyric.TimeTags.Select(convertTimeTag).ToArray(); - var lrcRubies = lrcLyric.RubyTags.Select(convertRubyTag).ToArray(); - var lrcRubyTimeTags = lrcLyric.RubyTags.Select(convertTimeTagsFromRubyTags).SelectMany(x => x).ToArray(); - - return new Lyric - { - Order = output.HitObjects.Count + 1, // should create default order. - Text = lrcLyric.Text, - TimeTags = TimeTagsUtils.Sort(lrcTimeTags.Concat(lrcRubyTimeTags)), - RubyTags = lrcRubies, - }; - }); + var newLyrics = LrcParserUtils.ConvertToLyrics(song); + // Clear all hitobjects + output.HitObjects.Clear(); output.HitObjects.AddRange(newLyrics); - - static TimeTag convertTimeTag(KeyValuePair timeTag) - => new(convertTextIndex(timeTag.Key), timeTag.Value); - - static Framework.Graphics.Sprites.TextIndex convertTextIndex(TextIndex textIndex) - { - int index = textIndex.Index; - var state = textIndex.State == IndexState.Start ? Framework.Graphics.Sprites.TextIndex.IndexState.Start : Framework.Graphics.Sprites.TextIndex.IndexState.End; - - return new Framework.Graphics.Sprites.TextIndex(index, state); - } - - static RubyTag convertRubyTag(LrcParser.Model.RubyTag rubyTag) - => new() - { - Text = rubyTag.Text, - StartIndex = rubyTag.StartCharIndex, - EndIndex = rubyTag.EndCharIndex, - }; - - static TimeTag[] convertTimeTagsFromRubyTags(LrcParser.Model.RubyTag rubyTag) - { - int startIndex = rubyTag.StartCharIndex; - return rubyTag.TimeTags.Select(x => convertTimeTag(new KeyValuePair(new TextIndex(startIndex), x.Value))).ToArray(); - } } } diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs index 056839c2f..522828fa6 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs @@ -1,17 +1,8 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Linq; -using LrcParser.Model; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Karaoke.Objects; -using osu.Game.Rulesets.Karaoke.Objects.Utils; -using osu.Game.Rulesets.Karaoke.Utils; -using Lyric = osu.Game.Rulesets.Karaoke.Objects.Lyric; -using RubyTag = LrcParser.Model.RubyTag; -using TextIndex = osu.Framework.Graphics.Sprites.TextIndex; +using osu.Game.Rulesets.Karaoke.Integration.Formats; namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; @@ -19,65 +10,7 @@ public class LrcEncoder { public string Encode(Beatmap output) { - // Note : save to lyric will lost some tags with no value. - var song = new Song - { - Lyrics = output.HitObjects.OfType().Select(encodeLyric).ToList(), - }; - string encodeResult = new LrcParser.Parser.Lrc.LrcParser().Encode(song); - return encodeResult; - - static LrcParser.Model.Lyric encodeLyric(Lyric lyric) => - new() - { - Text = lyric.Text, - TimeTags = convertTimeTag(lyric.TimeTags), - RubyTags = convertRubyTag(lyric.RubyTags), - }; - - static SortedDictionary convertTimeTag(IList timeTags) - { - // Note : save to lyric will lost some tags with duplicated index. - var timeTagDictionary = ToDictionary(timeTags).ToDictionary(k => convertTextIndex(k.Key), v => (int?)v.Value); - return new SortedDictionary(timeTagDictionary); - } - - static LrcParser.Model.TextIndex convertTextIndex(TextIndex textIndex) - { - int index = textIndex.Index; - var state = TextIndexUtils.GetValueByState(textIndex, IndexState.Start, IndexState.End); - - return new LrcParser.Model.TextIndex(index, state); - } - - static List convertRubyTag(IEnumerable rubyTags) - => rubyTags.Select(x => new RubyTag - { - Text = x.Text, - StartCharIndex = x.StartIndex, - EndCharIndex = x.EndIndex, - }).ToList(); - } - - /// - /// Convert list of time tag to dictionary. - /// - /// Time tags - /// Should auto-fix or not - /// Fix way - /// Fix way - /// Time tags with dictionary format. - internal static IReadOnlyDictionary ToDictionary(IList timeTags, bool applyFix = true, GroupCheck other = GroupCheck.Asc, - SelfCheck self = SelfCheck.BasedOnStart) - { - // sorted value - var sortedTimeTags = applyFix ? TimeTagsUtils.FixOverlapping(timeTags, other, self) : TimeTagsUtils.Sort(timeTags); - - // convert to dictionary, will get start's smallest time and end's largest time. - return sortedTimeTags.Where(x => x.Time != null).GroupBy(x => x.Index) - .Select(x => TextIndexUtils.GetValueByState(x.Key, x.FirstOrDefault, x.LastOrDefault)) - .ToDictionary( - k => k?.Index ?? throw new ArgumentNullException(nameof(k)), - v => v?.Time ?? throw new ArgumentNullException(nameof(v))); + var song = LrcParserUtils.ConvertToSong(output); + return new LrcParser.Parser.Lrc.LrcParser().Encode(song); } } diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcParserUtils.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcParserUtils.cs new file mode 100644 index 000000000..704e8fd4f --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcParserUtils.cs @@ -0,0 +1,124 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using LrcParser.Model; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Utils; +using osu.Game.Rulesets.Karaoke.Utils; +using Lyric = osu.Game.Rulesets.Karaoke.Objects.Lyric; +using RubyTag = osu.Game.Rulesets.Karaoke.Objects.RubyTag; +using TextIndex = osu.Framework.Graphics.Sprites.TextIndex; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class LrcParserUtils +{ + public static Song ConvertToSong(Beatmap beatmap) + { + // Note : save to lyric will lost some tags with no value. + return new Song + { + Lyrics = beatmap.HitObjects.OfType().Select(encodeLyric).ToList(), + }; + + static LrcParser.Model.Lyric encodeLyric(Lyric lyric) => + new() + { + Text = lyric.Text, + TimeTags = convertTimeTag(lyric.TimeTags), + RubyTags = convertRubyTag(lyric.RubyTags), + }; + + static SortedDictionary convertTimeTag(IList timeTags) + { + // Note : save to lyric will lost some tags with duplicated index. + var timeTagDictionary = ToDictionary(timeTags).ToDictionary(k => convertTextIndex(k.Key), v => (int?)v.Value); + return new SortedDictionary(timeTagDictionary); + } + + static LrcParser.Model.TextIndex convertTextIndex(TextIndex textIndex) + { + int index = textIndex.Index; + var state = TextIndexUtils.GetValueByState(textIndex, IndexState.Start, IndexState.End); + + return new LrcParser.Model.TextIndex(index, state); + } + + static List convertRubyTag(IEnumerable rubyTags) + => rubyTags.Select(x => new LrcParser.Model.RubyTag + { + Text = x.Text, + StartCharIndex = x.StartIndex, + EndCharIndex = x.EndIndex, + }).ToList(); + } + + /// + /// Convert list of time tag to dictionary. + /// + /// Time tags + /// Should auto-fix or not + /// Fix way + /// Fix way + /// Time tags with dictionary format. + internal static IReadOnlyDictionary ToDictionary(IList timeTags, bool applyFix = true, GroupCheck other = GroupCheck.Asc, + SelfCheck self = SelfCheck.BasedOnStart) + { + // sorted value + var sortedTimeTags = applyFix ? TimeTagsUtils.FixOverlapping(timeTags, other, self) : TimeTagsUtils.Sort(timeTags); + + // convert to dictionary, will get start's smallest time and end's largest time. + return sortedTimeTags.Where(x => x.Time != null).GroupBy(x => x.Index) + .Select(x => TextIndexUtils.GetValueByState(x.Key, x.FirstOrDefault, x.LastOrDefault)) + .ToDictionary( + k => k?.Index ?? throw new ArgumentNullException(nameof(k)), + v => v?.Time ?? throw new ArgumentNullException(nameof(v))); + } + + public static IEnumerable ConvertToLyrics(Song song) + { + return song.Lyrics.Select((lrcLyric, index) => + { + var lrcTimeTags = lrcLyric.TimeTags.Select(convertTimeTag).ToArray(); + var lrcRubies = lrcLyric.RubyTags.Select(convertRubyTag).ToArray(); + var lrcRubyTimeTags = lrcLyric.RubyTags.Select(convertTimeTagsFromRubyTags).SelectMany(x => x).ToArray(); + + return new Lyric + { + Order = index + 1, // should create default order. + Text = lrcLyric.Text, + TimeTags = TimeTagsUtils.Sort(lrcTimeTags.Concat(lrcRubyTimeTags)), + RubyTags = lrcRubies, + }; + }); + + static TimeTag convertTimeTag(KeyValuePair timeTag) + => new(convertTextIndex(timeTag.Key), timeTag.Value); + + static TextIndex convertTextIndex(LrcParser.Model.TextIndex textIndex) + { + int index = textIndex.Index; + var state = textIndex.State == IndexState.Start ? Framework.Graphics.Sprites.TextIndex.IndexState.Start : Framework.Graphics.Sprites.TextIndex.IndexState.End; + + return new TextIndex(index, state); + } + + static RubyTag convertRubyTag(LrcParser.Model.RubyTag rubyTag) + => new() + { + Text = rubyTag.Text, + StartIndex = rubyTag.StartCharIndex, + EndIndex = rubyTag.EndCharIndex, + }; + + static TimeTag[] convertTimeTagsFromRubyTags(LrcParser.Model.RubyTag rubyTag) + { + int startIndex = rubyTag.StartCharIndex; + return rubyTag.TimeTags.Select(x => convertTimeTag(new KeyValuePair(new LrcParser.Model.TextIndex(startIndex), x.Value))).ToArray(); + } + } +} From f3eeeaffc153420b7dbb505d6180327604c73fed Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 9 Aug 2024 22:22:42 +0800 Subject: [PATCH 3/9] Upgrade the pacakge. --- osu.Game.Rulesets.Karaoke/osu.Game.Rulesets.Karaoke.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Karaoke/osu.Game.Rulesets.Karaoke.csproj b/osu.Game.Rulesets.Karaoke/osu.Game.Rulesets.Karaoke.csproj index c8b1a92a8..47102df89 100644 --- a/osu.Game.Rulesets.Karaoke/osu.Game.Rulesets.Karaoke.csproj +++ b/osu.Game.Rulesets.Karaoke/osu.Game.Rulesets.Karaoke.csproj @@ -9,7 +9,7 @@ - + From 81101857487b0a437d7e93af1434e10cd03cb280 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 9 Aug 2024 23:02:53 +0800 Subject: [PATCH 4/9] Implement the encoder and decoder for lrc/kar format. --- .../Integration/Formats/KarDecoderTest.cs | 54 +++++++++++++++++++ .../Integration/Formats/KarEncoderTest.cs | 36 +++++++++++++ .../Integration/Formats/LrcDecoderTest.cs | 36 +++++++++++++ .../Integration/Formats/LrcEncoderTest.cs | 54 +++++++++++++++++++ .../Integration/Formats/IDecoder.cs | 11 ++++ .../Integration/Formats/IEncoder.cs | 11 ++++ .../Integration/Formats/KarDecoder.cs | 16 ++++++ .../Integration/Formats/KarEncoder.cs | 15 ++++++ .../Integration/Formats/LrcDecoder.cs | 16 ++++++ .../Integration/Formats/LrcEncoder.cs | 15 ++++++ 10 files changed, 264 insertions(+) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarDecoderTest.cs create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarEncoderTest.cs create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcDecoderTest.cs create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcEncoderTest.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/IDecoder.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/IEncoder.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/KarDecoder.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/KarEncoder.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/LrcDecoder.cs create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/LrcEncoder.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarDecoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarDecoderTest.cs new file mode 100644 index 000000000..845b8339d --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarDecoderTest.cs @@ -0,0 +1,54 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Tests.Asserts; +using osu.Game.Rulesets.Karaoke.Tests.Helper; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class KarDecoderTest +{ + [TestCase("[00:01.00]か[00:02.00]ら[00:03.00]お[00:04.00]け[00:05.00]", "からおけ", 1000, 5000)] + public void TestLyricTextAndTime(string lyricText, string expectedText, double expectedStartTime, double expectedEndTime) + { + // Get first lyric from beatmap + var lyrics = new KarDecoder().Decode(lyricText); + var actual = lyrics.FirstOrDefault()!; + + Assert.IsNotNull(actual); + Assert.AreEqual(expectedText, actual.Text); + Assert.AreEqual(expectedStartTime, actual.LyricTimingInfo?.StartTime); + Assert.AreEqual(expectedEndTime, actual.LyricTimingInfo?.EndTime); + } + + [TestCase("[00:01.00]か[00:02.00]ら[00:03.00]お[00:04.00]け[00:05.00]", new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] + public void TestLyricTimeTag(string text, string[] timeTags) + { + // Get first lyric from beatmap + var lyrics = new KarDecoder().Decode(text); + var lyric = lyrics.FirstOrDefault()!; + + // Check time tag + var expected = TestCaseTagHelper.ParseTimeTags(timeTags); + var actual = lyric?.TimeTags ?? throw new ArgumentNullException(nameof(lyric)); + TimeTagAssert.ArePropertyEqual(expected, actual); + } + + [Ignore("Time-tags with same time might be allowed.")] + [TestCase("[00:04.00]か[00:04.00]ら[00:05.00]お[00:06.00]け[00:07.00]")] + public void TestDecodeLyricWithDuplicatedTimeTag(string text) + { + Assert.Throws(() => new KarDecoder().Decode(text)); + } + + [Ignore("Waiting for lyric parser update.")] + [TestCase("[00:04.00]か[00:03.00]ら[00:02.00]お[00:01.00]け[00:00.00]")] + public void TestDecodeLyricWithTimeTagNotOrder(string text) + { + Assert.Throws(() => new KarDecoder().Decode(text)); + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarEncoderTest.cs new file mode 100644 index 000000000..c943f89ff --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarEncoderTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Tests.Helper; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class KarEncoderTest +{ + [TestCase("からおけ", new string[] { }, "からおけ")] + [TestCase("からおけ", new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" }, "[00:01.00]か[00:02.00]ら[00:03.00]お[00:04.00]け[00:05.00]")] + public void TestLyricWithTimeTag(string lyricText, string[] timeTags, string expected) + { + var lyric = new Lyric + { + Text = lyricText, + TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), + }; + + string actual = new KarEncoder().Encode(new Beatmap + { + HitObjects = new List + { + lyric, + }, + }); + + Assert.AreEqual(actual, expected); + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcDecoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcDecoderTest.cs new file mode 100644 index 000000000..508781791 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcDecoderTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Tests.Helper; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class LrcDecoderTest +{ + [TestCase("からおけ", new string[] { }, "[00:00.00] からおけ")] // todo: handle the start time. + [TestCase("からおけ", new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" }, "[00:00.00] <00:01.00>か<00:02.00>ら<00:03.00>お<00:04.00>け<00:05.00>")] + public void TestLyricWithTimeTag(string lyricText, string[] timeTags, string expected) + { + var lyric = new Lyric + { + Text = lyricText, + TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags), + }; + + string actual = new LrcEncoder().Encode(new Beatmap + { + HitObjects = new List + { + lyric, + }, + }); + + Assert.AreEqual(actual, expected); + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcEncoderTest.cs new file mode 100644 index 000000000..9215c99a4 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LrcEncoderTest.cs @@ -0,0 +1,54 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Tests.Asserts; +using osu.Game.Rulesets.Karaoke.Tests.Helper; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class LrcEncoderTest +{ + [TestCase("[00:01.00]<00:01.00>か<00:02.00>ら<00:03.00>お<00:04.00>け<00:05.00>", "からおけ", 1000, 5000)] + public void TestLyricTextAndTime(string lyricText, string expectedText, double expectedStartTime, double expectedEndTime) + { + // Get first lyric from beatmap + var lyrics = new LrcDecoder().Decode(lyricText); + var actual = lyrics.FirstOrDefault()!; + + Assert.IsNotNull(actual); + Assert.AreEqual(expectedText, actual.Text); + Assert.AreEqual(expectedStartTime, actual.LyricTimingInfo?.StartTime); + Assert.AreEqual(expectedEndTime, actual.LyricTimingInfo?.EndTime); + } + + [TestCase("[00:01.00]<00:01.00>か<00:02.00>ら<00:03.00>お<00:04.00>け<00:05.00>", new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] + public void TestLyricTimeTag(string text, string[] timeTags) + { + // Get first lyric from beatmap + var lyrics = new LrcDecoder().Decode(text); + var lyric = lyrics.FirstOrDefault()!; + + // Check time tag + var expected = TestCaseTagHelper.ParseTimeTags(timeTags); + var actual = lyric?.TimeTags ?? throw new ArgumentNullException(nameof(lyric)); + TimeTagAssert.ArePropertyEqual(expected, actual); + } + + [Ignore("Time-tags with same time might be allowed.")] + [TestCase("[00:04.00]<00:04.00>か<00:04.00>ら<00:05.00>お<00:06.00>け<00:07.00>")] + public void TestDecodeLyricWithDuplicatedTimeTag(string text) + { + Assert.Throws(() => new LrcDecoder().Decode(text)); + } + + [Ignore("Waiting for lyric parser update.")] + [TestCase("[00:04.00]<00:04.00>か<00:03.00>ら<00:02.00>お<00:01.00>け<00:00.00>")] + public void TestDecodeLyricWithTimeTagNotOrder(string text) + { + Assert.Throws(() => new LrcDecoder().Decode(text)); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/IDecoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/IDecoder.cs new file mode 100644 index 000000000..d863c6e33 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/IDecoder.cs @@ -0,0 +1,11 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public interface IDecoder : IDecoder; + +public interface IDecoder +{ + public TTargetFormat Decode(TSourceFormat source); +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/IEncoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/IEncoder.cs new file mode 100644 index 000000000..19215fbbf --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/IEncoder.cs @@ -0,0 +1,11 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public interface IEncoder : IEncoder; + +public interface IEncoder +{ + public TTargetFormat Encode(TSourceFormat source); +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/KarDecoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/KarDecoder.cs new file mode 100644 index 000000000..fdb69c1f8 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/KarDecoder.cs @@ -0,0 +1,16 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Karaoke.Objects; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class KarDecoder : IDecoder +{ + public Lyric[] Decode(string source) + { + var song = new LrcParser.Parser.Kar.KarParser().Decode(source); + return LrcParserUtils.ConvertToLyrics(song).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/KarEncoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/KarEncoder.cs new file mode 100644 index 000000000..9321505d1 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/KarEncoder.cs @@ -0,0 +1,15 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class KarEncoder : IEncoder +{ + public string Encode(Beatmap source) + { + var song = LrcParserUtils.ConvertToSong(source); + return new LrcParser.Parser.Kar.KarParser().Encode(song); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcDecoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcDecoder.cs new file mode 100644 index 000000000..12dedb208 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcDecoder.cs @@ -0,0 +1,16 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Karaoke.Objects; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class LrcDecoder : IDecoder +{ + public Lyric[] Decode(string source) + { + var song = new LrcParser.Parser.Lrc.LrcParser().Decode(source); + return LrcParserUtils.ConvertToLyrics(song).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcEncoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcEncoder.cs new file mode 100644 index 000000000..69e589518 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/LrcEncoder.cs @@ -0,0 +1,15 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class LrcEncoder : IEncoder +{ + public string Encode(Beatmap source) + { + var song = LrcParserUtils.ConvertToSong(source); + return new LrcParser.Parser.Lrc.LrcParser().Encode(song); + } +} From d7f386abca4f2d69edebacdd278d399f2fc91a06 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 9 Aug 2024 23:43:31 +0800 Subject: [PATCH 5/9] Rename all the resource from .lrc to .kar --- .../Beatmaps/Formats/LrcEncoderTest.cs | 2 +- .../Resources/TestResources.cs | 8 ++++---- .../Testing/{Lrc/default.lrc => Kar/default.kar} | 0 .../Resources/Testing/{Lrc/light.lrc => Kar/light.kar} | 0 .../Screens/Edit/Import/TestSceneLyricImporter.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/{Lrc/default.lrc => Kar/default.kar} (100%) rename osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/{Lrc/light.lrc => Kar/light.kar} (100%) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs index 460d3e133..e7ae014fe 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs @@ -33,7 +33,7 @@ public void TestDecodeEncodedBeatmap(string fileName) private static Beatmap decode(string filename, out Beatmap encoded) { - using var stream = TestResources.OpenLrcResource(filename); + using var stream = TestResources.OpenKarResource(filename); using var sr = new LineBufferedReader(stream); // Read file and decode to file diff --git a/osu.Game.Rulesets.Karaoke.Tests/Resources/TestResources.cs b/osu.Game.Rulesets.Karaoke.Tests/Resources/TestResources.cs index fa77e3166..0efcef204 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Resources/TestResources.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Resources/TestResources.cs @@ -24,13 +24,13 @@ public static class TestResources public static Stream OpenSkinResource(string name) => OpenResource($"Testing/Skin/{name}.skin"); - public static Stream OpenLrcResource(string name) => OpenResource($"Testing/Lrc/{name}.lrc"); + public static Stream OpenKarResource(string name) => OpenResource($"Testing/Kar/{name}.kar"); - public static string GetTestLrcForImport(string name) + public static string GetTestKarForImport(string name) { - string tempPath = Path.GetTempFileName() + ".lrc"; + string tempPath = Path.GetTempFileName() + ".kar"; - using (var stream = OpenLrcResource(name)) + using (var stream = OpenKarResource(name)) using (var newFile = File.Create(tempPath)) stream.CopyTo(newFile); diff --git a/osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Lrc/default.lrc b/osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Kar/default.kar similarity index 100% rename from osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Lrc/default.lrc rename to osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Kar/default.kar diff --git a/osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Lrc/light.lrc b/osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Kar/light.kar similarity index 100% rename from osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Lrc/light.lrc rename to osu.Game.Rulesets.Karaoke.Tests/Resources/Testing/Kar/light.kar diff --git a/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Import/TestSceneLyricImporter.cs b/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Import/TestSceneLyricImporter.cs index 8f5b5a3c1..818a961d2 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Import/TestSceneLyricImporter.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Import/TestSceneLyricImporter.cs @@ -31,7 +31,7 @@ public partial class TestSceneLyricImporter : ScreenTestScene Date: Fri, 9 Aug 2024 23:49:31 +0800 Subject: [PATCH 6/9] Add the test case to test all local .kar file in the test case. --- .../Integration/Formats/KarFileTest.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarFileTest.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarFileTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarFileTest.cs new file mode 100644 index 000000000..04a2d696e --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/KarFileTest.cs @@ -0,0 +1,61 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.IO; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Tests.Resources; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class KarFileTest +{ + private static IEnumerable allKarFileNames => TestResources.GetStore().GetAvailableResources() + .Where(res => res.EndsWith(".kar", StringComparison.Ordinal)).Select(x => Path.GetFileNameWithoutExtension(x!)); + + [TestCaseSource(nameof(allKarFileNames))] + public void TestDecodeEncodedBeatmap(string fileName) + { + var decoded = decode(fileName, out var encoded); + + // Note : this test case does not cover ruby property + Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); + Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); + } + + private static Beatmap decode(string filename, out Beatmap encoded) + { + using var stream = TestResources.OpenKarResource(filename); + using var sr = new LineBufferedReader(stream); + + // Read file and decode to file + var legacyDecoded = new Beatmap + { + HitObjects = new KarDecoder().Decode(sr.ReadToEnd()).OfType().ToList(), + }; + + using var ms = new MemoryStream(); + using var sw = new StreamWriter(ms); + using var sr2 = new LineBufferedReader(ms); + + // Then encode file to stream + string encodeResult = new KarEncoder().Encode(legacyDecoded); + sw.WriteLine(encodeResult); + sw.Flush(); + + ms.Position = 0; + + encoded = new Beatmap + { + HitObjects = new KarDecoder().Decode(sr2.ReadToEnd()).OfType().ToList(), + }; + return legacyDecoded; + } +} From f9823b13b58d8c35bbbfeda0aee985602a304e8a Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Aug 2024 00:12:26 +0800 Subject: [PATCH 7/9] Move `LyricTextEncoder` and `LyricTextDecoder` to better location. --- .../Formats/LyricTextDecoderTest.cs | 27 +++++++++++++++++++ .../Formats/LyricTextEncoderTest.cs | 4 +-- .../Integration/Formats/LyricTextDecoder.cs | 19 +++++++++++++ .../Formats/LyricTextEncoder.cs | 4 +-- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextDecoderTest.cs rename osu.Game.Rulesets.Karaoke.Tests/{Beatmaps => Integration}/Formats/LyricTextEncoderTest.cs (89%) create mode 100644 osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextDecoder.cs rename osu.Game.Rulesets.Karaoke/{Beatmaps => Integration}/Formats/LyricTextEncoder.cs (82%) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextDecoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextDecoderTest.cs new file mode 100644 index 000000000..5cf202c15 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextDecoderTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Integration.Formats; +using osu.Game.Rulesets.Karaoke.Tests.Helper; + +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; + +public class LyricTextDecoderTest +{ + [TestCase("karaoke", new[] { "[0,0]:karaoke" })] // only one lyric. + [TestCase("か\nら\nお\nけ", new[] { "[0,0]:か", "[0,0]:ら", "[0,0]:お", "[0,0]:け" })] // multi lyric. + public void TestDecodeBeatmapToPureText(string expected, string[] lyrics) + { + var decoder = new LyricTextDecoder(); + var actual = decoder.Decode(expected); + + var expectedLyrics = TestCaseTagHelper.ParseLyrics(lyrics); + Assert.AreEqual(expectedLyrics.Length, actual.Length); + + for (int i = 0; i < expectedLyrics.Length; i++) + { + Assert.AreEqual(expectedLyrics[i].Text, actual[i].Text); + } + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LyricTextEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextEncoderTest.cs similarity index 89% rename from osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LyricTextEncoderTest.cs rename to osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextEncoderTest.cs index ca4e29b00..f57d783f3 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LyricTextEncoderTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Integration/Formats/LyricTextEncoderTest.cs @@ -4,11 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Game.Rulesets.Karaoke.Beatmaps; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Tests.Helper; -namespace osu.Game.Rulesets.Karaoke.Tests.Beatmaps.Formats; +namespace osu.Game.Rulesets.Karaoke.Tests.Integration.Formats; [TestFixture] public class LyricTextEncoderTest diff --git a/osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextDecoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextDecoder.cs new file mode 100644 index 000000000..bb162e472 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextDecoder.cs @@ -0,0 +1,19 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Karaoke.Objects; + +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; + +public class LyricTextDecoder : IDecoder +{ + public Lyric[] Decode(string source) + { + return source.Split('\n').Select((text, index) => new Lyric + { + Order = index + 1, // should create default order. + Text = text, + }).ToArray(); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LyricTextEncoder.cs b/osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextEncoder.cs similarity index 82% rename from osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LyricTextEncoder.cs rename to osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextEncoder.cs index 4ad6c2960..e4c14d7c0 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LyricTextEncoder.cs +++ b/osu.Game.Rulesets.Karaoke/Integration/Formats/LyricTextEncoder.cs @@ -5,9 +5,9 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Karaoke.Objects; -namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; +namespace osu.Game.Rulesets.Karaoke.Integration.Formats; -public class LyricTextEncoder +public class LyricTextEncoder : IEncoder { public string Encode(IBeatmap output) { From 2e116b1ad396a94f02bee4be5498a36d6db93512 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Aug 2024 00:05:10 +0800 Subject: [PATCH 8/9] Use KarEncoder/KarDecoder instead. Also, rename some naming from .lrc to .kar --- .../KaraokeLegacyBeatmapDecoderTest.cs | 4 +-- .../Helper/TestCaseTagHelper.cs | 17 ++------- .../Edit/Beatmap/TestSceneEditorMenuBar.cs | 4 +-- .../Formats/KaraokeLegacyBeatmapDecoder.cs | 35 +++++++------------ .../Formats/KaraokeLegacyBeatmapEncoder.cs | 5 +-- .../Edit/Export/ExportLyricManager.cs | 10 +++--- .../Edit/Beatmaps/KaraokeBeatmapEditor.cs | 4 +-- .../Lyrics/DragFile/DragFileStepScreen.cs | 2 +- .../Edit/Import/Lyrics/ImportLyricManager.cs | 32 +++++++++++------ 9 files changed, 53 insertions(+), 60 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/KaraokeLegacyBeatmapDecoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/KaraokeLegacyBeatmapDecoderTest.cs index 52fb001b1..22a1f91a4 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/KaraokeLegacyBeatmapDecoderTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/KaraokeLegacyBeatmapDecoderTest.cs @@ -113,10 +113,10 @@ private static KaraokeBeatmap decodeBeatmap(string fileName) using var stream = new LineBufferedReader(resStream); // Create karaoke beatmap decoder - var lrcDecoder = new KaraokeLegacyBeatmapDecoder(); + var decoder = new KaraokeLegacyBeatmapDecoder(); // Create initial beatmap - var beatmap = lrcDecoder.Decode(stream); + var beatmap = decoder.Decode(stream); // Convert to karaoke beatmap return (KaraokeBeatmap)new KaraokeBeatmapConverter(beatmap, new KaraokeRuleset()).Convert(); diff --git a/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs b/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs index c1b62837f..1ae610cac 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Helper/TestCaseTagHelper.cs @@ -3,15 +3,13 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Graphics.Sprites; -using osu.Game.IO; using osu.Game.Rulesets.Karaoke.Beatmaps; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; using osu.Game.Rulesets.Karaoke.Beatmaps.Metadatas; using osu.Game.Rulesets.Karaoke.Extensions; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Tests.Extensions; @@ -203,18 +201,9 @@ public static Lyric ParseLyricWithTimeTag(string? str) if (string.IsNullOrEmpty(str)) return new Lyric(); - using var stream = new MemoryStream(); - using var writer = new StreamWriter(stream); - using var reader = new LineBufferedReader(stream); - - // Create stream - writer.Write(str); - writer.Flush(); - stream.Position = 0; - // Create karaoke note decoder - var decoder = new LrcDecoder(); - return decoder.Decode(reader).HitObjects.OfType().First(); + var decoder = new KarDecoder(); + return decoder.Decode(str).First(); } /// diff --git a/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Beatmap/TestSceneEditorMenuBar.cs b/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Beatmap/TestSceneEditorMenuBar.cs index bcf3b8544..48e4dd1ab 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Beatmap/TestSceneEditorMenuBar.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Screens/Edit/Beatmap/TestSceneEditorMenuBar.cs @@ -45,9 +45,9 @@ private void load() Items = new MenuItem[] { new ImportLyricMenu(null, "Import from text", null!), - new ImportLyricMenu(null, "Import from .lrc file", null!), + new ImportLyricMenu(null, "Import from .kar file", null!), new OsuMenuItemSpacer(), - new EditorMenuItem("Export to .lrc", MenuItemType.Standard, () => { }), + new EditorMenuItem("Export to .kar", MenuItemType.Standard, () => { }), new EditorMenuItem("Export to text", MenuItemType.Standard, () => { }), new EditorMenuItem("Export to json", MenuItemType.Destructive, () => { }), }, diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs index 216f29944..98c17ef0c 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs @@ -4,13 +4,13 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; -using osu.Game.IO; using osu.Game.Rulesets.Karaoke.Edit.Generator.Lyrics.Notes; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; @@ -31,7 +31,7 @@ public KaraokeLegacyBeatmapDecoder(int version = LATEST_VERSION) { } - private readonly IList lrcLines = new List(); + private readonly IList karFormatLines = new List(); private readonly IList noteLines = new List(); private readonly IList translations = new List(); @@ -52,8 +52,8 @@ protected override void ParseLine(Beatmap beatmap, Section section, string line) if (line.ToLower().StartsWith("@ruby", StringComparison.Ordinal)) { - // lrc queue - lrcLines.Add(line); + // kar format queue + karFormatLines.Add(line); } else if (line.ToLower().StartsWith("@note", StringComparison.Ordinal)) { @@ -67,28 +67,19 @@ protected override void ParseLine(Beatmap beatmap, Section section, string line) } else if (line.StartsWith('@')) { - // Remove @ in time tag and add into lrc queue - lrcLines.Add(line[1..]); + // Remove @ in time tag and add into kar queue + karFormatLines.Add(line[1..]); } else if (line.ToLower() == "end") { - // Start processing file - using (var stream = new MemoryStream()) - using (var writer = new StreamWriter(stream)) - using (var reader = new LineBufferedReader(stream)) - { - // Create stream - writer.Write(string.Join("\n", lrcLines)); - writer.Flush(); - stream.Position = 0; + string content = string.Join("\n", karFormatLines); - // Create lrc decoder - var decoder = new LrcDecoder(); - var lrcBeatmap = decoder.Decode(reader); + // Create decoder + var decoder = new KarDecoder(); + var lyrics = decoder.Decode(content); - // Apply hitobjects - beatmap.HitObjects = lrcBeatmap.HitObjects; - } + // Apply hitobjects + beatmap.HitObjects = lyrics.OfType().ToList(); processNotes(beatmap, noteLines); processTranslations(beatmap, translations); diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapEncoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapEncoder.cs index cd2efa9dc..526399017 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapEncoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapEncoder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Rulesets.Karaoke.Objects; namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; @@ -13,10 +14,10 @@ public class KaraokeLegacyBeatmapEncoder { public string Encode(Beatmap output) { - var lrcEncoder = new LrcEncoder(); + var encoder = new KarEncoder(); var results = new List { - lrcEncoder.Encode(output), + encoder.Encode(output), string.Join("\n", encodeNotes(output)), string.Join("\n", encodeTranslations(output)), }; diff --git a/osu.Game.Rulesets.Karaoke/Edit/Export/ExportLyricManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Export/ExportLyricManager.cs index 89cd8eced..eefdce9ae 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Export/ExportLyricManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Export/ExportLyricManager.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Karaoke.Edit.Export; @@ -20,15 +20,15 @@ public partial class ExportLyricManager : Component [Resolved] private EditorBeatmap beatmap { get; set; } = null!; - public void ExportToLrc() + public void ExportToKar() { - var exportStorage = storage.GetStorageForDirectory("lrc"); - string filename = $"{beatmap.Name}.lrc"; + var exportStorage = storage.GetStorageForDirectory("kar"); + string filename = $"{beatmap.Name}.kar"; using (var outputStream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) using (var sw = new StreamWriter(outputStream)) { - var encoder = new LrcEncoder(); + var encoder = new KarEncoder(); sw.WriteLine(encoder.Encode(new Beatmap { HitObjects = beatmap.HitObjects.ToList(), diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/KaraokeBeatmapEditor.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/KaraokeBeatmapEditor.cs index ab6bddfb4..cf3ed4451 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/KaraokeBeatmapEditor.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/KaraokeBeatmapEditor.cs @@ -102,9 +102,9 @@ protected override MenuItem[] GenerateMenuItems(KaraokeBeatmapEditorScreenMode s Items = new MenuItem[] { new ImportLyricMenu(this, "Import from text", importBeatmapChangeHandler), - new ImportLyricMenu(this, "Import from .lrc file", importBeatmapChangeHandler), + new ImportLyricMenu(this, "Import from .kar file", importBeatmapChangeHandler), new OsuMenuItemSpacer(), - new EditorMenuItem("Export to .lrc", MenuItemType.Standard, () => exportLyricManager.ExportToLrc()), + new EditorMenuItem("Export to .kar", MenuItemType.Standard, () => exportLyricManager.ExportToKar()), new EditorMenuItem("Export to text", MenuItemType.Standard, () => exportLyricManager.ExportToText()), }, }, diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/DragFile/DragFileStepScreen.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/DragFile/DragFileStepScreen.cs index 422cbbfba..f4c4f6769 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/DragFile/DragFileStepScreen.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/DragFile/DragFileStepScreen.cs @@ -86,7 +86,7 @@ public void ImportLyricFile(FileInfo fileInfo) try { - importManager.ImportLrcFile(fileInfo); + importManager.ImportFile(fileInfo); DialogOverlay.Push(createCompleteDialog()); } catch (Exception ex) diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/ImportLyricManager.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/ImportLyricManager.cs index 4d5c144d7..1c2fc1a46 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/ImportLyricManager.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Import/Lyrics/ImportLyricManager.cs @@ -1,6 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.IO; using System.Linq; using osu.Framework.Allocation; @@ -8,7 +9,7 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.IO; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; +using osu.Game.Rulesets.Karaoke.Integration.Formats; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Screens.Edit; using FileInfo = System.IO.FileInfo; @@ -19,7 +20,7 @@ public partial class ImportLyricManager : Component { public static string[] LyricFormatExtensions { get; } = { ".lrc", ".kar", ".txt" }; - private const string backup_lrc_name = "backup.lrc"; + private const string backup_file_name = "backup"; [Resolved] private EditorBeatmap editorBeatmap { get; set; } = null!; @@ -27,7 +28,7 @@ public partial class ImportLyricManager : Component [Resolved] private IBindable beatmap { get; set; } = null!; - public void ImportLrcFile(FileInfo info) + public void ImportFile(FileInfo info) { if (!info.Exists) throw new FileNotFoundException("Lyric file does not found!"); @@ -37,32 +38,43 @@ public void ImportLrcFile(FileInfo info) throw new FileLoadException("Only .lrc or .kar karaoke file is supported now"); var set = beatmap.Value.BeatmapSetInfo; - var oldFile = set.Files.FirstOrDefault(f => f.Filename == backup_lrc_name); + var oldFile = set.Files.FirstOrDefault(f => f.Filename == backup_file_name); using var stream = info.OpenRead(); // todo : make a backup if has new lyric file. /* if (oldFile != null) - beatmaps.ReplaceFile(set, oldFile, stream, backup_lrc_name); + beatmaps.ReplaceFile(set, oldFile, stream, backup_file_name); else - beatmaps.AddFile(set, stream, backup_lrc_name); + beatmaps.AddFile(set, stream, backup_file_name); */ // Import and replace all the file. using var reader = new LineBufferedReader(stream); - - var decoder = new LrcDecoder(); - var lrcBeatmap = decoder.Decode(reader); + string content = reader.ReadToEnd(); + var lyrics = decodeLyrics(content, info.Extension); // remove all hit objects (note and lyric) from beatmap editorBeatmap.Clear(); // then re-add the lyric. - var lyrics = lrcBeatmap.HitObjects.OfType(); editorBeatmap.AddRange(lyrics); } + private static Lyric[] decodeLyrics(string content, string extension) + { + IDecoder decoder = extension switch + { + ".lrc" => new LrcDecoder(), + ".kar" => new KarDecoder(), + ".txt" => new LyricTextDecoder(), + _ => throw new NotSupportedException("Unsupported lyric file format"), + }; + + return decoder.Decode(content); + } + public void AbortImport() { editorBeatmap.Clear(); From c5e922009d8bf7a3abfdade9135b6264001af54a Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Aug 2024 00:05:26 +0800 Subject: [PATCH 9/9] Remove un-need lyrc Decoder/Encoder. --- .../Beatmaps/Formats/LrcDecoderTest.cs | 75 ------------------- .../Beatmaps/Formats/LrcEncoderTest.cs | 56 -------------- .../Beatmaps/Formats/LrcDecoder.cs | 30 -------- .../Beatmaps/Formats/LrcEncoder.cs | 16 ---- 4 files changed, 177 deletions(-) delete mode 100644 osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcDecoderTest.cs delete mode 100644 osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs delete mode 100644 osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs delete mode 100644 osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcDecoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcDecoderTest.cs deleted file mode 100644 index 8c127335b..000000000 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcDecoderTest.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) andy840119 . Licensed under the GPL Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.IO; -using System.Linq; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.IO; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; -using osu.Game.Rulesets.Karaoke.Objects; -using osu.Game.Rulesets.Karaoke.Tests.Asserts; -using osu.Game.Rulesets.Karaoke.Tests.Helper; - -namespace osu.Game.Rulesets.Karaoke.Tests.Beatmaps.Formats; - -[TestFixture] -public class LrcDecoderTest -{ - [TestCase("[00:01.00]か[00:02.00]ら[00:03.00]お[00:04.00]け[00:05.00]", "からおけ", 1000, 5000)] - public void TestLyricTextAndTime(string lyricText, string expectedText, double expectedStartTime, double expectedEndTime) - { - var beatmap = decodeLrcLine(lyricText); - - // Get first lyric from beatmap - var actual = beatmap.HitObjects.OfType().FirstOrDefault()!; - Assert.IsNotNull(actual); - Assert.AreEqual(expectedText, actual.Text); - Assert.AreEqual(expectedStartTime, actual.LyricTimingInfo?.StartTime); - Assert.AreEqual(expectedEndTime, actual.LyricTimingInfo?.EndTime); - } - - [TestCase("[00:01.00]か[00:02.00]ら[00:03.00]お[00:04.00]け[00:05.00]", new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" })] - public void TestLyricTimeTag(string text, string[] timeTags) - { - // Get first lyric from beatmap - var beatmap = decodeLrcLine(text); - var lyric = beatmap.HitObjects.OfType().FirstOrDefault(); - - // Check time tag - var expected = TestCaseTagHelper.ParseTimeTags(timeTags); - var actual = lyric?.TimeTags ?? throw new ArgumentNullException(nameof(lyric)); - TimeTagAssert.ArePropertyEqual(expected, actual); - } - - [Ignore("Time-tags with same time might be allowed.")] - [TestCase("[00:04.00]か[00:04.00]ら[00:05.00]お[00:06.00]け[00:07.00]")] - public void TestDecodeLyricWithDuplicatedTimeTag(string text) - { - Assert.Throws(() => decodeLrcLine(text)); - } - - [Ignore("Waiting for lyric parser update.")] - [TestCase("[00:04.00]か[00:03.00]ら[00:02.00]お[00:01.00]け[00:00.00]")] - public void TestDecodeLyricWithTimeTagNotOrder(string text) - { - Assert.Throws(() => decodeLrcLine(text)); - } - - private static Beatmap decodeLrcLine(string line) - { - using var stream = new MemoryStream(); - using var writer = new StreamWriter(stream); - using var reader = new LineBufferedReader(stream); - - // Create stream - writer.Write(line); - writer.Flush(); - stream.Position = 0; - - // Create karaoke note decoder - var decoder = new LrcDecoder(); - return decoder.Decode(reader); - } -} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs deleted file mode 100644 index e7ae014fe..000000000 --- a/osu.Game.Rulesets.Karaoke.Tests/Beatmaps/Formats/LrcEncoderTest.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) andy840119 . Licensed under the GPL Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.IO; -using osu.Game.IO.Serialization; -using osu.Game.Rulesets.Karaoke.Beatmaps.Formats; -using osu.Game.Rulesets.Karaoke.Tests.Resources; -using LrcDecoder = osu.Game.Rulesets.Karaoke.Beatmaps.Formats.LrcDecoder; - -namespace osu.Game.Rulesets.Karaoke.Tests.Beatmaps.Formats; - -[TestFixture] -public class LrcEncoderTest -{ - private static IEnumerable allLrcFileNames => TestResources.GetStore().GetAvailableResources() - .Where(res => res.EndsWith(".lrc", StringComparison.Ordinal)).Select(x => Path.GetFileNameWithoutExtension(x!)); - - [TestCaseSource(nameof(allLrcFileNames))] - public void TestDecodeEncodedBeatmap(string fileName) - { - var decoded = decode(fileName, out var encoded); - - // Note : this test case does not cover ruby property - Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count)); - Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize())); - } - - private static Beatmap decode(string filename, out Beatmap encoded) - { - using var stream = TestResources.OpenKarResource(filename); - using var sr = new LineBufferedReader(stream); - - // Read file and decode to file - var legacyDecoded = new LrcDecoder().Decode(sr); - - using var ms = new MemoryStream(); - using var sw = new StreamWriter(ms); - using var sr2 = new LineBufferedReader(ms); - - // Then encode file to stream - string encodeResult = new LrcEncoder().Encode(legacyDecoded); - sw.WriteLine(encodeResult); - sw.Flush(); - - ms.Position = 0; - - encoded = new LrcDecoder().Decode(sr2); - return legacyDecoded; - } -} diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs deleted file mode 100644 index d30ae957e..000000000 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcDecoder.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) andy840119 . Licensed under the GPL Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; -using osu.Game.IO; -using osu.Game.Rulesets.Karaoke.Integration.Formats; - -namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; - -public class LrcDecoder : Decoder -{ - public static void Register() - { - // Lrc decoder looks like [mm:ss:__] - AddDecoder("[", _ => new LrcDecoder()); - } - - protected override void ParseStreamInto(LineBufferedReader stream, Beatmap output) - { - string lyricText = stream.ReadToEnd(); - var song = new LrcParser.Parser.Lrc.LrcParser().Decode(lyricText); - - var newLyrics = LrcParserUtils.ConvertToLyrics(song); - - // Clear all hitobjects - output.HitObjects.Clear(); - output.HitObjects.AddRange(newLyrics); - } -} diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs deleted file mode 100644 index 522828fa6..000000000 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/LrcEncoder.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) andy840119 . Licensed under the GPL Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Karaoke.Integration.Formats; - -namespace osu.Game.Rulesets.Karaoke.Beatmaps.Formats; - -public class LrcEncoder -{ - public string Encode(Beatmap output) - { - var song = LrcParserUtils.ConvertToSong(output); - return new LrcParser.Parser.Lrc.LrcParser().Encode(song); - } -}