#include "global.h" /* ----------------------------------------------------------------------------- Class: NoteDataUtil Desc: See header. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Chris Danford ----------------------------------------------------------------------------- */ #include "NoteDataUtil.h" #include "RageUtil.h" #include "RageLog.h" #include "PlayerOptions.h" 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= NOTE_TYPE_12TH ) iNumChaosNotes++; } float fReturn = iNumChaosNotes / fSongSeconds * 0.5f; return min( fReturn, 1.0f ); } void NoteDataUtil::RemoveHoldNotes(NoteData &in) { vector tracks, rows; // turn all the HoldNotes into TapNotes for( int i=0; i aiTracksLeftToMap; for( t=0; t=0; i-- ) if( fmodf(in.GetHoldNote(i).fStartBeat,1) != 0 ) // doesn't start on a beat in.RemoveHoldNote( i ); } void NoteDataUtil::Wide( NoteData &in ) { in.ConvertHoldNotesTo4s(); // make all all quarter notes jumps int max_row = in.GetLastRow(); for( int i=0; i<=max_row; i+=ROWS_PER_BEAT*2 ) // every even beat { if( in.GetNumTapNonEmptyTracks(i) != 1 ) continue; // skip. Don't place during holds if( in.GetNumTracksWithTap(i) != 1 ) continue; // skip bool bSpaceAroundIsEmpty = true; // no other notes with a 1/8th of this row for( int j=i-ROWS_PER_BEAT/2+1; j 0 ) { bSpaceAroundIsEmpty = false; break; } if( !bSpaceAroundIsEmpty ) continue; // skip // add a note determinitsitcally int iBeat = (int)roundf( NoteRowToBeat(i) ); int iTrackOfNote = in.GetFirstTrackWithTap(i); int iTrackToAdd = iTrackOfNote + (iBeat%5)-2; // won't be more than 2 tracks away from the existing note CLAMP( iTrackToAdd, 0, in.GetNumTracks()-1 ); if( iTrackToAdd == iTrackOfNote ) iTrackToAdd++; CLAMP( iTrackToAdd, 0, in.GetNumTracks()-1 ); if( iTrackToAdd == iTrackOfNote ) iTrackToAdd--; CLAMP( iTrackToAdd, 0, in.GetNumTracks()-1 ); if( in.GetTapNote(iTrackToAdd, i) != TAP_EMPTY ) { iTrackToAdd = (iTrackToAdd+1) % in.GetNumTracks(); } in.SetTapNote(iTrackToAdd, i, TAP_ADDITION); } in.Convert4sToHoldNotes(); } void NoteDataUtil::Big( NoteData &in ) { InsertIntelligentTaps(in,1.0f,0.5f,false); // add 8ths between 4ths } void NoteDataUtil::Quick( NoteData &in ) { InsertIntelligentTaps(in,0.5f,0.25f,false); // add 16ths between 8ths } void NoteDataUtil::Skippy( NoteData &in ) { InsertIntelligentTaps(in,1.0f,0.75f,true); // add 16ths between 4ths } void NoteDataUtil::InsertIntelligentTaps( NoteData &in, float fBeatInterval, float fInsertBeatOffset, bool bNewTapSameAsBeginning ) { ASSERT( fInsertBeatOffset <= fBeatInterval ); in.ConvertHoldNotesTo4s(); // insert a beat in the middle of every fBeatInterval int max_row = in.GetLastRow(); int rows_per_interval = BeatToNoteRow( fBeatInterval ); int insert_row_offset = BeatToNoteRow( fInsertBeatOffset ); for( int i=0; i<=max_row; i+=rows_per_interval ) { int iRowEarlier = i; int iRowLater = i + rows_per_interval; int iRowToAdd = i + insert_row_offset; if( in.GetNumTapNonEmptyTracks(iRowEarlier)!=1 || in.GetNumTracksWithTap(iRowEarlier)!=1 ) continue; if( in.GetNumTapNonEmptyTracks(iRowLater)!=1 || in.GetNumTracksWithTap(iRowLater)!=1 ) continue; // 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; for( int j=iRowEarlier+1; j<=iRowLater-1; j++ ) if( !in.IsRowEmpty(j) ) { bNoteInMiddle = true; break; } if( bNoteInMiddle ) continue; // add a note determinitsitcally somewhere on a track different from the two surrounding notes int iTrackOfNoteEarlier = in.GetFirstNonEmptyTrack(iRowEarlier); int iTrackOfNoteLater = in.GetFirstNonEmptyTrack(iRowLater); int iTrackOfNoteToAdd = 0; if( bNewTapSameAsBeginning && iTrackOfNoteEarlier != iTrackOfNoteLater ) // Don't make skips on the same note { iTrackOfNoteToAdd = iTrackOfNoteEarlier; } else // bNewTapSameAsBeginning { // choose a randomish track if( abs(iTrackOfNoteEarlier-iTrackOfNoteLater) == 2 ) iTrackOfNoteToAdd = (iTrackOfNoteEarlier+iTrackOfNoteLater)/2; else { for( int t=min(iTrackOfNoteEarlier,iTrackOfNoteLater)-1; t<=max(iTrackOfNoteEarlier,iTrackOfNoteLater)+1; t++ ) { iTrackOfNoteToAdd = t; CLAMP( iTrackOfNoteToAdd, 0, in.GetNumTracks()-1 ); if( iTrackOfNoteToAdd!=iTrackOfNoteEarlier && iTrackOfNoteToAdd!=iTrackOfNoteLater ) break; } } } in.SetTapNote(iTrackOfNoteToAdd, iRowToAdd, TAP_ADDITION); } in.Convert4sToHoldNotes(); } void NoteDataUtil::SnapToNearestNoteType( NoteData &in, NoteType nt1, NoteType nt2, float fBeginBeat, float fEndBeat ) { // nt2 is optional and should be -1 if it is not used float fSnapInterval1, fSnapInterval2; switch( nt1 ) { case NOTE_TYPE_4TH: fSnapInterval1 = 1/1.0f; break; case NOTE_TYPE_8TH: fSnapInterval1 = 1/2.0f; break; case NOTE_TYPE_12TH: fSnapInterval1 = 1/3.0f; break; case NOTE_TYPE_16TH: fSnapInterval1 = 1/4.0f; break; case NOTE_TYPE_24TH: fSnapInterval1 = 1/6.0f; break; case NOTE_TYPE_32ND: fSnapInterval1 = 1/8.0f; break; default: ASSERT( false ); return; } switch( nt2 ) { case NOTE_TYPE_4TH: fSnapInterval2 = 1/1.0f; break; case NOTE_TYPE_8TH: fSnapInterval2 = 1/2.0f; break; case NOTE_TYPE_12TH: fSnapInterval2 = 1/3.0f; break; case NOTE_TYPE_16TH: fSnapInterval2 = 1/4.0f; break; case NOTE_TYPE_24TH: fSnapInterval2 = 1/6.0f; break; case NOTE_TYPE_32ND: fSnapInterval2 = 1/8.0f; break; case -1: fSnapInterval2 = 10000; break; // nothing will ever snap to this. That's what we want! default: ASSERT( false ); return; } int iNoteIndexBegin = BeatToNoteRow( fBeginBeat ); int iNoteIndexEnd = BeatToNoteRow( fEndBeat ); in.ConvertHoldNotesTo2sAnd3s(); // iterate over all TapNotes in the interval and snap them for( int i=iNoteIndexBegin; i<=iNoteIndexEnd; i++ ) { LOG->Trace("index %i", i); int iOldIndex = i; float fOldBeat = NoteRowToBeat( iOldIndex ); float fNewBeat1 = froundf( fOldBeat, fSnapInterval1 ); float fNewBeat2 = froundf( fOldBeat, fSnapInterval2 ); LOG->Trace("oldbeat %.2f, nb1 %.2f, nb2 %.2f", fOldBeat, fNewBeat1, fNewBeat2); bool bNewBeat1IsCloser = fabsf(fNewBeat1-fOldBeat) < fabsf(fNewBeat2-fOldBeat); float fNewBeat = bNewBeat1IsCloser ? fNewBeat1 : fNewBeat2; int iNewIndex = BeatToNoteRow( fNewBeat ); LOG->Trace("closer %i, newbeat %.2f, index %i", bNewBeat1IsCloser, fNewBeat, iNewIndex); for( int c=0; c0; t-- ) // in.GetNumTracks()-1 times { int iTrackEarlier = t; int iTrackLater = (t+1) % in.GetNumTracks(); // swap TapNote tnEarlier = in.GetTapNote(iTrackEarlier, r); TapNote tnLater = in.GetTapNote(iTrackLater, r); in.SetTapNote(iTrackEarlier, r, tnLater); in.SetTapNote(iTrackLater, r, tnEarlier); } } in.Convert4sToHoldNotes(); } 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 } }, }; void NoteDataUtil::FixImpossibleRows( NoteData &in, StepsType st ) { for( unsigned i=0; i