/* * NoteData is organized by: * track - corresponds to different columns of notes on the screen * row/index - corresponds to subdivisions of beats */ #include "global.h" #include "NoteData.h" #include "RageUtil.h" #include "RageLog.h" NoteData::NoteData() { Init(); } void NoteData::Init() { ClearAll(); m_TapNotes.clear(); } NoteData::~NoteData() { } int NoteData::GetNumTracks() const { return m_TapNotes.size(); } void NoteData::SetNumTracks( int iNewNumTracks ) { ASSERT( iNewNumTracks > 0 ); m_TapNotes.resize( iNewNumTracks ); /* Remove all hold notes that are out of bounds. */ // Iterate backwards so that we can delete. for( int h = m_HoldNotes.size()-1; h >= 0; --h ) if( m_HoldNotes[h].iTrack >= iNewNumTracks ) m_HoldNotes.erase( m_HoldNotes.begin()+h ); } /* Clear [rowBegin,rowEnd]; that is, including rowEnd. */ void NoteData::ClearRange( int rowBegin, int rowEnd ) { this->ConvertHoldNotesTo4s(); for( int t=0; tConvert4sToHoldNotes(); } void NoteData::ClearAll() { for( int t=0; tFrom4s( To ); } void NoteData::Config( const NoteData& from ) { SetNumTracks( from.GetNumTracks() ); } void NoteData::CopyAll( const NoteData& from ) { Config(from); ClearAll(); for( int c=0; c& addTo ) const { for( int t=0; t=0; t-- ) { if( GetTapNoteX( t, row ).type == TapNote::empty ) { iEmptyTrackOut = t; return true; } } return false; } int NoteData::GetNumTracksWithTap( int row ) const { int iNum = 0; for( int t=0; t=0 && add.iEndRow>=0 ); // look for other hold notes that overlap and merge them // XXX: this is done implicitly with 4s, but 4s uses this function. // Rework this later. for( int i=0; i= 0 && iHoldIndex < GetNumHoldNotes() ); HoldNote& hn = GetHoldNote(iHoldIndex); const int iHoldStartIndex = hn.iStartRow; // delete a tap note at the start of this hold SetTapNote(hn.iTrack, iHoldStartIndex, TAP_EMPTY); // remove from list m_HoldNotes.erase(m_HoldNotes.begin()+iHoldIndex, m_HoldNotes.begin()+iHoldIndex+1); } /* Get a hold note with the same track and end row as hn. */ int NoteData::GetMatchingHoldNote( const HoldNote &hn ) const { for( int i=0; ifirst; else iEarliestRowFoundSoFar = min( iEarliestRowFoundSoFar, iter->first ); } for( int i=0; ifirst ); } for( int i=0; i iOldestRowFoundSoFar ) iOldestRowFoundSoFar = GetHoldNote(i).iEndRow; } return iOldestRowFoundSoFar; } int NoteData::GetNumTapNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = 999999; int iNumNotes = 0; int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); for( int t=0; t= 3; } int NoteData::GetNumHands( float fStartBeat, float fEndBeat ) const { /* Count the number of times you have to use your hands. This includes * three taps at the same time, a tap while two hold notes are being held, * etc. Only count rows that have at least one tap note (hold heads count). * Otherwise, every row of hold notes counts, so three simultaneous hold * notes will count as hundreds of "hands". */ if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); int iNum = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex ) { if( !RowNeedsHands(r) ) continue; iNum++; } return iNum; } int NoteData::GetNumN( int iMinTaps, float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); int iNum = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, r, iStartIndex, iEndIndex ) { int iNumNotesThisIndex = 0; for( int t=0; t= iMinTaps ) iNum++; } return iNum; } int NoteData::GetNumHoldNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); int iNumHolds = 0; for( int i=0; i= %i (to %i)", iOriginalTrack, Original.GetNumTracks(), iOriginalTrackToTakeFrom[t])); if( iOriginalTrack == -1 ) continue; m_TapNotes[t] = Original.m_TapNotes[iOriginalTrack]; } Convert4sToHoldNotes(); } void NoteData::MoveTapNoteTrack( int dest, int src ) { if(dest == src) return; m_TapNotes[dest] = m_TapNotes[src]; m_TapNotes[src].clear(); } void NoteData::SetTapNote( int track, int row, const TapNote& t ) { DEBUG_ASSERT( track>=0 && track& addTo ) { for( unsigned i=0; i viTracks; viTracks.clear(); GetTracksHeldAtRow( row, viTracks ); return viTracks.size(); } bool NoteData::GetNextTapNoteRowForTrack( int track, int &rowInOut ) const { const TrackMap &mapTrack = m_TapNotes[track]; // lower_bound and upper_bound have the same effect here because duplicate // keys aren't allowed. // // lower_bound "finds the first element whose key is not less than k" (>=); // upper_bound "finds the first element whose key greater than k". They don't // have the same effect, but lower_bound(row+1) should equal upper_bound(row). -glenn TrackMap::const_iterator iter = mapTrack.lower_bound( rowInOut+1 ); // "find the first note for which row+1 < key == false" if( iter == mapTrack.end() ) return false; ASSERT( iter->first > rowInOut ); rowInOut = iter->first; return true; } bool NoteData::GetPrevTapNoteRowForTrack( int track, int &rowInOut ) const { const TrackMap &mapTrack = m_TapNotes[track]; /* Find the first note >= rowInOut. */ TrackMap::const_iterator iter = mapTrack.lower_bound( rowInOut ); /* If we're at the beginning, we can't move back any more. */ if( iter == mapTrack.begin() ) return false; /* Move back by one. */ --iter; ASSERT( iter->first < rowInOut ); rowInOut = iter->first; return true; } bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const { int iClosestNextRow = 999999; bool bAnyHaveNextNote = false; for( int t=0; t