Title: [179838] trunk
Revision
179838
Author
[email protected]
Date
2015-02-09 11:45:54 -0800 (Mon, 09 Feb 2015)

Log Message

[WebAudio] AudioBufferSourceNodes should accurately play backwards if given a negative playbackRate.
https://bugs.webkit.org/show_bug.cgi?id=140955

Reviewed by Eric Carlson.

Source/WebCore:

Tests: webaudio/audiobuffersource-negative-playbackrate-interpolated.html
       webaudio/audiobuffersource-negative-playbackrate.html

Add support for playing an AudioBufferSourceNode at a negative playbackRate. Change the meaning of
start() to set the initial playback position at the end of the play range if the rate of playback
is negtive.

* Modules/webaudio/AudioBufferSourceNode.cpp:
(WebCore::AudioBufferSourceNode::AudioBufferSourceNode): Allow the playbackRate AudioParam to range from [-32, 32].
(WebCore::AudioBufferSourceNode::renderFromBuffer): Change variable names from "start" and "end" to "min" and "max"
    for clarity. Add a non-interpolated and interpolated render step for negative playback.
(WebCore::AudioBufferSourceNode::start): Drive-by fix: default value of grainDuration is not 0.02.
(WebCore::AudioBufferSourceNode::startPlaying): Start playing at the end of the buffer for negative playback.
(WebCore::AudioBufferSourceNode::totalPitchRate): Allow the pitch to be negative.

LayoutTests:

* webaudio/audiobuffersource-negative-playbackrate-expected.txt: Added.
* webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt: Added.
* webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt: Added.
* webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html: Added.
* webaudio/audiobuffersource-negative-playbackrate-interpolated.html:
* webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt: Added.
* webaudio/audiobuffersource-negative-playbackrate-loop.html: Added.
* webaudio/audiobuffersource-negative-playbackrate.html:
* webaudio/resources/audiobuffersource-testing.js:
(createRamp):

Get rid of extra HRTF padding as it's now unnecessary.

* webaudio/resources/note-grain-on-testing.js:
(createSignalBuffer):
(verifyStartAndEndFrames):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (179837 => 179838)


