public void TemporaryBeatRemover_RemovesAddedBeats()
        {
            var testBeat   = new Ebeat(222222, 1);
            var addedBeats = new List <Ebeat> {
                testBeat
            };

            testArrangement.Ebeats.Add(testBeat);

            new TemporaryBeatRemover(addedBeats).Apply(testArrangement, nullLog);

            testArrangement.Ebeats.Should().NotContain(testBeat);
        }
Пример #2
0
        public void BeatsInOrder(SngFile song)
        {
            Ebeat lastBeat = null;

            foreach (var beat in song.Beats)
            {
                if (lastBeat != null)
                {
                    Assert.IsTrue(lastBeat.Time < beat.Time);
                    if (lastBeat.Measure == beat.Measure)
                    {
                        Assert.AreEqual(lastBeat.Beat + 1, beat.Beat);
                    }
                    else
                    {
                        Assert.AreEqual(beat.Beat, 0);
                        Assert.LessOrEqual(lastBeat.Measure + 1, beat.Measure);
                    }
                }
                lastBeat = beat;
            }
        }
        public TabFile(SngFile sngFile, int maxDifficulty)
        {
            StringCount = 6;

            _measureQueue   = new List <TabMeasure>();
            _currentMeasure = null;
            Lines           = new List <string>(); // holds text stream

            // Create the tuning information table
            TuningInfos = new Dictionary <Tuning, TuningInfo>();
            TuningInfos[Tuning.Standard] = new TuningInfo(this, Tuning.Standard, "Standard", new string[] { "e", "B", "G", "D", "A", "E" });
            TuningInfos[Tuning.DropD]    = new TuningInfo(this, Tuning.DropD, "Drop D", new string[] { "e", "B", "G", "D", "A", "D" });
            TuningInfos[Tuning.EFlat]    = new TuningInfo(this, Tuning.EFlat, "E Flat", new string[] { "eb", "Bb", "Gb", "Db", "Ab", "Eb" });
            TuningInfos[Tuning.OpenG]    = new TuningInfo(this, Tuning.OpenG, "Open G", new string[] { "D", "B", "D", "G", "D", "G" });

            TuningInfo = TuningInfos[(Tuning)sngFile.Metadata.Tuning];

            // Start assembling the tab
            TabHeader header = new TabHeader(this, sngFile);

            /* Tab Entities are the parts that together make up a tab, i.e. notes, chords, measures and sections.
             * Since the notes and chords are all contained in a single array without any measure or section data,
             * the measure and section data needs to be merged with notes and chords so that all of that data can be
             * iterated over in the correct order. Entities get sorted by time index (the SortedList key).
             * Multiple entities (of different types) can have the same time index and need to be processed in the
             * correct order, i.e. sections before measures and measures before notes/chords. */
            SortedList <float, LinkedList <TabEntity> > entities = new SortedList <float, LinkedList <TabEntity> >();

            // Add SECTION entities
            foreach (SongSection s in sngFile.SongSections)
            {
                addEntity(new TabSection(this, s), entities);
            }

            // Add MEASURE entities
            float lastTime    = sngFile.Beats[0].Time;
            int   beatCount   = 1;
            int   lastMeasure = sngFile.Beats[0].Measure;

            for (int i = 1; i < sngFile.Beats.Length; i++)
            {
                Ebeat b = sngFile.Beats[i];
                // To indicate that the current beat is simply the next beat in the current measure and does not define
                // a new measure, either set b.Measure to -1 or to the same value as the beat before
                if (b.Measure == -1 || b.Measure == lastMeasure)
                {
                    beatCount++;
                }
                else
                {
                    addEntity(new TabMeasure(this, lastTime, b.Time, beatCount), entities);
                    lastTime    = b.Time;
                    beatCount   = 1;
                    lastMeasure = b.Measure;
                }
            }
            // Don't forget the final measure
            TabMeasure finalMeasure = new TabMeasure(this, lastTime, sngFile.Beats[sngFile.Beats.Length - 1].Time, beatCount);

            addEntity(finalMeasure, entities);

            MaxDifficulty = 0;
            // Add NOTE / CHORD entities
            foreach (PhraseIteration pi in sngFile.PhraseIterations)
            {
                Phrase p = sngFile.Phrases[pi.Id];
                if (MaxDifficulty < p.MaxDifficulty)
                {
                    MaxDifficulty = p.MaxDifficulty;
                }

                int         level = Math.Min(maxDifficulty, p.MaxDifficulty);
                List <Note> notes = new List <Note>();

                foreach (Note n in sngFile.SongLevels[level].Notes)
                {
                    if (n.Time < pi.StartTime || n.Time > pi.EndTime)
                    {
                        continue;
                    }

                    if (n.ChordId != -1)
                    {
                        addEntity(new TabChord(this, n, sngFile), entities);
                    }
                    else
                    {
                        addEntity(new TabNote(this, n), entities);
                    }
                }
            }

            // Now that the entity list has been build, iterate over it and Apply every single entity to this TabFile,
            // which will format the entity's data and add it to the TabFile's output buffer.
            header.Apply(this, maxDifficulty);
            foreach (LinkedList <TabEntity> lle in entities.Values)
            {
                foreach (TabEntity e in lle)
                {
                    e.Apply(this); // "this" contains a stream to make the tablature text
                }
            }
            flushMeasureQueue(); // Don't forget, otherwise the last section's measures will be missing
        }
Пример #4
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);
                }
            }
        }