diff --git a/Assets/Plugins/MoonscraperChartParser/Events/BPM.cs b/Assets/Plugins/MoonscraperChartParser/Events/BPM.cs index 6d437dbdf..3fbf41323 100644 --- a/Assets/Plugins/MoonscraperChartParser/Events/BPM.cs +++ b/Assets/Plugins/MoonscraperChartParser/Events/BPM.cs @@ -22,7 +22,7 @@ public float displayValue } } - public float? anchor = null; + public double? anchor = null; /// /// Basic constructor. @@ -41,7 +41,7 @@ public BPM(BPM _bpm) : base(_bpm.tick) anchor = _bpm.anchor; } - public float assignedTime = 0; + public double assignedTime = 0; public override SongObject Clone() { diff --git a/Assets/Plugins/MoonscraperChartParser/Events/MoonNote.cs b/Assets/Plugins/MoonscraperChartParser/Events/MoonNote.cs index 10ba594bc..49106f1be 100644 --- a/Assets/Plugins/MoonscraperChartParser/Events/MoonNote.cs +++ b/Assets/Plugins/MoonscraperChartParser/Events/MoonNote.cs @@ -44,7 +44,7 @@ public enum GHLiveGuitarFret Open } - public enum NoteType + public enum MoonNoteType { Natural, Strum, @@ -388,7 +388,7 @@ public int GetMaskWithRequiredFlags(Flags flags) /// /// Live calculation of what Note_Type this note would currently be. /// - public NoteType type + public MoonNoteType type { get { @@ -396,23 +396,23 @@ public NoteType type { if (!this.IsOpenNote() && (flags & Flags.ProDrums_Cymbal) == Flags.ProDrums_Cymbal) { - return NoteType.Cymbal; + return MoonNoteType.Cymbal; } - return NoteType.Strum; + return MoonNoteType.Strum; } else { if (!this.IsOpenNote() && (flags & Flags.Tap) == Flags.Tap) { - return NoteType.Tap; + return MoonNoteType.Tap; } else { if (isHopo) - return NoteType.Hopo; + return MoonNoteType.Hopo; else - return NoteType.Strum; + return MoonNoteType.Strum; } } } diff --git a/Assets/Plugins/MoonscraperChartParser/Events/SongObject.cs b/Assets/Plugins/MoonscraperChartParser/Events/SongObject.cs index 8fc00a6a8..431fdb8d6 100644 --- a/Assets/Plugins/MoonscraperChartParser/Events/SongObject.cs +++ b/Assets/Plugins/MoonscraperChartParser/Events/SongObject.cs @@ -36,7 +36,7 @@ public SongObject(uint _tick) /// /// Automatically converts the object's tick position into the time it will appear in the song. /// - public float time + public double time { get { diff --git a/Assets/Plugins/MoonscraperChartParser/IO/Chart/ChartReader.cs b/Assets/Plugins/MoonscraperChartParser/IO/Chart/ChartReader.cs index 7bdfc4dfb..5daa58899 100644 --- a/Assets/Plugins/MoonscraperChartParser/IO/Chart/ChartReader.cs +++ b/Assets/Plugins/MoonscraperChartParser/IO/Chart/ChartReader.cs @@ -22,7 +22,7 @@ public static class ChartReader struct Anchor { public uint tick; - public float anchorTime; + public double anchorTime; } struct NoteFlag diff --git a/Assets/Plugins/MoonscraperChartParser/IO/Midi/MidReader.cs b/Assets/Plugins/MoonscraperChartParser/IO/Midi/MidReader.cs index b298ff8dc..f4c3d4987 100644 --- a/Assets/Plugins/MoonscraperChartParser/IO/Midi/MidReader.cs +++ b/Assets/Plugins/MoonscraperChartParser/IO/Midi/MidReader.cs @@ -102,7 +102,7 @@ struct EventProcessParams { { MidIOHelper.SYSEX_CODE_GUITAR_OPEN, ProcessSysExEventPairAsOpenNoteModifier }, { MidIOHelper.SYSEX_CODE_GUITAR_TAP, (in EventProcessParams eventProcessParams) => { - ProcessSysExEventPairAsForcedType(eventProcessParams, MoonNote.NoteType.Tap); + ProcessSysExEventPairAsForcedType(eventProcessParams, MoonNote.MoonNoteType.Tap); }}, }; @@ -110,7 +110,7 @@ struct EventProcessParams { { MidIOHelper.SYSEX_CODE_GUITAR_OPEN, ProcessSysExEventPairAsOpenNoteModifier }, { MidIOHelper.SYSEX_CODE_GUITAR_TAP, (in EventProcessParams eventProcessParams) => { - ProcessSysExEventPairAsForcedType(eventProcessParams, MoonNote.NoteType.Tap); + ProcessSysExEventPairAsForcedType(eventProcessParams, MoonNote.MoonNoteType.Tap); }}, }; @@ -274,7 +274,7 @@ public static MoonSong ReadMidi(string path, ref CallbackState callBackState) break; } } - + // Display messages to user ProcessPendingUserMessages(messageList, ref callBackState); @@ -283,39 +283,39 @@ public static MoonSong ReadMidi(string path, ref CallbackState callBackState) static void ProcessPendingUserMessages(IList messageList, ref CallbackState callBackState) { - if (messageList == null) - { - Debug.Assert(false, $"No message list provided to {nameof(ProcessPendingUserMessages)}!"); - return; - } - - foreach (var processParams in messageList) - { + if (messageList == null) + { + Debug.Assert(false, $"No message list provided to {nameof(ProcessPendingUserMessages)}!"); + return; + } + + foreach (var processParams in messageList) + { #if UNITY_EDITOR - // The editor freezes when its message box API is used during parsing, - // we use the params to determine whether or not to execute actions instead - if (!processParams.executeInEditor) - { - Debug.Log("Auto-skipping action for message: " + processParams.message); - continue; - } - else - { - Debug.Log("Auto-executing action for message: " + processParams.message); - processParams.processFn(processParams); - } + // The editor freezes when its message box API is used during parsing, + // we use the params to determine whether or not to execute actions instead + if (!processParams.executeInEditor) + { + Debug.Log("Auto-skipping action for message: " + processParams.message); + continue; + } + else + { + Debug.Log("Auto-executing action for message: " + processParams.message); + processParams.processFn(processParams); + } #else - callBackState = CallbackState.WaitingForExternalInformation; - NativeMessageBox.Result result = NativeMessageBox.Show(processParams.message, processParams.title, NativeMessageBox.Type.YesNo, null); - callBackState = CallbackState.None; - if (result == NativeMessageBox.Result.Yes) - { - processParams.processFn(processParams); - } + // callBackState = CallbackState.WaitingForExternalInformation; + // NativeMessageBox.Result result = NativeMessageBox.Show(processParams.message, processParams.title, NativeMessageBox.Type.YesNo, null); + // callBackState = CallbackState.None; + // if (result == NativeMessageBox.Result.Yes) + // { + // processParams.processFn(processParams); + // } #endif - } + } } - + static void ReadTrack(IList track) { foreach (var me in track) @@ -738,7 +738,7 @@ static IReadOnlyDictionary BuildGuitarMidiNoteNumberToProce { { MidIOHelper.STARPOWER_NOTE, ProcessNoteOnEventAsStarpower }, { MidIOHelper.TAP_NOTE_CH, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, MoonNote.NoteType.Tap); + ProcessNoteOnEventAsForcedType(eventProcessParams, MoonNote.MoonNoteType.Tap); }}, { MidIOHelper.SOLO_NOTE, (in EventProcessParams eventProcessParams) => { ProcessNoteOnEventAsEvent(eventProcessParams, MidIOHelper.SOLO_EVENT_TEXT, 0, MidIOHelper.SOLO_END_EVENT_TEXT, SOLO_END_CORRECTION_OFFSET); @@ -780,14 +780,14 @@ static IReadOnlyDictionary BuildGuitarMidiNoteNumberToProce int flagKey = difficultyStartRange + 5; processFnDict.Add(flagKey, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.NoteType.Hopo); + ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.MoonNoteType.Hopo); }); } { int flagKey = difficultyStartRange + 6; processFnDict.Add(flagKey, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.NoteType.Strum); + ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.MoonNoteType.Strum); }); } }; @@ -801,7 +801,7 @@ static IReadOnlyDictionary BuildGhlGuitarMidiNoteNumberToPr { { MidIOHelper.STARPOWER_NOTE, ProcessNoteOnEventAsStarpower }, { MidIOHelper.TAP_NOTE_CH, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, MoonNote.NoteType.Tap); + ProcessNoteOnEventAsForcedType(eventProcessParams, MoonNote.MoonNoteType.Tap); }}, { MidIOHelper.SOLO_NOTE, (in EventProcessParams eventProcessParams) => { ProcessNoteOnEventAsEvent(eventProcessParams, MidIOHelper.SOLO_EVENT_TEXT, 0, MidIOHelper.SOLO_END_EVENT_TEXT, SOLO_END_CORRECTION_OFFSET); @@ -842,14 +842,14 @@ static IReadOnlyDictionary BuildGhlGuitarMidiNoteNumberToPr int flagKey = difficultyStartRange + 7; processFnDict.Add(flagKey, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.NoteType.Hopo); + ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.MoonNoteType.Hopo); }); } { int flagKey = difficultyStartRange + 8; processFnDict.Add(flagKey, (in EventProcessParams eventProcessParams) => { - ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.NoteType.Strum); + ProcessNoteOnEventAsForcedType(eventProcessParams, difficulty, MoonNote.MoonNoteType.Strum); }); } }; @@ -1035,7 +1035,7 @@ static void ProcessNoteOnEventAsDrumRoll(in EventProcessParams eventProcessParam } } - static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessParams, MoonNote.NoteType noteType) + static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessParams, MoonNote.MoonNoteType moonNoteType) { var flagEvent = eventProcessParams.midiEvent as NoteOnEvent; Debug.Assert(flagEvent != null, $"Wrong note event type passed to {nameof(ProcessNoteOnEventAsForcedType)}. Expected: {typeof(NoteOnEvent)}, Actual: {eventProcessParams.midiEvent.GetType()}"); @@ -1048,12 +1048,12 @@ static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessPar // Delay the actual processing once all the notes are actually in eventProcessParams.delayedProcessesList.Add((in EventProcessParams processParams) => { - ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, noteType); + ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, moonNoteType); }); } } - static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessParams, MoonSong.Difficulty difficulty, MoonNote.NoteType noteType) + static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessParams, MoonSong.Difficulty difficulty, MoonNote.MoonNoteType moonNoteType) { var flagEvent = eventProcessParams.midiEvent as NoteOnEvent; Debug.Assert(flagEvent != null, $"Wrong note event type passed to {nameof(ProcessNoteOnEventAsForcedType)}. Expected: {typeof(NoteOnEvent)}, Actual: {eventProcessParams.midiEvent.GetType()}"); @@ -1064,11 +1064,11 @@ static void ProcessNoteOnEventAsForcedType(in EventProcessParams eventProcessPar // Delay the actual processing once all the notes are actually in eventProcessParams.delayedProcessesList.Add((in EventProcessParams processParams) => { - ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, difficulty, noteType); + ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, difficulty, moonNoteType); }); } - static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcessParams, uint startTick, uint endTick, MoonSong.Difficulty difficulty, MoonNote.NoteType noteType) + static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcessParams, uint startTick, uint endTick, MoonSong.Difficulty difficulty, MoonNote.MoonNoteType moonNoteType) { var song = eventProcessParams.moonSong; var instrument = eventProcessParams.moonInstrument; @@ -1100,9 +1100,9 @@ static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcess expectedForceFailure = false; shouldBeForced = false; - switch (noteType) + switch (moonNoteType) { - case (MoonNote.NoteType.Strum): + case (MoonNote.MoonNoteType.Strum): { if (!moonNote.isChord && moonNote.isNaturalHopo) { @@ -1111,7 +1111,7 @@ static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcess break; } - case (MoonNote.NoteType.Hopo): + case (MoonNote.MoonNoteType.Hopo): { // Forcing consecutive same-fret HOPOs is possible in charts, but we do not allow it // (see RB2's chart of Steely Dan - Bodhisattva) @@ -1127,7 +1127,7 @@ static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcess break; } - case (MoonNote.NoteType.Tap): + case (MoonNote.MoonNoteType.Tap): { if (!moonNote.IsOpenNote()) { @@ -1149,7 +1149,7 @@ static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcess } default: - Debug.Assert(false, $"Unhandled note type {noteType} in .mid forced type processing"); + Debug.Assert(false, $"Unhandled note type {moonNoteType} in .mid forced type processing"); continue; // Unhandled } @@ -1167,7 +1167,7 @@ static void ProcessEventAsForcedTypePostDelay(in EventProcessParams eventProcess lastChordTick = moonNote.tick; - Debug.Assert(moonNote.type == noteType || expectedForceFailure, $"Failed to set forced type! Expected: {noteType} Actual: {moonNote.type} Natural HOPO: {moonNote.isNaturalHopo} Chord: {moonNote.isChord} Forceable: {!moonNote.cannotBeForced}\non {difficulty} {instrument} at tick {moonNote.tick} ({TimeSpan.FromSeconds(moonNote.time):mm':'ss'.'ff})"); + Debug.Assert(moonNote.type == moonNoteType || expectedForceFailure, $"Failed to set forced type! Expected: {moonNoteType} Actual: {moonNote.type} Natural HOPO: {moonNote.isNaturalHopo} Chord: {moonNote.isChord} Forceable: {!moonNote.cannotBeForced}\non {difficulty} {instrument} at tick {moonNote.tick} ({TimeSpan.FromSeconds(moonNote.time):mm':'ss'.'ff})"); } } @@ -1301,7 +1301,7 @@ static void ProcessTextEventPairAsStarpower(in EventProcessParams eventProcessPa } } - static void ProcessSysExEventPairAsForcedType(in EventProcessParams eventProcessParams, MoonNote.NoteType noteType) + static void ProcessSysExEventPairAsForcedType(in EventProcessParams eventProcessParams, MoonNote.MoonNoteType moonNoteType) { var startEvent = eventProcessParams.midiEvent as PhaseShiftSysExStart; Debug.Assert(startEvent != null, $"Wrong note event type passed to {nameof(ProcessSysExEventPairAsForcedType)}. Expected: {typeof(PhaseShiftSysExStart)}, Actual: {eventProcessParams.midiEvent.GetType()}"); @@ -1317,7 +1317,7 @@ static void ProcessSysExEventPairAsForcedType(in EventProcessParams eventProcess { eventProcessParams.delayedProcessesList.Add((in EventProcessParams processParams) => { - ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, noteType); + ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, moonNoteType); }); } } @@ -1326,7 +1326,7 @@ static void ProcessSysExEventPairAsForcedType(in EventProcessParams eventProcess var diff = MidIOHelper.SYSEX_TO_MS_DIFF_LOOKUP[startEvent.difficulty]; eventProcessParams.delayedProcessesList.Add((in EventProcessParams processParams) => { - ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, noteType); + ProcessEventAsForcedTypePostDelay(processParams, startTick, endTick, diff, moonNoteType); }); } } diff --git a/Assets/Plugins/MoonscraperChartParser/MoonSong.cs b/Assets/Plugins/MoonscraperChartParser/MoonSong.cs index 368c87d4d..f05466e06 100644 --- a/Assets/Plugins/MoonscraperChartParser/MoonSong.cs +++ b/Assets/Plugins/MoonscraperChartParser/MoonSong.cs @@ -63,6 +63,8 @@ public IO.ExportOptions defaultExportOptions MoonChart[] charts; public List unrecognisedCharts = new List(); + public IReadOnlyList Charts => charts.ToList(); + public List _events; List _syncTrack; @@ -238,7 +240,7 @@ public bool ChartExistsForInstrument(MoonInstrument moonInstrument) /// The time (in seconds) to convert. /// Ticks per beat, usually provided from the resolution song of a Song class. /// Returns the calculated tick position. - public uint TimeToTick(float time, float resolution) + public uint TimeToTick(double time, float resolution) { if (time < 0) time = 0; @@ -295,7 +297,7 @@ public Section GetPrevSection(uint position) /// /// Tick position. /// Returns the time in seconds. - public float TickToTime(uint position) + public double TickToTime(uint position) { return TickToTime(position, this.resolution); } @@ -306,15 +308,15 @@ public float TickToTime(uint position) /// Tick position. /// Ticks per beat, usually provided from the resolution song of a Song class. /// Returns the time in seconds. - public float TickToTime(uint position, float resolution) + public double TickToTime(uint position, float resolution) { int previousBPMPos = SongObjectHelper.FindClosestPosition(position, bpms); if (bpms[previousBPMPos].tick > position) --previousBPMPos; BPM prevBPM = bpms[previousBPMPos]; - float time = prevBPM.assignedTime; - time += (float)TickFunctions.DisToTime(prevBPM.tick, position, resolution, prevBPM.value / 1000.0f); + double time = prevBPM.assignedTime; + time += TickFunctions.DisToTime(prevBPM.tick, position, resolution, prevBPM.value / 1000.0f); return time; } @@ -452,17 +454,17 @@ void UpdateBPMTimeValues() foreach (BPM bpm in bpms) { time += TickFunctions.DisToTime(prevBPM.tick, bpm.tick, resolution, prevBPM.value / 1000.0f); - bpm.assignedTime = (float)time; + bpm.assignedTime = time; prevBPM = bpm; } } - public float LiveTickToTime(uint position, float resolution) + public double LiveTickToTime(uint position, float resolution) { return LiveTickToTime(position, resolution, bpms[0], _syncTrack); } - public static float LiveTickToTime(uint position, float resolution, BPM initialBpm, IList synctrack) + public static double LiveTickToTime(uint position, float resolution, BPM initialBpm, IList synctrack) { double time = 0; BPM prevBPM = initialBpm; @@ -487,12 +489,12 @@ public static float LiveTickToTime(uint position, float resolution, BPM initialB time += TickFunctions.DisToTime(prevBPM.tick, position, resolution, prevBPM.value / 1000.0f); - return (float)time; + return time; } public float ResolutionScaleRatio(float targetResoltion) { - return (targetResoltion / (float)resolution); + return (targetResoltion / resolution); } public string GetAudioName(AudioInstrument audio) diff --git a/Assets/Plugins/MoonscraperChartParser/TickFunctions.cs b/Assets/Plugins/MoonscraperChartParser/TickFunctions.cs index f4619fc09..0e0805c5f 100644 --- a/Assets/Plugins/MoonscraperChartParser/TickFunctions.cs +++ b/Assets/Plugins/MoonscraperChartParser/TickFunctions.cs @@ -27,7 +27,7 @@ public static double DisToBpm(uint tickStart, uint tickEnd, double deltatime, do return (tickEnd - tickStart) / resolution * SECONDS_PER_MINUTE / deltatime; } - public static uint TimeToDis(float timeStart, float timeEnd, float resolution, float bpm) + public static uint TimeToDis(double timeStart, double timeEnd, float resolution, float bpm) { return (uint)Math.Round((timeEnd - timeStart) * bpm / SECONDS_PER_MINUTE * resolution); } diff --git a/Assets/Script/Chart.meta b/Assets/Script/Chart.meta new file mode 100644 index 000000000..3e28fcbca --- /dev/null +++ b/Assets/Script/Chart.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f18a0e044630b6a4697eb243d11f7e7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Chart/Beat.cs b/Assets/Script/Chart/Beat.cs new file mode 100644 index 000000000..3ac149f98 --- /dev/null +++ b/Assets/Script/Chart/Beat.cs @@ -0,0 +1,20 @@ +namespace YARG.Chart { + public struct Beat { + public uint Position; + public float Time; + public BeatStyle Style; + + public Beat(uint position, float time, BeatStyle type) { + Position = position; + Time = time; + Style = type; + } + } + + public enum BeatStyle { + MEASURE, + STRONG, + WEAK, + } + +} \ No newline at end of file diff --git a/Assets/Script/Chart/Beat.cs.meta b/Assets/Script/Chart/Beat.cs.meta new file mode 100644 index 000000000..c6029599f --- /dev/null +++ b/Assets/Script/Chart/Beat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4b5d082b9afd4b4f88c94c2e01e3222d +timeCreated: 1682548245 \ No newline at end of file diff --git a/Assets/Script/Chart/BeatHandler.cs b/Assets/Script/Chart/BeatHandler.cs new file mode 100644 index 000000000..f43178c54 --- /dev/null +++ b/Assets/Script/Chart/BeatHandler.cs @@ -0,0 +1,158 @@ +using System.Collections.Generic; +using MoonscraperChartEditor.Song; + +namespace YARG.Chart { + public class BeatHandler { + + private readonly MoonSong _song; + + public List Beats; + public List Measures; + + public int TotalBeatCount => Beats.Count; + private int lastMeasure; + + public int CurrentMeasure { get; private set; } + + public BeatHandler(MoonSong song) { + _song = song; + + Beats = new List(); + Measures = new List(); + } + + private void AddBeat(uint tick, BeatStyle type) { + Beats.Add(new Beat(tick, (float) _song.TickToTime(tick), type)); + + if (type == BeatStyle.MEASURE) + Measures.Add(Beats[^1]); + } + + // Thanks mdsitton for the CH beat generation algorithm when I was making SideyBot :) + public void GenerateBeats() { + uint currentTick = 0; + uint lastTick = _song.GetLastTick(); + + // How many ticks to move forward to get to the next beat. + uint forwardStep = 0; + + TimeSignature lastTS = null; + TimeSignature currentTS; + + int currentBeatInMeasure = 0; + int beatsInTS = 0; + + // The last beat style that was generated. Always starts as a measure. + BeatStyle lastStyle = BeatStyle.MEASURE; + + // lastTick + forwardStep ensures we will always look ahead 1 more beat than the lastTick. + while (currentTick < lastTick + forwardStep || lastStyle != BeatStyle.MEASURE) { + // Gets previous time signature before currentTick. + currentTS = _song.GetPrevTS(currentTick); + + // The number of "beats within a beat" there is + uint currentSubBeat = currentTS.denominator / 4; + + bool hasTsChanged = lastTS == null || lastTS.numerator != currentTS.numerator || lastTS.denominator != currentTS.denominator; + + // If denominator is larger than 4 start off with weak beats, if 4 or less use strong beats + var style = currentTS.denominator > 4 ? BeatStyle.WEAK : BeatStyle.STRONG; + + // New time signature. First beat of a new time sig is always a measure. + if (hasTsChanged) { + beatsInTS = 0; + currentBeatInMeasure = 0; + } + + // Beat count is equal to TS numerator, so its a new measure. + if (currentBeatInMeasure == currentTS.numerator) { + currentBeatInMeasure = 0; + } + + if (currentTS.denominator <= 4 || currentBeatInMeasure % currentSubBeat == 0) { + style = BeatStyle.STRONG; + } + + // Make it a measure if first beat of a measure. + if (currentBeatInMeasure == 0) { + style = BeatStyle.MEASURE; + + // Handle 1/x TS's so that only the first beat in the TS gets a measure line + // and then from there it is marked at a strong beat every quarter note with everything else as weak + if (currentTS.numerator == 1 && beatsInTS > 0) { + if (currentTick >= lastTick) { + style = BeatStyle.MEASURE; + } + // if not quarter note length beats every quarter note is stressed + else if (currentTS.denominator <= 4 || (beatsInTS % currentSubBeat) == 0) { + style = BeatStyle.STRONG; + } else { + style = BeatStyle.WEAK; + } + } + } + + // Last beat should never be a strong beat if denominator is bigger than 4. + if (currentBeatInMeasure == currentTS.numerator - 1 && currentTS.denominator > 4) { + style = BeatStyle.WEAK; + } + + AddBeat(currentTick, style); + + currentBeatInMeasure++; + beatsInTS++; + + forwardStep = (uint) (_song.resolution * 4) / currentTS.denominator; + currentTick += forwardStep; + lastTS = currentTS; + lastStyle = style; + } + + lastMeasure = Measures.Count - 1; + } + + public void UpdateCurrentMeasure(double songTime) { + while (CurrentMeasure <= lastMeasure && songTime > Measures[CurrentMeasure].Time) { + CurrentMeasure++; + } + } + + public int GetNoteMeasure(Note note) { + int dif = CurrentMeasure; + + while (dif < Measures.Count && note.Time >= Measures[dif].Time) + dif++; + + return dif; + } + } + + public static class SongHelpers { + public static uint GetLastTick(this MoonSong song) + { + uint lastTick = 0; + foreach (var songEvent in song.events) + { + if (songEvent.tick > lastTick) + { + lastTick = songEvent.tick; + } + } + foreach (var chart in song.Charts) + { + foreach (var songObject in chart.chartObjects) { + if (songObject.tick <= lastTick) continue; + + lastTick = songObject.tick; + + if (songObject is MoonNote note) + { + lastTick += note.length; + } + } + } + + return lastTick; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Chart/BeatHandler.cs.meta b/Assets/Script/Chart/BeatHandler.cs.meta new file mode 100644 index 000000000..b60fad835 --- /dev/null +++ b/Assets/Script/Chart/BeatHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5bb1a915203d4b2c9f35b10a440f6221 +timeCreated: 1682548376 \ No newline at end of file diff --git a/Assets/Script/Chart/DrumNote.cs b/Assets/Script/Chart/DrumNote.cs new file mode 100644 index 000000000..431b4bf34 --- /dev/null +++ b/Assets/Script/Chart/DrumNote.cs @@ -0,0 +1,15 @@ +namespace YARG.Chart { + public class DrumNote : Note { + public int Pad { get; } + + public bool IsCymbal => (_flags & NoteFlags.Cymbal) != 0; + + public bool IsGhost => (_flags & NoteFlags.DrumsGhost) != 0; + public bool IsAccent => (_flags & NoteFlags.DrumsAccent) != 0; + + public DrumNote(Note previousNote, double time, uint tick, int pad, NoteFlags flags) + : base(previousNote, time, 0, tick, 0, flags) { + Pad = pad; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Chart/DrumNote.cs.meta b/Assets/Script/Chart/DrumNote.cs.meta new file mode 100644 index 000000000..03510adde --- /dev/null +++ b/Assets/Script/Chart/DrumNote.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1ac28f886e81496fbdde552ed263532b +timeCreated: 1682290868 \ No newline at end of file diff --git a/Assets/Script/Chart/GuitarNote.cs b/Assets/Script/Chart/GuitarNote.cs new file mode 100644 index 000000000..5dcb64a8b --- /dev/null +++ b/Assets/Script/Chart/GuitarNote.cs @@ -0,0 +1,76 @@ +using static MoonscraperChartEditor.Song.MoonNote; + +namespace YARG.Chart { + public class GuitarNote : Note { + public int Fret { get; } + public int NoteMask { get; private set; } + + public bool IsSustain { get; } + + public bool IsChord => (_flags & NoteFlags.Chord) != 0; + + public bool IsExtendedSustain => (_flags & NoteFlags.ExtendedSustain) != 0; + public bool IsDisjoint => (_flags & NoteFlags.Disjoint) != 0; + + private bool _isForced; + + private bool _isStrum; + private bool _isHopo; + private bool _isTap; + + public bool IsStrum { + get => _isStrum; + set { + if (value) { + IsHopo = false; + IsTap = false; + } + _isStrum = true; + } + } + + public bool IsHopo { + get => _isHopo; + set { + if (value) { + IsStrum = false; + IsTap = false; + } + _isHopo = true; + } + } + + public bool IsTap { + get => _isTap; + set { + if (value) { + IsStrum = false; + IsHopo = false; + } + _isTap = true; + } + } + + public GuitarNote(Note previousNote, double time, double timeLength, uint tick, uint tickLength, int fret, + MoonNoteType moonNoteType, NoteFlags flags) : base(previousNote, time, timeLength, tick, tickLength, flags) { + Fret = fret; + + IsSustain = tickLength > 0; + + _isStrum = moonNoteType == MoonNoteType.Strum; + _isTap = moonNoteType == MoonNoteType.Tap; + _isHopo = moonNoteType == MoonNoteType.Hopo && !_isTap; + + NoteMask = 1 << fret - 1; + } + + public override void AddChildNote(Note note) { + if (note is not GuitarNote guitarNote) + return; + + base.AddChildNote(note); + + NoteMask |= 1 << guitarNote.Fret - 1; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Chart/GuitarNote.cs.meta b/Assets/Script/Chart/GuitarNote.cs.meta new file mode 100644 index 000000000..b3174916f --- /dev/null +++ b/Assets/Script/Chart/GuitarNote.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f296f0debb5f6e74081a7acb2de63e77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Chart/Loaders.meta b/Assets/Script/Chart/Loaders.meta new file mode 100644 index 000000000..17c63fe63 --- /dev/null +++ b/Assets/Script/Chart/Loaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7d33c8e531a25542992ad9b0bed382e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Chart/Loaders/GuitarChartLoader.cs b/Assets/Script/Chart/Loaders/GuitarChartLoader.cs new file mode 100644 index 000000000..b4fe1dc13 --- /dev/null +++ b/Assets/Script/Chart/Loaders/GuitarChartLoader.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using MoonscraperChartEditor.Song; +using YARG.Data; + +namespace YARG.Chart { + public class GuitarChartLoader : IChartLoader { + public List GetNotesFromChart(MoonSong song, MoonChart chart) { + var notes = new List(); + + foreach (var moonNote in chart.notes) { + // Length of the note in realtime + double timeLength = song.TickToTime(moonNote.tick + moonNote.length, song.resolution) - moonNote.time; + + var note = new NoteInfo { + time = (float)moonNote.time, + length = (float)timeLength, + fret = moonNote.rawNote, + hopo = moonNote.type == MoonNote.MoonNoteType.Hopo, + tap = moonNote.type == MoonNote.MoonNoteType.Tap, + }; + + notes.Add(note); + } + + return notes; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Chart/Loaders/GuitarChartLoader.cs.meta b/Assets/Script/Chart/Loaders/GuitarChartLoader.cs.meta new file mode 100644 index 000000000..8eccc46ee --- /dev/null +++ b/Assets/Script/Chart/Loaders/GuitarChartLoader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 66a0d41892854a7392ca539c03e4ab56 +timeCreated: 1682544116 \ No newline at end of file diff --git a/Assets/Script/Chart/Loaders/IChartLoader.cs b/Assets/Script/Chart/Loaders/IChartLoader.cs new file mode 100644 index 000000000..e2164698c --- /dev/null +++ b/Assets/Script/Chart/Loaders/IChartLoader.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using MoonscraperChartEditor.Song; + +namespace YARG.Chart { + public interface IChartLoader { + + public List GetNotesFromChart(MoonSong song, MoonChart chart); + + } +} diff --git a/Assets/Script/Chart/Loaders/IChartLoader.cs.meta b/Assets/Script/Chart/Loaders/IChartLoader.cs.meta new file mode 100644 index 000000000..866aea37f --- /dev/null +++ b/Assets/Script/Chart/Loaders/IChartLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cce8c5f1311867b4994fb6a831b99610 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Chart/Loaders/New.meta b/Assets/Script/Chart/Loaders/New.meta new file mode 100644 index 000000000..7de030ed1 --- /dev/null +++ b/Assets/Script/Chart/Loaders/New.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c9cb70bd85f45819984d8a4ec717bb0 +timeCreated: 1682544105 \ No newline at end of file diff --git a/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs b/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs new file mode 100644 index 000000000..6b1e87aea --- /dev/null +++ b/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using MoonscraperChartEditor.Song; + +namespace YARG.Chart { + public class NewDrumChartLoader : IChartLoader { + + private readonly bool _isPro; + private readonly bool _isDoubleBass; + + public NewDrumChartLoader(bool isPro, bool doubleBass) { + _isPro = isPro; + _isDoubleBass = doubleBass; + } + + public List GetNotesFromChart(MoonSong song, MoonChart chart) { + var notes = new List(); + + // do star power later lol idk how it works + + // Previous note (could be same tick) + Note previousGameNote = null; + + // Previous note that is a different tick + Note previousSeparateGameNote = null; + + Note parentNote = null; + foreach (var moonNote in chart.notes) { + var flags = NoteFlags.None; + + if((moonNote.flags & MoonNote.Flags.ProDrums_Cymbal) != 0 && _isPro) { + flags |= NoteFlags.Cymbal; + } + + // Is kick, is double kick note but double bass not active + // Skip the note + if (moonNote.drumPad == MoonNote.DrumPad.Kick && (moonNote.flags & MoonNote.Flags.DoubleKick) != 0 && + !_isDoubleBass) { + continue; + } + + int fret = MoonDrumNoteToPad(moonNote); + var currentNote = new DrumNote(previousSeparateGameNote, moonNote.time, moonNote.tick, fret, flags); + + // First note, must be a parent note + if (previousGameNote is null) { + parentNote = currentNote; + + previousGameNote = currentNote; + notes.Add(currentNote); + + continue; + } + + // Ticks don't match previous game note + if (previousGameNote.Tick != currentNote.Tick) { + parentNote = currentNote; + + previousSeparateGameNote = previousGameNote; + + previousSeparateGameNote.nextNote = currentNote; + currentNote.previousNote = previousSeparateGameNote; + + notes.Add(currentNote); + } else { + // Add as a child note if ticks match + parentNote.AddChildNote(currentNote); + } + + previousGameNote = currentNote; + } + + return notes; + } + + private static int MoonDrumNoteToPad(MoonNote note) { + return note.drumPad switch { + MoonNote.DrumPad.Kick => 0, + MoonNote.DrumPad.Green => 1, + MoonNote.DrumPad.Red => 2, + MoonNote.DrumPad.Yellow => 3, + MoonNote.DrumPad.Blue => 4, + MoonNote.DrumPad.Orange => 5, + _ => 1 + }; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs.meta b/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs.meta new file mode 100644 index 000000000..e41fbe6c6 --- /dev/null +++ b/Assets/Script/Chart/Loaders/New/NewDrumChartLoader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 32ed3e4bc90045c88b478212cdf430f5 +timeCreated: 1682376357 \ No newline at end of file diff --git a/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs b/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs new file mode 100644 index 000000000..40d3c1d1b --- /dev/null +++ b/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using MoonscraperChartEditor.Song; + +namespace YARG.Chart { + public class NewGuitarChartLoader : IChartLoader { + + public List GetNotesFromChart(MoonSong song, MoonChart chart) { + var notes = new List(); + + var starpowers = chart.starPower.ToArray(); + + // Previous note (could be same tick) + Note previousGameNote = null; + + // Previous note that is a different tick + Note previousSeparateGameNote = null; + + Note parentNote = null; + foreach (var moonNote in chart.notes) { + var prevSeparateNote = moonNote.PreviousSeperateMoonNote; + var nextSeparateNote = moonNote.NextSeperateMoonNote; + + var flags = NoteFlags.None; + + foreach (var starpower in starpowers) { + uint spEndTick = starpower.tick + starpower.length; + + // Current note is within the bounds of current StarPower. + // MoonNote tick is bigger or equal to StarPower tick + // and smaller than StarPower end tick. + if (moonNote.tick >= starpower.tick && moonNote.tick < spEndTick) { + flags |= NoteFlags.StarPower; + + // If this is first note, or previous note is before starpower tick, mark as starpower start. + if (prevSeparateNote is null || prevSeparateNote.tick < starpower.tick) + { + flags |= NoteFlags.StarPowerStart; + } + // If this is last note, or next note is not in starpower range, mark as end of starpower. + if (nextSeparateNote is null || nextSeparateNote.tick >= spEndTick) + { + flags |= NoteFlags.StarPowerEnd; + } + } + } + + // The tick this note has ended + uint noteFinishTick = moonNote.tick + moonNote.length; + if(nextSeparateNote is not null && nextSeparateNote.tick < noteFinishTick) { + flags |= NoteFlags.ExtendedSustain; + } + if (moonNote.isChord) { + flags |= NoteFlags.Chord; + } + + // Length of the note in realtime + double timeLength = song.TickToTime(moonNote.tick + moonNote.length, song.resolution) - moonNote.time; + + int fret = MoonGuitarNoteToFret(moonNote); + var currentNote = new GuitarNote(previousSeparateGameNote, moonNote.time, timeLength, moonNote.tick, + moonNote.length, fret, moonNote.type, flags); + + // First note, must be a parent note + if (previousGameNote is null) { + parentNote = currentNote; + + previousGameNote = currentNote; + notes.Add(currentNote); + + continue; + } + + // Ticks don't match previous game note + if (previousGameNote.Tick != currentNote.Tick) { + parentNote = currentNote; + + previousSeparateGameNote = previousGameNote; + + previousSeparateGameNote.nextNote = currentNote; + currentNote.previousNote = previousSeparateGameNote; + + notes.Add(currentNote); + } else { + // Add as a child note if ticks match + parentNote.AddChildNote(currentNote); + } + + previousGameNote = currentNote; + } + + return notes; + } + + private static int MoonGuitarNoteToFret(MoonNote note) { + return note.guitarFret switch { + MoonNote.GuitarFret.Open => 0, + MoonNote.GuitarFret.Green => 1, + MoonNote.GuitarFret.Red => 2, + MoonNote.GuitarFret.Yellow => 3, + MoonNote.GuitarFret.Blue => 4, + MoonNote.GuitarFret.Orange => 5, + _ => 1 + }; + } + + } +} diff --git a/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs.meta b/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs.meta new file mode 100644 index 000000000..49a531089 --- /dev/null +++ b/Assets/Script/Chart/Loaders/New/NewGuitarChartLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 416ff209bbe25df42a02f1823be36b24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Chart/Note.cs b/Assets/Script/Chart/Note.cs new file mode 100644 index 000000000..0c4a6f1e5 --- /dev/null +++ b/Assets/Script/Chart/Note.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; + +namespace YARG.Chart { + public abstract class Note { + public Note previousNote; + public Note nextNote; + + private readonly List _childNotes; + public IReadOnlyList ChildNotes => _childNotes; + + protected NoteFlags _flags; + + public double Time { get; } + public double TimeLength { get; } + + public uint Tick { get; } + public uint TickLength { get; } + + public bool IsStarPowerStart => (_flags & NoteFlags.StarPowerStart) != 0; + public bool IsStarPower => (_flags & NoteFlags.StarPower) != 0; + public bool IsStarPowerEnd => (_flags & NoteFlags.StarPowerEnd) != 0; + + protected Note(Note previousNote, double time, double timeLength, uint tick, uint tickLength, NoteFlags flags) { + this.previousNote = previousNote; + Time = time; + TimeLength = timeLength; + Tick = tick; + TickLength = tickLength; + + _flags = flags; + } + + public virtual void AddChildNote(Note note) { + if (note.Tick != Tick || note.ChildNotes.Count > 0) { + return; + } + + _childNotes.Add(note); + } + } + + [Flags] + public enum NoteFlags { + None = 0, + Chord = 1, + ExtendedSustain = 2, + Disjoint = 4, + StarPowerStart = 8, + StarPower = 16, + StarPowerEnd = 32, + SoloStart = 64, + SoloEnd = 128, + Cymbal = 256, + DrumsGhost = 512, + DrumsAccent = 1024, + } +} + diff --git a/Assets/Script/Chart/Note.cs.meta b/Assets/Script/Chart/Note.cs.meta new file mode 100644 index 000000000..ad9bb9e5a --- /dev/null +++ b/Assets/Script/Chart/Note.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 799bee9efc6296a4592c77a3ee19f4cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Data/Chart.cs b/Assets/Script/Data/Chart.cs deleted file mode 100644 index 33495e976..000000000 --- a/Assets/Script/Data/Chart.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace YARG.Data { - public sealed class Chart { - public List[]> allParts; - -#pragma warning disable format - - public List[] guitar = CreateArray(); - public List[] guitarCoop = CreateArray(); - public List[] rhythm = CreateArray(); - public List[] bass = CreateArray(); - public List[] keys = CreateArray(); - public List[] realGuitar = CreateArray(); - public List[] realBass = CreateArray(); - - public List[] drums = CreateArray(5); - public List[] realDrums = CreateArray(5); - public List[] ghDrums = CreateArray(5); - -#pragma warning restore format - - public List events = new(); - public List beats = new(); - - /// - /// Lyrics to be displayed in the lyric view when no one is singing. - /// - public List genericLyrics = new(); - - /// - /// Solo vocal lyrics. - /// - public List realLyrics = new(); - - /// - /// Harmony lyrics. Size 0 by default, should be set by the harmony lyric parser. - /// - public List[] harmLyrics = new List[0]; - - public Chart() { - allParts = new() { - guitar, guitarCoop, rhythm,bass, keys, realGuitar, realBass, drums, realDrums, ghDrums - }; - } - - public List[] GetChartByName(string name) { - return name switch { - "guitar" => guitar, - "guitarCoop" => guitarCoop, - "rhythm" => rhythm, - "bass" => bass, - "keys" => keys, - "realGuitar" => realGuitar, - "realBass" => realBass, - "drums" => drums, - "realDrums" => realDrums, - "ghDrums" => ghDrums, - _ => throw new InvalidOperationException($"Unsupported chart type `{name}`.") - }; - } - - private static List[] CreateArray(int length = 4) { - var list = new List[length]; - for (int i = 0; i < length; i++) { - list[i] = new(); - } - - return list; - } - } -} \ No newline at end of file diff --git a/Assets/Script/Data/YargChart.cs b/Assets/Script/Data/YargChart.cs new file mode 100644 index 000000000..9362e2a45 --- /dev/null +++ b/Assets/Script/Data/YargChart.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using MoonscraperChartEditor.Song; +using YARG.Chart; + +namespace YARG.Data { + public sealed class YargChart { + + private MoonSong _song; + + public List[]> allParts; + +#pragma warning disable format + + private List[] guitar; + public List[] Guitar { + get => guitar ?? LoadArray(ref guitar, new GuitarChartLoader(), MoonSong.MoonInstrument.Guitar); + set => guitar = value; + } + + private List[] guitarCoop; + public List[] GuitarCoop { + get => guitarCoop ?? LoadArray(ref guitarCoop, new GuitarChartLoader(), MoonSong.MoonInstrument.GuitarCoop); + set => guitarCoop = value; + } + private List[] rhythm; + public List[] Rhythm { + get => rhythm ?? LoadArray(ref rhythm, new GuitarChartLoader(), MoonSong.MoonInstrument.Rhythm); + set => rhythm = value; + } + + private List[] bass; + public List[] Bass { + get => bass ?? LoadArray(ref bass, new GuitarChartLoader(), MoonSong.MoonInstrument.Bass); + set => bass = value; + } + + private List[] keys; + public List[] Keys { + get => keys ?? LoadArray(ref keys, new GuitarChartLoader(), MoonSong.MoonInstrument.Keys); + set => keys = value; + } + + public List[] RealGuitar { get; set; } + + public List[] RealBass { get; set; } + + private List[] drums; + public List[] Drums { + get => drums;// ?? LoadArray(new DrumsChartLoader(false), MoonSong.MoonInstrument.Drums, 4); + set => drums = value; + } + private List[] realDrums; + public List[] RealDrums { + get => realDrums;// ?? LoadArray(new DrumsChartLoader(true), MoonSong.MoonInstrument.Drums, 4); + set => realDrums = value; + } + + private List[] ghDrums; + public List[] GhDrums { + get => ghDrums;// ?? LoadArray(new DrumsChartLoader(false), MoonSong.MoonInstrument.Drums, 4); + set => ghDrums = value; + } + +#pragma warning restore format + + private List _loadedEvents = new(); + + public List events = new(); + public List beats = new(); + + /// + /// Lyrics to be displayed in the lyric view when no one is singing. + /// + public List genericLyrics = new(); + + /// + /// Solo vocal lyrics. + /// + public List realLyrics = new(); + + /// + /// Harmony lyrics. Size 0 by default, should be set by the harmony lyric parser. + /// + public List[] harmLyrics = new List[0]; + + public YargChart(MoonSong song) { + _song = song; + } + + public List[] GetChartByName(string name) { + return name switch { + "guitar" => Guitar, + "guitarCoop" => GuitarCoop, + "rhythm" => Rhythm, + "bass" => Bass, + "keys" => Keys, + "realGuitar" => RealGuitar, + "realBass" => RealBass, + "drums" => Drums, + "realDrums" => RealDrums, + "ghDrums" => GhDrums, + _ => throw new InvalidOperationException($"Unsupported chart type `{name}`.") + }; + } + + public void InitializeArrays() { + Guitar = CreateArray(); + GuitarCoop = CreateArray(); + Rhythm = CreateArray(); + Bass = CreateArray(); + Keys = CreateArray(); + RealGuitar = CreateArray(); + RealBass = CreateArray(); + Drums = CreateArray(5); + RealDrums = CreateArray(5); + GhDrums = CreateArray(5); + + allParts = new() { + guitar, guitarCoop, rhythm, bass, keys, RealGuitar, RealBass, drums, realDrums, ghDrums + }; + } + + private List[] LoadArray(ref List[] notes, IChartLoader loader, MoonSong.MoonInstrument instrument, int length = 4, + bool isPro = false, bool isGh = false) { + notes = new List[length]; + for (int i = 0; i < length; i++) { + notes[i] = loader.GetNotesFromChart(_song, _song.GetChart(instrument, (MoonSong.Difficulty) length - 1 - i)); + } + + if (_loadedEvents.Contains(instrument)) { + return notes; + } + + var chart = _song.GetChart(instrument, MoonSong.Difficulty.Expert); + foreach (var sp in chart.starPower) { + string name = GetNameFromInstrument(instrument, isPro, isGh); + + float finishTime = (float)_song.TickToTime(sp.tick + sp.length - 1); + + events.Add(new EventInfo($"starpower_{name}", (float)sp.time, finishTime - (float)sp.time)); + } + + for (int i = 0; i < chart.events.Count; i++) { + var chartEvent = chart.events[i]; + string name = GetNameFromInstrument(instrument, isPro, isGh); + + if (chartEvent.eventName == "solo") { + for(int k = i; k < chart.events.Count; k++) { + var chartEvent2 = chart.events[k]; + if (chartEvent2.eventName == "soloend") { + events.Add(new EventInfo($"solo_{name}", (float)chartEvent.time, (float)(chartEvent2.time - chartEvent.time))); + break; + } + } + } + } + + _loadedEvents.Add(instrument); + events.Sort((e1, e2) => e1.time.CompareTo(e2.time)); + + return notes; + } + + private static List[] CreateArray(int length = 4) { + var list = new List[length]; + for (int i = 0; i < length; i++) { + list[i] = new(); + } + + return list; + } + + private static string GetNameFromInstrument(MoonSong.MoonInstrument instrument, bool isPro, bool isGh) { + return instrument switch { + MoonSong.MoonInstrument.Guitar => "guitar", + MoonSong.MoonInstrument.GuitarCoop => "guitarCoop", + MoonSong.MoonInstrument.Rhythm => "rhythm", + MoonSong.MoonInstrument.Bass => "bass", + MoonSong.MoonInstrument.Keys => "keys", + MoonSong.MoonInstrument.Drums => isPro ? "realDrums" : isGh ? "ghDrums" : "drums", + }; + } + } +} \ No newline at end of file diff --git a/Assets/Script/Data/Chart.cs.meta b/Assets/Script/Data/YargChart.cs.meta similarity index 100% rename from Assets/Script/Data/Chart.cs.meta rename to Assets/Script/Data/YargChart.cs.meta diff --git a/Assets/Script/PlayMode/AbstractTrack.cs b/Assets/Script/PlayMode/AbstractTrack.cs index 90ea5647d..e50c3845c 100644 --- a/Assets/Script/PlayMode/AbstractTrack.cs +++ b/Assets/Script/PlayMode/AbstractTrack.cs @@ -27,6 +27,8 @@ public abstract class AbstractTrack : MonoBehaviour { protected int inputChartIndex = 0; protected int hitChartIndex = 0; protected int eventChartIndex = 0; + protected int beatChartIndex = 0; + protected CommonTrack commonTrack; protected TrackAnimations trackAnims; diff --git a/Assets/Script/PlayMode/DrumsTrack.cs b/Assets/Script/PlayMode/DrumsTrack.cs index 159086ffc..e0c429097 100644 --- a/Assets/Script/PlayMode/DrumsTrack.cs +++ b/Assets/Script/PlayMode/DrumsTrack.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using YARG.Chart; using YARG.Data; using YARG.Input; using YARG.Pools; @@ -101,17 +102,18 @@ protected override void UpdateTrack() { } var events = Play.Instance.chart.events; + var beats = Play.Instance.chart.beats; // Update events (beat lines, starpower, etc.) while (events.Count > eventChartIndex && events[eventChartIndex].time <= RelativeTime) { var eventInfo = events[eventChartIndex]; float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, eventInfo.time); - if (eventInfo.name == "beatLine_minor") { - genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == "beatLine_major") { - genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == $"starpower_{player.chosenInstrument}") { + // if (eventInfo.name == "beatLine_minor") { + // genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + // } else if (eventInfo.name == "beatLine_major") { + // genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + if (eventInfo.name == $"starpower_{player.chosenInstrument}") { StarpowerSection = eventInfo; } else if (eventInfo.name == $"fill_{player.chosenInstrument}") { FillSection = eventInfo; @@ -120,6 +122,18 @@ protected override void UpdateTrack() { } eventChartIndex++; } + + while (beats.Count > beatChartIndex && beats[beatChartIndex].Time <= RelativeTime) { + var beatInfo = beats[beatChartIndex]; + + float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, beatInfo.Time); + if (beatInfo.Style is BeatStyle.STRONG or BeatStyle.WEAK) { + genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + } else if (beatInfo.Style == BeatStyle.MEASURE) { + genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + } + beatChartIndex++; + } // Since chart is sorted, this is guaranteed to work while (Chart.Count > visualChartIndex && Chart[visualChartIndex].time <= RelativeTime) { diff --git a/Assets/Script/PlayMode/FiveFretTrack.cs b/Assets/Script/PlayMode/FiveFretTrack.cs index ff71bfc36..3c9abf53d 100644 --- a/Assets/Script/PlayMode/FiveFretTrack.cs +++ b/Assets/Script/PlayMode/FiveFretTrack.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using YARG.Chart; using YARG.Data; using YARG.Input; using YARG.Pools; @@ -94,15 +95,17 @@ protected override void UpdateTrack() { // Update events (beat lines, starpower, etc.) var events = Play.Instance.chart.events; + var beats = Play.Instance.chart.beats; + while (events.Count > eventChartIndex && events[eventChartIndex].time <= RelativeTime) { var eventInfo = events[eventChartIndex]; float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, eventInfo.time); - if (eventInfo.name == "beatLine_minor") { - genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == "beatLine_major") { - genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == $"starpower_{player.chosenInstrument}") { + // if (eventInfo.name == "beatLine_minor") { + // genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + // } else if (eventInfo.name == "beatLine_major") { + // genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + if (eventInfo.name == $"starpower_{player.chosenInstrument}") { StarpowerSection = eventInfo; } else if (eventInfo.name == $"solo_{player.chosenInstrument}") { SoloSection = eventInfo; @@ -110,6 +113,18 @@ protected override void UpdateTrack() { eventChartIndex++; } + + while (beats.Count > beatChartIndex && beats[beatChartIndex].Time <= RelativeTime) { + var beatInfo = beats[beatChartIndex]; + + float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, beatInfo.Time); + if (beatInfo.Style is BeatStyle.STRONG or BeatStyle.WEAK) { + genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + } else if (beatInfo.Style == BeatStyle.MEASURE) { + genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + } + beatChartIndex++; + } // Since chart is sorted, this is guaranteed to work while (Chart.Count > visualChartIndex && Chart[visualChartIndex].time <= RelativeTime) { diff --git a/Assets/Script/PlayMode/Play.cs b/Assets/Script/PlayMode/Play.cs index a57999508..0827b7599 100644 --- a/Assets/Script/PlayMode/Play.cs +++ b/Assets/Script/PlayMode/Play.cs @@ -1,9 +1,12 @@ using System; using System.Collections; using System.Collections.Generic; +using MoonscraperChartEditor.Song; +using MoonscraperChartEditor.Song.IO; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.InputSystem; +using YARG.Chart; using YARG.Data; using YARG.Serialization.Parser; using YARG.Settings; @@ -53,7 +56,7 @@ public float SongLength { private set; } - public Chart chart; + public YargChart chart; private int beatIndex = 0; private int lyricIndex = 0; @@ -191,13 +194,28 @@ private void LoadChart() { // } // Parse - var parser = new MidiParser(song, files.ToArray()); - chart = new Chart(); - parser.Parse(chart); + + MoonSong moonSong = null; + if (song.mainFile.EndsWith(".chart")) { + Debug.Log("Reading .chart file"); + moonSong = ChartReader.ReadChart(song.mainFile); + } + + chart = new YargChart(moonSong); + if (song.mainFile.EndsWith(".mid")) { + // Parse + var parser = new MidiParser(song, files.ToArray()); + chart.InitializeArrays(); + parser.Parse(chart); + } else if(song.mainFile.EndsWith(".chart")) { + var handler = new BeatHandler(moonSong); + handler.GenerateBeats(); + chart.beats = handler.Beats; + } // initialize current tempo if (chart.beats.Count > 2) - curBeatPerSecond = chart.beats[1] - chart.beats[0]; + curBeatPerSecond = chart.beats[1].Time - chart.beats[0].Time; } private void Update() { @@ -272,7 +290,7 @@ private void Update() { }); // Update beats - while (chart.beats.Count > beatIndex && chart.beats[beatIndex] <= SongTime) { + while (chart.beats.Count > beatIndex && chart.beats[beatIndex].Time <= SongTime) { foreach (var track in _tracks) { if (!track.IsStarPowerActive || !GameManager.AudioManager.UseStarpowerFx) continue; @@ -283,7 +301,7 @@ private void Update() { beatIndex++; if (beatIndex < chart.beats.Count) { - curBeatPerSecond = 1 / (chart.beats[beatIndex] - chart.beats[beatIndex - 1]); + curBeatPerSecond = 1 / (chart.beats[beatIndex].Time - chart.beats[beatIndex - 1].Time); } } diff --git a/Assets/Script/PlayMode/RealGuitarTrack.cs b/Assets/Script/PlayMode/RealGuitarTrack.cs index 883c6ed7c..2ed440043 100644 --- a/Assets/Script/PlayMode/RealGuitarTrack.cs +++ b/Assets/Script/PlayMode/RealGuitarTrack.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using TMPro; using UnityEngine; +using YARG.Chart; using YARG.Data; using YARG.Input; using YARG.Pools; @@ -88,17 +89,18 @@ protected override void UpdateTrack() { } var events = Play.Instance.chart.events; + var beats = Play.Instance.chart.beats; // Update events (beat lines, starpower, etc.) while (events.Count > eventChartIndex && events[eventChartIndex].time <= RelativeTime) { var eventInfo = events[eventChartIndex]; float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, eventInfo.time); - if (eventInfo.name == "beatLine_minor") { - genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == "beatLine_major") { - genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); - } else if (eventInfo.name == $"starpower_{player.chosenInstrument}") { + // if (eventInfo.name == "beatLine_minor") { + // genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + // } else if (eventInfo.name == "beatLine_major") { + // genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + if (eventInfo.name == $"starpower_{player.chosenInstrument}") { StarpowerSection = eventInfo; } else if (eventInfo.name == $"solo_{player.chosenInstrument}") { SoloSection = eventInfo; @@ -106,6 +108,18 @@ protected override void UpdateTrack() { eventChartIndex++; } + + while (beats.Count > beatChartIndex && beats[beatChartIndex].Time <= RelativeTime) { + var beatInfo = beats[beatChartIndex]; + + float compensation = TRACK_SPAWN_OFFSET - CalcLagCompensation(RelativeTime, beatInfo.Time); + if (beatInfo.Style is BeatStyle.STRONG or BeatStyle.WEAK) { + genericPool.Add("beatLine_minor", new(0f, 0.01f, compensation)); + } else if (beatInfo.Style == BeatStyle.MEASURE) { + genericPool.Add("beatLine_major", new(0f, 0.01f, compensation)); + } + beatChartIndex++; + } // Since chart is sorted, this is guaranteed to work while (Chart.Count > visualChartIndex && Chart[visualChartIndex].time <= RelativeTime) { diff --git a/Assets/Script/PlayMode/StarDisplay.cs b/Assets/Script/PlayMode/StarDisplay.cs index d1273214f..df1f1a0f4 100644 --- a/Assets/Script/PlayMode/StarDisplay.cs +++ b/Assets/Script/PlayMode/StarDisplay.cs @@ -3,6 +3,7 @@ using UnityEngine; using UnityEngine.UI; using Unity.Mathematics; +using YARG.Chart; namespace YARG.PlayMode { public class StarDisplay : MonoBehaviour { @@ -34,10 +35,10 @@ private void OnDisable() { } private void Start() { - var events = Play.Instance.chart.events; - foreach (var ev in events) { - if (ev.name == "beatLine_major") { - bars.Add(ev.time); + var beats = Play.Instance.chart.beats; + foreach (var ev in beats) { + if (ev.Style == BeatStyle.MEASURE) { + bars.Add(ev.Time); } } diff --git a/Assets/Script/PlayMode/SustainTracker.cs b/Assets/Script/PlayMode/SustainTracker.cs index e6eb93211..d4fca1713 100644 --- a/Assets/Script/PlayMode/SustainTracker.cs +++ b/Assets/Script/PlayMode/SustainTracker.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; +using YARG.Chart; using YARG.Data; namespace YARG.PlayMode { @@ -9,11 +10,11 @@ namespace YARG.PlayMode { /// Tracks sustain notes' progress, counted in beats. /// public class SustainTracker { - private List beatTimes; + private List _beats; private Dictionary noteProgress = new(); - public SustainTracker(List beatTimes) { - this.beatTimes = beatTimes; + public SustainTracker(List beats) { + _beats = beats; } /// diff --git a/Assets/Script/Serialization/Parser/AbstractParser.cs b/Assets/Script/Serialization/Parser/AbstractParser.cs index c6778a54e..bf3cd454a 100644 --- a/Assets/Script/Serialization/Parser/AbstractParser.cs +++ b/Assets/Script/Serialization/Parser/AbstractParser.cs @@ -10,6 +10,6 @@ public AbstractParser(SongInfo songInfo, string[] files) { this.files = files; } - public abstract void Parse(Chart chart); + public abstract void Parse(YargChart yargChart); } } \ No newline at end of file diff --git a/Assets/Script/Serialization/Parser/MidiParser.cs b/Assets/Script/Serialization/Parser/MidiParser.cs index e359d4e0e..71fbd29e8 100644 --- a/Assets/Script/Serialization/Parser/MidiParser.cs +++ b/Assets/Script/Serialization/Parser/MidiParser.cs @@ -5,6 +5,7 @@ using Melanchall.DryWetMidi.Interaction; using Melanchall.DryWetMidi.MusicTheory; using UnityEngine; +using YARG.Chart; using YARG.Data; using YARG.DiffDownsample; @@ -45,7 +46,7 @@ public MidiParser(SongInfo songInfo, string[] files) : base(songInfo, files) { } } - public override void Parse(Chart chart) { + public override void Parse(YargChart chart) { var eventIR = new List(); var tempo = midi.GetTempoMap(); @@ -93,35 +94,35 @@ public override void Parse(Chart chart) { switch (trackName.Text) { case "PART GUITAR": for (int i = 0; i < 4; i++) { - chart.guitar[i] = ParseFiveFret(trackChunk, i); + chart.Guitar[i] = ParseFiveFret(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "guitar"); ParseSolo(eventIR, trackChunk, "guitar"); break; case "PART GUITAR COOP": for (int i = 0; i < 4; i++) { - chart.guitarCoop[i] = ParseFiveFret(trackChunk, i); + chart.GuitarCoop[i] = ParseFiveFret(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "guitarCoop"); ParseSolo(eventIR, trackChunk, "guitarCoop"); break; case "PART RHYTHM": for (int i = 0; i < 4; i++) { - chart.rhythm[i] = ParseFiveFret(trackChunk, i); + chart.Rhythm[i] = ParseFiveFret(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "rhythm"); ParseSolo(eventIR, trackChunk, "rhythm"); break; case "PART BASS": for (int i = 0; i < 4; i++) { - chart.bass[i] = ParseFiveFret(trackChunk, i); + chart.Bass[i] = ParseFiveFret(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "bass"); ParseSolo(eventIR, trackChunk, "bass"); break; case "PART KEYS": for (int i = 0; i < 4; i++) { - chart.keys[i] = ParseFiveFret(trackChunk, i); + chart.Keys[i] = ParseFiveFret(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "keys"); ParseSolo(eventIR, trackChunk, "keys"); @@ -133,14 +134,14 @@ public override void Parse(Chart chart) { break; case "PART REAL_GUITAR": for (int i = 0; i < 4; i++) { - chart.realGuitar[i] = ParseRealGuitar(trackChunk, i); + chart.RealGuitar[i] = ParseRealGuitar(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "realGuitar"); ParseSolo(eventIR, trackChunk, "realGuitar", 8); break; case "PART REAL_BASS": for (int i = 0; i < 4; i++) { - chart.realBass[i] = ParseRealGuitar(trackChunk, i); + chart.RealBass[i] = ParseRealGuitar(trackChunk, i); } ParseStarpower(eventIR, trackChunk, "realBass"); ParseSolo(eventIR, trackChunk, "realBass", 8); @@ -151,10 +152,10 @@ public override void Parse(Chart chart) { if (drumType == SongInfo.DrumType.FOUR_LANE) { for (int i = 0; i < 5; i++) { - chart.drums[i] = ParseDrums(trackChunk, false, i, drumType, null); - chart.realDrums[i] = ParseDrums(trackChunk, true, i, drumType, null); + chart.Drums[i] = ParseDrums(trackChunk, false, i, drumType, null); + chart.RealDrums[i] = ParseDrums(trackChunk, true, i, drumType, null); - chart.ghDrums[i] = ParseGHDrums(trackChunk, i, drumType, chart.realDrums[i]); + chart.GhDrums[i] = ParseGHDrums(trackChunk, i, drumType, chart.RealDrums[i]); ParseStarpower(eventIR, trackChunk, "drums"); ParseStarpower(eventIR, trackChunk, "realDrums"); ParseDrumFills(eventIR, trackChunk, "drums"); @@ -162,10 +163,10 @@ public override void Parse(Chart chart) { } } else { for (int i = 0; i < 5; i++) { - chart.ghDrums[i] = ParseGHDrums(trackChunk, i, drumType, null); + chart.GhDrums[i] = ParseGHDrums(trackChunk, i, drumType, null); - chart.drums[i] = ParseDrums(trackChunk, false, i, drumType, chart.ghDrums[i]); - chart.realDrums[i] = ParseDrums(trackChunk, true, i, drumType, chart.ghDrums[i]); + chart.Drums[i] = ParseDrums(trackChunk, false, i, drumType, chart.GhDrums[i]); + chart.RealDrums[i] = ParseDrums(trackChunk, true, i, drumType, chart.GhDrums[i]); // TODO: SP is still a bit broken on 5-lane and is therefore disabled for now //ParseStarpower(eventIR, trackChunk, "ghDrums"); @@ -189,7 +190,7 @@ public override void Parse(Chart chart) { foreach (var subChart in chart.allParts) { try { // Downsample Five Fret instruments - if (subChart == chart.guitar || subChart == chart.bass || subChart == chart.keys) { + if (subChart == chart.Guitar || subChart == chart.Bass || subChart == chart.Keys) { if (subChart[3].Count >= 1 && (subChart[2].Count <= 0 || FORCE_DOWNSAMPLE)) { subChart[2] = FiveFretDownsample.DownsampleExpertToHard(subChart[3]); Debug.Log("Downsampled expert to hard."); @@ -284,8 +285,16 @@ public override void Parse(Chart chart) { chart.beats = new(); foreach (var ev in chart.events) { - if (ev.name == "beatLine_minor" || ev.name == "beatLine_major") { - chart.beats.Add(ev.time); + if (ev.name is "beatLine_minor") { + chart.beats.Add(new Beat { + Time = ev.time, + Style = BeatStyle.STRONG, + }); + } else if (ev.name is "beatLine_major") { + chart.beats.Add(new Beat { + Time = ev.time, + Style = BeatStyle.MEASURE, + }); } } diff --git a/Assets/Script/SongLibrary.cs b/Assets/Script/SongLibrary.cs index 08e3b923d..00b14c793 100644 --- a/Assets/Script/SongLibrary.cs +++ b/Assets/Script/SongLibrary.cs @@ -9,6 +9,7 @@ using UnityEngine; using YARG.Data; using YARG.Serialization; +using YARG.Serialization.Parser; using YARG.Settings; namespace YARG { @@ -236,15 +237,31 @@ private static void ReadSongInfo() { // See if .mid file exists var midPath = Path.Combine(info.path, "notes.mid"); - if (!File.Exists(midPath)) { - Debug.LogError($"`{info.path}` does not have a `notes.mid` file. Skipping."); + var chartPath = Path.Combine(info.path, "notes.chart"); + + // Create SongInfo + SongInfo songInfo; + + // Load midi + if (File.Exists(midPath) && !File.Exists(chartPath)) { + songInfo = new SongInfo(midPath, info.root, info.type); + SongIni.CompleteSongInfo(songInfo); + } else if (File.Exists(chartPath) && !File.Exists(midPath)) { + // Load chart + + songInfo = new SongInfo(chartPath, info.root, info.type); + SongIni.CompleteSongInfo(songInfo); + } else if (!File.Exists(midPath) && !File.Exists(chartPath)) { + Debug.LogError($"`{info.path}` does not have a `notes.mid` or `notes.chart` file. Skipping."); continue; + } else { + // Both exist? + + // choose midi lol + songInfo = new SongInfo(midPath, info.root, info.type); + SongIni.CompleteSongInfo(songInfo); } - // Create a SongInfo - var songInfo = new SongInfo(midPath, info.root, info.type); - SongIni.CompleteSongInfo(songInfo); - // Add it to the list of songs songsTemp.Add(songInfo); } else if (info.type == SongInfo.SongType.RB_CON) { diff --git a/Assets/Script/Util/Utils.cs b/Assets/Script/Util/Utils.cs index 2fb6fb695..3973ae9a3 100644 --- a/Assets/Script/Util/Utils.cs +++ b/Assets/Script/Util/Utils.cs @@ -2,6 +2,7 @@ using System.IO; using System.Collections.Generic; using UnityEngine; +using YARG.Chart; namespace YARG.Util { public static class Utils { @@ -28,30 +29,30 @@ public static bool PathsEqual(string a, string b) { /// /// List of beat times associated with the Info object. /// Length of the Info object in beats. - public static float InfoLengthInBeats(YARG.Data.AbstractInfo info, List beatTimes) { + public static float InfoLengthInBeats(YARG.Data.AbstractInfo info, List beatTimes) { int beatIndex = 1; // set beatIndex to first relevant beat - while (beatIndex < beatTimes.Count && beatTimes[beatIndex] <= info.time) { + while (beatIndex < beatTimes.Count && beatTimes[beatIndex].Time <= info.time) { ++beatIndex; } float beats = 0; // add segments of the length wrt tempo - for (; beatIndex < beatTimes.Count && beatTimes[beatIndex] <= info.EndTime; ++beatIndex) { - var curBPS = 1/(beatTimes[beatIndex] - beatTimes[beatIndex - 1]); + for (; beatIndex < beatTimes.Count && beatTimes[beatIndex].Time <= info.EndTime; ++beatIndex) { + var curBPS = 1/(beatTimes[beatIndex].Time - beatTimes[beatIndex - 1].Time); // Unit math: s * b/s = pt - beats += (beatTimes[beatIndex] - Mathf.Max(beatTimes[beatIndex - 1], info.time)) * curBPS; + beats += (beatTimes[beatIndex].Time - Mathf.Max(beatTimes[beatIndex - 1].Time, info.time)) * curBPS; } // segment where EndTime is between two beats (beatIndex-1 and beatIndex) - if (beatIndex < beatTimes.Count && beatTimes[beatIndex-1] < info.EndTime && info.EndTime < beatTimes[beatIndex]) { - var bps = 1/(beatTimes[beatIndex] - beatTimes[beatIndex - 1]); - beats += (info.EndTime - beatTimes[beatIndex - 1]) * bps; + if (beatIndex < beatTimes.Count && beatTimes[beatIndex-1].Time < info.EndTime && info.EndTime < beatTimes[beatIndex].Time) { + var bps = 1/(beatTimes[beatIndex].Time - beatTimes[beatIndex - 1].Time); + beats += (info.EndTime - beatTimes[beatIndex - 1].Time) * bps; } // segment where EndTime is BEYOND the final beat - else if (info.EndTime > beatTimes[^1]) { - var bps = 1/(beatTimes[^1] - beatTimes[^2]); - var toAdd = (info.EndTime - beatTimes[^1]) * bps; + else if (info.EndTime > beatTimes[^1].Time) { + var bps = 1/(beatTimes[^1].Time - beatTimes[^2].Time); + var toAdd = (info.EndTime - beatTimes[^1].Time) * bps; beats += toAdd; }