Diff
Modified: trunk/Source/WebCore/ChangeLog (267013 => 267014)
--- trunk/Source/WebCore/ChangeLog 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/ChangeLog 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1,3 +1,71 @@
+2020-09-14 Chris Dumez <[email protected]>
+
+ Add proper support for AudioContextOptions.sampleRate
+ https://bugs.webkit.org/show_bug.cgi?id=216425
+
+ Reviewed by Eric Carlson.
+
+ Add proper support for AudioContextOptions.sampleRate. Previously, our AudioContext always ran
+ at the hardware's sampleRate, no matter what value was set for AudioContextOptions.sampleRate.
+
+ This patch is based on the following Chromium changes:
+ - https://chromium-review.googlesource.com/c/chromium/src/+/1482957
+ - https://codereview.chromium.org/2549093009
+ - https://codereview.chromium.org/14189035
+
+ * Modules/webaudio/DefaultAudioDestinationNode.cpp:
+ (WebCore::DefaultAudioDestinationNode::createDestination):
+ When creating an AudioDestination, pass the requested AudioContext sample rate
+ instead of the hardware sample rate.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+
+ * platform/audio/AudioFIFO.cpp: Removed.
+ * platform/audio/AudioFIFO.h: Removed.
+ * platform/audio/AudioPullFIFO.cpp: Removed.
+ * platform/audio/AudioPullFIFO.h: Removed.
+ * platform/audio/PushPullFIFO.cpp: Added.
+ * platform/audio/PushPullFIFO.h: Added.
+ Replace AudioFIFO and AudioPullFIFO with a new PushPullFIFO replacement, similarly
+ to what was done in Chromium in:
+ - https://codereview.chromium.org/2549093009
+
+ * platform/audio/MultiChannelResampler.cpp:
+ (WebCore::MultiChannelResampler::MultiChannelResampler):
+ * platform/audio/MultiChannelResampler.h:
+ * platform/audio/SincResampler.cpp:
+ (WebCore::SincResampler::SincResampler):
+ (WebCore::SincResampler::updateRegions):
+ (WebCore::SincResampler::initializeKernel):
+ (WebCore::SincResampler::process):
+ * platform/audio/SincResampler.h:
+ Add parameter to MultiChannelResampler & SincResampler to allow the client to specify
+ the size of the buffer in frames when the resampler calls AudioSourceProvider::provideInput()
+ to get input data. This is necessary because our WebAudio implementation uses a static
+ buffer size of 128 frames. This is similar to what was done in Chromium in:
+ - https://codereview.chromium.org/14189035
+
+ * platform/audio/cocoa/AudioDestinationCocoa.cpp:
+ (WebCore::AudioDestinationCocoa::AudioDestinationCocoa):
+ (WebCore::AudioDestinationCocoa::setAudioStreamBasicDescription):
+ (WebCore::AudioDestinationCocoa::render):
+ (WebCore::AudioDestinationCocoa::provideInput):
+ * platform/audio/cocoa/AudioDestinationCocoa.h:
+ - Adopt PushPullFIFO to resolve the buffer size mismatch between the WebAudio engine and
+ the callback function from the actual audio device, similarly to what was done in Chromium.
+ - When the context's sample rate differs from the hardware sample rate, instantiate a
+ MultiChannelResampler and use it in render() to do the resampling.
+
+ * platform/audio/ios/AudioDestinationIOS.cpp:
+ (WebCore::AudioDestinationCocoa::configure):
+ * platform/audio/mac/AudioDestinationMac.cpp:
+ (WebCore::AudioDestinationCocoa::configure):
+ Drop sampleRate parameter as it is no longer needed.
+
+ * platform/mock/MockAudioDestinationCocoa.cpp:
+ (WebCore::MockAudioDestinationCocoa::tick):
+
2020-09-14 Sam Weinig <[email protected]>
[WebIDL] Split DOM Parsing related functions out of Element and ShadowRoot and into their own IDL files to match specs
Modified: trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp (267013 => 267014)
--- trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -82,10 +82,8 @@
void DefaultAudioDestinationNode::createDestination()
{
- float hardwareSampleRate = AudioDestination::hardwareSampleRate();
- LOG(WebAudio, ">>>> hardwareSampleRate = %f\n", hardwareSampleRate);
-
- m_destination = platformStrategies()->mediaStrategy().createAudioDestination(*this, m_inputDeviceId, m_numberOfInputChannels, channelCount(), hardwareSampleRate);
+ ALWAYS_LOG(LOGIDENTIFIER, "contextSampleRate = ", m_sampleRate, ", hardwareSampleRate = ", AudioDestination::hardwareSampleRate());
+ m_destination = platformStrategies()->mediaStrategy().createAudioDestination(*this, m_inputDeviceId, m_numberOfInputChannels, channelCount(), m_sampleRate);
}
void DefaultAudioDestinationNode::enableInput(const String& inputDeviceId)
Modified: trunk/Source/WebCore/Sources.txt (267013 => 267014)
--- trunk/Source/WebCore/Sources.txt 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/Sources.txt 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1788,9 +1788,7 @@
platform/audio/AudioBus.cpp
platform/audio/AudioChannel.cpp
platform/audio/AudioDSPKernelProcessor.cpp
-platform/audio/AudioFIFO.cpp
platform/audio/AudioHardwareListener.cpp
-platform/audio/AudioPullFIFO.cpp
platform/audio/AudioResampler.cpp
platform/audio/AudioResamplerKernel.cpp
platform/audio/AudioUtilities.cpp
@@ -1814,6 +1812,7 @@
platform/audio/Panner.cpp
platform/audio/PlatformMediaSession.cpp
platform/audio/PlatformMediaSessionManager.cpp
+platform/audio/PushPullFIFO.cpp
platform/audio/Reverb.cpp
platform/audio/ReverbAccumulationBuffer.cpp
platform/audio/ReverbConvolver.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (267013 => 267014)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-09-14 15:47:53 UTC (rev 267014)
@@ -2526,6 +2526,7 @@
83C5795D1DA5C301006FACA8 /* ScrollToOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 8350C3E71DA59B6200356446 /* ScrollToOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
83D35AEC1C7187FA00F70D5A /* XMLHttpRequestEventTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D35AEA1C7187ED00F70D5A /* XMLHttpRequestEventTarget.h */; };
83D35AF21C718D9000F70D5A /* JSXMLHttpRequestEventTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D35AF01C718D8400F70D5A /* JSXMLHttpRequestEventTarget.h */; };
+ 83D511F6250C1CBF002EDC51 /* PushPullFIFO.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D511F5250C1CA8002EDC51 /* PushPullFIFO.h */; };
83DB9E0F24DA19490037B468 /* BiquadFilterType.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DB9E0C24DA18B50037B468 /* BiquadFilterType.h */; };
83DB9E1024DA19570037B468 /* BiquadFilterOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DB9E0E24DA18B60037B468 /* BiquadFilterOptions.h */; };
83E359A21BB1031D002CEB98 /* JSHTMLTimeElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E359A01BB1031D002CEB98 /* JSHTMLTimeElement.h */; };
@@ -10710,6 +10711,8 @@
83D35AEB1C7187ED00F70D5A /* XMLHttpRequestEventTarget.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = XMLHttpRequestEventTarget.idl; sourceTree = "<group>"; };
83D35AEF1C718D8400F70D5A /* JSXMLHttpRequestEventTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSXMLHttpRequestEventTarget.cpp; sourceTree = "<group>"; };
83D35AF01C718D8400F70D5A /* JSXMLHttpRequestEventTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSXMLHttpRequestEventTarget.h; sourceTree = "<group>"; };
+ 83D511F3250C1CA8002EDC51 /* PushPullFIFO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PushPullFIFO.cpp; sourceTree = "<group>"; };
+ 83D511F5250C1CA8002EDC51 /* PushPullFIFO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushPullFIFO.h; sourceTree = "<group>"; };
83DB9E0A24DA18B40037B468 /* BiquadFilterType.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = BiquadFilterType.idl; sourceTree = "<group>"; };
83DB9E0C24DA18B50037B468 /* BiquadFilterType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BiquadFilterType.h; sourceTree = "<group>"; };
83DB9E0D24DA18B50037B468 /* BiquadFilterOptions.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = BiquadFilterOptions.idl; sourceTree = "<group>"; };
@@ -30036,6 +30039,8 @@
070E09181875ED93003A1D3C /* PlatformMediaSession.h */,
CDAE8C071746B95700532D78 /* PlatformMediaSessionManager.cpp */,
CDAE8C081746B95700532D78 /* PlatformMediaSessionManager.h */,
+ 83D511F3250C1CA8002EDC51 /* PushPullFIFO.cpp */,
+ 83D511F5250C1CA8002EDC51 /* PushPullFIFO.h */,
FD31606E12B026F700C1A359 /* Reverb.cpp */,
FD31606F12B026F700C1A359 /* Reverb.h */,
FD31607012B026F700C1A359 /* ReverbAccumulationBuffer.cpp */,
@@ -33267,6 +33272,7 @@
57303BEB20097F4000355965 /* PublicKeyCredentialType.h in Headers */,
0081FF0016B0A2D3008AAA7A /* PublicSuffix.h in Headers */,
10FB084B14E15C7E00A3DB98 /* PublicURLManager.h in Headers */,
+ 83D511F6250C1CBF002EDC51 /* PushPullFIFO.h in Headers */,
550A0BCA085F6039007353D6 /* QualifiedName.h in Headers */,
83C1F5941EDF69D300410D27 /* QualifiedNameCache.h in Headers */,
A15E31F41E0CB0B5004B371C /* QuickLook.h in Headers */,
Deleted: trunk/Source/WebCore/platform/audio/AudioFIFO.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/AudioFIFO.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/AudioFIFO.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#if ENABLE(WEB_AUDIO)
-
-#include "AudioFIFO.h"
-
-namespace WebCore {
-
-AudioFIFO::AudioFIFO(unsigned numberOfChannels, size_t fifoLength)
- : m_fifoAudioBus(AudioBus::create(numberOfChannels, fifoLength))
- , m_fifoLength(fifoLength)
- , m_framesInFifo(0)
- , m_readIndex(0)
- , m_writeIndex(0)
-{
-}
-
-void AudioFIFO::consume(AudioBus* destination, size_t framesToConsume)
-{
- bool isGood = destination && (framesToConsume <= m_fifoLength) && (framesToConsume <= m_framesInFifo) && (destination->length() >= framesToConsume);
- ASSERT(isGood);
- if (!isGood)
- return;
-
- // Copy the requested number of samples to the destination.
-
- size_t part1Length;
- size_t part2Length;
- findWrapLengths(m_readIndex, framesToConsume, part1Length, part2Length);
-
- size_t numberOfChannels = m_fifoAudioBus->numberOfChannels();
-
- for (size_t channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
- float* destinationData = destination->channel(channelIndex)->mutableData();
- const float* sourceData = m_fifoAudioBus->channel(channelIndex)->data();
-
- bool isCopyGood = ((m_readIndex < m_fifoLength)
- && (m_readIndex + part1Length) <= m_fifoLength
- && (part1Length <= destination->length())
- && (part1Length + part2Length) <= destination->length());
- ASSERT(isCopyGood);
- if (!isCopyGood)
- return;
-
- memcpy(destinationData, sourceData + m_readIndex, part1Length * sizeof(*sourceData));
- // Handle wrap around of the FIFO, if needed.
- if (part2Length)
- memcpy(destinationData + part1Length, sourceData, part2Length * sizeof(*sourceData));
- }
- m_readIndex = updateIndex(m_readIndex, framesToConsume);
- ASSERT(m_framesInFifo >= framesToConsume);
- m_framesInFifo -= framesToConsume;
-}
-
-void AudioFIFO::push(const AudioBus* sourceBus)
-{
- // Copy the sourceBus into the FIFO buffer.
-
- bool isGood = sourceBus && (m_framesInFifo + sourceBus->length() <= m_fifoLength);
- if (!isGood)
- return;
-
- size_t sourceLength = sourceBus->length();
- size_t part1Length;
- size_t part2Length;
- findWrapLengths(m_writeIndex, sourceLength, part1Length, part2Length);
-
- size_t numberOfChannels = m_fifoAudioBus->numberOfChannels();
-
- for (size_t channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
- float* destination = m_fifoAudioBus->channel(channelIndex)->mutableData();
- const float* source = sourceBus->channel(channelIndex)->data();
-
- bool isCopyGood = ((m_writeIndex < m_fifoLength)
- && (m_writeIndex + part1Length) <= m_fifoLength
- && part2Length < m_fifoLength
- && part1Length + part2Length <= sourceLength);
- ASSERT(isCopyGood);
- if (!isCopyGood)
- return;
-
- memcpy(destination + m_writeIndex, source, part1Length * sizeof(*destination));
-
- // Handle wrap around of the FIFO, if needed.
- if (part2Length)
- memcpy(destination, source + part1Length, part2Length * sizeof(*destination));
- }
-
- m_framesInFifo += sourceLength;
- ASSERT(m_framesInFifo <= m_fifoLength);
- m_writeIndex = updateIndex(m_writeIndex, sourceLength);
-}
-
-void AudioFIFO::findWrapLengths(size_t index, size_t size, size_t& part1Length, size_t& part2Length)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(index < m_fifoLength && size <= m_fifoLength);
- if (index < m_fifoLength && size <= m_fifoLength) {
- if (index + size > m_fifoLength) {
- // Need to wrap. Figure out the length of each piece.
- part1Length = m_fifoLength - index;
- part2Length = size - part1Length;
- } else {
- // No wrap needed.
- part1Length = size;
- part2Length = 0;
- }
- } else {
- // Invalid values for index or size. Set the part lengths to zero so nothing is copied.
- part1Length = 0;
- part2Length = 0;
- }
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(WEB_AUDIO)
Deleted: trunk/Source/WebCore/platform/audio/AudioFIFO.h (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/AudioFIFO.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/AudioFIFO.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef AudioFIFO_h
-#define AudioFIFO_h
-
-#include "AudioBus.h"
-
-namespace WebCore {
-
-class AudioFIFO final {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- // Create a FIFO large enough to hold |fifoLength| frames of data of |numberOfChannels| channels.
- AudioFIFO(unsigned numberOfChannels, size_t fifoLength);
-
- // Push the data from the bus into the FIFO.
- void push(const AudioBus*);
-
- // Consume |framesToConsume| frames of data from the FIFO and put them in |destination|. The
- // corresponding frames are removed from the FIFO.
- void consume(AudioBus* destination, size_t framesToConsume);
-
- // Number of frames of data that are currently in the FIFO.
- size_t framesInFifo() const { return m_framesInFifo; }
-
-private:
- // Update the FIFO index by the step, with appropriate wrapping around the endpoint.
- int updateIndex(int index, int step) { return (index + step) % m_fifoLength; }
-
- void findWrapLengths(size_t index, size_t providerSize, size_t& part1Length, size_t& part2Length);
-
- // The FIFO itself. In reality, the FIFO is a circular buffer.
- RefPtr<AudioBus> m_fifoAudioBus;
-
- // The total available space in the FIFO.
- size_t m_fifoLength;
-
- // The number of actual elements in the FIFO
- size_t m_framesInFifo;
-
- // Where to start reading from the FIFO.
- size_t m_readIndex;
-
- // Where to start writing to the FIFO.
- size_t m_writeIndex;
-};
-
-} // namespace WebCore
-
-#endif // AudioFIFO.h
Deleted: trunk/Source/WebCore/platform/audio/AudioPullFIFO.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/AudioPullFIFO.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/AudioPullFIFO.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#if ENABLE(WEB_AUDIO)
-
-#include "AudioPullFIFO.h"
-
-namespace WebCore {
-
-AudioPullFIFO::AudioPullFIFO(AudioSourceProvider& audioProvider, unsigned numberOfChannels, size_t fifoLength, size_t providerSize)
- : m_provider(audioProvider)
- , m_fifo(numberOfChannels, fifoLength)
- , m_providerSize(providerSize)
- , m_tempBus(AudioBus::create(numberOfChannels, providerSize))
-{
-}
-
-void AudioPullFIFO::consume(AudioBus* destination, size_t framesToConsume)
-{
- if (!destination)
- return;
-
- if (framesToConsume > m_fifo.framesInFifo()) {
- // We don't have enough data in the FIFO to fulfill the request. Ask for more data.
- fillBuffer(framesToConsume - m_fifo.framesInFifo());
- }
-
- m_fifo.consume(destination, framesToConsume);
-}
-
-void AudioPullFIFO::fillBuffer(size_t numberOfFrames)
-{
- // Keep asking the provider to give us data until we have received at least |numberOfFrames| of
- // data. Stuff the data into the FIFO.
- size_t framesProvided = 0;
-
- while (framesProvided < numberOfFrames) {
- m_provider.provideInput(m_tempBus.get(), m_providerSize);
-
- m_fifo.push(m_tempBus.get());
-
- framesProvided += m_providerSize;
- }
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(WEB_AUDIO)
Deleted: trunk/Source/WebCore/platform/audio/AudioPullFIFO.h (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/AudioPullFIFO.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/AudioPullFIFO.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef AudioPullFIFO_h
-#define AudioPullFIFO_h
-
-#include "AudioBus.h"
-#include "AudioFIFO.h"
-#include "AudioSourceProvider.h"
-
-namespace WebCore {
-
-// A FIFO (First In First Out) buffer to handle mismatches in buffer sizes between a provider and
-// receiver. The receiver will "pull" data from this FIFO. If data is already available in the
-// FIFO, it is provided to the receiver. If insufficient data is available to satisfy the request,
-// the FIFO will ask the provider for more data when necessary to fulfill a request. Contrast this
-// with a "push" FIFO, where the sender pushes data to the FIFO which will itself push the data to
-// the receiver when the FIFO is full.
-class AudioPullFIFO final {
- WTF_MAKE_FAST_ALLOCATED;
-public:
- // Create a FIFO that gets data from |provider|. The FIFO will be large enough to hold
- // |fifoLength| frames of data of |numberOfChannels| channels. The AudioSourceProvider will be
- // asked to produce |providerSize| frames when the FIFO needs more data.
- AudioPullFIFO(AudioSourceProvider& audioProvider, unsigned numberOfChannels, size_t fifoLength, size_t providerSize);
-
- // Read |framesToConsume| frames from the FIFO into the destination. If the FIFO does not have
- // enough data, we ask the |provider| to get more data to fulfill the request.
- void consume(AudioBus* destination, size_t framesToConsume);
-
-private:
- // Fill the FIFO buffer with at least |numberOfFrames| more data.
- void fillBuffer(size_t numberOfFrames);
-
- // The provider of the data in our FIFO.
- AudioSourceProvider& m_provider;
-
- // The actual FIFO
- AudioFIFO m_fifo;
-
- // Number of frames of data that the provider will produce per call.
- unsigned int m_providerSize;
-
- // Temporary workspace to hold the data from the provider.
- RefPtr<AudioBus> m_tempBus;
-};
-
-} // namespace WebCore
-
-#endif // AudioPullFIFO.h
Modified: trunk/Source/WebCore/platform/audio/MultiChannelResampler.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/MultiChannelResampler.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/MultiChannelResampler.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -92,13 +92,13 @@
size_t m_framesToProcess { 0 }; // Used to verify that all channels ask for the same amount.
};
-MultiChannelResampler::MultiChannelResampler(double scaleFactor, unsigned numberOfChannels)
+MultiChannelResampler::MultiChannelResampler(double scaleFactor, unsigned numberOfChannels, Optional<unsigned> requestFrames)
: m_numberOfChannels(numberOfChannels)
, m_channelProvider(makeUnique<ChannelProvider>(m_numberOfChannels))
{
// Create each channel's resampler.
for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex)
- m_kernels.append(makeUnique<SincResampler>(scaleFactor));
+ m_kernels.append(makeUnique<SincResampler>(scaleFactor, requestFrames));
}
MultiChannelResampler::~MultiChannelResampler() = default;
Modified: trunk/Source/WebCore/platform/audio/MultiChannelResampler.h (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/MultiChannelResampler.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/MultiChannelResampler.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -40,7 +40,8 @@
class MultiChannelResampler final {
WTF_MAKE_FAST_ALLOCATED;
public:
- explicit MultiChannelResampler(double scaleFactor, unsigned numberOfChannels);
+ // requestFrames constrols the size of the buffer in frames when AudioSourceProvider::provideInput() is called.
+ explicit MultiChannelResampler(double scaleFactor, unsigned numberOfChannels, Optional<unsigned> requestFrames = WTF::nullopt);
~MultiChannelResampler();
// Process given AudioSourceProvider for streaming applications.
Added: trunk/Source/WebCore/platform/audio/PushPullFIFO.cpp (0 => 267014)
--- trunk/Source/WebCore/platform/audio/PushPullFIFO.cpp (rev 0)
+++ trunk/Source/WebCore/platform/audio/PushPullFIFO.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017 The Chromium Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+#include "PushPullFIFO.h"
+
+#include "AudioBus.h"
+
+namespace WebCore {
+
+const size_t PushPullFIFO::kMaxFIFOLength = 65536;
+
+PushPullFIFO::PushPullFIFO(unsigned numberOfChannels, size_t fifoLength)
+ : m_fifoLength(fifoLength)
+{
+ ASSERT(m_fifoLength <= kMaxFIFOLength);
+ m_fifoBus = AudioBus::create(numberOfChannels, m_fifoLength);
+}
+
+PushPullFIFO::~PushPullFIFO() = default;
+
+// Push the data from |inputBus| to FIFO. The size of push is determined by
+// the length of |inputBus|.
+void PushPullFIFO::push(const AudioBus* inputBus)
+{
+ ASSERT(inputBus);
+ ASSERT(inputBus->length() <= m_fifoLength);
+ ASSERT(m_indexWrite < m_fifoLength);
+
+ const size_t inputBusLength = inputBus->length();
+ const size_t remainder = m_fifoLength - m_indexWrite;
+
+ for (unsigned i = 0; i < m_fifoBus->numberOfChannels(); ++i) {
+ float* fifoBusChannel = m_fifoBus->channel(i)->mutableData();
+ const float* inputBusChannel = inputBus->channel(i)->data();
+ if (remainder >= inputBusLength) {
+ // The remainder is big enough for the input data.
+ memcpy(fifoBusChannel + m_indexWrite, inputBusChannel, inputBusLength * sizeof(*fifoBusChannel));
+ } else {
+ // The input data overflows the remainder size. Wrap around the index.
+ memcpy(fifoBusChannel + m_indexWrite, inputBusChannel, remainder * sizeof(*fifoBusChannel));
+ memcpy(fifoBusChannel, inputBusChannel + remainder, (inputBusLength - remainder) * sizeof(*fifoBusChannel));
+ }
+ }
+
+ // Update the write index; wrap it around if necessary.
+ m_indexWrite = (m_indexWrite + inputBusLength) % m_fifoLength;
+
+ // In case of overflow, move the |indexRead| to the updated |indexWrite| to
+ // avoid reading overwritten frames by the next pull.
+ if (inputBusLength > m_fifoLength - m_framesAvailable)
+ m_indexRead = m_indexWrite;
+
+ // Update the number of frames available in FIFO.
+ m_framesAvailable = std::min(m_framesAvailable + inputBusLength, m_fifoLength);
+ ASSERT(((m_indexRead + m_framesAvailable) % m_fifoLength) == m_indexWrite);
+}
+
+// Pull the data out of FIFO to |outputBus|. If remaining frame in the FIFO
+// is less than the frames to pull, provides remaining frame plus the silence.
+size_t PushPullFIFO::pull(AudioBus* outputBus, size_t framesRequested)
+{
+ ASSERT(outputBus);
+ ASSERT(framesRequested <= outputBus->length());
+ ASSERT(framesRequested <= m_fifoLength);
+ ASSERT(m_indexRead < m_fifoLength);
+
+ const size_t remainder = m_fifoLength - m_indexRead;
+ const size_t framesToFill = std::min(m_framesAvailable, framesRequested);
+
+ for (unsigned i = 0; i < m_fifoBus->numberOfChannels(); ++i) {
+ const float* fifoBusChannel = m_fifoBus->channel(i)->data();
+ float* outputBusChannel = outputBus->channel(i)->mutableData();
+
+ // Fill up the output bus with the available frames first.
+ if (remainder >= framesToFill) {
+ // The remainder is big enough for the frames to pull.
+ memcpy(outputBusChannel, fifoBusChannel + m_indexRead, framesToFill * sizeof(*fifoBusChannel));
+ } else {
+ // The frames to pull is bigger than the remainder size.
+ // Wrap around the index.
+ memcpy(outputBusChannel, fifoBusChannel + m_indexRead, remainder * sizeof(*fifoBusChannel));
+ memcpy(outputBusChannel + remainder, fifoBusChannel, (framesToFill - remainder) * sizeof(*fifoBusChannel));
+ }
+
+ // The frames available was not enough to fulfill the requested frames. Fill
+ // the rest of the channel with silence.
+ if (framesRequested > framesToFill)
+ memset(outputBusChannel + framesToFill, 0, (framesRequested - framesToFill) * sizeof(*outputBusChannel));
+ }
+
+ // Update the read index; wrap it around if necessary.
+ m_indexRead = (m_indexRead + framesToFill) % m_fifoLength;
+
+ // In case of underflow, move the |indexWrite| to the updated |indexRead|.
+ if (framesRequested > framesToFill)
+ m_indexWrite = m_indexRead;
+
+ // Update the number of frames in FIFO.
+ m_framesAvailable -= framesToFill;
+ ASSERT(((m_indexRead + m_framesAvailable) % m_fifoLength) == m_indexWrite);
+
+ // |framesRequested > m_framesAvailable| means the frames in FIFO is not
+ // enough to fulfill the requested frames from the audio device.
+ return framesRequested > m_framesAvailable ? framesRequested - m_framesAvailable : 0;
+}
+
+unsigned PushPullFIFO::numberOfChannels() const
+{
+ return m_fifoBus->numberOfChannels();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
Added: trunk/Source/WebCore/platform/audio/PushPullFIFO.h (0 => 267014)
--- trunk/Source/WebCore/platform/audio/PushPullFIFO.h (rev 0)
+++ trunk/Source/WebCore/platform/audio/PushPullFIFO.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 The Chromium Authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+
+// PushPullFIFO class is an intermediate audio sample storage between
+// WebKit-WebAudio and the renderer. The renderer's hardware callback buffer size
+// varies on the platform, but the WebAudio always renders 128 frames (render
+// quantum, RQ) thus FIFO is needed to handle the general case.
+class PushPullFIFO {
+ WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(PushPullFIFO);
+
+public:
+ // Maximum FIFO length. (512 render quanta)
+ static const size_t kMaxFIFOLength;
+
+ // |fifoLength| cannot exceed |kMaxFIFOLength|. Otherwise it crashes.
+ PushPullFIFO(unsigned numberOfChannels, size_t fifoLength);
+ ~PushPullFIFO();
+
+ // Pushes the rendered frames by WebAudio engine.
+ // - The |inputBus| length is 128 frames (1 render quantum), fixed.
+ // - In case of overflow (FIFO full while push), the existing frames in FIFO
+ // will be overwritten and |indexRead| will be forcibly moved to
+ // |indexWrite| to avoid reading overwritten frames.
+ void push(const AudioBus* inputBus);
+
+ // Pulls |framesRequested| by the audio device thread and returns the actual
+ // number of frames to be rendered by the source. (i.e. WebAudio graph)
+ size_t pull(AudioBus* outputBus, size_t framesRequested);
+
+ size_t framesAvailable() const { return m_framesAvailable; }
+ size_t length() const { return m_fifoLength; }
+ unsigned numberOfChannels() const;
+ AudioBus* bus() const { return m_fifoBus.get(); }
+
+private:
+ // The size of the FIFO.
+ const size_t m_fifoLength = 0;
+
+ RefPtr<AudioBus> m_fifoBus;
+
+ // The number of frames in the FIFO actually available for pulling.
+ size_t m_framesAvailable { 0 };
+
+ size_t m_indexRead { 0 };
+ size_t m_indexWrite { 0 };
+};
+
+} // namespace WebCore
+
+
Modified: trunk/Source/WebCore/platform/audio/SincResampler.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/SincResampler.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/SincResampler.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -39,51 +39,113 @@
#include <emmintrin.h>
#endif
-// Input buffer layout, dividing the total buffer into regions (r0 - r5):
+// Initial input buffer layout, dividing into regions r0 to r4 (note: r0, r3
+// and r4 will move after the first load):
//
// |----------------|----------------------------------------------------------------|----------------|
//
-// blockSize + kernelSize / 2
+// m_requestFrames
// <-------------------------------------------------------------------------------->
-// r0
+// r0 (during first load)
//
// kernelSize / 2 kernelSize / 2 kernelSize / 2 kernelSize / 2
// <---------------> <---------------> <---------------> <--------------->
// r1 r2 r3 r4
//
-// blockSize
-// <-------------------------------------------------------------->
-// r5
+// m_blockSize == r4 - r2
+// <--------------------------------------->
+//
+// m_requestFrames
+// <------------------ ... ----------------->
+// r0 (during second load)
+//
+// On the second request r0 slides to the right by kernelSize / 2 and r3, r4
+// and m_blockSize are reinitialized via step (3) in the algorithm below.
+//
+// These new regions remain constant until a Flush() occurs. While complicated,
+// this allows us to reduce jitter by always requesting the same amount from the
+// provided callback.
// The Algorithm:
//
-// 1) Consume input frames into r0 (r1 is zero-initialized).
-// 2) Position kernel centered at start of r0 (r2) and generate output frames until kernel is centered at start of r4.
-// or we've finished generating all the output frames.
-// 3) Copy r3 to r1 and r4 to r2.
-// 4) Consume input frames into r5 (zero-pad if we run out of input).
-// 5) Goto (2) until all of input is consumed.
+// 1) Allocate input_buffer of size: m_requestFrames + kernelSize; this ensures
+// there's enough room to read m_requestFrames from the callback into region
+// r0 (which will move between the first and subsequent passes).
//
+// 2) Let r1, r2 each represent half the kernel centered around r0:
+//
+// r0 = m_inputBuffer + kernelSize / 2
+// r1 = m_inputBuffer
+// r2 = r0
+//
+// r0 is always m_requestFrames in size. r1, r2 are kernelSize / 2 in
+// size. r1 must be zero initialized to avoid convolution with garbage (see
+// step (5) for why).
+//
+// 3) Let r3, r4 each represent half the kernel right aligned with the end of
+// r0 and choose m_blockSize as the distance in frames between r4 and r2:
+//
+// r3 = r0 + m_requestFrames - kernelSize
+// r4 = r0 + m_requestFrames - kernelSize / 2
+// m_blockSize = r4 - r2 = m_requestFrames - kernelSize / 2
+//
+// 4) Consume m_requestFrames frames into r0.
+//
+// 5) Position kernel centered at start of r2 and generate output frames until
+// the kernel is centered at the start of r4 or we've finished generating
+// all the output frames.
+//
+// 6) Wrap left over data from the r3 to r1 and r4 to r2.
+//
+// 7) If we're on the second load, in order to avoid overwriting the frames we
+// just wrapped from r4 we need to slide r0 to the right by the size of
+// r4, which is kernelSize / 2:
+//
+// r0 = r0 + kernelSize / 2 = m_inputBuffer + kernelSize
+//
+// r3, r4, and m_blockSize then need to be reinitialized, so goto (3).
+//
+// 8) Else, if we're not on the second load, goto (4).
+//
// note: we're glossing over how the sub-sample handling works with m_virtualSourceIndex, etc.
namespace WebCore {
-SincResampler::SincResampler(double scaleFactor, unsigned kernelSize, unsigned numberOfKernelOffsets)
+constexpr unsigned defaultRequestFrames { 512 };
+constexpr unsigned kernelSize { 32 };
+constexpr unsigned numberOfKernelOffsets { 32 };
+
+SincResampler::SincResampler(double scaleFactor, Optional<unsigned> requestFrames)
: m_scaleFactor(scaleFactor)
- , m_kernelSize(kernelSize)
- , m_numberOfKernelOffsets(numberOfKernelOffsets)
- , m_kernelStorage(m_kernelSize * (m_numberOfKernelOffsets + 1))
- , m_virtualSourceIndex(0)
- , m_blockSize(512)
- , m_inputBuffer(m_blockSize + m_kernelSize) // See input buffer layout above.
- , m_source(0)
- , m_sourceFramesAvailable(0)
- , m_sourceProvider(0)
- , m_isBufferPrimed(false)
+ , m_kernelStorage(kernelSize * (numberOfKernelOffsets + 1))
+ , m_requestFrames(requestFrames.valueOr(defaultRequestFrames))
+ , m_inputBuffer(m_requestFrames + kernelSize) // See input buffer layout above.
+ , m_r1(m_inputBuffer.data())
+ , m_r2(m_inputBuffer.data() + kernelSize / 2)
{
+ ASSERT(m_requestFrames > 0);
+ updateRegions(false);
+ ASSERT(m_blockSize > kernelSize);
initializeKernel();
}
+void SincResampler::updateRegions(bool isSecondLoad)
+{
+ // Setup various region pointers in the buffer (see diagram above). If we're
+ // on the second load we need to slide m_r0 to the right by kernelSize / 2.
+ m_r0 = m_inputBuffer.data() + (isSecondLoad ? kernelSize : kernelSize / 2);
+ m_r3 = m_r0 + m_requestFrames - kernelSize;
+ m_r4 = m_r0 + m_requestFrames - kernelSize / 2;
+ m_blockSize = m_r4 - m_r2;
+
+ // m_r1 at the beginning of the buffer.
+ ASSERT(m_r1 == m_inputBuffer.data());
+ // m_r1 left of m_r2, m_r4 left of m_r3 and size correct.
+ ASSERT((m_r2 - m_r1) == (m_r4 - m_r3));
+ // m_r2 left of r3.
+ ASSERT(m_r2 <= m_r3);
+}
+
void SincResampler::initializeKernel()
{
// Blackman window parameters.
@@ -98,16 +160,16 @@
// The sinc function is an idealized brick-wall filter, but since we're windowing it the
// transition from pass to stop does not happen right away. So we should adjust the
// lowpass filter cutoff slightly downward to avoid some aliasing at the very high-end.
- // FIXME: this value is empirical and to be more exact should vary depending on m_kernelSize.
+ // FIXME: this value is empirical and to be more exact should vary depending on kernelSize.
sincScaleFactor *= 0.9;
- int n = m_kernelSize;
+ int n = kernelSize;
int halfSize = n / 2;
// Generates a set of windowed sinc() kernels.
// We generate a range of sub-sample offsets from 0.0 to 1.0.
- for (unsigned offsetIndex = 0; offsetIndex <= m_numberOfKernelOffsets; ++offsetIndex) {
- double subsampleOffset = static_cast<double>(offsetIndex) / m_numberOfKernelOffsets;
+ for (unsigned offsetIndex = 0; offsetIndex <= numberOfKernelOffsets; ++offsetIndex) {
+ double subsampleOffset = static_cast<double>(offsetIndex) / numberOfKernelOffsets;
for (int i = 0; i < n; ++i) {
// Compute the sinc() with offset.
@@ -120,7 +182,7 @@
double window = a0 - a1 * cos(2.0 * piDouble * x) + a2 * cos(4.0 * piDouble * x);
// Window the sinc() function and store at the correct offset.
- m_kernelStorage[i + offsetIndex * m_kernelSize] = sinc * window;
+ m_kernelStorage[i + offsetIndex * kernelSize] = sinc * window;
}
}
}
@@ -190,7 +252,7 @@
unsigned remaining = numberOfDestinationFrames;
while (remaining) {
- unsigned framesThisTime = std::min(remaining, m_blockSize);
+ unsigned framesThisTime = std::min(remaining, m_requestFrames);
process(&sourceProvider, destination, framesThisTime);
destination += framesThisTime;
@@ -200,27 +262,18 @@
void SincResampler::process(AudioSourceProvider* sourceProvider, float* destination, size_t framesToProcess)
{
- bool isGood = sourceProvider && m_blockSize > m_kernelSize && m_inputBuffer.size() >= m_blockSize + m_kernelSize && !(m_kernelSize % 2);
- ASSERT(isGood);
- if (!isGood)
+ ASSERT(sourceProvider);
+ if (!sourceProvider)
return;
m_sourceProvider = sourceProvider;
unsigned numberOfDestinationFrames = framesToProcess;
-
- // Setup various region pointers in the buffer (see diagram above).
- float* r0 = m_inputBuffer.data() + m_kernelSize / 2;
- float* r1 = m_inputBuffer.data();
- float* r2 = r0;
- float* r3 = r0 + m_blockSize - m_kernelSize / 2;
- float* r4 = r0 + m_blockSize;
- float* r5 = r0 + m_kernelSize / 2;
// Step (1)
// Prime the input buffer at the start of the input stream.
if (!m_isBufferPrimed) {
- consumeSource(r0, m_blockSize + m_kernelSize / 2);
+ consumeSource(m_r0, m_requestFrames);
m_isBufferPrimed = true;
}
@@ -232,14 +285,14 @@
int sourceIndexI = static_cast<int>(m_virtualSourceIndex);
double subsampleRemainder = m_virtualSourceIndex - sourceIndexI;
- double virtualOffsetIndex = subsampleRemainder * m_numberOfKernelOffsets;
+ double virtualOffsetIndex = subsampleRemainder * numberOfKernelOffsets;
int offsetIndex = static_cast<int>(virtualOffsetIndex);
- float* k1 = m_kernelStorage.data() + offsetIndex * m_kernelSize;
- float* k2 = k1 + m_kernelSize;
+ float* k1 = m_kernelStorage.data() + offsetIndex * kernelSize;
+ float* k2 = k1 + kernelSize;
// Initialize input pointer based on quantized m_virtualSourceIndex.
- float* inputP = r1 + sourceIndexI;
+ float* inputP = m_r1 + sourceIndexI;
// We'll compute "convolutions" for the two kernels which straddle m_virtualSourceIndex
float sum1 = 0;
@@ -249,7 +302,7 @@
double kernelInterpolationFactor = virtualOffsetIndex - offsetIndex;
// Generate a single output sample.
- int n = m_kernelSize;
+ int n = kernelSize;
#define CONVOLVE_ONE_SAMPLE \
input = *inputP++; \
@@ -455,16 +508,20 @@
}
// Wrap back around to the start.
+ ASSERT(m_virtualSourceIndex >= m_blockSize);
m_virtualSourceIndex -= m_blockSize;
- // Step (3) Copy r3 to r1 and r4 to r2.
+ // Step (3) Copy r3 to r1.
// This wraps the last input frames back to the start of the buffer.
- memcpy(r1, r3, sizeof(float) * (m_kernelSize / 2));
- memcpy(r2, r4, sizeof(float) * (m_kernelSize / 2));
+ memcpy(m_r1, m_r3, sizeof(float) * kernelSize);
- // Step (4)
+ // Step (4) -- Reinitialize regions if necessary.
+ if (m_r0 == m_r2)
+ updateRegions(true);
+
+ // Step (5)
// Refresh the buffer with more input.
- consumeSource(r5, m_blockSize);
+ consumeSource(m_r0, m_requestFrames);
}
}
Modified: trunk/Source/WebCore/platform/audio/SincResampler.h (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/SincResampler.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/SincResampler.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -39,11 +39,10 @@
class SincResampler final {
WTF_MAKE_FAST_ALLOCATED;
-public:
+public:
// scaleFactor == sourceSampleRate / destinationSampleRate
- // kernelSize can be adjusted for quality (higher is better)
- // numberOfKernelOffsets is used for interpolation and is the number of sub-sample kernel shifts.
- SincResampler(double scaleFactor, unsigned kernelSize = 32, unsigned numberOfKernelOffsets = 32);
+ // requestFrames controls the size in frames of the buffer requested by each provideInput() call.
+ SincResampler(double scaleFactor, Optional<unsigned> requestFrames = WTF::nullopt);
// Processes numberOfSourceFrames from source to produce numberOfSourceFrames / scaleFactor frames in destination.
void process(const float* source, float* destination, unsigned numberOfSourceFrames);
@@ -54,10 +53,9 @@
protected:
void initializeKernel();
void consumeSource(float* buffer, unsigned numberOfSourceFrames);
+ void updateRegions(bool isSecondLoad);
double m_scaleFactor;
- unsigned m_kernelSize;
- unsigned m_numberOfKernelOffsets;
// m_kernelStorage has m_numberOfKernelOffsets kernels back-to-back, each of size m_kernelSize.
// The kernel offsets are sub-sample shifts of a windowed sinc() shifted from 0.0 to 1.0 sample.
@@ -65,22 +63,33 @@
// m_virtualSourceIndex is an index on the source input buffer with sub-sample precision.
// It must be double precision to avoid drift.
- double m_virtualSourceIndex;
+ double m_virtualSourceIndex { 0 };
// This is the number of destination frames we generate per processing pass on the buffer.
- unsigned m_blockSize;
+ unsigned m_requestFrames;
+ // The number of source frames processed per pass.
+ unsigned m_blockSize { 0 };
+
// Source is copied into this buffer for each processing pass.
AudioFloatArray m_inputBuffer;
- const float* m_source;
- unsigned m_sourceFramesAvailable;
+ // Pointers to the various regions inside |m_inputBuffer|. See the diagram at
+ // the top of the .cpp file for more information.
+ float* m_r0 { nullptr };
+ float* const m_r1 { nullptr };
+ float* const m_r2 { nullptr };
+ float* m_r3 { nullptr };
+ float* m_r4 { nullptr };
+
+ const float* m_source { nullptr };
+ unsigned m_sourceFramesAvailable { 0 };
// m_sourceProvider is used to provide the audio input stream to the resampler.
- AudioSourceProvider* m_sourceProvider;
+ AudioSourceProvider* m_sourceProvider { nullptr };
// The buffer is primed once at the very beginning of processing.
- bool m_isBufferPrimed;
+ bool m_isBufferPrimed { false };
RefPtr<AudioBus> m_internalBus;
};
Modified: trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -29,14 +29,17 @@
#if ENABLE(WEB_AUDIO)
#include "AudioBus.h"
-#include "AudioIOCallback.h"
#include "AudioSession.h"
#include "Logging.h"
+#include "MultiChannelResampler.h"
+#include "PushPullFIFO.h"
namespace WebCore {
-const int kRenderBufferSize = 128;
+constexpr size_t kRenderBufferSize = 128;
+constexpr size_t fifoSize = 96 * kRenderBufferSize;
+
CreateAudioDestinationCocoaOverride AudioDestinationCocoa::createOverride = nullptr;
std::unique_ptr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, const String&, unsigned numberOfInputChannels, unsigned numberOfOutputChannels, float sampleRate)
@@ -68,14 +71,24 @@
return AudioSession::sharedSession().maximumNumberOfOutputChannels();
}
+// FIXME: We should not be hardcoding the number of input channels.
+constexpr unsigned legacyNumberOfOutputChannels { 2 };
+
AudioDestinationCocoa::AudioDestinationCocoa(AudioIOCallback& callback, float sampleRate)
: m_outputUnit(0)
, m_callback(callback)
- , m_renderBus(AudioBus::create(2, kRenderBufferSize, false).releaseNonNull())
- , m_spareBus(AudioBus::create(2, kRenderBufferSize, true).releaseNonNull())
- , m_sampleRate(sampleRate)
+ , m_outputBus(AudioBus::create(legacyNumberOfOutputChannels, kRenderBufferSize, false).releaseNonNull())
+ , m_renderBus(AudioBus::create(legacyNumberOfOutputChannels, kRenderBufferSize).releaseNonNull())
+ , m_fifo(makeUniqueRef<PushPullFIFO>(legacyNumberOfOutputChannels, fifoSize))
+ , m_contextSampleRate(sampleRate)
{
configure();
+
+ auto hardwareSampleRate = this->hardwareSampleRate();
+ if (sampleRate != hardwareSampleRate) {
+ double scaleFactor = static_cast<double>(sampleRate) / hardwareSampleRate;
+ m_resampler = makeUnique<MultiChannelResampler>(scaleFactor, legacyNumberOfOutputChannels, kRenderBufferSize);
+ }
}
AudioDestinationCocoa::~AudioDestinationCocoa()
@@ -116,11 +129,11 @@
m_callback.isPlayingDidChange();
}
-void AudioDestinationCocoa::setAudioStreamBasicDescription(AudioStreamBasicDescription& streamFormat, float sampleRate)
+void AudioDestinationCocoa::setAudioStreamBasicDescription(AudioStreamBasicDescription& streamFormat)
{
const int bytesPerFloat = sizeof(Float32);
const int bitsPerByte = 8;
- streamFormat.mSampleRate = sampleRate;
+ streamFormat.mSampleRate = hardwareSampleRate();
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerPacket = bytesPerFloat;
@@ -143,44 +156,31 @@
// Pulls on our provider to get rendered audio stream.
OSStatus AudioDestinationCocoa::render(const AudioTimeStamp* timestamp, UInt32 numberOfFrames, AudioBufferList* ioData)
{
- AudioIOPosition outputTimestamp;
+ if (m_fifo->length() < numberOfFrames)
+ return noErr;
+
if (timestamp) {
- outputTimestamp = {
- Seconds { timestamp->mSampleTime / m_sampleRate },
+ m_outputTimestamp = {
+ Seconds { timestamp->mSampleTime / sampleRate() },
MonotonicTime::fromMachAbsoluteTime(timestamp->mHostTime)
};
- }
+ } else
+ m_outputTimestamp = AudioIOPosition { };
+
auto* buffers = ioData->mBuffers;
UInt32 numberOfBuffers = ioData->mNumberBuffers;
- UInt32 framesRemaining = numberOfFrames;
- UInt32 frameOffset = 0;
- while (framesRemaining > 0) {
- if (m_startSpareFrame < m_endSpareFrame) {
- ASSERT(m_startSpareFrame < m_endSpareFrame);
- UInt32 framesThisTime = std::min<UInt32>(m_endSpareFrame - m_startSpareFrame, numberOfFrames);
- assignAudioBuffersToBus(buffers, m_renderBus.get(), numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
- m_renderBus->copyFromRange(m_spareBus.get(), m_startSpareFrame, m_endSpareFrame);
- processBusAfterRender(m_renderBus.get(), framesThisTime);
- frameOffset += framesThisTime;
- framesRemaining -= framesThisTime;
- m_startSpareFrame += framesThisTime;
- }
- UInt32 framesThisTime = std::min<UInt32>(kRenderBufferSize, framesRemaining);
- assignAudioBuffersToBus(buffers, m_renderBus.get(), numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
+ // Associate the destination data array with the output bus then fill the FIFO.
+ assignAudioBuffersToBus(buffers, m_outputBus.get(), numberOfBuffers, numberOfFrames, 0, numberOfFrames);
+ size_t framesToRender = m_fifo->pull(m_outputBus.ptr(), numberOfFrames);
- if (!framesThisTime)
- break;
- if (framesThisTime < kRenderBufferSize) {
- m_callback.render(0, m_spareBus.ptr(), kRenderBufferSize, outputTimestamp);
- m_renderBus->copyFromRange(m_spareBus.get(), 0, framesThisTime);
- m_startSpareFrame = framesThisTime;
- m_endSpareFrame = kRenderBufferSize;
- } else
- m_callback.render(0, m_renderBus.ptr(), framesThisTime, outputTimestamp);
- processBusAfterRender(m_renderBus.get(), framesThisTime);
- frameOffset += framesThisTime;
- framesRemaining -= framesThisTime;
+ for (size_t pushedFrames = 0; pushedFrames < framesToRender; pushedFrames += kRenderBufferSize) {
+ if (m_resampler)
+ m_resampler->process(this, m_renderBus.ptr(), kRenderBufferSize);
+ else
+ m_callback.render(0, m_renderBus.ptr(), kRenderBufferSize, m_outputTimestamp);
+
+ m_fifo->push(m_renderBus.ptr());
}
return noErr;
@@ -193,6 +193,12 @@
return audioOutput->render(timestamp, numberOfFrames, ioData);
}
+void AudioDestinationCocoa::provideInput(AudioBus* bus, size_t framesToProcess)
+{
+ ASSERT_UNUSED(framesToProcess, framesToProcess == kRenderBufferSize);
+ m_callback.render(0, bus, kRenderBufferSize, m_outputTimestamp);
+}
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
Modified: trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.h (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/cocoa/AudioDestinationCocoa.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -28,17 +28,22 @@
#if ENABLE(WEB_AUDIO)
#include "AudioDestination.h"
+#include "AudioIOCallback.h"
+#include "AudioSourceProvider.h"
#include <AudioUnit/AudioUnit.h>
#include <wtf/RefPtr.h>
+#include <wtf/UniqueRef.h>
namespace WebCore {
class AudioBus;
+class MultiChannelResampler;
+class PushPullFIFO;
using CreateAudioDestinationCocoaOverride = std::unique_ptr<AudioDestination>(*)(AudioIOCallback&, float sampleRate);
// An AudioDestination using CoreAudio's default output AudioUnit
-class AudioDestinationCocoa : public AudioDestination {
+class AudioDestinationCocoa : public AudioDestination, public AudioSourceProvider {
public:
AudioDestinationCocoa(AudioIOCallback&, float sampleRate);
virtual ~AudioDestinationCocoa();
@@ -49,7 +54,7 @@
void setIsPlaying(bool);
bool isPlaying() final { return m_isPlaying; }
- float sampleRate() const final { return m_sampleRate; }
+ float sampleRate() const final { return m_contextSampleRate; }
unsigned framesPerBuffer() const final;
AudioUnit& outputUnit() { return m_outputUnit; }
@@ -56,7 +61,7 @@
// DefaultOutputUnit callback
static OSStatus inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 busNumber, UInt32 numberOfFrames, AudioBufferList* ioData);
- void setAudioStreamBasicDescription(AudioStreamBasicDescription&, float sampleRate);
+ void setAudioStreamBasicDescription(AudioStreamBasicDescription&);
private:
void start() override;
@@ -64,6 +69,9 @@
friend std::unique_ptr<AudioDestination> AudioDestination::create(AudioIOCallback&, const String&, unsigned, unsigned, float);
+ // AudioSourceProvider.
+ void provideInput(AudioBus*, size_t framesToProcess) final;
+
void configure();
void processBusAfterRender(AudioBus&, UInt32 numberOfFrames);
@@ -71,14 +79,22 @@
AudioUnit m_outputUnit;
AudioIOCallback& m_callback;
+
+ // To pass the data from FIFO to the audio device callback.
+ Ref<AudioBus> m_outputBus;
+
+ // To push the rendered result from WebAudio graph into the FIFO.
Ref<AudioBus> m_renderBus;
- Ref<AudioBus> m_spareBus;
- float m_sampleRate;
+ // Resolves the buffer size mismatch between the WebAudio engine and
+ // the callback function from the actual audio device.
+ UniqueRef<PushPullFIFO> m_fifo;
+
+ std::unique_ptr<MultiChannelResampler> m_resampler;
+ AudioIOPosition m_outputTimestamp;
+
+ float m_contextSampleRate;
bool m_isPlaying { false };
-
- unsigned m_startSpareFrame { 0 };
- unsigned m_endSpareFrame { 0 };
};
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -91,7 +91,7 @@
result = AudioUnitGetProperty(outputUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, (void*)&streamFormat, &size);
ASSERT(!result);
- setAudioStreamBasicDescription(streamFormat, sampleRate());
+ setAudioStreamBasicDescription(streamFormat);
result = AudioUnitSetProperty(outputUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription));
ASSERT(!result);
Modified: trunk/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -67,7 +67,7 @@
// Set stream format
AudioStreamBasicDescription streamFormat;
- setAudioStreamBasicDescription(streamFormat, sampleRate());
+ setAudioStreamBasicDescription(streamFormat);
result = AudioUnitSetProperty(outputUnit(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription));
ASSERT(!result);
Modified: trunk/Source/WebCore/platform/graphics/cocoa/SourceBufferParserWebM.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/graphics/cocoa/SourceBufferParserWebM.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/graphics/cocoa/SourceBufferParserWebM.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -55,6 +55,9 @@
using namespace PAL;
+// FIXME: Remove this once kCMVideoCodecType_VP9 is added to CMFormatDescription.h
+constexpr CMVideoCodecType kCMVideoCodecType_VP9 { 'vp09' };
+
static bool isWebmParserAvailable()
{
return !!webm::swap && RuntimeEnabledFeatures::sharedFeatures().webMParserEnabled();
Modified: trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDevice.h (267013 => 267014)
--- trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDevice.h 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDevice.h 2020-09-14 15:47:53 UTC (rev 267014)
@@ -28,6 +28,7 @@
#if ENABLE(MEDIA_STREAM) && PLATFORM(MAC)
#include "CaptureDevice.h"
+#include <pal/spi/cf/CoreAudioSPI.h>
#include <wtf/RefPtr.h>
#include <wtf/text/WTFString.h>
Modified: trunk/Source/WebCore/platform/mock/MockAudioDestinationCocoa.cpp (267013 => 267014)
--- trunk/Source/WebCore/platform/mock/MockAudioDestinationCocoa.cpp 2020-09-14 15:44:59 UTC (rev 267013)
+++ trunk/Source/WebCore/platform/mock/MockAudioDestinationCocoa.cpp 2020-09-14 15:47:53 UTC (rev 267014)
@@ -67,7 +67,7 @@
{
m_workQueue->dispatch([this, sampleRate = sampleRate(), numberOfFramesToProcess = m_numberOfFramesToProcess] {
AudioStreamBasicDescription streamFormat;
- setAudioStreamBasicDescription(streamFormat, sampleRate);
+ setAudioStreamBasicDescription(streamFormat);
WebAudioBufferList webAudioBufferList { streamFormat, numberOfFramesToProcess };
AudioDestinationCocoa::inputProc(this, 0, 0, 0, numberOfFramesToProcess, webAudioBufferList.list());