From 0e89bba2f977533eab68bb5ba55ae077ecc6fbeb Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 13 Dec 2020 23:07:50 +0900 Subject: [PATCH 1/3] Nove split note into utils. --- .../Utils/NoteUtilsTest.cs | 9 +++ .../Formats/KaraokeLegacyBeatmapDecoder.cs | 2 +- .../Notes/NoteSelectionBlueprint.cs | 10 ++-- osu.Game.Rulesets.Karaoke/Objects/Note.cs | 30 ---------- osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs | 59 +++++++++++++++++++ 5 files changed, 75 insertions(+), 35 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs create mode 100644 osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs new file mode 100644 index 000000000..4c5179e1e --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs @@ -0,0 +1,9 @@ +// 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.Tests.Utils +{ + public class NoteUtilsTest + { + } +} diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs index 5f291123a..8d6734bae 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs @@ -168,7 +168,7 @@ private void processNotes(Beatmap beatmap, IList lines) } // Split note and apply them - var splitDefaultNote = defaultNote.CopyByPercentage(startPercentage, percentage); + var splitDefaultNote = NoteUtils.SplitNote(defaultNote, startPercentage, percentage); startPercentage += percentage; if (!string.IsNullOrEmpty(ruby)) splitDefaultNote.Text = ruby; diff --git a/osu.Game.Rulesets.Karaoke/Edit/Blueprints/Notes/NoteSelectionBlueprint.cs b/osu.Game.Rulesets.Karaoke/Edit/Blueprints/Notes/NoteSelectionBlueprint.cs index df617991b..76265199d 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Blueprints/Notes/NoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Blueprints/Notes/NoteSelectionBlueprint.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; @@ -8,6 +9,7 @@ using osu.Game.Rulesets.Karaoke.Edit.Blueprints.Notes.Components; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Objects.Drawables; +using osu.Game.Rulesets.Karaoke.Utils; using osu.Game.Screens.Edit; using osuTK; @@ -32,10 +34,10 @@ public class NoteSelectionBlueprint : KaraokeSelectionBlueprint private void splitNote() { // TODO : percentage should be enter by dialog - var splittedNote = HitObject.CopyByPercentage(0.5); - EditorBeatmap?.Add(splittedNote); - // Change object's duration - HitObject.Duration = HitObject.Duration - splittedNote.Duration; + var (firstNote, secondNote) = NoteUtils.SplitNote(HitObject, 0.5); + EditorBeatmap?.Add(firstNote); + EditorBeatmap?.Add(secondNote); + EditorBeatmap?.Remove(HitObject); } public void ChangeDisplay(bool display) diff --git a/osu.Game.Rulesets.Karaoke/Objects/Note.cs b/osu.Game.Rulesets.Karaoke/Objects/Note.cs index 6a336a6cb..024ef59b7 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Note.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Note.cs @@ -88,36 +88,6 @@ public virtual Tone Tone public Lyric ParentLyric { get; set; } - public Note CopyByPercentage(double startPercentage = 0, double durationPercentage = 0.5) - { - if (startPercentage < 0 || startPercentage + durationPercentage > 1) - throw new ArgumentOutOfRangeException($"{nameof(Note)} cannot assign split range of start from {startPercentage} and duration {durationPercentage}"); - - var startTime = StartTime + Duration * startPercentage; - var duration = Duration * durationPercentage; - - return CopyByTime(startTime, duration); - } - - public Note CopyByTime(double startTime, double duration) - { - if (startTime < StartTime || startTime + duration > EndTime) - throw new ArgumentOutOfRangeException($"{nameof(Note)} cannot assign split range of start from {startTime} and duration {duration}"); - - return new Note - { - StartTime = startTime, - Duration = duration, - StartIndex = StartIndex, - EndIndex = EndIndex, - Text = Text, - Singers = Singers, - Display = Display, - Tone = Tone, - ParentLyric = ParentLyric - }; - } - public override Judgement CreateJudgement() => new KaraokeNoteJudgement(); } } diff --git a/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs new file mode 100644 index 000000000..ecf6e5a6f --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs @@ -0,0 +1,59 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Karaoke.Objects; +using System; + +namespace osu.Game.Rulesets.Karaoke.Utils +{ + public static class NoteUtils + { + public static Note SplitNote(Note note, double startPercentage, double durationPercentage) + { + if (startPercentage < 0 || startPercentage + durationPercentage > 1) + throw new ArgumentOutOfRangeException($"{nameof(Note)} cannot assign split range of start from {startPercentage} and duration {durationPercentage}"); + + var startTime = note.StartTime + note.Duration * startPercentage; + var duration = note.Duration * durationPercentage; + + return copyByTime(note, startTime, duration); + } + public static Tuple SplitNote(Note note, double percentage = 0.5) + { + if (percentage <= 0 || percentage >= 1) + throw new ArgumentOutOfRangeException(nameof(Note)); + + var firstNoteStartTime = note.StartTime; + var firstNoteDuration = note.Duration * percentage; + + var secondNoteStartTime = firstNoteStartTime + firstNoteDuration; + var secondNoteDuration = note.Duration * (1 - percentage); + + var firstNote = copyByTime(note, firstNoteStartTime, firstNoteDuration); + var secondNote = copyByTime(note, secondNoteStartTime, secondNoteDuration); + + return new Tuple(firstNote, secondNote); + } + + private static Note copyByTime(Note oritinNote, double startTime, double duration) + { + return new Note + { + StartTime = startTime, + Duration = duration, + StartIndex = oritinNote.StartIndex, + EndIndex = oritinNote.EndIndex, + Text = oritinNote.Text, + Singers = oritinNote.Singers, + Display = oritinNote.Display, + Tone = oritinNote.Tone, + ParentLyric = oritinNote.ParentLyric + }; + } + + public static Note CombineNote(Note firstLyric, Note secondLyric) + { + return null; + } + } +} From 285eb0c23758eb916716a2fd2328a83e43314267 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Mon, 14 Dec 2020 00:53:43 +0900 Subject: [PATCH 2/3] Implement slice note utils and it's test case friend. --- .../Utils/NoteUtilsTest.cs | 107 ++++++++++++++++++ .../Formats/KaraokeLegacyBeatmapDecoder.cs | 2 +- osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs | 9 +- 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs index 4c5179e1e..e3af176b0 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs @@ -1,9 +1,116 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Utils; + namespace osu.Game.Rulesets.Karaoke.Tests.Utils { public class NoteUtilsTest { + [TestCase(new double[] { 1000, 3000 }, 0, 1, new double[] { 1000, 3000 })] + [TestCase(new double[] { 1000, 3000 }, 0, 0.5, new double[] { 1000, 1500 })] + [TestCase(new double[] { 1000, 3000 }, 0.5, 0.5, new double[] { 2500, 1500 })] + [TestCase(new double[] { 1000, 3000 }, 0.3, 0.4, new double[] { 1900, 1200 })] + [TestCase(new double[] { 1000, 3000 }, 0.3, 1, null)] // start + duration should not exceed 1 + public void TestSliceNoteTime(double[] time, double startPercentage, double durationPercentage, double[] actualTime) + { + var note = new Note + { + StartTime = time[0], + Duration = time[1], + }; + + try + { + var sliceNote = NoteUtils.SliceNote(note, startPercentage, durationPercentage); + Assert.AreEqual(sliceNote.StartTime, actualTime[0]); + Assert.AreEqual(sliceNote.Duration, actualTime[1]); + } + catch + { + Assert.IsNull(actualTime); + } + } + + [TestCase(new double[] { 1000, 5000 }, 0.2, new double[] { 1000, 1000 }, new double[] { 2000, 4000 })] + [TestCase(new double[] { 1000, 5000 }, 0.5, new double[] { 1000, 2500 }, new double[] { 3500, 2500 })] + [TestCase(new double[] { 1000, 0 }, 0.2, new double[] { 1000, 0 }, new double[] { 1000, 0 })] // it's ok to split if duration is 0. + [TestCase(new double[] { 1000, 0 }, 0.7, new double[] { 1000, 0 }, new double[] { 1000, 0 })] + [TestCase(new double[] { 1000, 5000 }, -1, null, null)] // should be in the range. + [TestCase(new double[] { 1000, 5000 }, 3, null, null)] + [TestCase(new double[] { 1000, 5000 }, 0, null, null)] // should not be 0 or 1. + [TestCase(new double[] { 1000, 5000 }, 1, null, null)] + public void TestSeparateNoteTime(double[] time, double percentage, double[] firstTime, double[] secondTime) + { + var note = new Note + { + StartTime = time[0], + Duration = time[1], + }; + + try + { + var (firstNote, secondNote) = NoteUtils.SplitNote(note, percentage); + Assert.AreEqual(firstNote.StartTime, firstTime[0]); + Assert.AreEqual(firstNote.Duration, firstTime[1]); + + Assert.AreEqual(secondNote.StartTime, secondTime[0]); + Assert.AreEqual(secondNote.Duration, secondTime[1]); + } + catch + { + Assert.IsNull(firstTime); + Assert.IsNull(secondTime); + } + } + + [Test] + public void TestSeparateLyricOtherPtoperty() + { + var lyric = new Lyric(); + + const double percentage = 0.3; + var note = new Note + { + StartTime = 1000, + Duration = 2000, + StartIndex = 1, + EndIndex = 2, + Text = "ka", + Singers = new int[] { 0 }, + Display = false, + Tone = new Tone(-1, true), + ParentLyric = lyric + }; + + // create other property and make sure other class is applied value. + var (firstNote, secondNote) = NoteUtils.SplitNote(note, percentage); + + Assert.AreEqual(firstNote.StartTime, 1000); + Assert.AreEqual(secondNote.StartTime, 1600); + + Assert.AreEqual(firstNote.Duration, 600); + Assert.AreEqual(secondNote.Duration, 1400); + + testRemainProperty(firstNote, note); + testRemainProperty(firstNote, note); + + static void testRemainProperty(Note expect, Note actual) + { + Assert.AreEqual(expect.StartIndex, actual.StartIndex); + Assert.AreEqual(expect.EndIndex, actual.EndIndex); + Assert.AreEqual(expect.Text, actual.Text); + + Assert.AreEqual(expect.Singers, actual.Singers); + Assert.AreNotSame(expect.Singers, actual.Singers); + + Assert.AreEqual(expect.Display, actual.Display); + Assert.AreEqual(expect.Tone, actual.Tone); + Assert.AreEqual(expect.ParentLyric, actual.ParentLyric); + } + } } } diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs index 8d6734bae..3589f910c 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/Formats/KaraokeLegacyBeatmapDecoder.cs @@ -168,7 +168,7 @@ private void processNotes(Beatmap beatmap, IList lines) } // Split note and apply them - var splitDefaultNote = NoteUtils.SplitNote(defaultNote, startPercentage, percentage); + var splitDefaultNote = NoteUtils.SliceNote(defaultNote, startPercentage, percentage); startPercentage += percentage; if (!string.IsNullOrEmpty(ruby)) splitDefaultNote.Text = ruby; diff --git a/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs index ecf6e5a6f..caa3ae59f 100644 --- a/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs +++ b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Karaoke.Utils { public static class NoteUtils { - public static Note SplitNote(Note note, double startPercentage, double durationPercentage) + public static Note SliceNote(Note note, double startPercentage, double durationPercentage) { if (startPercentage < 0 || startPercentage + durationPercentage > 1) throw new ArgumentOutOfRangeException($"{nameof(Note)} cannot assign split range of start from {startPercentage} and duration {durationPercentage}"); @@ -20,9 +20,12 @@ public static Note SplitNote(Note note, double startPercentage, double duration } public static Tuple SplitNote(Note note, double percentage = 0.5) { - if (percentage <= 0 || percentage >= 1) + if (percentage < 0 || percentage > 1) throw new ArgumentOutOfRangeException(nameof(Note)); + if (percentage == 0 || percentage == 1) + throw new InvalidOperationException($"{nameof(percentage)} cannot be {0} or {1}."); + var firstNoteStartTime = note.StartTime; var firstNoteDuration = note.Duration * percentage; @@ -44,7 +47,7 @@ private static Note copyByTime(Note oritinNote, double startTime, double duratio StartIndex = oritinNote.StartIndex, EndIndex = oritinNote.EndIndex, Text = oritinNote.Text, - Singers = oritinNote.Singers, + Singers = oritinNote.Singers?.Clone() as int[], Display = oritinNote.Display, Tone = oritinNote.Tone, ParentLyric = oritinNote.ParentLyric From 0a294ae4f84ed6ad578d53405543d6daf6ebac53 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Mon, 14 Dec 2020 01:16:32 +0900 Subject: [PATCH 3/3] Implement combine note. --- .../Utils/NoteUtilsTest.cs | 39 ++++++++++++++++-- osu.Game.Rulesets.Karaoke/Objects/Note.cs | 1 - osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs | 41 ++++++++++++------- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs index e3af176b0..cdd228694 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Utils/NoteUtilsTest.cs @@ -68,11 +68,11 @@ public void TestSeparateNoteTime(double[] time, double percentage, double[] firs } [Test] - public void TestSeparateLyricOtherPtoperty() + public void TestSeparateNoteOtherProperty() { + const double percentage = 0.3; var lyric = new Lyric(); - const double percentage = 0.3; var note = new Note { StartTime = 1000, @@ -80,7 +80,7 @@ public void TestSeparateLyricOtherPtoperty() StartIndex = 1, EndIndex = 2, Text = "ka", - Singers = new int[] { 0 }, + Singers = new[] { 0 }, Display = false, Tone = new Tone(-1, true), ParentLyric = lyric @@ -112,5 +112,38 @@ static void testRemainProperty(Note expect, Note actual) Assert.AreEqual(expect.ParentLyric, actual.ParentLyric); } } + + [TestCase(new double[] { 1000, 1000 }, new double[] { 2000, 4000 }, new double[] { 1000, 5000 })] + [TestCase(new double[] { 1000, 2500 }, new double[] { 3500, 2500 }, new double[] { 1000, 5000 })] + [TestCase(new double[] { 1000, 0 }, new double[] { 1000, 0 }, new double[] { 1000, 0 })] // it's ok to combine if duration is 0. + public void TestCombineNoteTime(double[] firstTime, double[] secondTime, double[] actualTime) + { + const int start_index = 3; + const int end_index = 5; + + var lyric = new Lyric(); + + var firstNote = new Note + { + StartIndex = start_index, + EndIndex = end_index, + ParentLyric = lyric, + StartTime = firstTime[0], + Duration = firstTime[1], + }; + + var secondNote = new Note + { + StartIndex = start_index, + EndIndex = end_index, + ParentLyric = lyric, + StartTime = secondTime[0], + Duration = secondTime[1], + }; + + var combineNote = NoteUtils.CombineNote(firstNote, secondNote); + Assert.AreEqual(combineNote.StartTime, actualTime[0]); + Assert.AreEqual(combineNote.Duration, actualTime[1]); + } } } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Note.cs b/osu.Game.Rulesets.Karaoke/Objects/Note.cs index 024ef59b7..1d7bd3fc5 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Note.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Note.cs @@ -1,7 +1,6 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; diff --git a/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs index caa3ae59f..f9a7c93e4 100644 --- a/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs +++ b/osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Karaoke.Utils { public static class NoteUtils { - public static Note SliceNote(Note note, double startPercentage, double durationPercentage) + public static Note SliceNote(Note note, double startPercentage, double durationPercentage) { if (startPercentage < 0 || startPercentage + durationPercentage > 1) throw new ArgumentOutOfRangeException($"{nameof(Note)} cannot assign split range of start from {startPercentage} and duration {durationPercentage}"); @@ -18,6 +18,7 @@ public static Note SliceNote(Note note, double startPercentage, double duration return copyByTime(note, startTime, duration); } + public static Tuple SplitNote(Note note, double percentage = 0.5) { if (percentage < 0 || percentage > 1) @@ -38,25 +39,37 @@ public static Tuple SplitNote(Note note, double percentage = 0.5) return new Tuple(firstNote, secondNote); } - private static Note copyByTime(Note oritinNote, double startTime, double duration) + public static Note CombineNote(Note firstLyric, Note secondLyric) + { + if (firstLyric.ParentLyric != secondLyric.ParentLyric) + throw new InvalidOperationException($"{nameof(firstLyric.ParentLyric)} and {nameof(secondLyric.ParentLyric)} should be same."); + + if (firstLyric.StartIndex != secondLyric.StartIndex) + throw new InvalidOperationException($"{nameof(firstLyric.StartIndex)} and {nameof(secondLyric.StartIndex)} should be same."); + + if (firstLyric.EndIndex != secondLyric.EndIndex) + throw new InvalidOperationException($"{nameof(firstLyric.EndIndex)} and {nameof(secondLyric.EndIndex)} should be same."); + + var startTime = Math.Min(firstLyric.StartTime, secondLyric.StartTime); + var endTime = Math.Max(firstLyric.EndTime, secondLyric.EndTime); + + return copyByTime(firstLyric, startTime, endTime - startTime); + } + + private static Note copyByTime(Note originNote, double startTime, double duration) { return new Note { StartTime = startTime, Duration = duration, - StartIndex = oritinNote.StartIndex, - EndIndex = oritinNote.EndIndex, - Text = oritinNote.Text, - Singers = oritinNote.Singers?.Clone() as int[], - Display = oritinNote.Display, - Tone = oritinNote.Tone, - ParentLyric = oritinNote.ParentLyric + StartIndex = originNote.StartIndex, + EndIndex = originNote.EndIndex, + Text = originNote.Text, + Singers = originNote.Singers?.Clone() as int[], + Display = originNote.Display, + Tone = originNote.Tone, + ParentLyric = originNote.ParentLyric }; } - - public static Note CombineNote(Note firstLyric, Note secondLyric) - { - return null; - } } }