summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/audio/AudioBus.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/audio/AudioBus.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/audio/AudioBus.cpp')
-rw-r--r--Source/WebCore/platform/audio/AudioBus.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/Source/WebCore/platform/audio/AudioBus.cpp b/Source/WebCore/platform/audio/AudioBus.cpp
new file mode 100644
index 0000000..dd4746d
--- /dev/null
+++ b/Source/WebCore/platform/audio/AudioBus.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2010 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 Computer, 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 "AudioBus.h"
+
+#include "VectorMath.h"
+#include <algorithm>
+#include <assert.h>
+#include <math.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+AudioBus::AudioBus(unsigned numberOfChannels, size_t length, bool allocate)
+ : m_length(length)
+ , m_busGain(1.0)
+ , m_isFirstTime(true)
+ , m_sampleRate(0.0)
+{
+ m_channels.reserveInitialCapacity(numberOfChannels);
+
+ for (unsigned i = 0; i < numberOfChannels; ++i) {
+ PassOwnPtr<AudioChannel> channel = allocate ? adoptPtr(new AudioChannel(length)) : adoptPtr(new AudioChannel(0, length));
+ m_channels.append(channel);
+ }
+
+ m_layout = LayoutCanonical; // for now this is the only layout we define
+}
+
+void AudioBus::setChannelMemory(unsigned channelIndex, float* storage, size_t length)
+{
+ if (channelIndex < m_channels.size()) {
+ channel(channelIndex)->set(storage, length);
+ m_length = length; // FIXME: verify that this length matches all the other channel lengths
+ }
+}
+
+void AudioBus::zero()
+{
+ for (unsigned i = 0; i < m_channels.size(); ++i)
+ m_channels[i]->zero();
+}
+
+AudioChannel* AudioBus::channelByType(unsigned channelType)
+{
+ // For now we only support canonical channel layouts...
+ if (m_layout != LayoutCanonical)
+ return 0;
+
+ switch (numberOfChannels()) {
+ case 1: // mono
+ if (channelType == ChannelMono || channelType == ChannelLeft)
+ return channel(0);
+ return 0;
+
+ case 2: // stereo
+ switch (channelType) {
+ case ChannelLeft: return channel(0);
+ case ChannelRight: return channel(1);
+ default: return 0;
+ }
+
+ case 4: // quad
+ switch (channelType) {
+ case ChannelLeft: return channel(0);
+ case ChannelRight: return channel(1);
+ case ChannelSurroundLeft: return channel(2);
+ case ChannelSurroundRight: return channel(3);
+ default: return 0;
+ }
+
+ case 5: // 5.0
+ switch (channelType) {
+ case ChannelLeft: return channel(0);
+ case ChannelRight: return channel(1);
+ case ChannelCenter: return channel(2);
+ case ChannelSurroundLeft: return channel(3);
+ case ChannelSurroundRight: return channel(4);
+ default: return 0;
+ }
+
+ case 6: // 5.1
+ switch (channelType) {
+ case ChannelLeft: return channel(0);
+ case ChannelRight: return channel(1);
+ case ChannelCenter: return channel(2);
+ case ChannelLFE: return channel(3);
+ case ChannelSurroundLeft: return channel(4);
+ case ChannelSurroundRight: return channel(5);
+ default: return 0;
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+// Returns true if the channel count and frame-size match.
+bool AudioBus::topologyMatches(const AudioBus& bus) const
+{
+ if (numberOfChannels() != bus.numberOfChannels())
+ return false; // channel mismatch
+
+ // Make sure source bus has enough frames.
+ if (length() > bus.length())
+ return false; // frame-size mismatch
+
+ return true;
+}
+
+PassOwnPtr<AudioBus> AudioBus::createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame)
+{
+ size_t numberOfSourceFrames = sourceBuffer->length();
+ unsigned numberOfChannels = sourceBuffer->numberOfChannels();
+
+ // Sanity checking
+ bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames;
+ ASSERT(isRangeSafe);
+ if (!isRangeSafe)
+ return 0;
+
+ size_t rangeLength = endFrame - startFrame;
+
+ OwnPtr<AudioBus> audioBus = adoptPtr(new AudioBus(numberOfChannels, rangeLength));
+ audioBus->setSampleRate(sourceBuffer->sampleRate());
+
+ for (unsigned i = 0; i < numberOfChannels; ++i)
+ audioBus->channel(i)->copyFromRange(sourceBuffer->channel(i), startFrame, endFrame);
+
+ return audioBus.release();
+}
+
+float AudioBus::maxAbsValue() const
+{
+ float max = 0.0f;
+ for (unsigned i = 0; i < numberOfChannels(); ++i) {
+ const AudioChannel* channel = this->channel(i);
+ max = std::max(max, channel->maxAbsValue());
+ }
+
+ return max;
+}
+
+void AudioBus::normalize()
+{
+ float max = maxAbsValue();
+ if (max)
+ scale(1.0f / max);
+}
+
+void AudioBus::scale(double scale)
+{
+ for (unsigned i = 0; i < numberOfChannels(); ++i)
+ channel(i)->scale(scale);
+}
+
+// Just copies the samples from the source bus to this one.
+// This is just a simple copy if the number of channels match, otherwise a mixup or mixdown is done.
+// For now, we just support a mixup from mono -> stereo.
+void AudioBus::copyFrom(const AudioBus& sourceBus)
+{
+ if (&sourceBus == this)
+ return;
+
+ if (numberOfChannels() == sourceBus.numberOfChannels()) {
+ for (unsigned i = 0; i < numberOfChannels(); ++i)
+ channel(i)->copyFrom(sourceBus.channel(i));
+ } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) {
+ // Handle mono -> stereo case (for now simply copy mono channel into both left and right)
+ // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
+ const AudioChannel* sourceChannel = sourceBus.channel(0);
+ channel(0)->copyFrom(sourceChannel);
+ channel(1)->copyFrom(sourceChannel);
+ } else {
+ // Case not handled
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void AudioBus::sumFrom(const AudioBus &sourceBus)
+{
+ if (numberOfChannels() == sourceBus.numberOfChannels()) {
+ for (unsigned i = 0; i < numberOfChannels(); ++i)
+ channel(i)->sumFrom(sourceBus.channel(i));
+ } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) {
+ // Handle mono -> stereo case (for now simply sum mono channel into both left and right)
+ // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
+ const AudioChannel* sourceChannel = sourceBus.channel(0);
+ channel(0)->sumFrom(sourceChannel);
+ channel(1)->sumFrom(sourceChannel);
+ } else {
+ // Case not handled
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void AudioBus::processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
+{
+ // We don't want to suddenly change the gain from mixing one time slice to the next,
+ // so we "de-zipper" by slowly changing the gain each sample-frame until we've achieved the target gain.
+
+ // FIXME: optimize this method (SSE, etc.)
+ // FIXME: Need fast path here when gain has converged on targetGain. In this case, de-zippering is no longer needed.
+ // FIXME: Need fast path when this==sourceBus && lastMixGain==targetGain==1.0 && sumToBus==false (this is a NOP)
+
+ // Take master bus gain into account as well as the targetGain.
+ double totalDesiredGain = m_busGain * targetGain;
+
+ // First time, snap directly to totalDesiredGain.
+ double gain = m_isFirstTime ? totalDesiredGain : *lastMixGain;
+ m_isFirstTime = false;
+
+ int numberOfSourceChannels = sourceBus.numberOfChannels();
+ int numberOfDestinationChannels = numberOfChannels();
+
+ AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus);
+ const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
+ const float* sourceR = numberOfSourceChannels > 1 ? sourceBusSafe.channelByType(ChannelRight)->data() : 0;
+
+ float* destinationL = channelByType(ChannelLeft)->data();
+ float* destinationR = numberOfDestinationChannels > 1 ? channelByType(ChannelRight)->data() : 0;
+
+ const double DezipperRate = 0.005;
+ int framesToProcess = length();
+
+ if (sumToBus) {
+ // Sum to our bus
+ if (sourceR && destinationR) {
+ // Stereo
+ while (framesToProcess--) {
+ float sampleL = *sourceL++;
+ float sampleR = *sourceR++;
+ *destinationL++ += static_cast<float>(gain * sampleL);
+ *destinationR++ += static_cast<float>(gain * sampleR);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ } else if (destinationR) {
+ // Mono -> stereo (mix equally into L and R)
+ // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
+ while (framesToProcess--) {
+ float sample = *sourceL++;
+ *destinationL++ += static_cast<float>(gain * sample);
+ *destinationR++ += static_cast<float>(gain * sample);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ } else {
+ // Mono
+ while (framesToProcess--) {
+ float sampleL = *sourceL++;
+ *destinationL++ += static_cast<float>(gain * sampleL);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ }
+ } else {
+ // Process directly (without summing) to our bus
+ if (sourceR && destinationR) {
+ // Stereo
+ while (framesToProcess--) {
+ float sampleL = *sourceL++;
+ float sampleR = *sourceR++;
+ *destinationL++ = static_cast<float>(gain * sampleL);
+ *destinationR++ = static_cast<float>(gain * sampleR);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ } else if (destinationR) {
+ // Mono -> stereo (mix equally into L and R)
+ // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
+ while (framesToProcess--) {
+ float sample = *sourceL++;
+ *destinationL++ = static_cast<float>(gain * sample);
+ *destinationR++ = static_cast<float>(gain * sample);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ } else {
+ // Mono
+ while (framesToProcess--) {
+ float sampleL = *sourceL++;
+ *destinationL++ = static_cast<float>(gain * sampleL);
+
+ // Slowly change gain to desired gain.
+ gain += (totalDesiredGain - gain) * DezipperRate;
+ }
+ }
+ }
+
+ // Save the target gain as the starting point for next time around.
+ *lastMixGain = gain;
+}
+
+void AudioBus::processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
+{
+ // Make sure we're summing from same type of bus.
+ // We *are* able to sum from mono -> stereo
+ if (sourceBus.numberOfChannels() != 1 && !topologyMatches(sourceBus))
+ return;
+
+ // Dispatch for different channel layouts
+ switch (numberOfChannels()) {
+ case 1: // mono
+ case 2: // stereo
+ processWithGainFromMonoStereo(sourceBus, lastMixGain, targetGain, sumToBus);
+ break;
+ case 4: // FIXME: implement quad
+ case 5: // FIXME: implement 5.0
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
+void AudioBus::copyWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain)
+{
+ processWithGainFrom(sourceBus, lastMixGain, targetGain, false);
+}
+
+void AudioBus::sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain)
+{
+ processWithGainFrom(sourceBus, lastMixGain, targetGain, true);
+}
+
+} // WebCore
+
+#endif // ENABLE(WEB_AUDIO)