Changeset 109518 in webkit


Ignore:
Timestamp:
Mar 2, 2012 12:01:01 AM (12 years ago)
Author:
commit-queue@webkit.org
Message:

AudioParam needs tests for the parameter automation routines.
https://bugs.webkit.org/show_bug.cgi?id=77666

Patch by Raymond Toy <Raymond Toy> on 2012-03-02
Reviewed by Chris Rogers.

Source/WebCore:

Tests: webaudio/audioparam-linearRampToValueAtTime.html

webaudio/audioparam-setTargetValueAtTime.html
webaudio/audioparam-setValueAtTime.html
webaudio/audioparam-setValueCurveAtTime.html

  • webaudio/AudioParamTimeline.cpp:

(WebCore::AudioParamTimeline::valuesForTimeRangeImpl): Round the
curveIndex to fix timing issue in setValueCurveAtTime.

LayoutTests:

  • webaudio/audioparam-exponentialRampToValueAtTime.html:
  • webaudio/audioparam-linearRampToValueAtTime-expected.txt: Added.
  • webaudio/audioparam-linearRampToValueAtTime.html: Added.
  • webaudio/audioparam-setTargetValueAtTime-expected.txt: Added.
  • webaudio/audioparam-setTargetValueAtTime.html: Added.
  • webaudio/audioparam-setValueAtTime-expected.txt: Added.
  • webaudio/audioparam-setValueAtTime.html: Added.
  • webaudio/audioparam-setValueCurveAtTime-expected.txt: Added.
  • webaudio/audioparam-setValueCurveAtTime.html: Added.
  • webaudio/resources/audioparam-testing.js:

(renderLength):
(createConstantArray):
(createLinearRampArray):
(discreteTimeConstantForSampleRate):
(createExponentialApproachArray):
(createSineWaveArray):
(endValueDelta):
(valueUpdate):
(comparePartialSignals):
(verifyDiscontinuities):
(compareSignals):
(checkResultFunction.return.var):
(checkResultFunction):
(doAutomation):
(createAudioGraphAndTest):

  • webaudio/resources/audio-testing.js: Moved isValidNumber from

biquad-testing.js to here.

  • webaudio/resources/biquad-testing.js: Moved isValidNumber from

here to audio-testing.js.