--- trunk/LayoutTests/ChangeLog	2015-02-09 18:59:44 UTC (rev 179837)
+++ trunk/LayoutTests/ChangeLog	2015-02-09 19:45:54 UTC (rev 179838)
@@ -1,3 +1,27 @@
+2015-02-09  Jer Noble  <[email protected]>
+
+        [WebAudio] AudioBufferSourceNodes should accurately play backwards if given a negative playbackRate.
+        https://bugs.webkit.org/show_bug.cgi?id=140955
+
+        Reviewed by Eric Carlson.
+
+        * webaudio/audiobuffersource-negative-playbackrate-expected.txt: Added.
+        * webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt: Added.
+        * webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt: Added.
+        * webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html: Added.
+        * webaudio/audiobuffersource-negative-playbackrate-interpolated.html:
+        * webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt: Added.
+        * webaudio/audiobuffersource-negative-playbackrate-loop.html: Added.
+        * webaudio/audiobuffersource-negative-playbackrate.html:
+        * webaudio/resources/audiobuffersource-testing.js:
+        (createRamp):
+
+        Get rid of extra HRTF padding as it's now unnecessary.
+
+        * webaudio/resources/note-grain-on-testing.js:
+        (createSignalBuffer):
+        (verifyStartAndEndFrames):
+
 2015-02-09  David Kilzer  <[email protected]>
 
         [iOS] Gardening: css3/masking/mask-repeat-space-padding.html

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-expected.txt (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-expected.txt	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,2 @@
+PASS Test playback at -1 playbackRate
+

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,2 @@
+PASS Test playback at -0.75 playbackRate
+

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,2 @@
+PASS Test looping playback at -0.75 playbackRate
+

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>audiobuffersource-negative-playbackrate-interpolated-loop</title>
+    <script src=""
+    <script src=""
+    <script src=""
+
+    <script>
+
+    var sampleRate = 44100.0;
+    var sourceFrames = 128;
+    var renderFrames = 10;
+    var testSpacingFrames = 0;
+    var startLoop = 100 / sampleRate;
+    var loopDuration = (renderFrames / 2) / sampleRate;
+    var endLoop = startLoop + loopDuration;
+
+    var tests = [{ 
+        description:"Test looping playback at -0.75 playbackRate",
+        offsetFrame:0,
+        renderFrames:renderFrames,
+        expected:[104,103.25,102.5,101.75,101,100.25,102,103.75,103,102.25],
+    }];
+
+    function go() {
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        context = new webkitOfflineAudioContext(1, renderFrames, sampleRate);
+
+        var bufferSource = context.createBufferSource();
+        bufferSource.buffer = createRamp(context, 0, 127, sourceFrames);
+
+        bufferSource.connect(context.destination);
+        bufferSource.playbackRate.value = -0.75;
+        bufferSource.loop = true;
+        bufferSource.loopStart = startLoop;
+        bufferSource.loopEnd = endLoop;
+        bufferSource.start(0, startLoop, loopDuration);
+        bufferSource.stop(loopDuration * 2);
+
+        context._oncomplete_ = checkAllTests;
+        context.startRendering();
+    }
+
+    </script>
+</head>
+<body _onload_="go()">
+</body>
+</html>

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated.html (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-interpolated.html	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>audiobuffersource-negative-playbackrate-interpolated</title>
+    <script src=""
+    <script src=""
+    <script src=""
+
+    <script>
+
+    var sampleRate = 44100.0;
+    var sourceFrames = 128;
+    var renderFrames = 10;
+    var testSpacingFrames = 0;
+
+    var tests = [{ 
+        description:"Test playback at -0.75 playbackRate",
+        offsetFrame:0,
+        renderFrames:renderFrames,
+        expected:[127, 126.25, 125.5, 124.75, 124, 123.25, 122.5, 121.75, 121, 120.25]
+    }];
+
+    function go() {
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        context = new webkitOfflineAudioContext(1, renderFrames, sampleRate);
+
+        var bufferSource = context.createBufferSource();
+        bufferSource.buffer = createRamp(context, 0, 127, sourceFrames);
+
+        bufferSource.connect(context.destination);
+        bufferSource.playbackRate.value = -0.75;
+        bufferSource.start(0);
+
+        context._oncomplete_ = checkAllTests;
+        context.startRendering();
+    }
+
+    </script>
+</head>
+<body _onload_="go()">
+</body>
+</html>

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,2 @@
+PASS Test looping playback at -1 playbackRate
+

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop.html (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate-loop.html	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>audiobuffersource-negative-playbackrate-loop</title>
+    <script src=""
+    <script src=""
+    <script src=""
+
+    <script>
+
+    var sampleRate = 44100.0;
+    var sourceFrames = 128;
+    var renderFrames = 10;
+    var testSpacingFrames = 0;
+    var startLoop = 100 / sampleRate;
+    var loopDuration = (renderFrames / 2) / sampleRate;
+    var endLoop = startLoop + loopDuration;
+
+    var tests = [{ 
+        description:"Test looping playback at -1 playbackRate",
+        offsetFrame:0,
+        renderFrames:renderFrames,
+        expected:[104, 103, 102, 101, 100, 104, 103, 102, 101, 100],
+    }];
+
+    function go() {
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        context = new webkitOfflineAudioContext(1, renderFrames, sampleRate);
+
+        var bufferSource = context.createBufferSource();
+        bufferSource.buffer = createRamp(context, 0, 127, sourceFrames);
+
+        bufferSource.connect(context.destination);
+        bufferSource.playbackRate.value = -1;
+        bufferSource.loop = true;
+        bufferSource.loopStart = startLoop;
+        bufferSource.loopEnd = endLoop;
+        bufferSource.start(0, startLoop, loopDuration);
+        bufferSource.stop(loopDuration * 2);
+
+        context._oncomplete_ = checkAllTests;
+        context.startRendering();
+    }
+
+    </script>
+</head>
+<body _onload_="go()">
+</body>
+</html>

Added: trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate.html (0 => 179838)


--- trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-negative-playbackrate.html	2015-02-09 19:45:54 UTC (rev 179838)
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>audiobuffersource-negative-playbackrate</title>
+    <script src=""
+    <script src=""
+    <script src=""
+
+    <script>
+
+    var sampleRate = 44100.0;
+    var sourceFrames = 128;
+    var renderFrames = 10;
+    var testSpacingFrames = 0;
+
+    var tests = [{ 
+        description:"Test playback at -1 playbackRate",
+        offsetFrame:0,
+        renderFrames:renderFrames,
+        expected:[127, 126, 125, 124, 123, 122, 121, 120, 119, 118],
+    }];
+
+    function go() {
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        context = new webkitOfflineAudioContext(1, renderFrames, sampleRate);
+
+        var bufferSource = context.createBufferSource();
+        bufferSource.buffer = createRamp(context, 0, 127, sourceFrames);
+
+        bufferSource.connect(context.destination);
+        bufferSource.playbackRate.value = -1;
+        bufferSource.start(0);
+
+        context._oncomplete_ = checkAllTests;
+        context.startRendering();
+    }
+
+    </script>
+</head>
+<body _onload_="go()">
+</body>
+</html>

Modified: trunk/LayoutTests/webaudio/resources/audiobuffersource-testing.js (179837 => 179838)


--- trunk/LayoutTests/webaudio/resources/audiobuffersource-testing.js	2015-02-09 18:59:44 UTC (rev 179837)
+++ trunk/LayoutTests/webaudio/resources/audiobuffersource-testing.js	2015-02-09 19:45:54 UTC (rev 179838)
@@ -9,6 +9,21 @@
     return audioBuffer;
 }
 
+function createRamp(context, startValue, endValue, numberOfSamples) {
+    var audioBuffer = context.createBuffer(1, numberOfSamples, context.sampleRate);
+    var channelData = audioBuffer.getChannelData(0);
+
+    var delta = (endValue - startValue) / (numberOfSamples - 1);
+    var nextValue = startValue;
+
+    for (var i = 0; i < numberOfSamples; ++i) {
+        channelData[i] = nextValue;
+        nextValue += delta;
+    }
+
+    return audioBuffer;
+}
+
 function checkSingleTest(renderedBuffer, i) {
     var renderedData = renderedBuffer.getChannelData(0);
     var offsetFrame = i * testSpacingFrames;

Modified: trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js (179837 => 179838)


--- trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js	2015-02-09 18:59:44 UTC (rev 179837)
+++ trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js	2015-02-09 19:45:54 UTC (rev 179838)
@@ -1,21 +1,13 @@
 var sampleRate = 44100.0;
 
-// HRTF extra frames.  This is a magic constant currently in
-// AudioBufferSourceNode::process that always extends the
-// duration by this number of samples.  See bug 77224
-// (https://bugs.webkit.org/show_bug.cgi?id=77224).
-var extraFramesHRTF = 512;
-
 // How many grains to play.
 var numberOfTests = 100;
 
 // Duration of each grain to be played
 var duration = 0.01;
 
-// Time step between the start of each grain.  We need to add a little
-// bit of silence so we can detect grain boundaries and also account
-// for the extra frames for HRTF.
-var timeStep = duration + .005 + extraFramesHRTF / sampleRate;
+// Time step between the start of each grain.
+var timeStep = duration + .005;
 
 // Time step between the start for each grain.
 var grainOffsetStep = 0.001;
@@ -33,7 +25,7 @@
     // Make sure the buffer has enough data for all of the possible
     // grain offsets and durations.  Need to include the extra frames
     // for HRTF.  The additional 1 is for any round-off errors.
-    var signalLength = Math.floor(1 + extraFramesHRTF + sampleRate * (numberOfTests * grainOffsetStep + duration));
+    var signalLength = Math.floor(1 + sampleRate * (numberOfTests * grainOffsetStep + duration));
 
     var buffer = context.createBuffer(2, signalLength, sampleRate);
     var data = ""
@@ -135,7 +127,7 @@
         var expectedStart = timeToSampleFrame(k * timeStep, sampleRate);
         // The end point is the duration, plus the extra frames
         // for HRTF.
-        var expectedEnd = extraFramesHRTF + expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);
+        var expectedEnd = expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);
 
         if (startFrames[k] != expectedStart) {
             testFailed("Pulse " + k + " started at " + startFrames[k] + " but expected at " + expectedStart);

Modified: trunk/Source/WebCore/ChangeLog (179837 => 179838)


--- trunk/Source/WebCore/ChangeLog	2015-02-09 18:59:44 UTC (rev 179837)
+++ trunk/Source/WebCore/ChangeLog	2015-02-09 19:45:54 UTC (rev 179838)
@@ -1,3 +1,25 @@
+2015-02-09  Jer Noble  <[email protected]>
+
+        [WebAudio] AudioBufferSourceNodes should accurately play backwards if given a negative playbackRate.
+        https://bugs.webkit.org/show_bug.cgi?id=140955
+
+        Reviewed by Eric Carlson.
+
+        Tests: webaudio/audiobuffersource-negative-playbackrate-interpolated.html
+               webaudio/audiobuffersource-negative-playbackrate.html
+
+        Add support for playing an AudioBufferSourceNode at a negative playbackRate. Change the meaning of
+        start() to set the initial playback position at the end of the play range if the rate of playback
+        is negtive.
+
+        * Modules/webaudio/AudioBufferSourceNode.cpp:
+        (WebCore::AudioBufferSourceNode::AudioBufferSourceNode): Allow the playbackRate AudioParam to range from [-32, 32].
+        (WebCore::AudioBufferSourceNode::renderFromBuffer): Change variable names from "start" and "end" to "min" and "max"
+            for clarity. Add a non-interpolated and interpolated render step for negative playback.
+        (WebCore::AudioBufferSourceNode::start): Drive-by fix: default value of grainDuration is not 0.02.
+        (WebCore::AudioBufferSourceNode::startPlaying): Start playing at the end of the buffer for negative playback.
+        (WebCore::AudioBufferSourceNode::totalPitchRate): Allow the pitch to be negative.
+
 2015-02-09  Darin Adler  <[email protected]>
 
         Try to fix build on platforms that use SVG "all in one" file (Windows).

Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp (179837 => 179838)


--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp	2015-02-09 18:59:44 UTC (rev 179837)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp	2015-02-09 19:45:54 UTC (rev 179838)
@@ -70,7 +70,7 @@
     setNodeType(NodeTypeAudioBufferSource);
 
     m_gain = AudioParam::create(context, "gain", 1.0, 0.0, 1.0);
-    m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, 0.0, MaxRate);
+    m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, -MaxRate, MaxRate);
 
     // Default to mono.  A call to setBuffer() will set the number of output channels to that of the buffer.
     addOutput(std::make_unique<AudioNodeOutput>(this, 1));
