/* * 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 ); // Enforce that all track vectors have the same length. unsigned uNumRows = m_TapNotes[0].size(); for( unsigned t=0; t= 0; --h ) if( m_HoldNotes[h].iTrack >= iNewNumTracks ) m_HoldNotes.erase( m_HoldNotes.begin()+h ); } /* Clear [iNoteIndexBegin,iNoteIndexEnd]; that is, including iNoteIndexEnd. */ void NoteData::ClearRange( int iNoteIndexBegin, int iNoteIndexEnd ) { this->ConvertHoldNotesTo4s(); for( unsigned t=0; tConvert4sToHoldNotes(); } void NoteData::ClearAll() { for( unsigned t=0; tGetNumTracks() == GetNumTracks() ); NoteData From, To; From.To4s( *pFrom ); To.To4s( *this ); // copy recorded TapNotes int f = iFromIndexBegin, t = iToIndexBegin; while( f<=iFromIndexEnd ) { for( int c=0; cFrom4s( To ); } void NoteData::Config( const NoteData &From ) { SetNumTracks( From.GetNumTracks() ); } void NoteData::CopyAll( const NoteData* pFrom ) { Config(*pFrom); ClearAll(); for( int c=0; cm_TapNotes[c]; m_HoldNotes = pFrom->m_HoldNotes; m_AttackMap = pFrom->m_AttackMap; } bool NoteData::IsRowEmpty( int index ) const { /* If this is out of range, we don't have any notes there, so all tracks are empty. */ if( index < 0 || index >= GetNumRows() ) return true; for( int t=0; t& addTo ) const { for( int t=0; t= GetNumRows() ) return 0; for( int t=0; t= GetNumRows() ) return 0; int iNum = 0; for( int t=0; t= GetNumRows() ) return 0; int iNum = 0; for( int t=0; t= GetNumRows() ) return -1; for( int t=0; t= GetNumRows() ) return -1; 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; i setUsedIndices; int num_rows = GetNumRows(); for( int t=0; t::iterator iter = m_AttackMap.find( tn.attackIndex ); ASSERT( iter != m_AttackMap.end() ); return iter->second; } int NoteData::GetFirstRow() const { int iEarliestRowFoundSoFar = -1; for( int i=0; i < int(m_TapNotes[0].size()); i++ ) { if( !IsRowEmpty(i) ) { iEarliestRowFoundSoFar = i; break; } } for( int i=0; i=0; i-- ) // iterate back to front { if( !IsRowEmpty(i) ) { iOldestRowFoundSoFar = i; break; } } for( int i=0; i iOldestRowFoundSoFar ) iOldestRowFoundSoFar = GetHoldNote(i).iEndRow; } return iOldestRowFoundSoFar; } float NoteData::GetLastBeat() const { return NoteRowToBeat( GetLastRow() ); } int NoteData::GetNumTapNotes( float fStartBeat, float fEndBeat ) const { int iNumNotes = 0; if( fEndBeat == -1 ) fEndBeat = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetNumRows()-1 ); 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 = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetNumRows()-1 ); int iNum = 0; for( int i=iStartIndex; i<=iEndIndex; i++ ) { if( !RowNeedsHands(i) ) continue; iNum++; } return iNum; } int NoteData::GetNumN( int MinTaps, float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetNumRows()-1 ); int iNum = 0; for( int i=iStartIndex; i<=iEndIndex; i++ ) { int iNumNotesThisIndex = 0; for( int t=0; t= MinTaps ) iNum++; } return iNum; } int NoteData::GetNumHoldNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetNumBeats(); 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(); m_AttackMap = Original.GetAttackMap(); } void NoteData::PadTapNotes(int rows) { int needed = rows - m_TapNotes[0].size() + 1; if(needed < 0) return; needed += 100; /* optimization: give it a little more than it needs */ for(int track = 0; track < GetNumTracks(); ++track) m_TapNotes[track].insert( m_TapNotes[track].end(), needed, TAP_EMPTY ); } 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, TapNote t) { DEBUG_ASSERT( track>=0 && track& addTo ) { for( unsigned i=0; i viTracks; viTracks.clear(); GetTracksHeldAtRow( row, viTracks ); return viTracks.size(); } /* * (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. */