예제 #1
0
        static byte[] GetInstrumentBytes(Song song, Song.Instrument instrument, ExportOptions exportOptions, float resolutionScaleRatio)
        {
            // Collect all bytes from each difficulty of the instrument, assigning the position for each event unsorted
            //List<SortableBytes> byteEvents = new List<SortableBytes>();
            SortableBytes[] easyBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Easy, exportOptions);
            SortableBytes[] mediumBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Medium, exportOptions);
            SortableBytes[] hardBytes   = GetChartSortableBytes(song, instrument, Song.Difficulty.Hard, exportOptions);
            SortableBytes[] expertBytes = GetChartSortableBytes(song, instrument, Song.Difficulty.Expert, exportOptions);

            SortableBytes[]      em           = SortableBytes.MergeAlreadySorted(easyBytes, mediumBytes);
            SortableBytes[]      he           = SortableBytes.MergeAlreadySorted(hardBytes, expertBytes);
            List <SortableBytes> sortedEvents = new List <SortableBytes>(SortableBytes.MergeAlreadySorted(em, he));

            // Perform merge sort to re-order everything correctly
            //SortableBytes[] sortedEvents = new SortableBytes[easyBytes.Length + mediumBytes.Length + hardBytes.Length + expertBytes.Length];//byteEvents.ToArray();
            //SortableBytes.Sort(sortedEvents);

            // Strip out duplicate events. This may occur with cymbal flags across multiple difficulties
            for (int i = sortedEvents.Count - 1; i >= 0; --i)
            {
                int next = i + 1;
                while (next < sortedEvents.Count && sortedEvents[i].tick == sortedEvents[next].tick)
                {
                    if (sortedEvents[i].bytes.SequenceEqual(sortedEvents[next].bytes))
                    {
                        sortedEvents.RemoveAt(next);
                    }

                    ++next;
                }
            }

            return(SortableBytesToTimedEventBytes(sortedEvents.ToArray(), song, exportOptions, resolutionScaleRatio));
        }
예제 #2
0
        static void InsertionSort(IList <SortableBytes> eventList, SortableBytes sortableByte)
        {
            int index = eventList.Count - 1;

            while (index >= 0 && sortableByte.tick < eventList[index].tick)
            {
                --index;
            }

            eventList.Insert(index + 1, sortableByte);
        }
예제 #3
0
        static byte[] GetUnrecognisedChartBytes(Chart chart, ExportOptions exportOptions, float resolutionScaleRatio)
        {
            List <SortableBytes> eventList = new List <SortableBytes>();

            foreach (ChartObject chartObject in chart.chartObjects)
            {
                SortableBytes onEvent  = null;
                SortableBytes offEvent = null;

                Note note = chartObject as Note;
                if (note != null)
                {
                    GetUnrecognisedChartNoteBytes(note, out onEvent, out offEvent);
                }

                Starpower sp = chartObject as Starpower;
                if (sp != null)     // Starpower cannot be split up between charts in a midi file
                {
                    GetStarpowerBytes(sp, out onEvent, out offEvent);
                }

                ChartEvent chartEvent = chartObject as ChartEvent;
                if (chartEvent != null)     // Text events cannot be split up in the file
                {
                    SortableBytes bytes = GetChartEventBytes(chartEvent);
                    InsertionSort(eventList, bytes);
                }

                if (onEvent != null && offEvent != null)
                {
                    InsertionSort(eventList, onEvent);

                    if (offEvent.tick == onEvent.tick)
                    {
                        ++offEvent.tick;
                    }

                    InsertionSort(eventList, offEvent);
                }
            }

            return(SortableBytesToTimedEventBytes(eventList.ToArray(), chart.song, exportOptions, resolutionScaleRatio));
        }
예제 #4
0
 static void GetNoteNumberBytes(int noteNumber, Note note, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     onEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)noteNumber, VELOCITY });
     offEvent = new SortableBytes(note.tick + note.length, new byte[] { OFF_EVENT, (byte)noteNumber, VELOCITY });
 }
예제 #5
0
 static void GetUnrecognisedChartNoteBytes(Note note, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     GetNoteNumberBytes(note.rawNote, note, out onEvent, out offEvent);
 }
예제 #6
0
 static void GetSoloBytes(ChartEvent solo, uint soloEndTick, out SortableBytes onEvent, out SortableBytes offEvent)
 {
     onEvent  = new SortableBytes(solo.tick, new byte[] { ON_EVENT, MidIOHelper.SOLO_NOTE, VELOCITY });
     offEvent = new SortableBytes(soloEndTick, new byte[] { OFF_EVENT, MidIOHelper.SOLO_NOTE, VELOCITY });
 }
