Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Engine support for treating solo frets separately from normal frets #224

Open
wants to merge 36 commits into
base: engine-fixes
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5454998
Fix KeyReleased not being set to null
RileyTheFox Oct 1, 2024
1cf1d2e
Remove source of inconsistent sustain rebase
RileyTheFox Oct 1, 2024
cb28b47
Fix jumps in star power amount when whammying
RileyTheFox Oct 1, 2024
0fd2fc1
Add documentation for SP stats
RileyTheFox Oct 25, 2024
d433709
Write missing stats and move SustainScore to base
RileyTheFox Oct 25, 2024
6654163
Track ignored fat fingers stat
RileyTheFox Oct 25, 2024
4ad5412
Add Drums dynamics stat tracking
RileyTheFox Oct 25, 2024
af9046a
Print stats in ReplayCli despite passing
RileyTheFox Oct 25, 2024
1439c1d
Add new stats to output for ReplayCli
RileyTheFox Oct 25, 2024
88ea636
Refactor replay analyzer stat logging; implement proper validation fo…
TheNathannator Oct 25, 2024
5c3df0a
Changed engine countdown events to be purely time-based (#218)
Purplo-cf Oct 26, 2024
5193bb0
Handle missing a SoloEnd note before the solo has started
RileyTheFox Oct 29, 2024
527f1cd
More concise `GetSoloSections()`
sonicfind Oct 29, 2024
c31f123
Simulate replay to Replay Length time
RileyTheFox Nov 26, 2024
c3c5119
Merge remote-tracking branch 'upstream/master' into engine-fixes
RileyTheFox Nov 27, 2024
c58e818
Calculate Time In SP every update and store a base time
RileyTheFox Nov 27, 2024
17c6ef1
Return string from analyzer stat diff instead of logging
RileyTheFox Nov 28, 2024
458aa5b
Initialize whammy timer in BaseEngine.Generic
RileyTheFox Nov 29, 2024
0f9bbd2
Fix some inconsistent whammy logic
RileyTheFox Nov 29, 2024
ea5bade
Merge remote-tracking branch 'upstream/master' into engine-fixes
RileyTheFox Nov 29, 2024
34ee620
Decouple whammy gain from sustains
RileyTheFox Nov 30, 2024
23615ed
Call UpdateStarPower before hit logic
RileyTheFox Nov 30, 2024
0b2f725
Queue update for when star power will reach half during whammy
RileyTheFox Nov 30, 2024
b0e208d
remove stupid hack that didnt actually work
RileyTheFox Dec 1, 2024
0d67aea
Adjust various chart-related units for better precision/intent
TheNathannator Dec 2, 2024
1ce91c8
Address tick <-> time conversion precision issues
TheNathannator Dec 2, 2024
45d6237
Change .mid reader warning logs to debug logs
TheNathannator Dec 2, 2024
a01e285
Make .chart tempo loading use double for division
TheNathannator Dec 2, 2024
5aae3aa
Refactor .mid reader to parse tempo track manually
TheNathannator Dec 2, 2024
1c725fe
Engine support for treating solo frets separately from normal frets
wyrdough Nov 27, 2024
dec820c
Make solo taps an engine parameter
wyrdough Nov 28, 2024
81facbe
Add SoloTaps to FiveFretGuitarPreset::Copy()
wyrdough Nov 28, 2024
79f596c
Replace IsSoloButton flag with StandardButtonCount int and reverse so…
wyrdough Nov 28, 2024
fe4a19b
Fix issue with solo tap not being allowed on first note of solo.
wyrdough Dec 8, 2024
40758c3
Add SoloTapAllowed clause to hopoleniency check in HitNote()
wyrdough Dec 9, 2024
f5f8903
Rework solo button detection
wyrdough Jan 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ReplayCli/Cli.SimulateFps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private bool RunSimulateFps()
{
double fps = i * 4 + 1;

var analyzerResults = ReplayAnalyzer.AnalyzeReplay(chart, _replayData, fps);
var analyzerResults = ReplayAnalyzer.AnalyzeReplay(chart, _replayInfo, _replayData, fps);
long bandScore = analyzerResults.Sum(x => (long) x.ResultStats.TotalScore);

if (scores.TryGetValue(bandScore, out int value))
Expand Down
16 changes: 14 additions & 2 deletions ReplayCli/Cli.Verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ private bool RunVerify()

Console.WriteLine("Analyzing replay...");

var results = ReplayAnalyzer.AnalyzeReplay(chart, _replayData);
var results = ReplayAnalyzer.AnalyzeReplay(chart, _replayInfo, _replayData);

Console.WriteLine("Done!\n");

// Print result data

var bandScore = results.Sum(x => x.ResultStats.TotalScore);
if (bandScore != _replayInfo.BandScore)
if (results.Any(x => !x.Passed))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("VERIFICATION FAILED!");
Expand Down Expand Up @@ -62,6 +62,18 @@ private bool RunVerify()
Console.WriteLine($"Metadata score : {_replayInfo.BandScore}");
Console.WriteLine($"Real score : {bandScore}");
Console.WriteLine($"Difference : {Math.Abs(bandScore - _replayInfo.BandScore)}\n");

for (int frameIndex = 0; frameIndex < _replayData.Frames.Length; frameIndex++)
{
var frame = _replayData.Frames[frameIndex];
var result = results[frameIndex];

Console.WriteLine($"-------------");
Console.WriteLine($"Frame {frameIndex + 1}");
Console.WriteLine($"-------------");
PrintStatDifferences(frame.Stats, result.ResultStats);
Console.WriteLine();
}
return true;
}
}
Expand Down
12 changes: 10 additions & 2 deletions ReplayCli/Cli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ static void PrintStatDifference<T>(string name, T frameStat, T resultStat)
Console.WriteLine($"Base stats:");
PrintStatDifference("CommittedScore", originalStats.CommittedScore, resultStats.CommittedScore);
PrintStatDifference("PendingScore", originalStats.PendingScore, resultStats.PendingScore);
PrintStatDifference("NoteScore", originalStats.NoteScore, resultStats.NoteScore);
PrintStatDifference("SustainScore", originalStats.SustainScore, resultStats.SustainScore);
PrintStatDifference("MultiplierScore", originalStats.MultiplierScore, resultStats.MultiplierScore);
PrintStatDifference("TotalScore", originalStats.TotalScore, resultStats.TotalScore);
PrintStatDifference("StarScore", originalStats.StarScore, resultStats.StarScore);
PrintStatDifference("Combo", originalStats.Combo, resultStats.Combo);
Expand All @@ -210,6 +213,7 @@ static void PrintStatDifference<T>(string name, T frameStat, T resultStat)
PrintStatDifference("NotesMissed", originalStats.NotesMissed, resultStats.NotesMissed);
PrintStatDifference("Percent", originalStats.Percent, resultStats.Percent);
PrintStatDifference("StarPowerTickAmount", originalStats.StarPowerTickAmount, resultStats.StarPowerTickAmount);
PrintStatDifference("StarPowerWhammyTicks", originalStats.StarPowerWhammyTicks, resultStats.StarPowerWhammyTicks);
PrintStatDifference("TotalStarPowerTicks", originalStats.TotalStarPowerTicks, resultStats.TotalStarPowerTicks);
PrintStatDifference("TimeInStarPower", originalStats.TimeInStarPower, resultStats.TimeInStarPower);
PrintStatDifference("IsStarPowerActive", originalStats.IsStarPowerActive, resultStats.IsStarPowerActive);
Expand All @@ -229,14 +233,17 @@ static void PrintStatDifference<T>(string name, T frameStat, T resultStat)
PrintStatDifference("Overstrums", originalGuitar.Overstrums, resultGuitar.Overstrums);
PrintStatDifference("HoposStrummed", originalGuitar.HoposStrummed, resultGuitar.HoposStrummed);
PrintStatDifference("GhostInputs", originalGuitar.GhostInputs, resultGuitar.GhostInputs);
PrintStatDifference("StarPowerWhammyTicks", originalGuitar.StarPowerWhammyTicks, resultGuitar.StarPowerWhammyTicks);
PrintStatDifference("SustainScore", originalGuitar.SustainScore, resultGuitar.SustainScore);
break;
}
case (DrumsStats originalDrums, DrumsStats resultDrums):
{
Console.WriteLine("Drums stats:");
PrintStatDifference("Overhits", originalDrums.Overhits, resultDrums.Overhits);
PrintStatDifference("GhostsHit", originalDrums.GhostsHit, resultDrums.GhostsHit);
PrintStatDifference("TotalGhosts", originalDrums.TotalGhosts, resultDrums.TotalGhosts);
PrintStatDifference("AccentsHit", originalDrums.AccentsHit, resultDrums.AccentsHit);
PrintStatDifference("TotalAccents", originalDrums.TotalAccents, resultDrums.TotalAccents);
PrintStatDifference("DynamicsBonus", originalDrums.DynamicsBonus, resultDrums.DynamicsBonus);
break;
}
case (VocalsStats originalVocals, VocalsStats resultVocals):
Expand All @@ -251,6 +258,7 @@ static void PrintStatDifference<T>(string name, T frameStat, T resultStat)
{
Console.WriteLine("Pro Keys stats:");
PrintStatDifference("Overhits", originalKeys.Overhits, resultKeys.Overhits);
PrintStatDifference("FatFingersIgnored", originalKeys.FatFingersIgnored, resultKeys.FatFingersIgnored);
break;
}
default:
Expand Down
3 changes: 0 additions & 3 deletions YARG.Core/Chart/Events/ChartEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ public abstract class ChartEvent
public uint TickLength { get; set; }
public uint TickEnd => Tick + TickLength;

