diff options
Diffstat (limited to 'Source/WebCore/platform/audio')
302 files changed, 7219 insertions, 0 deletions
diff --git a/Source/WebCore/platform/audio/AudioArray.h b/Source/WebCore/platform/audio/AudioArray.h new file mode 100644 index 0000000..9c25b0f --- /dev/null +++ b/Source/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/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) diff --git a/Source/WebCore/platform/audio/AudioBus.h b/Source/WebCore/platform/audio/AudioBus.h new file mode 100644 index 0000000..4318b81 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioBus.h @@ -0,0 +1,141 @@ +/* + * 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(); + + static PassOwnPtr<AudioBus> loadPlatformResource(const char* name, double sampleRate); + +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/Source/WebCore/platform/audio/AudioChannel.cpp b/Source/WebCore/platform/audio/AudioChannel.cpp new file mode 100644 index 0000000..a962deb --- /dev/null +++ b/Source/WebCore/platform/audio/AudioChannel.cpp @@ -0,0 +1,104 @@ +/* + * 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 "VectorMath.h" +#include <algorithm> +#include <math.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +using namespace VectorMath; + +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/Source/WebCore/platform/audio/AudioChannel.h b/Source/WebCore/platform/audio/AudioChannel.h new file mode 100644 index 0000000..6816830 --- /dev/null +++ b/Source/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 "AudioArray.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/Source/WebCore/platform/audio/AudioDSPKernel.h b/Source/WebCore/platform/audio/AudioDSPKernel.h new file mode 100644 index 0000000..d0719c5 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioDSPKernel.h @@ -0,0 +1,73 @@ +/* + * 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) + , m_sampleRate(kernelProcessor->sampleRate()) + { + } + + AudioDSPKernel(double sampleRate) + : m_kernelProcessor(0) + , m_sampleRate(sampleRate) + { + } + + 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 m_sampleRate; } + double nyquist() const { return 0.5 * sampleRate(); } + + AudioDSPKernelProcessor* processor() { return m_kernelProcessor; } + const AudioDSPKernelProcessor* processor() const { return m_kernelProcessor; } + +protected: + AudioDSPKernelProcessor* m_kernelProcessor; + double m_sampleRate; +}; + +} // namespace WebCore + +#endif // AudioDSPKernel_h diff --git a/Source/WebCore/platform/audio/AudioDSPKernelProcessor.cpp b/Source/WebCore/platform/audio/AudioDSPKernelProcessor.cpp new file mode 100644 index 0000000..d79afd5 --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/AudioDSPKernelProcessor.h b/Source/WebCore/platform/audio/AudioDSPKernelProcessor.h new file mode 100644 index 0000000..e87a810 --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/AudioDestination.h b/Source/WebCore/platform/audio/AudioDestination.h new file mode 100644 index 0000000..9498110 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioDestination.h @@ -0,0 +1,59 @@ +/* + * 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 AudioDestination_h +#define AudioDestination_h + +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class AudioSourceProvider; + +// Abstraction for an audio output to the audio hardware +// An AudioSourceProvider is called back periodically to provide the rendered audio stream. + +class AudioDestination { +public: + static PassOwnPtr<AudioDestination> create(AudioSourceProvider&, double sampleRate); + + virtual ~AudioDestination() { } + + virtual void start() = 0; + virtual void stop() = 0; + virtual bool isPlaying() = 0; + + // Sample-rate conversion may happen in AudioDestination to the hardware sample-rate + virtual double sampleRate() const = 0; + static double hardwareSampleRate(); +}; + +} // namespace WebCore + +#endif // AudioDestination_h diff --git a/Source/WebCore/platform/audio/AudioFileReader.h b/Source/WebCore/platform/audio/AudioFileReader.h new file mode 100644 index 0000000..3c02490 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioFileReader.h @@ -0,0 +1,54 @@ +/* + * 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 AudioFileReader_h +#define AudioFileReader_h + +#include <stdlib.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class AudioBus; + +// For both create functions: +// Pass in 0.0 for sampleRate to use the file's sample-rate, otherwise a sample-rate conversion to the requested +// sampleRate will be made (if it doesn't already match the file's sample-rate). +// The created buffer will have its sample-rate set correctly to the result. + +PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate); + +PassOwnPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate); + +// May pass in 0.0 for sampleRate in which case it will use the AudioBus's sampleRate +void writeBusToAudioFile(AudioBus* bus, const char* filePath, double fileSampleRate); + +} // namespace WebCore + +#endif // AudioFileReader_h diff --git a/Source/WebCore/platform/audio/AudioProcessor.h b/Source/WebCore/platform/audio/AudioProcessor.h new file mode 100644 index 0000000..69ba40f --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/AudioResampler.cpp b/Source/WebCore/platform/audio/AudioResampler.cpp new file mode 100644 index 0000000..ba5b58e --- /dev/null +++ b/Source/WebCore/platform/audio/AudioResampler.cpp @@ -0,0 +1,128 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioResampler.h" + +#include "AudioBus.h" +#include <algorithm> +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +const double AudioResampler::MaxRate = 8.0; + +AudioResampler::AudioResampler() + : m_rate(1.0) +{ + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + m_sourceBus = adoptPtr(new AudioBus(1, 0, false)); +} + +AudioResampler::AudioResampler(unsigned numberOfChannels) + : m_rate(1.0) +{ + for (unsigned i = 0; i < numberOfChannels; ++i) + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + + m_sourceBus = adoptPtr(new AudioBus(numberOfChannels, 0, false)); +} + +void AudioResampler::configureChannels(unsigned numberOfChannels) +{ + unsigned currentSize = m_kernels.size(); + if (numberOfChannels == currentSize) + return; // already setup + + // First deal with adding or removing kernels. + if (numberOfChannels > currentSize) { + for (unsigned i = currentSize; i < numberOfChannels; ++i) + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + } else + m_kernels.resize(numberOfChannels); + + // Reconfigure our source bus to the new channel size. + m_sourceBus = adoptPtr(new AudioBus(numberOfChannels, 0, false)); +} + +void AudioResampler::process(AudioSourceProvider* provider, AudioBus* destinationBus, size_t framesToProcess) +{ + ASSERT(provider); + if (!provider) + return; + + unsigned numberOfChannels = m_kernels.size(); + + // Make sure our configuration matches the bus we're rendering to. + bool channelsMatch = (destinationBus && destinationBus->numberOfChannels() == numberOfChannels); + ASSERT(channelsMatch); + if (!channelsMatch) + return; + + // Setup the source bus. + for (unsigned i = 0; i < numberOfChannels; ++i) { + // Figure out how many frames we need to get from the provider, and a pointer to the buffer. + size_t framesNeeded; + float* fillPointer = m_kernels[i]->getSourcePointer(framesToProcess, &framesNeeded); + ASSERT(fillPointer); + if (!fillPointer) + return; + + m_sourceBus->setChannelMemory(i, fillPointer, framesNeeded); + } + + // Ask the provider to supply the desired number of source frames. + provider->provideInput(m_sourceBus.get(), m_sourceBus->length()); + + // Now that we have the source data, resample each channel into the destination bus. + // FIXME: optimize for the common stereo case where it's faster to process both left/right channels in the same inner loop. + for (unsigned i = 0; i < numberOfChannels; ++i) { + float* destination = destinationBus->channel(i)->data(); + m_kernels[i]->process(destination, framesToProcess); + } +} + +void AudioResampler::setRate(double rate) +{ + if (isnan(rate) || isinf(rate) || rate <= 0.0) + return; + + m_rate = min(AudioResampler::MaxRate, rate); +} + +void AudioResampler::reset() +{ + unsigned numberOfChannels = m_kernels.size(); + for (unsigned i = 0; i < numberOfChannels; ++i) + m_kernels[i]->reset(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/AudioResampler.h b/Source/WebCore/platform/audio/AudioResampler.h new file mode 100644 index 0000000..ed352b8 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioResampler.h @@ -0,0 +1,68 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioResampler_h +#define AudioResampler_h + +#include "AudioBus.h" +#include "AudioResamplerKernel.h" +#include "AudioSourceProvider.h" +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// AudioResampler resamples the audio stream from an AudioSourceProvider. +// The audio stream may be single or multi-channel. +// The default constructor defaults to single-channel (mono). + +class AudioResampler { +public: + AudioResampler(); + AudioResampler(unsigned numberOfChannels); + ~AudioResampler() { } + + // Given an AudioSourceProvider, process() resamples the source stream into destinationBus. + void process(AudioSourceProvider*, AudioBus* destinationBus, size_t framesToProcess); + + // Resets the processing state. + void reset(); + + void configureChannels(unsigned numberOfChannels); + + // 0 < rate <= MaxRate + void setRate(double rate); + double rate() const { return m_rate; } + + static const double MaxRate; + +private: + double m_rate; + Vector<OwnPtr<AudioResamplerKernel> > m_kernels; + OwnPtr<AudioBus> m_sourceBus; +}; + +} // namespace WebCore + +#endif // AudioResampler_h diff --git a/Source/WebCore/platform/audio/AudioResamplerKernel.cpp b/Source/WebCore/platform/audio/AudioResamplerKernel.cpp new file mode 100644 index 0000000..7b99997 --- /dev/null +++ b/Source/WebCore/platform/audio/AudioResamplerKernel.cpp @@ -0,0 +1,143 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioResamplerKernel.h" + +#include "AudioResampler.h" +#include <algorithm> + +using namespace std; + +namespace WebCore { + +const size_t AudioResamplerKernel::MaxFramesToProcess = 128; + +AudioResamplerKernel::AudioResamplerKernel(AudioResampler* resampler) + : m_resampler(resampler) + // The buffer size must be large enough to hold up to two extra sample frames for the linear interpolation. + , m_sourceBuffer(2 + static_cast<int>(MaxFramesToProcess * AudioResampler::MaxRate)) + , m_virtualReadIndex(0.0) + , m_fillIndex(0) +{ + m_lastValues[0] = 0.0f; + m_lastValues[1] = 0.0f; +} + +float* AudioResamplerKernel::getSourcePointer(size_t framesToProcess, size_t* numberOfSourceFramesNeededP) +{ + ASSERT(framesToProcess <= MaxFramesToProcess); + + // Calculate the next "virtual" index. After process() is called, m_virtualReadIndex will equal this value. + double nextFractionalIndex = m_virtualReadIndex + framesToProcess * rate(); + + // Because we're linearly interpolating between the previous and next sample we need to round up so we include the next sample. + int endIndex = static_cast<int>(nextFractionalIndex + 1.0); // round up to next integer index + + // Determine how many input frames we'll need. + // We need to fill the buffer up to and including endIndex (so add 1) but we've already buffered m_fillIndex frames from last time. + size_t framesNeeded = 1 + endIndex - m_fillIndex; + if (numberOfSourceFramesNeededP) + *numberOfSourceFramesNeededP = framesNeeded; + + // Do bounds checking for the source buffer. + bool isGood = m_fillIndex < m_sourceBuffer.size() && m_fillIndex + framesNeeded <= m_sourceBuffer.size(); + ASSERT(isGood); + if (!isGood) + return 0; + + return m_sourceBuffer.data() + m_fillIndex; +} + +void AudioResamplerKernel::process(float* destination, size_t framesToProcess) +{ + ASSERT(framesToProcess <= MaxFramesToProcess); + + float* source = m_sourceBuffer.data(); + + double rate = this->rate(); + rate = max(0.0, rate); + rate = min(AudioResampler::MaxRate, rate); + + // Start out with the previous saved values (if any). + if (m_fillIndex > 0) { + source[0] = m_lastValues[0]; + source[1] = m_lastValues[1]; + } + + // Make a local copy. + double virtualReadIndex = m_virtualReadIndex; + + // Sanity check source buffer access. + ASSERT(framesToProcess > 0); + ASSERT(virtualReadIndex >= 0 && 1 + static_cast<unsigned>(virtualReadIndex + (framesToProcess - 1) * rate) < m_sourceBuffer.size()); + + // Do the linear interpolation. + int n = framesToProcess; + while (n--) { + unsigned readIndex = static_cast<unsigned>(virtualReadIndex); + double interpolationFactor = virtualReadIndex - readIndex; + + double sample1 = source[readIndex]; + double sample2 = source[readIndex + 1]; + + double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2; + + *destination++ = static_cast<float>(sample); + + virtualReadIndex += rate; + } + + // Save the last two sample-frames which will later be used at the beginning of the source buffer the next time around. + int readIndex = static_cast<int>(virtualReadIndex); + m_lastValues[0] = source[readIndex]; + m_lastValues[1] = source[readIndex + 1]; + m_fillIndex = 2; + + // Wrap the virtual read index back to the start of the buffer. + virtualReadIndex -= readIndex; + + // Put local copy back into member variable. + m_virtualReadIndex = virtualReadIndex; +} + +void AudioResamplerKernel::reset() +{ + m_virtualReadIndex = 0.0; + m_fillIndex = 0; + m_lastValues[0] = 0.0f; + m_lastValues[1] = 0.0f; +} + +double AudioResamplerKernel::rate() const +{ + return m_resampler->rate(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/AudioResamplerKernel.h b/Source/WebCore/platform/audio/AudioResamplerKernel.h new file mode 100644 index 0000000..99d877b --- /dev/null +++ b/Source/WebCore/platform/audio/AudioResamplerKernel.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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioResamplerKernel_h +#define AudioResamplerKernel_h + +#include "AudioArray.h" + +namespace WebCore { + +class AudioResampler; + +// AudioResamplerKernel does resampling on a single mono channel. +// It uses a simple linear interpolation for good performance. + +class AudioResamplerKernel { +public: + AudioResamplerKernel(AudioResampler*); + + // getSourcePointer() should be called each time before process() is called. + // Given a number of frames to process (for subsequent call to process()), it returns a pointer and numberOfSourceFramesNeeded + // where sample data should be copied. This sample data provides the input to the resampler when process() is called. + // framesToProcess must be less than or equal to MaxFramesToProcess. + float* getSourcePointer(size_t framesToProcess, size_t* numberOfSourceFramesNeeded); + + // process() resamples framesToProcess frames from the source into destination. + // Each call to process() must be preceded by a call to getSourcePointer() so that source input may be supplied. + // framesToProcess must be less than or equal to MaxFramesToProcess. + void process(float* destination, size_t framesToProcess); + + // Resets the processing state. + void reset(); + + static const size_t MaxFramesToProcess; + +private: + double rate() const; + + AudioResampler* m_resampler; + AudioFloatArray m_sourceBuffer; + + // This is a (floating point) read index on the input stream. + double m_virtualReadIndex; + + // We need to have continuity from one call of process() to the next. + // m_lastValues stores the last two sample values from the last call to process(). + // m_fillIndex represents how many buffered samples we have which can be as many as 2. + // For the first call to process() (or after reset()) there will be no buffered samples. + float m_lastValues[2]; + unsigned m_fillIndex; +}; + +} // namespace WebCore + +#endif // AudioResamplerKernel_h diff --git a/Source/WebCore/platform/audio/AudioSourceProvider.h b/Source/WebCore/platform/audio/AudioSourceProvider.h new file mode 100644 index 0000000..773546a --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/AudioUtilities.cpp b/Source/WebCore/platform/audio/AudioUtilities.cpp new file mode 100644 index 0000000..7a4b32e --- /dev/null +++ b/Source/WebCore/platform/audio/AudioUtilities.cpp @@ -0,0 +1,63 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioUtilities.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +namespace AudioUtilities { + +double decibelsToLinear(double decibels) +{ + return pow(10.0, 0.05 * decibels); +} + +double linearToDecibels(double linear) +{ + // It's not possible to calculate decibels for a zero linear value since it would be -Inf. + // -1000.0 dB represents a very tiny linear value in case we ever reach this case. + ASSERT(linear); + if (!linear) + return -1000.0; + + return 20.0 * log10(linear); +} + +double discreteTimeConstantForSampleRate(double timeConstant, double sampleRate) +{ + // hardcoded value is temporary build fix for Windows. + // FIXME: replace hardcode 2.718282 with M_E until the correct MathExtras.h solution is determined. + return 1.0 - pow(1.0 / 2.718282, 1.0 / (sampleRate * timeConstant)); +} + +} // AudioUtilites + +} // WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/AudioUtilities.h b/Source/WebCore/platform/audio/AudioUtilities.h new file mode 100644 index 0000000..7cf44ce --- /dev/null +++ b/Source/WebCore/platform/audio/AudioUtilities.h @@ -0,0 +1,45 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AudioUtilities_h +#define AudioUtilities_h + +namespace WebCore { + +namespace AudioUtilities { + +// Standard functions for converting to and from decibel values from linear. +double linearToDecibels(double); +double decibelsToLinear(double); + +// timeConstant is the time it takes a first-order linear time-invariant system +// to reach the value 1 - 1/e (around 63.2%) given a step input response. +// discreteTimeConstantForSampleRate() will return the discrete time-constant for the specific sampleRate. +double discreteTimeConstantForSampleRate(double timeConstant, double sampleRate); + +} // AudioUtilites + +} // WebCore + +#endif // AudioUtilities_h diff --git a/Source/WebCore/platform/audio/Biquad.cpp b/Source/WebCore/platform/audio/Biquad.cpp new file mode 100644 index 0000000..20143e3 --- /dev/null +++ b/Source/WebCore/platform/audio/Biquad.cpp @@ -0,0 +1,282 @@ +/* + * 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 <algorithm> +#include <stdio.h> +#include <wtf/MathExtras.h> + +#if OS(DARWIN) +#include <Accelerate/Accelerate.h> +#endif + +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 = piDouble * 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 = piDouble * 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 = piDouble * 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/Source/WebCore/platform/audio/Biquad.h b/Source/WebCore/platform/audio/Biquad.h new file mode 100644 index 0000000..d68bf4e --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/Cone.cpp b/Source/WebCore/platform/audio/Cone.cpp new file mode 100644 index 0000000..f514cde --- /dev/null +++ b/Source/WebCore/platform/audio/Cone.cpp @@ -0,0 +1,85 @@ +/* + * 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 "Cone.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +ConeEffect::ConeEffect() + : m_innerAngle(360.0) + , m_outerAngle(360.0) + , m_outerGain(0.0) +{ +} + +double ConeEffect::gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition) +{ + if (sourceOrientation.isZero() || ((m_innerAngle == 360.0) && (m_outerAngle == 360.0))) + return 1.0; // no cone specified - unity gain + + // Normalized source-listener vector + FloatPoint3D sourceToListener = listenerPosition - sourcePosition; + sourceToListener.normalize(); + + FloatPoint3D normalizedSourceOrientation = sourceOrientation; + normalizedSourceOrientation.normalize(); + + // Angle between the source orientation vector and the source-listener vector + double dotProduct = sourceToListener.dot(normalizedSourceOrientation); + double angle = 180.0 * acos(dotProduct) / piDouble; + double absAngle = fabs(angle); + + // Divide by 2.0 here since API is entire angle (not half-angle) + double absInnerAngle = fabs(m_innerAngle) / 2.0; + double absOuterAngle = fabs(m_outerAngle) / 2.0; + double gain = 1.0; + + if (absAngle <= absInnerAngle) + // No attenuation + gain = 1.0; + else if (absAngle >= absOuterAngle) + // Max attenuation + gain = m_outerGain; + else { + // Between inner and outer cones + // inner -> outer, x goes from 0 -> 1 + double x = (absAngle - absInnerAngle) / (absOuterAngle - absInnerAngle); + gain = (1.0 - x) + m_outerGain * x; + } + + return gain; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/Cone.h b/Source/WebCore/platform/audio/Cone.h new file mode 100644 index 0000000..f566018 --- /dev/null +++ b/Source/WebCore/platform/audio/Cone.h @@ -0,0 +1,63 @@ +/* + * 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 Cone_h +#define Cone_h + +#include "FloatPoint3D.h" + +namespace WebCore { + +// Cone gain is defined according to the OpenAL specification + +class ConeEffect { +public: + ConeEffect(); + + // Returns scalar gain for the given source/listener positions/orientations + double gain(FloatPoint3D sourcePosition, FloatPoint3D sourceOrientation, FloatPoint3D listenerPosition); + + // Angles in degrees + void setInnerAngle(double innerAngle) { m_innerAngle = innerAngle; } + double innerAngle() const { return m_innerAngle; } + + void setOuterAngle(double outerAngle) { m_outerAngle = outerAngle; } + double outerAngle() const { return m_outerAngle; } + + void setOuterGain(double outerGain) { m_outerGain = outerGain; } + double outerGain() const { return m_outerGain; } + +protected: + double m_innerAngle; + double m_outerAngle; + double m_outerGain; +}; + +} // namespace WebCore + +#endif // Cone_h diff --git a/Source/WebCore/platform/audio/Distance.cpp b/Source/WebCore/platform/audio/Distance.cpp new file mode 100644 index 0000000..0f1b005 --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/Distance.h b/Source/WebCore/platform/audio/Distance.h new file mode 100644 index 0000000..c7edded --- /dev/null +++ b/Source/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/Source/WebCore/platform/audio/EqualPowerPanner.cpp b/Source/WebCore/platform/audio/EqualPowerPanner.cpp new file mode 100644 index 0000000..002b7c6 --- /dev/null +++ b/Source/WebCore/platform/audio/EqualPowerPanner.cpp @@ -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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "EqualPowerPanner.h" + +#include "AudioBus.h" +#include "AudioUtilities.h" +#include <wtf/MathExtras.h> + +// Use a 50ms smoothing / de-zippering time-constant. +const double SmoothingTimeConstant = 0.050; + +namespace WebCore { + +EqualPowerPanner::EqualPowerPanner(double sampleRate) + : Panner(PanningModelEqualPower) + , m_isFirstRender(true) + , m_gainL(0.0) + , m_gainR(0.0) +{ + m_smoothingConstant = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, sampleRate); +} + +void EqualPowerPanner::pan(double azimuth, double /*elevation*/, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) +{ + // FIXME: implement stereo sources + bool isInputSafe = inputBus && inputBus->numberOfChannels() == 1 && framesToProcess <= inputBus->length(); + ASSERT(isInputSafe); + if (!isInputSafe) + return; + + bool isOutputSafe = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length(); + ASSERT(isOutputSafe); + if (!isOutputSafe) + return; + + AudioChannel* channel = inputBus->channel(0); + float* sourceP = channel->data(); + float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->data(); + float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->data(); + + if (!sourceP || !destinationL || !destinationR) + return; + + // Pan smoothly from left to right with azimuth going from -30 -> +30 degrees. + double desiredPanPosition; + if (azimuth > 30.0) + desiredPanPosition = 1.0; + else if (azimuth < -30.0) + desiredPanPosition = 0.0; + else + desiredPanPosition = (azimuth + 30.0) / 60.0; + + double desiredGainL = 0.5 * cos(piDouble * desiredPanPosition) + 0.5; + double desiredGainR = sqrt(1.0 - desiredGainL*desiredGainL); + + // Don't de-zipper on first render call. + if (m_isFirstRender) { + m_isFirstRender = false; + m_gainL = desiredGainL; + m_gainR = desiredGainR; + } + + // Cache in local variables. + double gainL = m_gainL; + double gainR = m_gainR; + + // Get local copy of smoothing constant. + const double SmoothingConstant = m_smoothingConstant; + + int n = framesToProcess; + + while (n--) { + float input = *sourceP++; + gainL += (desiredGainL - gainL) * SmoothingConstant; + gainR += (desiredGainR - gainR) * SmoothingConstant; + *destinationL++ = static_cast<float>(input * gainL); + *destinationR++ = static_cast<float>(input * gainR); + } + + m_gainL = gainL; + m_gainR = gainR; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/EqualPowerPanner.h b/Source/WebCore/platform/audio/EqualPowerPanner.h new file mode 100644 index 0000000..f20617e --- /dev/null +++ b/Source/WebCore/platform/audio/EqualPowerPanner.h @@ -0,0 +1,53 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EqualPowerPanner_h +#define EqualPowerPanner_h + +#include "Panner.h" + +namespace WebCore { + +// Common type of stereo panner as found in normal audio mixing equipment. + +class EqualPowerPanner : public Panner { +public: + EqualPowerPanner(double sampleRate); + + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBuf, size_t framesToProcess); + + virtual void reset() { m_isFirstRender = true; } + +private: + // For smoothing / de-zippering + bool m_isFirstRender; + double m_smoothingConstant; + + double m_gainL; + double m_gainR; +}; + +} // namespace WebCore + +#endif // EqualPowerPanner_h diff --git a/Source/WebCore/platform/audio/FFTConvolver.cpp b/Source/WebCore/platform/audio/FFTConvolver.cpp new file mode 100644 index 0000000..9093433 --- /dev/null +++ b/Source/WebCore/platform/audio/FFTConvolver.cpp @@ -0,0 +1,110 @@ +/* + * 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 "FFTConvolver.h" + +#include "VectorMath.h" + +namespace WebCore { + +using namespace VectorMath; + +FFTConvolver::FFTConvolver(size_t fftSize) + : m_frame(fftSize) + , m_readWriteIndex(0) + , m_inputBuffer(fftSize) // 2nd half of buffer is always zeroed + , m_outputBuffer(fftSize) + , m_lastOverlapBuffer(fftSize / 2) +{ +} + +void FFTConvolver::process(FFTFrame* fftKernel, float* sourceP, float* destP, size_t framesToProcess) +{ + // FIXME: make so framesToProcess is not required to fit evenly into fftSize/2 + + // Copy samples to input buffer (note contraint above!) + float* inputP = m_inputBuffer.data(); + + // Sanity check + bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + framesToProcess <= m_inputBuffer.size(); + ASSERT(isCopyGood1); + if (!isCopyGood1) + return; + + memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * framesToProcess); + + // Copy samples from output buffer + float* outputP = m_outputBuffer.data(); + + // Sanity check + bool isCopyGood2 = destP && outputP && m_readWriteIndex + framesToProcess <= m_outputBuffer.size(); + ASSERT(isCopyGood2); + if (!isCopyGood2) + return; + + memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * framesToProcess); + m_readWriteIndex += framesToProcess; + + + // Check if it's time to perform the next FFT + size_t halfSize = fftSize() / 2; + if (m_readWriteIndex == halfSize) { + // The input buffer is now filled (get frequency-domain version) + m_frame.doFFT(m_inputBuffer.data()); + m_frame.multiply(*fftKernel); + m_frame.doInverseFFT(m_outputBuffer.data()); + + // Overlap-add 1st half from previous time + vadd(m_outputBuffer.data(), 1, m_lastOverlapBuffer.data(), 1, m_outputBuffer.data(), 1, halfSize); + + // Finally, save 2nd half of result + bool isCopyGood3 = m_outputBuffer.size() == 2 * halfSize && m_lastOverlapBuffer.size() == halfSize; + ASSERT(isCopyGood3); + if (!isCopyGood3) + return; + + memcpy(m_lastOverlapBuffer.data(), m_outputBuffer.data() + halfSize, sizeof(float) * halfSize); + + // Reset index back to start for next time + m_readWriteIndex = 0; + } +} + +void FFTConvolver::reset() +{ + m_lastOverlapBuffer.zero(); + m_readWriteIndex = 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/FFTConvolver.h b/Source/WebCore/platform/audio/FFTConvolver.h new file mode 100644 index 0000000..c1b5002 --- /dev/null +++ b/Source/WebCore/platform/audio/FFTConvolver.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 FFTConvolver_h +#define FFTConvolver_h + +#include "AudioArray.h" +#include "FFTFrame.h" + +namespace WebCore { + +class FFTConvolver { +public: + // fftSize must be a power of two + FFTConvolver(size_t fftSize); + + // For now, with multiple calls to Process(), framesToProcess MUST add up EXACTLY to fftSize / 2 + // + // FIXME: Later, we can do more sophisticated buffering to relax this requirement... + // + // The input to output latency is equal to fftSize / 2 + // + // Processing in-place is allowed... + void process(FFTFrame* fftKernel, float* sourceP, float* destP, size_t framesToProcess); + + void reset(); + + size_t fftSize() const { return m_frame.fftSize(); } + +private: + FFTFrame m_frame; + + // Buffer input until we get fftSize / 2 samples then do an FFT + size_t m_readWriteIndex; + AudioFloatArray m_inputBuffer; + + // Stores output which we read a little at a time + AudioFloatArray m_outputBuffer; + + // Saves the 2nd half of the FFT buffer, so we can do an overlap-add with the 1st half of the next one + AudioFloatArray m_lastOverlapBuffer; +}; + +} // namespace WebCore + +#endif // FFTConvolver_h diff --git a/Source/WebCore/platform/audio/FFTFrame.cpp b/Source/WebCore/platform/audio/FFTFrame.cpp new file mode 100644 index 0000000..d9979d9 --- /dev/null +++ b/Source/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 > piDouble) + deltaPhase1 -= 2.0 * piDouble; + if (deltaPhase1 < -piDouble) + deltaPhase1 += 2.0 * piDouble; + if (deltaPhase2 > piDouble) + deltaPhase2 -= 2.0 * piDouble; + if (deltaPhase2 < -piDouble) + deltaPhase2 += 2.0 * piDouble; + + // Blend group-delays + double deltaPhaseBlend; + + if (deltaPhase1 - deltaPhase2 > piDouble) + deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * piDouble + deltaPhase2); + else if (deltaPhase2 - deltaPhase1 > piDouble) + deltaPhaseBlend = s1 * (2.0 * piDouble + deltaPhase1) + s2 * deltaPhase2; + else + deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2; + + phaseAccum += deltaPhaseBlend; + + // Unwrap + if (phaseAccum > piDouble) + phaseAccum -= 2.0 * piDouble; + if (phaseAccum < -piDouble) + phaseAccum += 2.0 * piDouble; + + 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 * piDouble) / 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 < -piDouble) + deltaPhase += 2.0 * piDouble; + if (deltaPhase > piDouble) + deltaPhase -= 2.0 * piDouble; + + 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 * piDouble) / 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/Source/WebCore/platform/audio/FFTFrame.h b/Source/WebCore/platform/audio/FFTFrame.h new file mode 100644 index 0000000..1a82ef0 --- /dev/null +++ b/Source/WebCore/platform/audio/FFTFrame.h @@ -0,0 +1,123 @@ +/* + * 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 + +#if !OS(DARWIN) && USE(WEBAUDIO_MKL) +#include "mkl_dfti.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) +#if !OS(DARWIN) && USE(WEBAUDIO_MKL) + // Interleaves the planar real and imaginary data and returns a + // pointer to the resulting storage which can be used for in-place + // or out-of-place operations. FIXME: ideally all of the MKL + // routines would operate on planar data and this method would be + // removed. + float* getUpToDateComplexData(); + + static DFTI_DESCRIPTOR_HANDLE descriptorHandleForSize(unsigned fftSize); + + static DFTI_DESCRIPTOR_HANDLE* descriptorHandles; + + DFTI_DESCRIPTOR_HANDLE m_handle; + AudioFloatArray m_complexData; + AudioFloatArray m_realData; + AudioFloatArray m_imagData; +#endif // !OS(DARWIN) && USE(WEBAUDIO_MKL) +}; + +} // namespace WebCore + +#endif // FFTFrame_h diff --git a/Source/WebCore/platform/audio/HRTFDatabase.cpp b/Source/WebCore/platform/audio/HRTFDatabase.cpp new file mode 100644 index 0000000..ef1229f --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFDatabase.cpp @@ -0,0 +1,124 @@ +/* + * 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 "HRTFDatabase.h" + +#include "HRTFElevation.h" + +using namespace std; + +namespace WebCore { + +const int HRTFDatabase::MinElevation = -45; +const int HRTFDatabase::MaxElevation = 90; +const unsigned HRTFDatabase::RawElevationAngleSpacing = 15; +const unsigned HRTFDatabase::NumberOfRawElevations = 10; // -45 -> +90 (each 15 degrees) +const unsigned HRTFDatabase::InterpolationFactor = 1; +const unsigned HRTFDatabase::NumberOfTotalElevations = NumberOfRawElevations * InterpolationFactor; + +PassOwnPtr<HRTFDatabase> HRTFDatabase::create(double sampleRate) +{ + OwnPtr<HRTFDatabase> hrtfDatabase = adoptPtr(new HRTFDatabase(sampleRate)); + return hrtfDatabase.release(); +} + +HRTFDatabase::HRTFDatabase(double sampleRate) + : m_elevations(NumberOfTotalElevations) + , m_sampleRate(sampleRate) +{ + unsigned elevationIndex = 0; + for (int elevation = MinElevation; elevation <= MaxElevation; elevation += RawElevationAngleSpacing) { + OwnPtr<HRTFElevation> hrtfElevation = HRTFElevation::createForSubject("Composite", elevation, sampleRate); + ASSERT(hrtfElevation.get()); + if (!hrtfElevation.get()) + return; + + m_elevations[elevationIndex] = hrtfElevation.release(); + elevationIndex += InterpolationFactor; + } + + // Now, go back and interpolate elevations. + if (InterpolationFactor > 1) { + for (unsigned i = 0; i < NumberOfTotalElevations; i += InterpolationFactor) { + unsigned j = (i + InterpolationFactor); + if (j >= NumberOfTotalElevations) + j = i; // for last elevation interpolate with itself + + // Create the interpolated convolution kernels and delays. + for (unsigned jj = 1; jj < InterpolationFactor; ++jj) { + double x = static_cast<double>(jj) / static_cast<double>(InterpolationFactor); + m_elevations[i + jj] = HRTFElevation::createByInterpolatingSlices(m_elevations[i].get(), m_elevations[j].get(), x, sampleRate); + ASSERT(m_elevations[i + jj].get()); + } + } + } +} + +void HRTFDatabase::getKernelsFromAzimuthElevation(double azimuthBlend, unsigned azimuthIndex, double elevationAngle, HRTFKernel* &kernelL, HRTFKernel* &kernelR, + double& frameDelayL, double& frameDelayR) +{ + unsigned elevationIndex = indexFromElevationAngle(elevationAngle); + ASSERT(elevationIndex < m_elevations.size() && m_elevations.size() > 0); + + if (!m_elevations.size()) { + kernelL = 0; + kernelR = 0; + return; + } + + if (elevationIndex > m_elevations.size() - 1) + elevationIndex = m_elevations.size() - 1; + + HRTFElevation* hrtfElevation = m_elevations[elevationIndex].get(); + ASSERT(hrtfElevation); + if (!hrtfElevation) { + kernelL = 0; + kernelR = 0; + return; + } + + hrtfElevation->getKernelsFromAzimuth(azimuthBlend, azimuthIndex, kernelL, kernelR, frameDelayL, frameDelayR); +} + +unsigned HRTFDatabase::indexFromElevationAngle(double elevationAngle) +{ + // Clamp to allowed range. + elevationAngle = max(static_cast<double>(MinElevation), elevationAngle); + elevationAngle = min(static_cast<double>(MaxElevation), elevationAngle); + + unsigned elevationIndex = static_cast<int>(InterpolationFactor * (elevationAngle - MinElevation) / RawElevationAngleSpacing); + return elevationIndex; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/HRTFDatabase.h b/Source/WebCore/platform/audio/HRTFDatabase.h new file mode 100644 index 0000000..c33b38f --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFDatabase.h @@ -0,0 +1,87 @@ +/* + * 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 HRTFDatabase_h +#define HRTFDatabase_h + +#include "HRTFElevation.h" +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class HRTFKernel; + +class HRTFDatabase : public Noncopyable { +public: + static PassOwnPtr<HRTFDatabase> create(double sampleRate); + + // getKernelsFromAzimuthElevation() returns a left and right ear kernel, and an interpolated left and right frame delay for the given azimuth and elevation. + // azimuthBlend must be in the range 0 -> 1. + // Valid values for azimuthIndex are 0 -> HRTFElevation::NumberOfTotalAzimuths - 1 (corresponding to angles of 0 -> 360). + // Valid values for elevationAngle are MinElevation -> MaxElevation. + void getKernelsFromAzimuthElevation(double azimuthBlend, unsigned azimuthIndex, double elevationAngle, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR); + + // Returns the number of different azimuth angles. + static unsigned numberOfAzimuths() { return HRTFElevation::NumberOfTotalAzimuths; } + + double sampleRate() const { return m_sampleRate; } + +private: + explicit HRTFDatabase(double sampleRate); + + // Minimum and maximum elevation angles (inclusive) for a HRTFDatabase. + static const int MinElevation; + static const int MaxElevation; + static const unsigned RawElevationAngleSpacing; + + // Number of elevations loaded from resource. + static const unsigned NumberOfRawElevations; + + // Interpolates by this factor to get the total number of elevations from every elevation loaded from resource. + static const unsigned InterpolationFactor; + + // Total number of elevations after interpolation. + static const unsigned NumberOfTotalElevations; + + // Returns the index for the correct HRTFElevation given the elevation angle. + static unsigned indexFromElevationAngle(double); + + Vector<OwnPtr<HRTFElevation> > m_elevations; + double m_sampleRate; +}; + +} // namespace WebCore + +#endif // HRTFDatabase_h diff --git a/Source/WebCore/platform/audio/HRTFDatabaseLoader.cpp b/Source/WebCore/platform/audio/HRTFDatabaseLoader.cpp new file mode 100644 index 0000000..4368d22 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFDatabaseLoader.cpp @@ -0,0 +1,133 @@ +/* + * 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 "HRTFDatabaseLoader.h" + +#include "HRTFDatabase.h" + +namespace WebCore { + +// Singleton +HRTFDatabaseLoader* HRTFDatabaseLoader::s_loader = 0; + +PassRefPtr<HRTFDatabaseLoader> HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(double sampleRate) +{ + ASSERT(isMainThread()); + + RefPtr<HRTFDatabaseLoader> loader; + + if (!s_loader) { + // Lazily create and load. + loader = adoptRef(new HRTFDatabaseLoader(sampleRate)); + s_loader = loader.get(); + loader->loadAsynchronously(); + } else { + loader = s_loader; + ASSERT(sampleRate == loader->databaseSampleRate()); + } + + return loader; +} + +HRTFDatabaseLoader::HRTFDatabaseLoader(double sampleRate) + : m_hrtfDatabase(0) + , m_databaseLoaderThread(0) + , m_startedLoadingDatabase(false) + , m_databaseSampleRate(sampleRate) +{ + ASSERT(isMainThread()); +} + +HRTFDatabaseLoader::~HRTFDatabaseLoader() +{ + ASSERT(isMainThread()); + + if (m_startedLoadingDatabase) + waitForThreadCompletion(m_databaseLoaderThread, 0); + + m_startedLoadingDatabase = false; + m_databaseLoaderThread = 0; + + m_hrtfDatabase.clear(); + + // Clear out singleton. + ASSERT(this == s_loader); + s_loader = 0; +} + + +// Asynchronously load the database in this thread. +static void* databaseLoaderEntry(void* threadData) +{ + HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData); + ASSERT(loader); + loader->load(); + + return 0; +} + +void HRTFDatabaseLoader::load() +{ + ASSERT(!isMainThread()); + if (!m_hrtfDatabase.get()) { + // Load the default HRTF database. + m_hrtfDatabase = HRTFDatabase::create(m_databaseSampleRate); + } +} + +void HRTFDatabaseLoader::loadAsynchronously() +{ + ASSERT(isMainThread()); + + if (!m_hrtfDatabase.get() && !m_startedLoadingDatabase) { + // Start the asynchronous database loading process. + m_startedLoadingDatabase = true; + m_databaseLoaderThread = createThread(databaseLoaderEntry, this, "HRTF database loader"); + } +} + +bool HRTFDatabaseLoader::isLoaded() const +{ + return m_hrtfDatabase.get(); +} + +HRTFDatabase* HRTFDatabaseLoader::defaultHRTFDatabase() +{ + if (!s_loader) + return 0; + + return s_loader->database(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/HRTFDatabaseLoader.h b/Source/WebCore/platform/audio/HRTFDatabaseLoader.h new file mode 100644 index 0000000..72002c5 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFDatabaseLoader.h @@ -0,0 +1,85 @@ +/* + * 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 HRTFDatabaseLoader_h +#define HRTFDatabaseLoader_h + +#include "HRTFDatabase.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + +// HRTFDatabaseLoader will asynchronously load the default HRTFDatabase in a new thread. + +class HRTFDatabaseLoader : public RefCounted<HRTFDatabaseLoader> { +public: + // Lazily creates the singleton HRTFDatabaseLoader (if not already created) and starts loading asynchronously (when created the first time). + // Returns the singleton HRTFDatabaseLoader. + // Must be called from the main thread. + static PassRefPtr<HRTFDatabaseLoader> createAndLoadAsynchronouslyIfNecessary(double sampleRate); + + // Both constructor and destructor must be called from the main thread. + ~HRTFDatabaseLoader(); + + // Returns true once the default database has been completely loaded. + bool isLoaded() const; + + HRTFDatabase* database() { return m_hrtfDatabase.get(); } + + // Called in asynchronous loading thread. + void load(); + + // defaultHRTFDatabase() gives access to the loaded database. + // This can be called from any thread, but it is the callers responsibilty to call this while the context (and thus HRTFDatabaseLoader) + // is still alive. Otherwise this will return 0. + static HRTFDatabase* defaultHRTFDatabase(); + +private: + // Both constructor and destructor must be called from the main thread. + explicit HRTFDatabaseLoader(double sampleRate); + + // If it hasn't already been loaded, creates a new thread and initiates asynchronous loading of the default database. + // This must be called from the main thread. + void loadAsynchronously(); + + double databaseSampleRate() const { return m_databaseSampleRate; } + + static HRTFDatabaseLoader* s_loader; // singleton + OwnPtr<HRTFDatabase> m_hrtfDatabase; + ThreadIdentifier m_databaseLoaderThread; + bool m_startedLoadingDatabase; + double m_databaseSampleRate; +}; + + +} // namespace WebCore + +#endif // HRTFDatabaseLoader_h diff --git a/Source/WebCore/platform/audio/HRTFElevation.cpp b/Source/WebCore/platform/audio/HRTFElevation.cpp new file mode 100644 index 0000000..06d03ea --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFElevation.cpp @@ -0,0 +1,266 @@ +/* + * 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 "HRTFElevation.h" + +#include "AudioBus.h" +#include "AudioFileReader.h" +#include "Biquad.h" +#include "FFTFrame.h" +#include "HRTFPanner.h" +#include <algorithm> +#include <math.h> +#include <wtf/OwnPtr.h> + +using namespace std; + +namespace WebCore { + +const unsigned HRTFElevation::AzimuthSpacing = 15; +const unsigned HRTFElevation::NumberOfRawAzimuths = 360 / AzimuthSpacing; +const unsigned HRTFElevation::InterpolationFactor = 8; +const unsigned HRTFElevation::NumberOfTotalAzimuths = NumberOfRawAzimuths * InterpolationFactor; + +// Takes advantage of the symmetry and creates a composite version of the two measured versions. For example, we have both azimuth 30 and -30 degrees +// where the roles of left and right ears are reversed with respect to each other. +bool HRTFElevation::calculateSymmetricKernelsForAzimuthElevation(int azimuth, int elevation, double sampleRate, const String& subjectName, + RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR) +{ + RefPtr<HRTFKernel> kernelL1; + RefPtr<HRTFKernel> kernelR1; + bool success = calculateKernelsForAzimuthElevation(azimuth, elevation, sampleRate, subjectName, kernelL1, kernelR1); + if (!success) + return false; + + // And symmetric version + int symmetricAzimuth = !azimuth ? 0 : 360 - azimuth; + + RefPtr<HRTFKernel> kernelL2; + RefPtr<HRTFKernel> kernelR2; + success = calculateKernelsForAzimuthElevation(symmetricAzimuth, elevation, sampleRate, subjectName, kernelL2, kernelR2); + if (!success) + return false; + + // Notice L/R reversal in symmetric version. + kernelL = HRTFKernel::createInterpolatedKernel(kernelL1.get(), kernelR2.get(), 0.5); + kernelR = HRTFKernel::createInterpolatedKernel(kernelR1.get(), kernelL2.get(), 0.5); + + return true; +} + +bool HRTFElevation::calculateKernelsForAzimuthElevation(int azimuth, int elevation, double sampleRate, const String& subjectName, + RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR) +{ + // Valid values for azimuth are 0 -> 345 in 15 degree increments. + // Valid values for elevation are -45 -> +90 in 15 degree increments. + + bool isAzimuthGood = azimuth >= 0 && azimuth <= 345 && (azimuth / 15) * 15 == azimuth; + ASSERT(isAzimuthGood); + if (!isAzimuthGood) + return false; + + bool isElevationGood = elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation; + ASSERT(isElevationGood); + if (!isElevationGood) + return false; + + // Construct the resource name from the subject name, azimuth, and elevation, for example: + // "IRC_Composite_C_R0195_T015_P000" + // Note: the passed in subjectName is not a string passed in via JavaScript or the web. + // It's passed in as an internal ASCII identifier and is an implementation detail. + int positiveElevation = elevation < 0 ? elevation + 360 : elevation; + String resourceName = String::format("IRC_%s_C_R0195_T%03d_P%03d", subjectName.utf8().data(), azimuth, positiveElevation); + + OwnPtr<AudioBus> impulseResponse(AudioBus::loadPlatformResource(resourceName.utf8().data(), sampleRate)); + + ASSERT(impulseResponse.get()); + if (!impulseResponse.get()) + return false; + + size_t responseLength = impulseResponse->length(); + size_t expectedLength = static_cast<size_t>(256 * (sampleRate / 44100.0)); + + // Check number of channels and length. For now these are fixed and known. + bool isBusGood = responseLength == expectedLength && impulseResponse->numberOfChannels() == 2; + ASSERT(isBusGood); + if (!isBusGood) + return false; + + AudioChannel* leftEarImpulseResponse = impulseResponse->channelByType(AudioBus::ChannelLeft); + AudioChannel* rightEarImpulseResponse = impulseResponse->channelByType(AudioBus::ChannelRight); + + // Note that depending on the fftSize returned by the panner, we may be truncating the impulse response we just loaded in. + const size_t fftSize = HRTFPanner::fftSizeForSampleRate(sampleRate); + kernelL = HRTFKernel::create(leftEarImpulseResponse, fftSize, sampleRate, true); + kernelR = HRTFKernel::create(rightEarImpulseResponse, fftSize, sampleRate, true); + + return true; +} + +// The range of elevations for the IRCAM impulse responses varies depending on azimuth, but the minimum elevation appears to always be -45. +// +// Here's how it goes: +static int maxElevations[] = { + // Azimuth + // + 90, // 0 + 45, // 15 + 60, // 30 + 45, // 45 + 75, // 60 + 45, // 75 + 60, // 90 + 45, // 105 + 75, // 120 + 45, // 135 + 60, // 150 + 45, // 165 + 75, // 180 + 45, // 195 + 60, // 210 + 45, // 225 + 75, // 240 + 45, // 255 + 60, // 270 + 45, // 285 + 75, // 300 + 45, // 315 + 60, // 330 + 45 // 345 +}; + +PassOwnPtr<HRTFElevation> HRTFElevation::createForSubject(const String& subjectName, int elevation, double sampleRate) +{ + bool isElevationGood = elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation; + ASSERT(isElevationGood); + if (!isElevationGood) + return 0; + + OwnPtr<HRTFKernelList> kernelListL = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths)); + OwnPtr<HRTFKernelList> kernelListR = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths)); + + // Load convolution kernels from HRTF files. + int interpolatedIndex = 0; + for (unsigned rawIndex = 0; rawIndex < NumberOfRawAzimuths; ++rawIndex) { + // Don't let elevation exceed maximum for this azimuth. + int maxElevation = maxElevations[rawIndex]; + int actualElevation = min(elevation, maxElevation); + + bool success = calculateKernelsForAzimuthElevation(rawIndex * AzimuthSpacing, actualElevation, sampleRate, subjectName, kernelListL->at(interpolatedIndex), kernelListR->at(interpolatedIndex)); + if (!success) + return 0; + + interpolatedIndex += InterpolationFactor; + } + + // Now go back and interpolate intermediate azimuth values. + for (unsigned i = 0; i < NumberOfTotalAzimuths; i += InterpolationFactor) { + int j = (i + InterpolationFactor) % NumberOfTotalAzimuths; + + // Create the interpolated convolution kernels and delays. + for (unsigned jj = 1; jj < InterpolationFactor; ++jj) { + double x = double(jj) / double(InterpolationFactor); // interpolate from 0 -> 1 + + (*kernelListL)[i + jj] = HRTFKernel::createInterpolatedKernel(kernelListL->at(i).get(), kernelListL->at(j).get(), x); + (*kernelListR)[i + jj] = HRTFKernel::createInterpolatedKernel(kernelListR->at(i).get(), kernelListR->at(j).get(), x); + } + } + + OwnPtr<HRTFElevation> hrtfElevation = adoptPtr(new HRTFElevation(kernelListL.release(), kernelListR.release(), elevation, sampleRate)); + return hrtfElevation.release(); +} + +PassOwnPtr<HRTFElevation> HRTFElevation::createByInterpolatingSlices(HRTFElevation* hrtfElevation1, HRTFElevation* hrtfElevation2, double x, double sampleRate) +{ + ASSERT(hrtfElevation1 && hrtfElevation2); + if (!hrtfElevation1 || !hrtfElevation2) + return 0; + + ASSERT(x >= 0.0 && x < 1.0); + + OwnPtr<HRTFKernelList> kernelListL = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths)); + OwnPtr<HRTFKernelList> kernelListR = adoptPtr(new HRTFKernelList(NumberOfTotalAzimuths)); + + HRTFKernelList* kernelListL1 = hrtfElevation1->kernelListL(); + HRTFKernelList* kernelListR1 = hrtfElevation1->kernelListR(); + HRTFKernelList* kernelListL2 = hrtfElevation2->kernelListL(); + HRTFKernelList* kernelListR2 = hrtfElevation2->kernelListR(); + + // Interpolate kernels of corresponding azimuths of the two elevations. + for (unsigned i = 0; i < NumberOfTotalAzimuths; ++i) { + (*kernelListL)[i] = HRTFKernel::createInterpolatedKernel(kernelListL1->at(i).get(), kernelListL2->at(i).get(), x); + (*kernelListR)[i] = HRTFKernel::createInterpolatedKernel(kernelListR1->at(i).get(), kernelListR2->at(i).get(), x); + } + + // Interpolate elevation angle. + double angle = (1.0 - x) * hrtfElevation1->elevationAngle() + x * hrtfElevation2->elevationAngle(); + + OwnPtr<HRTFElevation> hrtfElevation = adoptPtr(new HRTFElevation(kernelListL.release(), kernelListR.release(), static_cast<int>(angle), sampleRate)); + return hrtfElevation.release(); +} + +void HRTFElevation::getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR) +{ + bool checkAzimuthBlend = azimuthBlend >= 0.0 && azimuthBlend < 1.0; + ASSERT(checkAzimuthBlend); + if (!checkAzimuthBlend) + azimuthBlend = 0.0; + + unsigned numKernels = m_kernelListL->size(); + + bool isIndexGood = azimuthIndex < numKernels; + ASSERT(isIndexGood); + if (!isIndexGood) { + kernelL = 0; + kernelR = 0; + return; + } + + // Return the left and right kernels. + kernelL = m_kernelListL->at(azimuthIndex).get(); + kernelR = m_kernelListR->at(azimuthIndex).get(); + + frameDelayL = m_kernelListL->at(azimuthIndex)->frameDelay(); + frameDelayR = m_kernelListR->at(azimuthIndex)->frameDelay(); + + int azimuthIndex2 = (azimuthIndex + 1) % numKernels; + double frameDelay2L = m_kernelListL->at(azimuthIndex2)->frameDelay(); + double frameDelay2R = m_kernelListR->at(azimuthIndex2)->frameDelay(); + + // Linearly interpolate delays. + frameDelayL = (1.0 - azimuthBlend) * frameDelayL + azimuthBlend * frameDelay2L; + frameDelayR = (1.0 - azimuthBlend) * frameDelayR + azimuthBlend * frameDelay2R; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/HRTFElevation.h b/Source/WebCore/platform/audio/HRTFElevation.h new file mode 100644 index 0000000..b388b34 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFElevation.h @@ -0,0 +1,110 @@ +/* + * 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 HRTFElevation_h +#define HRTFElevation_h + +#include "HRTFKernel.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +// HRTFElevation contains all of the HRTFKernels (one left ear and one right ear per azimuth angle) for a particular elevation. + +class HRTFElevation : public Noncopyable { +public: + // Loads and returns an HRTFElevation with the given HRTF database subject name and elevation from browser (or WebKit.framework) resources. + // Normally, there will only be a single HRTF database set, but this API supports the possibility of multiple ones with different names. + // Interpolated azimuths will be generated based on InterpolationFactor. + // Valid values for elevation are -45 -> +90 in 15 degree increments. + static PassOwnPtr<HRTFElevation> createForSubject(const String& subjectName, int elevation, double sampleRate); + + // Given two HRTFElevations, and an interpolation factor x: 0 -> 1, returns an interpolated HRTFElevation. + static PassOwnPtr<HRTFElevation> createByInterpolatingSlices(HRTFElevation* hrtfElevation1, HRTFElevation* hrtfElevation2, double x, double sampleRate); + + // Returns the list of left or right ear HRTFKernels for all the azimuths going from 0 to 360 degrees. + HRTFKernelList* kernelListL() { return m_kernelListL.get(); } + HRTFKernelList* kernelListR() { return m_kernelListR.get(); } + + double elevationAngle() const { return m_elevationAngle; } + unsigned numberOfAzimuths() { return NumberOfTotalAzimuths; } + double sampleRate() const { return m_sampleRate; } + + // Returns the left and right kernels for the given azimuth index. + // The interpolated delays based on azimuthBlend: 0 -> 1 are returned in frameDelayL and frameDelayR. + void getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR); + + // Spacing, in degrees, between every azimuth loaded from resource. + static const unsigned AzimuthSpacing; + + // Number of azimuths loaded from resource. + static const unsigned NumberOfRawAzimuths; + + // Interpolates by this factor to get the total number of azimuths from every azimuth loaded from resource. + static const unsigned InterpolationFactor; + + // Total number of azimuths after interpolation. + static const unsigned NumberOfTotalAzimuths; + + // Given a specific azimuth and elevation angle, returns the left and right HRTFKernel. + // Valid values for azimuth are 0 -> 345 in 15 degree increments. + // Valid values for elevation are -45 -> +90 in 15 degree increments. + // Returns true on success. + static bool calculateKernelsForAzimuthElevation(int azimuth, int elevation, double sampleRate, const String& subjectName, + RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR); + + // Given a specific azimuth and elevation angle, returns the left and right HRTFKernel in kernelL and kernelR. + // This method averages the measured response using symmetry of azimuth (for example by averaging the -30.0 and +30.0 azimuth responses). + // Returns true on success. + static bool calculateSymmetricKernelsForAzimuthElevation(int azimuth, int elevation, double sampleRate, const String& subjectName, + RefPtr<HRTFKernel>& kernelL, RefPtr<HRTFKernel>& kernelR); +private: + HRTFElevation(PassOwnPtr<HRTFKernelList> kernelListL, PassOwnPtr<HRTFKernelList> kernelListR, int elevation, double sampleRate) + : m_kernelListL(kernelListL) + , m_kernelListR(kernelListR) + , m_elevationAngle(elevation) + , m_sampleRate(sampleRate) + { + } + + OwnPtr<HRTFKernelList> m_kernelListL; + OwnPtr<HRTFKernelList> m_kernelListR; + double m_elevationAngle; + double m_sampleRate; +}; + +} // namespace WebCore + +#endif // HRTFElevation_h diff --git a/Source/WebCore/platform/audio/HRTFKernel.cpp b/Source/WebCore/platform/audio/HRTFKernel.cpp new file mode 100644 index 0000000..22d4b12 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFKernel.cpp @@ -0,0 +1,141 @@ +/* + * 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 "HRTFKernel.h" + +#include "AudioChannel.h" +#include "Biquad.h" +#include "FFTFrame.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +// Takes the input AudioChannel as an input impulse response and calculates the average group delay. +// This represents the initial delay before the most energetic part of the impulse response. +// The sample-frame delay is removed from the impulseP impulse response, and this value is returned. +// the length of the passed in AudioChannel must be a power of 2. +static double extractAverageGroupDelay(AudioChannel* channel) +{ + ASSERT(channel); + + float* impulseP = channel->data(); + size_t length = channel->length(); + + // Check that length is power-of-2; + ASSERT(1UL << static_cast<unsigned>(log2(length)) == length); + + FFTFrame estimationFrame(length); + estimationFrame.doFFT(impulseP); + + double frameDelay = estimationFrame.extractAverageGroupDelay(); + estimationFrame.doInverseFFT(impulseP); + + return frameDelay; +} + +HRTFKernel::HRTFKernel(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost) + : m_frameDelay(0.0) + , m_sampleRate(sampleRate) +{ + ASSERT(channel); + + // Determine the leading delay (average group delay) for the response. + m_frameDelay = extractAverageGroupDelay(channel); + + float* impulseResponse = channel->data(); + size_t responseLength = channel->length(); + + if (bassBoost) { + // Run through some post-processing to boost the bass a little -- the HRTF's seem to be a little bass-deficient. + // FIXME: this post-processing should have already been applied to the HRTF file resources. Once the files are put into this form, + // then this code path can be removed along with the bassBoost parameter. + Biquad filter; + filter.setLowShelfParams(700.0 / nyquist(), 6.0); // boost 6dB at 700Hz + filter.process(impulseResponse, impulseResponse, responseLength); + } + + // We need to truncate to fit into 1/2 the FFT size (with zero padding) in order to do proper convolution. + size_t truncatedResponseLength = min(responseLength, fftSize / 2); // truncate if necessary to max impulse response length allowed by FFT + + // Quick fade-out (apply window) at truncation point + unsigned numberOfFadeOutFrames = static_cast<unsigned>(sampleRate / 4410); // 10 sample-frames @44.1KHz sample-rate + ASSERT(numberOfFadeOutFrames < truncatedResponseLength); + if (numberOfFadeOutFrames < truncatedResponseLength) { + for (unsigned i = truncatedResponseLength - numberOfFadeOutFrames; i < truncatedResponseLength; ++i) { + float x = 1.0f - static_cast<float>(i - (truncatedResponseLength - numberOfFadeOutFrames)) / numberOfFadeOutFrames; + impulseResponse[i] *= x; + } + } + + m_fftFrame = adoptPtr(new FFTFrame(fftSize)); + m_fftFrame->doPaddedFFT(impulseResponse, truncatedResponseLength); +} + +PassOwnPtr<AudioChannel> HRTFKernel::createImpulseResponse() +{ + OwnPtr<AudioChannel> channel = adoptPtr(new AudioChannel(fftSize())); + FFTFrame fftFrame(*m_fftFrame); + + // Add leading delay back in. + fftFrame.addConstantGroupDelay(m_frameDelay); + fftFrame.doInverseFFT(channel->data()); + + return channel.release(); +} + +// Interpolates two kernels with x: 0 -> 1 and returns the result. +PassRefPtr<HRTFKernel> HRTFKernel::createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, double x) +{ + ASSERT(kernel1 && kernel2); + if (!kernel1 || !kernel2) + return 0; + + ASSERT(x >= 0.0 && x < 1.0); + x = min(1.0, max(0.0, x)); + + double sampleRate1 = kernel1->sampleRate(); + double sampleRate2 = kernel2->sampleRate(); + ASSERT(sampleRate1 == sampleRate2); + if (sampleRate1 != sampleRate2) + return 0; + + double frameDelay = (1.0 - x) * kernel1->frameDelay() + x * kernel2->frameDelay(); + + OwnPtr<FFTFrame> interpolatedFrame = FFTFrame::createInterpolatedFrame(*kernel1->fftFrame(), *kernel2->fftFrame(), x); + return HRTFKernel::create(interpolatedFrame.release(), frameDelay, sampleRate1); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/HRTFKernel.h b/Source/WebCore/platform/audio/HRTFKernel.h new file mode 100644 index 0000000..572a085 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFKernel.h @@ -0,0 +1,98 @@ +/* + * 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 HRTFKernel_h +#define HRTFKernel_h + +#include "FFTFrame.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioChannel; + +// HRTF stands for Head-Related Transfer Function. +// HRTFKernel is a frequency-domain representation of an impulse-response used as part of the spatialized panning system. +// For a given azimuth / elevation angle there will be one HRTFKernel for the left ear transfer function, and one for the right ear. +// The leading delay (average group delay) for each impulse response is extracted: +// m_fftFrame is the frequency-domain representation of the impulse response with the delay removed +// m_frameDelay is the leading delay of the original impulse response. +class HRTFKernel : public RefCounted<HRTFKernel> { +public: + // Note: this is destructive on the passed in AudioChannel. + // The length of channel must be a power of two. + static PassRefPtr<HRTFKernel> create(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost) + { + return adoptRef(new HRTFKernel(channel, fftSize, sampleRate, bassBoost)); + } + + static PassRefPtr<HRTFKernel> create(PassOwnPtr<FFTFrame> fftFrame, double frameDelay, double sampleRate) + { + return adoptRef(new HRTFKernel(fftFrame, frameDelay, sampleRate)); + } + + // Given two HRTFKernels, and an interpolation factor x: 0 -> 1, returns an interpolated HRTFKernel. + static PassRefPtr<HRTFKernel> createInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, double x); + + FFTFrame* fftFrame() { return m_fftFrame.get(); } + + size_t fftSize() const { return m_fftFrame->fftSize(); } + double frameDelay() const { return m_frameDelay; } + + double sampleRate() const { return m_sampleRate; } + double nyquist() const { return 0.5 * sampleRate(); } + + // Converts back into impulse-response form. + PassOwnPtr<AudioChannel> createImpulseResponse(); + +private: + // Note: this is destructive on the passed in AudioChannel. + HRTFKernel(AudioChannel* channel, size_t fftSize, double sampleRate, bool bassBoost); + + HRTFKernel(PassOwnPtr<FFTFrame> fftFrame, double frameDelay, double sampleRate) + : m_fftFrame(fftFrame) + , m_frameDelay(frameDelay) + , m_sampleRate(sampleRate) + { + } + + OwnPtr<FFTFrame> m_fftFrame; + double m_frameDelay; + double m_sampleRate; +}; + +typedef Vector<RefPtr<HRTFKernel> > HRTFKernelList; + +} // namespace WebCore + +#endif // HRTFKernel_h diff --git a/Source/WebCore/platform/audio/HRTFPanner.cpp b/Source/WebCore/platform/audio/HRTFPanner.cpp new file mode 100644 index 0000000..68bc505 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFPanner.cpp @@ -0,0 +1,229 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "HRTFPanner.h" + +#include "AudioBus.h" +#include "FFTConvolver.h" +#include "HRTFDatabase.h" +#include "HRTFDatabaseLoader.h" +#include <algorithm> +#include <wtf/MathExtras.h> +#include <wtf/RefPtr.h> + +using namespace std; + +namespace WebCore { + +// The value of 2 milliseconds is larger than the largest delay which exists in any HRTFKernel from the default HRTFDatabase (0.0136 seconds). +// We ASSERT the delay values used in process() with this value. +const double MaxDelayTimeSeconds = 0.002; + +HRTFPanner::HRTFPanner(double sampleRate) + : Panner(PanningModelHRTF) + , m_sampleRate(sampleRate) + , m_isFirstRender(true) + , m_azimuthIndex(0) + , m_convolverL(fftSizeForSampleRate(sampleRate)) + , m_convolverR(fftSizeForSampleRate(sampleRate)) + , m_delayLineL(MaxDelayTimeSeconds, sampleRate) + , m_delayLineR(MaxDelayTimeSeconds, sampleRate) +{ +} + +HRTFPanner::~HRTFPanner() +{ +} + +size_t HRTFPanner::fftSizeForSampleRate(double sampleRate) +{ + // The HRTF impulse responses (loaded as audio resources) are 512 sample-frames @44.1KHz. + // Currently, we truncate the impulse responses to half this size, but an FFT-size of twice impulse response size is needed (for convolution). + // So for sample rates around 44.1KHz an FFT size of 512 is good. We double that size for higher sample rates. + ASSERT(sampleRate >= 44100 && sampleRate <= 96000.0); + return (sampleRate <= 48000.0) ? 512 : 1024; +} + +void HRTFPanner::reset() +{ + m_isFirstRender = true; + m_convolverL.reset(); + m_convolverR.reset(); + m_delayLineL.reset(); + m_delayLineR.reset(); +} + +static bool wrapDistance(int i, int j, int length) +{ + int directDistance = abs(i - j); + int indirectDistance = length - directDistance; + + return indirectDistance < directDistance; +} + +int HRTFPanner::calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend) +{ + // Convert the azimuth angle from the range -180 -> +180 into the range 0 -> 360. + // The azimuth index may then be calculated from this positive value. + if (azimuth < 0) + azimuth += 360.0; + + HRTFDatabase* database = HRTFDatabaseLoader::defaultHRTFDatabase(); + ASSERT(database); + + int numberOfAzimuths = database->numberOfAzimuths(); + const double angleBetweenAzimuths = 360.0 / numberOfAzimuths; + + // Calculate the azimuth index and the blend (0 -> 1) for interpolation. + double desiredAzimuthIndexFloat = azimuth / angleBetweenAzimuths; + int desiredAzimuthIndex = static_cast<int>(desiredAzimuthIndexFloat); + azimuthBlend = desiredAzimuthIndexFloat - static_cast<double>(desiredAzimuthIndex); + + // We don't immediately start using this azimuth index, but instead approach this index from the last index we rendered at. + // This minimizes the clicks and graininess for moving sources which occur otherwise. + desiredAzimuthIndex = max(0, desiredAzimuthIndex); + desiredAzimuthIndex = min(numberOfAzimuths - 1, desiredAzimuthIndex); + return desiredAzimuthIndex; +} + +void HRTFPanner::pan(double desiredAzimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) +{ + unsigned numInputChannels = inputBus ? inputBus->numberOfChannels() : 0; + + bool isInputGood = inputBus && numInputChannels >= 1 && numInputChannels <= 2; + ASSERT(isInputGood); + + bool isOutputGood = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length(); + ASSERT(isOutputGood); + + if (!isInputGood || !isOutputGood) { + if (outputBus) + outputBus->zero(); + return; + } + + // This code only runs as long as the context is alive and after database has been loaded. + HRTFDatabase* database = HRTFDatabaseLoader::defaultHRTFDatabase(); + ASSERT(database); + if (!database) { + outputBus->zero(); + return; + } + + // IRCAM HRTF azimuths values from the loaded database is reversed from the panner's notion of azimuth. + double azimuth = -desiredAzimuth; + + bool isAzimuthGood = azimuth >= -180.0 && azimuth <= 180.0; + ASSERT(isAzimuthGood); + if (!isAzimuthGood) { + outputBus->zero(); + return; + } + + // Normally, we'll just be dealing with mono sources. + // If we have a stereo input, implement stereo panning with left source processed by left HRTF, and right source by right HRTF. + AudioChannel* inputChannelL = inputBus->channelByType(AudioBus::ChannelLeft); + AudioChannel* inputChannelR = numInputChannels > 1 ? inputBus->channelByType(AudioBus::ChannelRight) : 0; + + // Get source and destination pointers. + float* sourceL = inputChannelL->data(); + float* sourceR = numInputChannels > 1 ? inputChannelR->data() : sourceL; + float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->data(); + float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->data(); + + double azimuthBlend; + int desiredAzimuthIndex = calculateDesiredAzimuthIndexAndBlend(azimuth, azimuthBlend); + + // This algorithm currently requires that we process in power-of-two size chunks at least 128. + ASSERT(1UL << static_cast<int>(log2(framesToProcess)) == framesToProcess); + ASSERT(framesToProcess >= 128); + + const unsigned framesPerSegment = 128; + const unsigned numberOfSegments = framesToProcess / framesPerSegment; + + for (unsigned segment = 0; segment < numberOfSegments; ++segment) { + if (m_isFirstRender) { + // Snap exactly to desired position (first time and after reset()). + m_azimuthIndex = desiredAzimuthIndex; + m_isFirstRender = false; + } else { + // Each segment renders with an azimuth index closer by one to the desired azimuth index. + // Because inter-aural time delay is mostly a factor of azimuth and the delay is where the clicks and graininess come from, + // we don't bother smoothing the elevations. + int numberOfAzimuths = database->numberOfAzimuths(); + bool wrap = wrapDistance(m_azimuthIndex, desiredAzimuthIndex, numberOfAzimuths); + if (wrap) { + if (m_azimuthIndex < desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex - 1 + numberOfAzimuths) % numberOfAzimuths; + else if (m_azimuthIndex > desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex + 1) % numberOfAzimuths; + } else { + if (m_azimuthIndex < desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex + 1) % numberOfAzimuths; + else if (m_azimuthIndex > desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex - 1 + numberOfAzimuths) % numberOfAzimuths; + } + } + + // Get the HRTFKernels and interpolated delays. + HRTFKernel* kernelL; + HRTFKernel* kernelR; + double frameDelayL; + double frameDelayR; + database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex, elevation, kernelL, kernelR, frameDelayL, frameDelayR); + + ASSERT(kernelL && kernelR); + if (!kernelL || !kernelR) { + outputBus->zero(); + return; + } + + ASSERT(frameDelayL / sampleRate() < MaxDelayTimeSeconds && frameDelayR / sampleRate() < MaxDelayTimeSeconds); + + // Calculate the source and destination pointers for the current segment. + unsigned offset = segment * framesPerSegment; + float* segmentSourceL = sourceL + offset; + float* segmentSourceR = sourceR + offset; + float* segmentDestinationL = destinationL + offset; + float* segmentDestinationR = destinationR + offset; + + // First run through delay lines for inter-aural time difference. + m_delayLineL.setDelayFrames(frameDelayL); + m_delayLineR.setDelayFrames(frameDelayR); + m_delayLineL.process(segmentSourceL, segmentDestinationL, framesPerSegment); + m_delayLineR.process(segmentSourceR, segmentDestinationR, framesPerSegment); + + // Now do the convolutions in-place. + m_convolverL.process(kernelL->fftFrame(), segmentDestinationL, segmentDestinationL, framesPerSegment); + m_convolverR.process(kernelR->fftFrame(), segmentDestinationR, segmentDestinationR, framesPerSegment); + } +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/HRTFPanner.h b/Source/WebCore/platform/audio/HRTFPanner.h new file mode 100644 index 0000000..6c13d48 --- /dev/null +++ b/Source/WebCore/platform/audio/HRTFPanner.h @@ -0,0 +1,68 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HRTFPanner_h +#define HRTFPanner_h + +#include "DelayDSPKernel.h" +#include "FFTConvolver.h" +#include "Panner.h" + +namespace WebCore { + +class HRTFPanner : public Panner { +public: + explicit HRTFPanner(double sampleRate); + virtual ~HRTFPanner(); + + // Panner + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess); + virtual void reset(); + + size_t fftSize() { return fftSizeForSampleRate(m_sampleRate); } + static size_t fftSizeForSampleRate(double sampleRate); + + double sampleRate() const { return m_sampleRate; } + +private: + // Given an azimuth angle in the range -180 -> +180, returns the corresponding azimuth index for the database, + // and azimuthBlend which is an interpolation value from 0 -> 1. + int calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend); + + double m_sampleRate; + + // m_isFirstRender and m_azimuthIndex are used to avoid harshly changing from rendering at one azimuth angle to another angle very far away. + // Changing the azimuth gradually produces a smoother sound. + bool m_isFirstRender; + int m_azimuthIndex; + + FFTConvolver m_convolverL; + FFTConvolver m_convolverR; + DelayDSPKernel m_delayLineL; + DelayDSPKernel m_delayLineR; +}; + +} // namespace WebCore + +#endif // HRTFPanner_h diff --git a/Source/WebCore/platform/audio/Panner.cpp b/Source/WebCore/platform/audio/Panner.cpp new file mode 100644 index 0000000..5e8efca --- /dev/null +++ b/Source/WebCore/platform/audio/Panner.cpp @@ -0,0 +1,66 @@ +/* + * 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 <wtf/OwnPtr.h> + +namespace WebCore { + +PassOwnPtr<Panner> Panner::create(PanningModel model, double sampleRate) +{ + OwnPtr<Panner> panner; + + switch (model) { + case PanningModelEqualPower: + panner = adoptPtr(new EqualPowerPanner(sampleRate)); + break; + + case PanningModelHRTF: + panner = adoptPtr(new HRTFPanner(sampleRate)); + 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/Source/WebCore/platform/audio/Panner.h b/Source/WebCore/platform/audio/Panner.h new file mode 100644 index 0000000..b57ceda --- /dev/null +++ b/Source/WebCore/platform/audio/Panner.h @@ -0,0 +1,68 @@ +/* + * 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 { + PanningModelEqualPower = 0, + PanningModelHRTF = 1, + PanningModelSoundField = 2 + }; + + 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/Source/WebCore/platform/audio/Reverb.cpp b/Source/WebCore/platform/audio/Reverb.cpp new file mode 100644 index 0000000..e59ff46 --- /dev/null +++ b/Source/WebCore/platform/audio/Reverb.cpp @@ -0,0 +1,227 @@ +/* + * 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 "Reverb.h" + +#include "AudioBus.h" +#include "AudioFileReader.h" +#include "ReverbConvolver.h" +#include <math.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +#if OS(DARWIN) +using namespace std; +#endif + +namespace WebCore { + +// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal +const double GainCalibration = -58.0; + +// A minimum power value to when normalizing a silent (or very quiet) impulse response +const double MinPower = 0.000125; + +static double calculateNormalizationScale(AudioBus* response) +{ + // Normalize by RMS power + size_t numberOfChannels = response->numberOfChannels(); + size_t length = response->length(); + + double power = 0.0; + + for (size_t i = 0; i < numberOfChannels; ++i) { + int n = length; + float* p = response->channel(i)->data(); + + while (n--) { + float sample = *p++; + power += sample * sample; + } + } + + power = sqrt(power / (numberOfChannels * length)); + + // Protect against accidental overload + if (isinf(power) || isnan(power) || power < MinPower) + power = MinPower; + + double scale = 1.0 / power; + + scale *= pow(10.0, GainCalibration * 0.05); // calibrate to make perceived volume same as unprocessed + + // True-stereo compensation + if (response->numberOfChannels() == 4) + scale *= 0.5; + + return scale; +} + +Reverb::Reverb(AudioBus* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) +{ + double scale = calculateNormalizationScale(impulseResponse); + if (scale) + impulseResponse->scale(scale); + + initialize(impulseResponse, renderSliceSize, maxFFTSize, numberOfChannels, useBackgroundThreads); + + // Undo scaling since this shouldn't be a destructive operation on impulseResponse + if (scale) + impulseResponse->scale(1.0 / scale); +} + +void Reverb::initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) +{ + m_impulseResponseLength = impulseResponseBuffer->length(); + + // The reverb can handle a mono impulse response and still do stereo processing + size_t numResponseChannels = impulseResponseBuffer->numberOfChannels(); + m_convolvers.reserveCapacity(numberOfChannels); + + int convolverRenderPhase = 0; + for (size_t i = 0; i < numResponseChannels; ++i) { + AudioChannel* channel = impulseResponseBuffer->channel(i); + + OwnPtr<ReverbConvolver> convolver = adoptPtr(new ReverbConvolver(channel, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); + m_convolvers.append(convolver.release()); + + convolverRenderPhase += renderSliceSize; + } + + // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method. + // It can be bad to allocate memory in a real-time thread. + if (numResponseChannels == 4) + m_tempBuffer = new AudioBus(2, MaxFrameSize); +} + +void Reverb::process(AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess) +{ + // Do a fairly comprehensive sanity check. + // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases. + bool isSafeToProcess = sourceBus && destinationBus && sourceBus->numberOfChannels() > 0 && destinationBus->numberOfChannels() > 0 + && framesToProcess <= MaxFrameSize && framesToProcess <= sourceBus->length() && framesToProcess <= destinationBus->length(); + + ASSERT(isSafeToProcess); + if (!isSafeToProcess) + return; + + // For now only handle mono or stereo output + if (destinationBus->numberOfChannels() > 2) { + destinationBus->zero(); + return; + } + + AudioChannel* destinationChannelL = destinationBus->channel(0); + AudioChannel* sourceChannelL = sourceBus->channel(0); + + // Handle input -> output matrixing... + size_t numInputChannels = sourceBus->numberOfChannels(); + size_t numOutputChannels = destinationBus->numberOfChannels(); + size_t numReverbChannels = m_convolvers.size(); + + if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) { + // 2 -> 2 -> 2 + AudioChannel* sourceChannelR = sourceBus->channel(1); + AudioChannel* destinationChannelR = destinationBus->channel(1); + m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + m_convolvers[1]->process(sourceChannelR, destinationChannelR, framesToProcess); + } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) { + // 1 -> 2 -> 2 + for (int i = 0; i < 2; ++i) { + AudioChannel* destinationChannel = destinationBus->channel(i); + m_convolvers[i]->process(sourceChannelL, destinationChannel, framesToProcess); + } + } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) { + // 1 -> 1 -> 2 + m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + + // simply copy L -> R + AudioChannel* destinationChannelR = destinationBus->channel(1); + bool isCopySafe = destinationChannelL->data() && destinationChannelR->data() && destinationChannelL->length() >= framesToProcess && destinationChannelR->length() >= framesToProcess; + ASSERT(isCopySafe); + if (!isCopySafe) + return; + memcpy(destinationChannelR->data(), destinationChannelL->data(), sizeof(float) * framesToProcess); + } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) { + // 1 -> 1 -> 1 + m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) { + // 2 -> 4 -> 2 ("True" stereo) + AudioChannel* sourceChannelR = sourceBus->channel(1); + AudioChannel* destinationChannelR = destinationBus->channel(1); + + AudioChannel* tempChannelL = m_tempBuffer->channel(0); + AudioChannel* tempChannelR = m_tempBuffer->channel(1); + + // Process left virtual source + m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess); + + // Process right virtual source + m_convolvers[2]->process(sourceChannelR, tempChannelL, framesToProcess); + m_convolvers[3]->process(sourceChannelR, tempChannelR, framesToProcess); + + destinationBus->sumFrom(*m_tempBuffer); + } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) { + // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response) + // This is an inefficient use of a four-channel impulse response, but we should handle the case. + AudioChannel* destinationChannelR = destinationBus->channel(1); + + AudioChannel* tempChannelL = m_tempBuffer->channel(0); + AudioChannel* tempChannelR = m_tempBuffer->channel(1); + + // Process left virtual source + m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess); + + // Process right virtual source + m_convolvers[2]->process(sourceChannelL, tempChannelL, framesToProcess); + m_convolvers[3]->process(sourceChannelL, tempChannelR, framesToProcess); + + destinationBus->sumFrom(*m_tempBuffer); + } else { + // Handle gracefully any unexpected / unsupported matrixing + // FIXME: add code for 5.1 support... + destinationBus->zero(); + } +} + +void Reverb::reset() +{ + for (size_t i = 0; i < m_convolvers.size(); ++i) + m_convolvers[i]->reset(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/Reverb.h b/Source/WebCore/platform/audio/Reverb.h new file mode 100644 index 0000000..26f5f8e --- /dev/null +++ b/Source/WebCore/platform/audio/Reverb.h @@ -0,0 +1,66 @@ +/* + * 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 Reverb_h +#define Reverb_h + +#include "ReverbConvolver.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioBus; + +// Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally. + +class Reverb { +public: + enum { MaxFrameSize = 256 }; + + // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread). + Reverb(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads); + + void process(AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess); + void reset(); + + unsigned impulseResponseLength() const { return m_impulseResponseLength; } + +private: + void initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads); + + size_t m_impulseResponseLength; + + Vector<OwnPtr<ReverbConvolver> > m_convolvers; + + // For "True" stereo processing + OwnPtr<AudioBus> m_tempBuffer; +}; + +} // namespace WebCore + +#endif // Reverb_h diff --git a/Source/WebCore/platform/audio/ReverbAccumulationBuffer.cpp b/Source/WebCore/platform/audio/ReverbAccumulationBuffer.cpp new file mode 100644 index 0000000..3d694d1 --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbAccumulationBuffer.cpp @@ -0,0 +1,119 @@ +/* + * 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 "ReverbAccumulationBuffer.h" + +#include "VectorMath.h" + +namespace WebCore { + +using namespace VectorMath; + +ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length) + : m_buffer(length) + , m_readIndex(0) + , m_readTimeFrame(0) +{ +} + +void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfFrames) +{ + size_t bufferLength = m_buffer.size(); + bool isCopySafe = m_readIndex <= bufferLength && numberOfFrames <= bufferLength; + + ASSERT(isCopySafe); + if (!isCopySafe) + return; + + size_t framesAvailable = bufferLength - m_readIndex; + size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable); + size_t numberOfFrames2 = numberOfFrames - numberOfFrames1; + + float* source = m_buffer.data(); + memcpy(destination, source + m_readIndex, sizeof(float) * numberOfFrames1); + memset(source + m_readIndex, 0, sizeof(float) * numberOfFrames1); + + // Handle wrap-around if necessary + if (numberOfFrames2 > 0) { + memcpy(destination + numberOfFrames1, source, sizeof(float) * numberOfFrames2); + memset(source, 0, sizeof(float) * numberOfFrames2); + } + + m_readIndex = (m_readIndex + numberOfFrames) % bufferLength; + m_readTimeFrame += numberOfFrames; +} + +void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFrames) const +{ + // Update caller's readIndex + *readIndex = (*readIndex + numberOfFrames) % m_buffer.size(); +} + +int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames) +{ + size_t bufferLength = m_buffer.size(); + + size_t writeIndex = (*readIndex + delayFrames) % bufferLength; + + // Update caller's readIndex + *readIndex = (*readIndex + numberOfFrames) % bufferLength; + + size_t framesAvailable = bufferLength - writeIndex; + size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable); + size_t numberOfFrames2 = numberOfFrames - numberOfFrames1; + + float* destination = m_buffer.data(); + + bool isSafe = writeIndex <= bufferLength && numberOfFrames1 + writeIndex <= bufferLength && numberOfFrames2 <= bufferLength; + ASSERT(isSafe); + if (!isSafe) + return 0; + + vadd(source, 1, destination + writeIndex, 1, destination + writeIndex, 1, numberOfFrames1); + + // Handle wrap-around if necessary + if (numberOfFrames2 > 0) + vadd(source + numberOfFrames1, 1, destination, 1, destination, 1, numberOfFrames2); + + return writeIndex; +} + +void ReverbAccumulationBuffer::reset() +{ + m_buffer.zero(); + m_readIndex = 0; + m_readTimeFrame = 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/ReverbAccumulationBuffer.h b/Source/WebCore/platform/audio/ReverbAccumulationBuffer.h new file mode 100644 index 0000000..f5ead2a --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbAccumulationBuffer.h @@ -0,0 +1,67 @@ +/* + * 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 ReverbAccumulationBuffer_h +#define ReverbAccumulationBuffer_h + +#include "AudioArray.h" + +namespace WebCore { + +// ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients +// writing/accumulating to it at different delay offsets from the read position. The read operation will zero the memory +// just read from the buffer, so it will be ready for accumulation the next time around. +class ReverbAccumulationBuffer { +public: + ReverbAccumulationBuffer(size_t length); + + // This will read from, then clear-out numberOfFrames + void readAndClear(float* destination, size_t numberOfFrames); + + // Each ReverbConvolverStage will accumulate its output at the appropriate delay from the read position. + // We need to pass in and update readIndex here, since each ReverbConvolverStage may be running in + // a different thread than the realtime thread calling ReadAndClear() and maintaining m_readIndex + // Returns the writeIndex where the accumulation took place + int accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames); + + size_t readIndex() const { return m_readIndex; } + void updateReadIndex(int* readIndex, size_t numberOfFrames) const; + + size_t readTimeFrame() const { return m_readTimeFrame; } + + void reset(); + +private: + AudioFloatArray m_buffer; + size_t m_readIndex; + size_t m_readTimeFrame; // for debugging (frame on continuous timeline) +}; + +} // namespace WebCore + +#endif // ReverbAccumulationBuffer_h diff --git a/Source/WebCore/platform/audio/ReverbConvolver.cpp b/Source/WebCore/platform/audio/ReverbConvolver.cpp new file mode 100644 index 0000000..120c9f2 --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbConvolver.cpp @@ -0,0 +1,230 @@ +/* + * 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 "ReverbConvolver.h" + +#include "VectorMath.h" +#include "AudioBus.h" + +namespace WebCore { + +using namespace VectorMath; + +const int InputBufferSize = 8 * 16384; + +// We only process the leading portion of the impulse response in the real-time thread. We don't exceed this length. +// It turns out then, that the background thread has about 278msec of scheduling slop. +// Empirically, this has been found to be a good compromise between giving enough time for scheduling slop, +// while still minimizing the amount of processing done in the primary (high-priority) thread. +// This was found to be a good value on Mac OS X, and may work well on other platforms as well, assuming +// the very rough scheduling latencies are similar on these time-scales. Of course, this code may need to be +// tuned for individual platforms if this assumption is found to be incorrect. +const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz + +const size_t MinFFTSize = 256; +const size_t MaxRealtimeFFTSize = 2048; + +static void* backgroundThreadEntry(void* threadData) +{ + ReverbConvolver* reverbConvolver = static_cast<ReverbConvolver*>(threadData); + reverbConvolver->backgroundThreadEntry(); + return 0; +} + +ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads) + : m_impulseResponseLength(impulseResponse->length()) + , m_accumulationBuffer(impulseResponse->length() + renderSliceSize) + , m_inputBuffer(InputBufferSize) + , m_renderSliceSize(renderSliceSize) + , m_minFFTSize(MinFFTSize) // First stage will have this size - successive stages will double in size each time + , m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize + , m_useBackgroundThreads(useBackgroundThreads) + , m_backgroundThread(0) + , m_wantsToExit(false) + , m_moreInputBuffered(false) +{ + // If we are using background threads then don't exceed this FFT size for the + // stages which run in the real-time thread. This avoids having only one or two + // large stages (size 16384 or so) at the end which take a lot of time every several + // processing slices. This way we amortize the cost over more processing slices. + m_maxRealtimeFFTSize = MaxRealtimeFFTSize; + + // For the moment, a good way to know if we have real-time constraint is to check if we're using background threads. + // Otherwise, assume we're being run from a command-line tool. + bool hasRealtimeConstraint = useBackgroundThreads; + + float* response = impulseResponse->data(); + size_t totalResponseLength = impulseResponse->length(); + + // Because we're not using direct-convolution in the leading portion, the reverb has an overall latency of half the first-stage FFT size + size_t reverbTotalLatency = m_minFFTSize / 2; + + size_t stageOffset = 0; + int i = 0; + size_t fftSize = m_minFFTSize; + while (stageOffset < totalResponseLength) { + size_t stageSize = fftSize / 2; + + // For the last stage, it's possible that stageOffset is such that we're straddling the end + // of the impulse response buffer (if we use stageSize), so reduce the last stage's length... + if (stageSize + stageOffset > totalResponseLength) + stageSize = totalResponseLength - stageOffset; + + // This "staggers" the time when each FFT happens so they don't all happen at the same time + int renderPhase = convolverRenderPhase + i * renderSliceSize; + + OwnPtr<ReverbConvolverStage> stage(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer)); + + bool isBackgroundStage = false; + + if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) { + m_backgroundStages.append(stage.release()); + isBackgroundStage = true; + } else + m_stages.append(stage.release()); + + stageOffset += stageSize; + ++i; + + // Figure out next FFT size + fftSize *= 2; + if (hasRealtimeConstraint && !isBackgroundStage && fftSize > m_maxRealtimeFFTSize) + fftSize = m_maxRealtimeFFTSize; + if (fftSize > m_maxFFTSize) + fftSize = m_maxFFTSize; + } + + // Start up background thread + // FIXME: would be better to up the thread priority here. It doesn't need to be real-time, but higher than the default... + if (this->useBackgroundThreads() && m_backgroundStages.size() > 0) + m_backgroundThread = createThread(WebCore::backgroundThreadEntry, this, "convolution background thread"); +} + +ReverbConvolver::~ReverbConvolver() +{ + // Wait for background thread to stop + if (useBackgroundThreads() && m_backgroundThread) { + m_wantsToExit = true; + + // Wake up thread so it can return + { + MutexLocker locker(m_backgroundThreadLock); + m_moreInputBuffered = true; + m_backgroundThreadCondition.signal(); + } + + waitForThreadCompletion(m_backgroundThread, 0); + } +} + +void ReverbConvolver::backgroundThreadEntry() +{ + while (!m_wantsToExit) { + // Wait for realtime thread to give us more input + m_moreInputBuffered = false; + { + MutexLocker locker(m_backgroundThreadLock); + while (!m_moreInputBuffered && !m_wantsToExit) + m_backgroundThreadCondition.wait(m_backgroundThreadLock); + } + + // Process all of the stages until their read indices reach the input buffer's write index + int writeIndex = m_inputBuffer.writeIndex(); + + // Even though it doesn't seem like every stage needs to maintain its own version of readIndex + // we do this in case we want to run in more than one background thread. + int readIndex; + + while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != writeIndex) { // FIXME: do better to detect buffer overrun... + // The ReverbConvolverStages need to process in amounts which evenly divide half the FFT size + const int SliceSize = MinFFTSize / 2; + + // Accumulate contributions from each stage + for (size_t i = 0; i < m_backgroundStages.size(); ++i) + m_backgroundStages[i]->processInBackground(this, SliceSize); + } + } +} + +void ReverbConvolver::process(AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess) +{ + bool isSafe = sourceChannel && destinationChannel && sourceChannel->length() >= framesToProcess && destinationChannel->length() >= framesToProcess; + ASSERT(isSafe); + if (!isSafe) + return; + + float* source = sourceChannel->data(); + float* destination = destinationChannel->data(); + bool isDataSafe = source && destination; + ASSERT(isDataSafe); + if (!isDataSafe) + return; + + // Feed input buffer (read by all threads) + m_inputBuffer.write(source, framesToProcess); + + // Accumulate contributions from each stage + for (size_t i = 0; i < m_stages.size(); ++i) + m_stages[i]->process(source, framesToProcess); + + // Finally read from accumulation buffer + m_accumulationBuffer.readAndClear(destination, framesToProcess); + + // Now that we've buffered more input, wake up our background thread. + + // Not using a MutexLocker looks strange, but we use a tryLock() instead because this is run on the real-time + // thread where it is a disaster for the lock to be contended (causes audio glitching). It's OK if we fail to + // signal from time to time, since we'll get to it the next time we're called. We're called repeatedly + // and frequently (around every 3ms). The background thread is processing well into the future and has a considerable amount of + // leeway here... + if (m_backgroundThreadLock.tryLock()) { + m_moreInputBuffered = true; + m_backgroundThreadCondition.signal(); + m_backgroundThreadLock.unlock(); + } +} + +void ReverbConvolver::reset() +{ + for (size_t i = 0; i < m_stages.size(); ++i) + m_stages[i]->reset(); + + for (size_t i = 0; i < m_backgroundStages.size(); ++i) + m_backgroundStages[i]->reset(); + + m_accumulationBuffer.reset(); + m_inputBuffer.reset(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/ReverbConvolver.h b/Source/WebCore/platform/audio/ReverbConvolver.h new file mode 100644 index 0000000..013b684 --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbConvolver.h @@ -0,0 +1,97 @@ +/* + * 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 ReverbConvolver_h +#define ReverbConvolver_h + +#include "AudioArray.h" +#include "FFTConvolver.h" +#include "ReverbAccumulationBuffer.h" +#include "ReverbConvolverStage.h" +#include "ReverbInputBuffer.h" +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioChannel; + +class ReverbConvolver { +public: + // maxFFTSize can be adjusted (from say 2048 to 32768) depending on how much precision is necessary. + // For certain tweaky de-convolving applications the phase errors add up quickly and lead to non-sensical results with + // larger FFT sizes and single-precision floats. In these cases 2048 is a good size. + // If not doing multi-threaded convolution, then should not go > 8192. + ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads); + ~ReverbConvolver(); + + void process(AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess); + void reset(); + + size_t impulseResponseLength() const { return m_impulseResponseLength; } + + ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; } + + bool useBackgroundThreads() const { return m_useBackgroundThreads; } + void backgroundThreadEntry(); + +private: + Vector<OwnPtr<ReverbConvolverStage> > m_stages; + Vector<OwnPtr<ReverbConvolverStage> > m_backgroundStages; + size_t m_impulseResponseLength; + + ReverbAccumulationBuffer m_accumulationBuffer; + + // One or more background threads read from this input buffer which is fed from the realtime thread. + ReverbInputBuffer m_inputBuffer; + + // We're given a rendering hint, so the FFTs can be optimized to not all occur at the same time + // (very bad when rendering on a real-time thread). + size_t m_renderSliceSize; + + // First stage will be of size m_minFFTSize. Each next stage will be twice as big until we hit m_maxFFTSize. + size_t m_minFFTSize; + size_t m_maxFFTSize; + + // But don't exceed this size in the real-time thread (if we're doing background processing). + size_t m_maxRealtimeFFTSize; + + // Background thread and synchronization + bool m_useBackgroundThreads; + ThreadIdentifier m_backgroundThread; + bool m_wantsToExit; + bool m_moreInputBuffered; + mutable Mutex m_backgroundThreadLock; + mutable ThreadCondition m_backgroundThreadCondition; +}; + +} // namespace WebCore + +#endif // ReverbConvolver_h diff --git a/Source/WebCore/platform/audio/ReverbConvolverStage.cpp b/Source/WebCore/platform/audio/ReverbConvolverStage.cpp new file mode 100644 index 0000000..e722813 --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbConvolverStage.cpp @@ -0,0 +1,165 @@ +/* + * 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 "ReverbConvolverStage.h" + +#include "VectorMath.h" +#include "ReverbAccumulationBuffer.h" +#include "ReverbConvolver.h" +#include "ReverbInputBuffer.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace VectorMath; + +ReverbConvolverStage::ReverbConvolverStage(float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, + size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer) + : m_fftKernel(fftSize) + , m_accumulationBuffer(accumulationBuffer) + , m_accumulationReadIndex(0) + , m_inputReadIndex(0) + , m_impulseResponseLength(responseLength) +{ + ASSERT(impulseResponse); + ASSERT(accumulationBuffer); + + m_fftKernel.doPaddedFFT(impulseResponse + stageOffset, stageLength); + m_convolver = adoptPtr(new FFTConvolver(fftSize)); + m_temporaryBuffer.resize(renderSliceSize); + + // The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset. + size_t totalDelay = stageOffset + reverbTotalLatency; + + // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out... + size_t halfSize = fftSize / 2; + ASSERT(totalDelay >= halfSize); + if (totalDelay >= halfSize) + totalDelay -= halfSize; + + // We divide up the total delay, into pre and post delay sections so that we can schedule at exactly the moment when the FFT will happen. + // This is coordinated with the other stages, so they don't all do their FFTs at the same time... + int maxPreDelayLength = std::min(halfSize, totalDelay); + m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0; + if (m_preDelayLength > totalDelay) + m_preDelayLength = 0; + + m_postDelayLength = totalDelay - m_preDelayLength; + m_preReadWriteIndex = 0; + m_framesProcessed = 0; // total frames processed so far + + m_preDelayBuffer.resize(m_preDelayLength < fftSize ? fftSize : m_preDelayLength); +} + +void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess) +{ + ReverbInputBuffer* inputBuffer = convolver->inputBuffer(); + float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess); + process(source, framesToProcess); +} + +void ReverbConvolverStage::process(float* source, size_t framesToProcess) +{ + ASSERT(source); + if (!source) + return; + + // Deal with pre-delay stream : note special handling of zero delay. + + float* preDelayedSource; + float* temporaryBuffer; + bool isTemporaryBufferSafe = false; + if (m_preDelayLength > 0) { + // Handles both the read case (call to process() ) and the write case (memcpy() ) + bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.size(); + ASSERT(isPreDelaySafe); + if (!isPreDelaySafe) + return; + + isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.size(); + + preDelayedSource = m_preDelayBuffer.data() + m_preReadWriteIndex; + temporaryBuffer = m_temporaryBuffer.data(); + } else { + // Zero delay + preDelayedSource = source; + temporaryBuffer = m_preDelayBuffer.data(); + + isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size(); + } + + ASSERT(isTemporaryBufferSafe); + if (!isTemporaryBufferSafe) + return; + + int writeIndex = 0; + + if (m_framesProcessed < m_preDelayLength) { + // For the first m_preDelayLength frames don't process the convolver, instead simply buffer in the pre-delay. + // But while buffering the pre-delay, we still need to update our index. + m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex, framesToProcess); + } else { + // Now, run the convolution (into the delay buffer). + // An expensive FFT will happen every fftSize / 2 frames. + // We process in-place here... + m_convolver->process(&m_fftKernel, preDelayedSource, temporaryBuffer, framesToProcess); + + // Now accumulate into reverb's accumulation buffer. + writeIndex = m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength); + } + + // Finally copy input to pre-delay. + if (m_preDelayLength > 0) { + memcpy(preDelayedSource, source, sizeof(float) * framesToProcess); + m_preReadWriteIndex += framesToProcess; + + ASSERT(m_preReadWriteIndex <= m_preDelayLength); + if (m_preReadWriteIndex >= m_preDelayLength) + m_preReadWriteIndex = 0; + } + + m_framesProcessed += framesToProcess; +} + +void ReverbConvolverStage::reset() +{ + m_convolver->reset(); + m_preDelayBuffer.zero(); + m_accumulationReadIndex = 0; + m_inputReadIndex = 0; + m_framesProcessed = 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/ReverbConvolverStage.h b/Source/WebCore/platform/audio/ReverbConvolverStage.h new file mode 100644 index 0000000..fc05a0e --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbConvolverStage.h @@ -0,0 +1,83 @@ +/* + * 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 ReverbConvolverStage_h +#define ReverbConvolverStage_h + +#include "AudioArray.h" +#include "FFTFrame.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class ReverbAccumulationBuffer; +class ReverbConvolver; +class FFTConvolver; + +// A ReverbConvolverStage represents the convolution associated with a sub-section of a large impulse response. +// It incorporates a delay line to account for the offset of the sub-section within the larger impulse response. +class ReverbConvolverStage { +public: + // renderPhase is useful to know so that we can manipulate the pre versus post delay so that stages will perform + // their heavy work (FFT processing) on different slices to balance the load in a real-time thread. + ReverbConvolverStage(float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, + size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer); + + // WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset). + void process(float* source, size_t framesToProcess); + + void processInBackground(ReverbConvolver* convolver, size_t framesToProcess); + + void reset(); + + // Useful for background processing + int inputReadIndex() const { return m_inputReadIndex; } + +private: + FFTFrame m_fftKernel; + OwnPtr<FFTConvolver> m_convolver; + + AudioFloatArray m_preDelayBuffer; + + ReverbAccumulationBuffer* m_accumulationBuffer; + int m_accumulationReadIndex; + int m_inputReadIndex; + + size_t m_preDelayLength; + size_t m_postDelayLength; + size_t m_preReadWriteIndex; + size_t m_framesProcessed; + + AudioFloatArray m_temporaryBuffer; + + size_t m_impulseResponseLength; +}; + +} // namespace WebCore + +#endif // ReverbConvolverStage_h diff --git a/Source/WebCore/platform/audio/ReverbInputBuffer.cpp b/Source/WebCore/platform/audio/ReverbInputBuffer.cpp new file mode 100644 index 0000000..f270f6f --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbInputBuffer.cpp @@ -0,0 +1,89 @@ +/* + * 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 "ReverbInputBuffer.h" + +namespace WebCore { + +ReverbInputBuffer::ReverbInputBuffer(size_t length) + : m_buffer(length) + , m_writeIndex(0) +{ +} + +void ReverbInputBuffer::write(float* sourceP, size_t numberOfFrames) +{ + size_t bufferLength = m_buffer.size(); + bool isCopySafe = m_writeIndex + numberOfFrames <= bufferLength; + ASSERT(isCopySafe); + if (!isCopySafe) + return; + + memcpy(m_buffer.data() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames); + + m_writeIndex += numberOfFrames; + ASSERT(m_writeIndex <= bufferLength); + + if (m_writeIndex >= bufferLength) + m_writeIndex = 0; +} + +float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames) +{ + size_t bufferLength = m_buffer.size(); + bool isPointerGood = readIndex && *readIndex >= 0 && *readIndex + numberOfFrames <= bufferLength; + ASSERT(isPointerGood); + if (!isPointerGood) { + // Should never happen in practice but return pointer to start of buffer (avoid crash) + if (readIndex) + *readIndex = 0; + return m_buffer.data(); + } + + float* sourceP = m_buffer.data(); + float* p = sourceP + *readIndex; + + // Update readIndex + *readIndex = (*readIndex + numberOfFrames) % bufferLength; + + return p; +} + +void ReverbInputBuffer::reset() +{ + m_buffer.zero(); + m_writeIndex = 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/ReverbInputBuffer.h b/Source/WebCore/platform/audio/ReverbInputBuffer.h new file mode 100644 index 0000000..15a2818 --- /dev/null +++ b/Source/WebCore/platform/audio/ReverbInputBuffer.h @@ -0,0 +1,64 @@ +/* + * 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 ReverbInputBuffer_h +#define ReverbInputBuffer_h + +#include "AudioArray.h" + +namespace WebCore { + +// ReverbInputBuffer is used to buffer input samples for deferred processing by the background threads. +class ReverbInputBuffer { +public: + ReverbInputBuffer(size_t length); + + // The realtime audio thread keeps writing samples here. + // The assumption is that the buffer's length is evenly divisible by numberOfFrames (for nearly all cases this will be fine). + // FIXME: remove numberOfFrames restriction... + void write(float* sourceP, size_t numberOfFrames); + + // Background threads can call this to check if there's anything to read... + size_t writeIndex() const { return m_writeIndex; } + + // The individual background threads read here (and hope that they can keep up with the buffer writing). + // readIndex is updated with the next readIndex to read from... + // The assumption is that the buffer's length is evenly divisible by numberOfFrames. + // FIXME: remove numberOfFrames restriction... + float* directReadFrom(int* readIndex, size_t numberOfFrames); + + void reset(); + +private: + AudioFloatArray m_buffer; + size_t m_writeIndex; +}; + +} // namespace WebCore + +#endif // ReverbInputBuffer_h diff --git a/Source/WebCore/platform/audio/VectorMath.cpp b/Source/WebCore/platform/audio/VectorMath.cpp new file mode 100644 index 0000000..64e192b --- /dev/null +++ b/Source/WebCore/platform/audio/VectorMath.cpp @@ -0,0 +1,94 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "VectorMath.h" + +#if OS(DARWIN) +#include <Accelerate/Accelerate.h> +#endif + +namespace WebCore { + +namespace VectorMath { + +#if OS(DARWIN) +// On the Mac we use the highly optimized versions in Accelerate.framework +// In 32-bit mode (__ppc__ or __i386__) <Accelerate/Accelerate.h> includes <vecLib/vDSP_translate.h> which defines macros of the same name as +// our namespaced function names, so we must handle this case differently. Other architectures (64bit, ARM, etc.) do not include this header file. + +void vsmul(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess) +{ +#if defined(__ppc__) || defined(__i386__) + ::vsmul(sourceP, sourceStride, scale, destP, destStride, framesToProcess); +#else + vDSP_vsmul(sourceP, sourceStride, scale, destP, destStride, framesToProcess); +#endif +} + +void vadd(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess) +{ +#if defined(__ppc__) || defined(__i386__) + ::vadd(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess); +#else + vDSP_vadd(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess); +#endif +} + +#else + +void vsmul(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess) +{ + // FIXME: optimize for SSE + int n = framesToProcess; + float k = *scale; + while (n--) { + *destP = k * *sourceP; + sourceP += sourceStride; + destP += destStride; + } +} + +void vadd(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess) +{ + // FIXME: optimize for SSE + int n = framesToProcess; + while (n--) { + *destP = *source1P + *source2P; + source1P += sourceStride1; + source2P += sourceStride2; + destP += destStride; + } +} + +#endif // OS(DARWIN) + +} // namespace VectorMath + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/VectorMath.h b/Source/WebCore/platform/audio/VectorMath.h new file mode 100644 index 0000000..0360bdb --- /dev/null +++ b/Source/WebCore/platform/audio/VectorMath.h @@ -0,0 +1,41 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VectorMath_h +#define VectorMath_h + +// Defines the interface for several vector math functions whose implementation will ideally be optimized. + +namespace WebCore { + +namespace VectorMath { + +void vsmul(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess); +void vadd(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess); + +} // namespace VectorMath + +} // namespace WebCore + +#endif // VectorMath_h diff --git a/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp new file mode 100644 index 0000000..a93703d --- /dev/null +++ b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp @@ -0,0 +1,61 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioBus.h" + +#include "AudioFileReader.h" +#include "ChromiumBridge.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +// We will use this version of loadPlatformResource() once the resources are checked into Chromium. + +// PassOwnPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, double sampleRate) +// { +// return ChromiumBridge::loadPlatformAudioResource(name, sampleRate); +// } + +PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate) +{ + OwnPtr<AudioBus> audioBus = ChromiumBridge::decodeAudioFileData(static_cast<const char*>(data), dataSize, sampleRate); + if (audioBus->numberOfChannels() == 2 && mixToMono) { + OwnPtr<AudioBus> monoAudioBus = adoptPtr(new AudioBus(1, audioBus->length())); + + // FIXME: AudioBus::copyFrom() should be able to do a downmix to mono. + // for now simply copy the left channel. + monoAudioBus->channel(0)->copyFrom(audioBus->channel(0)); + return monoAudioBus.release(); + } + + return audioBus.release(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioBusMac.mm b/Source/WebCore/platform/audio/mac/AudioBusMac.mm new file mode 100644 index 0000000..3d454a9 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioBusMac.mm @@ -0,0 +1,66 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#if ENABLE(WEB_AUDIO) + +#import "AudioBus.h" + +#import "AudioFileReader.h" +#import <wtf/OwnPtr.h> +#import <wtf/PassOwnPtr.h> +#import <Foundation/Foundation.h> + +@interface WebCoreAudioBundleClass : NSObject +@end + +@implementation WebCoreAudioBundleClass +@end + +namespace WebCore { + +PassOwnPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, double sampleRate) +{ + // This method can be called from other than the main thread, so we need an auto-release pool. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSBundle *bundle = [NSBundle bundleForClass:[WebCoreAudioBundleClass class]]; + NSString *audioFilePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"wav" inDirectory:@"audio"]; + NSData *audioData = [NSData dataWithContentsOfFile:audioFilePath]; + + if (audioData) { + OwnPtr<AudioBus> bus(createBusFromInMemoryAudioFile([audioData bytes], [audioData length], false, sampleRate)); + [pool release]; + return bus.release(); + } + + ASSERT_NOT_REACHED(); + [pool release]; + return 0; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp new file mode 100644 index 0000000..523729f --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp @@ -0,0 +1,171 @@ +/* + * 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 "AudioDestinationMac.h" + +#include "AudioSourceProvider.h" +#include <CoreAudio/AudioHardware.h> + +namespace WebCore { + +const int kBufferSize = 128; + +// Factory method: Mac-implementation +PassOwnPtr<AudioDestination> AudioDestination::create(AudioSourceProvider& provider, double sampleRate) +{ + return adoptPtr(new AudioDestinationMac(provider, sampleRate)); +} + +double AudioDestination::hardwareSampleRate() +{ + // Determine the default output device's sample-rate. + AudioDeviceID deviceID = kAudioDeviceUnknown; + UInt32 infoSize = sizeof(deviceID); + + AudioObjectPropertyAddress defaultInputDeviceAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultInputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID); + if (result) + return 0.0; // error + + Float64 nominalSampleRate; + infoSize = sizeof(Float64); + + AudioObjectPropertyAddress nominalSampleRateAddress = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + result = AudioObjectGetPropertyData(deviceID, &nominalSampleRateAddress, 0, 0, &infoSize, (void*)&nominalSampleRate); + if (result) + return 0.0; // error + + return nominalSampleRate; +} + +AudioDestinationMac::AudioDestinationMac(AudioSourceProvider& provider, double sampleRate) + : m_outputUnit(0) + , m_provider(provider) + , m_renderBus(2, kBufferSize, false) + , m_sampleRate(sampleRate) + , m_isPlaying(false) +{ + // Open and initialize DefaultOutputUnit + Component comp; + ComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + comp = FindNextComponent(0, &desc); + + ASSERT(comp); + + OSStatus result = OpenAComponent(comp, &m_outputUnit); + ASSERT(!result); + + result = AudioUnitInitialize(m_outputUnit); + ASSERT(!result); + + configure(); +} + +AudioDestinationMac::~AudioDestinationMac() +{ + if (m_outputUnit) + CloseComponent(m_outputUnit); +} + +void AudioDestinationMac::configure() +{ + // Set render callback + AURenderCallbackStruct input; + input.inputProc = inputProc; + input.inputProcRefCon = this; + OSStatus result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); + ASSERT(!result); + + // Set stream format + AudioStreamBasicDescription streamFormat; + streamFormat.mSampleRate = m_sampleRate; + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagsCanonical | kAudioFormatFlagIsNonInterleaved; + streamFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType); + streamFormat.mChannelsPerFrame = 2; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerPacket = sizeof(AudioSampleType); + streamFormat.mBytesPerFrame = sizeof(AudioSampleType); + + result = AudioUnitSetProperty(m_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, (void*)&streamFormat, sizeof(AudioStreamBasicDescription)); + ASSERT(!result); + + // Set the buffer frame size. + UInt32 bufferSize = kBufferSize; + result = AudioUnitSetProperty(m_outputUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Output, 0, (void*)&bufferSize, sizeof(bufferSize)); + ASSERT(!result); +} + +void AudioDestinationMac::start() +{ + OSStatus result = AudioOutputUnitStart(m_outputUnit); + + if (!result) + m_isPlaying = true; +} + +void AudioDestinationMac::stop() +{ + OSStatus result = AudioOutputUnitStop(m_outputUnit); + + if (!result) + m_isPlaying = false; +} + +// Pulls on our provider to get rendered audio stream. +OSStatus AudioDestinationMac::render(UInt32 numberOfFrames, AudioBufferList* ioData) +{ + AudioBuffer* buffers = ioData->mBuffers; + m_renderBus.setChannelMemory(0, (float*)buffers[0].mData, numberOfFrames); + m_renderBus.setChannelMemory(1, (float*)buffers[1].mData, numberOfFrames); + + m_provider.provideInput(&m_renderBus, numberOfFrames); + + return noErr; +} + +// DefaultOutputUnit callback +OSStatus AudioDestinationMac::inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 /*busNumber*/, UInt32 numberOfFrames, AudioBufferList* ioData) +{ + AudioDestinationMac* audioOutput = static_cast<AudioDestinationMac*>(userData); + return audioOutput->render(numberOfFrames, ioData); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioDestinationMac.h b/Source/WebCore/platform/audio/mac/AudioDestinationMac.h new file mode 100644 index 0000000..197440c --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioDestinationMac.h @@ -0,0 +1,69 @@ +/* + * 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 AudioDestinationMac_h +#define AudioDestinationMac_h + +#include "AudioBus.h" +#include "AudioDestination.h" +#include <AudioUnit/AudioUnit.h> + +namespace WebCore { + +// An AudioDestination using CoreAudio's default output AudioUnit + +class AudioDestinationMac : public AudioDestination { +public: + AudioDestinationMac(AudioSourceProvider&, double sampleRate); + virtual ~AudioDestinationMac(); + + virtual void start(); + virtual void stop(); + bool isPlaying() { return m_isPlaying; } + + double sampleRate() const { return m_sampleRate; } + +private: + void configure(); + + // DefaultOutputUnit callback + static OSStatus inputProc(void* userData, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 busNumber, UInt32 numberOfFrames, AudioBufferList* ioData); + + OSStatus render(UInt32 numberOfFrames, AudioBufferList* ioData); + + AudioUnit m_outputUnit; + AudioSourceProvider& m_provider; + AudioBus m_renderBus; + + double m_sampleRate; + bool m_isPlaying; +}; + +} // namespace WebCore + +#endif // AudioDestinationMac_h diff --git a/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp new file mode 100644 index 0000000..6d17152 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.cpp @@ -0,0 +1,258 @@ +/* + * 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 "AudioFileReaderMac.h" + +#include "AudioBus.h" +#include "AudioFileReader.h" +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> + +namespace WebCore { + +static AudioBufferList* createAudioBufferList(size_t numberOfBuffers) +{ + size_t bufferListSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); + bufferListSize += numberOfBuffers * sizeof(AudioBuffer); + + AudioBufferList* bufferList = static_cast<AudioBufferList*>(calloc(1, bufferListSize)); + if (bufferList) + bufferList->mNumberBuffers = numberOfBuffers; + + return bufferList; +} + +static void destroyAudioBufferList(AudioBufferList* bufferList) +{ + free(bufferList); +} + +AudioFileReader::AudioFileReader(const char* filePath) + : m_data(0) + , m_dataSize(0) + , m_filePath(filePath) + , m_audioFileID(0) + , m_extAudioFileRef(0) +{ + FSRef fsref; + OSStatus result = FSPathMakeRef((UInt8*)filePath, &fsref, 0); + if (result != noErr) + return; + + CFURLRef urlRef = CFURLCreateFromFSRef(0, &fsref); + if (!urlRef) + return; + + ExtAudioFileOpenURL(urlRef, &m_extAudioFileRef); + + if (urlRef) + CFRelease(urlRef); +} + +AudioFileReader::AudioFileReader(const void* data, size_t dataSize) + : m_data(data) + , m_dataSize(dataSize) + , m_filePath(0) + , m_audioFileID(0) + , m_extAudioFileRef(0) +{ + OSStatus result = AudioFileOpenWithCallbacks(this, readProc, 0, getSizeProc, 0, 0, &m_audioFileID); + + if (result != noErr) + return; + + result = ExtAudioFileWrapAudioFileID(m_audioFileID, false, &m_extAudioFileRef); + if (result != noErr) + m_extAudioFileRef = 0; +} + +AudioFileReader::~AudioFileReader() +{ + if (m_extAudioFileRef) + ExtAudioFileDispose(m_extAudioFileRef); + + m_extAudioFileRef = 0; + + if (m_audioFileID) + AudioFileClose(m_audioFileID); + + m_audioFileID = 0; +} + +OSStatus AudioFileReader::readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount) +{ + AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData); + + size_t dataSize = audioFileReader->dataSize(); + const void* data = audioFileReader->data(); + size_t bytesToRead = 0; + + if (static_cast<UInt64>(position) < dataSize) { + size_t bytesAvailable = dataSize - static_cast<size_t>(position); + bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable; + memcpy(buffer, static_cast<const char*>(data) + position, bytesToRead); + } else + bytesToRead = 0; + + if (actualCount) + *actualCount = bytesToRead; + + return noErr; +} + +SInt64 AudioFileReader::getSizeProc(void* clientData) +{ + AudioFileReader* audioFileReader = static_cast<AudioFileReader*>(clientData); + return audioFileReader->dataSize(); +} + +PassOwnPtr<AudioBus> AudioFileReader::createBus(double sampleRate, bool mixToMono) +{ + if (!m_extAudioFileRef) + return 0; + + // Get file's data format + UInt32 size = sizeof(m_fileDataFormat); + OSStatus result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileDataFormat, &size, &m_fileDataFormat); + if (result != noErr) + return 0; + + // Number of channels + size_t numberOfChannels = m_fileDataFormat.mChannelsPerFrame; + + // Number of frames + SInt64 numberOfFrames64 = 0; + size = sizeof(numberOfFrames64); + result = ExtAudioFileGetProperty(m_extAudioFileRef, kExtAudioFileProperty_FileLengthFrames, &size, &numberOfFrames64); + if (result != noErr) + return 0; + + // Sample-rate + double fileSampleRate = m_fileDataFormat.mSampleRate; + + // Make client format same number of channels as file format, but tweak a few things. + // Client format will be linear PCM (canonical), and potentially change sample-rate. + m_clientDataFormat = m_fileDataFormat; + + m_clientDataFormat.mFormatID = kAudioFormatLinearPCM; + m_clientDataFormat.mFormatFlags = kAudioFormatFlagsCanonical; + m_clientDataFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType); + m_clientDataFormat.mChannelsPerFrame = numberOfChannels; + m_clientDataFormat.mFramesPerPacket = 1; + m_clientDataFormat.mBytesPerPacket = sizeof(AudioSampleType); + m_clientDataFormat.mBytesPerFrame = sizeof(AudioSampleType); + m_clientDataFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved; + + if (sampleRate) + m_clientDataFormat.mSampleRate = sampleRate; + + result = ExtAudioFileSetProperty(m_extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &m_clientDataFormat); + if (result != noErr) + return 0; + + // Change numberOfFrames64 to destination sample-rate + numberOfFrames64 = numberOfFrames64 * (m_clientDataFormat.mSampleRate / fileSampleRate); + size_t numberOfFrames = static_cast<size_t>(numberOfFrames64); + + size_t busChannelCount = mixToMono ? 1 : numberOfChannels; + + // Create AudioBus where we'll put the PCM audio data + OwnPtr<AudioBus> audioBus = adoptPtr(new AudioBus(busChannelCount, numberOfFrames)); + audioBus->setSampleRate(m_clientDataFormat.mSampleRate); // save for later + + // Only allocated in the mixToMono case + AudioFloatArray bufL; + AudioFloatArray bufR; + float* bufferL = 0; + float* bufferR = 0; + + // Setup AudioBufferList in preparation for reading + AudioBufferList* bufferList = createAudioBufferList(numberOfChannels); + + if (mixToMono && numberOfChannels == 2) { + bufL.resize(numberOfFrames); + bufR.resize(numberOfFrames); + bufferL = bufL.data(); + bufferR = bufR.data(); + + bufferList->mBuffers[0].mNumberChannels = 1; + bufferList->mBuffers[0].mDataByteSize = numberOfFrames * sizeof(float); + bufferList->mBuffers[0].mData = bufferL; + + bufferList->mBuffers[1].mNumberChannels = 1; + bufferList->mBuffers[1].mDataByteSize = numberOfFrames * sizeof(float); + bufferList->mBuffers[1].mData = bufferR; + } else { + ASSERT(!mixToMono || numberOfChannels == 1); + + // for True-stereo (numberOfChannels == 4) + for (size_t i = 0; i < numberOfChannels; ++i) { + bufferList->mBuffers[i].mNumberChannels = 1; + bufferList->mBuffers[i].mDataByteSize = numberOfFrames * sizeof(float); + bufferList->mBuffers[i].mData = audioBus->channel(i)->data(); + } + } + + // Read from the file (or in-memory version) + UInt32 framesToRead = numberOfFrames; + result = ExtAudioFileRead(m_extAudioFileRef, &framesToRead, bufferList); + if (result != noErr) + return 0; + + if (mixToMono && numberOfChannels == 2) { + // Mix stereo down to mono + float* destL = audioBus->channel(0)->data(); + for (size_t i = 0; i < numberOfFrames; i++) + destL[i] = 0.5f * (bufferL[i] + bufferR[i]); + } + + // Cleanup + destroyAudioBufferList(bufferList); + + return audioBus.release(); +} + +PassOwnPtr<AudioBus> createBusFromAudioFile(const char* filePath, bool mixToMono, double sampleRate) +{ + AudioFileReader reader(filePath); + return reader.createBus(sampleRate, mixToMono); +} + +PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate) +{ + AudioFileReader reader(data, dataSize); + return reader.createBus(sampleRate, mixToMono); +} + +} // WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/AudioFileReaderMac.h b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.h new file mode 100644 index 0000000..d531266 --- /dev/null +++ b/Source/WebCore/platform/audio/mac/AudioFileReaderMac.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 AudioFileReaderMac_h +#define AudioFileReaderMac_h + +#include <AudioToolbox/AudioFile.h> +#include <AudioToolbox/ExtendedAudioFile.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class AudioBus; + +// Wrapper class for AudioFile and ExtAudioFile CoreAudio APIs for reading files and in-memory versions of them... + +class AudioFileReader { +public: + AudioFileReader(const char* filePath); + AudioFileReader(const void* data, size_t dataSize); + ~AudioFileReader(); + + // Returns 0 if error + PassOwnPtr<AudioBus> createBus(double sampleRate, bool mixToMono); + + const void* data() const { return m_data; } + size_t dataSize() const { return m_dataSize; } + +private: + static OSStatus readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount); + static SInt64 getSizeProc(void* clientData); + + const void* m_data; + size_t m_dataSize; + const char* m_filePath; + + AudioFileID m_audioFileID; + ExtAudioFileRef m_extAudioFileRef; + + AudioStreamBasicDescription m_fileDataFormat; + AudioStreamBasicDescription m_clientDataFormat; +}; + +} // namespace WebCore + +#endif // AudioFileReaderMac_h diff --git a/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp b/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp new file mode 100644 index 0000000..0f7efb7 --- /dev/null +++ b/Source/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) diff --git a/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp b/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp new file mode 100644 index 0000000..f66a485 --- /dev/null +++ b/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp @@ -0,0 +1,260 @@ +/* + * 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. + * + * 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. + */ + +// FFTFrame implementation using Intel's Math Kernel Library (MKL), +// suitable for use on Windows and Linux. + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +#include "mkl_vml.h" +#include <wtf/MathExtras.h> + +namespace { + +DFTI_DESCRIPTOR_HANDLE createDescriptorHandle(int fftSize) +{ + DFTI_DESCRIPTOR_HANDLE handle = 0; + + // Create DFTI descriptor for 1D single precision transform. + MKL_LONG status = DftiCreateDescriptor(&handle, DFTI_SINGLE, DFTI_REAL, 1, fftSize); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // Set placement of result to DFTI_NOT_INPLACE. + status = DftiSetValue(handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // Set packing format to PERM; this produces the layout which + // matches Accelerate.framework's on the Mac, though interleaved. + status = DftiSetValue(handle, DFTI_PACKED_FORMAT, DFTI_PERM_FORMAT); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // Set the forward scale factor to 2 to match Accelerate.framework's. + // FIXME: FFTFrameMac's scaling factor could be fixed to be 1.0, + // in which case this code would need to be changed as well. + status = DftiSetValue(handle, DFTI_FORWARD_SCALE, 2.0); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // Set the backward scale factor to 1 / 2n to match Accelerate.framework's. + // FIXME: if the above scaling factor is fixed then this needs to be as well. + double scale = 1.0 / (2.0 * fftSize); + status = DftiSetValue(handle, DFTI_BACKWARD_SCALE, scale); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // Use the default DFTI_CONJUGATE_EVEN_STORAGE = DFTI_COMPLEX_REAL. + + // Commit DFTI descriptor. + status = DftiCommitDescriptor(handle); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + return handle; +} + +} // anonymous namespace + +namespace WebCore { + +const int kMaxFFTPow2Size = 24; + +DFTI_DESCRIPTOR_HANDLE* FFTFrame::descriptorHandles = 0; + +// Normal constructor: allocates for a given fftSize. +FFTFrame::FFTFrame(unsigned fftSize) + : m_FFTSize(fftSize) + , m_log2FFTSize(static_cast<unsigned>(log2(fftSize))) + , m_handle(0) + , m_complexData(fftSize) + , m_realData(fftSize / 2) + , m_imagData(fftSize / 2) +{ + // We only allow power of two. + ASSERT(1UL << m_log2FFTSize == m_FFTSize); + + m_handle = descriptorHandleForSize(fftSize); +} + +// Creates a blank/empty frame (interpolate() must later be called). +FFTFrame::FFTFrame() + : m_FFTSize(0) + , m_log2FFTSize(0) + , m_handle(0) +{ +} + +// Copy constructor. +FFTFrame::FFTFrame(const FFTFrame& frame) + : m_FFTSize(frame.m_FFTSize) + , m_log2FFTSize(frame.m_log2FFTSize) + , m_handle(0) + , m_complexData(frame.m_FFTSize) + , m_realData(frame.m_FFTSize / 2) + , m_imagData(frame.m_FFTSize / 2) +{ + m_handle = descriptorHandleForSize(m_FFTSize); + + // Copy/setup frame data. + unsigned nbytes = sizeof(float) * (m_FFTSize / 2); + memcpy(realData(), frame.realData(), nbytes); + memcpy(imagData(), frame.imagData(), nbytes); +} + +FFTFrame::~FFTFrame() +{ +} + +void FFTFrame::multiply(const FFTFrame& frame) +{ + FFTFrame& frame1 = *this; + FFTFrame& frame2 = const_cast<FFTFrame&>(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. + // FIXME: this scaling factor will be 1.0f if the above 2.0 -> 1.0 + // scaling factor is fixed. + 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. + float* interleavedData1 = frame1.getUpToDateComplexData(); + float* interleavedData2 = frame2.getUpToDateComplexData(); + + unsigned halfSize = m_FFTSize / 2; + + // Complex multiply. + vcMul(halfSize - 1, + reinterpret_cast<MKL_Complex8*>(interleavedData1) + 1, + reinterpret_cast<MKL_Complex8*>(interleavedData2) + 1, + reinterpret_cast<MKL_Complex8*>(interleavedData1) + 1); + + // De-interleave and scale the rest of the data. + // FIXME: find an MKL routine to do at least the scaling more efficiently. + for (unsigned i = 1; i < halfSize; ++i) { + int baseComplexIndex = 2 * i; + realP1[i] = scale * interleavedData1[baseComplexIndex]; + imagP1[i] = scale * interleavedData1[baseComplexIndex + 1]; + } +} + +void FFTFrame::doFFT(float* data) +{ + // Compute Forward transform. + MKL_LONG status = DftiComputeForward(m_handle, data, m_complexData.data()); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + + // De-interleave to separate real and complex arrays. FIXME: + // figure out if it's possible to get MKL to use split-complex + // form for 1D real-to-complex out-of-place FFTs. + int len = m_FFTSize / 2; + for (int i = 0; i < len; ++i) { + int baseComplexIndex = 2 * i; + // m_realData[0] is the DC component and m_imagData[0] the + // Nyquist component since the interleaved complex data is + // packed. + m_realData[i] = m_complexData[baseComplexIndex]; + m_imagData[i] = m_complexData[baseComplexIndex + 1]; + } +} + +void FFTFrame::doInverseFFT(float* data) +{ + // Prepare interleaved data. FIXME: figure out if it's possible to + // get MKL to use split-complex form for 1D backward + // (complex-to-real) out-of-place FFTs. + float* interleavedData = getUpToDateComplexData(); + + // Compute backward transform. + MKL_LONG status = DftiComputeBackward(m_handle, interleavedData, data); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); +} + +void FFTFrame::cleanup() +{ + if (!descriptorHandles) + return; + + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + if (descriptorHandles[i]) { + MKL_LONG status = DftiFreeDescriptor(&descriptorHandles[i]); + ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); + } + } + + delete[] descriptorHandles; + descriptorHandles = 0; +} + +float* FFTFrame::realData() const +{ + return const_cast<float*>(m_realData.data()); +} + +float* FFTFrame::imagData() const +{ + return const_cast<float*>(m_imagData.data()); +} + +float* FFTFrame::getUpToDateComplexData() +{ + // FIXME: if we can't completely get rid of this method, SSE + // optimization could be considered if it shows up hot on profiles. + int len = m_FFTSize / 2; + for (int i = 0; i < len; ++i) { + int baseComplexIndex = 2 * i; + m_complexData[baseComplexIndex] = m_realData[i]; + m_complexData[baseComplexIndex + 1] = m_imagData[i]; + } + return const_cast<float*>(m_complexData.data()); +} + +DFTI_DESCRIPTOR_HANDLE FFTFrame::descriptorHandleForSize(unsigned fftSize) +{ + if (!descriptorHandles) { + descriptorHandles = new DFTI_DESCRIPTOR_HANDLE[kMaxFFTPow2Size]; + for (int i = 0; i < kMaxFFTPow2Size; ++i) + descriptorHandles[i] = 0; + } + + ASSERT(fftSize); + int pow2size = static_cast<int>(log2(fftSize)); + ASSERT(pow2size < kMaxFFTPow2Size); + if (!descriptorHandles[pow2size]) + descriptorHandles[pow2size] = createDescriptorHandle(fftSize); + return descriptorHandles[pow2size]; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav Binary files differnew file mode 100644 index 0000000..57f2ef3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav Binary files differnew file mode 100644 index 0000000..3ecea33 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav Binary files differnew file mode 100644 index 0000000..7320802 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav Binary files differnew file mode 100644 index 0000000..1a10d9a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav Binary files differnew file mode 100644 index 0000000..9b12c22 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav Binary files differnew file mode 100644 index 0000000..3153bb8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav Binary files differnew file mode 100644 index 0000000..3282da9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav Binary files differnew file mode 100644 index 0000000..b999852 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav Binary files differnew file mode 100644 index 0000000..53a03b6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav Binary files differnew file mode 100644 index 0000000..16d5766 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav Binary files differnew file mode 100644 index 0000000..3788e16 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav Binary files differnew file mode 100644 index 0000000..ad2efb6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav Binary files differnew file mode 100644 index 0000000..ee86702 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav Binary files differnew file mode 100644 index 0000000..98ff82e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav Binary files differnew file mode 100644 index 0000000..98ff82e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav Binary files differnew file mode 100644 index 0000000..98ff82e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav Binary files differnew file mode 100644 index 0000000..98ff82e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav Binary files differnew file mode 100644 index 0000000..10bdf78 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav Binary files differnew file mode 100644 index 0000000..c8398a4 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav Binary files differnew file mode 100644 index 0000000..82b92a8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav Binary files differnew file mode 100644 index 0000000..8b8714c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav Binary files differnew file mode 100644 index 0000000..882efd4 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav Binary files differnew file mode 100644 index 0000000..abd99e6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav Binary files differnew file mode 100644 index 0000000..28765be --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav Binary files differnew file mode 100644 index 0000000..42c1445 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav Binary files differnew file mode 100644 index 0000000..42c1445 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav Binary files differnew file mode 100644 index 0000000..42c1445 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav Binary files differnew file mode 100644 index 0000000..99b00f7 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav Binary files differnew file mode 100644 index 0000000..f81bee2 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav Binary files differnew file mode 100644 index 0000000..139d0cb --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav Binary files differnew file mode 100644 index 0000000..68b7b4f --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav Binary files differnew file mode 100644 index 0000000..d6773ae --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav Binary files differnew file mode 100644 index 0000000..9e786bb --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav Binary files differnew file mode 100644 index 0000000..fbef2f3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav Binary files differnew file mode 100644 index 0000000..fbef2f3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav Binary files differnew file mode 100644 index 0000000..fbef2f3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav Binary files differnew file mode 100644 index 0000000..fbef2f3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav Binary files differnew file mode 100644 index 0000000..3c53b76 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav Binary files differnew file mode 100644 index 0000000..e4524c0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav Binary files differnew file mode 100644 index 0000000..ff12535 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav Binary files differnew file mode 100644 index 0000000..5bb1b17 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav Binary files differnew file mode 100644 index 0000000..47e0209 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav Binary files differnew file mode 100644 index 0000000..536b4ac --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav Binary files differnew file mode 100644 index 0000000..05152ad --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav Binary files differnew file mode 100644 index 0000000..221637b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav Binary files differnew file mode 100644 index 0000000..7d6d07f --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav Binary files differnew file mode 100644 index 0000000..7d6d07f --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav Binary files differnew file mode 100644 index 0000000..a4eca78 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav Binary files differnew file mode 100644 index 0000000..37393c2 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav Binary files differnew file mode 100644 index 0000000..3d56e26 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav Binary files differnew file mode 100644 index 0000000..2159f3d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav Binary files differnew file mode 100644 index 0000000..8b689f6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav Binary files differnew file mode 100644 index 0000000..9ee8ac5 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav Binary files differnew file mode 100644 index 0000000..88124e9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav Binary files differnew file mode 100644 index 0000000..88124e9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav Binary files differnew file mode 100644 index 0000000..88124e9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav Binary files differnew file mode 100644 index 0000000..88124e9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav Binary files differnew file mode 100644 index 0000000..59441a6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav Binary files differnew file mode 100644 index 0000000..7cac0f5 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav Binary files differnew file mode 100644 index 0000000..dc28d64 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav Binary files differnew file mode 100644 index 0000000..ae7e583 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav Binary files differnew file mode 100644 index 0000000..509449e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav Binary files differnew file mode 100644 index 0000000..e23b20c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav Binary files differnew file mode 100644 index 0000000..cf247b9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav Binary files differnew file mode 100644 index 0000000..f49d520 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav Binary files differnew file mode 100644 index 0000000..f49d520 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav Binary files differnew file mode 100644 index 0000000..f49d520 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav Binary files differnew file mode 100644 index 0000000..e5472f1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav Binary files differnew file mode 100644 index 0000000..8e1af83 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav Binary files differnew file mode 100644 index 0000000..c477193 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav Binary files differnew file mode 100644 index 0000000..4236e08 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav Binary files differnew file mode 100644 index 0000000..2461fb0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav Binary files differnew file mode 100644 index 0000000..11d549b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav Binary files differnew file mode 100644 index 0000000..0aa100e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav Binary files differnew file mode 100644 index 0000000..0aa100e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav Binary files differnew file mode 100644 index 0000000..0aa100e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav Binary files differnew file mode 100644 index 0000000..0aa100e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav Binary files differnew file mode 100644 index 0000000..572e602 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav Binary files differnew file mode 100644 index 0000000..7f41da3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav Binary files differnew file mode 100644 index 0000000..d0101b8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav Binary files differnew file mode 100644 index 0000000..800fbd7 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav Binary files differnew file mode 100644 index 0000000..9b35e72 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav Binary files differnew file mode 100644 index 0000000..bb58c4e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav Binary files differnew file mode 100644 index 0000000..8963e3e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav Binary files differnew file mode 100644 index 0000000..22241ee --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav Binary files differnew file mode 100644 index 0000000..9e4fee0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav Binary files differnew file mode 100644 index 0000000..9e4fee0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav Binary files differnew file mode 100644 index 0000000..95976c6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav Binary files differnew file mode 100644 index 0000000..8fc55f1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav Binary files differnew file mode 100644 index 0000000..eeeb702 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav Binary files differnew file mode 100644 index 0000000..1847d8d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav Binary files differnew file mode 100644 index 0000000..52e812c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav Binary files differnew file mode 100644 index 0000000..32ca344 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav Binary files differnew file mode 100644 index 0000000..3de60c8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav Binary files differnew file mode 100644 index 0000000..3de60c8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav Binary files differnew file mode 100644 index 0000000..3de60c8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav Binary files differnew file mode 100644 index 0000000..3de60c8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav Binary files differnew file mode 100644 index 0000000..2668e41 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav Binary files differnew file mode 100644 index 0000000..e69670b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav Binary files differnew file mode 100644 index 0000000..1cc48ee --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav Binary files differnew file mode 100644 index 0000000..252968b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav Binary files differnew file mode 100644 index 0000000..50aff3c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav Binary files differnew file mode 100644 index 0000000..3abd6e8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav Binary files differnew file mode 100644 index 0000000..3f0d5ef --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav Binary files differnew file mode 100644 index 0000000..616f760 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav Binary files differnew file mode 100644 index 0000000..616f760 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav Binary files differnew file mode 100644 index 0000000..616f760 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav Binary files differnew file mode 100644 index 0000000..bfb6032 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav Binary files differnew file mode 100644 index 0000000..1983cdb --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav Binary files differnew file mode 100644 index 0000000..27c0762 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav Binary files differnew file mode 100644 index 0000000..b04391b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav Binary files differnew file mode 100644 index 0000000..5955612 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav Binary files differnew file mode 100644 index 0000000..af5d83a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav Binary files differnew file mode 100644 index 0000000..a592f71 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav Binary files differnew file mode 100644 index 0000000..a592f71 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav Binary files differnew file mode 100644 index 0000000..a592f71 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav Binary files differnew file mode 100644 index 0000000..a592f71 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav Binary files differnew file mode 100644 index 0000000..a985aa1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav Binary files differnew file mode 100644 index 0000000..a8b83d1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav Binary files differnew file mode 100644 index 0000000..7e649a3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav Binary files differnew file mode 100644 index 0000000..b74985c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav Binary files differnew file mode 100644 index 0000000..e112ee0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav Binary files differnew file mode 100644 index 0000000..ac842cc --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav Binary files differnew file mode 100644 index 0000000..95c3a6d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav Binary files differnew file mode 100644 index 0000000..610eedb --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav Binary files differnew file mode 100644 index 0000000..d4a57bf --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav Binary files differnew file mode 100644 index 0000000..d4a57bf --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav Binary files differnew file mode 100644 index 0000000..bd6e4f8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav Binary files differnew file mode 100644 index 0000000..7d4be6f --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav Binary files differnew file mode 100644 index 0000000..b7ef81a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav Binary files differnew file mode 100644 index 0000000..0c4af2a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav Binary files differnew file mode 100644 index 0000000..dd7a505 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav Binary files differnew file mode 100644 index 0000000..e169049 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav Binary files differnew file mode 100644 index 0000000..49008a0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav Binary files differnew file mode 100644 index 0000000..49008a0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav Binary files differnew file mode 100644 index 0000000..49008a0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav Binary files differnew file mode 100644 index 0000000..49008a0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav Binary files differnew file mode 100644 index 0000000..1e7d478 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav Binary files differnew file mode 100644 index 0000000..2a77d74 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav Binary files differnew file mode 100644 index 0000000..843b928 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav Binary files differnew file mode 100644 index 0000000..770af17 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav Binary files differnew file mode 100644 index 0000000..437a1c6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav Binary files differnew file mode 100644 index 0000000..f0d9d8e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav Binary files differnew file mode 100644 index 0000000..cd4ae55 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav Binary files differnew file mode 100644 index 0000000..7dd5a1a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav Binary files differnew file mode 100644 index 0000000..7dd5a1a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav Binary files differnew file mode 100644 index 0000000..7dd5a1a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav Binary files differnew file mode 100644 index 0000000..bfac19d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav Binary files differnew file mode 100644 index 0000000..8b3e086 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav Binary files differnew file mode 100644 index 0000000..8b4da46 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav Binary files differnew file mode 100644 index 0000000..b6d4703 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav Binary files differnew file mode 100644 index 0000000..0ff35e9 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav Binary files differnew file mode 100644 index 0000000..e934c78 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav Binary files differnew file mode 100644 index 0000000..c931e3c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav Binary files differnew file mode 100644 index 0000000..c931e3c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav Binary files differnew file mode 100644 index 0000000..c931e3c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav Binary files differnew file mode 100644 index 0000000..c931e3c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav Binary files differnew file mode 100644 index 0000000..f999966 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav Binary files differnew file mode 100644 index 0000000..d958590 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav Binary files differnew file mode 100644 index 0000000..ac06260 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav Binary files differnew file mode 100644 index 0000000..b720ed1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav Binary files differnew file mode 100644 index 0000000..b48852a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav Binary files differnew file mode 100644 index 0000000..92c7ef0 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav Binary files differnew file mode 100644 index 0000000..2d5ff65 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav Binary files differnew file mode 100644 index 0000000..07dcfde --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav Binary files differnew file mode 100644 index 0000000..283e250 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav Binary files differnew file mode 100644 index 0000000..283e250 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav Binary files differnew file mode 100644 index 0000000..b99ad7d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav Binary files differnew file mode 100644 index 0000000..4886915 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav Binary files differnew file mode 100644 index 0000000..c932833 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav Binary files differnew file mode 100644 index 0000000..b204def --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav Binary files differnew file mode 100644 index 0000000..fa48113 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav Binary files differnew file mode 100644 index 0000000..2e2de70 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav Binary files differnew file mode 100644 index 0000000..685f102 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav Binary files differnew file mode 100644 index 0000000..685f102 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav Binary files differnew file mode 100644 index 0000000..685f102 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav Binary files differnew file mode 100644 index 0000000..685f102 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav Binary files differnew file mode 100644 index 0000000..c7cce6e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav Binary files differnew file mode 100644 index 0000000..93a6b8a --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav Binary files differnew file mode 100644 index 0000000..efc72bc --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav Binary files differnew file mode 100644 index 0000000..8f49078 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav Binary files differnew file mode 100644 index 0000000..96510f7 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav Binary files differnew file mode 100644 index 0000000..60b84f4 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav Binary files differnew file mode 100644 index 0000000..ec995e6 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav Binary files differnew file mode 100644 index 0000000..e287d0e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav Binary files differnew file mode 100644 index 0000000..e287d0e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav Binary files differnew file mode 100644 index 0000000..e287d0e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav Binary files differnew file mode 100644 index 0000000..01f0921 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav Binary files differnew file mode 100644 index 0000000..380e707 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav Binary files differnew file mode 100644 index 0000000..124a534 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav Binary files differnew file mode 100644 index 0000000..1a577f3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav Binary files differnew file mode 100644 index 0000000..ce698bb --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav Binary files differnew file mode 100644 index 0000000..8d5134f --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav Binary files differnew file mode 100644 index 0000000..583c6ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav Binary files differnew file mode 100644 index 0000000..583c6ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav Binary files differnew file mode 100644 index 0000000..583c6ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav Binary files differnew file mode 100644 index 0000000..583c6ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav Binary files differnew file mode 100644 index 0000000..7b1b43b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav Binary files differnew file mode 100644 index 0000000..e9ed7ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav Binary files differnew file mode 100644 index 0000000..6ce83ed --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav Binary files differnew file mode 100644 index 0000000..b4ea6bf --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav Binary files differnew file mode 100644 index 0000000..76d5b71 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav Binary files differnew file mode 100644 index 0000000..04ee003 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav Binary files differnew file mode 100644 index 0000000..22d7413 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav Binary files differnew file mode 100644 index 0000000..1b35018 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav Binary files differnew file mode 100644 index 0000000..2f55df8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav Binary files differnew file mode 100644 index 0000000..2f55df8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav Binary files differnew file mode 100644 index 0000000..7bcc8a4 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav Binary files differnew file mode 100644 index 0000000..a3bacf3 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav Binary files differnew file mode 100644 index 0000000..bdfba2d --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav Binary files differnew file mode 100644 index 0000000..719320c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav Binary files differnew file mode 100644 index 0000000..5d366fc --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav Binary files differnew file mode 100644 index 0000000..e10e88b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav Binary files differnew file mode 100644 index 0000000..ecb4b50 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav Binary files differnew file mode 100644 index 0000000..ecb4b50 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav Binary files differnew file mode 100644 index 0000000..ecb4b50 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav Binary files differnew file mode 100644 index 0000000..ecb4b50 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav Binary files differnew file mode 100644 index 0000000..35c44d4 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav Binary files differnew file mode 100644 index 0000000..8fe859b --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav Binary files differnew file mode 100644 index 0000000..3e44b83 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav Binary files differnew file mode 100644 index 0000000..e878220 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav Binary files differnew file mode 100644 index 0000000..7628cbc --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav Binary files differnew file mode 100644 index 0000000..7c4430c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav Binary files differnew file mode 100644 index 0000000..55e3c5e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav Binary files differnew file mode 100644 index 0000000..563313e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav Binary files differnew file mode 100644 index 0000000..563313e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav Binary files differnew file mode 100644 index 0000000..563313e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav Binary files differnew file mode 100644 index 0000000..3eccc16 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav Binary files differnew file mode 100644 index 0000000..fd3f5e1 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav Binary files differnew file mode 100644 index 0000000..5937c59 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav Binary files differnew file mode 100644 index 0000000..99dc851 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav Binary files differnew file mode 100644 index 0000000..28994d5 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav Binary files differnew file mode 100644 index 0000000..beb24a2 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav Binary files differnew file mode 100644 index 0000000..f840c59 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav Binary files differnew file mode 100644 index 0000000..f840c59 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav Binary files differnew file mode 100644 index 0000000..f840c59 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav Binary files differnew file mode 100644 index 0000000..f840c59 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav Binary files differnew file mode 100644 index 0000000..68baa8e --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav Binary files differnew file mode 100644 index 0000000..6cb01b8 --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav diff --git a/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav Binary files differnew file mode 100644 index 0000000..b2ae88c --- /dev/null +++ b/Source/WebCore/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav |
