#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "AudioBus.h"
#if !PLATFORM(MAC)
#include "SincResampler.h"
#endif
#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; }
void AudioBus::setChannelMemory(unsigned channelIndex, float* storage, size_t length)
{
if (channelIndex < m_channels.size()) {
channel(channelIndex)->set(storage, length);
m_length = length; }
}
void AudioBus::zero()
{
for (unsigned i = 0; i < m_channels.size(); ++i)
m_channels[i]->zero();
}
AudioChannel* AudioBus::channelByType(unsigned channelType)
{
if (m_layout != LayoutCanonical)
return 0;
switch (numberOfChannels()) {
case 1: if (channelType == ChannelMono || channelType == ChannelLeft)
return channel(0);
return 0;
case 2: switch (channelType) {
case ChannelLeft: return channel(0);
case ChannelRight: return channel(1);
default: return 0;
}
case 4: 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: 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: 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;
}
bool AudioBus::topologyMatches(const AudioBus& bus) const
{
if (numberOfChannels() != bus.numberOfChannels())
return false;
if (length() > bus.length())
return false;
return true;
}
PassOwnPtr<AudioBus> AudioBus::createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame)
{
size_t numberOfSourceFrames = sourceBuffer->length();
unsigned numberOfChannels = sourceBuffer->numberOfChannels();
bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames;
ASSERT(isRangeSafe);
if (!isRangeSafe)
return nullptr;
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);
}
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) {
const AudioChannel* sourceChannel = sourceBus.channel(0);
channel(0)->copyFrom(sourceChannel);
channel(1)->copyFrom(sourceChannel);
} else {
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) {
const AudioChannel* sourceChannel = sourceBus.channel(0);
channel(0)->sumFrom(sourceChannel);
channel(1)->sumFrom(sourceChannel);
} else {
ASSERT_NOT_REACHED();
}
}
void AudioBus::processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
{
double totalDesiredGain = m_busGain * targetGain;
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) {
if (sourceR && destinationR) {
while (framesToProcess--) {
float sampleL = *sourceL++;
float sampleR = *sourceR++;
*destinationL++ += static_cast<float>(gain * sampleL);
*destinationR++ += static_cast<float>(gain * sampleR);
gain += (totalDesiredGain - gain) * DezipperRate;
}
} else if (destinationR) {
while (framesToProcess--) {
float sample = *sourceL++;
*destinationL++ += static_cast<float>(gain * sample);
*destinationR++ += static_cast<float>(gain * sample);
gain += (totalDesiredGain - gain) * DezipperRate;
}
} else {
while (framesToProcess--) {
float sampleL = *sourceL++;
*destinationL++ += static_cast<float>(gain * sampleL);
gain += (totalDesiredGain - gain) * DezipperRate;
}
}
} else {
if (sourceR && destinationR) {
while (framesToProcess--) {
float sampleL = *sourceL++;
float sampleR = *sourceR++;
*destinationL++ = static_cast<float>(gain * sampleL);
*destinationR++ = static_cast<float>(gain * sampleR);
gain += (totalDesiredGain - gain) * DezipperRate;
}
} else if (destinationR) {
while (framesToProcess--) {
float sample = *sourceL++;
*destinationL++ = static_cast<float>(gain * sample);
*destinationR++ = static_cast<float>(gain * sample);
gain += (totalDesiredGain - gain) * DezipperRate;
}
} else {
while (framesToProcess--) {
float sampleL = *sourceL++;
*destinationL++ = static_cast<float>(gain * sampleL);
gain += (totalDesiredGain - gain) * DezipperRate;
}
}
}
*lastMixGain = gain;
}
void AudioBus::processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
{
if (sourceBus.numberOfChannels() != 1 && !topologyMatches(sourceBus))
return;
switch (numberOfChannels()) {
case 1: case 2: processWithGainFromMonoStereo(sourceBus, lastMixGain, targetGain, sumToBus);
break;
case 4: case 5: 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);
}
#if !PLATFORM(MAC)
PassOwnPtr<AudioBus> AudioBus::createBySampleRateConverting(AudioBus* sourceBus, bool mixToMono, double newSampleRate)
{
ASSERT(sourceBus && sourceBus->sampleRate());
if (!sourceBus || !sourceBus->sampleRate())
return nullptr;
double sourceSampleRate = sourceBus->sampleRate();
double destinationSampleRate = newSampleRate;
unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
if (numberOfSourceChannels == 1)
mixToMono = false;
if (sourceSampleRate == destinationSampleRate) {
if (mixToMono)
return AudioBus::createByMixingToMono(sourceBus);
return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
}
AudioBus* resamplerSourceBus;
OwnPtr<AudioBus> mixedMonoBus;
if (mixToMono) {
mixedMonoBus = AudioBus::createByMixingToMono(sourceBus);
resamplerSourceBus = mixedMonoBus.get();
} else {
resamplerSourceBus = sourceBus;
}
double sampleRateRatio = sourceSampleRate / destinationSampleRate;
int sourceLength = resamplerSourceBus->length();
int destinationLength = sourceLength / sampleRateRatio;
unsigned numberOfDestinationChannels = resamplerSourceBus->numberOfChannels();
OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(numberOfDestinationChannels, destinationLength)));
for (unsigned i = 0; i < numberOfDestinationChannels; ++i) {
float* source = resamplerSourceBus->channel(i)->data();
float* destination = destinationBus->channel(i)->data();
SincResampler resampler(sampleRateRatio);
resampler.process(source, destination, sourceLength);
}
destinationBus->setSampleRate(newSampleRate);
return destinationBus.release();
}
#endif // !PLATFORM(MAC)
PassOwnPtr<AudioBus> AudioBus::createByMixingToMono(AudioBus* sourceBus)
{
switch (sourceBus->numberOfChannels()) {
case 1:
return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
case 2:
{
unsigned n = sourceBus->length();
OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(1, n)));
float* sourceL = sourceBus->channel(0)->data();
float* sourceR = sourceBus->channel(1)->data();
float* destination = destinationBus->channel(0)->data();
for (unsigned i = 0; i < n; ++i)
destination[i] = 0.5 * (sourceL[i] + sourceR[i]);
destinationBus->setSampleRate(sourceBus->sampleRate());
return destinationBus.release();
}
}
ASSERT_NOT_REACHED();
return nullptr;
}
}
#endif // ENABLE(WEB_AUDIO)