// For subclasses that set the base properties through other parameters
public ChartEvent() {}

public ChartEvent(double time, double timeLength, uint tick, uint tickLength)
{
Time = time;
Expand Down
51 changes: 5 additions & 46 deletions YARG.Core/Chart/Events/WaitCountdown.cs
Original file line number Diff line number Diff line change
@@ -1,59 +1,18 @@
using System.Collections.Generic;
using YARG.Core.Logging;

namespace YARG.Core.Chart
{
public class WaitCountdown : ChartEvent
{
public const float MIN_SECONDS = 9;
public const uint MIN_MEASURES = 4;
public const float MIN_MEASURE_LENGTH = 1;
public const float FADE_ANIM_LENGTH = 0.45f;
public const int END_COUNTDOWN_MEASURE = 1;

public int TotalMeasures => _measureBeatlines.Count;
public const double MIN_SECONDS = 9;
public const double END_COUNTDOWN_SECOND = 1;

//The time where the countdown should start fading out and overstrums will break combo again
public double DeactivateTime => _measureBeatlines[^(END_COUNTDOWN_MEASURE + 1)].Time;
public bool IsActive => MeasuresLeft > END_COUNTDOWN_MEASURE;

private List<Beatline> _measureBeatlines;

public int MeasuresLeft {get; private set; }
public double DeactivateTime => TimeEnd - END_COUNTDOWN_SECOND;

public WaitCountdown(List<Beatline> measureBeatlines)
public WaitCountdown(double time, double timeLength, uint tick, uint tickLength) : base(time, timeLength, tick, tickLength)
{
_measureBeatlines = measureBeatlines;

var firstCountdownMeasure = measureBeatlines[0];
var lastCountdownMeasure = measureBeatlines[^1];

Time = firstCountdownMeasure.Time;
Tick = firstCountdownMeasure.Tick;
TimeLength = lastCountdownMeasure.Time - Time;
TickLength = lastCountdownMeasure.Tick - Tick;

MeasuresLeft = TotalMeasures;
}

public int CalculateMeasuresLeft(uint currentTick)
{
int newMeasuresLeft;
if (currentTick >= TickEnd)
{
newMeasuresLeft = 0;
}
else if (currentTick < Tick)
{
newMeasuresLeft = TotalMeasures;
}
else
{
newMeasuresLeft = TotalMeasures - _measureBeatlines.GetIndexOfNext(currentTick);
}

MeasuresLeft = newMeasuresLeft;

return newMeasuresLeft;
}
}
}
4 changes: 2 additions & 2 deletions YARG.Core/Chart/SongChart.AutoGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ private void ParseForActivationPhrases(InstrumentDifficulty<DrumNote> diffChart,
}

// Limits for placing activation phrases (in seconds)
const float MIN_SPACING_TIME = 2;
const float MAX_SPACING_TIME = 10;
const double MIN_SPACING_TIME = 2;
const double MAX_SPACING_TIME = 10;

// Update this time to the latest SP/Solo/Activation phrase encountered for comparison with the above constants
// Start parsing after the end of the 1st SP phrase
Expand Down
42 changes: 35 additions & 7 deletions YARG.Core/Chart/Sync/SyncTrack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,16 +242,24 @@ public static double TickRangeToTimeDelta(uint tickStart, uint tickEnd, uint res
TempoChange currentTempo)
{
if (tickStart < currentTempo.Tick)
{
throw new ArgumentOutOfRangeException(nameof(tickStart), tickStart,
$"The given start tick must occur during the given tempo (starting at {currentTempo.Tick})!");
}

if (tickEnd < tickStart)
{
throw new ArgumentOutOfRangeException(nameof(tickEnd), tickEnd,
$"The given end tick must occur after the starting tick ({tickStart})!");
}

double tickDelta = tickEnd - tickStart;

uint tickDelta = tickEnd - tickStart;
double beatDelta = tickDelta / (double)resolution;
double timeDelta = beatDelta * 60.0 / currentTempo.BeatsPerMinute;
// The active code below is a slightly more precise version of the commented code,
// it seems to incur fewer rounding errors and should improve consistency.
// double beatDelta = tickDelta / resolution;
// double timeDelta = beatDelta * currentTempo.SecondsPerBeat;
double timeDelta = (tickDelta * 60.0) / (resolution * currentTempo.BeatsPerMinute);

return timeDelta;
}
Expand All @@ -260,18 +268,38 @@ public static uint TimeRangeToTickDelta(double timeStart, double timeEnd, uint r
TempoChange currentTempo)
{
if (timeStart < currentTempo.Time)
{
throw new ArgumentOutOfRangeException(nameof(timeStart), timeStart,
$"The given start time must occur during the given tempo (starting at {currentTempo.Tick})!");
$"The given start time must occur during the given tempo (starting at {currentTempo.Time})!");
}

if (timeEnd < timeStart)
{
throw new ArgumentOutOfRangeException(nameof(timeEnd), timeEnd,
$"The given end time must occur after the starting time ({timeStart})!");
}

double timeDelta = timeEnd - timeStart;
double beatDelta = timeDelta * currentTempo.BeatsPerMinute / 60.0;
uint tickDelta = (uint)Math.Round(beatDelta * resolution, 8);

return tickDelta;
// The active code below is a slightly more precise version of the commented code,
// it seems to incur fewer rounding errors and should improve consistency somewhat.
// double beatDelta = timeDelta / currentTempo.SecondsPerBeat;
// double tickDelta = beatDelta * resolution;
double tickDelta = (timeDelta * resolution * currentTempo.BeatsPerMinute) / 60.0;

// Despite the precision improvements above, there are still some floating-point imprecisions,
// making time <-> tick conversions not accurately round-trippable. Thus, we need to round to
// prevent truncation from resulting in the wrong tick.
//
// A more conservative approach is taken here, where rounding is only done if the result is
// within 0.1 of the next whole value.
double deltaDecimals = (tickDelta + 1) - tickDelta;
if (Math.Abs(deltaDecimals) >= 0.9)
{
tickDelta += 0.1;
}

return (uint) tickDelta;
}

