Beispiel #1
0
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            var events = arrangement.Events;

            var width3events = events.Where(ev => ev.Code.Equals("w3", StringComparison.OrdinalIgnoreCase)).ToList();

            foreach (var w3event in width3events)
            {
                var modifiedAnchors = arrangement.Levels
                                      .SelectMany(lvl => lvl.Anchors)
                                      .Where(a => a.Time == w3event.Time);

                foreach (var anchor in modifiedAnchors)
                {
                    anchor.Width = 3;
                    Log($"Changed width of anchor at {anchor.Time.TimeToString()} to 3.");
                }

                events.Remove(w3event);
            }
        }
        public void Apply(InstrumentalArrangement arrangement, Action<string> Log)
        {
            const int ngPhraseId = 1;

            if (arrangement.Phrases[ngPhraseId].MaxDifficulty != 0)
                return;

            var ngPhrasesIterations = from pi in arrangement.PhraseIterations
                                      where pi.PhraseId == ngPhraseId
                                      select pi;

            if (!ngPhrasesIterations.Any())
            {
                Log("Removed unnecessary noguitar phrase.");

                arrangement.Phrases.RemoveAt(ngPhraseId);

                // Set correct phrase IDs for phrase iterations
                foreach (var pi in arrangement.PhraseIterations)
                {
                    if (pi.PhraseId > ngPhraseId)
                    {
                        --pi.PhraseId;
                    }
                }

                // Set correct phrase IDs for NLDs
                foreach (var nld in arrangement.NewLinkedDiffs)
                {
                    for (int i = 0; i < nld.PhraseCount; i++)
                    {
                        if (nld.PhraseIds[i] > ngPhraseId)
                        {
                            nld.PhraseIds[i]--;
                        }
                    }
                }
            }
        }
Beispiel #3
0
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            if (arrangement.MetaData.ArrangementProperties.UnpitchedSlides)
            {
                return;
            }

            // TODO: Could optimize by looking at hardest level only
            var notes = arrangement.Levels.SelectMany(l => l.Notes);

            if (notes.Any(n => n.IsUnpitchedSlide))
            {
                arrangement.MetaData.ArrangementProperties.UnpitchedSlides = true;
            }
            else
            {
                foreach (var chord in arrangement.Levels.SelectMany(l => l.Chords))
                {
                    if (chord.ChordNotes is null)
                    {
                        continue;
                    }

                    if (chord.ChordNotes.Any(n => n.IsUnpitchedSlide))
                    {
                        arrangement.MetaData.ArrangementProperties.UnpitchedSlides = true;

                        break;
                    }
                }
            }

            if (arrangement.MetaData.ArrangementProperties.UnpitchedSlides)
            {
                Log("Enabled unpitched slides in arrangement properties.");
            }
        }
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            // Increase the number attribute of any following noguitar sections
            foreach (var section in arrangement.Sections.Where(s => s.Name == "noguitar"))
            {
                ++section.Number;
            }

            // Add the removed noguitar section back
            var newFirstSection = new Section("noguitar", _firstNGSectionTime, 1);

            arrangement.Sections.Insert(0, newFirstSection);

            // Add a new NG phrase as the last phrase
            arrangement.Phrases.Add(new Phrase("NG", maxDifficulty: 0, PhraseMask.None));

            // Recreate the removed phrase iteration (with the phrase id of the new NG phrase)
            var newNGPhraseIteration = new PhraseIteration(_firstNGSectionTime, arrangement.Phrases.Count - 1);

            // Add after the first phraseIteration (COUNT)
            arrangement.PhraseIterations.Insert(1, newNGPhraseIteration);

            Log("Restored first noguitar section.");
        }
        /// <summary>
        /// Adds a second difficulty level to phrases that have only one level.
        /// </summary>
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            Log("********** Begin one level phrase fix **********");

            for (int phraseID = 0; phraseID < arrangement.Phrases.Count; phraseID++)
            {
                if (arrangement.Phrases[phraseID].MaxDifficulty == 0)
                {
                    Log($"--Phrase #{phraseID} ({arrangement.Phrases[phraseID].Name}, {arrangement.PhraseIterations.First(pi => pi.PhraseId == phraseID).Time.TimeToString()}) has only one level.");

                    var phraseIterations = from pi in arrangement.PhraseIterations
                                           where pi.PhraseId == phraseID
                                           select pi;

                    foreach (var pi in phraseIterations)
                    {
                        int startTime = pi.Time;
                        int piIndex   = arrangement.PhraseIterations.IndexOf(pi);
                        if (piIndex == 0 || piIndex == arrangement.PhraseIterations.Count - 1)
                        {
                            // Skip first phrase (COUNT) and last phrase (END)
                            continue;
                        }
                        int endTime = arrangement.PhraseIterations[piIndex + 1].Time;

                        var firstLevel  = arrangement.Levels[0];
                        var secondLevel = arrangement.Levels[1];

                        var firstLevelNotes = (from note in firstLevel.Notes
                                               where note.Time >= startTime && note.Time < endTime
                                               select note).ToArray();

                        int notesInPhrase = firstLevelNotes.Length;

                        if (notesInPhrase == 0) // Phrase is a noguitar phrase
                        {
                            Log("Skipping empty phrase.");
                            break;
                        }

                        Log($"Phrase has {notesInPhrase} note{(notesInPhrase == 1 ? "" : "s")}.");

                        // Make copies of current notes that will be added to the harder difficulty level
                        var harderLevelNotes = new List <Note>(from note in firstLevelNotes select new Note(note));

                        int notesRemoved = 0;

                        var removableSustainNotes =
                            firstLevelNotes.Where(note => note.Sustain != 0 &&
                                                  !note.IsBend &&
                                                  !note.IsVibrato &&
                                                  !note.IsTremolo &&
                                                  !note.IsLinkNext &&
                                                  !note.IsSlide &&
                                                  !note.IsUnpitchedSlide);

                        var firstLevelAnchors  = firstLevel.Anchors;
                        var secondLevelAnchors = secondLevel.Anchors;

                        var anchorsToAddToSecondLevel =
                            from anchor in firstLevelAnchors
                            where anchor.Time >= startTime && anchor.Time < endTime
                            select anchor;

                        var newSecondLevelAnchors = new List <Anchor>();
                        newSecondLevelAnchors.AddRange(secondLevelAnchors.Union(anchorsToAddToSecondLevel).OrderBy(a => a.Time));

                        secondLevel.Anchors = newSecondLevelAnchors;

                        if (notesInPhrase > 1)
                        {
                            foreach (var note in firstLevelNotes)
                            {
                                // Remove sliding notes and vibratos
                                if (note.IsSlide || note.IsUnpitchedSlide || note.IsVibrato)
                                {
                                    Log($"Removed note at time {note.Time.TimeToString()}");
                                    firstLevel.Notes.Remove(note);
                                    notesRemoved++;
                                }
                                else if (note.IsLinkNext) // Remove linkNext from the note
                                {
                                    Log($"Removed linkNext from note at time {note.Time.TimeToString()}");
                                    note.IsLinkNext = false;

                                    if (note.Sustain < 500 && !note.IsBend) // Remove sustain if it is very short
                                    {
                                        note.Sustain = 0;
                                        Log($"Removed sustain from note at time {note.Time.TimeToString()}");
                                    }
                                }

                                // If note has more than two bendValues, remove the rest
                                if (note.IsBend && note.BendValues?.Count > 2)
                                {
                                    // Remove bendValues
                                    var secondBendValue = note.BendValues[1];
                                    note.BendValues.RemoveRange(2, note.BendValues.Count - 2);

                                    // Set sustain of the note to end at the second bendValue
                                    note.Sustain = secondBendValue.Time - note.Time;

                                    Log($"Removed bendvalues and adjusted the sustain of note at {note.Time.TimeToString()}");
                                }
                            }
                        }

                        if (notesRemoved == 0 && removableSustainNotes.Any())
                        {
                            foreach (var note in removableSustainNotes)
                            {
                                note.Sustain = 0;
                            }

                            Log($"Solution: Removed sustain from note{(notesInPhrase == 1 ? "" : "s")}.");
                        }
                        else if (notesRemoved == 0)
                        {
                            // TODO: One note phrase -> remove all techniques?

                            Log("Solution: Kept phrase as is.");
                        }

                        pi.HeroLevels = new HeroLevels(easy: 0, medium: 1, hard: 1);

                        // Find correct place where to insert the notes in the second difficulty level
                        int lastNoteTime   = harderLevelNotes.Last().Time;
                        int noteAfterIndex = secondLevel.Notes.FindIndex(n => n.Time > lastNoteTime);

                        if (noteAfterIndex == -1) // Add to the end
                        {
                            secondLevel.Notes.AddRange(harderLevelNotes);
                        }
                        else
                        {
                            secondLevel.Notes.InsertRange(noteAfterIndex, harderLevelNotes);
                        }

                        arrangement.Phrases[phraseID].MaxDifficulty = 1;
                    }
                }
            }
            Log("**********  End one level phrase fix  **********");
        }
        public PostProcessorBlocksTests(ConfigurationFixture fixture)
        {
            XMLProcessor.Preferences = fixture.Configuration;

            testArrangement = InstrumentalArrangement.Load(@".\TestFiles\postTest_RS2.xml");
        }
 public PreProcessorBlocksTests()
 {
     testArrangement = InstrumentalArrangement.Load(@".\TestFiles\preTest_RS2.xml");
 }
