Replace #MEASUREINFO with two separate tags, #NPSPERMEASURE and #NOTESPERMEASURE. Stop storing MeasureInfo objects on Steps, and just directly store NpsPerMeasure, NotesPerMeasure, and peakNPS.

This commit is contained in:
Michael Votaw
2025-02-15 13:57:50 -06:00
committed by teejusb
parent 2414595063
commit fd790eb83e
6 changed files with 150 additions and 45 deletions
-5
View File
@@ -4,11 +4,6 @@
#include "GameConstantsAndTypes.h"
#include "NoteData.h"
/** This is a container for per-measure stats of a stepchart.
This data is calculated and saved to the song cache files as the #MEASUREINFO tag.
This data is provided to the theme via lua functions in Steps.cpp and Trail.cpp,
*/
struct MeasureInfo
{
int measureCount;
+40 -6
View File
@@ -408,19 +408,52 @@ void SetTechCounts(StepsTagInfo& info)
info.ssc_format= true;
}
void SetMeasureInfo(StepsTagInfo& info)
void SetNpsPerMeasure(StepsTagInfo& info)
{
if (info.from_cache || info.for_load_edit)
{
std::vector<RString> values;
split((*info.params)[1], "|", values, true);
split((*info.params)[1], ",", values, true);
std::size_t measures_per_player = values.size() / NUM_PlayerNumber;
std::vector<std::vector<float>> npsPerMeasure;
npsPerMeasure.resize(NUM_PLAYERS);
MeasureInfo v[NUM_PLAYERS];
FOREACH_PlayerNumber(pn)
{
v[pn].FromString(values[pn]);
npsPerMeasure[pn].resize(measures_per_player, 0);
for(std::size_t i= 0; i < measures_per_player; ++i)
{
npsPerMeasure[pn][i]= StringToFloat(values[pn * measures_per_player + i]);
}
}
info.steps->SetCachedMeasureInfo(v);
info.steps->SetCachedNpsPerMeasure(npsPerMeasure);
}
else
{
// just recalc at time.
}
info.ssc_format= true;
}
void SetNotesPerMeasure(StepsTagInfo& info)
{
if (info.from_cache || info.for_load_edit)
{
std::vector<RString> values;
split((*info.params)[1], ",", values, true);
std::size_t measures_per_player = values.size() / NUM_PlayerNumber;
std::vector<std::vector<int>> notesPerMeasure;
notesPerMeasure.resize(NUM_PLAYERS);
FOREACH_PlayerNumber(pn)
{
notesPerMeasure[pn].resize(measures_per_player, 0);
for(std::size_t i= 0; i < measures_per_player; ++i)
{
notesPerMeasure[pn][i]= StringToInt(values[pn * measures_per_player + i]);
}
}
info.steps->SetCachedNotesPerMeasure(notesPerMeasure);
}
else
{
@@ -670,7 +703,8 @@ struct ssc_parser_helper_t
steps_tag_handlers["FAKES"]= &SetStepsFakes;
steps_tag_handlers["LABELS"]= &SetStepsLabels;
steps_tag_handlers["TECHCOUNTS"] = &SetTechCounts;
steps_tag_handlers["MEASUREINFO"] = &SetMeasureInfo;
steps_tag_handlers["NPSPERMEASURE"] = &SetNpsPerMeasure;
steps_tag_handlers["NOTESPERMEASURE"] = &SetNotesPerMeasure;
/* If this is called, the chart does not use the same attacks
* as the Song's timing. No other changes are required. */
+22 -7
View File
@@ -439,16 +439,31 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
}
}
lines.push_back(ssprintf("#TECHCOUNTS:%s;", join(",", asTechCounts).c_str()));
std::vector<RString> asMeasureInfo;
FOREACH_PlayerNumber( pn )
std::vector<RString> asNpsPerMeasure;
FOREACH_PlayerNumber(pn)
{
const MeasureInfo &ms = in.GetMeasureInfo(pn);
asMeasureInfo.push_back(ms.ToString());
const std::vector<float> &npsPerMeasure = in.GetNpsPerMeasure(pn);
for(unsigned i = 0; i < npsPerMeasure.size(); i++)
{
asNpsPerMeasure.push_back(ssprintf("%.3f", npsPerMeasure[i]));
}
}
RString allMeasureInfo = "#MEASUREINFO:" + join("|", asMeasureInfo) + ";";
lines.push_back(allMeasureInfo);
lines.push_back( ssprintf( "#NPSPERMEASURE:%s;", join(",",asNpsPerMeasure).c_str() ) );
std::vector<RString> asNotesPerMeasure;
FOREACH_PlayerNumber(pn)
{
const std::vector<int> &notesPerMeasure = in.GetNotesPerMeasure(pn);
for(unsigned i = 0; i < notesPerMeasure.size(); i++)
{
asNotesPerMeasure.push_back(ssprintf("%d", notesPerMeasure[i]));
}
}
lines.push_back( ssprintf( "#NOTESPERMEASURE:%s;", join(",",asNotesPerMeasure).c_str() ) );
// NOTE(MV): #STEPFILENAME has to be at the end of the cache tags,
// because it's used in SSCLoader::LoadFromSimfile to determine when
// to switch the state back to GETTING_SONG_INFO, which means any tags
+1 -1
View File
@@ -51,7 +51,7 @@
* @brief The internal version of the cache for StepMania.
*
* Increment this value to invalidate the current cache. */
const int FILE_CACHE_VERSION = 228;
const int FILE_CACHE_VERSION = 229;
/** @brief How long does a song sample last by default? */
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
+70 -21
View File
@@ -59,9 +59,16 @@ Steps::Steps(Song *song): m_StepsType(StepsType_Invalid), m_pSong(song),
m_Difficulty(Difficulty_Invalid), m_iMeter(0),
m_bAreCachedRadarValuesJustLoaded(false),
m_bAreCachedTechCountsValuesJustLoaded(false),
m_bAreCachedMeasureInfoJustLoaded(false),
m_AreCachedNpsPerMeasureJustLoaded(false),
m_AreCachedNotesPerMeasureJustLoaded(false),
m_sCredit(""), displayBPMType(DISPLAY_BPM_ACTUAL),
specifiedBPMMin(0), specifiedBPMMax(0) {}
specifiedBPMMin(0), specifiedBPMMax(0) {
m_CachedNpsPerMeasure.resize(NUM_PLAYERS);
m_CachedNotesPerMeasure.resize(NUM_PLAYERS);
FOREACH_PlayerNumber(pn) {
m_PeakNps[pn] = 0;
}
}
Steps::~Steps()
{
@@ -411,17 +418,19 @@ void Steps::CalculateMeasureInfo()
return;
}
if( m_bAreCachedMeasureInfoJustLoaded )
if( m_AreCachedNpsPerMeasureJustLoaded )
{
m_bAreCachedMeasureInfoJustLoaded = false;
m_AreCachedNpsPerMeasureJustLoaded = false;
return;
}
NoteData tempNoteData;
this->GetNoteData( tempNoteData );
MeasureInfo measureInfo[NUM_PLAYERS];
FOREACH_PlayerNumber(pn)
m_CachedMeasureInfo[pn]
measureInfo[pn]
.Zero();
GAMESTATE->SetProcessedTimingData(this->GetTimingData());
@@ -432,7 +441,7 @@ void Steps::CalculateMeasureInfo()
NoteDataUtil::SplitCompositeNoteData( tempNoteData, vParts );
for( std::size_t pn = 0; pn < std::min(vParts.size(), std::size_t(NUM_PLAYERS)); ++pn )
{
MeasureInfo::CalculateMeasureInfo(vParts[pn], m_CachedMeasureInfo[pn]);
MeasureInfo::CalculateMeasureInfo(vParts[pn], measureInfo[pn]);
}
}
else if (GAMEMAN->GetStepsTypeInfo(this->m_StepsType).m_StepsTypeCategory == StepsTypeCategory_Couple)
@@ -441,16 +450,25 @@ void Steps::CalculateMeasureInfo()
// XXX: Assumption that couple will always have an even number of notes.
const int tracks = tempNoteData.GetNumTracks() / 2;
p1.SetNumTracks(tracks);
MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[PLAYER_1]);
MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[PLAYER_1]);
NoteDataUtil::ShiftTracks(tempNoteData, tracks);
tempNoteData.SetNumTracks(tracks);
MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[PLAYER_2]);
MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[PLAYER_2]);
}
else
{
MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[0]);
std::fill_n( m_CachedMeasureInfo + 1, NUM_PLAYERS-1, m_CachedMeasureInfo[0] );
MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[0]);
std::fill_n( measureInfo + 1, NUM_PLAYERS-1, measureInfo[0] );
}
FOREACH_PlayerNumber(pn)
{
m_CachedNpsPerMeasure[pn].assign(measureInfo[pn].npsPerMeasure.begin(), measureInfo[pn].npsPerMeasure.end());
m_CachedNotesPerMeasure[pn].assign(measureInfo[pn].notesPerMeasure.begin(), measureInfo[pn].notesPerMeasure.end());
m_PeakNps[pn] = measureInfo[pn].peakNps;
}
GAMESTATE->SetProcessedTimingData(nullptr);
}
@@ -604,7 +622,11 @@ void Steps::DeAutogen( bool bCopyNoteData )
m_iMeter = Real()->m_iMeter;
std::copy( Real()->m_CachedRadarValues, Real()->m_CachedRadarValues + NUM_PLAYERS, m_CachedRadarValues );
std::copy( Real()->m_CachedTechCounts, Real()->m_CachedTechCounts + NUM_PLAYERS, m_CachedTechCounts );
std::copy( Real()->m_CachedMeasureInfo, Real()->m_CachedMeasureInfo + NUM_PLAYERS, m_CachedMeasureInfo );
m_CachedNpsPerMeasure.assign(Real()->m_CachedNpsPerMeasure.begin(), Real()->m_CachedNpsPerMeasure.end());
m_CachedNotesPerMeasure.assign(Real()->m_CachedNotesPerMeasure.begin(), Real()->m_CachedNotesPerMeasure.end());
m_sCredit = Real()->m_sCredit;
parent = nullptr;
@@ -740,11 +762,40 @@ void Steps::SetCachedTechCounts( const TechCounts ts[NUM_PLAYERS] )
m_bAreCachedTechCountsValuesJustLoaded = true;
}
void Steps::SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS])
void Steps::SetCachedNpsPerMeasure(std::vector<std::vector<float>>& npsPerMeasure)
{
DeAutogen();
std::copy(ms, ms + NUM_PLAYERS, m_CachedMeasureInfo);
m_bAreCachedMeasureInfoJustLoaded = true;
if(npsPerMeasure.size() != NUM_PLAYERS)
{
return;
}
FOREACH_PlayerNumber(pn)
{
m_CachedNpsPerMeasure[pn].assign(npsPerMeasure[pn].begin(), npsPerMeasure[pn].end());
std::vector<float>::iterator peakNps = std::max_element(npsPerMeasure[pn].begin(), npsPerMeasure[pn].end());
if(peakNps != npsPerMeasure[pn].end())
{
m_PeakNps[pn] = *peakNps;
}
}
m_AreCachedNpsPerMeasureJustLoaded = true;
}
void Steps::SetCachedNotesPerMeasure(std::vector<std::vector<int>>& notesPerMeasure)
{
DeAutogen();
if(notesPerMeasure.size() != NUM_PLAYERS)
{
return;
}
FOREACH_PlayerNumber(pn)
{
m_CachedNotesPerMeasure[pn].assign(notesPerMeasure[pn].begin(), notesPerMeasure[pn].end());
}
m_AreCachedNotesPerMeasureJustLoaded = true;
}
RString Steps::GenerateChartKey()
@@ -900,8 +951,8 @@ public:
if (!lua_isnil(L, 1)) {
pn = Enum::Check<PlayerNumber>(L, 1);
}
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
LuaHelpers::CreateTableFromArray(ts.npsPerMeasure, L);
std::vector<float> &ts = const_cast<std::vector<float> &>(p->GetNpsPerMeasure(pn));
LuaHelpers::CreateTableFromArray(ts, L);
return 1;
}
@@ -911,9 +962,8 @@ public:
if (!lua_isnil(L, 1)) {
pn = Enum::Check<PlayerNumber>(L, 1);
}
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
LuaHelpers::CreateTableFromArray(ts.notesPerMeasure, L);
std::vector<int> &ts = const_cast<std::vector<int> &>(p->GetNotesPerMeasure(pn));
LuaHelpers::CreateTableFromArray(ts, L);
return 1;
}
@@ -923,8 +973,7 @@ public:
if (!lua_isnil(L, 1)) {
pn = Enum::Check<PlayerNumber>(L, 1);
}
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
lua_pushnumber(L, ts.peakNps);
lua_pushnumber(L, p->GetPeakNps(pn));
return 1;
}
+17 -5
View File
@@ -145,7 +145,8 @@ public:
void SetMeter( int meter );
void SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] );
void SetCachedTechCounts(const TechCounts ts[NUM_PLAYERS]);
void SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS]);
void SetCachedNpsPerMeasure(std::vector<std::vector<float>>& npsPerMeasure);
void SetCachedNotesPerMeasure(std::vector<std::vector<int>>& notesPerMeasure);
float PredictMeter() const;
unsigned GetHash() const;
@@ -178,7 +179,10 @@ public:
const TechCounts &GetTechCounts(PlayerNumber pn) const { return Real()->m_CachedTechCounts[pn]; }
void CalculateMeasureInfo();
const MeasureInfo &GetMeasureInfo(PlayerNumber pn) const { return Real()->m_CachedMeasureInfo[pn]; }
const std::vector<float> &GetNpsPerMeasure(PlayerNumber pn) const { return Real()->m_CachedNpsPerMeasure[pn]; }
const std::vector<int> &GetNotesPerMeasure(PlayerNumber pn) const { return Real()->m_CachedNotesPerMeasure[pn]; }
float GetPeakNps(PlayerNumber pn) const { return Real()->m_PeakNps[pn]; }
/**
* @brief The TimingData used by the Steps.
@@ -276,9 +280,17 @@ private:
/** @brief The tech stats used for each player */
mutable TechCounts m_CachedTechCounts[NUM_PLAYERS];
bool m_bAreCachedTechCountsValuesJustLoaded;
mutable MeasureInfo m_CachedMeasureInfo[NUM_PLAYERS];
bool m_bAreCachedMeasureInfoJustLoaded;
std::vector<std::vector<float>> m_CachedNpsPerMeasure;
bool m_AreCachedNpsPerMeasureJustLoaded;
std::vector<std::vector<int>> m_CachedNotesPerMeasure;
bool m_AreCachedNotesPerMeasureJustLoaded;
float m_PeakNps[NUM_PLAYERS];
/** @brief The name of the person who created the Steps. */
RString m_sCredit;