[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); } }
private void processNotes(Beatmap beatmap, IList <string> lines) { // Remove all karaoke note beatmap.HitObjects.RemoveAll(x => x is Note); var lyrics = beatmap.HitObjects.OfType <Lyric>().ToList(); for (int l = 0; l < lyrics.Count; l++) { var lyric = lyrics[l]; var line = lines.ElementAtOrDefault(l)?.Split('=').Last(); // Create default note if not exist if (string.IsNullOrEmpty(line)) { beatmap.HitObjects.AddRange(LyricUtils.CreateDefaultNotes(lyric)); continue; } var notes = line.Split(','); var defaultNotes = LyricUtils.CreateDefaultNotes(lyric).ToList(); var minNoteNumber = Math.Min(notes.Length, defaultNotes.Count); // Process each note for (int i = 0; i < minNoteNumber; i++) { var note = notes[i]; var defaultNote = defaultNotes[i]; // Support multi note in one time tag, format like ([1;0.5;か]|1#|...) if (!note.StartsWith("(", StringComparison.Ordinal) || !note.EndsWith(")", StringComparison.Ordinal)) { // Process and add note applyNote(defaultNote, note); beatmap.HitObjects.Add(defaultNote); } else { float startPercentage = 0; var rubyNotes = note.Replace("(", "").Replace(")", "").Split('|'); for (int j = 0; j < rubyNotes.Length; j++) { string rubyNote = rubyNotes[j]; string tone; float percentage = (float)Math.Round((float)1 / rubyNotes.Length, 2, MidpointRounding.AwayFromZero); string ruby = defaultNote.AlternativeText?.ElementAtOrDefault(j).ToString(); // Format like [1;0.5;か] if (note.StartsWith("[", StringComparison.Ordinal) && note.EndsWith("]", StringComparison.Ordinal)) { var rubyNoteProperty = note.Replace("[", "").Replace("]", "").Split(';'); // Copy tome property tone = rubyNoteProperty[0]; // Copy percentage property if (rubyNoteProperty.Length >= 2) { float.TryParse(rubyNoteProperty[1], out percentage); } // Copy text property if (rubyNoteProperty.Length >= 3) { ruby = rubyNoteProperty[2]; } } else { tone = rubyNote; } // Split note and apply them var splitDefaultNote = NoteUtils.SliceNote(defaultNote, startPercentage, percentage); startPercentage += percentage; if (!string.IsNullOrEmpty(ruby)) { splitDefaultNote.Text = ruby; } // Process and add note applyNote(splitDefaultNote, tone); beatmap.HitObjects.Add(splitDefaultNote); } } } } void applyNote(Note note, string noteStr, string ruby = null, double?duration = null) { if (noteStr == "-") { note.Display = false; } else { note.Display = true; note.Tone = convertTone(noteStr); } if (!string.IsNullOrEmpty(ruby)) { note.Text = ruby; } if (duration != null) { note.Duration = duration.Value; } //Support format : 1 1. 1.5 1+ 1# Tone convertTone(string tone) { var half = false; if (tone.Contains(".") || tone.Contains("#")) { half = true; // only get digit part tone = tone.Split('.').FirstOrDefault()?.Split('#').FirstOrDefault(); } if (!int.TryParse(tone, out int scale)) { throw new ArgumentOutOfRangeException($"{tone} does not support in {nameof(KaraokeLegacyBeatmapDecoder)}"); } return(new Tone { Scale = scale, Half = half }); } } }