@@ -200,47 +200,54 @@
 
     size_t bufferLength = buffer()->length();
     double bufferSampleRate = buffer()->sampleRate();
+    double pitchRate = totalPitchRate();
+    bool reverse = pitchRate < 0;
 
     // Avoid converting from time to sample-frames twice by computing
     // the grain end time first before computing the sample frame.
-    unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength;
-    
-    // This is a HACK to allow for HRTF tail-time - avoids glitch at end.
-    // FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
-    // https://bugs.webkit.org/show_bug.cgi?id=77224
+    unsigned maxFrame;
     if (m_isGrain)
-        endFrame += 512;
+        maxFrame = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate);
+    else
+        maxFrame = bufferLength;
 
     // Do some sanity checking.
-    if (endFrame > bufferLength)
-        endFrame = bufferLength;
-    if (m_virtualReadIndex >= endFrame)
+    if (maxFrame > bufferLength)
+        maxFrame = bufferLength;
+    if (reverse && m_virtualReadIndex <= 0)
+        m_virtualReadIndex = maxFrame - 1;
+    else if (!reverse && m_virtualReadIndex >= maxFrame)
         m_virtualReadIndex = 0; // reset to start
 
     // If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEnd == 0 implies
     // that we should use the entire buffer as the loop, otherwise use the loop values in m_loopStart and m_loopEnd.
