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;
}