Beispiel #8
0
        private static (IList <Note> notes, IList <Chord> chords) GetNotesAndChordsFromSong(InstrumentalArrangement song)
        {
            // Check if the song has DD levels
            if (song.Levels.Count != 1)
            {
                int transcriptionNoteCount  = song.TranscriptionTrack?.Notes?.Count ?? 0;
                int transcriptionChordCount = song.TranscriptionTrack?.Chords?.Count ?? 0;

                if (transcriptionNoteCount == 0 && transcriptionChordCount == 0)
                {
                    // Manual DD, no transcription track available
                    return(GenerateTranscriptionTrack(song));
                }
                else
                {
                    // Use transcription track
                    return(song.TranscriptionTrack.Notes,
                           song.TranscriptionTrack.Chords);
                }
            }
            else // No DD, just grab all notes and chords
            {
                return(song.Levels[0].Notes, song.Levels[0].Chords);
            }
        }
Beispiel #9
0
        private static (IList <Note> notes, IList <Chord> chords) GenerateTranscriptionTrack(InstrumentalArrangement song)
        {
            var phrases = song.Phrases;
            var notes   = new List <Note>();
            var chords  = new List <Chord>();

            // Ignore the last phrase iteration (END)
            for (int i = 0; i < song.PhraseIterations.Count - 1; i++)
            {
                var phraseIteration = song.PhraseIterations[i];
                int phraseId        = phraseIteration.PhraseId;
                int maxDifficulty   = phrases[phraseId].MaxDifficulty;
                if (maxDifficulty == 0)
                {
                    continue;
                }

                int phraseStartTime       = phraseIteration.Time;
                int phraseEndTime         = song.PhraseIterations[i + 1].Time;
                var highestLevelForPhrase = song.Levels[maxDifficulty];

                var notesInPhraseIteration = highestLevelForPhrase.Notes
                                             .Where(n => n.Time >= phraseStartTime && n.Time < phraseEndTime);

                var chordsInPhraseIteration = highestLevelForPhrase.Chords
                                              .Where(c => c.Time >= phraseStartTime && c.Time < phraseEndTime);

                notes.AddRange(notesInPhraseIteration);
                chords.AddRange(chordsInPhraseIteration);
            }

            return(notes, chords);
        }