-    double virtualEndFrame = endFrame;
-    double virtualDeltaFrames = endFrame;
+    double virtualMaxFrame = maxFrame;
+    double virtualMinFrame = 0;
+    double virtualDeltaFrames = maxFrame;
 
     if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && m_loopEnd > 0 && m_loopStart < m_loopEnd) {
         // Convert from seconds to sample-frames.
-        double loopStartFrame = m_loopStart * buffer()->sampleRate();
-        double loopEndFrame = m_loopEnd * buffer()->sampleRate();
+        double loopMinFrame = m_loopStart * buffer()->sampleRate();
+        double loopMaxFrame = m_loopEnd * buffer()->sampleRate();
 
-        virtualEndFrame = std::min(loopEndFrame, virtualEndFrame);
-        virtualDeltaFrames = virtualEndFrame - loopStartFrame;
+        virtualMaxFrame = std::min(loopMaxFrame, virtualMaxFrame);
+        virtualMinFrame = std::max(loopMinFrame, virtualMinFrame);
+        virtualDeltaFrames = virtualMaxFrame - virtualMinFrame;
     }
 
 
-    double pitchRate = totalPitchRate();
-
     // Sanity check that our playback rate isn't larger than the loop size.
-    if (pitchRate >= virtualDeltaFrames)
+    if (fabs(pitchRate) >= virtualDeltaFrames)
         return false;
 
     // Get local copy.
     double virtualReadIndex = m_virtualReadIndex;
 
+    bool needsInterpolation = virtualReadIndex != floor(virtualReadIndex)
+        || virtualDeltaFrames != floor(virtualDeltaFrames)
+        || virtualMaxFrame != floor(virtualMaxFrame)
+        || virtualMinFrame != floor(virtualMinFrame);
+
     // Render loop - reading from the source buffer to the destination using linear interpolation.
     int framesToProcess = numberOfFrames;
 
@@ -249,14 +256,12 @@
 
     // Optimize for the very common case of playing back with pitchRate == 1.
     // We can avoid the linear interpolation.
-    if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex)
-        && virtualDeltaFrames == floor(virtualDeltaFrames)
-        && virtualEndFrame == floor(virtualEndFrame)) {
+    if (pitchRate == 1 && !needsInterpolation) {
         unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
         unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames);
-        endFrame = static_cast<unsigned>(virtualEndFrame);
+        maxFrame = static_cast<unsigned>(virtualMaxFrame);
         while (framesToProcess > 0) {
-            int framesToEnd = endFrame - readIndex;
+            int framesToEnd = maxFrame - readIndex;
             int framesThisTime = std::min(framesToProcess, framesToEnd);
             framesThisTime = std::max(0, framesThisTime);
 
@@ -268,13 +273,83 @@
             framesToProcess -= framesThisTime;
 
             // Wrap-around.
-            if (readIndex >= endFrame) {
+            if (readIndex >= maxFrame) {
                 readIndex -= deltaFrames;
                 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
                     break;
             }
         }
         virtualReadIndex = readIndex;
+    } else if (pitchRate == -1 && !needsInterpolation) {
+        int readIndex = static_cast<int>(virtualReadIndex);
+        int deltaFrames = static_cast<int>(virtualDeltaFrames);
+        int minFrame = static_cast<int>(virtualMinFrame) - 1;
+        while (framesToProcess > 0) {
+            int framesToEnd = readIndex - minFrame;
+            int framesThisTime = std::min<int>(framesToProcess, framesToEnd);
+            framesThisTime = std::max<int>(0, framesThisTime);
+
+            while (framesThisTime--) {
+                for (unsigned i = 0; i < numberOfChannels; ++i) {
+                    float* destination = destinationChannels[i];
+                    const float* source = sourceChannels[i];
+
+                    destination[writeIndex] = source[readIndex];
+                }
+
+                ++writeIndex;
+                --readIndex;
+                --framesToProcess;
+            }
+
+            // Wrap-around.
+            if (readIndex <= minFrame) {
+                readIndex += deltaFrames;
+                if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
+                    break;
+            }
+        }
+        virtualReadIndex = readIndex;
+    } else if (!pitchRate) {
+        unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+
+        for (unsigned i = 0; i < numberOfChannels; ++i)
+            std::fill_n(destinationChannels[i], framesToProcess, sourceChannels[i][readIndex]);
+    } else if (reverse) {
+        unsigned maxFrame = static_cast<unsigned>(virtualMaxFrame);
+        unsigned minFrame = static_cast<unsigned>(floorf(virtualMinFrame));
+
+        while (framesToProcess--) {
+            unsigned readIndex = static_cast<unsigned>(floorf(virtualReadIndex));
+            double interpolationFactor = virtualReadIndex - readIndex;
+
+            unsigned readIndex2 = readIndex + 1;
+            if (readIndex2 >= maxFrame)
+                readIndex2 = loop() ? minFrame : maxFrame - 1;
+
+            // Linear interpolation.
+            for (unsigned i = 0; i < numberOfChannels; ++i) {
+                float* destination = destinationChannels[i];
+                const float* source = sourceChannels[i];
+
+                double sample1 = source[readIndex];
+                double sample2 = source[readIndex2];
+                double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
+
+                destination[writeIndex] = narrowPrecisionToFloat(sample);
+            }
+
+            writeIndex++;
+
+            virtualReadIndex += pitchRate;
+
+            // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
+            if (virtualReadIndex < virtualMinFrame) {
+                virtualReadIndex += virtualDeltaFrames;
+                if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
+                    break;
+            }
+        }
     } else {
         while (framesToProcess--) {
             unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
@@ -311,7 +386,7 @@
             virtualReadIndex += pitchRate;
 
             // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
-            if (virtualReadIndex >= virtualEndFrame) {
+            if (virtualReadIndex >= virtualMaxFrame) {
                 virtualReadIndex -= virtualDeltaFrames;
                 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
                     break;
@@ -382,7 +457,7 @@
 
 void AudioBufferSourceNode::start(double when, double grainOffset, ExceptionCode& ec)
 {
-    startPlaying(Partial, when, grainOffset, 0, ec);
+    startPlaying(Partial, when, grainOffset, buffer() ? buffer()->duration() - grainOffset : 0, ec);
 }
 
 void AudioBufferSourceNode::start(double when, double grainOffset, double grainDuration, ExceptionCode& ec)
@@ -431,7 +506,7 @@
         m_grainDuration = std::min(maxDuration, grainDuration);
     } else {
         m_grainOffset = 0.0;
-        m_grainDuration = DefaultGrainDuration;
+        m_grainDuration = buffer()->duration();
     }
 
     m_startTime = when;
@@ -440,7 +515,10 @@
     // at a sub-sample position since it will degrade the quality.
     // When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer.
     // Since playbackRate == 1 is very common, it's worth considering quality.
-    m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
+    if (totalPitchRate() < 0)
+        m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, buffer()->sampleRate()) - 1;
+    else
+        m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
     
     m_playbackState = SCHEDULED_STATE;
 }
@@ -471,11 +549,7 @@
 
     double totalRate = dopplerRate * sampleRateFactor * basePitchRate;
 
-    // Sanity check the total rate.  It's very important that the resampler not get any bad rate values.
-    totalRate = std::max(0.0, totalRate);
-    if (!totalRate)
-        totalRate = 1; // zero rate is considered illegal
-    totalRate = std::min(MaxRate, totalRate);
+    totalRate = std::max(-MaxRate, std::min(MaxRate, totalRate));
     
     bool isTotalRateValid = !std::isnan(totalRate) && !std::isinf(totalRate);
     ASSERT(isTotalRateValid);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to