Added MeasureInfo calculations, and loader/writer for #MEASUREINFO cache
This commit is contained in:
@@ -0,0 +1,133 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "MeasureInfo.h"
|
||||||
|
#include "NoteData.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
#include "LocalizedString.h"
|
||||||
|
#include "LuaBinding.h"
|
||||||
|
#include "TimingData.h"
|
||||||
|
#include "GameState.h"
|
||||||
|
|
||||||
|
|
||||||
|
RString MeasureInfo::ToString() const
|
||||||
|
{
|
||||||
|
std::vector<RString> asMeasureInfo;
|
||||||
|
for (unsigned i = 0; i < npsPerMeasure.size(); i++)
|
||||||
|
{
|
||||||
|
asMeasureInfo.push_back(ssprintf("%.6f", npsPerMeasure[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < notesPerMeasure.size(); i++)
|
||||||
|
{
|
||||||
|
asMeasureInfo.push_back(ssprintf("%d", notesPerMeasure[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(",", asMeasureInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeasureInfo::FromString(RString sValues)
|
||||||
|
{
|
||||||
|
std::vector<RString> asValues;
|
||||||
|
split( sValues, ",", asValues, true );
|
||||||
|
int half_size = static_cast<int>(asValues.size()) / 2;
|
||||||
|
|
||||||
|
float peak_nps = 0;
|
||||||
|
for (int i = 0; i < half_size; i++)
|
||||||
|
{
|
||||||
|
float nps = StringToFloat(asValues[i]);
|
||||||
|
this->npsPerMeasure.push_back(nps);
|
||||||
|
if(nps > peak_nps)
|
||||||
|
{
|
||||||
|
peak_nps = nps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = half_size; i < asValues.size(); i++)
|
||||||
|
{
|
||||||
|
this->notesPerMeasure.push_back(StringToInt(asValues[i]));
|
||||||
|
}
|
||||||
|
this->measureCount = half_size;
|
||||||
|
this->peakNps = peak_nps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeasureInfo::CalculateMeasureInfo(const NoteData &in, MeasureInfo &out)
|
||||||
|
{
|
||||||
|
int lastRow = in.GetLastRow();
|
||||||
|
int lastRowMeasureIndex = 0;
|
||||||
|
int lastRowBeatIndex = 0;
|
||||||
|
int lastRowRemainder = 0;
|
||||||
|
TimingData *timing = GAMESTATE->GetProcessedTimingData();
|
||||||
|
timing->NoteRowToMeasureAndBeat(lastRow, lastRowMeasureIndex, lastRowBeatIndex, lastRowRemainder);
|
||||||
|
|
||||||
|
int totalMeasureCount = lastRowMeasureIndex + 1;
|
||||||
|
|
||||||
|
out.notesPerMeasure.clear();
|
||||||
|
out.npsPerMeasure.clear();
|
||||||
|
|
||||||
|
out.notesPerMeasure.resize(totalMeasureCount, 0);
|
||||||
|
out.npsPerMeasure.resize(totalMeasureCount, 0);
|
||||||
|
|
||||||
|
NoteData::all_tracks_const_iterator curr_note = in.GetTapNoteRangeAllTracks(0, MAX_NOTE_ROW);
|
||||||
|
|
||||||
|
int iMeasureIndexOut = 0;
|
||||||
|
int iBeatIndexOut = 0;
|
||||||
|
int iRowsRemainder = 0;
|
||||||
|
|
||||||
|
float peak_nps = 0;
|
||||||
|
int curr_row = -1;
|
||||||
|
int notes_this_row = 0;
|
||||||
|
|
||||||
|
while (!curr_note.IsAtEnd())
|
||||||
|
{
|
||||||
|
if(curr_note.Row() != curr_row)
|
||||||
|
{
|
||||||
|
// Before moving on to a new row, update the row count for the "current" measure
|
||||||
|
// Note that we're only add
|
||||||
|
out.notesPerMeasure[iMeasureIndexOut] += notes_this_row;
|
||||||
|
// Update iMeasureIndex for the current row
|
||||||
|
timing->NoteRowToMeasureAndBeat(curr_note.Row(), iMeasureIndexOut, iBeatIndexOut, iRowsRemainder);
|
||||||
|
curr_row = curr_note.Row();
|
||||||
|
notes_this_row = 0;
|
||||||
|
}
|
||||||
|
// Update tap and mine count for the current measure
|
||||||
|
// Regardless of how many notes are on this row, it's only considered 1 "note" when we want to
|
||||||
|
// calculate nps. So jumps/brackets don't inflate the nps value. Maybe we should call it
|
||||||
|
// "steps per second" or something like that?
|
||||||
|
if (curr_note->type == TapNoteType_Tap || curr_note->type == TapNoteType_HoldHead)
|
||||||
|
{
|
||||||
|
notes_this_row = 1;
|
||||||
|
}
|
||||||
|
++curr_note;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And handle the final note...
|
||||||
|
out.notesPerMeasure[iMeasureIndexOut] += notes_this_row;
|
||||||
|
|
||||||
|
// Now that all of the notes have been parsed, calculate nps for each measure
|
||||||
|
for (int m = 0; m < totalMeasureCount; m++)
|
||||||
|
{
|
||||||
|
if(out.notesPerMeasure[m] == 0)
|
||||||
|
{
|
||||||
|
out.npsPerMeasure[m] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float measureDuration = timing->GetElapsedTimeFromBeat(4 * (m+1)) - timing->GetElapsedTimeFromBeat(4 * m);
|
||||||
|
float nps = out.notesPerMeasure[m] / measureDuration;
|
||||||
|
|
||||||
|
if(measureDuration < 0.12)
|
||||||
|
{
|
||||||
|
out.npsPerMeasure[m] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.npsPerMeasure[m] = nps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nps > peak_nps)
|
||||||
|
{
|
||||||
|
peak_nps = nps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.peakNps = peak_nps;
|
||||||
|
out.measureCount = totalMeasureCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef MEASURE_INFO_H
|
||||||
|
#define MEASURE_INFO_H
|
||||||
|
|
||||||
|
#include "GameConstantsAndTypes.h"
|
||||||
|
class NoteData;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
float peakNps;
|
||||||
|
std::vector<float> npsPerMeasure;
|
||||||
|
std::vector<int> notesPerMeasure;
|
||||||
|
|
||||||
|
MeasureInfo()
|
||||||
|
{
|
||||||
|
Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Zero()
|
||||||
|
{
|
||||||
|
measureCount = 0;
|
||||||
|
peakNps = 0;
|
||||||
|
npsPerMeasure.clear();
|
||||||
|
notesPerMeasure.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
RString ToString() const;
|
||||||
|
void FromString( RString sValues );
|
||||||
|
static void CalculateMeasureInfo(const NoteData &in, MeasureInfo &out);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -383,6 +383,30 @@ void SetRadarValues(StepsTagInfo& info)
|
|||||||
}
|
}
|
||||||
info.ssc_format= true;
|
info.ssc_format= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetMeasureInfo(StepsTagInfo& info)
|
||||||
|
{
|
||||||
|
if (info.from_cache || info.for_load_edit)
|
||||||
|
{
|
||||||
|
std::vector<RString> values;
|
||||||
|
split((*info.params)[1], "|", values, true);
|
||||||
|
|
||||||
|
MeasureInfo v[NUM_PLAYERS];
|
||||||
|
FOREACH_PlayerNumber(pn)
|
||||||
|
{
|
||||||
|
v[pn].FromString(values[pn]);
|
||||||
|
|
||||||
|
}
|
||||||
|
info.steps->SetCachedMeasureInfo(v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// just recalc at time.
|
||||||
|
}
|
||||||
|
info.ssc_format= true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetCredit(StepsTagInfo& info)
|
void SetCredit(StepsTagInfo& info)
|
||||||
{
|
{
|
||||||
info.steps->SetCredit((*info.params)[1]);
|
info.steps->SetCredit((*info.params)[1]);
|
||||||
@@ -623,6 +647,8 @@ struct ssc_parser_helper_t
|
|||||||
steps_tag_handlers["SCROLLS"]= &SetStepsScrolls;
|
steps_tag_handlers["SCROLLS"]= &SetStepsScrolls;
|
||||||
steps_tag_handlers["FAKES"]= &SetStepsFakes;
|
steps_tag_handlers["FAKES"]= &SetStepsFakes;
|
||||||
steps_tag_handlers["LABELS"]= &SetStepsLabels;
|
steps_tag_handlers["LABELS"]= &SetStepsLabels;
|
||||||
|
steps_tag_handlers["MEASUREINFO"] = &SetMeasureInfo;
|
||||||
|
|
||||||
/* If this is called, the chart does not use the same attacks
|
/* If this is called, the chart does not use the same attacks
|
||||||
* as the Song's timing. No other changes are required. */
|
* as the Song's timing. No other changes are required. */
|
||||||
steps_tag_handlers["ATTACKS"]= &SetStepsAttacks;
|
steps_tag_handlers["ATTACKS"]= &SetStepsAttacks;
|
||||||
|
|||||||
@@ -428,6 +428,14 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
|||||||
}
|
}
|
||||||
if (bSavingCache)
|
if (bSavingCache)
|
||||||
{
|
{
|
||||||
|
std::vector<RString> asMeasureInfo;
|
||||||
|
FOREACH_PlayerNumber( pn )
|
||||||
|
{
|
||||||
|
const MeasureInfo &ms = in.GetMeasureInfo(pn);
|
||||||
|
asMeasureInfo.push_back(ms.ToString());
|
||||||
|
}
|
||||||
|
lines.push_back(ssprintf("#MEASUREINFO:%s;", join("|", asMeasureInfo).c_str()));
|
||||||
|
|
||||||
lines.push_back(ssprintf("#STEPFILENAME:%s;", in.GetFilename().c_str()));
|
lines.push_back(ssprintf("#STEPFILENAME:%s;", in.GetFilename().c_str()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ Steps::Steps(Song *song): m_StepsType(StepsType_Invalid), m_pSong(song),
|
|||||||
m_sDescription(""), m_sChartStyle(""),
|
m_sDescription(""), m_sChartStyle(""),
|
||||||
m_Difficulty(Difficulty_Invalid), m_iMeter(0),
|
m_Difficulty(Difficulty_Invalid), m_iMeter(0),
|
||||||
m_bAreCachedRadarValuesJustLoaded(false),
|
m_bAreCachedRadarValuesJustLoaded(false),
|
||||||
|
m_bAreCachedMeasureInfoJustLoaded(false),
|
||||||
m_sCredit(""), displayBPMType(DISPLAY_BPM_ACTUAL),
|
m_sCredit(""), displayBPMType(DISPLAY_BPM_ACTUAL),
|
||||||
specifiedBPMMin(0), specifiedBPMMax(0) {}
|
specifiedBPMMin(0), specifiedBPMMax(0) {}
|
||||||
|
|
||||||
@@ -354,6 +355,58 @@ void Steps::CalculateRadarValues( float fMusicLengthSeconds )
|
|||||||
GAMESTATE->SetProcessedTimingData(nullptr);
|
GAMESTATE->SetProcessedTimingData(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Steps::CalculateMeasureInfo()
|
||||||
|
{
|
||||||
|
if(parent != nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_bAreCachedMeasureInfoJustLoaded )
|
||||||
|
{
|
||||||
|
m_bAreCachedMeasureInfoJustLoaded = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoteData tempNoteData;
|
||||||
|
this->GetNoteData( tempNoteData );
|
||||||
|
|
||||||
|
FOREACH_PlayerNumber(pn)
|
||||||
|
m_CachedMeasureInfo[pn]
|
||||||
|
.Zero();
|
||||||
|
|
||||||
|
GAMESTATE->SetProcessedTimingData(this->GetTimingData());
|
||||||
|
|
||||||
|
if( tempNoteData.IsComposite() )
|
||||||
|
{
|
||||||
|
std::vector<NoteData> vParts;
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (GAMEMAN->GetStepsTypeInfo(this->m_StepsType).m_StepsTypeCategory == StepsTypeCategory_Couple)
|
||||||
|
{
|
||||||
|
NoteData p1 = tempNoteData;
|
||||||
|
// 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]);
|
||||||
|
NoteDataUtil::ShiftTracks(tempNoteData, tracks);
|
||||||
|
tempNoteData.SetNumTracks(tracks);
|
||||||
|
MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[PLAYER_2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[0]);
|
||||||
|
std::fill_n( m_CachedMeasureInfo + 1, NUM_PLAYERS-1, m_CachedMeasureInfo[0] );
|
||||||
|
}
|
||||||
|
GAMESTATE->SetProcessedTimingData(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Steps::ChangeFilenamesForCustomSong()
|
void Steps::ChangeFilenamesForCustomSong()
|
||||||
{
|
{
|
||||||
m_sFilename= custom_songify_path(m_sFilename);
|
m_sFilename= custom_songify_path(m_sFilename);
|
||||||
@@ -503,6 +556,7 @@ void Steps::DeAutogen( bool bCopyNoteData )
|
|||||||
m_Difficulty = Real()->m_Difficulty;
|
m_Difficulty = Real()->m_Difficulty;
|
||||||
m_iMeter = Real()->m_iMeter;
|
m_iMeter = Real()->m_iMeter;
|
||||||
std::copy( Real()->m_CachedRadarValues, Real()->m_CachedRadarValues + NUM_PLAYERS, m_CachedRadarValues );
|
std::copy( Real()->m_CachedRadarValues, Real()->m_CachedRadarValues + NUM_PLAYERS, m_CachedRadarValues );
|
||||||
|
std::copy( Real()->m_CachedMeasureInfo, Real()->m_CachedMeasureInfo + NUM_PLAYERS, m_CachedMeasureInfo );
|
||||||
m_sCredit = Real()->m_sCredit;
|
m_sCredit = Real()->m_sCredit;
|
||||||
parent = nullptr;
|
parent = nullptr;
|
||||||
|
|
||||||
@@ -631,6 +685,13 @@ void Steps::SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] )
|
|||||||
m_bAreCachedRadarValuesJustLoaded = true;
|
m_bAreCachedRadarValuesJustLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Steps::SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS])
|
||||||
|
{
|
||||||
|
DeAutogen();
|
||||||
|
std::copy(ms, ms + NUM_PLAYERS, m_CachedMeasureInfo);
|
||||||
|
m_bAreCachedMeasureInfoJustLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
RString Steps::GenerateChartKey()
|
RString Steps::GenerateChartKey()
|
||||||
{
|
{
|
||||||
ChartKey = this->GenerateChartKey(*m_pNoteData, this->GetTimingData());
|
ChartKey = this->GenerateChartKey(*m_pNoteData, this->GetTimingData());
|
||||||
@@ -813,6 +874,40 @@ public:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int GetNPSPerMeasure(T *p, lua_State *L)
|
||||||
|
{
|
||||||
|
PlayerNumber pn = PLAYER_1;
|
||||||
|
if (!lua_isnil(L, 1)) {
|
||||||
|
pn = Enum::Check<PlayerNumber>(L, 1);
|
||||||
|
}
|
||||||
|
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
|
||||||
|
LuaHelpers::CreateTableFromArray(ts.npsPerMeasure, L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetNotesPerMeasure(T *p, lua_State * L)
|
||||||
|
{
|
||||||
|
PlayerNumber pn = PLAYER_1;
|
||||||
|
if (!lua_isnil(L, 1)) {
|
||||||
|
pn = Enum::Check<PlayerNumber>(L, 1);
|
||||||
|
}
|
||||||
|
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
|
||||||
|
LuaHelpers::CreateTableFromArray(ts.notesPerMeasure, L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetPeakNPS(T *p, lua_State *L)
|
||||||
|
{
|
||||||
|
PlayerNumber pn = PLAYER_1;
|
||||||
|
if (!lua_isnil(L, 1)) {
|
||||||
|
pn = Enum::Check<PlayerNumber>(L, 1);
|
||||||
|
}
|
||||||
|
MeasureInfo &ts = const_cast<MeasureInfo &>(p->GetMeasureInfo(pn));
|
||||||
|
lua_pushnumber(L, ts.peakNps);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int GetColumnCues(T *p, lua_State*L)
|
static int GetColumnCues(T *p, lua_State*L)
|
||||||
{
|
{
|
||||||
float minDuration = 1.5;
|
float minDuration = 1.5;
|
||||||
@@ -883,6 +978,9 @@ public:
|
|||||||
ADD_METHOD( PredictMeter );
|
ADD_METHOD( PredictMeter );
|
||||||
ADD_METHOD( GetDisplayBPMType );
|
ADD_METHOD( GetDisplayBPMType );
|
||||||
ADD_METHOD( GetColumnCues );
|
ADD_METHOD( GetColumnCues );
|
||||||
|
ADD_METHOD( GetNPSPerMeasure );
|
||||||
|
ADD_METHOD( GetNotesPerMeasure );
|
||||||
|
ADD_METHOD( GetPeakNPS );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "RageUtil_AutoPtr.h"
|
#include "RageUtil_AutoPtr.h"
|
||||||
#include "TimingData.h"
|
#include "TimingData.h"
|
||||||
#include "ColumnCues.h"
|
#include "ColumnCues.h"
|
||||||
|
#include "MeasureInfo.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -140,6 +141,7 @@ public:
|
|||||||
void SetLoadedFromProfile( ProfileSlot slot ) { m_LoadedFromProfile = slot; }
|
void SetLoadedFromProfile( ProfileSlot slot ) { m_LoadedFromProfile = slot; }
|
||||||
void SetMeter( int meter );
|
void SetMeter( int meter );
|
||||||
void SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] );
|
void SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] );
|
||||||
|
void SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS]);
|
||||||
float PredictMeter() const;
|
float PredictMeter() const;
|
||||||
|
|
||||||
unsigned GetHash() const;
|
unsigned GetHash() const;
|
||||||
@@ -164,6 +166,9 @@ public:
|
|||||||
void TidyUpData();
|
void TidyUpData();
|
||||||
void CalculateRadarValues( float fMusicLengthSeconds );
|
void CalculateRadarValues( float fMusicLengthSeconds );
|
||||||
|
|
||||||
|
void CalculateMeasureInfo();
|
||||||
|
const MeasureInfo &GetMeasureInfo(PlayerNumber pn) const { return Real()->m_CachedMeasureInfo[pn]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The TimingData used by the Steps.
|
* @brief The TimingData used by the Steps.
|
||||||
*
|
*
|
||||||
@@ -256,6 +261,10 @@ private:
|
|||||||
/** @brief The radar values used for each player. */
|
/** @brief The radar values used for each player. */
|
||||||
RadarValues m_CachedRadarValues[NUM_PLAYERS];
|
RadarValues m_CachedRadarValues[NUM_PLAYERS];
|
||||||
bool m_bAreCachedRadarValuesJustLoaded;
|
bool m_bAreCachedRadarValuesJustLoaded;
|
||||||
|
|
||||||
|
mutable MeasureInfo m_CachedMeasureInfo[NUM_PLAYERS];
|
||||||
|
bool m_bAreCachedMeasureInfoJustLoaded;
|
||||||
|
|
||||||
/** @brief The name of the person who created the Steps. */
|
/** @brief The name of the person who created the Steps. */
|
||||||
RString m_sCredit;
|
RString m_sCredit;
|
||||||
/** @brief The name of the chart. */
|
/** @brief The name of the chart. */
|
||||||
|
|||||||
Reference in New Issue
Block a user