2011-03-17 01:47:30 -04:00
/*
2023-04-19 14:22:59 +02:00
* AdjustSync defines two methods for fixing the sync.
2011-03-17 01:47:30 -04:00
*
2023-04-19 14:22:59 +02:00
* The first method adjusts either the song or the machine by the
2011-03-17 01:47:30 -04:00
* average offset of the user's steps. In other words, if the user
* averages to step early by 10 ms, either the song or the global
2023-04-19 14:22:59 +02:00
* offset is adjusted by 10 ms to compensate for that. These
2011-03-17 01:47:30 -04:00
* adjustments only require a small set of data, so this method
* updates the offset while the song is playing.
*
* The second method adjusts both the offset and the tempo of an
* individual song. It records all of the steps during a play of
* the song and uses linear least squares regression to minimize the
2023-04-19 14:22:59 +02:00
* error of those steps. It makes one adjustment for the tempo of
2011-03-17 01:47:30 -04:00
* the entire song, rather than adding many different tempo segments
* to match the steps. If there are already several tempo segments
* in the stepfile, this method makes a proportional change to each
* of them. For example, if it changes 100 bpm to 101 bpm, it will
* also change 200 bpm to 202 bpm. This method also adjusts the stops.
* It assumes that a given stop is measured in terms of beats and makes
* the appropriate change.
2023-04-19 14:22:59 +02:00
*
* If we use this method on a small set of data late in the song, it
2011-03-17 01:47:30 -04:00
* can have very chaotic effects on the early settings. For example,
* it may change the offset by several hundred milliseconds and make a
* large change to the BPM to compensate if that would minimize the
* error. This problem occurs especially when the user makes a couple
* steps that are significantly off beat. The way to avoid this is to
* perform the least squares regression once on all of the data
* collected, rather than adjusting the sync every time we get another
* 50 or so data points. In fact, if we are playing in edit mode and
2023-04-19 14:22:59 +02:00
* the user loops through the song more than once, we use all of the
2011-03-17 01:47:30 -04:00
* steps made.
*/
# include "global.h"
# include "Song.h"
2011-05-26 14:46:08 -04:00
# include "Steps.h"
2011-03-17 01:47:30 -04:00
# include "AdjustSync.h"
# include "GameState.h"
# include "LocalizedString.h"
# include "PrefsManager.h"
# include "ScreenManager.h"
2019-06-22 12:35:38 -07:00
2023-04-19 14:22:59 +02:00
# include <cmath>
2023-04-19 23:04:25 +02:00
# include <cstddef>
2023-04-20 19:02:13 +02:00
# include <vector>
2023-04-19 14:22:59 +02:00
2011-03-17 01:47:30 -04:00
2022-07-10 18:28:56 +03:00
std : : vector < TimingData > AdjustSync : : s_vpTimingDataOriginal ;
2011-03-17 01:47:30 -04:00
float AdjustSync : : s_fGlobalOffsetSecondsOriginal = 0.0f ;
int AdjustSync : : s_iAutosyncOffsetSample = 0 ;
float AdjustSync : : s_fAutosyncOffset [ AdjustSync : : OFFSET_SAMPLE_COUNT ] ;
float AdjustSync : : s_fStandardDeviation = 0.0f ;
2022-07-10 18:28:56 +03:00
std : : vector < std : : pair < float , float > > AdjustSync : : s_vAutosyncTempoData ;
2011-03-17 01:47:30 -04:00
float AdjustSync : : s_fAverageError = 0.0f ;
const float AdjustSync : : ERROR_TOO_HIGH = 0.025f ;
int AdjustSync : : s_iStepsFiltered = 0 ;
void AdjustSync : : ResetOriginalSyncData ( )
{
2011-05-26 22:00:37 -04:00
s_vpTimingDataOriginal . clear ( ) ;
2011-05-19 13:58:17 -04:00
2011-03-17 01:47:30 -04:00
if ( GAMESTATE - > m_pCurSong )
2011-05-26 14:35:54 -04:00
{
2011-05-26 22:00:37 -04:00
s_vpTimingDataOriginal . push_back ( GAMESTATE - > m_pCurSong - > m_SongTiming ) ;
2022-07-10 18:28:56 +03:00
const std : : vector < Steps * > & vpSteps = GAMESTATE - > m_pCurSong - > GetAllSteps ( ) ;
2019-06-22 12:35:38 -07:00
for ( Steps const * s : vpSteps )
2011-05-26 15:10:22 -04:00
{
2019-06-22 12:35:38 -07:00
s_vpTimingDataOriginal . push_back ( s - > m_Timing ) ;
2011-05-26 15:10:22 -04:00
}
2011-05-26 14:35:54 -04:00
}
2011-03-17 01:47:30 -04:00
else
2011-05-26 14:35:54 -04:00
{
2011-05-26 22:00:37 -04:00
s_vpTimingDataOriginal . push_back ( TimingData ( ) ) ;
2011-05-26 14:35:54 -04:00
}
2011-03-17 01:47:30 -04:00
s_fGlobalOffsetSecondsOriginal = PREFSMAN - > m_fGlobalOffsetSeconds ;
ResetAutosync ( ) ;
}
void AdjustSync : : ResetAutosync ( )
{
s_iAutosyncOffsetSample = 0 ;
s_vAutosyncTempoData . clear ( ) ;
}
bool AdjustSync : : IsSyncDataChanged ( )
{
// Can't sync in course modes
if ( GAMESTATE - > IsCourseMode ( ) )
return false ;
2022-07-10 18:28:56 +03:00
std : : vector < RString > vs ;
2011-03-17 01:47:30 -04:00
AdjustSync : : GetSyncChangeTextGlobal ( vs ) ;
AdjustSync : : GetSyncChangeTextSong ( vs ) ;
return ! vs . empty ( ) ;
}
void AdjustSync : : SaveSyncChanges ( )
{
if ( GAMESTATE - > IsCourseMode ( ) )
return ;
2011-09-18 21:02:29 -05:00
2011-05-26 14:19:13 -04:00
/* TODO: Save all of the timing data changes.
* Luckily, only the song timing data needs comparing here. */
2011-05-26 22:00:37 -04:00
if ( GAMESTATE - > m_pCurSong & & s_vpTimingDataOriginal [ 0 ] ! = GAMESTATE - > m_pCurSong - > m_SongTiming )
2011-03-17 01:47:30 -04:00
{
if ( GAMESTATE - > IsEditing ( ) )
{
MESSAGEMAN - > Broadcast ( Message_SongModified ) ;
}
else
{
GAMESTATE - > m_pCurSong - > Save ( ) ;
}
}
if ( s_fGlobalOffsetSecondsOriginal ! = PREFSMAN - > m_fGlobalOffsetSeconds )
PREFSMAN - > SavePrefsToDisk ( ) ;
ResetOriginalSyncData ( ) ;
s_fStandardDeviation = 0.0f ;
s_fAverageError = 0.0f ;
}
void AdjustSync : : RevertSyncChanges ( )
{
if ( GAMESTATE - > IsCourseMode ( ) )
return ;
PREFSMAN - > m_fGlobalOffsetSeconds . Set ( s_fGlobalOffsetSecondsOriginal ) ;
2011-09-18 21:02:29 -05:00
2011-05-26 15:38:15 -04:00
// The first one is ALWAYS the song timing.
2011-05-26 22:00:37 -04:00
GAMESTATE - > m_pCurSong - > m_SongTiming = s_vpTimingDataOriginal [ 0 ] ;
2011-09-18 21:02:29 -05:00
2011-05-26 15:38:15 -04:00
unsigned location = 1 ;
2022-07-10 18:28:56 +03:00
const std : : vector < Steps * > & vpSteps = GAMESTATE - > m_pCurSong - > GetAllSteps ( ) ;
2019-06-22 12:35:38 -07:00
for ( Steps * s : vpSteps )
2011-05-26 15:38:15 -04:00
{
2019-06-22 12:35:38 -07:00
s - > m_Timing = s_vpTimingDataOriginal [ location ] ;
2011-05-26 15:38:15 -04:00
location + + ;
}
2011-09-18 21:02:29 -05:00
2011-03-17 01:47:30 -04:00
ResetOriginalSyncData ( ) ;
s_fStandardDeviation = 0.0f ;
s_fAverageError = 0.0f ;
}
static LocalizedString AUTOSYNC_CORRECTION_APPLIED ( " AdjustSync " , " Autosync: Correction applied. " ) ;
static LocalizedString AUTOSYNC_CORRECTION_NOT_APPLIED ( " AdjustSync " , " Autosync: Correction NOT applied. Deviation too high. " ) ;
void AdjustSync : : HandleAutosync ( float fNoteOffBySeconds , float fStepTime )
{
if ( GAMESTATE - > IsCourseMode ( ) )
return ;
2014-05-03 19:27:32 -06:00
AutosyncType type = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_AutosyncType ;
2012-12-27 16:59:35 -05:00
switch ( type ) {
2014-05-03 19:27:32 -06:00
case AutosyncType_Off :
2011-03-17 01:47:30 -04:00
return ;
2014-05-03 19:27:32 -06:00
case AutosyncType_Tempo :
2011-03-17 01:47:30 -04:00
{
// We collect all of the data and process it at the end
2022-07-10 18:28:56 +03:00
s_vAutosyncTempoData . push_back ( std : : make_pair ( fStepTime , fNoteOffBySeconds ) ) ;
2011-03-17 01:47:30 -04:00
break ;
}
2014-05-03 19:27:32 -06:00
case AutosyncType_Machine :
case AutosyncType_Song :
2011-03-17 01:47:30 -04:00
{
s_fAutosyncOffset [ s_iAutosyncOffsetSample ] = fNoteOffBySeconds ;
+ + s_iAutosyncOffsetSample ;
2023-04-19 14:22:59 +02:00
if ( s_iAutosyncOffsetSample < OFFSET_SAMPLE_COUNT )
2011-03-17 01:47:30 -04:00
break ; // need more
AutosyncOffset ( ) ;
break ;
2011-09-18 21:02:29 -05:00
}
2011-03-17 01:47:30 -04:00
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( " Invalid autosync type: %i " , type ) ) ;
2011-03-17 01:47:30 -04:00
}
}
void AdjustSync : : HandleSongEnd ( )
{
if ( GAMESTATE - > IsCourseMode ( ) )
return ;
2014-05-03 19:27:32 -06:00
if ( GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_AutosyncType = = AutosyncType_Tempo )
2011-03-17 01:47:30 -04:00
{
AutosyncTempo ( ) ;
}
// all other states don't care
}
void AdjustSync : : AutosyncOffset ( )
{
const float mean = calc_mean ( s_fAutosyncOffset , s_fAutosyncOffset + OFFSET_SAMPLE_COUNT ) ;
const float stddev = calc_stddev ( s_fAutosyncOffset , s_fAutosyncOffset + OFFSET_SAMPLE_COUNT ) ;
2014-05-03 19:27:32 -06:00
AutosyncType type = GAMESTATE - > m_SongOptions . GetCurrent ( ) . m_AutosyncType ;
2011-03-17 01:47:30 -04:00
if ( stddev < .03f ) // If they stepped with less than .03 error
{
2012-12-27 16:59:35 -05:00
switch ( type )
2011-03-17 01:47:30 -04:00
{
2014-05-03 19:27:32 -06:00
case AutosyncType_Song :
2011-05-26 14:57:28 -04:00
{
GAMESTATE - > m_pCurSong - > m_SongTiming . m_fBeat0OffsetInSeconds + = mean ;
2022-07-10 18:28:56 +03:00
const std : : vector < Steps * > & vpSteps = GAMESTATE - > m_pCurSong - > GetAllSteps ( ) ;
2019-06-22 12:35:38 -07:00
for ( Steps * s : vpSteps )
2011-05-26 14:57:28 -04:00
{
2013-01-23 14:51:18 -05:00
// Empty TimingData means it's inherited
// from the song and is already changed.
2019-06-22 12:35:38 -07:00
if ( s - > m_Timing . empty ( ) )
2013-01-23 14:51:18 -05:00
continue ;
2019-06-22 12:35:38 -07:00
s - > m_Timing . m_fBeat0OffsetInSeconds + = mean ;
2011-05-26 14:57:28 -04:00
}
break ;
}
2014-05-03 19:27:32 -06:00
case AutosyncType_Machine :
2011-05-26 14:57:28 -04:00
// Step timing is not needed for this operation.
PREFSMAN - > m_fGlobalOffsetSeconds . Set ( PREFSMAN - > m_fGlobalOffsetSeconds + mean ) ;
break ;
default :
2012-12-27 16:59:35 -05:00
FAIL_M ( ssprintf ( " Invalid autosync type: %i " , type ) ) ;
2011-03-17 01:47:30 -04:00
}
SCREENMAN - > SystemMessage ( AUTOSYNC_CORRECTION_APPLIED . GetValue ( ) ) ;
}
else
{
SCREENMAN - > SystemMessage ( AUTOSYNC_CORRECTION_NOT_APPLIED . GetValue ( ) ) ;
}
s_iAutosyncOffsetSample = 0 ;
s_fStandardDeviation = stddev ;
}
void AdjustSync : : AutosyncTempo ( )
{
float fSlope = 0.0f ;
float fIntercept = 0.0f ;
if ( ! CalcLeastSquares ( s_vAutosyncTempoData , fSlope , fIntercept , s_fAverageError ) )
{
s_vAutosyncTempoData . clear ( ) ;
return ;
}
if ( s_fAverageError < ERROR_TOO_HIGH )
{
2011-09-18 21:02:29 -05:00
/* Here we filter out any steps that are too far off.
* If it turns out that we want to be even more selective, we can keep
* only a fraction of the data, such as the 80% with the lowest error.
* However, throwing away the ones with high error should be enough
* in most cases. */
2011-06-12 03:37:10 -04:00
float fFilteredError = 0 ;
2011-03-17 01:47:30 -04:00
s_iStepsFiltered = s_vAutosyncTempoData . size ( ) ;
FilterHighErrorPoints ( s_vAutosyncTempoData , fSlope , fIntercept , ERROR_TOO_HIGH ) ;
s_iStepsFiltered - = s_vAutosyncTempoData . size ( ) ;
if ( ! CalcLeastSquares ( s_vAutosyncTempoData , fSlope , fIntercept , fFilteredError ) )
return ;
2011-05-09 21:03:30 -04:00
GAMESTATE - > m_pCurSong - > m_SongTiming . m_fBeat0OffsetInSeconds + = fIntercept ;
2011-03-17 01:47:30 -04:00
const float fScaleBPM = 1.0f / ( 1.0f - fSlope ) ;
2011-07-14 13:10:02 -04:00
TimingData & timing = GAMESTATE - > m_pCurSong - > m_SongTiming ;
2011-09-15 03:28:58 +00:00
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & bpms = timing . GetTimingSegments ( SEGMENT_BPM ) ;
2011-07-14 13:10:02 -04:00
for ( unsigned i = 0 ; i < bpms . size ( ) ; i + + )
{
2011-09-15 03:28:58 +00:00
const BPMSegment * b = ToBPM ( bpms [ i ] ) ;
timing . AddSegment ( BPMSegment ( b - > GetRow ( ) , b - > GetBPM ( ) * fScaleBPM ) ) ;
2011-07-14 13:10:02 -04:00
}
2011-03-17 01:47:30 -04:00
2011-06-01 09:50:34 -04:00
/* We assume that the stops were measured as a number of beats.
* Therefore, if we change the bpms, we need to make a similar
* change to the stops. */
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & stops = timing . GetTimingSegments ( SEGMENT_STOP ) ;
2011-07-14 13:10:02 -04:00
for ( unsigned i = 0 ; i < stops . size ( ) ; i + + )
{
2011-09-15 03:28:58 +00:00
const StopSegment * s = ToStop ( stops [ i ] ) ;
timing . AddSegment ( StopSegment ( s - > GetRow ( ) , s - > GetPause ( ) * ( 1.0f - fSlope ) ) ) ;
2011-07-14 13:10:02 -04:00
}
2011-07-27 23:03:33 -04:00
// Do the same for delays.
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & delays = timing . GetTimingSegments ( SEGMENT_DELAY ) ;
2011-07-27 23:03:33 -04:00
for ( unsigned i = 0 ; i < delays . size ( ) ; i + + )
{
2011-09-15 03:28:58 +00:00
const DelaySegment * s = ToDelay ( delays [ i ] ) ;
timing . AddSegment ( DelaySegment ( s - > GetRow ( ) , s - > GetPause ( ) * ( 1.0f - fSlope ) ) ) ;
2011-07-27 23:03:33 -04:00
}
2011-03-17 01:47:30 -04:00
SCREENMAN - > SystemMessage ( AUTOSYNC_CORRECTION_APPLIED . GetValue ( ) ) ;
}
else
{
// deviation... error... close enough for an error message
SCREENMAN - > SystemMessage ( AUTOSYNC_CORRECTION_NOT_APPLIED . GetValue ( ) ) ;
}
s_vAutosyncTempoData . clear ( ) ;
}
static LocalizedString EARLIER ( " AdjustSync " , " earlier " ) ;
static LocalizedString LATER ( " AdjustSync " , " later " ) ;
static LocalizedString GLOBAL_OFFSET_FROM ( " AdjustSync " , " Global Offset from %+.3f to %+.3f (notes %s) " ) ;
// We need to limit the length of lines so each one fits on one line of the SM console.
// The tempo and stop change message can get very long in a complicated song, and at
// a low resolution, the keep/revert menu would be pushed off the bottom of the screen
2023-04-19 14:22:59 +02:00
// if we didn't limit the length of the message. Keeping the lines short lets us fit
2011-03-17 01:47:30 -04:00
// more information on the screen.
static LocalizedString SONG_OFFSET_FROM ( " AdjustSync " , " Song offset from %+.3f to %+.3f (notes %s) " ) ;
static LocalizedString TEMPO_SEGMENT_FROM ( " AdjustSync " , " %s BPM from %.3f BPM to %.3f BPM. " ) ;
static LocalizedString CHANGED_STOP ( " AdjustSync " , " The stop segment #%d changed from %+.3fs to %+.3fs (change of %+.3f) . " ) ;
static LocalizedString ERROR ( " AdjustSync " , " Average Error %.5fs " ) ;
static LocalizedString ETC ( " AdjustSync " , " Etc. " ) ;
static LocalizedString TAPS_IGNORED ( " AdjustSync " , " %d taps ignored. " ) ;
2022-07-10 18:28:56 +03:00
void AdjustSync : : GetSyncChangeTextGlobal ( std : : vector < RString > & vsAddTo )
2011-03-17 01:47:30 -04:00
{
{
float fOld = Quantize ( AdjustSync : : s_fGlobalOffsetSecondsOriginal , 0.001f ) ;
float fNew = Quantize ( PREFSMAN - > m_fGlobalOffsetSeconds , 0.001f ) ;
float fDelta = fNew - fOld ;
2023-04-19 14:22:59 +02:00
if ( std : : abs ( fDelta ) > 0.0001f )
2011-03-17 01:47:30 -04:00
{
2023-04-19 14:22:59 +02:00
vsAddTo . push_back ( ssprintf (
2011-03-17 01:47:30 -04:00
GLOBAL_OFFSET_FROM . GetValue ( ) ,
2011-09-18 21:02:29 -05:00
fOld , fNew ,
( fDelta > 0 ? EARLIER : LATER ) . GetValue ( ) . c_str ( ) ) ) ;
2011-03-17 01:47:30 -04:00
}
}
}
2011-09-15 03:28:58 +00:00
// XXX: needs cleanup still -- vyhd
2022-07-10 18:28:56 +03:00
void AdjustSync : : GetSyncChangeTextSong ( std : : vector < RString > & vsAddTo )
2011-03-17 01:47:30 -04:00
{
if ( GAMESTATE - > m_pCurSong . Get ( ) )
{
2015-02-13 02:17:49 -07:00
# define SEGMENTS_MISMATCH_MESSAGE(orig, test, segments_name) \
if(orig.size() != test.size()) \
{ \
LuaHelpers::ReportScriptError("The sync overlay's " #segments_name " segment list is a different size from the song's. Please report this bug with steps to reproduce it."); \
}
2011-03-17 01:47:30 -04:00
unsigned int iOriginalSize = vsAddTo . size ( ) ;
2011-09-15 03:28:58 +00:00
TimingData & original = s_vpTimingDataOriginal [ 0 ] ;
2011-05-26 18:36:30 -04:00
TimingData & testing = GAMESTATE - > m_pCurSong - > m_SongTiming ;
2011-03-17 01:47:30 -04:00
{
2011-05-26 22:00:37 -04:00
float fOld = Quantize ( original . m_fBeat0OffsetInSeconds , 0.001f ) ;
2011-05-26 18:36:30 -04:00
float fNew = Quantize ( testing . m_fBeat0OffsetInSeconds , 0.001f ) ;
2011-03-17 01:47:30 -04:00
float fDelta = fNew - fOld ;
2023-04-19 14:22:59 +02:00
if ( std : : abs ( fDelta ) > 0.0001f )
2011-03-17 01:47:30 -04:00
{
2023-04-19 14:22:59 +02:00
vsAddTo . push_back ( ssprintf (
2011-03-17 01:47:30 -04:00
SONG_OFFSET_FROM . GetValue ( ) ,
2023-04-19 14:22:59 +02:00
fOld ,
2011-03-17 01:47:30 -04:00
fNew ,
( fDelta > 0 ? EARLIER : LATER ) . GetValue ( ) . c_str ( ) ) ) ;
}
}
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & bpmTest = testing . GetTimingSegments ( SEGMENT_BPM ) ;
const std : : vector < TimingSegment * > & bpmOrig = original . GetTimingSegments ( SEGMENT_BPM ) ;
2015-02-13 02:17:49 -07:00
SEGMENTS_MISMATCH_MESSAGE ( bpmOrig , bpmTest , bpm ) ;
2024-10-01 00:43:07 -07:00
for ( size_t i = 0 ; i < bpmTest . size ( ) & & i < bpmOrig . size ( ) ; i + + )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
float fNew = Quantize ( ToBPM ( bpmTest [ i ] ) - > GetBPM ( ) , 0.001f ) ;
float fOld = Quantize ( ToBPM ( bpmOrig [ i ] ) - > GetBPM ( ) , 0.001f ) ;
2011-03-17 01:47:30 -04:00
2023-04-19 14:22:59 +02:00
if ( std : : abs ( fNew - fOld ) < 1e-4 )
2011-09-15 03:28:58 +00:00
continue ;
if ( i > = 4 )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
vsAddTo . push_back ( ETC . GetValue ( ) ) ;
break ;
2011-03-17 01:47:30 -04:00
}
2011-09-15 03:28:58 +00:00
RString s = ssprintf ( TEMPO_SEGMENT_FROM . GetValue ( ) ,
FormatNumberAndSuffix ( i + 1 ) . c_str ( ) , fOld , fNew ) ;
vsAddTo . push_back ( s ) ;
2011-03-17 01:47:30 -04:00
}
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & stopTest = testing . GetTimingSegments ( SEGMENT_STOP ) ;
const std : : vector < TimingSegment * > & stopOrig = original . GetTimingSegments ( SEGMENT_STOP ) ;
2011-09-15 03:28:58 +00:00
2015-02-13 02:17:49 -07:00
SEGMENTS_MISMATCH_MESSAGE ( stopOrig , stopTest , stop ) ;
2024-10-01 00:43:07 -07:00
for ( size_t i = 0 ; i < stopTest . size ( ) & & i < stopOrig . size ( ) ; i + + )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
float fOld = Quantize ( ToStop ( stopOrig [ i ] ) - > GetPause ( ) , 0.001f ) ;
float fNew = Quantize ( ToStop ( stopTest [ i ] ) - > GetPause ( ) , 0.001f ) ;
2011-03-17 01:47:30 -04:00
float fDelta = fNew - fOld ;
2023-04-19 14:22:59 +02:00
if ( std : : abs ( fDelta ) < 1e-4 )
2011-09-15 03:28:58 +00:00
continue ;
if ( i > = 4 )
2011-03-17 01:47:30 -04:00
{
2011-09-15 03:28:58 +00:00
vsAddTo . push_back ( ETC . GetValue ( ) ) ;
break ;
2011-03-17 01:47:30 -04:00
}
2011-09-15 03:28:58 +00:00
RString s = ssprintf ( CHANGED_STOP . GetValue ( ) , i + 1 , fOld , fNew , fDelta ) ;
vsAddTo . push_back ( s ) ;
2011-03-17 01:47:30 -04:00
}
2011-09-15 03:28:58 +00:00
2022-07-10 18:28:56 +03:00
const std : : vector < TimingSegment * > & delyTest = testing . GetTimingSegments ( SEGMENT_DELAY ) ;
const std : : vector < TimingSegment * > & delyOrig = original . GetTimingSegments ( SEGMENT_DELAY ) ;
2011-09-15 03:28:58 +00:00
2015-02-13 02:17:49 -07:00
SEGMENTS_MISMATCH_MESSAGE ( delyOrig , delyTest , delay ) ;
2024-10-01 00:43:07 -07:00
for ( size_t i = 0 ; i < delyTest . size ( ) & & i < delyOrig . size ( ) ; i + + )
2011-07-27 23:03:33 -04:00
{
2011-09-15 03:28:58 +00:00
if ( delyTest [ i ] = = delyOrig [ i ] )
continue ;
float fOld = Quantize ( ToDelay ( delyOrig [ i ] ) - > GetPause ( ) , 0.001f ) ;
float fNew = Quantize ( ToDelay ( delyTest [ i ] ) - > GetPause ( ) , 0.001f ) ;
2011-07-27 23:03:33 -04:00
float fDelta = fNew - fOld ;
2011-09-15 03:28:58 +00:00
2023-04-19 14:22:59 +02:00
if ( std : : abs ( fDelta ) < 1e-4 )
2011-09-15 03:28:58 +00:00
continue ;
if ( i > = 4 )
2011-07-27 23:03:33 -04:00
{
2011-09-15 03:28:58 +00:00
vsAddTo . push_back ( ETC . GetValue ( ) ) ;
break ;
2011-07-27 23:03:33 -04:00
}
2011-09-15 03:28:58 +00:00
RString s = ssprintf ( CHANGED_STOP . GetValue ( ) ,
i + 1 , fOld , fNew , fDelta ) ;
vsAddTo . push_back ( s ) ;
2011-07-27 23:03:33 -04:00
}
2011-03-17 01:47:30 -04:00
if ( vsAddTo . size ( ) > iOriginalSize & & s_fAverageError > 0.0f )
{
vsAddTo . push_back ( ssprintf ( ERROR . GetValue ( ) , s_fAverageError ) ) ;
}
if ( vsAddTo . size ( ) > iOriginalSize & & s_iStepsFiltered > 0 )
{
vsAddTo . push_back ( ssprintf ( TAPS_IGNORED . GetValue ( ) , s_iStepsFiltered ) ) ;
}
2015-02-13 02:17:49 -07:00
# undef SEGMENTS_MISMATCH_MESSAGE
2011-03-17 01:47:30 -04:00
}
}
/*
* (c) 2003-2006 Chris Danford, John Bauer
* All rights reserved.
2023-04-19 14:22:59 +02:00
*
2011-03-17 01:47:30 -04:00
* 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.
2023-04-19 14:22:59 +02:00
*
2011-03-17 01:47:30 -04:00
* 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.
*/