Beispiel #10
0
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            int notesRemoved = 0;

            foreach (var level in arrangement.Levels)
            {
                var notesToRemove = new List <Note>();
                var notes         = level.Notes;
                var chords        = level.Chords;

                for (int i = 0; i < notes.Count; i++)
                {
                    Note note = notes[i];

                    if (note.IsLinkNext)
                    {
                        // Find the next note on the same string
                        int j = i + 1;
                        while (j < notes.Count && notes[j].String != note.String)
                        {
                            j++;
                        }

                        if (j == notes.Count)
                        {
                            break;
                        }

                        // Remove the note if it has no sustain
                        if (notes[j].Sustain == 0)
                        {
                            notesToRemove.Add(notes[j]);
                            note.IsLinkNext = false;
                        }
                    }
                }

                for (int i = 0; i < chords.Count; i++)
                {
                    Chord chord = chords[i];

                    if (chord.IsLinkNext && chord.ChordNotes?.Any(n => n.IsSlide) == true)
                    {
                        // Find the first note after the chord
                        int noteIndex = notes.FindIndex(n => n.Time > chord.Time);

                        if (noteIndex == -1)
                        {
                            continue;
                        }

                        // Get all the chord notes that have LinkNext slides
                        var chordNotes = chord.ChordNotes.Where(n => n.IsLinkNext && n.IsSlide).ToDictionary(n => n.String);
                        while (chordNotes.Count > 0 && noteIndex < notes.Count)
                        {
                            var   note    = notes[noteIndex];
                            sbyte nString = note.String;
                            if (chordNotes.ContainsKey(nString))
                            {
                                if (note.Sustain == 0)
                                {
                                    chordNotes[nString].IsLinkNext = false;
                                    notesToRemove.Add(note);
                                }
                                chordNotes.Remove(nString);
                            }
                            ++noteIndex;
                        }
                        chord.IsLinkNext = chord.ChordNotes.Any(n => n.IsLinkNext);
                    }
                }

                notesRemoved += notesToRemove.Count;
                foreach (var note in notesToRemove)
                {
                    notes.Remove(note);
                }
            }

            if (notesRemoved > 0)
            {
                Log($"Removed {notesRemoved} anchor placeholder note{(notesRemoved == 1 ? "" : "s")}.");
            }
        }
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            foreach (var level in arrangement.Levels)
            {
                var handShapes = level.HandShapes;

                for (int i = 1; i < handShapes.Count; i++)
                {
                    int followingStartTime = handShapes[i].StartTime;
                    int followingEndTime   = handShapes[i].EndTime;

                    var precedingHandshape = handShapes[i - 1];
                    int precedingStartTime = precedingHandshape.StartTime;
                    int precedingEndTime   = precedingHandshape.EndTime;

                    // Ignore nested handshapes
                    if (precedingEndTime >= followingEndTime)
                    {
                        Log($"Skipped nested handshape starting at {precedingStartTime.TimeToString()}.");
                        continue;
                    }

                    int beat1Index = arrangement.Ebeats.FindIndex(b => b.Time > precedingEndTime);
                    var beat1      = arrangement.Ebeats[beat1Index - 1];
                    var beat2      = arrangement.Ebeats[beat1Index];

                    int  note32nd          = (beat2.Time - beat1.Time) / 8;
                    bool shortenBy16thNote = false;

                    // Check if the chord that starts the handshape is a LinkNext slide
                    var startChord = level.Chords.FindByTime(precedingStartTime);
                    if (startChord?.IsLinkNext == true && startChord?.ChordNotes?.Any(cn => cn.IsSlide) == true)
                    {
                        // Check if the handshape length is an 8th note or longer
                        if (precedingEndTime - precedingStartTime > note32nd * 4)
                        {
                            shortenBy16thNote = true;
                        }
                    }

                    int minDistance = shortenBy16thNote ? note32nd * 2 : note32nd;

                    // Shorten the min. distance required for 32nd notes or smaller
                    if (precedingEndTime - precedingStartTime <= note32nd)
                    {
                        minDistance = (beat2.Time - beat1.Time) / 12;
                    }

                    // Sometimes the following handshape may begin before the preceding one ends (floating point rounding error?)
                    int currentDistance = followingStartTime - precedingEndTime;

                    if (currentDistance < minDistance)
                    {
                        int newEndTime  = followingStartTime - minDistance;
                        int safetyCount = 0;

                        // Shorten the distance for very small note values
                        while (newEndTime <= precedingStartTime && ++safetyCount < 3)
                        {
                            minDistance /= 2;
                            newEndTime   = followingStartTime - minDistance;
#if DEBUG
                            Log("Reduced handshape min. distance by half.");
#endif
                        }

                        handShapes[i - 1].EndTime = newEndTime;

                        // Skip logging < 5ms adjustments
                        if (minDistance - currentDistance > 5)
                        {
                            var message = $"Adjusted the length of handshape starting at {precedingHandshape.StartTime.TimeToString()} (Distance: {currentDistance}ms -> {minDistance}ms)";
                            if (shortenBy16thNote)
                            {
                                message += " (Chord slide)";
                            }

                            Log(message);
                        }
                    }
                }
            }
        }
Beispiel #12
0
        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)