예제 #7
0
        /* CHART EVENT BYTE DETERMINING
         ***********************************************************************************************/

        static void GetStarpowerBytes(Starpower sp, out SortableBytes onEvent, out SortableBytes offEvent)
        {
            onEvent  = new SortableBytes(sp.tick, new byte[] { ON_EVENT, MidIOHelper.STARPOWER_NOTE, VELOCITY });
            offEvent = new SortableBytes(sp.tick + sp.length, new byte[] { OFF_EVENT, MidIOHelper.STARPOWER_NOTE, VELOCITY });
        }
예제 #8
0
        static SortableBytes[] GetChartSortableBytes(Song song, Song.Instrument instrument, Song.Difficulty difficulty, ExportOptions exportOptions)
        {
            Chart chart = song.GetChart(instrument, difficulty);

            Chart.GameMode gameMode = chart.gameMode;

            if (exportOptions.copyDownEmptyDifficulty)
            {
                Song.Difficulty chartDiff = difficulty;
                while (chart.notes.Count <= 0)
                {
                    switch (chartDiff)
                    {
                    case (Song.Difficulty.Easy):
                        chartDiff = Song.Difficulty.Medium;
                        break;

                    case (Song.Difficulty.Medium):
                        chartDiff = Song.Difficulty.Hard;
                        break;

                    case (Song.Difficulty.Hard):
                        chartDiff = Song.Difficulty.Expert;
                        break;

                    case (Song.Difficulty.Expert):
                    default:
                        return(new SortableBytes[0]);
                    }

                    chart = song.GetChart(instrument, chartDiff);
                }
            }

            List <SortableBytes> eventList = new List <SortableBytes>();

            ChartEvent soloOnEvent = null;

            foreach (ChartObject chartObject in chart.chartObjects)
            {
                Note note = chartObject as Note;

                SortableBytes onEvent  = null;
                SortableBytes offEvent = null;

                if (note != null)
                {
                    int noteNumber = GetMidiNoteNumber(note, gameMode, difficulty);

                    GetNoteNumberBytes(noteNumber, note, out onEvent, out offEvent);

                    if (exportOptions.forced)
                    {
                        // Forced notes
                        if ((note.flags & Note.Flags.Forced) != 0 && note.type != Note.NoteType.Tap && (note.previous == null || (note.previous.tick != note.tick)))     // Don't overlap on chords
                        {
                            // Add a note
                            int difficultyNumber;
                            int forcingOffset;

                            if (!c_difficultyToMidiNoteWriteDict.TryGetValue(difficulty, out difficultyNumber))
                            {
                                throw new Exception("Unhandled difficulty");
                            }

                            if (!c_forcingMidiWriteOffsets.TryGetValue(note.type, out forcingOffset))
                            {
                                throw new Exception("Unhandled note type found when trying to write forcing flag");
                            }

                            int forcedNoteNumber = difficultyNumber + forcingOffset;

                            SortableBytes forceOnEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)forcedNoteNumber, VELOCITY });
                            SortableBytes forceOffEvent = new SortableBytes(note.tick + 1, new byte[] { OFF_EVENT, (byte)forcedNoteNumber, VELOCITY });

                            InsertionSort(eventList, forceOnEvent);
                            InsertionSort(eventList, forceOffEvent);
                        }

                        if (instrument == Song.Instrument.Drums && ((note.flags & Note.Flags.ProDrums_Cymbal) == 0))     // We want to write our flags if the cymbal is toggled OFF, as these notes are cymbals by default
                        {
                            int tomToggleNoteNumber;
                            if (MidIOHelper.PAD_TO_CYMBAL_LOOKUP.TryGetValue(note.drumPad, out tomToggleNoteNumber))
                            {
                                SortableBytes tomToggleOnEvent  = new SortableBytes(note.tick, new byte[] { ON_EVENT, (byte)tomToggleNoteNumber, VELOCITY });
                                SortableBytes tomToggleOffEvent = new SortableBytes(note.tick + 1, new byte[] { OFF_EVENT, (byte)tomToggleNoteNumber, VELOCITY });

                                InsertionSort(eventList, tomToggleOnEvent);
                                InsertionSort(eventList, tomToggleOffEvent);
                            }
                        }

                        int openNote = gameMode == Chart.GameMode.GHLGuitar ? (int)Note.GHLiveGuitarFret.Open : (int)Note.GuitarFret.Open;
                        // Add tap sysex events
                        if (difficulty == Song.Difficulty.Expert && note.rawNote != openNote && (note.flags & Note.Flags.Tap) != 0 && (note.previous == null || (note.previous.flags & Note.Flags.Tap) == 0))  // This note is a tap while the previous one isn't as we're creating a range
                        {
                            // Find the next non-tap note
                            Note nextNonTap = note;
                            while (nextNonTap.next != null && nextNonTap.rawNote != openNote && (nextNonTap.next.flags & Note.Flags.Tap) != 0)
                            {
                                nextNonTap = nextNonTap.next;
                            }

                            // Tap event = 08-50-53-00-00-FF-04-01, end with 01 for On, 00 for Off
                            byte[] tapOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_ON, SYSEX_END };
                            byte[] tapOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, 0xFF, 0x04, SYSEX_OFF, SYSEX_END };

                            SortableBytes tapOnEvent  = new SortableBytes(note.tick, tapOnEventBytes);
                            SortableBytes tapOffEvent = new SortableBytes(nextNonTap.tick + 1, tapOffEventBytes);

                            InsertionSort(eventList, tapOnEvent);
                            InsertionSort(eventList, tapOffEvent);
                        }
                    }

                    if (gameMode != Chart.GameMode.Drums && gameMode != Chart.GameMode.GHLGuitar &&
                        difficulty == Song.Difficulty.Expert && note.guitarFret == Note.GuitarFret.Open && (note.previous == null || (note.previous.guitarFret != Note.GuitarFret.Open)))
                    {
                        // Find the next non-open note
                        Note nextNonOpen = note;
                        while (nextNonOpen.next != null && nextNonOpen.next.guitarFret == Note.GuitarFret.Open)
                        {
                            nextNonOpen = nextNonOpen.next;
                        }

                        byte diff;

                        switch (difficulty)
                        {
                        case (Song.Difficulty.Easy):
                            diff = 0;
                            break;

                        case (Song.Difficulty.Medium):
                            diff = 1;
                            break;

                        case (Song.Difficulty.Hard):
                            diff = 2;
                            break;

                        case (Song.Difficulty.Expert):
                            diff = 3;
                            break;

                        default:
                            continue;
                        }

                        byte[] openOnEventBytes  = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_ON, SYSEX_END };
                        byte[] openOffEventBytes = new byte[] { SYSEX_START, 0x08, 0x50, 0x53, 0x00, 0x00, diff, 0x01, SYSEX_OFF, SYSEX_END };

                        SortableBytes openOnEvent  = new SortableBytes(note.tick, openOnEventBytes);
                        SortableBytes openOffEvent = new SortableBytes(nextNonOpen.tick + 1, openOffEventBytes);

                        InsertionSort(eventList, openOnEvent);
                        InsertionSort(eventList, openOffEvent);
                    }
                }

                Starpower sp = chartObject as Starpower;
                if (sp != null && difficulty == Song.Difficulty.Expert)     // Starpower cannot be split up between charts in a midi file
                {
                    GetStarpowerBytes(sp, out onEvent, out offEvent);
                }

                ChartEvent chartEvent = chartObject as ChartEvent;
                if (chartEvent != null && difficulty == Song.Difficulty.Expert)     // Text events cannot be split up in the file
                {
                    if (soloOnEvent != null && chartEvent.eventName == MidIOHelper.SoloEndEventText)
                    {
                        GetSoloBytes(soloOnEvent, chartEvent.tick, out onEvent, out offEvent);
                        soloOnEvent = null;
                    }
                    else if (chartEvent.eventName == MidIOHelper.SoloEventText)
                    {
                        soloOnEvent = chartEvent;
                    }
                    else
                    {
                        InsertionSort(eventList, GetChartEventBytes(chartEvent));
                    }
                }

                if (onEvent != null && offEvent != null)
                {
                    InsertionSort(eventList, onEvent);

                    if (offEvent.tick == onEvent.tick)
                    {
                        ++offEvent.tick;
                    }

                    InsertionSort(eventList, offEvent);
                }
            }

            if (soloOnEvent != null)        // Found a solo event with no end. Assume the solo lasts for the rest of the song
            {
                SortableBytes onEvent  = null;
                SortableBytes offEvent = null;

                uint soloEndTick = chart.chartObjects[chart.chartObjects.Count - 1].tick;   // In order to get a solo event the chart objects needed to have some object in this container, no need to check size, hopefully...
                GetSoloBytes(soloOnEvent, soloEndTick, out onEvent, out offEvent);

                if (onEvent != null && offEvent != null)
                {
                    InsertionSort(eventList, onEvent);

                    if (offEvent.tick == onEvent.tick)
                    {
                        ++offEvent.tick;
                    }

                    InsertionSort(eventList, offEvent);
                }
            }

            return(eventList.ToArray());
        }