public void FindTimeOfNthNoteFrom_SkipsSplitChordCorrectly() { const int chordTime = 1800; var testArr = new InstrumentalArrangement(); testArr.Levels.Add(new Level()); testArr.Levels[0].Notes.Add(new Note { Time = chordTime - 500, String = 1 }); testArr.Levels[0].Notes.Add(new Note { Time = chordTime - 500, String = 2 }); testArr.Levels[0].Notes.Add(new Note { Time = chordTime - 500, String = 3 }); testArr.Levels[0].Chords.Add(new Chord { Time = chordTime }); int result = TimeParser.FindTimeOfNthNoteFrom(testArr.Levels[0], 0, 2); result.Should().Be(chordTime); }
public void FindTimeOfNthNoteFrom_FindsCorrectNote() { const int noteTime = 1200; var testArr = new InstrumentalArrangement(); testArr.Levels.Add(new Level()); testArr.Levels[0].Notes.Add(new Note { Time = noteTime }); int result = TimeParser.FindTimeOfNthNoteFrom(testArr.Levels[0], 0, 1); result.Should().Be(noteTime); }
public void Apply(InstrumentalArrangement arrangement, Action <string> Log) { var events = arrangement.Events; var removeBeatsEvent = events.Find(ev => ev.Code.Equals("removebeats", StringComparison.OrdinalIgnoreCase)); if (removeBeatsEvent is not null) { arrangement.Ebeats.RemoveAll(b => b.Time >= removeBeatsEvent.Time); Log($"removebeats event found: Removed beats from {removeBeatsEvent.Time.TimeToString()} onward."); events.Remove(removeBeatsEvent); } var slideoutEvents = events.Where(ev => ev.Code.StartsWith("so", StringComparison.OrdinalIgnoreCase)).ToList(); foreach (var slideEvent in slideoutEvents) { int slideTime = slideEvent.Time; // Find the max level for the phrase the slide is in var phraseIter = arrangement.PhraseIterations.Last(pi => pi.Time <= slideTime); int diff = arrangement.Phrases[phraseIter.PhraseId].MaxDifficulty; var level = arrangement.Levels[diff]; // If a number was given after the event code, get the time of the chord or note that is right of the event by that number if (slideEvent.Code.Length > 2 && int.TryParse(slideEvent.Code.Substring(2), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out int rightBy)) { slideTime = TimeParser.FindTimeOfNthNoteFrom(level, slideTime, rightBy); } int noteIndex = level.Notes.FindIndexByTime(slideTime); int chordIndex = level.Chords.FindIndexByTime(slideTime); if (noteIndex == -1 && chordIndex == -1) { string error = $"Could not find the notes or chord for SlideOut event at {slideEvent.Time.TimeToString()}"; _statusMessages.Add(new ImproverMessage(error, MessageType.Error, slideEvent.Time)); Log(error); continue; } var notes = new List <Note>(); ChordTemplate originalChordTemplate; if (noteIndex != -1) // These are notes that follow a LinkNext chord { do { var note = level.Notes[noteIndex]; if (note.IsUnpitchedSlide) { notes.Add(note); } noteIndex++; } while (noteIndex < level.Notes.Count && level.Notes[noteIndex].Time == slideTime); // Find the chord that is linked to the slide, its template and handshape var linkNextChord = level.Chords.Last(c => c.Time < slideTime); var linkNextChordHs = level.HandShapes.Find(hs => hs.StartTime == linkNextChord.Time); originalChordTemplate = arrangement.ChordTemplates[linkNextChord.ChordId]; // Shorten handshapes that EOF has set to include the slide out // If chord notes is null here, there is an error in the XML file if (linkNextChordHs is not null && linkNextChordHs.EndTime > linkNextChord.Time + linkNextChord.ChordNotes ![0].Sustain)
public void Apply(InstrumentalArrangement arrangement, Action <string> Log) { var phrasesToMove = arrangement.Phrases .Where(p => p.Name.StartsWith("moveto", StringComparison.OrdinalIgnoreCase) || p.Name.StartsWith("moveR", StringComparison.OrdinalIgnoreCase)) .ToList(); if (phrasesToMove.Count > 0) { Log("Processing 'move' phrases:"); } else { return; } foreach (var phraseToMove in phrasesToMove) { // Find phrase iterations by matching phrase index int phraseId = arrangement.Phrases.IndexOf(phraseToMove); foreach (var phraseIterationToMove in arrangement.PhraseIterations.Where(pi => pi.PhraseId == phraseId)) { int phraseTime = phraseIterationToMove.Time; int movetoTime; string phraseToMoveName = phraseToMove.Name; // Relative phrase moving right if (phraseToMoveName.StartsWith("moveR", StringComparison.OrdinalIgnoreCase)) { if (int.TryParse(phraseToMoveName.Substring("moveR".Length), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out int moveRightBy)) { var level = arrangement.Levels[phraseToMove.MaxDifficulty]; movetoTime = TimeParser.FindTimeOfNthNoteFrom(level, phraseTime, moveRightBy); } else { string errorMessage = $"Unable to read value for 'moveR' phrase at {phraseTime.TimeToString()}. (Usage example: moveR2)"; _statusMessages.Add(new ImproverMessage(errorMessage, MessageType.Warning)); Log(errorMessage); continue; } } else // Parse the absolute time to move to from the phrase name { int?parsedTime = TimeParser.Parse(phraseToMoveName); if (parsedTime.HasValue) { movetoTime = parsedTime.Value; } else { MoveToParseFailure(phraseTime, Log); continue; } } // Check if anchor(s) should be moved foreach (var level in arrangement.Levels) { if (level.Difficulty > phraseToMove.MaxDifficulty) { break; } var anchors = level.Anchors; int originalAnchorIndex = anchors.FindIndexByTime(phraseTime); int movetoAnchorIndex = anchors.FindIndexByTime(movetoTime); // If there is an anchor at the original position, but not at the new position, move it if (originalAnchorIndex != -1 && movetoAnchorIndex == -1) { var originalAnchor = anchors[originalAnchorIndex]; anchors.Insert(originalAnchorIndex + 1, new Anchor(originalAnchor.Fret, movetoTime, originalAnchor.Width)); // Remove anchor at original phrase position if no note or chord present if (level.Notes.FindIndexByTime(phraseTime) == -1 && level.Chords.FindIndexByTime(phraseTime) == -1) { anchors.RemoveAt(originalAnchorIndex); Log($"--Moved anchor from {phraseTime.TimeToString()} to {movetoTime.TimeToString()}"); } else { Log($"--Added anchor at {movetoTime.TimeToString()}"); } } } // Move phraseIteration phraseIterationToMove.Time = movetoTime; // Move section (if present) var sectionToMove = arrangement.Sections.FindByTime(phraseTime); if (sectionToMove is not null) { sectionToMove.Time = movetoTime; Log($"--Moved phrase and section from {phraseTime.TimeToString()} to {movetoTime.TimeToString()}"); } else { Log($"--Moved phrase from {phraseTime.TimeToString()} to {movetoTime.TimeToString()}"); } // Add new temporary beat var beatToAdd = new Ebeat(movetoTime, XMLProcessor.TempMeasureNumber); var insertIndex = arrangement.Ebeats.FindIndex(b => b.Time > movetoTime); arrangement.Ebeats.Insert(insertIndex, beatToAdd); // Set the beat for later removal _addedBeats.Add(beatToAdd); } } }