Beispiel #13
0
 public ProcessorContext(InstrumentalArrangement arrangement, Action <string> logAction)
 {
     this.arrangement = arrangement;
     this.logAction   = logAction;
 }
 public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
 {
     var  lastBeat        = arrangement.Ebeats[^ 1];
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            int hiDensRemoved = 0;

            void removeHighDensity(Chord chord, bool removeChordNotes)
            {
                if (chord.IsHighDensity)
                {
                    chord.IsHighDensity = false;
                    if (removeChordNotes)
                    {
                        // Set the chord as ignored if it has any harmonics in it
                        if (chord.ChordNotes?.Count > 0 && chord.ChordNotes.Any(cn => cn.IsHarmonic))
                        {
                            chord.IsIgnore = true;
                        }

                        chord.ChordNotes = null;
                    }
                    hiDensRemoved++;
                }
            }

            // Make sure that the version of the XML file is 8
            arrangement.Version = 8;

            foreach (var level in arrangement.Levels)
            {
                foreach (var hs in level.HandShapes)
                {
                    var chordsInHs =
                        from chord in level.Chords
                        where chord.Time >= hs.StartTime && chord.Time < hs.EndTime
                        select chord;

                    bool startsWithMute = false;
                    int  chordNum       = 0;

                    foreach (var chord in chordsInHs)
                    {
                        chordNum++;

                        // If the handshape starts with a fret hand mute, we need to be careful
                        if (chordNum == 1 && chord.IsFretHandMute)
                        {
                            startsWithMute = true;
                            // Frethand-muted chords without techniques should not have chord notes
                            if (chord.ChordNotes?.All(cn => cn.Sustain == 0) == true)
                            {
                                chord.ChordNotes = null;
                            }
                        }
                        else if (chordNum == 1)
                        {
                            // Do not remove the chord notes even if the first chord somehow has "high density"
                            removeHighDensity(chord, false);
                            continue;
                        }

                        if (startsWithMute && !chord.IsFretHandMute)
                        {
                            // Do not remove the chord notes on the first non-muted chord after muted chord(s)
                            removeHighDensity(chord, false);
                            startsWithMute = false;
                        }
                        else
                        {
                            removeHighDensity(chord, true);
                        }
                    }
                }
            }

            if (hiDensRemoved > 0)
            {
                Log($"{hiDensRemoved} high density statuses removed.");
            }
        }
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            var chordRenamed = new Dictionary <string, bool>();

            Log("Processing chord names...");

            for (int i = 0; i < arrangement.ChordTemplates.Count; i++)
            {
                var    currentChordTemplate = arrangement.ChordTemplates[i];
                string chordName            = currentChordTemplate.Name;

                // One fret handshape fret moving
                if (chordName.StartsWith("OF"))
                {
                    Match match = Regex.Match(chordName, @"\d+$");
                    if (match.Success)
                    {
                        sbyte newFretNumber = sbyte.Parse(match.Value, NumberFormatInfo.InvariantInfo);

                        for (int fretIndex = 0; fretIndex < 6; fretIndex++)
                        {
                            if (currentChordTemplate.Frets[fretIndex] == 0)
                            {
                                // Remove unnecessary open string notes
                                currentChordTemplate.Frets[fretIndex] = -1;
                            }
                            else if (currentChordTemplate.Frets[fretIndex] != -1)
                            {
                                currentChordTemplate.Frets[fretIndex] = newFretNumber;
                            }
                        }

                        Log($"Adjusted fret number of one fret chord: {currentChordTemplate.Name}");

                        // Remove chord name
                        currentChordTemplate.Name        = "";
                        currentChordTemplate.DisplayName = "";
                    }
                    else
                    {
                        const string errorMessage = "Unable to read fret value from OF chord.";
                        _statusMessages.Add(new ImproverMessage(errorMessage, MessageType.Warning));
                        Log(errorMessage);
                    }
                }

                string oldChordName   = currentChordTemplate.Name;
                string oldDisplayName = currentChordTemplate.DisplayName;

                string newChordName =
                    string.IsNullOrWhiteSpace(oldChordName) ?
                    string.Empty :
                    oldChordName.Replace("min", "m")
                    .Replace("CONV", "")
                    .Replace("-nop", "")
                    .Replace("-arp", "");

                string newDisplayName =
                    string.IsNullOrWhiteSpace(oldDisplayName) ?
                    string.Empty :
                    oldDisplayName.Replace("min", "m")
                    .Replace("CONV", "-arp");

                // Log message for changed chord names that are not empty
                if (newChordName != oldChordName && newChordName.Length != 0 && !chordRenamed.ContainsKey(oldChordName))
                {
                    if (oldChordName.Contains("CONV") || oldChordName.Contains("-arp"))
                    {
                        Log($"--Converted {newChordName} handshape into an arpeggio.");
                    }
                    else
                    {
                        Log($"--Renamed \"{oldChordName}\" to \"{newChordName}\"");
                    }

                    // Display renamed chords with the same name only once
                    chordRenamed[oldChordName] = true;
                }

                currentChordTemplate.Name        = newChordName;
                currentChordTemplate.DisplayName = newDisplayName;
            }
        }