public double GetStartTime()
Expand Down
14 changes: 7 additions & 7 deletions YARG.Core/Chart/Sync/TempoChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ namespace YARG.Core.Chart
{
public class TempoChange : SyncEvent, IEquatable<TempoChange>, ICloneable<TempoChange>
{
private const float SECONDS_PER_MINUTE = 60f;
private const double SECONDS_PER_MINUTE = 60;

public float BeatsPerMinute { get; }
public float SecondsPerBeat => SECONDS_PER_MINUTE / BeatsPerMinute;
public double BeatsPerMinute { get; }
public double SecondsPerBeat => SECONDS_PER_MINUTE / BeatsPerMinute;
public long MilliSecondsPerBeat => BpmToMicroSeconds(BeatsPerMinute) / 1000;
public long MicroSecondsPerBeat => BpmToMicroSeconds(BeatsPerMinute);

public TempoChange(float tempo, double time, uint tick) : base(time, tick)
public TempoChange(double tempo, double time, uint tick) : base(time, tick)
{
BeatsPerMinute = tempo;
}
Expand All @@ -23,19 +23,19 @@ public TempoChange Clone()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long BpmToMicroSeconds(float tempo)
public static long BpmToMicroSeconds(double tempo)
{
double secondsPerBeat = SECONDS_PER_MINUTE / tempo;
double microseconds = secondsPerBeat * 1000 * 1000;
return (long) microseconds;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MicroSecondsToBpm(long usecs)
public static double MicroSecondsToBpm(long usecs)
{
double secondsPerBeat = usecs / 1000f / 1000f;
double tempo = SECONDS_PER_MINUTE / secondsPerBeat;
return (float) tempo;
return tempo;
}

public static bool operator ==(TempoChange? left, TempoChange? right)
Expand Down
2 changes: 1 addition & 1 deletion YARG.Core/Chart/Sync/TimeSignatureEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace YARG.Core.Chart
{
public partial class TimeSignatureChange : SyncEvent, IEquatable<TimeSignatureChange>, ICloneable<TimeSignatureChange>
{
public const float QUARTER_NOTE_DENOMINATOR = 4f;
public const double QUARTER_NOTE_DENOMINATOR = 4;

public uint Numerator { get; }
public uint Denominator { get; }
Expand Down
Loading