Title: [198035] trunk/Source/WebCore
Revision
198035
Author
[email protected]
Date
2016-03-11 11:35:15 -0800 (Fri, 11 Mar 2016)

Log Message

Web Audio becomes distorted after sample rate changes
https://bugs.webkit.org/show_bug.cgi?id=154538
<rdar://problem/24771292>

Reviewed by Darin Adler.

When the underlying audio hardware sample rate changes, the AudioUnit render callback will begin asking
for fewer or more frames. For example, when the sample rate goes from 44.1kHz to 48kHz, it will ask for
118 samples instead of 128. (And vice-versa, 140 samples instead of 128.) But the Web Audio engine can only
really handle requests in multiples of 128 samples. In the case where there are requests for < 128 samples,
actually render 128, but save off the unrequested samples in a separate bus. Then fill that bus during the
next request.

* platform/audio/AudioBus.cpp:
(WebCore::AudioBus::copyFromRange): Added utility method.
* platform/audio/AudioBus.h:
* platform/audio/ios/AudioDestinationIOS.cpp:
(WebCore::AudioDestinationIOS::AudioDestinationIOS): Create a "spare" bus.
(WebCore::assignAudioBuffersToBus): Moved from inside render.
(WebCore::AudioDestinationIOS::render): Save off extra samples to the "spare" bus.
* platform/audio/ios/AudioDestinationIOS.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (198034 => 198035)


--- trunk/Source/WebCore/ChangeLog	2016-03-11 19:30:55 UTC (rev 198034)
+++ trunk/Source/WebCore/ChangeLog	2016-03-11 19:35:15 UTC (rev 198035)
@@ -1,3 +1,27 @@
+2016-03-10  Jer Noble  <[email protected]>
+
+        Web Audio becomes distorted after sample rate changes
+        https://bugs.webkit.org/show_bug.cgi?id=154538
+        <rdar://problem/24771292>
+
+        Reviewed by Darin Adler.
+
+        When the underlying audio hardware sample rate changes, the AudioUnit render callback will begin asking
+        for fewer or more frames. For example, when the sample rate goes from 44.1kHz to 48kHz, it will ask for
+        118 samples instead of 128. (And vice-versa, 140 samples instead of 128.) But the Web Audio engine can only
+        really handle requests in multiples of 128 samples. In the case where there are requests for < 128 samples,
+        actually render 128, but save off the unrequested samples in a separate bus. Then fill that bus during the
+        next request.
+
+        * platform/audio/AudioBus.cpp:
+        (WebCore::AudioBus::copyFromRange): Added utility method.
+        * platform/audio/AudioBus.h:
+        * platform/audio/ios/AudioDestinationIOS.cpp:
+        (WebCore::AudioDestinationIOS::AudioDestinationIOS): Create a "spare" bus.
+        (WebCore::assignAudioBuffersToBus): Moved from inside render.
+        (WebCore::AudioDestinationIOS::render): Save off extra samples to the "spare" bus.
+        * platform/audio/ios/AudioDestinationIOS.h:
+
 2016-03-11  Yusuke Suzuki  <[email protected]>
 
         Unreviewed build fix after r198023.

Modified: trunk/Source/WebCore/platform/audio/AudioBus.cpp (198034 => 198035)


--- trunk/Source/WebCore/platform/audio/AudioBus.cpp	2016-03-11 19:30:55 UTC (rev 198034)
+++ trunk/Source/WebCore/platform/audio/AudioBus.cpp	2016-03-11 19:35:15 UTC (rev 198035)
@@ -213,6 +213,33 @@
         channel(i)->scale(scale);
 }
 
