Ejemplo n.º 1
0
void SndBuffer::UpdateTempoChangeAsyncMixing()
{
	float statusPct = GetStatusPct();

	lastPct = statusPct;
	if( statusPct < -0.4f )
	{
		TickInterval -= 4;
		if( TickInterval <= 200 ) TickInterval = 200;
		//printf("-- %d, %f\n",TickInterval,statusPct);
	}
	else if( statusPct > 0.5f )
	{
		TickInterval += 1;
		if( TickInterval >= 7000 ) TickInterval = 7000;
		//printf("++ %d, %f\n",TickInterval,statusPct);
	}
	else TickInterval = 768;
}
Ejemplo n.º 2
0
void SndBuffer::UpdateTempoChange()
{
    if( --freezeTempo > 0 )
    {
        return;
    }

    float statusPct = GetStatusPct();
    float pctChange = statusPct - lastPct;

    float tempoChange;
    float emergencyAdj = 0;
    float newcee = cTempo;		// workspace var. for cTempo

    // IMPORTANT!
    // If you plan to tweak these values, make sure you're using a release build
    // OUTSIDE THE DEBUGGER to test it!  The Visual Studio debugger can really cause
    // erratic behavior in the audio buffers, and makes the timestretcher seem a
    // lot more inconsistent than it really is.

    // We have two factors.
    //   * Distance from nominal buffer status (50% full)
    //   * The change from previous update to this update.

    // Prediction based on the buffer change:
    // (linear seems to work better here)

    tempoChange = pctChange * 0.75f;

    if( statusPct * tempoChange < 0.0f )
    {
        // only apply tempo change if it is in synch with the buffer status.
        // In other words, if the buffer is high (over 0%), and is decreasing,
        // ignore it.  It'll just muck things up.

        tempoChange = 0;
    }

    // Sudden spikes in framerate can cause the nominal buffer status
    // to go critical, in which case we have to enact an emergency
    // stretch. The following cubic formulas do that.  Values near
    // the extremeites give much larger results than those near 0.
    // And the value is added only this time, and does not accumulate.
    // (otherwise a large value like this would cause problems down the road)

    // Constants:
    // Weight - weights the statusPct's "emergency" consideration.
    //   higher values here will make the buffer perform more drastic
    //   compensations at the outer edges of the buffer (at -75 or +75%
    //   or beyond, for example).

    // Range - scales the adjustment to the given range (more or less).
    //   The actual range is dependent on the weight used, so if you increase
    //   Weight you'll usually want to decrease Range somewhat to compensate.

    // Prediction based on the buffer fill status:

    const float statusWeight = 2.99f;
    const float statusRange = 0.068f;

    // "non-emergency" deadzone:  In this area stretching will be strongly discouraged.
    // Note: due tot he nature of timestretch latency, it's always a wee bit harder to
    // cope with low fps (underruns) than it is high fps (overruns).  So to help out a
    // little, the low-end portions of this check are less forgiving than the high-sides.

    if( cTempo < 0.965f || cTempo > 1.060f ||
            pctChange < -0.38f || pctChange > 0.54f ||
            statusPct < -0.32f || statusPct > 0.39f ||
            eTempo < 0.89f || eTempo > 1.19f )
    {
        emergencyAdj = ( pow( statusPct*statusWeight, 3.0f ) * statusRange);
    }

    // Smooth things out by factoring our previous adjustment into this one.
    // It helps make the system 'feel' a little smarter by  giving it at least
    // one packet worth of history to help work off of:

    emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f );

    lastEmergencyAdj = emergencyAdj;
    lastPct = statusPct;

    // Accumulate a fraction of the tempo change into the tempo itself.
    // This helps the system run "smarter" to games that run consistently
    // fast or slow by altering the base tempo to something closer to the
    // game's active speed.  In tests most games normalize within 2 seconds
    // at 100ms latency, which is pretty good (larger buffers normalize even
    // quicker).

    newcee += newcee * (tempoChange+emergencyAdj) * 0.03f;

    // Apply tempoChange as a scale of cTempo.  That way the effect is proportional
    // to the current tempo.  (otherwise tempos rate of change at the extremes would
    // be too drastic)

    float newTempo = newcee + ( emergencyAdj * cTempo );

    // ... and as a final optimization, only stretch if the new tempo is outside
    // a nominal threshold.  Keep this threshold check small, because it could
    // cause some serious side effects otherwise. (enlarging the cTempo check above
    // is usually better/safer)
    if( newTempo < 0.970f || newTempo > 1.045f )
    {
        cTempo = (float)newcee;

        if( newTempo < 0.10f ) newTempo = 0.10f;
        else if( newTempo > 10.0f ) newTempo = 10.0f;

        if( cTempo < 0.15f ) cTempo = 0.15f;
        else if( cTempo > 7.5f ) cTempo = 7.5f;

        pSoundTouch->setTempo( eTempo = (float)newTempo );
        ts_stats_stretchblocks++;

        /*ConLog(" * SPU2: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
        	//(relation < 0.0) ? "Normalize" : "",
        	(int)(tempoChange * 100.0 * 0.03),
        	(int)(emergencyAdj * 100.0),
        	(int)(cTempo * 100.0),
        	(int)(newTempo * 100.0),
        	(int)(statusPct * 100.0)
        );*/
    }
    else
    {
        // Nominal operation -- turn off stretching.
        // note: eTempo 'slides' toward 1.0 for smoother audio and better
        // protection against spikes.
        if( cTempo != 1.0f )
        {
            cTempo = 1.0f;
            eTempo = ( 1.0f + eTempo ) * 0.5f;
            pSoundTouch->setTempo( eTempo );
        }
        else
        {
            if( eTempo != cTempo )
                pSoundTouch->setTempo( eTempo=cTempo );
            ts_stats_normalblocks++;
        }
    }
}