Location:
trunk
Files:
8 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r109516 r109518  
     12012-03-02  Raymond Toy  <rtoy@google.com>
     2
     3        AudioParam needs tests for the parameter automation routines.
     4        https://bugs.webkit.org/show_bug.cgi?id=77666
     5
     6        Reviewed by Chris Rogers.
     7
     8        * webaudio/audioparam-exponentialRampToValueAtTime.html:
     9        * webaudio/audioparam-linearRampToValueAtTime-expected.txt: Added.
     10        * webaudio/audioparam-linearRampToValueAtTime.html: Added.
     11        * webaudio/audioparam-setTargetValueAtTime-expected.txt: Added.
     12        * webaudio/audioparam-setTargetValueAtTime.html: Added.
     13        * webaudio/audioparam-setValueAtTime-expected.txt: Added.
     14        * webaudio/audioparam-setValueAtTime.html: Added.
     15        * webaudio/audioparam-setValueCurveAtTime-expected.txt: Added.
     16        * webaudio/audioparam-setValueCurveAtTime.html: Added.
     17        * webaudio/resources/audioparam-testing.js:
     18        (renderLength):
     19        (createConstantArray):
     20        (createLinearRampArray):
     21        (discreteTimeConstantForSampleRate):
     22        (createExponentialApproachArray):
     23        (createSineWaveArray):
     24        (endValueDelta):
     25        (valueUpdate):
     26        (comparePartialSignals):
     27        (verifyDiscontinuities):
     28        (compareSignals):
     29        (checkResultFunction.return.var):
     30        (checkResultFunction):
     31        (doAutomation):
     32        (createAudioGraphAndTest):
     33        * webaudio/resources/audio-testing.js: Moved isValidNumber from
     34        biquad-testing.js to here.
     35        * webaudio/resources/biquad-testing.js: Moved isValidNumber from
     36        here to audio-testing.js.
     37
    1382012-03-01  Dirk Pranke  <dpranke@chromium.org>
    239
  • trunk/LayoutTests/webaudio/audioparam-exponentialRampToValueAtTime.html

    r108592 r109518  
    2121      // decreasing ramp for each interval.
    2222
     23      // Number of tests to run.
     24      var numberOfTests = 100;
     25
    2326      // Max allowed difference between the rendered data and the expected result.
    2427      var maxAllowedError = 6.75e-4;
    2528
    26       // Information about the starting and ending times and starting and ending values for each
    27       // time interval.
    28       var timeValueInfo;
    29 
    3029      // The AudioGainNode starts with this value instead of the default value.
    3130      var initialValue = 100;
    32 
    33       // The difference between starting values between each time interval.
    34       var startingValueDelta = initialValue / numberOfTests;
    35      
    36       // Each exponential ramp will increase or decrease by this much from the start to the end of
    37       // the time interval.  We choose half of startingValueDelta so that the ending value of the
    38       // ramp will be distinct from the starting value of the ramp for next time interval.  This
    39       // allows us to detect where the ramp begins and ends.
    40       var startEndValueChange = startingValueDelta / 2;
    41 
    42       // Threshold to use for detecting discontinuities that should appear at each time interval.
    43       var discontinuityThreshold = startEndValueChange / 2;
    44 
    45       var gainNode;
    46      
    47       var constantBuffer;
    48       var bufferSource;
    49 
    50       // Return the difference between the starting value and the ending value for time interval
    51       // |timeIntervalIndex|.  We alternate between an end value that is above or below the starting
    52       // value.
    53       function endValueDelta(timeIntervalIndex)
    54       {
    55           if (timeIntervalIndex & 1) {
    56               return -startEndValueChange;
    57           } else {
    58               return startEndValueChange;
    59           }
    60       }
    6131
    6232      // Set the gain node value to the specified value at the specified time.
     
    7444      }
    7545     
    76       // Return the difference between the starting value at |timeIntervalIndex| and the starting
    77       // value at the next time interval.  Since we started at a large initial value, we decrease
    78       // the value at each time interval.
    79       function valueUpdate(timeIntervalIndex)
    80       {
    81           return -startingValueDelta;
    82       }
    83 
    84       function checkResult(event)
    85       {
    86           var buffer = event.renderedBuffer;
    87           renderedData = buffer.getChannelData(0);
    88 
    89           compareSignals("exponentialRampToValueAtTime()", maxAllowedError, renderedData, createExponentialRampArray, timeValueInfo, discontinuityThreshold);
    90 
    91           finishJSTest();
    92       }
    93      
    9446      function runTest()
    9547      {
    96           if (window.layoutTestController) {
    97               layoutTestController.dumpAsText();
    98               layoutTestController.waitUntilDone();
    99           }
     48          createAudioGraphAndTest(numberOfTests,
     49                                  initialValue,
     50                                  setValue,
     51                                  generateRamp,
     52                                  "exponentialRampToValueAtTime()",
     53                                  maxAllowedError,
     54                                  createExponentialRampArray);
    10055
    101           window.jsTestIsAsync = true;
    102 
    103           // Create offline audio context.
    104           context = new webkitAudioContext(2, renderLength, sampleRate);
    105           constantBuffer = createConstantBuffer(context, 1, renderLength);
    106 
    107           // We use an AudioGainNode here simply as a convenient way to test the AudioParam
    108           // automation, since it's easy to pass a constant value through the node, automate the
    109           // .gain attribute and observe the resulting values.
    110 
    111           gainNode = context.createGainNode();
    112 
    113           bufferSource = context.createBufferSource();
    114           bufferSource.buffer = constantBuffer;
    115           bufferSource.connect(gainNode);
    116           gainNode.connect(context.destination);
    117 
    118           // At each time interval, we adjust the value by startingValueDelta.  However, the
    119           // exponential ramp end value is the value +/- startEndValueChange.  (We alternately
    120           // specify the end value to be greater or smaller than the starting value, to test the
    121           // ramp going up and down.)
    122           //
    123           // By doing it this way, we get a discontinuity at each time interval which we can use to
    124           // test if the ramp started and ended at the correct time.
    125           timeValueInfo = doAutomation(initialValue,
    126                                        endValueDelta,
    127                                        setValue,
    128                                        generateRamp,
    129                                        valueUpdate);
    130 
    131           bufferSource.noteOn(0);
    132      
    133           context.oncomplete = checkResult;
    134           context.startRendering();
    13556      }
    13657
  • trunk/LayoutTests/webaudio/resources/audio-testing.js

    r108604 r109518  
    129129    return endFrame - startFrame;
    130130}
     131
     132// True if the number is not an infinity or NaN
     133function isValidNumber(x) {
     134    return !isNaN(x) && (x != Infinity) && (x != -Infinity);
     135}
  • trunk/LayoutTests/webaudio/resources/audioparam-testing.js

    r108592 r109518  
    11var sampleRate = 44100;
    22
    3 // Number of tests to run.
    4 var numberOfTests = 100;
    5 
    6 // Time interval between value changes.  It is best if 1 / numberOfTests is not close to
    7 // timeInterval.
     3// Information about the starting/ending times and starting/ending values for each time interval.
     4var timeValueInfo;
     5
     6// The difference between starting values between each time interval.
     7var startingValueDelta;
     8     
     9// For any automation function that has an end or target value, the end value is based the starting
     10// value of the time interval.  The starting value will be increased or decreased by
     11// |startEndValueChange|. We choose half of |startingValueDelta| so that the ending value will be
     12// distinct from the starting value for next time interval.  This allows us to detect where the ramp
     13// begins and ends.
     14var startEndValueChange;
     15
     16// Default threshold to use for detecting discontinuities that should appear at each time interval.
     17var discontinuityThreshold;
     18
     19// Time interval between value changes.  It is best if 1 / numberOfTests is not close to timeInterval.
    820var timeInterval = .03;
    921
     22// Some suitable time constant so that we can see a significant change over a timeInterval.  This is
     23// only needed by setTargetValueAtTime() which needs a time constant.
     24var timeConstant = timeInterval / 3;
     25
     26var gainNode;
     27
     28var context;
     29
    1030// Make sure we render long enough to capture all of our test data.
    11 var renderTime = (numberOfTests + 1) * timeInterval;
    12 var renderLength = timeToSampleFrame(renderTime, sampleRate);
    13 
    14 var context;
     31function renderLength(numberOfTests)
     32{
     33    return timeToSampleFrame((numberOfTests + 1) * timeInterval, sampleRate);
     34}
    1535
    1636// Create a buffer containing the same constant value.
     
    2747}
    2848
     49// Create a constant reference signal with the given |value|.  Basically the same as
     50// |createConstantBuffer|, but with the parameters to match the other create functions.  The
     51// |endValue| is ignored.
     52function createConstantArray(startTime, endTime, value, endValue, sampleRate)
     53{
     54    var startFrame = timeToSampleFrame(startTime, sampleRate);
     55    var endFrame = timeToSampleFrame(endTime, sampleRate);
     56    var length = endFrame - startFrame;
     57
     58    var buffer = createConstantBuffer(context, value, length);
     59
     60    return buffer.getChannelData(0);
     61}
     62
     63// Create a linear ramp starting at |startValue| and ending at |endValue|.  The ramp starts at time
     64// |startTime| and ends at |endTime|.  (The start and end times are only used to compute how many
     65// samples to return.)
     66function createLinearRampArray(startTime, endTime, startValue, endValue, sampleRate)
     67{
     68    var startFrame = timeToSampleFrame(startTime, sampleRate);
     69    var endFrame = timeToSampleFrame(endTime, sampleRate);
     70    var length = endFrame - startFrame;
     71    var array = new Array(length);
     72
     73    var step = (endValue - startValue) / length;
     74
     75    for (k = 0; k < length; ++k) {
     76        array[k] = startValue + k * step;
     77    }
     78
     79    return array;
     80}
     81
    2982// Create an exponential ramp starting at |startValue| and ending at |endValue|.  The ramp starts at
    3083// time |startTime| and ends at |endTime|.  (The start and end times are only used to compute how
     
    3588    var endFrame = timeToSampleFrame(endTime, sampleRate);
    3689    var length = endFrame - startFrame;
    37     var buffer = new Array(length);
     90    var array = new Array(length);
    3891
    3992    var multiplier = Math.pow(endValue / startValue, 1 / length);
    4093   
    4194    for (var k = 0; k < length; ++k) {
    42         buffer[k] = startValue * Math.pow(multiplier, k);
    43     }
    44 
    45     return buffer;
     95        array[k] = startValue * Math.pow(multiplier, k);
     96    }
     97
     98    return array;
     99}
     100
     101function discreteTimeConstantForSampleRate(timeConstant, sampleRate)
     102{
     103    return 1 - Math.exp(-1 / (sampleRate * timeConstant));
     104}
     105
     106// Create a signal that starts at |startValue| and exponentially approaches the target value of
     107// |targetValue|, using a time constant of |timeConstant|.  The ramp starts at time |startTime| and
     108// ends at |endTime|.  (The start and end times are only used to compute how many samples to
     109// return.)
     110function createExponentialApproachArray(startTime, endTime, startValue, targetValue, sampleRate, timeConstant)
     111{
     112    var startFrame = timeToSampleFrame(startTime, sampleRate);
     113    var endFrame = timeToSampleFrame(endTime, sampleRate);
     114    var length = endFrame - startFrame;
     115    var array = new Array(length);
     116    var c = discreteTimeConstantForSampleRate(timeConstant, sampleRate);
     117
     118    var value = startValue;
     119   
     120    for (var k = 0; k < length; ++k) {
     121        array[k] = value;
     122        value += (targetValue - value) * c;
     123    }
     124
     125    return array;
     126}
     127
     128// Create a sine wave of the given frequency and amplitude.  The sine wave is offset by half the
     129// amplitude so that result is always positive.
     130function createSineWaveArray(durationSeconds, freqHz, amplitude, sampleRate)
     131{
     132    var length = timeToSampleFrame(durationSeconds, sampleRate);
     133    var signal = new Float32Array(length);
     134    var omega = 2 * Math.PI * freqHz / sampleRate;
     135    var halfAmplitude = amplitude / 2;
     136   
     137    for (var k = 0; k < length; ++k) {
     138        signal[k] = halfAmplitude + halfAmplitude * Math.sin(omega * k);
     139    }
     140
     141    return signal;
     142}
     143
     144// Return the difference between the starting value and the ending value for time interval
     145// |timeIntervalIndex|.  We alternate between an end value that is above or below the starting
     146// value.
     147function endValueDelta(timeIntervalIndex)
     148{
     149    if (timeIntervalIndex & 1) {
     150        return -startEndValueChange;
     151    } else {
     152        return startEndValueChange;
     153    }
     154}
     155
     156// Return the difference between the starting value at |timeIntervalIndex| and the starting value at
     157// the next time interval.  Since we started at a large initial value, we decrease the value at each
     158// time interval.
     159function valueUpdate(timeIntervalIndex)
     160{
     161    return -startingValueDelta;
    46162}
    47163
     
    50166{
    51167    var startSample = timeToSampleFrame(startTime, sampleRate);
    52     var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate);
     168    var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate, timeConstant);
    53169
    54170    var n = expected.length;
     
    57173   
    58174    for (var k = 0; k < n; ++k) {
     175        // Make sure we don't pass these tests because a NaN has been generated in either the
     176        // rendered data or the reference data.
     177        if (!isValidNumber(rendered[startSample + k])) {
     178            maxError = Infinity;
     179            maxErrorIndex = startSample + k;
     180            testFailed("NaN or infinity for rendered data at " + maxErrorIndex);
     181            break;
     182        }
     183        if (!isValidNumber(expected[k])) {
     184            maxError = Infinity;
     185            maxErrorIndex = startSample + k;
     186            testFailed("Nan or infinity for reference data at " + maxErrorIndex);
     187            break;
     188        }
    59189        var error = Math.abs(rendered[startSample + k] - expected[k]);
    60190        if (error > maxError) {
     
    84214    }
    85215
     216    var testCount;
     217
     218    // If there are numberOfTests intervals, there are only numberOfTests - 1 internal interval
     219    // boundaries. Hence the maximum number of discontinuties we expect to find is numberOfTests -
     220    // 1. If we find more than that, we have no reference to compare against. We also assume that
     221    // the actual discontinuities are close to the expected ones.
     222    //
     223    // This is just a sanity check when something goes really wrong.  For example, if the threshold
     224    // is too low, every sample frame looks like a discontinuity.
     225    if (breaks.length >= numberOfTests) {
     226        testCount = numberOfTests - 1;
     227        testFailed("Found more discontinuities (" + breaks.length + ") than expected.  Only comparing first " + testCount + "discontinuities.");
     228        success = false;
     229    } else {
     230        testCount = breaks.length;
     231    }
     232   
    86233    // Compare the location of each discontinuity with the end time of each interval. (There is no
    87234    // discontinuity at the start of the signal.)
    88     for (var k = 0; k < breaks.length; ++k) {
     235    for (var k = 0; k < testCount; ++k) {
    89236        var expectedSampleFrame = timeToSampleFrame(times[k + 1], sampleRate);
    90237        if (breaks[k] != expectedSampleFrame) {
     
    97244    if (badLocations) {
    98245        testFailed(badLocations + " discontinuities at incorrect locations");
     246        success = false;
    99247    } else {
    100         testPassed("All " + (breaks.length + 1) + " tests started and ended at the correct time.");
     248        if (breaks.length == numberOfTests - 1) {
     249            testPassed("All " + numberOfTests + " tests started and ended at the correct time.");
     250        } else {
     251            testFailed("Found " + breaks.length + " discontinuities but expected " + (numberOfTests - 1));
     252            success = false;
     253        }
    101254    }
    102255   
     
    132285
    133286        if (result.maxError > maxError) {
    134             testFailed("Incorrect gain for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate)));
     287            testFailed("Incorrect value for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate)));
    135288            ++failedTestCount;
    136289        }
     
    151304}
    152305
     306// Create a function to test the rendered data with the reference data.
     307//
     308// testName - string describing the test
     309//
     310// error - max allowed error between rendered data and the reference data.
     311//
     312// referenceFunction - function that generates the reference data to be compared with the rendered
     313// data.
     314//
     315// jumpThreshold - optional parameter that specifies the threshold to use for detecting
     316// discontinuities.  If not specified, defaults to discontinuityThreshold.
     317//
     318function checkResultFunction(testName, error, referenceFunction, jumpThreshold)
     319{
     320    return function(event) {
     321        var buffer = event.renderedBuffer;
     322        renderedData = buffer.getChannelData(0);
     323
     324        var threshold;
     325
     326        if (!jumpThreshold) {
     327            threshold = discontinuityThreshold;
     328        } else {
     329            threshold = jumpThreshold;
     330        }
     331       
     332        compareSignals(testName, error, renderedData, referenceFunction, timeValueInfo, threshold);
     333
     334        finishJSTest();
     335    }
     336}
     337
    153338// Run all the automation tests.
    154339//
     340// numberOfTests - number of tests (time intervals) to run.
     341//
    155342// initialValue - The initial value of the first time interval.
    156 //
    157 // endValueDeltaFunction - How much the end value differs from the value at the start of the
    158 // interval.
    159343//
    160344// setValueFunction - function that sets the specified value at the start of a time interval.
     
    163347// the value approaches the end value.
    164348//
    165 // valueUpdateFunction - The starting value at time interval k is adjusted by valueUpdateFunction(k) to give the new starting value at time interval k + 1.
    166 //
    167349// An object is returned containing an array of start times for each time interval, and an array
    168350// giving the start and end values for the interval.
    169 function doAutomation(initialValue, endValueDeltaFunction, setValueFunction, automationFunction, valueUpdateFunction)
     351function doAutomation(numberOfTests, initialValue, setValueFunction, automationFunction)
    170352{
    171353    var timeInfo = [0];
     
    176358        var startTime = k * timeInterval;
    177359        var endTime = (k + 1) * timeInterval;
    178         var endValue = value + endValueDeltaFunction(k);
     360        var endValue = value + endValueDelta(k);
    179361
    180362        // Set the value at the start of the time interval.
    181363        setValueFunction(value, startTime);
    182364
    183         // Specify the target value, and how we should approach the target value.
     365        // Specify the end or target value, and how we should approach it.
    184366        automationFunction(endValue, startTime, endTime);
    185367
     
    188370        valueInfo.push({startValue: value, endValue : endValue});
    189371
    190         value += valueUpdateFunction(k);
     372        value += valueUpdate(k);
    191373    }
    192374
    193375    return {times : timeInfo, values : valueInfo};
    194376}
     377
     378// Create the audio graph for the test and then run the test.
     379//
     380// numberOfTests - number of time intervals (tests) to run.
     381//
     382// initialValue - the initial value of the gain at time 0.
     383//
     384// setValueFunction - function to set the value at the beginning of each time interval.
     385//
     386// automationFunction - the AudioParamTimeline automation function
     387//
     388// testName - string indicating the test that is being run.
     389//
     390// maxError - maximum allowed error between the rendered data and the reference data
     391//
     392// referenceFunction - function that generates the reference data to be compared against the
     393// rendered data.
     394//
     395// jumpThreshold - optional parameter that specifies the threshold to use for detecting
     396// discontinuities.  If not specified, defaults to discontinuityThreshold.
     397//
     398function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, automationFunction, testName, maxError, referenceFunction, jumpThreshold)
     399{
     400    if (window.layoutTestController) {
     401        layoutTestController.dumpAsText();
     402        layoutTestController.waitUntilDone();
     403    }
     404
     405    window.jsTestIsAsync = true;
     406
     407    // Create offline audio context.
     408    context = new webkitAudioContext(2, renderLength(numberOfTests), sampleRate);
     409    var constantBuffer = createConstantBuffer(context, 1, renderLength(numberOfTests));
     410
     411    // We use an AudioGainNode here simply as a convenient way to test the AudioParam
     412    // automation, since it's easy to pass a constant value through the node, automate the
     413    // .gain attribute and observe the resulting values.
     414
     415    gainNode = context.createGainNode();
     416
     417    var bufferSource = context.createBufferSource();
     418    bufferSource.buffer = constantBuffer;
     419    bufferSource.connect(gainNode);
     420    gainNode.connect(context.destination);
     421
     422    // Set up default values for the parameters that control how the automation test values progress
     423    // for each time interval.
     424    startingValueDelta = initialValue / numberOfTests;
     425    startEndValueChange = startingValueDelta / 2;
     426    discontinuityThreshold = startEndValueChange / 2;
     427
     428    // Run the automation tests.
     429    timeValueInfo = doAutomation(numberOfTests,
     430                                 initialValue,
     431                                 setValueFunction,
     432                                 automationFunction);
     433    bufferSource.noteOn(0);
     434     
     435    context.oncomplete = checkResultFunction(testName,
     436                                             maxError,
     437                                             referenceFunction,
     438                                             jumpThreshold);
     439    context.startRendering();
     440}
  • trunk/LayoutTests/webaudio/resources/biquad-testing.js

    r109458 r109518  
    481481}
    482482
    483 // True if the number is not an infinity or NaN
    484 function isValidNumber(x) {
    485     return !isNaN(x) && (x != Infinity) && (x != -Infinity);
    486 }
    487 
    488483function checkFilterResponse(filterType, filterParameters) {
    489484    return function(event) {
  • trunk/Source/WebCore/ChangeLog

    r109515 r109518  
     12012-03-02  Raymond Toy  <rtoy@google.com>
     2
     3        AudioParam needs tests for the parameter automation routines.
     4        https://bugs.webkit.org/show_bug.cgi?id=77666
     5
     6        Reviewed by Chris Rogers.
     7
     8        Tests: webaudio/audioparam-linearRampToValueAtTime.html
     9               webaudio/audioparam-setTargetValueAtTime.html
     10               webaudio/audioparam-setValueAtTime.html
     11               webaudio/audioparam-setValueCurveAtTime.html
     12
     13        * webaudio/AudioParamTimeline.cpp:
     14        (WebCore::AudioParamTimeline::valuesForTimeRangeImpl): Round the
     15        curveIndex to fix timing issue in setValueCurveAtTime.
     16
    1172012-03-01  Pablo Flouret  <pablof@motorola.com>
    218
  • trunk/Source/WebCore/webaudio/AudioParamTimeline.cpp

    r108592 r109518  
    330330                    // Oversampled curve data can be provided if smoothness is desired.
    331331                    for (; writeIndex < fillToFrame; ++writeIndex) {
    332                         unsigned curveIndex = static_cast<unsigned>(curveVirtualIndex);
     332                        // Ideally we'd use round() from MathExtras, but we're in a tight loop here
     333                        // and we're trading off precision for extra speed.
     334                        unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
     335
    333336                        curveVirtualIndex += curvePointsPerFrame;
    334337
Note: See TracChangeset for help on using the changeset viewer.