diff options
Diffstat (limited to 'WebCore/platform/audio')
-rw-r--r-- | WebCore/platform/audio/AudioArray.h | 71 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioBus.cpp | 363 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioBus.h | 139 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioChannel.cpp | 102 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioChannel.h | 112 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioDSPKernel.h | 65 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioDSPKernelProcessor.cpp | 116 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioDSPKernelProcessor.h | 76 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioProcessor.h | 75 | ||||
-rw-r--r-- | WebCore/platform/audio/AudioSourceProvider.h | 46 | ||||
-rw-r--r-- | WebCore/platform/audio/Biquad.cpp | 280 | ||||
-rw-r--r-- | WebCore/platform/audio/Biquad.h | 99 | ||||
-rw-r--r-- | WebCore/platform/audio/Distance.cpp | 93 | ||||
-rw-r--r-- | WebCore/platform/audio/Distance.h | 80 | ||||
-rw-r--r-- | WebCore/platform/audio/FFTFrame.cpp | 269 | ||||
-rw-r--r-- | WebCore/platform/audio/FFTFrame.h | 102 | ||||
-rw-r--r-- | WebCore/platform/audio/Panner.cpp | 71 | ||||
-rw-r--r-- | WebCore/platform/audio/Panner.h | 69 | ||||
-rw-r--r-- | WebCore/platform/audio/mac/FFTFrameMac.cpp | 191 |
19 files changed, 2419 insertions, 0 deletions
diff --git a/WebCore/platform/audio/AudioArray.h b/WebCore/platform/audio/AudioArray.h new file mode 100644 index 0000000..9c25b0f --- /dev/null +++ b/WebCore/platform/audio/AudioArray.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef AudioArray_h +#define AudioArray_h + +#include <string.h> +#include <wtf/Vector.h> + +namespace WebCore { + +template<typename T> +class AudioArray : public Vector<T> { +public: + AudioArray() : Vector<T>(0) { } + explicit AudioArray(size_t n) : Vector<T>(n, 0) { } + + void zero() { memset(this->data(), 0, sizeof(T) * this->size()); } + + void zeroRange(unsigned start, unsigned end) + { + bool isSafe = (start <= end) && (end <= this->size()); + ASSERT(isSafe); + if (!isSafe) + return; + + memset(this->data() + start, 0, sizeof(T) * (end - start)); + } + + void copyToRange(T* sourceData, unsigned start, unsigned end) + { + bool isSafe = (start <= end) && (end <= this->size()); + ASSERT(isSafe); + if (!isSafe) + return; + + memcpy(this->data() + start, sourceData, sizeof(T) * (end - start)); + } +}; + +typedef AudioArray<float> AudioFloatArray; +typedef AudioArray<double> AudioDoubleArray; + +} // WebCore + +#endif // AudioArray_h diff --git a/WebCore/platform/audio/AudioBus.cpp b/WebCore/platform/audio/AudioBus.cpp new file mode 100644 index 0000000..6b7ec3f --- /dev/null +++ b/WebCore/platform/audio/AudioBus.cpp @@ -0,0 +1,363 @@ +/* + * 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 "Accelerate.h" +#include <algorithm> +#include <assert.h> +#include <math.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +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) diff --git a/WebCore/platform/audio/AudioBus.h b/WebCore/platform/audio/AudioBus.h new file mode 100644 index 0000000..72357e8 --- /dev/null +++ b/WebCore/platform/audio/AudioBus.h @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#ifndef AudioBus_h +#define AudioBus_h + +#include "AudioChannel.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// An AudioBus represents a collection of one or more AudioChannels. +// The data layout is "planar" as opposed to "interleaved". +// An AudioBus with one channel is mono, an AudioBus with two channels is stereo, etc. +class AudioBus : public Noncopyable { +public: + enum { + ChannelLeft = 0, + ChannelRight = 1, + ChannelCenter = 2, // center and mono are the same + ChannelMono = 2, + ChannelLFE = 3, + ChannelSurroundLeft = 4, + ChannelSurroundRight = 5, + }; + + enum { + LayoutCanonical = 0 + // Can define non-standard layouts here + }; + + // allocate indicates whether or not to initially have the AudioChannels created with managed storage. + // Normal usage is to pass true here, in which case the AudioChannels will memory-manage their own storage. + // If allocate is false then setChannelMemory() has to be called later on for each channel before the AudioBus is useable... + AudioBus(unsigned numberOfChannels, size_t length, bool allocate = true); + + // Tells the given channel to use an externally allocated buffer. + void setChannelMemory(unsigned channelIndex, float* storage, size_t length); + + // Channels + unsigned numberOfChannels() const { return m_channels.size(); } + + AudioChannel* channel(unsigned channel) { return m_channels[channel].get(); } + const AudioChannel* channel(unsigned channel) const { return const_cast<AudioBus*>(this)->m_channels[channel].get(); } + AudioChannel* channelByType(unsigned type); + + // Number of sample-frames + size_t length() const { return m_length; } + + // Sample-rate : 0.0 if unknown or "don't care" + double sampleRate() const { return m_sampleRate; } + void setSampleRate(double sampleRate) { m_sampleRate = sampleRate; } + + // Zeroes all channels. + void zero(); + + // Returns true if the channel count and frame-size match. + bool topologyMatches(const AudioBus &sourceBus) const; + + // Creates a new buffer from a range in the source buffer. + // 0 may be returned if the range does not fit in the sourceBuffer + static PassOwnPtr<AudioBus> createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame); + + // Scales all samples by the same amount. + void scale(double scale); + + // Master gain for this bus - used with sumWithGainFrom() below + void setGain(double gain) { m_busGain = gain; } + double gain() { return m_busGain; } + + void reset() { m_isFirstTime = true; } // for de-zippering + + // Assuming sourceBus has the same topology, copies sample data from each channel of sourceBus to our corresponding channel. + void copyFrom(const AudioBus &sourceBus); + + // Sums the sourceBus into our bus with unity gain. + // Our own internal gain m_busGain is ignored. + void sumFrom(const AudioBus &sourceBus); + + // Copy or sum each channel from sourceBus into our corresponding channel. + // We scale by targetGain (and our own internal gain m_busGain), performing "de-zippering" to smoothly change from *lastMixGain to (targetGain*m_busGain). + // The caller is responsible for setting up lastMixGain to point to storage which is unique for every "stream" which will be summed to this bus. + // This represents the dezippering memory. + void copyWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain); + void sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain); + + // Returns maximum absolute value across all channels (useful for normalization). + float maxAbsValue() const; + + // Makes maximum absolute value == 1.0 (if possible). + void normalize(); + +protected: + AudioBus() { }; + + void processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus); + void processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus); + + size_t m_length; + + Vector<OwnPtr<AudioChannel> > m_channels; + + int m_layout; + + double m_busGain; + bool m_isFirstTime; + double m_sampleRate; // 0.0 if unknown or N/A +}; + +} // WebCore + +#endif // AudioBus_h diff --git a/WebCore/platform/audio/AudioChannel.cpp b/WebCore/platform/audio/AudioChannel.cpp new file mode 100644 index 0000000..ad38219 --- /dev/null +++ b/WebCore/platform/audio/AudioChannel.cpp @@ -0,0 +1,102 @@ +/* + * 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 "AudioChannel.h" + +#include "Accelerate.h" +#include <algorithm> +#include <math.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +void AudioChannel::scale(double scale) +{ + float s = static_cast<float>(scale); + vsmul(data(), 1, &s, data(), 1, length()); +} + +void AudioChannel::copyFrom(const AudioChannel* sourceChannel) +{ + bool isSafe = (sourceChannel && sourceChannel->length() >= length()); + ASSERT(isSafe); + if (!isSafe) + return; + + memcpy(data(), sourceChannel->data(), sizeof(float) * length()); +} + +void AudioChannel::copyFromRange(const AudioChannel* sourceChannel, unsigned startFrame, unsigned endFrame) +{ + // Check that range is safe for reading from sourceChannel. + bool isRangeSafe = sourceChannel && startFrame < endFrame && endFrame <= sourceChannel->length(); + ASSERT(isRangeSafe); + if (!isRangeSafe) + return; + + // Check that this channel has enough space. + size_t rangeLength = endFrame - startFrame; + bool isRangeLengthSafe = rangeLength <= length(); + ASSERT(isRangeLengthSafe); + if (!isRangeLengthSafe) + return; + + const float* source = sourceChannel->data(); + float* destination = data(); + memcpy(destination, source + startFrame, sizeof(float) * rangeLength); +} + +void AudioChannel::sumFrom(const AudioChannel* sourceChannel) +{ + bool isSafe = sourceChannel && sourceChannel->length() >= length(); + ASSERT(isSafe); + if (!isSafe) + return; + + vadd(data(), 1, sourceChannel->data(), 1, data(), 1, length()); +} + +float AudioChannel::maxAbsValue() const +{ + const float* p = data(); + int n = length(); + + float max = 0.0f; + while (n--) + max = std::max(max, fabsf(*p++)); + + return max; +} + +} // WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioChannel.h b/WebCore/platform/audio/AudioChannel.h new file mode 100644 index 0000000..511048c --- /dev/null +++ b/WebCore/platform/audio/AudioChannel.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#ifndef AudioChannel_h +#define AudioChannel_h + +#include "AudioFloatArray.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +// An AudioChannel represents a buffer of non-interleaved floating-point audio samples. +// The PCM samples are normally assumed to be in a nominal range -1.0 -> +1.0 +class AudioChannel : public Noncopyable { +public: + // Memory can be externally referenced, or can be internally allocated with an AudioFloatArray. + + // Reference an external buffer. + AudioChannel(float* storage, size_t length) + : m_length(length), m_rawPointer(storage) { } + + // Manage storage for us. + explicit AudioChannel(size_t length) + : m_length(length) + , m_rawPointer(0) + { + m_memBuffer = adoptPtr(new AudioFloatArray(length)); + } + + // A "blank" audio channel -- must call set() before it's useful... + AudioChannel() + : m_length(0) + , m_rawPointer(0) + { + } + + // Redefine the memory for this channel. + // storage represents external memory not managed by this object. + void set(float* storage, size_t length) + { + m_memBuffer.clear(); // cleanup managed storage + m_rawPointer = storage; + m_length = length; + } + + // How many sample-frames do we contain? + size_t length() const { return m_length; } + + // Direct access to PCM sample data + float* data() { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); } + const float* data() const { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); } + + // Zeroes out all sample values in buffer. + void zero() + { + if (m_memBuffer.get()) + m_memBuffer->zero(); + else + memset(m_rawPointer, 0, sizeof(float) * m_length); + } + + // Scales all samples by the same amount. + void scale(double scale); + + // A simple memcpy() from the source channel + void copyFrom(const AudioChannel* sourceChannel); + + // Copies the given range from the source channel. + void copyFromRange(const AudioChannel* sourceChannel, unsigned startFrame, unsigned endFrame); + + // Sums (with unity gain) from the source channel. + void sumFrom(const AudioChannel* sourceChannel); + + // Returns maximum absolute value (useful for normalization). + float maxAbsValue() const; + +private: + size_t m_length; + + float* m_rawPointer; + OwnPtr<AudioFloatArray> m_memBuffer; +}; + +} // WebCore + +#endif // AudioChannel_h diff --git a/WebCore/platform/audio/AudioDSPKernel.h b/WebCore/platform/audio/AudioDSPKernel.h new file mode 100644 index 0000000..d9be6dc --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernel.h @@ -0,0 +1,65 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 AudioDSPKernel_h +#define AudioDSPKernel_h + +#include "AudioDSPKernelProcessor.h" + +namespace WebCore { + +// AudioDSPKernel does the processing for one channel of an AudioDSPKernelProcessor. + +class AudioDSPKernel { +public: + AudioDSPKernel(AudioDSPKernelProcessor* kernelProcessor) + : m_kernelProcessor(kernelProcessor) + { + } + + virtual ~AudioDSPKernel() { }; + + // Subclasses must override process() to do the processing and reset() to reset DSP state. + virtual void process(const float* source, float* destination, size_t framesToProcess) = 0; + virtual void reset() = 0; + + double sampleRate() const { return processor()->sampleRate(); } + double nyquist() const { return 0.5 * sampleRate(); } + + AudioDSPKernelProcessor* processor() { return m_kernelProcessor; } + const AudioDSPKernelProcessor* processor() const { return m_kernelProcessor; } + +protected: + AudioDSPKernelProcessor* m_kernelProcessor; +}; + +} // namespace WebCore + +#endif // AudioDSPKernel_h diff --git a/WebCore/platform/audio/AudioDSPKernelProcessor.cpp b/WebCore/platform/audio/AudioDSPKernelProcessor.cpp new file mode 100644 index 0000000..d79afd5 --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernelProcessor.cpp @@ -0,0 +1,116 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 "AudioDSPKernelProcessor.h" + +#include "AudioDSPKernel.h" + +namespace WebCore { + +// setNumberOfChannels() may later be called if the object is not yet in an "initialized" state. +AudioDSPKernelProcessor::AudioDSPKernelProcessor(double sampleRate, unsigned numberOfChannels) + : AudioProcessor(sampleRate) + , m_numberOfChannels(numberOfChannels) + , m_hasJustReset(true) +{ +} + +void AudioDSPKernelProcessor::initialize() +{ + if (isInitialized()) + return; + + ASSERT(!m_kernels.size()); + + // Create processing kernels, one per channel. + for (unsigned i = 0; i < numberOfChannels(); ++i) + m_kernels.append(createKernel()); + + m_initialized = true; +} + +void AudioDSPKernelProcessor::uninitialize() +{ + if (!isInitialized()) + return; + + m_kernels.clear(); + + m_initialized = false; +} + +void AudioDSPKernelProcessor::process(AudioBus* source, AudioBus* destination, size_t framesToProcess) +{ + ASSERT(source && destination); + if (!source || !destination) + return; + + if (!isInitialized()) { + destination->zero(); + return; + } + + bool channelCountMatches = source->numberOfChannels() == destination->numberOfChannels() && source->numberOfChannels() == m_kernels.size(); + ASSERT(channelCountMatches); + if (!channelCountMatches) + return; + + for (unsigned i = 0; i < m_kernels.size(); ++i) + m_kernels[i]->process(source->channel(i)->data(), destination->channel(i)->data(), framesToProcess); +} + +// Resets filter state +void AudioDSPKernelProcessor::reset() +{ + if (!isInitialized()) + return; + + // Forces snap to parameter values - first time. + // Any processing depending on this value must set it to false at the appropriate time. + m_hasJustReset = true; + + for (unsigned i = 0; i < m_kernels.size(); ++i) + m_kernels[i]->reset(); +} + +void AudioDSPKernelProcessor::setNumberOfChannels(unsigned numberOfChannels) +{ + ASSERT(!isInitialized()); + if (!isInitialized()) + m_numberOfChannels = numberOfChannels; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioDSPKernelProcessor.h b/WebCore/platform/audio/AudioDSPKernelProcessor.h new file mode 100644 index 0000000..e87a810 --- /dev/null +++ b/WebCore/platform/audio/AudioDSPKernelProcessor.h @@ -0,0 +1,76 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 AudioDSPKernelProcessor_h +#define AudioDSPKernelProcessor_h + +#include "AudioBus.h" +#include "AudioProcessor.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioBus; +class AudioDSPKernel; +class AudioProcessor; + +// AudioDSPKernelProcessor processes one input -> one output (N channels each) +// It uses one AudioDSPKernel object per channel to do the processing, thus there is no cross-channel processing. +// Despite this limitation it turns out to be a very common and useful type of processor. + +class AudioDSPKernelProcessor : public AudioProcessor { +public: + // numberOfChannels may be later changed if object is not yet in an "initialized" state + AudioDSPKernelProcessor(double sampleRate, unsigned numberOfChannels); + + // Subclasses create the appropriate type of processing kernel here. + // We'll call this to create a kernel for each channel. + virtual PassOwnPtr<AudioDSPKernel> createKernel() = 0; + + // AudioProcessor methods + virtual void initialize(); + virtual void uninitialize(); + virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess); + virtual void reset(); + virtual void setNumberOfChannels(unsigned numberOfChannels); + + unsigned numberOfChannels() const { return m_numberOfChannels; } + +protected: + unsigned m_numberOfChannels; + Vector<OwnPtr<AudioDSPKernel> > m_kernels; + bool m_hasJustReset; +}; + +} // namespace WebCore + +#endif // AudioDSPKernelProcessor_h diff --git a/WebCore/platform/audio/AudioProcessor.h b/WebCore/platform/audio/AudioProcessor.h new file mode 100644 index 0000000..69ba40f --- /dev/null +++ b/WebCore/platform/audio/AudioProcessor.h @@ -0,0 +1,75 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 AudioProcessor_h +#define AudioProcessor_h + +namespace WebCore { + +class AudioBus; + +// AudioProcessor is an abstract base class representing an audio signal processing object with a single input and a single output, +// where the number of input channels equals the number of output channels. It can be used as one part of a complex DSP algorithm, +// or as the processor for a basic (one input - one output) AudioNode. + +class AudioProcessor { +public: + AudioProcessor(double sampleRate) + : m_initialized(false) + , m_sampleRate(sampleRate) + { + } + + virtual ~AudioProcessor() { } + + // Full initialization can be done here instead of in the constructor. + virtual void initialize() = 0; + virtual void uninitialize() = 0; + + // Processes the source to destination bus. The number of channels must match in source and destination. + virtual void process(AudioBus* source, AudioBus* destination, size_t framesToProcess) = 0; + + // Resets filter state + virtual void reset() = 0; + + virtual void setNumberOfChannels(unsigned) = 0; + + bool isInitialized() const { return m_initialized; } + + double sampleRate() const { return m_sampleRate; } + +protected: + bool m_initialized; + double m_sampleRate; +}; + +} // namespace WebCore + +#endif // AudioProcessor_h diff --git a/WebCore/platform/audio/AudioSourceProvider.h b/WebCore/platform/audio/AudioSourceProvider.h new file mode 100644 index 0000000..773546a --- /dev/null +++ b/WebCore/platform/audio/AudioSourceProvider.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef AudioSourceProvider_h +#define AudioSourceProvider_h + +namespace WebCore { + +class AudioBus; + +// Abstract base-class for a pull-model client. +// provideInput() gets called repeatedly to render time-slices of a continuous audio stream. +class AudioSourceProvider { +public: + virtual void provideInput(AudioBus* bus, size_t framesToProcess) = 0; + virtual ~AudioSourceProvider() { } +}; + +} // WebCore + +#endif // AudioSourceProvider_h diff --git a/WebCore/platform/audio/Biquad.cpp b/WebCore/platform/audio/Biquad.cpp new file mode 100644 index 0000000..6918dd6 --- /dev/null +++ b/WebCore/platform/audio/Biquad.cpp @@ -0,0 +1,280 @@ +/* + * 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 "Biquad.h" + +#include "Accelerate.h" +#include <algorithm> +#include <float.h> +#include <math.h> +#include <stdio.h> + +namespace WebCore { + +const int kBufferSize = 1024; + +Biquad::Biquad() +{ +#if OS(DARWIN) + // Allocate two samples more for filter history + m_inputBuffer.resize(kBufferSize + 2); + m_outputBuffer.resize(kBufferSize + 2); +#endif + + // Initialize as pass-thru (straight-wire, no filter effect) + m_a0 = 1.0; + m_a1 = 0.0; + m_a2 = 0.0; + m_b1 = 0.0; + m_b2 = 0.0; + + m_g = 1.0; + + reset(); // clear filter memory +} + +void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess) +{ +#if OS(DARWIN) + // Use vecLib if available + processFast(sourceP, destP, framesToProcess); +#else + int n = framesToProcess; + + // Create local copies of member variables + double x1 = m_x1; + double x2 = m_x2; + double y1 = m_y1; + double y2 = m_y2; + + double a0 = m_a0; + double a1 = m_a1; + double a2 = m_a2; + double b1 = m_b1; + double b2 = m_b2; + + while (n--) { + // FIXME: this can be optimized by pipelining the multiply adds... + float x = *sourceP++; + float y = a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2; + + y *= m_g; + + *destP++ = y; + + // Update state variables + x2 = x1; + x1 = x; + y2 = y1; + y1 = y; + } + + // Local variables back to member + m_x1 = x1; + m_x2 = x2; + m_y1 = y1; + m_y2 = y2; + + m_a0 = a0; + m_a1 = a1; + m_a2 = a2; + m_b1 = b1; + m_b2 = b2; +#endif +} + +#if OS(DARWIN) + +// Here we have optimized version using Accelerate.framework + +void Biquad::processFast(const float* sourceP, float* destP, size_t framesToProcess) +{ + // Filter coefficients + double B[5]; + B[0] = m_a0; + B[1] = m_a1; + B[2] = m_a2; + B[3] = m_b1; + B[4] = m_b2; + + double* inputP = m_inputBuffer.data(); + double* outputP = m_outputBuffer.data(); + + double* input2P = inputP + 2; + double* output2P = outputP + 2; + + // Break up processing into smaller slices (kBufferSize) if necessary. + + int n = framesToProcess; + + while (n > 0) { + int framesThisTime = n < kBufferSize ? n : kBufferSize; + + // Copy input to input buffer + for (int i = 0; i < framesThisTime; ++i) + input2P[i] = *sourceP++; + + processSliceFast(inputP, outputP, B, framesThisTime); + + // Copy output buffer to output (converts float -> double). + for (int i = 0; i < framesThisTime; ++i) + *destP++ = static_cast<float>(output2P[i]); + + n -= framesThisTime; + } +} + +void Biquad::processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess) +{ + // Use double-precision for filter stability + vDSP_deq22D(sourceP, 1, coefficientsP, destP, 1, framesToProcess); + + // Save history. Note that sourceP and destP reference m_inputBuffer and m_outputBuffer respectively. + // These buffers are allocated (in the constructor) with space for two extra samples so it's OK to access + // array values two beyond framesToProcess. + sourceP[0] = sourceP[framesToProcess - 2 + 2]; + sourceP[1] = sourceP[framesToProcess - 1 + 2]; + destP[0] = destP[framesToProcess - 2 + 2]; + destP[1] = destP[framesToProcess - 1 + 2]; +} + +#endif // OS(DARWIN) + + +void Biquad::reset() +{ + m_x1 = m_x2 = m_y1 = m_y2 = 0.0; + +#if OS(DARWIN) + // Two extra samples for filter history + double* inputP = m_inputBuffer.data(); + inputP[0] = 0.0; + inputP[1] = 0.0; + + double* outputP = m_outputBuffer.data(); + outputP[0] = 0.0; + outputP[1] = 0.0; +#endif +} + +void Biquad::setLowpassParams(double cutoff, double resonance) +{ + resonance = std::max(0.0, resonance); // can't go negative + + double g = pow(10.0, 0.05 * resonance); + double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0); + + // Compute biquad coefficients for lopass filter + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); + double beta = 0.5 * (1.0 - sn) / (1.0 + sn); + double gamma = (0.5 + beta) * cos(theta); + double alpha = 0.25 * (0.5 + beta - gamma); + + m_a0 = 2.0 * alpha; + m_a1 = 2.0 * 2.0*alpha; + m_a2 = 2.0 * alpha; + m_b1 = 2.0 * -gamma; + m_b2 = 2.0 * beta; +} + +void Biquad::setHighpassParams(double cutoff, double resonance) +{ + resonance = std::max(0.0, resonance); // can't go negative + + double g = pow(10.0, 0.05 * resonance); + double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0); + + // Compute biquad coefficients for highpass filter + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); + double beta = 0.5 * (1.0 - sn) / (1.0 + sn); + double gamma = (0.5 + beta) * cos(theta); + double alpha = 0.25 * (0.5 + beta + gamma); + + m_a0 = 2.0 * alpha; + m_a1 = 2.0 * -2.0*alpha; + m_a2 = 2.0 * alpha; + m_b1 = 2.0 * -gamma; + m_b2 = 2.0 * beta; +} + +void Biquad::setLowShelfParams(double cutoff, double dbGain) +{ + double theta = M_PI * cutoff; + + double A = pow(10.0, dbGain / 40.0); + double S = 1.0; // filter slope (1.0 is max value) + double alpha = 0.5 * sin(theta) * sqrt((A + 1.0 / A) * (1.0 / S - 1.0) + 2.0); + + double k = cos(theta); + double k2 = 2.0 * sqrt(A) * alpha; + + double b0 = A * ((A + 1.0) - (A - 1.0) * k + k2); + double b1 = 2.0 * A * ((A - 1.0) - (A + 1.0) * k); + double b2 = A * ((A + 1.0) - (A - 1.0) * k - k2); + double a0 = (A + 1.0) + (A - 1.0) * k + k2; + double a1 = -2.0 * ((A - 1.0) + (A + 1.0) * k); + double a2 = (A + 1.0) + (A - 1.0) * k - k2; + + double a0Inverse = 1.0 / a0; + + m_a0 = b0 * a0Inverse; + m_a1 = b1 * a0Inverse; + m_a2 = b2 * a0Inverse; + m_b1 = a1 * a0Inverse; + m_b2 = a2 * a0Inverse; +} + +void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole) +{ + m_a0 = 1.0; + m_a1 = -2.0 * zero.real(); + + double zeroMag = abs(zero); + m_a2 = zeroMag * zeroMag; + + m_b1 = -2.0 * pole.real(); + + double poleMag = abs(pole); + m_b2 = poleMag * poleMag; +} + +void Biquad::setAllpassPole(const Complex &pole) +{ + Complex zero = Complex(1.0, 0.0) / pole; + setZeroPolePairs(zero, pole); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Biquad.h b/WebCore/platform/audio/Biquad.h new file mode 100644 index 0000000..d68bf4e --- /dev/null +++ b/WebCore/platform/audio/Biquad.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef Biquad_h +#define Biquad_h + +#include "AudioArray.h" +#include <sys/types.h> +#include <wtf/Complex.h> +#include <wtf/Platform.h> + +namespace WebCore { + +// A basic biquad (two-zero / two-pole digital filter) +// +// It can be configured to a number of common and very useful filters: +// lowpass, highpass, shelving, parameteric, notch, allpass, ... + +class Biquad { +public: + Biquad(); + virtual ~Biquad() { } + + void process(const float* sourceP, float* destP, size_t framesToProcess); + + // cutoff is 0-1 normalized, resonance is in dB >= 0.0 + void setLowpassParams(double cutoff, double resonance); + void setHighpassParams(double cutoff, double resonance); + + void setLowShelfParams(double cutoff, double dbGain); + + // FIXME: need to implement a few more common filters + // void setHighShelfParams(double cutoff, double dbGain); + // void setParametricEQParams(double cutoff, double resonance); + + // Set the biquad coefficients given a single zero (other zero will be conjugate) + // and a single pole (other pole will be conjugate) + void setZeroPolePairs(const Complex& zero, const Complex& pole); + + // Set the biquad coefficients given a single pole (other pole will be conjugate) + // (The zeroes will be the inverse of the poles) + void setAllpassPole(const Complex& pole); + + // Resets filter state + void reset(); + +private: + // Filter coefficients + double m_a0; + double m_a1; + double m_a2; + double m_b1; + double m_b2; + + double m_g; + + // Filter memory + double m_x1; // input delayed by 1 sample + double m_x2; // input delayed by 2 samples + double m_y1; // output delayed by 1 sample + double m_y2; // output delayed by 2 samples + +#if OS(DARWIN) + void processFast(const float* sourceP, float* destP, size_t framesToProcess); + void processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess); + + AudioDoubleArray m_inputBuffer; + AudioDoubleArray m_outputBuffer; +#endif +}; + +} // namespace WebCore + +#endif // Biquad_h diff --git a/WebCore/platform/audio/Distance.cpp b/WebCore/platform/audio/Distance.cpp new file mode 100644 index 0000000..0f1b005 --- /dev/null +++ b/WebCore/platform/audio/Distance.cpp @@ -0,0 +1,93 @@ +/* + * 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 "Distance.h" + +#include <algorithm> +#include <math.h> + +using namespace std; + +namespace WebCore { + +DistanceEffect::DistanceEffect() + : m_model(ModelInverse) + , m_isClamped(true) + , m_refDistance(1.0) + , m_maxDistance(10000.0) + , m_rolloffFactor(1.0) +{ +} + +double DistanceEffect::gain(double distance) +{ + // don't go beyond maximum distance + distance = min(distance, m_maxDistance); + + // if clamped, don't get closer than reference distance + if (m_isClamped) + distance = max(distance, m_refDistance); + + switch (m_model) { + case ModelLinear: + return linearGain(distance); + break; + case ModelInverse: + return inverseGain(distance); + break; + case ModelExponential: + return exponentialGain(distance); + break; + + default: + return 0.0; + } +} + +double DistanceEffect::linearGain(double distance) +{ + return (1.0 - m_rolloffFactor * (distance - m_refDistance)) / (m_maxDistance - m_refDistance); +} + +double DistanceEffect::inverseGain(double distance) +{ + return m_refDistance / (m_refDistance + m_rolloffFactor * (distance - m_refDistance)); +} + +double DistanceEffect::exponentialGain(double distance) +{ + return pow(distance / m_refDistance, -m_rolloffFactor); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Distance.h b/WebCore/platform/audio/Distance.h new file mode 100644 index 0000000..c7edded --- /dev/null +++ b/WebCore/platform/audio/Distance.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef Distance_h +#define Distance_h + +namespace WebCore { + +// Distance models are defined according to the OpenAL specification + +class DistanceEffect { +public: + enum ModelType { + ModelLinear = 0, + ModelInverse = 1, + ModelExponential = 2 + }; + + DistanceEffect(); + + // Returns scalar gain for the given distance the current distance model is used + double gain(double distance); + + ModelType model() { return m_model; } + + void setModel(ModelType model, bool clamped) + { + m_model = model; + m_isClamped = clamped; + } + + // Distance params + void setRefDistance(double refDistance) { m_refDistance = refDistance; } + void setMaxDistance(double maxDistance) { m_maxDistance = maxDistance; } + void setRolloffFactor(double rolloffFactor) { m_rolloffFactor = rolloffFactor; } + + double refDistance() const { return m_refDistance; } + double maxDistance() const { return m_maxDistance; } + double rolloffFactor() const { return m_rolloffFactor; } + +protected: + double linearGain(double distance); + double inverseGain(double distance); + double exponentialGain(double distance); + + ModelType m_model; + bool m_isClamped; + double m_refDistance; + double m_maxDistance; + double m_rolloffFactor; +}; + +} // namespace WebCore + +#endif // Distance_h diff --git a/WebCore/platform/audio/FFTFrame.cpp b/WebCore/platform/audio/FFTFrame.cpp new file mode 100644 index 0000000..17292b6 --- /dev/null +++ b/WebCore/platform/audio/FFTFrame.cpp @@ -0,0 +1,269 @@ +/* + * 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 "FFTFrame.h" + +#include <wtf/Complex.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +void FFTFrame::doPaddedFFT(float* data, size_t dataSize) +{ + // Zero-pad the impulse response + AudioFloatArray paddedResponse(fftSize()); // zero-initialized + paddedResponse.copyToRange(data, 0, dataSize); + + // Get the frequency-domain version of padded response + doFFT(paddedResponse.data()); +} + +PassOwnPtr<FFTFrame> FFTFrame::createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x) +{ + OwnPtr<FFTFrame> newFrame = adoptPtr(new FFTFrame(frame1.fftSize())); + + newFrame->interpolateFrequencyComponents(frame1, frame2, x); + + // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing... + int fftSize = newFrame->fftSize(); + AudioFloatArray buffer(fftSize); + newFrame->doInverseFFT(buffer.data()); + buffer.zeroRange(fftSize / 2, fftSize); + + // Put back into frequency domain. + newFrame->doFFT(buffer.data()); + + return newFrame.release(); +} + +void FFTFrame::interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double interp) +{ + // FIXME : with some work, this method could be optimized + + float* realP = realData(); + float* imagP = imagData(); + + const float* realP1 = frame1.realData(); + const float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + m_FFTSize = frame1.fftSize(); + m_log2FFTSize = frame1.log2FFTSize(); + + double s1base = (1.0 - interp); + double s2base = interp; + + double phaseAccum = 0.0; + double lastPhase1 = 0.0; + double lastPhase2 = 0.0; + + realP[0] = static_cast<float>(s1base * realP1[0] + s2base * realP2[0]); + imagP[0] = static_cast<float>(s1base * imagP1[0] + s2base * imagP2[0]); + + int n = m_FFTSize / 2; + + for (int i = 1; i < n; ++i) { + Complex c1(realP1[i], imagP1[i]); + Complex c2(realP2[i], imagP2[i]); + + double mag1 = abs(c1); + double mag2 = abs(c2); + + // Interpolate magnitudes in decibels + double mag1db = 20.0 * log10(mag1); + double mag2db = 20.0 * log10(mag2); + + double s1 = s1base; + double s2 = s2base; + + double magdbdiff = mag1db - mag2db; + + // Empirical tweak to retain higher-frequency zeroes + double threshold = (i > 16) ? 5.0 : 2.0; + + if (magdbdiff < -threshold && mag1db < 0.0) { + s1 = pow(s1, 0.75); + s2 = 1.0 - s1; + } else if (magdbdiff > threshold && mag2db < 0.0) { + s2 = pow(s2, 0.75); + s1 = 1.0 - s2; + } + + // Average magnitude by decibels instead of linearly + double magdb = s1 * mag1db + s2 * mag2db; + double mag = pow(10.0, 0.05 * magdb); + + // Now, deal with phase + double phase1 = arg(c1); + double phase2 = arg(c2); + + double deltaPhase1 = phase1 - lastPhase1; + double deltaPhase2 = phase2 - lastPhase2; + lastPhase1 = phase1; + lastPhase2 = phase2; + + // Unwrap phase deltas + if (deltaPhase1 > M_PI) + deltaPhase1 -= 2.0 * M_PI; + if (deltaPhase1 < -M_PI) + deltaPhase1 += 2.0 * M_PI; + if (deltaPhase2 > M_PI) + deltaPhase2 -= 2.0 * M_PI; + if (deltaPhase2 < -M_PI) + deltaPhase2 += 2.0 * M_PI; + + // Blend group-delays + double deltaPhaseBlend; + + if (deltaPhase1 - deltaPhase2 > M_PI) + deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * M_PI + deltaPhase2); + else if (deltaPhase2 - deltaPhase1 > M_PI) + deltaPhaseBlend = s1 * (2.0 * M_PI + deltaPhase1) + s2 * deltaPhase2; + else + deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2; + + phaseAccum += deltaPhaseBlend; + + // Unwrap + if (phaseAccum > M_PI) + phaseAccum -= 2.0 * M_PI; + if (phaseAccum < -M_PI) + phaseAccum += 2.0 * M_PI; + + Complex c = complexFromMagnitudePhase(mag, phaseAccum); + + realP[i] = static_cast<float>(c.real()); + imagP[i] = static_cast<float>(c.imag()); + } +} + +double FFTFrame::extractAverageGroupDelay() +{ + float* realP = realData(); + float* imagP = imagData(); + + double aveSum = 0.0; + double weightSum = 0.0; + double lastPhase = 0.0; + + int halfSize = fftSize() / 2; + + const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize()); + + // Calculate weighted average group delay + for (int i = 0; i < halfSize; i++) { + Complex c(realP[i], imagP[i]); + double mag = abs(c); + double phase = arg(c); + + double deltaPhase = phase - lastPhase; + lastPhase = phase; + + // Unwrap + if (deltaPhase < -M_PI) + deltaPhase += 2.0 * M_PI; + if (deltaPhase > M_PI) + deltaPhase -= 2.0 * M_PI; + + aveSum += mag * deltaPhase; + weightSum += mag; + } + + // Note how we invert the phase delta wrt frequency since this is how group delay is defined + double ave = aveSum / weightSum; + double aveSampleDelay = -ave / kSamplePhaseDelay; + + // Leave 20 sample headroom (for leading edge of impulse) + if (aveSampleDelay > 20.0) + aveSampleDelay -= 20.0; + + // Remove average group delay (minus 20 samples for headroom) + addConstantGroupDelay(-aveSampleDelay); + + // Remove DC offset + realP[0] = 0.0f; + + return aveSampleDelay; +} + +void FFTFrame::addConstantGroupDelay(double sampleFrameDelay) +{ + int halfSize = fftSize() / 2; + + float* realP = realData(); + float* imagP = imagData(); + + const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize()); + + double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay; + + // Add constant group delay + for (int i = 1; i < halfSize; i++) { + Complex c(realP[i], imagP[i]); + double mag = abs(c); + double phase = arg(c); + + phase += i * phaseAdj; + + Complex c2 = complexFromMagnitudePhase(mag, phase); + + realP[i] = static_cast<float>(c2.real()); + imagP[i] = static_cast<float>(c2.imag()); + } +} + +#ifndef NDEBUG +void FFTFrame::print() +{ + FFTFrame& frame = *this; + float* realP = frame.realData(); + float* imagP = frame.imagData(); + printf("**** \n"); + printf("DC = %f : nyquist = %f\n", realP[0], imagP[0]); + + int n = m_FFTSize / 2; + + for (int i = 1; i < n; i++) { + double mag = sqrt(realP[i] * realP[i] + imagP[i] * imagP[i]); + double phase = atan2(realP[i], imagP[i]); + + printf("[%d] (%f %f)\n", i, mag, phase); + } + printf("****\n"); +} +#endif // NDEBUG + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/FFTFrame.h b/WebCore/platform/audio/FFTFrame.h new file mode 100644 index 0000000..6147fc1 --- /dev/null +++ b/WebCore/platform/audio/FFTFrame.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef FFTFrame_h +#define FFTFrame_h + +#include "AudioArray.h" + +#if OS(DARWIN) +#include <Accelerate/Accelerate.h> +#endif + +#include <wtf/PassOwnPtr.h> +#include <wtf/Platform.h> + +namespace WebCore { + +// Defines the interface for an "FFT frame", an object which is able to perform a forward +// and reverse FFT, internally storing the resultant frequency-domain data. + +class FFTFrame { +public: + // The constructors, destructor, and methods up to the CROSS-PLATFORM section have platform-dependent implementations. + + FFTFrame(unsigned fftSize); + FFTFrame(); // creates a blank/empty frame for later use with createInterpolatedFrame() + FFTFrame(const FFTFrame& frame); + ~FFTFrame(); + + static void cleanup(); + void doFFT(float* data); + void doInverseFFT(float* data); + void multiply(const FFTFrame& frame); // multiplies ourself with frame : effectively operator*=() + + float* realData() const; + float* imagData() const; + + void print(); // for debugging + + // CROSS-PLATFORM + // The remaining public methods have cross-platform implementations: + + // Interpolates from frame1 -> frame2 as x goes from 0.0 -> 1.0 + static PassOwnPtr<FFTFrame> createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x); + + void doPaddedFFT(float* data, size_t dataSize); // zero-padding with dataSize <= fftSize + double extractAverageGroupDelay(); + void addConstantGroupDelay(double sampleFrameDelay); + + unsigned fftSize() const { return m_FFTSize; } + unsigned log2FFTSize() const { return m_log2FFTSize; } + +private: + unsigned m_FFTSize; + unsigned m_log2FFTSize; + + void interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double x); + +#if OS(DARWIN) + DSPSplitComplex& dspSplitComplex() { return m_frame; } + DSPSplitComplex dspSplitComplex() const { return m_frame; } + + static FFTSetup fftSetupForSize(unsigned fftSize); + + static FFTSetup* fftSetups; + + FFTSetup m_FFTSetup; + + DSPSplitComplex m_frame; + AudioFloatArray m_realData; + AudioFloatArray m_imagData; +#endif // OS(DARWIN) +}; + +} // namespace WebCore + +#endif // FFTFrame_h diff --git a/WebCore/platform/audio/Panner.cpp b/WebCore/platform/audio/Panner.cpp new file mode 100644 index 0000000..29a1fbe --- /dev/null +++ b/WebCore/platform/audio/Panner.cpp @@ -0,0 +1,71 @@ +/* + * 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 "Panner.h" + +#include "EqualPowerPanner.h" +#include "HRTFPanner.h" +#include "PassThroughPanner.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +PassOwnPtr<Panner> Panner::create(PanningModel model, double sampleRate) +{ + OwnPtr<Panner> panner; + + switch (model) { + case PanningModelEqualPower: + panner = adoptPtr(new EqualPowerPanner()); + break; + + case PanningModelHRTF: + panner = adoptPtr(new HRTFPanner(sampleRate)); + break; + + case PanningModelPassthrough: + panner = adoptPtr(new PassThroughPanner()); + break; + + // FIXME: sound field panning is not yet implemented... + case PanningModelSoundField: + default: + ASSERT_NOT_REACHED(); + return 0; + } + + return panner.release(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/Panner.h b/WebCore/platform/audio/Panner.h new file mode 100644 index 0000000..c8e219b --- /dev/null +++ b/WebCore/platform/audio/Panner.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 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. + */ + +#ifndef Panner_h +#define Panner_h + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class AudioBus; + +// Abstract base class for panning a mono or stereo source. + +class Panner { +public: + enum { + PanningModelPassthrough = 0, + PanningModelEqualPower = 1, + PanningModelHRTF = 2, + PanningModelSoundField = 3 + }; + + typedef unsigned PanningModel; + + static PassOwnPtr<Panner> create(PanningModel model, double sampleRate); + + virtual ~Panner() { }; + + PanningModel panningModel() const { return m_panningModel; } + + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) = 0; + + virtual void reset() = 0; + +protected: + Panner(PanningModel model) : m_panningModel(model) { } + + PanningModel m_panningModel; +}; + +} // namespace WebCore + +#endif // Panner_h diff --git a/WebCore/platform/audio/mac/FFTFrameMac.cpp b/WebCore/platform/audio/mac/FFTFrameMac.cpp new file mode 100644 index 0000000..0f7efb7 --- /dev/null +++ b/WebCore/platform/audio/mac/FFTFrameMac.cpp @@ -0,0 +1,191 @@ +/* + * 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. + */ + +// Mac OS X - specific FFTFrame implementation + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +namespace WebCore { + +const int kMaxFFTPow2Size = 24; + +FFTSetup* FFTFrame::fftSetups = 0; + +// Normal constructor: allocates for a given fftSize +FFTFrame::FFTFrame(unsigned fftSize) + : m_realData(fftSize) + , m_imagData(fftSize) +{ + m_FFTSize = fftSize; + m_log2FFTSize = static_cast<unsigned>(log2(fftSize)); + + // We only allow power of two + ASSERT(1UL << m_log2FFTSize == m_FFTSize); + + // Lazily create and share fftSetup with other frames + m_FFTSetup = fftSetupForSize(fftSize); + + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); +} + +// Creates a blank/empty frame (interpolate() must later be called) +FFTFrame::FFTFrame() + : m_realData(0) + , m_imagData(0) +{ + // Later will be set to correct values when interpolate() is called + m_frame.realp = 0; + m_frame.imagp = 0; + + m_FFTSize = 0; + m_log2FFTSize = 0; +} + +// Copy constructor +FFTFrame::FFTFrame(const FFTFrame& frame) + : m_FFTSize(frame.m_FFTSize) + , m_log2FFTSize(frame.m_log2FFTSize) + , m_FFTSetup(frame.m_FFTSetup) + , m_realData(frame.m_FFTSize) + , m_imagData(frame.m_FFTSize) +{ + // Setup frame data + m_frame.realp = m_realData.data(); + m_frame.imagp = m_imagData.data(); + + // Copy/setup frame data + unsigned nbytes = sizeof(float) * m_FFTSize; + memcpy(realData(), frame.m_frame.realp, nbytes); + memcpy(imagData(), frame.m_frame.imagp, nbytes); +} + +FFTFrame::~FFTFrame() +{ +} + +void FFTFrame::multiply(const FFTFrame& frame) +{ + FFTFrame& frame1 = *this; + const FFTFrame& frame2 = frame; + + float* realP1 = frame1.realData(); + float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + // Scale accounts for vecLib's peculiar scaling + // This ensures the right scaling all the way back to inverse FFT + float scale = 0.5f; + + // Multiply packed DC/nyquist component + realP1[0] *= scale * realP2[0]; + imagP1[0] *= scale * imagP2[0]; + + // Multiply the rest, skipping packed DC/Nyquist components + DSPSplitComplex sc1 = frame1.dspSplitComplex(); + sc1.realp++; + sc1.imagp++; + + DSPSplitComplex sc2 = frame2.dspSplitComplex(); + sc2.realp++; + sc2.imagp++; + + unsigned halfSize = m_FFTSize / 2; + + // Complex multiply + vDSP_zvmul(&sc1, 1, &sc2, 1, &sc1, 1, halfSize - 1, 1 /* normal multiplication */); + + // We've previously scaled the packed part, now scale the rest..... + vDSP_vsmul(sc1.realp, 1, &scale, sc1.realp, 1, halfSize - 1); + vDSP_vsmul(sc1.imagp, 1, &scale, sc1.imagp, 1, halfSize - 1); +} + +void FFTFrame::doFFT(float* data) +{ + vDSP_ctoz((DSPComplex*)data, 2, &m_frame, 1, m_FFTSize / 2); + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_FORWARD); +} + +void FFTFrame::doInverseFFT(float* data) +{ + vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_INVERSE); + vDSP_ztoc(&m_frame, 1, (DSPComplex*)data, 2, m_FFTSize / 2); + + // Do final scaling so that x == IFFT(FFT(x)) + float scale = 0.5f / m_FFTSize; + vDSP_vsmul(data, 1, &scale, data, 1, m_FFTSize); +} + +FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize) +{ + if (!fftSetups) { + fftSetups = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size); + memset(fftSetups, 0, sizeof(FFTSetup) * kMaxFFTPow2Size); + } + + int pow2size = static_cast<int>(log2(fftSize)); + ASSERT(pow2size < kMaxFFTPow2Size); + if (!fftSetups[pow2size]) + fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2); + + return fftSetups[pow2size]; +} + +void FFTFrame::cleanup() +{ + if (!fftSetups) + return; + + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + if (fftSetups[i]) + vDSP_destroy_fftsetup(fftSetups[i]); + } + + free(fftSetups); + fftSetups = 0; +} + +float* FFTFrame::realData() const +{ + return m_frame.realp; +} + +float* FFTFrame::imagData() const +{ + return m_frame.imagp; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) |