evil big commit (sorry):

Remove m_HoldNote array entirely.  Remove 2sAnd3s.  Store hold note
durations in the hold head.  hold_tail only still exists when InsertHoldTails
is used, to make SM parsing a little easier (may go away).  Add helpers
for iterating over ranges while including or excluding adjacent and overlapping
hold notes.  Range operations are now [start,end) instead of [start,end].
(probably more details coming to sm-dev soon)
This commit is contained in:
Glenn Maynard
2005-01-25 05:02:35 +00:00
parent 6ece8829c4
commit bb7482e722
12 changed files with 771 additions and 762 deletions
+343 -344
View File
@@ -30,69 +30,82 @@ 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. */
/* Clear [rowBegin,rowEnd). */
void NoteData::ClearRangeForTrack( int rowBegin, int rowEnd, int iTrack )
{
/* Optimization: if the range encloses everything, just clear the whole maps. */
if( rowBegin == 0 && rowEnd == MAX_NOTE_ROW )
{
m_TapNotes[iTrack].clear();
for( int i = GetNumHoldNotes()-1; i >= 0; --i )
{
HoldNote hn = GetHoldNote(i);
if( hn.iTrack != iTrack )
continue;
this->RemoveHoldNote( i );
}
return;
}
/* Crop or split hold notes overlapping the range. */
for( int i = GetNumHoldNotes()-1; i >= 0; --i ) // for each HoldNote
iterator begin, end;
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
if( begin != end && begin->first < rowBegin && begin->first + begin->second.iDuration > rowEnd )
{
HoldNote hn = GetHoldNote(i);
if( hn.iTrack != iTrack )
continue;
/* A hold note overlaps the whole range. Truncate it, and add the remainder to
* the end. */
TapNote tn1 = begin->second;
TapNote tn2 = tn1;
if( !hn.RangeOverlaps(rowBegin, rowEnd) )
continue;
int iEndRow = begin->first + tn1.iDuration;
int iRow = begin->first;
this->RemoveHoldNote( i );
tn1.iDuration = rowBegin - iRow;
tn2.iDuration = iEndRow - rowEnd;
/* If the range encloses the hold note completely, just delete it. */
if( hn.ContainedByRange(rowBegin, rowEnd) )
continue;
SetTapNote( iTrack, iRow, tn1 );
SetTapNote( iTrack, rowEnd, tn2 );
if( hn.RangeInside(rowBegin, rowEnd) )
{
/* The hold note encloses the range, so we need to split the hold note. */
HoldNote hnLater(hn);
hn.iEndRow = rowBegin;
hnLater.iStartRow = rowEnd;
this->AddHoldNote( hn );
this->AddHoldNote( hnLater );
continue;
}
/* We may have invalidated our iterators. */
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
}
else if( begin != end && begin->first < rowBegin )
{
/* A hold note overlaps the beginning of the range. Truncate it. */
TapNote &tn1 = begin->second;
int iRow = begin->first;
tn1.iDuration = rowBegin - iRow;
if( hn.iStartRow < rowBegin )
hn.iEndRow = min( hn.iEndRow, rowBegin );
else
hn.iStartRow = max( hn.iStartRow, rowEnd );
this->AddHoldNote( hn );
++begin;
}
/* Clear other notes in the region. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, iTrack, r, rowBegin, rowEnd )
SetTapNote( iTrack, r, TAP_EMPTY );
if( begin != end )
{
iterator prev = end;
--prev;
TapNote tn = begin->second;
int iRow = prev->first;
if( tn.type == TapNote::hold_head && iRow + tn.iDuration > rowEnd )
{
/* A hold note overlaps the end of the range. Separate it. */
SetTapNote( iTrack, iRow, TAP_EMPTY );
int iAdd = rowEnd - iRow;
tn.iDuration -= iAdd;
iRow += iAdd;
SetTapNote( iTrack, iRow, tn );
end = prev;
}
/* We may have invalidated our iterators. */
GetTapNoteRangeInclusive( iTrack, rowBegin, rowEnd, begin, end );
}
while( begin != end )
{
iterator next = begin;
++next;
RemoveTapNote( iTrack, begin );
begin = next;
}
}
void NoteData::ClearRange( int rowBegin, int rowEnd )
@@ -105,7 +118,6 @@ void NoteData::ClearAll()
{
for( int t=0; t<GetNumTracks(); t++ )
m_TapNotes[t].clear();
m_HoldNotes.clear();
}
/* Copy a range from pFrom to this. (Note that this does *not* overlay;
@@ -114,97 +126,52 @@ void NoteData::CopyRange( const NoteData& from, int rowFromBegin, int rowFromEnd
{
ASSERT( from.GetNumTracks() == GetNumTracks() );
int rowToEnd = (rowFromEnd-rowFromBegin) + rowToBegin;
if( rowFromBegin > rowFromEnd )
return; /* empty range */
const int rowToEnd = (rowFromEnd-rowFromBegin) + rowToBegin;
const int iMoveBy = rowToBegin-rowFromBegin;
/* Clear the region. */
ClearRange( rowToBegin, rowToEnd );
/* Copy everything except for hold notes. */
for( int t=0; t<GetNumTracks(); t++ )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( from, t, iFrom, rowFromBegin, rowFromEnd )
const_iterator begin, end;
from.GetTapNoteRangeInclusive( t, rowFromBegin, rowFromEnd, begin, end );
for( ; begin != end; ++begin )
{
const TapNote &tn = from.GetTapNote( t, iFrom );
switch( tn.type )
{
case TapNote::empty:
case TapNote::hold_tail:
case TapNote::hold_head:
TapNote head = begin->second;
if( head.type == TapNote::empty )
continue;
if( head.type == TapNote::hold_head )
{
int iStartRow = begin->first + iMoveBy;
int iEndRow = iStartRow + head.iDuration;
iStartRow = clamp( iStartRow, rowToBegin, rowToEnd );
iEndRow = clamp( iEndRow, rowToBegin, rowToEnd );
this->AddHoldNote( t, iStartRow, iEndRow, head );
}
else
{
int iTo = begin->first + iMoveBy;
if( iTo >= rowToBegin && iTo <= rowToEnd )
this->SetTapNote( t, iTo, head );
}
int iTo = rowToBegin + iFrom - rowFromBegin;
this->SetTapNote( t, iTo, tn );
}
}
/* Copy hold notes. */
for( int i=0; i<from.GetNumHoldNotes(); i++ ) // for each HoldNote
{
HoldNote hn = from.GetHoldNote(i);
if( !hn.RangeOverlaps(rowFromBegin, rowFromEnd) )
continue;
/* Move the hold note. */
int iMoveBy = rowToBegin-rowFromBegin;
hn.iStartRow += iMoveBy;
hn.iEndRow += iMoveBy;
/* Crop the hold note to the region. */
hn.iStartRow = max( hn.iStartRow, rowToBegin );
hn.iEndRow = min( hn.iEndRow, rowToEnd );
/* The beginning of the hold might match up to the end of an existing hold, the end
* may match with the beginning, or both. */
int iEarlierHoldNote = -1, iLaterHoldNote = -1;
for( int j=0; j<this->GetNumHoldNotes(); ++j ) // for each HoldNote
{
const HoldNote &hn2 = this->GetHoldNote(j);
if( hn2.iEndRow == hn.iStartRow )
iEarlierHoldNote = j;
if( hn2.iStartRow == hn.iEndRow )
iLaterHoldNote = j;
}
if( iEarlierHoldNote != -1 && iLaterHoldNote != -1 )
{
HoldNote &hnEarlier = this->GetHoldNote( iEarlierHoldNote );
const HoldNote &hnLater = this->GetHoldNote( iLaterHoldNote );
hnEarlier.iEndRow = hnLater.iEndRow;
this->RemoveHoldNote( iLaterHoldNote );
}
else if( iEarlierHoldNote == -1 && iLaterHoldNote == -1 )
AddHoldNote( hn );
else if( iEarlierHoldNote != -1 )
{
HoldNote &hn2 = this->GetHoldNote(iEarlierHoldNote);
hn2.iEndRow = hn.iEndRow;
}
else if( iLaterHoldNote != -1 )
{
HoldNote &hn2 = this->GetHoldNote(iLaterHoldNote);
hn2.iStartRow = hn.iStartRow;
}
else
FAIL_M(ssprintf("%i,%i", iEarlierHoldNote, iLaterHoldNote));
}
}
void NoteData::Config( const NoteData& from )
{
SetNumTracks( from.GetNumTracks() );
}
void NoteData::CopyAll( const NoteData& from )
{
Config(from);
SetNumTracks( from.GetNumTracks() );
ClearAll();
for( int c=0; c<GetNumTracks(); c++ )
m_TapNotes[c] = from.m_TapNotes[c];
m_HoldNotes = from.m_HoldNotes;
}
bool NoteData::IsRowEmpty( int row ) const
@@ -326,144 +293,98 @@ int NoteData::GetFirstTrackWithTapOrHoldHead( int row ) const
return -1;
}
void NoteData::AddHoldNote( HoldNote add )
void NoteData::AddHoldNote( int iTrack, int iStartRow, int iEndRow, TapNote tn )
{
ASSERT( add.iStartRow>=0 && add.iEndRow>=0 );
ASSERT( iStartRow>=0 && iEndRow>=0 );
ASSERT( iEndRow >= iStartRow );
tn.iDuration = iEndRow - iStartRow;
/* Include adjacent (non-overlapping) hold notes, since we need to merge with them. */
iterator begin, end;
GetTapNoteRangeInclusive( iTrack, iStartRow, iStartRow+tn.iDuration, begin, end, true );
// look for other hold notes that overlap and merge them
for( int i=0; i<GetNumHoldNotes(); i++ ) // for each HoldNote
/* Look for other hold notes that overlap and merge them into add. */
for( iterator it = begin; it != end; ++it )
{
HoldNote &other = GetHoldNote(i);
if( add.iTrack == other.iTrack && // the tracks correspond
add.RangeOverlaps(other) ) // they overlap
int iRow = it->first;
if( it->second.type == TapNote::hold_head )
{
add.iStartRow = min(add.iStartRow, other.iStartRow);
add.iEndRow = max(add.iEndRow, other.iEndRow);
// delete this HoldNote
RemoveHoldNote( i );
--i;
if( iRow < iStartRow )
{
/* extend tn up */
int iExtendBy = iStartRow - iRow;
tn.iDuration += iExtendBy;
iStartRow -= iExtendBy;
}
else
{
/* extend tn down */
int iEndRow = iRow + it->second.iDuration;
tn.iDuration = iEndRow - iStartRow;
}
}
}
int iAddStartIndex = add.iStartRow;
int iAddEndIndex = add.iEndRow;
/* Remove everything in the range. */
while( begin != end )
{
iterator next = begin;
++next;
// delete TapNotes under this HoldNote
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, add.iTrack, i, iAddStartIndex+1, iAddEndIndex )
SetTapNote( add.iTrack, i, TAP_EMPTY );
RemoveTapNote( iTrack, begin );
begin = next;
}
/* Additionally, if there's a tap note lying at the end of our range, remove it,
* too. */
SetTapNote( iTrack, iEndRow, TAP_EMPTY );
// add a tap note at the start of this hold
SetTapNote( add.iTrack, iAddStartIndex, TAP_ORIGINAL_HOLD_HEAD ); // Hold begin marker. Don't draw this, but do grade it.
m_HoldNotes.push_back(add);
SetTapNote( iTrack, iStartRow, tn );
}
void NoteData::RemoveHoldNote( int iHoldIndex )
/* Return true if a hold note lies on or adjacent to the given spot. */
bool NoteData::IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow ) const
{
ASSERT( iHoldIndex >= 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);
}
/* Return true if a hold note lies on the given spot. Must be in 2sAnd3s. */
bool NoteData::IsHoldNoteAtBeat( int iTrack, int iRow, int *pHeadRow, int *pTailRow ) const
{
/* If neither the actual head nor tail row were requested, search for the head to
* determine the return value. */
int iDummy;
if( pHeadRow == NULL )
pHeadRow = &iDummy;
bool bFoundHead = false;
if( pHeadRow != NULL )
/* Starting at iRow, search upwards. If we find a TapNote::hold_head, we're within
* a hold. If we find a tap, mine or attack, we're not--those never lie within hold
* notes. Ignore autoKeysound. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( *this, iTrack, r, 0, iRow )
{
/* Starting at iRow, search upwards. If we find a TapNote::hold_head, we're within
* a hold. If we find a tap, mine or attack, we're not--those never lie within hold
* notes. Ignore autoKeysound. */
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE_REVERSE( *this, iTrack, r, 0, iRow )
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
case TapNote::hold_head:
*pHeadRow = r;
bFoundHead = true;
break;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
case TapNote::hold_tail:
case TapNote::hold_head:
// if( tn.iDuration + r <= iRow )
if( tn.iDuration + r < iRow )
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundHead )
break;
}
/* If we didn't find a matching head, we're not within a hold note, so don't bother
* searching for a tail. */
if( !bFoundHead )
return false;
}
bool bFoundTail = false;
if( pTailRow != NULL )
{
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, iTrack, r, iRow, MAX_NOTE_ROW )
{
const TapNote &tn = GetTapNote( iTrack, r );
switch( tn.type )
{
case TapNote::hold_tail:
*pTailRow = r;
bFoundTail = true;
break;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
return false;
case TapNote::hold_head:
/* If iRow is a hold head, we're within a hold note, and need to continue searching;
* if any row after that is a head, we're not in it, so stop. This is different
* than above, since holds are [head,tail); the row of the tail isn't actually
* part of the hold. */
if( r == iRow )
continue;
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundTail )
break;
*pHeadRow = r;
return true;
case TapNote::tap:
case TapNote::mine:
case TapNote::attack:
return false;
case TapNote::empty:
case TapNote::autoKeysound:
/* ignore */
continue;
default:
FAIL_M( ssprintf("%i", tn.type) );
}
if( bFoundHead )
break;
}
return bFoundHead || bFoundTail;
return bFoundHead;
}
int NoteData::GetFirstRow() const
@@ -482,13 +403,6 @@ int NoteData::GetFirstRow() const
iEarliestRowFoundSoFar = min( iEarliestRowFoundSoFar, iRow );
}
for( int i=0; i<GetNumHoldNotes(); i++ )
{
if( iEarliestRowFoundSoFar == -1 ||
GetHoldNote(i).iStartRow < iEarliestRowFoundSoFar )
iEarliestRowFoundSoFar = GetHoldNote(i).iStartRow;
}
if( iEarliestRowFoundSoFar == -1 ) // there are no notes
return 0;
@@ -504,13 +418,14 @@ int NoteData::GetLastRow() const
int iRow = MAX_NOTE_ROW;
if( !GetPrevTapNoteRowForTrack( t, iRow ) )
continue;
iOldestRowFoundSoFar = max( iOldestRowFoundSoFar, iRow );
}
for( int i=0; i<GetNumHoldNotes(); i++ )
{
if( GetHoldNote(i).iEndRow > iOldestRowFoundSoFar )
iOldestRowFoundSoFar = GetHoldNote(i).iEndRow;
/* XXX: We might have a hold note near the end with autoplay sounds
* after it. Do something else with autoplay sounds ... */
const TapNote &tn = GetTapNote( t, iRow );
if( tn.type == TapNote::hold_head )
iRow += tn.iDuration;
iOldestRowFoundSoFar = max( iOldestRowFoundSoFar, iRow );
}
return iOldestRowFoundSoFar;
@@ -576,7 +491,7 @@ int NoteData::RowNeedsHands( const int row ) const
{
case TapNote::mine:
case TapNote::empty:
case TapNote::hold_tail:
case TapNote::hold_head: // handled below
continue; // skip these types - they don't count
}
++iNumNotesThisIndex;
@@ -588,11 +503,10 @@ int NoteData::RowNeedsHands( const int row ) const
if( iNumNotesThisIndex < 3 )
{
/* We have at least one, but not enough. Count holds. */
for( int j=0; j<GetNumHoldNotes(); j++ )
/* We have at least one, but not enough. Count holds. Do count adjacent holds. */
for( int t=0; t<GetNumTracks(); ++t )
{
const HoldNote &hn = GetHoldNote(j);
if( hn.iStartRow+1 <= row && row <= hn.iEndRow )
if( IsHoldNoteAtBeat(t, row) )
++iNumNotesThisIndex;
}
}
@@ -641,82 +555,18 @@ int NoteData::GetNumN( int iMinTaps, int iStartIndex, int iEndIndex ) const
int NoteData::GetNumHoldNotes( int iStartIndex, int iEndIndex ) const
{
int iNumHolds = 0;
for( int i=0; i<GetNumHoldNotes(); i++ )
for( int t=0; t<GetNumTracks(); ++t )
{
const HoldNote &hn = GetHoldNote(i);
if( iStartIndex <= hn.iStartRow && hn.iEndRow <= iEndIndex )
iNumHolds++;
}
return iNumHolds;
}
void NoteData::Convert2sAnd3sToHoldNotes()
{
// Any note will end a hold (not just a TAP_HOLD_TAIL). This makes parsing DWIs much easier.
// Plus, allowing tap notes in the middle of a hold doesn't make sense!
for( int t=0; t<GetNumTracks(); t++ ) // foreach column
{
FOREACH_NONEMPTY_ROW_IN_TRACK( *this, t, r )
const_iterator begin, end;
GetTapNoteRangeExclusive( t, iStartIndex, iEndIndex, begin, end );
for( ; begin != end; ++begin )
{
TapNote head = GetTapNote(t,r);
if( head.type != TapNote::hold_head )
continue; // skip
SetTapNote(t, r, TAP_EMPTY); // clear the hold head marker
// search for end of HoldNote
FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE( *this, t, j, r+1, MAX_NOTE_ROW )
{
// End hold on the next note we see. This should be a hold_tail if the
// data is in a consistent state, but doesn't have to be.
if( GetTapNote(t, j).type == TapNote::empty )
continue;
SetTapNote(t, j, TAP_EMPTY);
HoldNote hold(t, r, j);
hold.result = head.HoldResult;
AddHoldNote( hold );
break; // done searching for the end of this hold
}
if( begin->second.type != TapNote::hold_head )
continue;
iNumHolds++;
}
}
}
/* "102000301" ==
* "104444001" */
void NoteData::ConvertHoldNotesTo2sAnd3s()
{
// copy HoldNotes into the new structure, but expand them into 2s and 3s
for( int i=0; i<GetNumHoldNotes(); i++ )
{
const HoldNote &hn = GetHoldNote(i);
/* If they're the same, then they got clamped together, so just ignore it. */
if( hn.iStartRow == hn.iEndRow )
continue;
TapNote head = TAP_ORIGINAL_HOLD_HEAD;
head.HoldResult = hn.result;
SetTapNote( hn.iTrack, hn.iStartRow, head );
SetTapNote( hn.iTrack, hn.iEndRow, TAP_ORIGINAL_HOLD_TAIL );
}
m_HoldNotes.clear();
}
void NoteData::To2sAnd3s( const NoteData& from )
{
CopyAll( from );
ConvertHoldNotesTo2sAnd3s();
}
void NoteData::From2sAnd3s( const NoteData& from )
{
CopyAll( from );
Convert2sAnd3sToHoldNotes();
return iNumHolds;
}
// -1 for iOriginalTracksToTakeFrom means no track
@@ -726,10 +576,6 @@ void NoteData::LoadTransformed( const NoteData& in, int iNewNumTracks, const int
Init();
SetNumTracks( iNewNumTracks );
ConvertHoldNotesTo2sAnd3s();
NoteData Input;
Input.To2sAnd3s( in );
// copy tracks
for( int t=0; t<GetNumTracks(); t++ )
@@ -740,10 +586,8 @@ void NoteData::LoadTransformed( const NoteData& in, int iNewNumTracks, const int
if( iOriginalTrack == -1 )
continue;
m_TapNotes[t] = Input.m_TapNotes[iOriginalTrack];
m_TapNotes[t] = in.m_TapNotes[iOriginalTrack];
}
Convert2sAnd3sToHoldNotes();
}
void NoteData::MoveTapNoteTrack( int dest, int src )
@@ -779,12 +623,9 @@ void NoteData::SetTapNote( int track, int row, const TapNote& t )
void NoteData::GetTracksHeldAtRow( int row, set<int>& addTo )
{
for( unsigned i=0; i<m_HoldNotes.size(); i++ )
{
const HoldNote& hn = m_HoldNotes[i];
if( hn.RowIsInRange(row) )
addTo.insert( hn.iTrack );
}
for( int t=0; t<GetNumTracks(); ++t )
if( IsHoldNoteAtBeat( t, row ) )
addTo.insert( t );
}
int NoteData::GetNumTracksHeldAtRow( int row )
@@ -832,21 +673,117 @@ bool NoteData::GetPrevTapNoteRowForTrack( int track, int &rowInOut ) const
return true;
}
/* Return an iterator range. This can be used to iterate trackwise over a range of
* notes. It's like FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE, except it only requires
* two map searches (iterating is O(1)), but the iterators will become invalid if
* the notes they represent disappear, so you need to pay attention to how you modify
* the data. */
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
/* Return an iterator range for [rowBegin,rowEnd). This can be used to efficiently
* iterate trackwise over a range of notes. It's like FOREACH_NONEMPTY_ROW_IN_TRACK_RANGE,
* except it only requires two map searches (iterating is constant time), but the iterators will
* become invalid if the notes they represent disappear, so you need to pay attention to
* how you modify the data. */
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::iterator &begin, TrackMap::iterator &end )
{
ASSERT_M( iTrack < GetNumTracks(), ssprintf("%i,%i", iTrack, GetNumTracks()) );
ASSERT_M( iStartRow <= iEndRow, ssprintf("%i > %i", iStartRow, iEndRow) );
TrackMap &mapTrack = m_TapNotes[iTrack];
const TrackMap &mapTrack = m_TapNotes[iTrack];
begin = mapTrack.lower_bound( iStartRow );
end = mapTrack.upper_bound( iEndRow );
if( iStartRow > iEndRow )
{
begin = end = mapTrack.end();
return;
}
if( iStartRow == 0 )
begin = mapTrack.begin(); /* optimization */
else if( iStartRow == MAX_NOTE_ROW )
begin = mapTrack.end(); /* optimization */
else
begin = mapTrack.lower_bound( iStartRow );
if( iEndRow == 0 )
end = mapTrack.begin(); /* optimization */
else if( iEndRow == MAX_NOTE_ROW )
end = mapTrack.end(); /* optimization */
else
end = mapTrack.lower_bound( iEndRow );
}
/* Include hold notes that overlap the edges. If a hold note completely surrounds the given
* range, included it, too. If bIncludeAdjacent is true, also include hold notes adjacent to,
* but not overlapping, the edge. */
void NoteData::GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end, bool bIncludeAdjacent )
{
GetTapNoteRange( iTrack, iStartRow, iEndRow, begin, end );
if( begin != this->begin(iTrack) )
{
iterator prev = Decrement(begin);
const TapNote &tn = prev->second;
if( tn.type == TapNote::hold_head )
{
int iHoldStartRow = prev->first;
int iHoldEndRow = iHoldStartRow + tn.iDuration;
if( iHoldEndRow > iStartRow || (bIncludeAdjacent && iHoldEndRow == iStartRow ) )
{
/* The previous note is a hold. */
begin = prev;
}
}
}
if( bIncludeAdjacent && end != this->end(iTrack) )
{
/* Include the next note if it's a hold and starts on iEndRow. */
const TapNote &tn = end->second;
int iHoldStartRow = end->first;
if( tn.type == TapNote::hold_head && iHoldStartRow == iEndRow )
++end;
}
}
void NoteData::GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, iterator &begin, iterator &end )
{
GetTapNoteRange( iTrack, iStartRow, iEndRow, begin, end );
/* If end-1 is a hold_head, and extends beyond iEndRow, exclude it. */
if( begin != end && end != this->begin(iTrack) )
{
iterator prev = end;
--prev;
if( prev->second.type == TapNote::hold_head )
{
int iStartRow = prev->first;
const TapNote &tn = prev->second;
if( iStartRow + tn.iDuration >= iEndRow )
end = prev;
}
}
}
void NoteData::GetTapNoteRange( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRange( iTrack, iStartRow, iEndRow, const_begin, const_end );
begin = const_begin;
end = const_end;
}
void NoteData::GetTapNoteRangeInclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end, bool bIncludeAdjacent ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRangeInclusive( iTrack, iStartRow, iEndRow, const_begin, const_end, bIncludeAdjacent );
begin = const_begin;
end = const_end;
}
void NoteData::GetTapNoteRangeExclusive( int iTrack, int iStartRow, int iEndRow, TrackMap::const_iterator &begin, TrackMap::const_iterator &end ) const
{
TrackMap::iterator const_begin, const_end;
const_cast<NoteData *>(this)->GetTapNoteRange( iTrack, iStartRow, iEndRow, const_begin, const_end );
begin = const_begin;
end = const_end;
}
bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const
{
int iClosestNextRow = MAX_NOTE_ROW;
@@ -873,6 +810,68 @@ bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const
}
}
void NoteData::InsertHoldTails()
{
for( int t=0; t<GetNumTracks(); t++ )
{
iterator begin, end;
GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
for( ; begin != end; ++begin )
{
int iRow = begin->first;
const TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
TapNote tail = tn;
tail.type = TapNote::hold_tail;
SetTapNote( t, iRow + tn.iDuration, tail );
}
}
}
void NoteData::RemoveHoldTails()
{
for( int t=0; t<GetNumTracks(); t++ )
{
iterator begin, end;
GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end );
iterator next;
for( ; begin != end; begin = next )
{
next = begin; ++next;
int iStartRow = begin->first;
const TapNote &tn = begin->second;
if( tn.type != TapNote::hold_head )
continue;
/* Search forward until we find a hold_tail. */
iterator tail = begin;
while( tail != end && tail->second.type != TapNote::hold_tail )
++tail;
if( tail == end )
{
/* If we didn't find one, the hold is invalid; delete it. */
RemoveTapNote( t, begin );
}
else
{
/* Delete the tail, and update iDuration of the head. */
if( next == tail )
++next;
int iEndRow = tail->first;
begin->second.iDuration = iEndRow - iStartRow;
RemoveTapNote( t, tail );
}
}
}
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.