Beispiel #17
0
        public void Apply(InstrumentalArrangement arrangement, Action <string> Log)
        {
            var ChordTemplates = arrangement.ChordTemplates;

            for (int chordId = 0; chordId < ChordTemplates.Count; chordId++)
            {
                var    currentChordTemplate = ChordTemplates[chordId];
                string chordName            = currentChordTemplate.Name;

                if (chordName.Contains("FIXOPEN"))
                {
                    // Remove open notes from the chord template
                    for (int i = 0; i < 6; ++i)
                    {
                        if (currentChordTemplate.Frets[i] == 0)
                        {
                            currentChordTemplate.Frets[i] = -1;
                        }
                    }

                    foreach (var level in arrangement.Levels)
                    {
                        var chordsToFix = from chord in level.Chords
                                          where chord.ChordId == chordId
                                          select chord;

                        foreach (var chord in chordsToFix)
                        {
                            if (chord.ChordNotes is null)
                            {
                                Log("ERROR: FIXOPEN chord does not have chord notes at " + chord.Time.TimeToString());
                                continue;
                            }

                            var chordNotesToRemove = chord.ChordNotes.Where(cn => cn.Fret == 0).ToArray();
                            // Store sustain of chord
                            int initSustain = chordNotesToRemove[0].Sustain;

                            foreach (var chordNote in chordNotesToRemove)
                            {
                                int sustain = initSustain;

                                var noteToRemove =
                                    (from note in level.Notes
                                     where note.Fret == 0 &&
                                     note.String == chordNote.String &&
                                     note.Time > chord.Time
                                     select note).FirstOrDefault();

                                while (noteToRemove is not null)
                                {
                                    sustain += noteToRemove.Sustain;

                                    if (noteToRemove.IsLinkNext)
                                    {
                                        level.Notes.Remove(noteToRemove);

                                        int nextIndex = level.Notes.FindIndex(n => n.Time > chordNote.Time && n.String == chordNote.String);
                                        noteToRemove = nextIndex == -1 ? null : level.Notes[nextIndex];
                                    }
                                    else
                                    {
                                        level.Notes.Remove(noteToRemove);
                                        break;
                                    }
                                }

                                // Add new note
                                var newNote = new Note(chordNote)
                                {
                                    Sustain    = sustain,
                                    IsLinkNext = false
                                };

                                int insertIndex = level.Notes.FindIndex(n => n.Time > chord.Time);
                                if (insertIndex != -1)
                                {
                                    level.Notes.Insert(insertIndex, newNote);
                                }
                                else
                                {
                                    // Should not be able to get here
                                    level.Notes.Insert(0, newNote);
                                }

                                // Remove open notes from chord
                                chord.ChordNotes.Remove(chordNote);
                            }

                            // Move chord and handshape forward 20 ms (hack for DDC)
                            const int amountToMove = 20;
                            var       handShape    = level.HandShapes.First(hs => hs.StartTime == chord.Time);
                            handShape.StartTime += amountToMove;
                            chord.Time          += amountToMove;

                            foreach (var cn in chord.ChordNotes)
                            {
                                cn.Time += amountToMove;

                                // Reduce sustain by amount moved
                                if (cn.Sustain >= amountToMove)
                                {
                                    cn.Sustain -= amountToMove;
                                }
                            }

                            Log($"Applied open note sustain fix to chord at {chord.Time.TimeToString()}");
                        }
                    }

                    // Set correct name
                    currentChordTemplate.Name        = currentChordTemplate.Name.Replace("FIXOPEN", "");
                    currentChordTemplate.DisplayName = currentChordTemplate.DisplayName.Replace("FIXOPEN", "");
                }
            }
        }
Beispiel #18
0
        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);
                }
            }
        }