2003-08-07 06:36:34 +00:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "NoteDataUtil.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "RageLog.h"
|
2003-08-13 19:17:28 +00:00
|
|
|
#include "PlayerOptions.h"
|
2003-11-20 16:17:34 +00:00
|
|
|
#include "song.h"
|
2004-02-29 09:55:03 +00:00
|
|
|
#include "GameState.h"
|
2004-07-11 07:21:33 +00:00
|
|
|
#include "RadarValues.h"
|
2004-07-23 04:45:48 +00:00
|
|
|
#include "Foreach.h"
|
2003-08-07 06:36:34 +00:00
|
|
|
|
|
|
|
|
NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex )
|
|
|
|
|
{
|
|
|
|
|
const int iMeasureStartIndex = iMeasureIndex * ROWS_PER_MEASURE;
|
|
|
|
|
const int iMeasureLastIndex = (iMeasureIndex+1) * ROWS_PER_MEASURE - 1;
|
|
|
|
|
|
|
|
|
|
// probe to find the smallest note type
|
|
|
|
|
NoteType nt;
|
|
|
|
|
for( nt=(NoteType)0; nt<NUM_NOTE_TYPES; nt=NoteType(nt+1) ) // for each NoteType, largest to largest
|
|
|
|
|
{
|
|
|
|
|
float fBeatSpacing = NoteTypeToBeat( nt );
|
|
|
|
|
int iRowSpacing = int(roundf( fBeatSpacing * ROWS_PER_BEAT ));
|
|
|
|
|
|
|
|
|
|
bool bFoundSmallerNote = false;
|
2005-01-22 19:18:42 +00:00
|
|
|
// for each index in this measure
|
|
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( n, i, iMeasureStartIndex, iMeasureLastIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
if( i % iRowSpacing == 0 )
|
|
|
|
|
continue; // skip
|
|
|
|
|
|
|
|
|
|
if( !n.IsRowEmpty(i) )
|
|
|
|
|
{
|
|
|
|
|
bFoundSmallerNote = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( bFoundSmallerNote )
|
|
|
|
|
continue; // searching the next NoteType
|
|
|
|
|
else
|
|
|
|
|
break; // stop searching. We found the smallest NoteType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( nt == NUM_NOTE_TYPES ) // we didn't find one
|
|
|
|
|
return NOTE_TYPE_INVALID; // well-formed notes created in the editor should never get here
|
|
|
|
|
else
|
|
|
|
|
return nt;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
//
|
|
|
|
|
// Load note data
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/* Clear notes, but keep the same number of tracks. */
|
|
|
|
|
int iNumTracks = out.GetNumTracks();
|
|
|
|
|
out.Init();
|
|
|
|
|
out.SetNumTracks( iNumTracks );
|
|
|
|
|
|
|
|
|
|
// strip comments out of sSMNoteData
|
|
|
|
|
while( sSMNoteData.Find("//") != -1 )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
int iIndexCommentStart = sSMNoteData.Find("//");
|
|
|
|
|
int iIndexCommentEnd = sSMNoteData.Find("\n", iIndexCommentStart);
|
|
|
|
|
if( iIndexCommentEnd == -1 ) // comment doesn't have an end?
|
|
|
|
|
sSMNoteData.erase( iIndexCommentStart, 2 );
|
|
|
|
|
else
|
|
|
|
|
sSMNoteData.erase( iIndexCommentStart, iIndexCommentEnd-iIndexCommentStart );
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
CStringArray asMeasures;
|
|
|
|
|
split( sSMNoteData, ",", asMeasures, true ); // ignore empty is important
|
|
|
|
|
for( unsigned m=0; m<asMeasures.size(); m++ ) // foreach measure
|
|
|
|
|
{
|
|
|
|
|
CString &sMeasureString = asMeasures[m];
|
|
|
|
|
TrimLeft(sMeasureString);
|
|
|
|
|
TrimRight(sMeasureString);
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
CStringArray asMeasureLines;
|
|
|
|
|
split( sMeasureString, "\n", asMeasureLines, true ); // ignore empty is important
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
for( unsigned l=0; l<asMeasureLines.size(); l++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
CString &sMeasureLine = asMeasureLines[l];
|
|
|
|
|
TrimLeft(sMeasureLine);
|
|
|
|
|
TrimRight(sMeasureLine);
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
const float fPercentIntoMeasure = l/(float)asMeasureLines.size();
|
|
|
|
|
const float fBeat = (m + fPercentIntoMeasure) * BEATS_PER_MEASURE;
|
|
|
|
|
const int iIndex = BeatToNoteRow( fBeat );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
// if( m_iNumTracks != sMeasureLine.GetLength() )
|
|
|
|
|
// RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() );
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
const char *p = sMeasureLine;
|
|
|
|
|
int iTrack = 0;
|
2005-01-03 20:53:03 +00:00
|
|
|
while( iTrack < iNumTracks && *p )
|
2004-10-23 23:41:49 +00:00
|
|
|
{
|
|
|
|
|
TapNote tn;
|
|
|
|
|
char ch = *p;
|
2005-02-07 21:31:18 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
switch( ch )
|
|
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
case '0': tn = TAP_EMPTY; break;
|
|
|
|
|
case '1': tn = TAP_ORIGINAL_TAP; break;
|
2005-02-07 21:31:18 +00:00
|
|
|
case '2':
|
|
|
|
|
tn = TAP_ORIGINAL_HOLD_HEAD;
|
|
|
|
|
|
|
|
|
|
/* Set the hold note to have infinite length. We'll clamp it when
|
|
|
|
|
* we hit the tail. */
|
|
|
|
|
tn.iDuration = MAX_NOTE_ROW;
|
|
|
|
|
break;
|
|
|
|
|
case '3':
|
|
|
|
|
{
|
|
|
|
|
/* This is the end of a hold. Search for the beginning. */
|
|
|
|
|
int iHeadRow;
|
|
|
|
|
if( !out.IsHoldNoteAtBeat( iTrack, iIndex, &iHeadRow ) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Unmatched 3 in \"%s\"", sMeasureLine.c_str() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TapNote head_tap = out.GetTapNote( iTrack, iHeadRow );
|
|
|
|
|
head_tap.iDuration = iIndex - iHeadRow;
|
|
|
|
|
out.SetTapNote( iTrack, iHeadRow, head_tap );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This won't write tn, but keep parsing normally anyway. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2004-10-23 23:41:49 +00:00
|
|
|
// case 'm':
|
|
|
|
|
// Don't be loose with the definition. Use only 'M' since
|
|
|
|
|
// that's what we've been writing to disk. -Chris
|
2004-10-24 10:20:24 +00:00
|
|
|
case 'M': tn = TAP_ORIGINAL_MINE; break;
|
|
|
|
|
case 'A': tn = TAP_ORIGINAL_ATTACK; break;
|
2004-10-24 10:45:30 +00:00
|
|
|
case 'K': tn = TAP_ORIGINAL_AUTO_KEYSOUND; break;
|
2004-10-23 23:41:49 +00:00
|
|
|
default:
|
|
|
|
|
/* Invalid data. We don't want to assert, since there might
|
|
|
|
|
* simply be invalid data in an .SM, and we don't want to die
|
|
|
|
|
* due to invalid data. We should probably check for this when
|
|
|
|
|
* we load SM data for the first time ... */
|
|
|
|
|
// ASSERT(0);
|
|
|
|
|
tn = TAP_EMPTY;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
// look for optional attack info (e.g. "{tipsy,50% drunk:15.2}")
|
|
|
|
|
if( *p == '{' )
|
|
|
|
|
{
|
|
|
|
|
p++;
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
char szModifiers[256] = "";
|
|
|
|
|
float fDurationSeconds = 0;
|
2005-01-09 22:28:31 +00:00
|
|
|
if( sscanf( p, "%255[^:]:%f}", szModifiers, &fDurationSeconds ) == 2 ) // not fatal if this fails due to malformed data
|
2004-10-23 23:41:49 +00:00
|
|
|
{
|
|
|
|
|
tn.type = TapNote::attack;
|
|
|
|
|
tn.sAttackModifiers = szModifiers;
|
|
|
|
|
tn.fAttackDurationSeconds = fDurationSeconds;
|
|
|
|
|
}
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
// skip past the '}'
|
|
|
|
|
while( *p )
|
2003-11-17 03:38:24 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
if( *p == '}' )
|
2003-11-17 03:38:24 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
p++;
|
|
|
|
|
break;
|
2003-11-17 03:38:24 +00:00
|
|
|
}
|
2004-10-23 23:41:49 +00:00
|
|
|
p++;
|
2003-11-17 03:38:24 +00:00
|
|
|
}
|
2004-10-23 23:41:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// look for optional keysound index (e.g. "[123]")
|
|
|
|
|
if( *p == '[' )
|
|
|
|
|
{
|
2004-09-29 07:19:44 +00:00
|
|
|
p++;
|
2004-10-23 23:41:49 +00:00
|
|
|
int iKeysoundIndex = 0;
|
|
|
|
|
if( 1 == sscanf( p, "%d]", &iKeysoundIndex ) ) // not fatal if this fails due to malformed data
|
2004-09-29 07:19:44 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
tn.bKeysound = true;
|
|
|
|
|
tn.iKeysoundIndex = iKeysoundIndex;
|
|
|
|
|
}
|
2004-09-29 07:19:44 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
// skip past the ']'
|
|
|
|
|
while( *p )
|
|
|
|
|
{
|
|
|
|
|
if( *p == ']' )
|
2004-09-29 07:19:44 +00:00
|
|
|
{
|
|
|
|
|
p++;
|
2004-10-23 23:41:49 +00:00
|
|
|
break;
|
2004-09-29 07:19:44 +00:00
|
|
|
}
|
2004-10-23 23:41:49 +00:00
|
|
|
p++;
|
2004-09-29 07:19:44 +00:00
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-03 20:54:28 +00:00
|
|
|
/* Optimization: if we pass TAP_EMPTY, NoteData will do a search
|
|
|
|
|
* to remove anything in this position. We know that there's nothing
|
|
|
|
|
* there, so avoid the search. */
|
2005-02-07 21:31:18 +00:00
|
|
|
if( tn.type != TapNote::empty && ch != '3' )
|
2005-01-03 20:54:28 +00:00
|
|
|
out.SetTapNote( iTrack, iIndex, tn );
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
iTrack++;
|
|
|
|
|
}
|
2003-11-17 03:38:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-01-25 05:02:35 +00:00
|
|
|
|
2005-02-07 21:31:18 +00:00
|
|
|
/* Make sure we don't have any hold notes that didn't find a tail. */
|
|
|
|
|
for( int t=0; t<out.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
NoteData::iterator begin = out.begin( t );
|
|
|
|
|
NoteData::iterator end = out.end( t );
|
|
|
|
|
while( begin != end )
|
|
|
|
|
{
|
|
|
|
|
NoteData::iterator next = Increment( begin );
|
|
|
|
|
const TapNote &tn = begin->second;
|
|
|
|
|
if( tn.type == TapNote::hold_head && tn.iDuration == MAX_NOTE_ROW )
|
|
|
|
|
{
|
|
|
|
|
int iRow = begin->first;
|
|
|
|
|
LOG->Warn( "Unmatched 2 at beat %f", NoteRowToBeat(iRow) );
|
|
|
|
|
out.RemoveTapNote( t, begin );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
begin = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-11-17 03:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-12 08:16:02 +00:00
|
|
|
void NoteDataUtil::InsertHoldTails( NoteData &inout )
|
2005-02-07 22:03:32 +00:00
|
|
|
{
|
2005-02-12 08:16:02 +00:00
|
|
|
for( int t=0; t < inout.GetNumTracks(); t++ )
|
2005-02-07 22:03:32 +00:00
|
|
|
{
|
2005-02-12 08:16:02 +00:00
|
|
|
NoteData::iterator begin = inout.begin(t), end = inout.end(t);
|
2005-02-07 22:03:32 +00:00
|
|
|
|
2005-02-12 08:16:02 +00:00
|
|
|
for( ; begin != end; ++begin )
|
|
|
|
|
{
|
|
|
|
|
int iRow = begin->first;
|
|
|
|
|
const TapNote &tn = begin->second;
|
|
|
|
|
if( tn.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
2005-02-07 22:03:32 +00:00
|
|
|
|
2005-02-12 08:16:02 +00:00
|
|
|
TapNote tail = tn;
|
|
|
|
|
tail.type = TapNote::hold_tail;
|
2005-02-07 22:03:32 +00:00
|
|
|
|
2005-02-12 08:16:02 +00:00
|
|
|
/* If iDuration is 0, we'd end up overwriting the head with the tail
|
|
|
|
|
* (and invalidating our iterator). Empty hold notes aren't valid. */
|
|
|
|
|
ASSERT( tn.iDuration != 0 );
|
2005-02-12 06:57:52 +00:00
|
|
|
|
2005-02-12 08:16:02 +00:00
|
|
|
inout.SetTapNote( t, iRow + tn.iDuration, tail );
|
2005-02-07 22:03:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-02-12 08:16:02 +00:00
|
|
|
}
|
2005-02-07 22:03:32 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_out )
|
2003-11-17 03:38:24 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
//
|
|
|
|
|
// Get note data
|
|
|
|
|
//
|
2005-01-25 05:02:35 +00:00
|
|
|
NoteData in( in_ );
|
2005-02-07 22:03:32 +00:00
|
|
|
InsertHoldTails( in );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
float fLastBeat = in.GetLastBeat();
|
|
|
|
|
int iLastMeasure = int( fLastBeat/BEATS_PER_MEASURE );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
CString &sRet = notes_out;
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
sRet = "";
|
|
|
|
|
|
|
|
|
|
for( int m=0; m<=iLastMeasure; m++ ) // foreach measure
|
|
|
|
|
{
|
|
|
|
|
if( m )
|
|
|
|
|
sRet.append( 1, ',' );
|
2003-12-01 22:26:33 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
NoteType nt = GetSmallestNoteTypeForMeasure( in, m );
|
|
|
|
|
int iRowSpacing;
|
|
|
|
|
if( nt == NOTE_TYPE_INVALID )
|
|
|
|
|
iRowSpacing = 1;
|
|
|
|
|
else
|
|
|
|
|
iRowSpacing = int(roundf( NoteTypeToBeat(nt) * ROWS_PER_BEAT ));
|
2005-01-22 19:18:42 +00:00
|
|
|
// (verify first)
|
|
|
|
|
// iRowSpacing = BeatToNoteRow( NoteTypeToBeat(nt) );
|
2004-10-23 23:41:49 +00:00
|
|
|
sRet += ssprintf(" // measure %d\n", m+1);
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
const int iMeasureStartRow = m * ROWS_PER_MEASURE;
|
|
|
|
|
const int iMeasureLastRow = (m+1) * ROWS_PER_MEASURE - 1;
|
2003-11-17 03:38:24 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
for( int r=iMeasureStartRow; r<=iMeasureLastRow; r+=iRowSpacing )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2003-11-12 08:13:02 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t, r);
|
2004-10-23 23:41:49 +00:00
|
|
|
char c;
|
|
|
|
|
switch( tn.type )
|
2003-11-12 08:13:02 +00:00
|
|
|
{
|
2004-10-23 23:41:49 +00:00
|
|
|
case TapNote::empty: c = '0'; break;
|
|
|
|
|
case TapNote::tap: c = '1'; break;
|
|
|
|
|
case TapNote::hold_head: c = '2'; break;
|
|
|
|
|
case TapNote::hold_tail: c = '3'; break;
|
|
|
|
|
case TapNote::mine: c = 'M'; break;
|
|
|
|
|
case TapNote::attack: c = 'A'; break;
|
2004-10-24 10:45:30 +00:00
|
|
|
case TapNote::autoKeysound: c = 'K'; break;
|
2004-10-23 23:41:49 +00:00
|
|
|
default:
|
2004-10-25 02:17:58 +00:00
|
|
|
FAIL_M( ssprintf("tn %i", tn.type) ); // invalid enum value
|
2004-10-23 23:41:49 +00:00
|
|
|
}
|
|
|
|
|
sRet.append(1, c);
|
2004-09-29 07:19:44 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
if( tn.type == TapNote::attack )
|
|
|
|
|
{
|
|
|
|
|
sRet.append( ssprintf("{%s:%.2f}",tn.sAttackModifiers.c_str(), tn.fAttackDurationSeconds) );
|
|
|
|
|
}
|
|
|
|
|
if( tn.bKeysound )
|
|
|
|
|
{
|
|
|
|
|
sRet.append( ssprintf("[%d]",tn.iKeysoundIndex) );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-10-23 23:41:49 +00:00
|
|
|
|
|
|
|
|
sRet.append(1, '\n');
|
2003-11-17 03:38:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-07-17 05:10:11 +00:00
|
|
|
void NoteDataUtil::LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks )
|
|
|
|
|
{
|
|
|
|
|
// reset all notes
|
|
|
|
|
out.Init();
|
|
|
|
|
|
|
|
|
|
if( in.GetNumTracks() > iNewNumTracks )
|
|
|
|
|
{
|
|
|
|
|
/* Use a different algorithm for reducing tracks. */
|
|
|
|
|
LoadOverlapped( in, out, iNewNumTracks );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out.SetNumTracks( iNewNumTracks );
|
|
|
|
|
|
2005-03-25 09:36:08 +00:00
|
|
|
if( in.GetNumTracks() == 0 )
|
|
|
|
|
return; // nothing to do and don't AV below
|
2004-07-17 05:10:11 +00:00
|
|
|
|
|
|
|
|
int iCurTrackOffset = 0;
|
|
|
|
|
int iTrackOffsetMin = 0;
|
2005-01-25 05:02:35 +00:00
|
|
|
int iTrackOffsetMax = abs( iNewNumTracks - in.GetNumTracks() );
|
2004-07-17 05:10:11 +00:00
|
|
|
int bOffsetIncreasing = true;
|
|
|
|
|
|
2005-01-09 22:24:52 +00:00
|
|
|
int iLastMeasure = 0;
|
2005-01-23 18:21:30 +00:00
|
|
|
int iMeasuresSinceChange = 0;
|
2005-01-25 05:02:35 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, r )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
2005-01-23 18:21:30 +00:00
|
|
|
const int iMeasure = r / ROWS_PER_MEASURE;
|
|
|
|
|
if( iMeasure != iLastMeasure )
|
|
|
|
|
++iMeasuresSinceChange;
|
2004-07-17 05:10:11 +00:00
|
|
|
|
2005-01-23 18:21:30 +00:00
|
|
|
if( iMeasure != iLastMeasure && iMeasuresSinceChange >= 4 ) // adjust sliding window every 4 measures at most
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
|
|
|
|
// See if there is a hold crossing the beginning of this measure
|
|
|
|
|
bool bHoldCrossesThisMeasure = false;
|
|
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
if( in.IsHoldNoteAtBeat( t, r-1 ) &&
|
|
|
|
|
in.IsHoldNoteAtBeat( t, r ) )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
|
|
|
|
bHoldCrossesThisMeasure = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// adjust offset
|
|
|
|
|
if( !bHoldCrossesThisMeasure )
|
|
|
|
|
{
|
2005-01-23 18:21:30 +00:00
|
|
|
iMeasuresSinceChange = 0;
|
2004-07-17 05:10:11 +00:00
|
|
|
iCurTrackOffset += bOffsetIncreasing ? 1 : -1;
|
|
|
|
|
if( iCurTrackOffset == iTrackOffsetMin || iCurTrackOffset == iTrackOffsetMax )
|
|
|
|
|
bOffsetIncreasing ^= true;
|
|
|
|
|
CLAMP( iCurTrackOffset, iTrackOffsetMin, iTrackOffsetMax );
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-01-09 22:24:52 +00:00
|
|
|
|
|
|
|
|
iLastMeasure = iMeasure;
|
2005-01-23 18:21:30 +00:00
|
|
|
|
|
|
|
|
// copy notes in this measure
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
2005-01-23 18:21:30 +00:00
|
|
|
{
|
|
|
|
|
int iOldTrack = t;
|
|
|
|
|
int iNewTrack = (iOldTrack + iCurTrackOffset) % iNewNumTracks;
|
2005-01-25 05:02:35 +00:00
|
|
|
const TapNote &tn = in.GetTapNote( iOldTrack, r );
|
2005-01-23 18:21:30 +00:00
|
|
|
out.SetTapNote( iNewTrack, r, tn );
|
|
|
|
|
}
|
2004-07-17 05:10:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
void NoteDataUtil::LoadOverlapped( const NoteData &in, NoteData &out, int iNewNumTracks )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
|
|
|
|
out.SetNumTracks( iNewNumTracks );
|
|
|
|
|
|
|
|
|
|
/* Keep track of the last source track that put a tap into each destination track,
|
|
|
|
|
* and the row of that tap. Then, if two rows are trying to put taps into the
|
|
|
|
|
* same row within the shift threshold, shift the newcomer source row. */
|
|
|
|
|
int LastSourceTrack[MAX_NOTE_TRACKS];
|
|
|
|
|
int LastSourceRow[MAX_NOTE_TRACKS];
|
|
|
|
|
int DestRow[MAX_NOTE_TRACKS];
|
|
|
|
|
|
2004-10-05 10:52:36 +00:00
|
|
|
for( int tr = 0; tr < MAX_NOTE_TRACKS; ++tr )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
|
|
|
|
LastSourceTrack[tr] = -1;
|
2005-01-23 18:52:15 +00:00
|
|
|
LastSourceRow[tr] = -MAX_NOTE_ROW;
|
2004-07-17 05:10:11 +00:00
|
|
|
DestRow[tr] = tr;
|
|
|
|
|
wrap( DestRow[tr], iNewNumTracks );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int ShiftThreshold = BeatToNoteRow(1);
|
|
|
|
|
|
2005-01-23 18:52:15 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, row )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
2005-01-23 18:52:15 +00:00
|
|
|
for( int iTrackFrom = 0; iTrackFrom < in.GetNumTracks(); ++iTrackFrom )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
const TapNote &tnFrom = in.GetTapNote( iTrackFrom, row );
|
|
|
|
|
if( tnFrom.type == TapNote::empty )
|
2004-07-17 05:10:11 +00:00
|
|
|
continue;
|
2005-01-23 18:52:15 +00:00
|
|
|
|
|
|
|
|
/* If this is a hold note, find the end. */
|
|
|
|
|
int iEndIndex = row;
|
|
|
|
|
if( tnFrom.type == TapNote::hold_head )
|
2005-01-25 05:02:35 +00:00
|
|
|
iEndIndex = row + tnFrom.iDuration;
|
2004-07-17 05:10:11 +00:00
|
|
|
|
2005-01-23 18:52:15 +00:00
|
|
|
int &iTrackTo = DestRow[iTrackFrom];
|
|
|
|
|
if( LastSourceTrack[iTrackTo] != iTrackFrom )
|
2004-07-17 05:10:11 +00:00
|
|
|
{
|
2005-01-23 18:52:15 +00:00
|
|
|
if( iEndIndex - LastSourceRow[iTrackTo] < ShiftThreshold )
|
|
|
|
|
{
|
|
|
|
|
/* This destination track is in use by a different source track. Use the
|
|
|
|
|
* least-recently-used track. */
|
|
|
|
|
for( int DestTrack = 0; DestTrack < iNewNumTracks; ++DestTrack )
|
|
|
|
|
if( LastSourceRow[DestTrack] < LastSourceRow[iTrackTo] )
|
|
|
|
|
iTrackTo = DestTrack;
|
|
|
|
|
}
|
2004-07-17 05:10:11 +00:00
|
|
|
|
2005-01-23 18:52:15 +00:00
|
|
|
/* If it's still in use, then we just don't have an available track. */
|
|
|
|
|
if( iEndIndex - LastSourceRow[iTrackTo] < ShiftThreshold )
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2004-07-17 05:10:11 +00:00
|
|
|
|
|
|
|
|
LastSourceTrack[iTrackTo] = iTrackFrom;
|
2005-01-23 18:52:15 +00:00
|
|
|
LastSourceRow[iTrackTo] = iEndIndex;
|
2004-10-24 10:20:24 +00:00
|
|
|
out.SetTapNote( iTrackTo, row, tnFrom );
|
2005-01-23 18:52:15 +00:00
|
|
|
if( tnFrom.type == TapNote::hold_head )
|
|
|
|
|
{
|
|
|
|
|
TapNote tnTail = in.GetTapNote( iTrackFrom, iEndIndex );
|
|
|
|
|
out.SetTapNote( iTrackTo, iEndIndex, tnTail );
|
|
|
|
|
}
|
2004-07-17 05:10:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-23 17:58:32 +00:00
|
|
|
int FindLongestOverlappingHoldNoteForAnyTrack( const NoteData &in, int iRow )
|
|
|
|
|
{
|
|
|
|
|
int iMaxTailRow = -1;
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = in.GetTapNote( t, iRow );
|
|
|
|
|
if( tn.type == TapNote::hold_head )
|
|
|
|
|
iMaxTailRow = max( iMaxTailRow, iRow + tn.iDuration );
|
|
|
|
|
}
|
2005-01-23 17:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return iMaxTailRow;
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-20 19:05:37 +00:00
|
|
|
void NoteDataUtil::LoadTransformedLights( const NoteData &in, NoteData &out, int iNewNumTracks )
|
|
|
|
|
{
|
|
|
|
|
// reset all notes
|
|
|
|
|
out.Init();
|
|
|
|
|
|
|
|
|
|
out.SetNumTracks( iNewNumTracks );
|
|
|
|
|
|
2005-01-22 20:22:12 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, r )
|
2004-05-20 19:05:37 +00:00
|
|
|
{
|
2005-01-23 17:58:32 +00:00
|
|
|
/* If any row starts a hold note, find the end of the hold note, and keep searching
|
|
|
|
|
* until we've extended to the end of the latest overlapping hold note. */
|
|
|
|
|
int iHoldStart = r;
|
|
|
|
|
int iHoldEnd = -1;
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
int iMaxTailRow = FindLongestOverlappingHoldNoteForAnyTrack( in, r );
|
2005-01-23 17:58:32 +00:00
|
|
|
if( iMaxTailRow == -1 )
|
|
|
|
|
break;
|
|
|
|
|
iHoldEnd = iMaxTailRow;
|
|
|
|
|
r = iMaxTailRow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( iHoldEnd != -1 )
|
|
|
|
|
{
|
|
|
|
|
/* If we found a hold note, add it to all tracks. */
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
out.AddHoldNote( t, iHoldStart, iHoldEnd, TAP_ORIGINAL_HOLD_HEAD );
|
2004-05-20 19:05:37 +00:00
|
|
|
continue;
|
2005-01-23 17:58:32 +00:00
|
|
|
}
|
2004-05-20 19:05:37 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
if( in.IsRowEmpty( r ) )
|
2005-01-23 17:58:32 +00:00
|
|
|
continue;
|
2004-05-20 19:05:37 +00:00
|
|
|
|
2005-01-23 17:58:32 +00:00
|
|
|
/* Enable every track in the output. */
|
2004-05-20 19:05:37 +00:00
|
|
|
for( int t=0; t<out.GetNumTracks(); t++ )
|
2005-01-23 17:58:32 +00:00
|
|
|
out.SetTapNote( t, r, TAP_ORIGINAL_TAP );
|
2004-05-20 19:05:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-11 07:21:33 +00:00
|
|
|
void NoteDataUtil::GetRadarValues( const NoteData &in, float fSongSeconds, RadarValues& out )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-07-11 07:21:33 +00:00
|
|
|
// The for loop and the assert are used to ensure that all fields of
|
|
|
|
|
// RadarValue get set in here.
|
|
|
|
|
FOREACH_RadarCategory( rc )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-07-11 07:21:33 +00:00
|
|
|
switch( rc )
|
|
|
|
|
{
|
|
|
|
|
case RADAR_STREAM: out[rc] = GetStreamRadarValue( in, fSongSeconds ); break;
|
|
|
|
|
case RADAR_VOLTAGE: out[rc] = GetVoltageRadarValue( in, fSongSeconds ); break;
|
|
|
|
|
case RADAR_AIR: out[rc] = GetAirRadarValue( in, fSongSeconds ); break;
|
|
|
|
|
case RADAR_FREEZE: out[rc] = GetFreezeRadarValue( in, fSongSeconds ); break;
|
|
|
|
|
case RADAR_CHAOS: out[rc] = GetChaosRadarValue( in, fSongSeconds ); break;
|
|
|
|
|
case RADAR_NUM_TAPS_AND_HOLDS: out[rc] = (float) in.GetNumRowsWithTapOrHoldHead(); break;
|
2005-03-20 20:15:41 +00:00
|
|
|
case RADAR_NUM_JUMPS: out[rc] = (float) in.GetNumJumps(); break;
|
2004-07-11 07:21:33 +00:00
|
|
|
case RADAR_NUM_HOLDS: out[rc] = (float) in.GetNumHoldNotes(); break;
|
|
|
|
|
case RADAR_NUM_MINES: out[rc] = (float) in.GetNumMines(); break;
|
|
|
|
|
case RADAR_NUM_HANDS: out[rc] = (float) in.GetNumHands(); break;
|
|
|
|
|
default: ASSERT(0);
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NoteDataUtil::GetStreamRadarValue( const NoteData &in, float fSongSeconds )
|
|
|
|
|
{
|
|
|
|
|
if( !fSongSeconds )
|
|
|
|
|
return 0.0f;
|
|
|
|
|
// density of steps
|
2004-05-24 04:26:54 +00:00
|
|
|
int iNumNotes = in.GetNumTapNotes() + in.GetNumHoldNotes();
|
2003-08-07 06:36:34 +00:00
|
|
|
float fNotesPerSecond = iNumNotes/fSongSeconds;
|
|
|
|
|
float fReturn = fNotesPerSecond / 7;
|
|
|
|
|
return min( fReturn, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NoteDataUtil::GetVoltageRadarValue( const NoteData &in, float fSongSeconds )
|
|
|
|
|
{
|
|
|
|
|
if( !fSongSeconds )
|
|
|
|
|
return 0.0f;
|
|
|
|
|
|
|
|
|
|
const float fLastBeat = in.GetLastBeat();
|
|
|
|
|
const float fAvgBPS = fLastBeat / fSongSeconds;
|
|
|
|
|
|
|
|
|
|
// peak density of steps
|
|
|
|
|
float fMaxDensitySoFar = 0;
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
const float BEAT_WINDOW = 8;
|
|
|
|
|
const int BEAT_WINDOW_ROWS = BeatToNoteRow( BEAT_WINDOW );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
for( int i=0; i<=int(fLastBeat); i+=BEAT_WINDOW_ROWS )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
int iNumNotesThisWindow = in.GetNumTapNotes( i, i+BEAT_WINDOW_ROWS ) + in.GetNumHoldNotes( i, i+BEAT_WINDOW_ROWS );
|
|
|
|
|
float fDensityThisWindow = iNumNotesThisWindow / BEAT_WINDOW;
|
2003-08-07 06:36:34 +00:00
|
|
|
fMaxDensitySoFar = max( fMaxDensitySoFar, fDensityThisWindow );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float fReturn = fMaxDensitySoFar*fAvgBPS/10;
|
|
|
|
|
return min( fReturn, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NoteDataUtil::GetAirRadarValue( const NoteData &in, float fSongSeconds )
|
|
|
|
|
{
|
|
|
|
|
if( !fSongSeconds )
|
|
|
|
|
return 0.0f;
|
|
|
|
|
// number of doubles
|
2005-03-20 20:15:41 +00:00
|
|
|
int iNumDoubles = in.GetNumJumps();
|
2003-08-07 06:36:34 +00:00
|
|
|
float fReturn = iNumDoubles / fSongSeconds;
|
|
|
|
|
return min( fReturn, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NoteDataUtil::GetFreezeRadarValue( const NoteData &in, float fSongSeconds )
|
|
|
|
|
{
|
|
|
|
|
if( !fSongSeconds )
|
|
|
|
|
return 0.0f;
|
|
|
|
|
// number of hold steps
|
|
|
|
|
float fReturn = in.GetNumHoldNotes() / fSongSeconds;
|
|
|
|
|
return min( fReturn, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NoteDataUtil::GetChaosRadarValue( const NoteData &in, float fSongSeconds )
|
|
|
|
|
{
|
|
|
|
|
if( !fSongSeconds )
|
|
|
|
|
return 0.0f;
|
|
|
|
|
// count number of triplets or 16ths
|
|
|
|
|
int iNumChaosNotes = 0;
|
2004-09-29 06:43:57 +00:00
|
|
|
|
|
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( in, r )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-09-29 06:43:57 +00:00
|
|
|
if( GetNoteType(r) >= NOTE_TYPE_12TH )
|
2003-08-07 06:36:34 +00:00
|
|
|
iNumChaosNotes++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float fReturn = iNumChaosNotes / fSongSeconds * 0.5f;
|
|
|
|
|
return min( fReturn, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveHoldNotes( NoteData &in, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-05-24 04:17:19 +00:00
|
|
|
// turn all the HoldNotes into TapNotes
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<in.GetNumTracks(); ++t )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
NoteData::iterator begin, end;
|
|
|
|
|
in.GetTapNoteRangeInclusive( t, iStartIndex, iEndIndex, begin, end );
|
|
|
|
|
for( ; begin != end; ++begin )
|
|
|
|
|
{
|
|
|
|
|
if( begin->second.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
|
|
|
|
begin->second.type = TapNote::tap;
|
|
|
|
|
}
|
2003-11-07 08:45:21 +00:00
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveSimultaneousNotes( NoteData &in, int iMaxSimultaneous, int iStartIndex, int iEndIndex )
|
2004-01-02 11:25:21 +00:00
|
|
|
{
|
2004-05-24 04:17:19 +00:00
|
|
|
// turn all the HoldNotes into TapNotes
|
2004-09-29 06:43:57 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( in, r, iStartIndex, iEndIndex )
|
2004-01-02 11:25:21 +00:00
|
|
|
{
|
2004-05-15 03:00:43 +00:00
|
|
|
set<int> viTracksHeld;
|
2004-01-02 11:25:21 +00:00
|
|
|
in.GetTracksHeldAtRow( r, viTracksHeld );
|
|
|
|
|
|
|
|
|
|
// remove the first tap note or the first hold note that starts on this row
|
|
|
|
|
int iTotalTracksPressed = in.GetNumTracksWithTap(r) + viTracksHeld.size();
|
2004-01-11 06:34:30 +00:00
|
|
|
int iTracksToRemove = max( 0, iTotalTracksPressed - iMaxSimultaneous );
|
2004-01-02 11:25:21 +00:00
|
|
|
for( int t=0; iTracksToRemove>0 && t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
const TapNote &tn = in.GetTapNote(t,r);
|
|
|
|
|
if( tn.type == TapNote::tap )
|
2004-01-02 11:25:21 +00:00
|
|
|
{
|
|
|
|
|
in.SetTapNote( t, r, TAP_EMPTY );
|
|
|
|
|
iTracksToRemove--;
|
|
|
|
|
}
|
2005-01-25 05:02:35 +00:00
|
|
|
else if( tn.type == TapNote::hold_head )
|
2004-01-02 11:25:21 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
int iStartTrack;
|
|
|
|
|
if( !in.IsHoldNoteAtBeat( t, r, &iStartTrack ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
in.SetTapNote( t, iStartTrack, TAP_EMPTY );
|
2004-01-02 11:25:21 +00:00
|
|
|
iTracksToRemove--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveJumps( NoteData &inout, int iStartIndex, int iEndIndex )
|
2004-01-11 06:34:30 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
RemoveSimultaneousNotes( inout, 1, iStartIndex, iEndIndex );
|
2004-01-11 06:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveHands( NoteData &inout, int iStartIndex, int iEndIndex )
|
2004-01-11 06:34:30 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
RemoveSimultaneousNotes( inout, 2, iStartIndex, iEndIndex );
|
2004-01-11 06:34:30 +00:00
|
|
|
}
|
2004-01-02 11:25:21 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveQuads( NoteData &inout, int iStartIndex, int iEndIndex )
|
2004-01-12 03:47:55 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
RemoveSimultaneousNotes( inout, 3, iStartIndex, iEndIndex );
|
2004-01-12 03:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::RemoveMines( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-11-07 07:25:52 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, t, r, iStartIndex, iEndIndex )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t,r).type == TapNote::mine )
|
|
|
|
|
inout.SetTapNote( t, r, TAP_EMPTY );
|
2003-11-07 07:25:52 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-25 03:36:42 +00:00
|
|
|
void NoteDataUtil::RemoveAllButOneTap( NoteData &inout, int row )
|
|
|
|
|
{
|
|
|
|
|
if(row < 0) return;
|
|
|
|
|
|
|
|
|
|
int track;
|
|
|
|
|
for( track = 0; track < inout.GetNumTracks(); ++track )
|
|
|
|
|
{
|
|
|
|
|
if( inout.GetTapNote(track, row).type == TapNote::tap )
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track++;
|
|
|
|
|
|
|
|
|
|
for( ; track < inout.GetNumTracks(); ++track )
|
|
|
|
|
{
|
|
|
|
|
if( inout.GetTapNote(track, row).type == TapNote::tap )
|
|
|
|
|
inout.SetTapNote(track, row, TAP_EMPTY );
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-11-07 07:25:52 +00:00
|
|
|
|
2004-01-02 21:02:06 +00:00
|
|
|
static void GetTrackMapping( StepsType st, NoteDataUtil::TrackMapping tt, int NumTracks, int *iTakeFromTrack )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-01-02 21:02:06 +00:00
|
|
|
// Identity transform for cases not handled below.
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t = 0; t < MAX_NOTE_TRACKS; ++t )
|
2004-01-02 21:02:06 +00:00
|
|
|
iTakeFromTrack[t] = t;
|
|
|
|
|
|
2003-08-07 06:36:34 +00:00
|
|
|
switch( tt )
|
|
|
|
|
{
|
2003-12-16 04:35:43 +00:00
|
|
|
case NoteDataUtil::left:
|
|
|
|
|
case NoteDataUtil::right:
|
2003-08-07 06:36:34 +00:00
|
|
|
// Is there a way to do this withoutn handling each StepsType? -Chris
|
|
|
|
|
switch( st )
|
|
|
|
|
{
|
|
|
|
|
case STEPS_TYPE_DANCE_SINGLE:
|
|
|
|
|
case STEPS_TYPE_DANCE_DOUBLE:
|
|
|
|
|
case STEPS_TYPE_DANCE_COUPLE:
|
|
|
|
|
iTakeFromTrack[0] = 2;
|
|
|
|
|
iTakeFromTrack[1] = 0;
|
|
|
|
|
iTakeFromTrack[2] = 3;
|
|
|
|
|
iTakeFromTrack[3] = 1;
|
|
|
|
|
iTakeFromTrack[4] = 6;
|
|
|
|
|
iTakeFromTrack[5] = 4;
|
|
|
|
|
iTakeFromTrack[6] = 7;
|
|
|
|
|
iTakeFromTrack[7] = 5;
|
|
|
|
|
break;
|
|
|
|
|
case STEPS_TYPE_DANCE_SOLO:
|
|
|
|
|
iTakeFromTrack[0] = 5;
|
|
|
|
|
iTakeFromTrack[1] = 4;
|
|
|
|
|
iTakeFromTrack[2] = 0;
|
|
|
|
|
iTakeFromTrack[3] = 3;
|
|
|
|
|
iTakeFromTrack[4] = 1;
|
|
|
|
|
iTakeFromTrack[5] = 2;
|
|
|
|
|
break;
|
|
|
|
|
case STEPS_TYPE_PUMP_SINGLE:
|
|
|
|
|
case STEPS_TYPE_PUMP_COUPLE:
|
|
|
|
|
iTakeFromTrack[0] = 3;
|
|
|
|
|
iTakeFromTrack[1] = 4;
|
|
|
|
|
iTakeFromTrack[2] = 2;
|
|
|
|
|
iTakeFromTrack[3] = 0;
|
|
|
|
|
iTakeFromTrack[4] = 1;
|
|
|
|
|
iTakeFromTrack[5] = 8;
|
|
|
|
|
iTakeFromTrack[6] = 9;
|
|
|
|
|
iTakeFromTrack[7] = 7;
|
|
|
|
|
iTakeFromTrack[8] = 5;
|
|
|
|
|
iTakeFromTrack[9] = 6;
|
|
|
|
|
break;
|
|
|
|
|
case STEPS_TYPE_PUMP_HALFDOUBLE:
|
|
|
|
|
iTakeFromTrack[0] = 2;
|
|
|
|
|
iTakeFromTrack[1] = 0;
|
|
|
|
|
iTakeFromTrack[2] = 1;
|
|
|
|
|
iTakeFromTrack[3] = 3;
|
|
|
|
|
iTakeFromTrack[4] = 4;
|
|
|
|
|
iTakeFromTrack[5] = 5;
|
|
|
|
|
break;
|
|
|
|
|
case STEPS_TYPE_PUMP_DOUBLE:
|
|
|
|
|
iTakeFromTrack[0] = 8;
|
|
|
|
|
iTakeFromTrack[1] = 9;
|
|
|
|
|
iTakeFromTrack[2] = 7;
|
|
|
|
|
iTakeFromTrack[3] = 5;
|
|
|
|
|
iTakeFromTrack[4] = 6;
|
|
|
|
|
iTakeFromTrack[5] = 3;
|
|
|
|
|
iTakeFromTrack[6] = 4;
|
|
|
|
|
iTakeFromTrack[7] = 2;
|
|
|
|
|
iTakeFromTrack[8] = 0;
|
|
|
|
|
iTakeFromTrack[9] = 1;
|
|
|
|
|
break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-16 04:35:43 +00:00
|
|
|
if( tt == NoteDataUtil::right )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
/* Invert. */
|
|
|
|
|
int iTrack[MAX_NOTE_TRACKS];
|
|
|
|
|
memcpy( iTrack, iTakeFromTrack, sizeof(iTrack) );
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t = 0; t < MAX_NOTE_TRACKS; ++t )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
const int to = iTrack[t];
|
|
|
|
|
iTakeFromTrack[to] = t;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2003-12-16 04:35:43 +00:00
|
|
|
case NoteDataUtil::mirror:
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<NumTracks; t++ )
|
2003-12-16 04:35:43 +00:00
|
|
|
iTakeFromTrack[t] = NumTracks-t-1;
|
2003-08-07 06:36:34 +00:00
|
|
|
break;
|
2003-12-16 04:35:43 +00:00
|
|
|
case NoteDataUtil::shuffle:
|
|
|
|
|
case NoteDataUtil::super_shuffle: // use shuffle code to mix up HoldNotes without creating impossible patterns
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-02-29 09:55:03 +00:00
|
|
|
// TRICKY: Shuffle so that both player get the same shuffle mapping
|
|
|
|
|
// in the same round.
|
2004-07-01 18:32:19 +00:00
|
|
|
int iOrig[MAX_NOTE_TRACKS];
|
|
|
|
|
memcpy( iOrig, iTakeFromTrack, sizeof(iOrig) );
|
2004-02-29 09:55:03 +00:00
|
|
|
|
2005-03-11 18:09:34 +00:00
|
|
|
int iShuffleSeed = GAMESTATE->m_iStageSeed;
|
2004-07-01 18:32:19 +00:00
|
|
|
do {
|
|
|
|
|
RandomGen rnd( iShuffleSeed );
|
|
|
|
|
random_shuffle( &iTakeFromTrack[0], &iTakeFromTrack[NumTracks], rnd );
|
2004-02-29 09:55:03 +00:00
|
|
|
iShuffleSeed++;
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2004-07-01 18:32:19 +00:00
|
|
|
while ( !memcmp( iOrig, iTakeFromTrack, sizeof(iOrig) ) );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2004-01-02 21:02:06 +00:00
|
|
|
case NoteDataUtil::stomp:
|
|
|
|
|
switch( st )
|
|
|
|
|
{
|
|
|
|
|
case STEPS_TYPE_DANCE_SINGLE:
|
|
|
|
|
case STEPS_TYPE_DANCE_COUPLE:
|
|
|
|
|
iTakeFromTrack[0] = 3;
|
|
|
|
|
iTakeFromTrack[1] = 2;
|
|
|
|
|
iTakeFromTrack[2] = 1;
|
|
|
|
|
iTakeFromTrack[3] = 0;
|
|
|
|
|
iTakeFromTrack[4] = 7;
|
|
|
|
|
iTakeFromTrack[5] = 6;
|
|
|
|
|
iTakeFromTrack[6] = 5;
|
|
|
|
|
iTakeFromTrack[7] = 4;
|
|
|
|
|
break;
|
2004-05-08 00:08:29 +00:00
|
|
|
case STEPS_TYPE_DANCE_DOUBLE:
|
|
|
|
|
iTakeFromTrack[0] = 1;
|
|
|
|
|
iTakeFromTrack[1] = 0;
|
|
|
|
|
iTakeFromTrack[2] = 3;
|
|
|
|
|
iTakeFromTrack[3] = 2;
|
|
|
|
|
iTakeFromTrack[4] = 5;
|
|
|
|
|
iTakeFromTrack[5] = 4;
|
|
|
|
|
iTakeFromTrack[6] = 7;
|
|
|
|
|
iTakeFromTrack[7] = 6;
|
|
|
|
|
break;
|
2004-01-02 21:02:06 +00:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2003-08-07 06:36:34 +00:00
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
/*
|
|
|
|
|
* We already did the normal shuffling code above, which did a good job
|
2003-12-16 04:35:43 +00:00
|
|
|
* of shuffling HoldNotes without creating impossible patterns.
|
2004-05-24 04:17:19 +00:00
|
|
|
* Now, go in and shuffle the TapNotes per-row.
|
2003-12-16 04:35:43 +00:00
|
|
|
*
|
2005-01-25 05:02:35 +00:00
|
|
|
* This is only called by NoteDataUtil::Turn.
|
|
|
|
|
*/
|
2004-10-24 10:20:24 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t1=0; t1<inout.GetNumTracks(); t1++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-10 06:23:04 +00:00
|
|
|
const TapNote tn1 = inout.GetTapNote(t1, r);
|
2005-01-23 05:16:09 +00:00
|
|
|
switch( tn1.type )
|
|
|
|
|
{
|
|
|
|
|
case TapNote::empty:
|
|
|
|
|
case TapNote::hold_head:
|
2005-03-11 03:15:54 +00:00
|
|
|
case TapNote::hold_tail:
|
|
|
|
|
case TapNote::autoKeysound:
|
|
|
|
|
continue; // skip
|
|
|
|
|
case TapNote::tap:
|
|
|
|
|
case TapNote::mine:
|
|
|
|
|
case TapNote::attack:
|
|
|
|
|
break; // shuffle this
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
2005-01-23 05:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
2005-03-11 03:15:54 +00:00
|
|
|
#if _DEBUG
|
|
|
|
|
ASSERT_M( !inout.IsHoldNoteAtBeat(t1,r), ssprintf("There is a tap.type = %d inside of a hold at row %d", tn1.type, r) );
|
|
|
|
|
#endif
|
2003-12-16 04:35:43 +00:00
|
|
|
|
2005-03-11 03:15:54 +00:00
|
|
|
// Probe for a spot to swap with.
|
|
|
|
|
set<int> vTriedTracks;
|
|
|
|
|
for( int i=0; i<4; i++ ) // probe max 4 times
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-03-11 03:15:54 +00:00
|
|
|
int t2 = rand() % inout.GetNumTracks();
|
|
|
|
|
if( vTriedTracks.find(t2) != vTriedTracks.end() ) // already tried this track
|
|
|
|
|
continue; // skip
|
|
|
|
|
vTriedTracks.insert( t2 );
|
|
|
|
|
|
|
|
|
|
// swapping with ourself is a no-op
|
|
|
|
|
if( t1 == t2 )
|
|
|
|
|
break; // done swapping
|
|
|
|
|
|
2005-01-10 06:23:04 +00:00
|
|
|
const TapNote tn2 = inout.GetTapNote(t2, r);
|
2005-03-11 03:15:54 +00:00
|
|
|
switch( tn2.type )
|
|
|
|
|
{
|
|
|
|
|
case TapNote::hold_head:
|
|
|
|
|
case TapNote::hold_tail:
|
|
|
|
|
case TapNote::autoKeysound:
|
|
|
|
|
continue; // don't swap with these
|
|
|
|
|
case TapNote::empty:
|
|
|
|
|
case TapNote::tap:
|
|
|
|
|
case TapNote::mine:
|
|
|
|
|
case TapNote::attack:
|
|
|
|
|
break; // ok to swap with this
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// don't swap into the middle of a hold note
|
2005-01-23 05:16:09 +00:00
|
|
|
if( inout.IsHoldNoteAtBeat(t2,r) )
|
2003-12-16 04:35:43 +00:00
|
|
|
continue;
|
|
|
|
|
|
2005-03-11 03:15:54 +00:00
|
|
|
// do the swap
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote(t1, r, tn2);
|
|
|
|
|
inout.SetTapNote(t2, r, tn1);
|
2005-03-11 03:15:54 +00:00
|
|
|
|
|
|
|
|
break; // done swapping
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-12-16 04:35:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Turn( NoteData &inout, StepsType st, TrackMapping tt, int iStartIndex, int iEndIndex )
|
2003-12-16 04:35:43 +00:00
|
|
|
{
|
|
|
|
|
int iTakeFromTrack[MAX_NOTE_TRACKS]; // New track "t" will take from old track iTakeFromTrack[t]
|
2004-10-24 10:20:24 +00:00
|
|
|
GetTrackMapping( st, tt, inout.GetNumTracks(), iTakeFromTrack );
|
2003-12-16 04:35:43 +00:00
|
|
|
|
|
|
|
|
NoteData tempNoteData;
|
2005-01-10 06:23:04 +00:00
|
|
|
tempNoteData.LoadTransformed( inout, inout.GetNumTracks(), iTakeFromTrack );
|
2003-12-16 04:35:43 +00:00
|
|
|
|
|
|
|
|
if( tt == super_shuffle )
|
2005-01-25 05:02:35 +00:00
|
|
|
SuperShuffleTaps( tempNoteData, iStartIndex, iEndIndex );
|
2003-12-16 04:35:43 +00:00
|
|
|
|
2005-01-10 06:23:04 +00:00
|
|
|
inout.CopyAll( tempNoteData );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::Backwards( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 05:56:25 +00:00
|
|
|
NoteData out;
|
|
|
|
|
out.SetNumTracks( inout.GetNumTracks() );
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
int max_row = inout.GetLastRow();
|
|
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 05:56:25 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, t, r, 0, max_row )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-09-29 06:43:57 +00:00
|
|
|
int iRowEarlier = r;
|
|
|
|
|
int iRowLater = max_row-r;
|
|
|
|
|
|
2005-01-23 05:56:25 +00:00
|
|
|
TapNote tnEarlier = inout.GetTapNote(t, iRowEarlier);
|
2005-01-25 05:02:35 +00:00
|
|
|
if( tnEarlier.type == TapNote::hold_head )
|
|
|
|
|
iRowLater -= tnEarlier.iDuration;
|
2005-01-23 05:56:25 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
out.SetTapNote(t, iRowLater, tnEarlier);
|
2005-01-23 05:56:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inout = out;
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::SwapSides( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 05:20:55 +00:00
|
|
|
int iOriginalTrackToTakeFrom[MAX_NOTE_TRACKS];
|
|
|
|
|
for( int t = 0; t < inout.GetNumTracks()/2; ++t )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 05:20:55 +00:00
|
|
|
int iTrackEarlier = t;
|
|
|
|
|
int iTrackLater = t + inout.GetNumTracks()/2 + inout.GetNumTracks()%2;
|
|
|
|
|
iOriginalTrackToTakeFrom[iTrackEarlier] = iTrackLater;
|
|
|
|
|
iOriginalTrackToTakeFrom[iTrackLater] = iTrackEarlier;
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2005-01-23 05:20:55 +00:00
|
|
|
|
|
|
|
|
NoteData orig( inout );
|
|
|
|
|
inout.LoadTransformed( orig, orig.GetNumTracks(), iOriginalTrackToTakeFrom );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Little( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
// filter out all non-quarter notes
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2005-01-25 05:02:35 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, t, i, iStartIndex, iEndIndex )
|
2005-01-25 05:02:35 +00:00
|
|
|
{
|
|
|
|
|
if( i % ROWS_PER_BEAT == 0 )
|
|
|
|
|
continue;
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
inout.SetTapNote( t, i, TAP_EMPTY );
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// Make all all quarter notes into jumps.
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Wide( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2003-10-24 22:48:21 +00:00
|
|
|
/* Start on an even beat. */
|
2005-01-22 17:34:10 +00:00
|
|
|
iStartIndex = Quantize( iStartIndex, BeatToNoteRow(2.0f) );
|
2003-08-18 04:20:51 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, i, iStartIndex, iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
if( i % BeatToNoteRow(2.0f) != 0 )
|
|
|
|
|
continue; // even beats only
|
|
|
|
|
|
2005-01-23 05:39:07 +00:00
|
|
|
bool bHoldNoteAtBeat = false;
|
|
|
|
|
for( int t = 0; !bHoldNoteAtBeat && t < inout.GetNumTracks(); ++t )
|
|
|
|
|
if( inout.IsHoldNoteAtBeat(t, i) )
|
|
|
|
|
bHoldNoteAtBeat = true;
|
|
|
|
|
if( bHoldNoteAtBeat )
|
2003-08-07 06:36:34 +00:00
|
|
|
continue; // skip. Don't place during holds
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetNumTracksWithTap(i) != 1 )
|
2003-08-07 06:36:34 +00:00
|
|
|
continue; // skip
|
|
|
|
|
|
|
|
|
|
bool bSpaceAroundIsEmpty = true; // no other notes with a 1/8th of this row
|
2005-01-09 22:24:52 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, j, i-ROWS_PER_BEAT/2+1, i+ROWS_PER_BEAT/2 )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( j!=i && inout.GetNumTapNonEmptyTracks(j) > 0 )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
bSpaceAroundIsEmpty = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !bSpaceAroundIsEmpty )
|
|
|
|
|
continue; // skip
|
|
|
|
|
|
|
|
|
|
// add a note determinitsitcally
|
|
|
|
|
int iBeat = (int)roundf( NoteRowToBeat(i) );
|
2004-10-24 10:20:24 +00:00
|
|
|
int iTrackOfNote = inout.GetFirstTrackWithTap(i);
|
2003-08-07 06:36:34 +00:00
|
|
|
int iTrackToAdd = iTrackOfNote + (iBeat%5)-2; // won't be more than 2 tracks away from the existing note
|
2004-10-24 10:20:24 +00:00
|
|
|
CLAMP( iTrackToAdd, 0, inout.GetNumTracks()-1 );
|
2003-08-07 06:36:34 +00:00
|
|
|
if( iTrackToAdd == iTrackOfNote )
|
|
|
|
|
iTrackToAdd++;
|
2004-10-24 10:20:24 +00:00
|
|
|
CLAMP( iTrackToAdd, 0, inout.GetNumTracks()-1 );
|
2003-08-07 06:36:34 +00:00
|
|
|
if( iTrackToAdd == iTrackOfNote )
|
|
|
|
|
iTrackToAdd--;
|
2004-10-24 10:20:24 +00:00
|
|
|
CLAMP( iTrackToAdd, 0, inout.GetNumTracks()-1 );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(iTrackToAdd, i).type != TapNote::empty )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
iTrackToAdd = (iTrackToAdd+1) % inout.GetNumTracks();
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote(iTrackToAdd, i, TAP_ADDITION_TAP);
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Big( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
// add 8ths between 4ths
|
2005-01-22 17:51:46 +00:00
|
|
|
InsertIntelligentTaps( inout,BeatToNoteRow(1.0f), BeatToNoteRow(0.5f), BeatToNoteRow(1.0f), false,iStartIndex,iEndIndex );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Quick( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
// add 16ths between 8ths
|
2005-01-22 17:51:46 +00:00
|
|
|
InsertIntelligentTaps( inout, BeatToNoteRow(0.5f), BeatToNoteRow(0.25f), BeatToNoteRow(1.0f), false,iStartIndex,iEndIndex );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-12 06:54:54 +00:00
|
|
|
// Due to popular request by people annoyed with the "new" implementation of Quick, we now have
|
|
|
|
|
// this BMR-izer for your steps. Use with caution.
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::BMRize( NoteData &inout, int iStartIndex, int iEndIndex )
|
2004-02-12 06:54:54 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
Big( inout, iStartIndex, iEndIndex );
|
|
|
|
|
Quick( inout, iStartIndex, iEndIndex );
|
2004-02-12 06:54:54 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Skippy( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
// add 16ths between 4ths
|
2005-01-22 17:51:46 +00:00
|
|
|
InsertIntelligentTaps( inout, BeatToNoteRow(1.0f), BeatToNoteRow(0.75f),BeatToNoteRow(1.0f), true,iStartIndex,iEndIndex );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-03-01 08:59:22 +00:00
|
|
|
void NoteDataUtil::InsertIntelligentTaps(
|
2004-10-24 10:20:24 +00:00
|
|
|
NoteData &inout,
|
2005-01-22 17:51:46 +00:00
|
|
|
int iWindowSizeRows,
|
|
|
|
|
int iInsertOffsetRows,
|
|
|
|
|
int iWindowStrideRows,
|
2004-03-01 08:59:22 +00:00
|
|
|
bool bSkippy,
|
2005-01-22 17:34:10 +00:00
|
|
|
int iStartIndex,
|
|
|
|
|
int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:51:46 +00:00
|
|
|
ASSERT( iInsertOffsetRows <= iWindowSizeRows );
|
|
|
|
|
ASSERT( iWindowSizeRows <= iWindowStrideRows );
|
2005-01-22 17:34:10 +00:00
|
|
|
|
2004-03-01 08:59:22 +00:00
|
|
|
bool bRequireNoteAtBeginningOfWindow = !bSkippy;
|
|
|
|
|
bool bRequireNoteAtEndOfWindow = true;
|
|
|
|
|
|
2003-10-27 04:23:08 +00:00
|
|
|
/* Start on a multiple of fBeatInterval. */
|
2005-01-22 17:51:46 +00:00
|
|
|
iStartIndex = Quantize( iStartIndex, iWindowStrideRows );
|
2003-10-24 22:48:21 +00:00
|
|
|
|
2005-02-01 01:02:55 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, i, iStartIndex, iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-02-01 01:02:55 +00:00
|
|
|
// Insert a beat in the middle of every fBeatInterval.
|
|
|
|
|
if( i % iWindowStrideRows != 0 )
|
|
|
|
|
continue; // even beats only
|
|
|
|
|
|
2003-08-07 06:36:34 +00:00
|
|
|
int iRowEarlier = i;
|
2005-01-22 17:51:46 +00:00
|
|
|
int iRowLater = i + iWindowSizeRows;
|
|
|
|
|
int iRowToAdd = i + iInsertOffsetRows;
|
2004-02-12 06:54:54 +00:00
|
|
|
// following two lines have been changed because the behavior of treating hold-heads
|
|
|
|
|
// as different from taps doesn't feel right, and because we need to check
|
|
|
|
|
// against TAP_ADDITION with the BMRize mod.
|
2004-03-01 08:59:22 +00:00
|
|
|
if( bRequireNoteAtBeginningOfWindow )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetNumTapNonEmptyTracks(iRowEarlier)!=1 || inout.GetNumTracksWithTapOrHoldHead(iRowEarlier)!=1 )
|
2004-03-01 08:59:22 +00:00
|
|
|
continue;
|
|
|
|
|
if( bRequireNoteAtEndOfWindow )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetNumTapNonEmptyTracks(iRowLater)!=1 || inout.GetNumTracksWithTapOrHoldHead(iRowLater)!=1 )
|
2004-03-01 08:59:22 +00:00
|
|
|
continue;
|
2003-08-07 06:36:34 +00:00
|
|
|
// there is a 4th and 8th note surrounding iRowBetween
|
|
|
|
|
|
|
|
|
|
// don't insert a new note if there's already one within this interval
|
|
|
|
|
bool bNoteInMiddle = false;
|
2005-01-23 03:49:12 +00:00
|
|
|
for( int t = 0; t < inout.GetNumTracks(); ++t )
|
|
|
|
|
if( inout.IsHoldNoteAtBeat(t, iRowEarlier+1) )
|
|
|
|
|
bNoteInMiddle = true;
|
2005-01-09 22:24:52 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, j, iRowEarlier+1, iRowLater-1 )
|
|
|
|
|
bNoteInMiddle = true;
|
2003-08-07 06:36:34 +00:00
|
|
|
if( bNoteInMiddle )
|
|
|
|
|
continue;
|
|
|
|
|
|
2005-01-09 22:24:52 +00:00
|
|
|
// add a note deterministically somewhere on a track different from the two surrounding notes
|
2004-10-24 10:20:24 +00:00
|
|
|
int iTrackOfNoteEarlier = -1;
|
|
|
|
|
bool bEarlierHasNonEmptyTrack = inout.GetTapFirstNonEmptyTrack( iRowEarlier, iTrackOfNoteEarlier );
|
|
|
|
|
int iTrackOfNoteLater = -1;
|
2005-01-22 17:21:40 +00:00
|
|
|
inout.GetTapFirstNonEmptyTrack( iRowLater, iTrackOfNoteLater );
|
2003-08-07 06:36:34 +00:00
|
|
|
int iTrackOfNoteToAdd = 0;
|
2003-08-18 02:37:43 +00:00
|
|
|
if( bSkippy &&
|
2003-08-07 06:36:34 +00:00
|
|
|
iTrackOfNoteEarlier != iTrackOfNoteLater ) // Don't make skips on the same note
|
|
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
if( bEarlierHasNonEmptyTrack )
|
2004-03-01 08:59:22 +00:00
|
|
|
{
|
|
|
|
|
iTrackOfNoteToAdd = iTrackOfNoteEarlier;
|
|
|
|
|
goto done_looking_for_track_to_add;
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2004-01-02 22:19:27 +00:00
|
|
|
|
|
|
|
|
// try to choose a track between the earlier and later notes
|
|
|
|
|
if( abs(iTrackOfNoteEarlier-iTrackOfNoteLater) >= 2 )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-01-02 22:19:27 +00:00
|
|
|
iTrackOfNoteToAdd = min(iTrackOfNoteEarlier,iTrackOfNoteLater)+1;
|
|
|
|
|
goto done_looking_for_track_to_add;
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2004-01-02 22:19:27 +00:00
|
|
|
|
2004-03-01 08:59:22 +00:00
|
|
|
// try to choose a track just to the left
|
2004-01-02 22:19:27 +00:00
|
|
|
if( min(iTrackOfNoteEarlier,iTrackOfNoteLater)-1 >= 0 )
|
|
|
|
|
{
|
|
|
|
|
iTrackOfNoteToAdd = min(iTrackOfNoteEarlier,iTrackOfNoteLater)-1;
|
|
|
|
|
goto done_looking_for_track_to_add;
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-01 08:59:22 +00:00
|
|
|
// try to choose a track just to the right
|
2004-10-24 10:20:24 +00:00
|
|
|
if( max(iTrackOfNoteEarlier,iTrackOfNoteLater)+1 < inout.GetNumTracks() )
|
2004-01-02 22:19:27 +00:00
|
|
|
{
|
|
|
|
|
iTrackOfNoteToAdd = max(iTrackOfNoteEarlier,iTrackOfNoteLater)+1;
|
|
|
|
|
goto done_looking_for_track_to_add;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done_looking_for_track_to_add:
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote(iTrackOfNoteToAdd, iRowToAdd, TAP_ADDITION_TAP);
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
class TrackIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TrackIterator();
|
|
|
|
|
|
|
|
|
|
/* If called, iterate only over [iStart,iEnd]. */
|
|
|
|
|
void SetRange( int iStart, int iEnd )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If called, pay attention to iTrack only. */
|
|
|
|
|
void SetTrack( iTrack );
|
|
|
|
|
|
|
|
|
|
/* Extend iStart and iEnd to include hold notes overlapping the boundaries. Call SetRange()
|
|
|
|
|
* and SetTrack() first. */
|
|
|
|
|
void HoldInclusive();
|
|
|
|
|
|
|
|
|
|
/* Reduce iStart and iEnd to exclude hold notes overlapping the boundaries. Call SetRange()
|
|
|
|
|
* and SetTrack() first. */
|
|
|
|
|
void HoldExclusive();
|
|
|
|
|
|
|
|
|
|
/* If called, keep the iterator around. This results in much faster iteration. If used,
|
|
|
|
|
* ensure that the current row will always remain valid. SetTrack() must be called first. */
|
|
|
|
|
void Fast();
|
|
|
|
|
|
|
|
|
|
/* Retrieve an iterator for the current row. SetTrack() must be called first (but Fast()
|
|
|
|
|
* does not). */
|
|
|
|
|
TapNote::iterator Get();
|
|
|
|
|
|
|
|
|
|
int GetRow() const { return m_iCurrentRow; }
|
|
|
|
|
bool Prev();
|
|
|
|
|
bool Next();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
int m_iStart, m_iEnd;
|
|
|
|
|
int m_iTrack;
|
|
|
|
|
|
|
|
|
|
bool m_bFast;
|
|
|
|
|
|
|
|
|
|
int m_iCurrentRow;
|
|
|
|
|
|
|
|
|
|
NoteData::iterator m_Iterator;
|
|
|
|
|
|
|
|
|
|
/* m_bFast only: */
|
|
|
|
|
NoteData::iterator m_Begin, m_End;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool TrackIterator::Next()
|
|
|
|
|
{
|
|
|
|
|
if( m_bFast )
|
|
|
|
|
{
|
|
|
|
|
if( m_Iterator ==
|
|
|
|
|
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
TrackIterator::TrackIterator()
|
|
|
|
|
{
|
|
|
|
|
m_iStart = 0;
|
|
|
|
|
m_iEnd = MAX_NOTE_ROW;
|
|
|
|
|
m_iTrack = -1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::AddMines( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-08-18 02:37:43 +00:00
|
|
|
{
|
2003-08-23 19:01:35 +00:00
|
|
|
//
|
|
|
|
|
// Change whole rows at a time to be tap notes. Otherwise, it causes
|
|
|
|
|
// major problems for our scoring system. -Chris
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
int iRowCount = 0;
|
2004-01-02 04:08:25 +00:00
|
|
|
int iPlaceEveryRows = 6;
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
|
2003-08-31 22:38:06 +00:00
|
|
|
{
|
2005-01-09 22:24:52 +00:00
|
|
|
iRowCount++;
|
2003-08-31 22:38:06 +00:00
|
|
|
|
2005-01-09 22:24:52 +00:00
|
|
|
// place every 6 or 7 rows
|
|
|
|
|
// XXX: What is "6 or 7" derived from? Can we calculate that in a way
|
|
|
|
|
// that won't break if ROWS_PER_MEASURE changes?
|
|
|
|
|
if( iRowCount>=iPlaceEveryRows )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
|
|
|
|
if( inout.GetTapNote(t,r).type == TapNote::tap )
|
|
|
|
|
inout.SetTapNote(t,r,TAP_ADDITION_MINE);
|
|
|
|
|
|
|
|
|
|
iRowCount = 0;
|
|
|
|
|
if( iPlaceEveryRows == 6 )
|
|
|
|
|
iPlaceEveryRows = 7;
|
|
|
|
|
else
|
|
|
|
|
iPlaceEveryRows = 6;
|
2003-08-23 19:01:35 +00:00
|
|
|
}
|
2003-08-31 22:38:06 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-12 08:13:02 +00:00
|
|
|
// Place mines right after hold so player must lift their foot.
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int iTrack=0; iTrack<inout.GetNumTracks(); ++iTrack )
|
2003-08-31 22:38:06 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( inout, iTrack, r, iStartIndex, iEndIndex )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = inout.GetTapNote( iTrack, r );
|
|
|
|
|
if( tn.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
2004-02-25 08:40:03 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
int iMineRow = r + tn.iDuration + BeatToNoteRow(0.5f);
|
|
|
|
|
if( iMineRow < iStartIndex || iMineRow > iEndIndex )
|
|
|
|
|
continue;
|
2004-02-25 08:40:03 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// Only place a mines if there's not another step nearby
|
|
|
|
|
int iMineRangeBegin = iMineRow - BeatToNoteRow( 0.5f ) + 1;
|
|
|
|
|
int iMineRangeEnd = iMineRow + BeatToNoteRow( 0.5f ) - 1;
|
|
|
|
|
if( !inout.IsRangeEmpty(iTrack, iMineRangeBegin, iMineRangeEnd) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Add a mine right after the hold end.
|
|
|
|
|
inout.SetTapNote( iTrack, iMineRow, TAP_ADDITION_MINE );
|
2003-08-31 22:38:06 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// Convert all notes in this row to mines.
|
|
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
|
|
|
|
if( inout.GetTapNote(t,iMineRow).type == TapNote::tap )
|
|
|
|
|
inout.SetTapNote(t,iMineRow,TAP_ADDITION_MINE);
|
2003-09-15 06:02:26 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
iRowCount = 0;
|
|
|
|
|
}
|
2003-08-31 22:38:06 +00:00
|
|
|
}
|
2003-08-18 02:37:43 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Echo( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
|
|
|
|
// add 8th note tap "echos" after all taps
|
|
|
|
|
int iEchoTrack = -1;
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
const int rows_per_interval = BeatToNoteRow( 0.5f );
|
2005-01-25 05:02:35 +00:00
|
|
|
iStartIndex = Quantize( iStartIndex, rows_per_interval );
|
2003-11-07 05:17:41 +00:00
|
|
|
|
2005-02-08 01:05:02 +00:00
|
|
|
/* Clamp iEndIndex to the last real tap note. Otherwise, we'll keep adding
|
|
|
|
|
* echos of our echos all the way up to MAX_TAP_ROW. */
|
|
|
|
|
iEndIndex = min( iEndIndex, inout.GetLastRow() )+1;
|
|
|
|
|
|
2003-11-07 05:17:41 +00:00
|
|
|
// window is one beat wide and slides 1/2 a beat at a time
|
2005-01-25 05:02:35 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
if( r % rows_per_interval != 0 )
|
|
|
|
|
continue; // 8th notes only
|
|
|
|
|
|
2003-11-08 01:57:04 +00:00
|
|
|
const int iRowWindowBegin = r;
|
|
|
|
|
const int iRowWindowEnd = r + rows_per_interval*2;
|
2003-11-07 05:17:41 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
const int iFirstTapInRow = inout.GetFirstTrackWithTap(iRowWindowBegin);
|
2003-11-07 05:17:41 +00:00
|
|
|
if( iFirstTapInRow != -1 )
|
|
|
|
|
iEchoTrack = iFirstTapInRow;
|
2003-11-08 01:57:04 +00:00
|
|
|
|
2003-11-07 09:27:28 +00:00
|
|
|
if( iEchoTrack==-1 )
|
|
|
|
|
continue; // don't lay
|
2003-11-07 05:17:41 +00:00
|
|
|
|
|
|
|
|
// don't insert a new note if there's already a tap within this interval
|
|
|
|
|
bool bTapInMiddle = false;
|
2005-01-09 22:24:52 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r2, iRowWindowBegin+1, iRowWindowEnd-1 )
|
|
|
|
|
bTapInMiddle = true;
|
2003-11-07 05:17:41 +00:00
|
|
|
if( bTapInMiddle )
|
2003-11-07 09:27:28 +00:00
|
|
|
continue; // don't lay
|
2003-11-07 05:17:41 +00:00
|
|
|
|
|
|
|
|
|
2003-11-08 01:57:04 +00:00
|
|
|
const int iRowEcho = r + rows_per_interval;
|
2003-11-07 09:27:28 +00:00
|
|
|
{
|
2004-05-15 03:00:43 +00:00
|
|
|
set<int> viTracks;
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.GetTracksHeldAtRow( iRowEcho, viTracks );
|
2003-11-07 09:27:28 +00:00
|
|
|
|
|
|
|
|
// don't lay if holding 2 already
|
|
|
|
|
if( viTracks.size() >= 2 )
|
|
|
|
|
continue; // don't lay
|
|
|
|
|
|
|
|
|
|
// don't lay echos on top of a HoldNote
|
|
|
|
|
if( find(viTracks.begin(),viTracks.end(),iEchoTrack) != viTracks.end() )
|
|
|
|
|
continue; // don't lay
|
|
|
|
|
}
|
2003-11-07 05:17:41 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote( iEchoTrack, iRowEcho, TAP_ADDITION_TAP );
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Planted( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-11-07 08:45:21 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
ConvertTapsToHolds( inout, 1, iStartIndex, iEndIndex );
|
2003-11-07 08:45:21 +00:00
|
|
|
}
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Floored( NoteData &inout, int iStartIndex, int iEndIndex )
|
2003-11-07 08:45:21 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
ConvertTapsToHolds( inout, 2, iStartIndex, iEndIndex );
|
2003-11-07 08:45:21 +00:00
|
|
|
}
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Twister( NoteData &inout, int iStartIndex, int iEndIndex )
|
2004-08-29 21:26:52 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
ConvertTapsToHolds( inout, 3, iStartIndex, iEndIndex );
|
2004-08-29 21:26:52 +00:00
|
|
|
}
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::ConvertTapsToHolds( NoteData &inout, int iSimultaneousHolds, int iStartIndex, int iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
|
|
|
|
// Convert all taps to freezes.
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2004-05-15 03:00:43 +00:00
|
|
|
int iTrackAddedThisRow = 0;
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2004-05-15 03:00:43 +00:00
|
|
|
if( iTrackAddedThisRow > iSimultaneousHolds )
|
|
|
|
|
break;
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t,r).type == TapNote::tap )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2004-05-15 03:00:43 +00:00
|
|
|
// Find the ending row for this hold
|
2003-11-07 08:45:21 +00:00
|
|
|
int iTapsLeft = iSimultaneousHolds;
|
2004-05-15 03:00:43 +00:00
|
|
|
|
|
|
|
|
int r2 = r+1;
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, next_row, r+1, iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
r2 = next_row;
|
2004-05-15 03:00:43 +00:00
|
|
|
|
2005-01-09 22:13:18 +00:00
|
|
|
// If there are two taps in a row on the same track,
|
2004-05-15 03:00:43 +00:00
|
|
|
// don't convert the earlier one to a hold.
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t,r2).type != TapNote::empty )
|
2004-05-15 03:00:43 +00:00
|
|
|
goto dont_add_hold;
|
|
|
|
|
|
|
|
|
|
set<int> tracksDown;
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.GetTracksHeldAtRow( r2, tracksDown );
|
|
|
|
|
inout.GetTapNonEmptyTracks( r2, tracksDown );
|
2004-05-15 03:00:43 +00:00
|
|
|
iTapsLeft -= tracksDown.size();
|
|
|
|
|
if( iTapsLeft == 0 )
|
|
|
|
|
break; // we found the ending row for this hold
|
|
|
|
|
else if( iTapsLeft < 0 )
|
|
|
|
|
goto dont_add_hold;
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
2004-05-15 03:00:43 +00:00
|
|
|
|
2005-01-09 22:13:18 +00:00
|
|
|
// If the steps end in a tap, convert that tap
|
2003-11-07 05:17:41 +00:00
|
|
|
// to a hold that lasts for at least one beat.
|
2005-01-22 17:51:46 +00:00
|
|
|
if( r2 == r+1 )
|
|
|
|
|
r2 = r+BeatToNoteRow(1);
|
2004-05-15 03:00:43 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
inout.AddHoldNote( t, r, r2, TAP_ORIGINAL_HOLD_HEAD );
|
2004-05-15 03:00:43 +00:00
|
|
|
iTrackAddedThisRow++;
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
|
|
|
|
dont_add_hold:
|
2003-11-08 01:57:04 +00:00
|
|
|
;
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-05-15 03:00:43 +00:00
|
|
|
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::Stomp( NoteData &inout, StepsType st, int iStartIndex, int iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
|
|
|
|
// Make all non jumps with ample space around them into jumps.
|
2004-01-02 21:02:06 +00:00
|
|
|
int iTrackMapping[MAX_NOTE_TRACKS];
|
2004-10-24 10:20:24 +00:00
|
|
|
GetTrackMapping( st, stomp, inout.GetNumTracks(), iTrackMapping );
|
2004-01-02 21:02:06 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetNumTracksWithTap(r) != 1 )
|
2003-11-07 05:17:41 +00:00
|
|
|
continue; // skip
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t, r).type == TapNote::tap ) // there is a tap here
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
|
|
|
|
// Look to see if there is enough empty space on either side of the note
|
|
|
|
|
// to turn this into a jump.
|
|
|
|
|
int iRowWindowBegin = r - BeatToNoteRow(0.5);
|
|
|
|
|
int iRowWindowEnd = r + BeatToNoteRow(0.5);
|
|
|
|
|
|
|
|
|
|
bool bTapInMiddle = false;
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r2, iRowWindowBegin+1, iRowWindowEnd-1 )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.IsThereATapAtRow(r2) && r2 != r ) // don't count the note we're looking around
|
2003-11-07 05:17:41 +00:00
|
|
|
{
|
|
|
|
|
bTapInMiddle = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( bTapInMiddle )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// don't convert to jump if there's a hold here
|
2004-10-24 10:20:24 +00:00
|
|
|
int iNumTracksHeld = inout.GetNumTracksHeldAtRow(r);
|
2003-11-07 05:17:41 +00:00
|
|
|
if( iNumTracksHeld >= 1 )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-01-02 21:02:06 +00:00
|
|
|
int iOppositeTrack = iTrackMapping[t];
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote( iOppositeTrack, r, TAP_ADDITION_TAP );
|
2003-11-07 05:17:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::SnapToNearestNoteType( NoteData &inout, NoteType nt1, NoteType nt2, int iStartIndex, int iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
// nt2 is optional and should be NOTE_TYPE_INVALID if it is not used
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
float fSnapInterval1 = NoteTypeToBeat( nt1 );
|
|
|
|
|
float fSnapInterval2 = 10000; // nothing will ever snap to this. That's what we want!
|
|
|
|
|
if( nt2 != NOTE_TYPE_INVALID )
|
|
|
|
|
fSnapInterval2 = NoteTypeToBeat( nt2 );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-05-24 04:17:19 +00:00
|
|
|
// iterate over all TapNotes in the interval and snap them
|
2005-01-22 17:34:10 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, iOldIndex, iStartIndex, iEndIndex )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-22 17:34:10 +00:00
|
|
|
int iNewIndex1 = Quantize( iOldIndex, BeatToNoteRow(fSnapInterval1) );
|
|
|
|
|
int iNewIndex2 = Quantize( iOldIndex, BeatToNoteRow(fSnapInterval2) );
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
bool bNewBeat1IsCloser = abs(iNewIndex1-iOldIndex) < abs(iNewIndex2-iOldIndex);
|
|
|
|
|
int iNewIndex = bNewBeat1IsCloser? iNewIndex1 : iNewIndex2;
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int c=0; c<inout.GetNumTracks(); c++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
TapNote tnNew = inout.GetTapNote(c, iOldIndex);
|
|
|
|
|
if( tnNew.type == TapNote::empty )
|
2003-08-07 06:36:34 +00:00
|
|
|
continue;
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote(c, iOldIndex, TAP_EMPTY);
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-03-30 22:05:11 +00:00
|
|
|
if( tnNew.type == TapNote::tap && inout.IsHoldNoteAtBeat(c, iNewIndex) )
|
2004-05-24 04:17:19 +00:00
|
|
|
continue; // HoldNotes override TapNotes
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-03-30 22:05:11 +00:00
|
|
|
if( tnNew.type == TapNote::hold_head )
|
|
|
|
|
{
|
|
|
|
|
/* Quantize the duration. If the result is empty, just discard the hold. */
|
|
|
|
|
tnNew.iDuration = Quantize( tnNew.iDuration, BeatToNoteRow(fSnapInterval1) );
|
|
|
|
|
if( tnNew.iDuration == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* We might be moving a hold note downwards, or extending its duration
|
|
|
|
|
* downwards. Make sure there isn't anything else in the new range. */
|
|
|
|
|
inout.ClearRangeForTrack( iNewIndex, iNewIndex+tnNew.iDuration+1, c );
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-03-30 22:05:11 +00:00
|
|
|
inout.SetTapNote( c, iNewIndex, tnNew );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::CopyLeftToRight( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 21:36:42 +00:00
|
|
|
/* XXX
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.ConvertHoldNotesTo4s();
|
2005-01-09 22:13:18 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks()/2; t++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( inout, t, r )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
int iTrackEarlier = t;
|
2004-10-24 10:20:24 +00:00
|
|
|
int iTrackLater = inout.GetNumTracks()-1-t;
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
const TapNote &tnEarlier = inout.GetTapNote(iTrackEarlier, r);
|
|
|
|
|
inout.SetTapNote(iTrackLater, r, tnEarlier);
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.Convert4sToHoldNotes();
|
2005-01-23 21:36:42 +00:00
|
|
|
*/
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::CopyRightToLeft( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 21:36:42 +00:00
|
|
|
/* XXX
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.ConvertHoldNotesTo4s();
|
2005-01-09 22:13:18 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks()/2; t++ )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( inout, t, r )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
|
|
|
|
int iTrackEarlier = t;
|
2004-10-24 10:20:24 +00:00
|
|
|
int iTrackLater = inout.GetNumTracks()-1-t;
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
TapNote tnLater = inout.GetTapNote(iTrackLater, r);
|
|
|
|
|
inout.SetTapNote(iTrackEarlier, r, tnLater);
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.Convert4sToHoldNotes();
|
2005-01-23 21:36:42 +00:00
|
|
|
*/
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::ClearLeft( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks()/2; t++ )
|
2005-01-23 05:31:55 +00:00
|
|
|
inout.ClearRangeForTrack( 0, MAX_NOTE_ROW, t );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::ClearRight( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
for( int t=(inout.GetNumTracks()+1)/2; t<inout.GetNumTracks(); t++ )
|
2005-01-23 05:31:55 +00:00
|
|
|
inout.ClearRangeForTrack( 0, MAX_NOTE_ROW, t );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::CollapseToOne( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( inout, r )
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
|
|
|
|
if( inout.GetTapNote(t,r).type != TapNote::empty )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
TapNote tn = inout.GetTapNote(t,r);
|
|
|
|
|
inout.SetTapNote(t, r, TAP_EMPTY);
|
|
|
|
|
inout.SetTapNote(0, r, tn);
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::CollapseLeft( NoteData &inout )
|
2003-12-31 07:49:30 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( inout, r )
|
2003-12-31 07:49:30 +00:00
|
|
|
{
|
|
|
|
|
int iNumTracksFilled = 0;
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2005-01-09 22:13:18 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t,r).type != TapNote::empty )
|
2003-12-31 07:49:30 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
TapNote tn = inout.GetTapNote(t,r);
|
|
|
|
|
inout.SetTapNote(t, r, TAP_EMPTY);
|
2005-01-09 22:13:18 +00:00
|
|
|
if( iNumTracksFilled < inout.GetNumTracks() )
|
|
|
|
|
{
|
|
|
|
|
inout.SetTapNote(iNumTracksFilled, r, tn);
|
|
|
|
|
++iNumTracksFilled;
|
|
|
|
|
}
|
2003-12-31 07:49:30 +00:00
|
|
|
}
|
2005-01-09 22:13:18 +00:00
|
|
|
}
|
2003-12-31 07:49:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-23 04:59:10 +00:00
|
|
|
void NoteDataUtil::ShiftTracks( NoteData &inout, int iShiftBy )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 04:59:10 +00:00
|
|
|
int iOriginalTrackToTakeFrom[MAX_NOTE_TRACKS];
|
|
|
|
|
for( int i = 0; i < inout.GetNumTracks(); ++i )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 04:59:10 +00:00
|
|
|
int iFrom = i-iShiftBy;
|
|
|
|
|
wrap( iFrom, inout.GetNumTracks() );
|
|
|
|
|
iOriginalTrackToTakeFrom[i] = iFrom;
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
2005-01-23 04:59:10 +00:00
|
|
|
|
|
|
|
|
NoteData orig( inout );
|
|
|
|
|
inout.LoadTransformed( orig, orig.GetNumTracks(), iOriginalTrackToTakeFrom );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-23 04:59:10 +00:00
|
|
|
void NoteDataUtil::ShiftLeft( NoteData &inout )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2005-01-23 04:59:10 +00:00
|
|
|
ShiftTracks( inout, -1 );
|
|
|
|
|
}
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2005-01-23 04:59:10 +00:00
|
|
|
void NoteDataUtil::ShiftRight( NoteData &inout )
|
|
|
|
|
{
|
|
|
|
|
ShiftTracks( inout, +1 );
|
2003-08-07 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-08-07 07:18:43 +00:00
|
|
|
struct ValidRow
|
|
|
|
|
{
|
|
|
|
|
StepsType st;
|
|
|
|
|
bool bValidMask[MAX_NOTE_TRACKS];
|
|
|
|
|
};
|
|
|
|
|
#define T true
|
|
|
|
|
#define f false
|
|
|
|
|
const ValidRow g_ValidRows[] =
|
|
|
|
|
{
|
|
|
|
|
{ STEPS_TYPE_DANCE_DOUBLE, { T,T,T,T,f,f,f,f } },
|
|
|
|
|
{ STEPS_TYPE_DANCE_DOUBLE, { f,T,T,T,T,f,f,f } },
|
|
|
|
|
{ STEPS_TYPE_DANCE_DOUBLE, { f,f,f,T,T,T,T,f } },
|
|
|
|
|
{ STEPS_TYPE_DANCE_DOUBLE, { f,f,f,f,T,T,T,T } },
|
|
|
|
|
};
|
2003-08-07 06:36:34 +00:00
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::FixImpossibleRows( NoteData &inout, StepsType st )
|
2003-08-07 06:36:34 +00:00
|
|
|
{
|
2003-09-21 23:16:44 +00:00
|
|
|
vector<const ValidRow*> vpValidRowsToCheck;
|
2003-09-24 09:39:09 +00:00
|
|
|
for( unsigned i=0; i<ARRAYSIZE(g_ValidRows); i++ )
|
2003-08-07 07:18:43 +00:00
|
|
|
{
|
2003-09-21 23:16:44 +00:00
|
|
|
if( g_ValidRows[i].st == st )
|
|
|
|
|
vpValidRowsToCheck.push_back( &g_ValidRows[i] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bail early if there's nothing to validate against
|
|
|
|
|
if( vpValidRowsToCheck.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// each row must pass at least one valid mask
|
2005-01-10 01:03:36 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS( inout, r )
|
2003-09-21 23:16:44 +00:00
|
|
|
{
|
|
|
|
|
// only check rows with jumps
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetNumTapNonEmptyTracks(r) < 2 )
|
2003-09-21 23:16:44 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
bool bPassedOneMask = false;
|
|
|
|
|
for( unsigned i=0; i<vpValidRowsToCheck.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const ValidRow &vr = *vpValidRowsToCheck[i];
|
2004-10-24 10:20:24 +00:00
|
|
|
if( NoteDataUtil::RowPassesValidMask(inout,r,vr.bValidMask) )
|
2003-09-21 23:16:44 +00:00
|
|
|
{
|
|
|
|
|
bPassedOneMask = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-08-07 07:18:43 +00:00
|
|
|
|
2003-09-21 23:16:44 +00:00
|
|
|
if( !bPassedOneMask )
|
2004-10-25 03:36:42 +00:00
|
|
|
RemoveAllButOneTap( inout, r );
|
2003-08-07 07:18:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
bool NoteDataUtil::RowPassesValidMask( NoteData &inout, int row, const bool bValidMask[] )
|
2003-08-07 07:18:43 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
2003-09-21 23:16:44 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
if( !bValidMask[t] && inout.GetTapNote(t,row).type != TapNote::empty )
|
2003-08-07 07:18:43 +00:00
|
|
|
return false;
|
2003-09-21 23:16:44 +00:00
|
|
|
}
|
2003-08-07 07:18:43 +00:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
void NoteDataUtil::ConvertAdditionsToRegular( NoteData &inout )
|
2003-08-10 06:00:30 +00:00
|
|
|
{
|
2005-01-09 22:13:18 +00:00
|
|
|
for( int t=0; t<inout.GetNumTracks(); t++ )
|
|
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( inout, t, r )
|
2004-10-24 10:20:24 +00:00
|
|
|
if( inout.GetTapNote(t,r).source == TapNote::addition )
|
2004-09-12 05:56:24 +00:00
|
|
|
{
|
2004-10-24 10:20:24 +00:00
|
|
|
TapNote tn = inout.GetTapNote(t,r);
|
2004-09-12 05:56:24 +00:00
|
|
|
tn.source = TapNote::original;
|
2004-10-24 10:20:24 +00:00
|
|
|
inout.SetTapNote( t, r, tn );
|
2004-09-12 05:56:24 +00:00
|
|
|
}
|
2003-08-10 06:00:30 +00:00
|
|
|
}
|
2003-08-10 10:12:50 +00:00
|
|
|
|
2004-07-23 04:45:48 +00:00
|
|
|
void NoteDataUtil::TransformNoteData( NoteData &nd, const AttackArray &aa, StepsType st, Song* pSong )
|
|
|
|
|
{
|
|
|
|
|
FOREACH_CONST( Attack, aa, a )
|
|
|
|
|
{
|
|
|
|
|
PlayerOptions po;
|
|
|
|
|
po.FromString( a->sModifier );
|
|
|
|
|
if( po.ContainsTransformOrTurn() )
|
|
|
|
|
{
|
|
|
|
|
float fStartBeat, fEndBeat;
|
2004-12-20 06:25:59 +00:00
|
|
|
a->GetAttackBeats( pSong, NULL, fStartBeat, fEndBeat );
|
2004-07-23 04:45:48 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
NoteDataUtil::TransformNoteData( nd, po, st, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat) );
|
2004-07-23 04:45:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
void NoteDataUtil::TransformNoteData( NoteData &nd, const PlayerOptions &po, StepsType st, int iStartIndex, int iEndIndex )
|
2003-08-13 19:17:28 +00:00
|
|
|
{
|
2004-08-29 21:14:26 +00:00
|
|
|
// Apply remove transforms before others so that we don't go removing
|
|
|
|
|
// notes we just inserted.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_LITTLE] ) NoteDataUtil::Little( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_NOHOLDS] ) NoteDataUtil::RemoveHoldNotes( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_NOMINES] ) NoteDataUtil::RemoveMines( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_NOJUMPS] ) NoteDataUtil::RemoveJumps( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_NOHANDS] ) NoteDataUtil::RemoveHands( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_NOQUADS] ) NoteDataUtil::RemoveQuads( nd, iStartIndex, iEndIndex );
|
2004-03-01 08:59:22 +00:00
|
|
|
|
2004-08-29 21:14:26 +00:00
|
|
|
// Apply inserts.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_BIG] ) NoteDataUtil::Big( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_QUICK] ) NoteDataUtil::Quick( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_BMRIZE] ) NoteDataUtil::BMRize( nd, iStartIndex, iEndIndex );
|
2004-03-01 08:59:22 +00:00
|
|
|
|
|
|
|
|
// Skippy will still add taps to places that the other
|
|
|
|
|
// AddIntelligentTaps above won't.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_SKIPPY] ) NoteDataUtil::Skippy( nd, iStartIndex, iEndIndex );
|
2004-03-01 08:59:22 +00:00
|
|
|
|
2004-08-29 21:14:26 +00:00
|
|
|
// These aren't affects by the above inserts very much.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_MINES] ) NoteDataUtil::AddMines( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_ECHO] ) NoteDataUtil::Echo( nd, iStartIndex, iEndIndex );
|
2004-03-01 08:59:22 +00:00
|
|
|
|
2004-08-29 21:14:26 +00:00
|
|
|
// Jump-adding transforms aren't much affected by additional taps.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_WIDE] ) NoteDataUtil::Wide( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_STOMP] ) NoteDataUtil::Stomp( nd, st, iStartIndex, iEndIndex );
|
2004-03-01 08:59:22 +00:00
|
|
|
|
|
|
|
|
// Transforms that add holds go last. If they went first, most tap-adding
|
2004-08-29 21:14:26 +00:00
|
|
|
// transforms wouldn't do anything because tap-adding transforms skip areas
|
|
|
|
|
// where there's a hold.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_PLANTED] ) NoteDataUtil::Planted( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_FLOORED] ) NoteDataUtil::Floored( nd, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTransforms[PlayerOptions::TRANSFORM_TWISTER] ) NoteDataUtil::Twister( nd, iStartIndex, iEndIndex );
|
2004-08-29 21:14:26 +00:00
|
|
|
|
|
|
|
|
// Apply turns and shuffles last to that they affect inserts.
|
2005-01-22 17:34:10 +00:00
|
|
|
if( po.m_bTurns[PlayerOptions::TURN_MIRROR] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::mirror, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTurns[PlayerOptions::TURN_LEFT] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::left, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTurns[PlayerOptions::TURN_RIGHT] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::right, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTurns[PlayerOptions::TURN_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::shuffle, iStartIndex, iEndIndex );
|
|
|
|
|
if( po.m_bTurns[PlayerOptions::TURN_SUPER_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::super_shuffle, iStartIndex, iEndIndex );
|
2003-08-13 19:17:28 +00:00
|
|
|
}
|
|
|
|
|
|
2003-11-20 06:50:05 +00:00
|
|
|
void NoteDataUtil::AddTapAttacks( NoteData &nd, Song* pSong )
|
|
|
|
|
{
|
|
|
|
|
// throw an attack in every 30 seconds
|
|
|
|
|
|
|
|
|
|
const char* szAttacks[3] =
|
|
|
|
|
{
|
|
|
|
|
"2x",
|
|
|
|
|
"drunk",
|
|
|
|
|
"dizzy",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for( float sec=15; sec<pSong->m_fMusicLengthSeconds; sec+=30 )
|
|
|
|
|
{
|
|
|
|
|
float fBeat = pSong->GetBeatFromElapsedTime( sec );
|
|
|
|
|
int iBeat = (int)fBeat;
|
|
|
|
|
int iTrack = iBeat % nd.GetNumTracks(); // deterministically calculates track
|
2005-01-22 19:25:05 +00:00
|
|
|
TapNote tn(
|
|
|
|
|
TapNote::attack, TapNote::original,
|
|
|
|
|
szAttacks[rand()%ARRAYSIZE(szAttacks)],
|
|
|
|
|
15.0f,
|
|
|
|
|
false,
|
|
|
|
|
0 );
|
|
|
|
|
nd.SetTapNote( iTrack, BeatToNoteRow(fBeat), tn );
|
2003-11-20 06:50:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-25 08:25:39 +00:00
|
|
|
#if 0 // undo this if ScaleRegion breaks more things than it fixes
|
2003-09-07 22:28:46 +00:00
|
|
|
void NoteDataUtil::Scale( NoteData &nd, float fScale )
|
|
|
|
|
{
|
|
|
|
|
ASSERT( fScale > 0 );
|
|
|
|
|
|
|
|
|
|
NoteData temp;
|
|
|
|
|
temp.CopyAll( &nd );
|
|
|
|
|
nd.ClearAll();
|
|
|
|
|
|
|
|
|
|
for( int r=0; r<=temp.GetLastRow(); r++ )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<temp.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
TapNote tn = temp.GetTapNote( t, r );
|
|
|
|
|
if( tn != TAP_EMPTY )
|
|
|
|
|
{
|
|
|
|
|
temp.SetTapNote( t, r, TAP_EMPTY );
|
|
|
|
|
|
2003-09-07 22:48:25 +00:00
|
|
|
int new_row = int(r*fScale);
|
2003-09-07 22:28:46 +00:00
|
|
|
nd.SetTapNote( t, new_row, tn );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-02-25 08:25:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
2005-01-22 17:51:46 +00:00
|
|
|
void NoteDataUtil::ScaleRegion( NoteData &nd, float fScale, int iStartIndex, int iEndIndex )
|
2004-02-26 03:07:49 +00:00
|
|
|
{
|
2004-02-25 08:25:39 +00:00
|
|
|
ASSERT( fScale > 0 );
|
2005-01-22 17:51:46 +00:00
|
|
|
ASSERT( iStartIndex < iEndIndex );
|
|
|
|
|
ASSERT( iStartIndex >= 0 );
|
2004-02-25 08:25:39 +00:00
|
|
|
|
|
|
|
|
NoteData temp1, temp2;
|
2005-01-25 05:02:35 +00:00
|
|
|
temp1.SetNumTracks( nd.GetNumTracks() );
|
|
|
|
|
temp2.SetNumTracks( nd.GetNumTracks() );
|
2004-02-25 08:25:39 +00:00
|
|
|
|
2005-01-22 17:51:46 +00:00
|
|
|
if( iStartIndex != 0 )
|
|
|
|
|
temp1.CopyRange( nd, 0, iStartIndex );
|
2005-01-25 05:02:35 +00:00
|
|
|
if( iEndIndex != MAX_NOTE_ROW )
|
|
|
|
|
{
|
|
|
|
|
const int iScaledFirstRowAfterRegion = int(iStartIndex + (iEndIndex - iStartIndex) * fScale);
|
|
|
|
|
temp1.CopyRange( nd, iEndIndex, MAX_NOTE_ROW, iScaledFirstRowAfterRegion );
|
|
|
|
|
}
|
|
|
|
|
temp2.CopyRange( nd, iStartIndex, iEndIndex );
|
2004-02-25 08:25:39 +00:00
|
|
|
nd.ClearAll();
|
|
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int t=0; t<temp2.GetNumTracks(); t++ )
|
2004-02-25 08:25:39 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( temp2, t, r )
|
2004-02-25 08:25:39 +00:00
|
|
|
{
|
|
|
|
|
TapNote tn = temp2.GetTapNote( t, r );
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type != TapNote::empty )
|
2004-02-25 08:25:39 +00:00
|
|
|
{
|
|
|
|
|
temp2.SetTapNote( t, r, TAP_EMPTY );
|
|
|
|
|
|
2005-01-22 17:51:46 +00:00
|
|
|
int new_row = int(r*fScale + iStartIndex);
|
2004-02-25 08:25:39 +00:00
|
|
|
temp1.SetTapNote( t, new_row, tn );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
nd.CopyAll( temp1 );
|
2004-02-25 08:25:39 +00:00
|
|
|
}
|
2003-09-07 22:28:46 +00:00
|
|
|
|
2005-01-22 17:51:46 +00:00
|
|
|
void NoteDataUtil::ShiftRows( NoteData &nd, int iStartIndex, int iRowsToShift )
|
2003-09-07 22:28:46 +00:00
|
|
|
{
|
2005-01-22 17:51:46 +00:00
|
|
|
int iTakeFromRow = iStartIndex;
|
|
|
|
|
int iPasteAtRow = iStartIndex;
|
2003-09-07 22:28:46 +00:00
|
|
|
|
2005-01-22 17:51:46 +00:00
|
|
|
if( iRowsToShift > 0 ) // add blank rows
|
|
|
|
|
iPasteAtRow += iRowsToShift;
|
2003-09-07 22:28:46 +00:00
|
|
|
else // delete rows
|
2005-01-22 17:51:46 +00:00
|
|
|
iTakeFromRow -= iRowsToShift;
|
2003-09-07 22:28:46 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
NoteData temp;
|
|
|
|
|
temp.SetNumTracks( nd.GetNumTracks() );
|
|
|
|
|
temp.CopyRange( nd, iTakeFromRow, MAX_NOTE_ROW );
|
|
|
|
|
nd.ClearRange( min(iTakeFromRow,iPasteAtRow), MAX_NOTE_ROW );
|
|
|
|
|
nd.CopyRange( temp, 0, MAX_NOTE_ROW, iPasteAtRow );
|
2003-09-13 19:46:37 +00:00
|
|
|
}
|
2004-05-31 22:42:12 +00:00
|
|
|
|
2004-10-24 10:45:30 +00:00
|
|
|
void NoteDataUtil::RemoveAllTapsOfType( NoteData& ndInOut, TapNote::Type typeToRemove )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<ndInOut.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( ndInOut, t, row )
|
|
|
|
|
{
|
|
|
|
|
if( ndInOut.GetTapNote(t, row).type == typeToRemove )
|
|
|
|
|
ndInOut.SetTapNote( t, row, TAP_EMPTY );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NoteDataUtil::RemoveAllTapsExceptForType( NoteData& ndInOut, TapNote::Type typeToKeep )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<ndInOut.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( ndInOut, t, row )
|
|
|
|
|
{
|
|
|
|
|
if( ndInOut.GetTapNote(t, row).type != typeToKeep )
|
|
|
|
|
ndInOut.SetTapNote( t, row, TAP_EMPTY );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-25 09:36:08 +00:00
|
|
|
int NoteDataUtil::GetNumUsedTracks( const NoteData& in )
|
|
|
|
|
{
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
bool bHasAnyTapsInTrack = false;
|
|
|
|
|
FOREACH_NONEMPTY_ROW_IN_TRACK( in, t, row )
|
|
|
|
|
{
|
|
|
|
|
bHasAnyTapsInTrack = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( !bHasAnyTapsInTrack )
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
return in.GetNumTracks();
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 10:20:24 +00:00
|
|
|
|
2004-05-31 22:42:12 +00:00
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|