| /* | |
| * 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 "platform/audio/AudioBus.h" | |
| #include "platform/audio/AudioFileReader.h" | |
| #include "platform/audio/DenormalDisabler.h" | |
| #include "platform/audio/SincResampler.h" | |
| #include "platform/audio/VectorMath.h" | |
| #include "public/platform/Platform.h" | |
| #include "public/platform/WebAudioBus.h" | |
| #include "wtf/PtrUtil.h" | |
| #include <algorithm> | |
| #include <assert.h> | |
| #include <math.h> | |
| #include <memory> | |
| namespace blink { | |
| using namespace VectorMath; | |
| const unsigned MaxBusChannels = 32; | |
| PassRefPtr<AudioBus> AudioBus::create(unsigned numberOfChannels, | |
| size_t length, | |
| bool allocate) { | |
| ASSERT(numberOfChannels <= MaxBusChannels); | |
| if (numberOfChannels > MaxBusChannels) | |
| return nullptr; | |
| return adoptRef(new AudioBus(numberOfChannels, length, allocate)); | |
| } | |
| AudioBus::AudioBus(unsigned numberOfChannels, size_t length, bool allocate) | |
| : m_length(length), m_busGain(1), m_isFirstTime(true), m_sampleRate(0) { | |
| m_channels.reserveInitialCapacity(numberOfChannels); | |
| for (unsigned i = 0; i < numberOfChannels; ++i) { | |
| std::unique_ptr<AudioChannel> channel = | |
| allocate ? WTF::wrapUnique(new AudioChannel(length)) | |
| : WTF::wrapUnique(new AudioChannel(0, length)); | |
| m_channels.push_back(std::move(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); | |
| // FIXME: verify that this length matches all the other channel lengths | |
| m_length = length; | |
| } | |
| } | |
| void AudioBus::resizeSmaller(size_t newLength) { | |
| ASSERT(newLength <= m_length); | |
| if (newLength <= m_length) | |
| m_length = newLength; | |
| for (unsigned i = 0; i < m_channels.size(); ++i) | |
| m_channels[i]->resizeSmaller(newLength); | |
| } | |
| 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 nullptr; | |
| switch (numberOfChannels()) { | |
| case 1: // mono | |
| if (channelType == ChannelMono || channelType == ChannelLeft) | |
| return channel(0); | |
| return nullptr; | |
| case 2: // stereo | |
| switch (channelType) { | |
| case ChannelLeft: | |
| return channel(0); | |
| case ChannelRight: | |
| return channel(1); | |
| default: | |
| return nullptr; | |
| } | |
| 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 nullptr; | |
| } | |
| 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 nullptr; | |
| } | |
| 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 nullptr; | |
| } | |
| } | |
| NOTREACHED(); | |
| return nullptr; | |
| } | |
| const AudioChannel* AudioBus::channelByType(unsigned type) const { | |
| return const_cast<AudioBus*>(this)->channelByType(type); | |
| } | |
| // 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; | |
| } | |
| PassRefPtr<AudioBus> AudioBus::createBufferFromRange( | |
| const 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 nullptr; | |
| size_t rangeLength = endFrame - startFrame; | |
| RefPtr<AudioBus> audioBus = create(numberOfChannels, rangeLength); | |
| audioBus->setSampleRate(sourceBuffer->sampleRate()); | |
| for (unsigned i = 0; i < numberOfChannels; ++i) | |
| audioBus->channel(i)->copyFromRange(sourceBuffer->channel(i), startFrame, | |
| endFrame); | |
| return audioBus; | |
| } | |
| 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(float scale) { | |
| for (unsigned i = 0; i < numberOfChannels(); ++i) | |
| channel(i)->scale(scale); | |
| } | |
| void AudioBus::copyFrom(const AudioBus& sourceBus, | |
| ChannelInterpretation channelInterpretation) { | |
| if (&sourceBus == this) | |
| return; | |
| // Copying bus is equivalent to zeroing and then summing. | |
| zero(); | |
| sumFrom(sourceBus, channelInterpretation); | |
| } | |
| void AudioBus::sumFrom(const AudioBus& sourceBus, | |
| ChannelInterpretation channelInterpretation) { | |
| if (&sourceBus == this) | |
| return; | |
| unsigned numberOfSourceChannels = sourceBus.numberOfChannels(); | |
| unsigned numberOfDestinationChannels = numberOfChannels(); | |
| // If the channel numbers are equal, perform channels-wise summing. | |
| if (numberOfSourceChannels == numberOfDestinationChannels) { | |
| for (unsigned i = 0; i < numberOfSourceChannels; ++i) | |
| channel(i)->sumFrom(sourceBus.channel(i)); | |
| return; | |
| } | |
| // Otherwise perform up/down-mix or the discrete transfer based on the | |
| // number of channels and the channel interpretation. | |
| switch (channelInterpretation) { | |
| case Speakers: | |
| if (numberOfSourceChannels < numberOfDestinationChannels) | |
| sumFromByUpMixing(sourceBus); | |
| else | |
| sumFromByDownMixing(sourceBus); | |
| break; | |
| case Discrete: | |
| discreteSumFrom(sourceBus); | |
| break; | |
| } | |
| } | |
| void AudioBus::discreteSumFrom(const AudioBus& sourceBus) { | |
| unsigned numberOfSourceChannels = sourceBus.numberOfChannels(); | |
| unsigned numberOfDestinationChannels = numberOfChannels(); | |
| if (numberOfDestinationChannels < numberOfSourceChannels) { | |
| // Down-mix by summing channels and dropping the remaining. | |
| for (unsigned i = 0; i < numberOfDestinationChannels; ++i) | |
| channel(i)->sumFrom(sourceBus.channel(i)); | |
| } else if (numberOfDestinationChannels > numberOfSourceChannels) { | |
| // Up-mix by summing as many channels as we have. | |
| for (unsigned i = 0; i < numberOfSourceChannels; ++i) | |
| channel(i)->sumFrom(sourceBus.channel(i)); | |
| } | |
| } | |
| void AudioBus::sumFromByUpMixing(const AudioBus& sourceBus) { | |
| unsigned numberOfSourceChannels = sourceBus.numberOfChannels(); | |
| unsigned numberOfDestinationChannels = numberOfChannels(); | |
| if ((numberOfSourceChannels == 1 && numberOfDestinationChannels == 2) || | |
| (numberOfSourceChannels == 1 && numberOfDestinationChannels == 4)) { | |
| // Up-mixing: 1 -> 2, 1 -> 4 | |
| // output.L = input | |
| // output.R = input | |
| // output.SL = 0 (in the case of 1 -> 4) | |
| // output.SR = 0 (in the case of 1 -> 4) | |
| const AudioChannel* sourceL = sourceBus.channelByType(ChannelLeft); | |
| channelByType(ChannelLeft)->sumFrom(sourceL); | |
| channelByType(ChannelRight)->sumFrom(sourceL); | |
| } else if (numberOfSourceChannels == 1 && numberOfDestinationChannels == 6) { | |
| // Up-mixing: 1 -> 5.1 | |
| // output.L = 0 | |
| // output.R = 0 | |
| // output.C = input (put in center channel) | |
| // output.LFE = 0 | |
| // output.SL = 0 | |
| // output.SR = 0 | |
| channelByType(ChannelCenter)->sumFrom(sourceBus.channelByType(ChannelLeft)); | |
| } else if ((numberOfSourceChannels == 2 && | |
| numberOfDestinationChannels == 4) || | |
| (numberOfSourceChannels == 2 && | |
| numberOfDestinationChannels == 6)) { | |
| // Up-mixing: 2 -> 4, 2 -> 5.1 | |
| // output.L = input.L | |
| // output.R = input.R | |
| // output.C = 0 (in the case of 2 -> 5.1) | |
| // output.LFE = 0 (in the case of 2 -> 5.1) | |
| // output.SL = 0 | |
| // output.SR = 0 | |
| channelByType(ChannelLeft)->sumFrom(sourceBus.channelByType(ChannelLeft)); | |
| channelByType(ChannelRight)->sumFrom(sourceBus.channelByType(ChannelRight)); | |
| } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 6) { | |
| // Up-mixing: 4 -> 5.1 | |
| // output.L = input.L | |
| // output.R = input.R | |
| // output.C = 0 | |
| // output.LFE = 0 | |
| // output.SL = input.SL | |
| // output.SR = input.SR | |
| channelByType(ChannelLeft)->sumFrom(sourceBus.channelByType(ChannelLeft)); | |
| channelByType(ChannelRight)->sumFrom(sourceBus.channelByType(ChannelRight)); | |
| channelByType(ChannelSurroundLeft) | |
| ->sumFrom(sourceBus.channelByType(ChannelSurroundLeft)); | |
| channelByType(ChannelSurroundRight) | |
| ->sumFrom(sourceBus.channelByType(ChannelSurroundRight)); | |
| } else { | |
| // All other cases, fall back to the discrete sum. This will silence the | |
| // excessive channels. | |
| discreteSumFrom(sourceBus); | |
| } | |
| } | |
| void AudioBus::sumFromByDownMixing(const AudioBus& sourceBus) { | |
| unsigned numberOfSourceChannels = sourceBus.numberOfChannels(); | |
| unsigned numberOfDestinationChannels = numberOfChannels(); | |
| if (numberOfSourceChannels == 2 && numberOfDestinationChannels == 1) { | |
| // Down-mixing: 2 -> 1 | |
| // output = 0.5 * (input.L + input.R) | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| float* destination = channelByType(ChannelLeft)->mutableData(); | |
| float scale = 0.5; | |
| vsma(sourceL, 1, &scale, destination, 1, length()); | |
| vsma(sourceR, 1, &scale, destination, 1, length()); | |
| } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 1) { | |
| // Down-mixing: 4 -> 1 | |
| // output = 0.25 * (input.L + input.R + input.SL + input.SR) | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| const float* sourceSL = | |
| sourceBus.channelByType(ChannelSurroundLeft)->data(); | |
| const float* sourceSR = | |
| sourceBus.channelByType(ChannelSurroundRight)->data(); | |
| float* destination = channelByType(ChannelLeft)->mutableData(); | |
| float scale = 0.25; | |
| vsma(sourceL, 1, &scale, destination, 1, length()); | |
| vsma(sourceR, 1, &scale, destination, 1, length()); | |
| vsma(sourceSL, 1, &scale, destination, 1, length()); | |
| vsma(sourceSR, 1, &scale, destination, 1, length()); | |
| } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 1) { | |
| // Down-mixing: 5.1 -> 1 | |
| // output = sqrt(1/2) * (input.L + input.R) + input.C | |
| // + 0.5 * (input.SL + input.SR) | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| const float* sourceC = sourceBus.channelByType(ChannelCenter)->data(); | |
| const float* sourceSL = | |
| sourceBus.channelByType(ChannelSurroundLeft)->data(); | |
| const float* sourceSR = | |
| sourceBus.channelByType(ChannelSurroundRight)->data(); | |
| float* destination = channelByType(ChannelLeft)->mutableData(); | |
| float scaleSqrtHalf = sqrtf(0.5); | |
| float scaleHalf = 0.5; | |
| vsma(sourceL, 1, &scaleSqrtHalf, destination, 1, length()); | |
| vsma(sourceR, 1, &scaleSqrtHalf, destination, 1, length()); | |
| vadd(sourceC, 1, destination, 1, destination, 1, length()); | |
| vsma(sourceSL, 1, &scaleHalf, destination, 1, length()); | |
| vsma(sourceSR, 1, &scaleHalf, destination, 1, length()); | |
| } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 2) { | |
| // Down-mixing: 4 -> 2 | |
| // output.L = 0.5 * (input.L + input.SL) | |
| // output.R = 0.5 * (input.R + input.SR) | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| const float* sourceSL = | |
| sourceBus.channelByType(ChannelSurroundLeft)->data(); | |
| const float* sourceSR = | |
| sourceBus.channelByType(ChannelSurroundRight)->data(); | |
| float* destinationL = channelByType(ChannelLeft)->mutableData(); | |
| float* destinationR = channelByType(ChannelRight)->mutableData(); | |
| float scaleHalf = 0.5; | |
| vsma(sourceL, 1, &scaleHalf, destinationL, 1, length()); | |
| vsma(sourceSL, 1, &scaleHalf, destinationL, 1, length()); | |
| vsma(sourceR, 1, &scaleHalf, destinationR, 1, length()); | |
| vsma(sourceSR, 1, &scaleHalf, destinationR, 1, length()); | |
| } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 2) { | |
| // Down-mixing: 5.1 -> 2 | |
| // output.L = input.L + sqrt(1/2) * (input.C + input.SL) | |
| // output.R = input.R + sqrt(1/2) * (input.C + input.SR) | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| const float* sourceC = sourceBus.channelByType(ChannelCenter)->data(); | |
| const float* sourceSL = | |
| sourceBus.channelByType(ChannelSurroundLeft)->data(); | |
| const float* sourceSR = | |
| sourceBus.channelByType(ChannelSurroundRight)->data(); | |
| float* destinationL = channelByType(ChannelLeft)->mutableData(); | |
| float* destinationR = channelByType(ChannelRight)->mutableData(); | |
| float scaleSqrtHalf = sqrtf(0.5); | |
| vadd(sourceL, 1, destinationL, 1, destinationL, 1, length()); | |
| vsma(sourceC, 1, &scaleSqrtHalf, destinationL, 1, length()); | |
| vsma(sourceSL, 1, &scaleSqrtHalf, destinationL, 1, length()); | |
| vadd(sourceR, 1, destinationR, 1, destinationR, 1, length()); | |
| vsma(sourceC, 1, &scaleSqrtHalf, destinationR, 1, length()); | |
| vsma(sourceSR, 1, &scaleSqrtHalf, destinationR, 1, length()); | |
| } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 4) { | |
| // Down-mixing: 5.1 -> 4 | |
| // output.L = input.L + sqrt(1/2) * input.C | |
| // output.R = input.R + sqrt(1/2) * input.C | |
| // output.SL = input.SL | |
| // output.SR = input.SR | |
| const float* sourceL = sourceBus.channelByType(ChannelLeft)->data(); | |
| const float* sourceR = sourceBus.channelByType(ChannelRight)->data(); | |
| const float* sourceC = sourceBus.channelByType(ChannelCenter)->data(); | |
| float* destinationL = channelByType(ChannelLeft)->mutableData(); | |
| float* destinationR = channelByType(ChannelRight)->mutableData(); | |
| float scaleSqrtHalf = sqrtf(0.5); | |
| vadd(sourceL, 1, destinationL, 1, destinationL, 1, length()); | |
| vsma(sourceC, 1, &scaleSqrtHalf, destinationL, 1, length()); | |
| vadd(sourceR, 1, destinationR, 1, destinationR, 1, length()); | |
| vsma(sourceC, 1, &scaleSqrtHalf, destinationR, 1, length()); | |
| channel(2)->sumFrom(sourceBus.channel(4)); | |
| channel(3)->sumFrom(sourceBus.channel(5)); | |
| } else { | |
| // All other cases, fall back to the discrete sum. This will perform | |
| // channel-wise sum until the destination channels run out. | |
| discreteSumFrom(sourceBus); | |
| } | |
| } | |
| void AudioBus::copyWithGainFrom(const AudioBus& sourceBus, | |
| float* lastMixGain, | |
| float targetGain) { | |
| if (!topologyMatches(sourceBus)) { | |
| NOTREACHED(); | |
| zero(); | |
| return; | |
| } | |
| if (sourceBus.isSilent()) { | |
| zero(); | |
| return; | |
| } | |
| unsigned numberOfChannels = this->numberOfChannels(); | |
| ASSERT(numberOfChannels <= MaxBusChannels); | |
| if (numberOfChannels > MaxBusChannels) | |
| return; | |
| // If it is copying from the same bus and no need to change gain, just return. | |
| if (this == &sourceBus && *lastMixGain == targetGain && targetGain == 1) | |
| return; | |
| AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus); | |
| const float* sources[MaxBusChannels]; | |
| float* destinations[MaxBusChannels]; | |
| for (unsigned i = 0; i < numberOfChannels; ++i) { | |
| sources[i] = sourceBusSafe.channel(i)->data(); | |
| destinations[i] = channel(i)->mutableData(); | |
| } | |
| // 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. | |
| // Take master bus gain into account as well as the targetGain. | |
| float totalDesiredGain = static_cast<float>(m_busGain * targetGain); | |
| // First time, snap directly to totalDesiredGain. | |
| float gain = | |
| static_cast<float>(m_isFirstTime ? totalDesiredGain : *lastMixGain); | |
| m_isFirstTime = false; | |
| const float DezipperRate = 0.005f; | |
| unsigned framesToProcess = length(); | |
| // If the gain is within epsilon of totalDesiredGain, we can skip dezippering. | |
| // FIXME: this value may need tweaking. | |
| const float epsilon = 0.001f; | |
| float gainDiff = fabs(totalDesiredGain - gain); | |
| // Number of frames to de-zipper before we are close enough to the target | |
| // gain. | |
| // FIXME: framesToDezipper could be smaller when target gain is close enough | |
| // within this process loop. | |
| unsigned framesToDezipper = (gainDiff < epsilon) ? 0 : framesToProcess; | |
| if (framesToDezipper) { | |
| if (!m_dezipperGainValues.get() || | |
| m_dezipperGainValues->size() < framesToDezipper) | |
| m_dezipperGainValues = WTF::makeUnique<AudioFloatArray>(framesToDezipper); | |
| float* gainValues = m_dezipperGainValues->data(); | |
| for (unsigned i = 0; i < framesToDezipper; ++i) { | |
| gain += (totalDesiredGain - gain) * DezipperRate; | |
| // FIXME: If we are clever enough in calculating the framesToDezipper | |
| // value, we can probably get rid of this | |
| // DenormalDisabler::flushDenormalFloatToZero() call. | |
| gain = DenormalDisabler::flushDenormalFloatToZero(gain); | |
| *gainValues++ = gain; | |
| } | |
| for (unsigned channelIndex = 0; channelIndex < numberOfChannels; | |
| ++channelIndex) { | |
| vmul(sources[channelIndex], 1, m_dezipperGainValues->data(), 1, | |
| destinations[channelIndex], 1, framesToDezipper); | |
| sources[channelIndex] += framesToDezipper; | |
| destinations[channelIndex] += framesToDezipper; | |
| } | |
| } else | |
| gain = totalDesiredGain; | |
| // Apply constant gain after de-zippering has converged on target gain. | |
| if (framesToDezipper < framesToProcess) { | |
| // Handle gains of 0 and 1 (exactly) specially. | |
| if (gain == 1) { | |
| for (unsigned channelIndex = 0; channelIndex < numberOfChannels; | |
| ++channelIndex) { | |
| memcpy(destinations[channelIndex], sources[channelIndex], | |
| (framesToProcess - framesToDezipper) * | |
| sizeof(*destinations[channelIndex])); | |
| } | |
| } else if (gain == 0) { | |
| for (unsigned channelIndex = 0; channelIndex < numberOfChannels; | |
| ++channelIndex) { | |
| memset(destinations[channelIndex], 0, | |
| (framesToProcess - framesToDezipper) * | |
| sizeof(*destinations[channelIndex])); | |
| } | |
| } else { | |
| for (unsigned channelIndex = 0; channelIndex < numberOfChannels; | |
| ++channelIndex) | |
| vsmul(sources[channelIndex], 1, &gain, destinations[channelIndex], 1, | |
| framesToProcess - framesToDezipper); | |
| } | |
| } | |
| // Save the target gain as the starting point for next time around. | |
| *lastMixGain = gain; | |
| } | |
| void AudioBus::copyWithSampleAccurateGainValuesFrom( | |
| const AudioBus& sourceBus, | |
| float* gainValues, | |
| unsigned numberOfGainValues) { | |
| // Make sure we're processing from the same type of bus. | |
| // We *are* able to process from mono -> stereo | |
| if (sourceBus.numberOfChannels() != 1 && !topologyMatches(sourceBus)) { | |
| NOTREACHED(); | |
| return; | |
| } | |
| if (!gainValues || numberOfGainValues > sourceBus.length()) { | |
| NOTREACHED(); | |
| return; | |
| } | |
| if (sourceBus.length() == numberOfGainValues && | |
| sourceBus.length() == length() && sourceBus.isSilent()) { | |
| zero(); | |
| return; | |
| } | |
| // We handle both the 1 -> N and N -> N case here. | |
| const float* source = sourceBus.channel(0)->data(); | |
| for (unsigned channelIndex = 0; channelIndex < numberOfChannels(); | |
| ++channelIndex) { | |
| if (sourceBus.numberOfChannels() == numberOfChannels()) | |
| source = sourceBus.channel(channelIndex)->data(); | |
| float* destination = channel(channelIndex)->mutableData(); | |
| vmul(source, 1, gainValues, 1, destination, 1, numberOfGainValues); | |
| } | |
| } | |
| PassRefPtr<AudioBus> AudioBus::createBySampleRateConverting( | |
| const AudioBus* sourceBus, | |
| bool mixToMono, | |
| double newSampleRate) { | |
| // sourceBus's sample-rate must be known. | |
| ASSERT(sourceBus && sourceBus->sampleRate()); | |
| if (!sourceBus || !sourceBus->sampleRate()) | |
| return nullptr; | |
| double sourceSampleRate = sourceBus->sampleRate(); | |
| double destinationSampleRate = newSampleRate; | |
| double sampleRateRatio = sourceSampleRate / destinationSampleRate; | |
| unsigned numberOfSourceChannels = sourceBus->numberOfChannels(); | |
| if (numberOfSourceChannels == 1) | |
| mixToMono = false; // already mono | |
| if (sourceSampleRate == destinationSampleRate) { | |
| // No sample-rate conversion is necessary. | |
| if (mixToMono) | |
| return AudioBus::createByMixingToMono(sourceBus); | |
| // Return exact copy. | |
| return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length()); | |
| } | |
| if (sourceBus->isSilent()) { | |
| RefPtr<AudioBus> silentBus = | |
| create(numberOfSourceChannels, sourceBus->length() / sampleRateRatio); | |
| silentBus->setSampleRate(newSampleRate); | |
| return silentBus; | |
| } | |
| // First, mix to mono (if necessary) then sample-rate convert. | |
| const AudioBus* resamplerSourceBus; | |
| RefPtr<AudioBus> mixedMonoBus; | |
| if (mixToMono) { | |
| mixedMonoBus = AudioBus::createByMixingToMono(sourceBus); | |
| resamplerSourceBus = mixedMonoBus.get(); | |
| } else { | |
| // Directly resample without down-mixing. | |
| resamplerSourceBus = sourceBus; | |
| } | |
| // Calculate destination length based on the sample-rates. | |
| int sourceLength = resamplerSourceBus->length(); | |
| int destinationLength = sourceLength / sampleRateRatio; | |
| // Create destination bus with same number of channels. | |
| unsigned numberOfDestinationChannels = resamplerSourceBus->numberOfChannels(); | |
| RefPtr<AudioBus> destinationBus = | |
| create(numberOfDestinationChannels, destinationLength); | |
| // Sample-rate convert each channel. | |
| for (unsigned i = 0; i < numberOfDestinationChannels; ++i) { | |
| const float* source = resamplerSourceBus->channel(i)->data(); | |
| float* destination = destinationBus->channel(i)->mutableData(); | |
| SincResampler resampler(sampleRateRatio); | |
| resampler.process(source, destination, sourceLength); | |
| } | |
| destinationBus->clearSilentFlag(); | |
| destinationBus->setSampleRate(newSampleRate); | |
| return destinationBus; | |
| } | |
| PassRefPtr<AudioBus> AudioBus::createByMixingToMono(const AudioBus* sourceBus) { | |
| if (sourceBus->isSilent()) | |
| return create(1, sourceBus->length()); | |
| switch (sourceBus->numberOfChannels()) { | |
| case 1: | |
| // Simply create an exact copy. | |
| return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length()); | |
| case 2: { | |
| unsigned n = sourceBus->length(); | |
| RefPtr<AudioBus> destinationBus = create(1, n); | |
| const float* sourceL = sourceBus->channel(0)->data(); | |
| const float* sourceR = sourceBus->channel(1)->data(); | |
| float* destination = destinationBus->channel(0)->mutableData(); | |
| // Do the mono mixdown. | |
| for (unsigned i = 0; i < n; ++i) | |
| destination[i] = (sourceL[i] + sourceR[i]) / 2; | |
| destinationBus->clearSilentFlag(); | |
| destinationBus->setSampleRate(sourceBus->sampleRate()); | |
| return destinationBus; | |
| } | |
| } | |
| NOTREACHED(); | |
| return nullptr; | |
| } | |
| bool AudioBus::isSilent() const { | |
| for (size_t i = 0; i < m_channels.size(); ++i) { | |
| if (!m_channels[i]->isSilent()) | |
| return false; | |
| } | |
| return true; | |
| } | |
| void AudioBus::clearSilentFlag() { | |
| for (size_t i = 0; i < m_channels.size(); ++i) | |
| m_channels[i]->clearSilentFlag(); | |
| } | |
| PassRefPtr<AudioBus> decodeAudioFileData(const char* data, size_t size) { | |
| WebAudioBus webAudioBus; | |
| if (Platform::current()->loadAudioResource(&webAudioBus, data, size)) | |
| return webAudioBus.release(); | |
| return nullptr; | |
| } | |
| PassRefPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, | |
| float sampleRate) { | |
| const WebData& resource = Platform::current()->loadResource(name); | |
| if (resource.isEmpty()) | |
| return nullptr; | |
| RefPtr<AudioBus> audioBus = | |
| decodeAudioFileData(resource.data(), resource.size()); | |
| if (!audioBus.get()) | |
| return nullptr; | |
| // If the bus is already at the requested sample-rate then return as is. | |
| if (audioBus->sampleRate() == sampleRate) | |
| return audioBus; | |
| return AudioBus::createBySampleRateConverting(audioBus.get(), false, | |
| sampleRate); | |
| } | |
| PassRefPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, | |
| size_t dataSize, | |
| bool mixToMono, | |
| float sampleRate) { | |
| RefPtr<AudioBus> audioBus = | |
| decodeAudioFileData(static_cast<const char*>(data), dataSize); | |
| if (!audioBus.get()) | |
| return nullptr; | |
| // If the bus needs no conversion then return as is. | |
| if ((!mixToMono || audioBus->numberOfChannels() == 1) && | |
| audioBus->sampleRate() == sampleRate) | |
| return audioBus; | |
| return AudioBus::createBySampleRateConverting(audioBus.get(), mixToMono, | |
| sampleRate); | |
| } | |
| } // namespace blink |