+void AudioBus::copyFromRange(const AudioBus& sourceBus, unsigned startFrame, unsigned endFrame)
+{
+    if (!topologyMatches(sourceBus)) {
+        ASSERT_NOT_REACHED();
+        zero();
+        return;
+    }
+
+    size_t numberOfSourceFrames = sourceBus.length();
+    bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames;
+    ASSERT(isRangeSafe);
+    if (!isRangeSafe) {
+        zero();
+        return;
+    }
+
+    unsigned numberOfChannels = this->numberOfChannels();
+    ASSERT(numberOfChannels <= MaxBusChannels);
+    if (numberOfChannels > MaxBusChannels) {
+        zero();
+        return;
+    }
+
+    for (unsigned i = 0; i < numberOfChannels; ++i)
+        channel(i)->copyFromRange(sourceBus.channel(i), startFrame, endFrame);
+}
+
 void AudioBus::copyFrom(const AudioBus& sourceBus, ChannelInterpretation channelInterpretation)
 {
     if (&sourceBus == this)

Modified: trunk/Source/WebCore/platform/audio/AudioBus.h (198034 => 198035)


--- trunk/Source/WebCore/platform/audio/AudioBus.h	2016-03-11 19:30:55 UTC (rev 198034)
+++ trunk/Source/WebCore/platform/audio/AudioBus.h	2016-03-11 19:35:15 UTC (rev 198035)
@@ -122,6 +122,9 @@
     void reset() { m_isFirstTime = true; } // for de-zippering
 
     // Copies the samples from the source bus to this one.
+    void copyFromRange(const AudioBus& sourceBus, unsigned startFrame, unsigned endFrame);
+
+    // Copies the samples from the source bus to this one.
     // This is just a simple per-channel copy if the number of channels match, otherwise an up-mix or down-mix is done.
     void copyFrom(const AudioBus& sourceBus, ChannelInterpretation = Speakers);
 

Modified: trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp (198034 => 198035)


--- trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp	2016-03-11 19:30:55 UTC (rev 198034)
+++ trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp	2016-03-11 19:35:15 UTC (rev 198035)
@@ -101,6 +101,7 @@
     : m_outputUnit(0)
     , m_callback(callback)
     , m_renderBus(AudioBus::create(2, kRenderBufferSize, false))
+    , m_spareBus(AudioBus::create(2, kRenderBufferSize, true))
     , m_sampleRate(sampleRate)
     , m_isPlaying(false)
 {
@@ -199,19 +200,47 @@
         setIsPlaying(false);
 }
 
+static void assignAudioBuffersToBus(AudioBuffer* buffers, AudioBus& bus, UInt32 numberOfBuffers, UInt32 numberOfFrames, UInt32 frameOffset, UInt32 framesThisTime)
+{
+    for (UInt32 i = 0; i < numberOfBuffers; ++i) {
+        UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
+        UInt32 byteOffset = frameOffset * bytesPerFrame;
+        float* memory = reinterpret_cast<float*>(reinterpret_cast<char*>(buffers[i].mData) + byteOffset);
+        bus.setChannelMemory(i, memory, framesThisTime);
+    }
+}
+
 // Pulls on our provider to get rendered audio stream.
 OSStatus AudioDestinationIOS::render(UInt32 numberOfFrames, AudioBufferList* ioData)
 {
     AudioBuffer* buffers = ioData->mBuffers;
-    for (UInt32 frameOffset = 0; frameOffset + kRenderBufferSize <= numberOfFrames; frameOffset += kRenderBufferSize) {
-        UInt32 remainingFrames = std::min<UInt32>(kRenderBufferSize, numberOfFrames - frameOffset);
-        for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) {
-            UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
-            UInt32 byteOffset = frameOffset * bytesPerFrame;
-            float* memory = (float*)((char*)buffers[i].mData + byteOffset);
-            m_renderBus->setChannelMemory(i, memory, remainingFrames);
+    UInt32 numberOfBuffers = ioData->mNumberBuffers;
+    UInt32 framesRemaining = numberOfFrames;
+    UInt32 frameOffset = 0;
+    while (framesRemaining > 0) {
+        if (m_firstSpareFrame && m_lastSpareFrame) {
+            ASSERT(m_firstSpareFrame < m_lastSpareFrame);
+            UInt32 framesThisTime = m_lastSpareFrame - m_firstSpareFrame;
+            assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
+            m_renderBus->copyFromRange(*m_spareBus, m_firstSpareFrame, m_lastSpareFrame);
+            frameOffset += framesThisTime;
+            framesRemaining -= framesThisTime;
+            m_firstSpareFrame = 0;
+            m_lastSpareFrame = 0;
         }
-        m_callback.render(0, m_renderBus.get(), remainingFrames);
+
+        UInt32 framesThisTime = std::min<UInt32>(kRenderBufferSize, framesRemaining);
+        assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
+
+        if (framesThisTime < kRenderBufferSize) {
+            m_callback.render(0, m_spareBus.get(), kRenderBufferSize);
+            m_renderBus->copyFromRange(*m_spareBus, 0, framesThisTime);
+            m_firstSpareFrame = framesThisTime;
+            m_lastSpareFrame = kRenderBufferSize - 1;
+        } else
+            m_callback.render(0, m_renderBus.get(), framesThisTime);
+        frameOffset += framesThisTime;
+        framesRemaining -= framesThisTime;
     }
 
     return noErr;

Modified: trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.h (198034 => 198035)


--- trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.h	2016-03-11 19:30:55 UTC (rev 198034)
+++ trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.h	2016-03-11 19:35:15 UTC (rev 198035)
@@ -65,6 +65,9 @@
     AudioUnit m_outputUnit;
     AudioIOCallback& m_callback;
     RefPtr<AudioBus> m_renderBus;
+    RefPtr<AudioBus> m_spareBus;
+    unsigned m_firstSpareFrame { 0 };
+    unsigned m_lastSpareFrame { 0 };
 
     double m_sampleRate;
     bool m_isPlaying;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to