AudioBufferSourceNode.cpp [plain text]
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "AudioBufferSourceNode.h"
#include "AudioContext.h"
#include "AudioNodeOutput.h"
#include "AudioUtilities.h"
#include "Document.h"
#include "FloatConversion.h"
#include "ScriptCallStack.h"
#include <algorithm>
#include <wtf/MainThread.h>
#include <wtf/MathExtras.h>
using namespace std;
namespace WebCore {
const double DefaultGrainDuration = 0.020;
const double MaxRate = 1024;
PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, float sampleRate)
{
return adoptRef(new AudioBufferSourceNode(context, sampleRate));
}
AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sampleRate)
: AudioScheduledSourceNode(context, sampleRate)
, m_buffer(0)
, m_isLooping(false)
, m_virtualReadIndex(0)
, m_isGrain(false)
, m_grainOffset(0.0)
, m_grainDuration(DefaultGrainDuration)
, m_lastGain(1.0)
, m_pannerNode(0)
{
setNodeType(NodeTypeAudioBufferSource);
m_gain = AudioGain::create("gain", 1.0, 0.0, 1.0);
m_playbackRate = AudioParam::create("playbackRate", 1.0, 0.0, MaxRate);
m_gain->setContext(context);
m_playbackRate->setContext(context);
addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
initialize();
}
AudioBufferSourceNode::~AudioBufferSourceNode()
{
uninitialize();
}
void AudioBufferSourceNode::process(size_t framesToProcess)
{
AudioBus* outputBus = output(0)->bus();
if (!isInitialized()) {
outputBus->zero();
return;
}
MutexTryLocker tryLocker(m_processLock);
if (tryLocker.locked()) {
if (!buffer()) {
outputBus->zero();
return;
}
size_t quantumFrameOffset;
size_t bufferFramesToProcess;
updateSchedulingInfo(framesToProcess,
outputBus,
quantumFrameOffset,
bufferFramesToProcess);
if (!bufferFramesToProcess) {
outputBus->zero();
return;
}
for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
m_destinationChannels[i] = outputBus->channel(i)->mutableData();
renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess);
float totalGain = gain()->value() * m_buffer->gain();
outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
outputBus->clearSilentFlag();
} else {
outputBus->zero();
}
}
bool AudioBufferSourceNode::renderSilenceAndFinishIfNotLooping(AudioBus*, unsigned index, size_t framesToProcess)
{
if (!loop()) {
if (framesToProcess > 0) {
for (unsigned i = 0; i < numberOfChannels(); ++i)
memset(m_destinationChannels[i] + index, 0, sizeof(float) * framesToProcess);
}
finish();
return true;
}
return false;
}
void AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destinationFrameOffset, size_t numberOfFrames)
{
ASSERT(context()->isAudioThread());
ASSERT(bus);
ASSERT(buffer());
if (!bus || !buffer())
return;
unsigned numberOfChannels = this->numberOfChannels();
unsigned busNumberOfChannels = bus->numberOfChannels();
bool channelCountGood = numberOfChannels && numberOfChannels == busNumberOfChannels;
ASSERT(channelCountGood);
if (!channelCountGood)
return;
size_t destinationLength = bus->length();
bool isLengthGood = destinationLength <= 4096 && numberOfFrames <= 4096;
ASSERT(isLengthGood);
if (!isLengthGood)
return;
bool isOffsetGood = destinationFrameOffset <= destinationLength && destinationFrameOffset + numberOfFrames <= destinationLength;
ASSERT(isOffsetGood);
if (!isOffsetGood)
return;
if (destinationFrameOffset) {
for (unsigned i = 0; i < numberOfChannels; ++i)
memset(m_destinationChannels[i], 0, sizeof(float) * destinationFrameOffset);
}
unsigned writeIndex = destinationFrameOffset;
size_t bufferLength = buffer()->length();
double bufferSampleRate = buffer()->sampleRate();
unsigned startFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset, bufferSampleRate) : 0;
unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength;
ASSERT(endFrame >= startFrame);
if (endFrame < startFrame)
return;
unsigned deltaFrames = endFrame - startFrame;
if (m_isGrain)
endFrame += 512;
if (startFrame >= bufferLength)
startFrame = !bufferLength ? 0 : bufferLength - 1;
if (endFrame > bufferLength)
endFrame = bufferLength;
if (m_virtualReadIndex >= endFrame)
m_virtualReadIndex = startFrame;
double pitchRate = totalPitchRate();
double virtualReadIndex = m_virtualReadIndex;
int framesToProcess = numberOfFrames;
const float** sourceChannels = m_sourceChannels.get();
float** destinationChannels = m_destinationChannels.get();
if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex)) {
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
while (framesToProcess > 0) {
int framesToEnd = endFrame - readIndex;
int framesThisTime = min(framesToProcess, framesToEnd);
framesThisTime = max(0, framesThisTime);
for (unsigned i = 0; i < numberOfChannels; ++i)
memcpy(destinationChannels[i] + writeIndex, sourceChannels[i] + readIndex, sizeof(float) * framesThisTime);
writeIndex += framesThisTime;
readIndex += framesThisTime;
framesToProcess -= framesThisTime;
if (readIndex >= endFrame) {
readIndex -= deltaFrames;
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
break;
}
}
virtualReadIndex = readIndex;
} else {
while (framesToProcess--) {
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
double interpolationFactor = virtualReadIndex - readIndex;
unsigned readIndex2 = readIndex + 1;
if (readIndex2 >= endFrame) {
if (loop()) {
readIndex2 -= deltaFrames;
} else
readIndex2 = readIndex;
}
if (readIndex >= bufferLength || readIndex2 >= bufferLength)
break;
for (unsigned i = 0; i < numberOfChannels; ++i) {
float* destination = destinationChannels[i];
const float* source = sourceChannels[i];
double sample1 = source[readIndex];
double sample2 = source[readIndex2];
double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
destination[writeIndex] = narrowPrecisionToFloat(sample);
}
writeIndex++;
virtualReadIndex += pitchRate;
if (virtualReadIndex >= endFrame) {
virtualReadIndex -= deltaFrames;
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
break;
}
}
}
bus->clearSilentFlag();
m_virtualReadIndex = virtualReadIndex;
}
void AudioBufferSourceNode::reset()
{
m_virtualReadIndex = 0;
m_lastGain = gain()->value();
}
bool AudioBufferSourceNode::setBuffer(AudioBuffer* buffer)
{
ASSERT(isMainThread());
AudioContext::AutoLocker contextLocker(context());
MutexLocker processLocker(m_processLock);
if (buffer) {
unsigned numberOfChannels = buffer->numberOfChannels();
if (numberOfChannels > AudioContext::maxNumberOfChannels())
return false;
output(0)->setNumberOfChannels(numberOfChannels);
m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
for (unsigned i = 0; i < numberOfChannels; ++i)
m_sourceChannels[i] = buffer->getChannelData(i)->data();
}
m_virtualReadIndex = 0;
m_buffer = buffer;
return true;
}
unsigned AudioBufferSourceNode::numberOfChannels()
{
return output(0)->numberOfChannels();
}
void AudioBufferSourceNode::noteGrainOn(double when, double grainOffset, double grainDuration)
{
ASSERT(isMainThread());
if (m_playbackState != UNSCHEDULED_STATE)
return;
if (!buffer())
return;
double bufferDuration = buffer()->duration();
if (grainDuration > bufferDuration)
return;
double maxGrainOffset = bufferDuration - grainDuration;
maxGrainOffset = max(0.0, maxGrainOffset);
grainOffset = max(0.0, grainOffset);
grainOffset = min(maxGrainOffset, grainOffset);
m_grainOffset = grainOffset;
m_grainDuration = grainDuration;
m_isGrain = true;
m_startTime = when;
m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
m_playbackState = SCHEDULED_STATE;
}
double AudioBufferSourceNode::totalPitchRate()
{
double dopplerRate = 1.0;
if (m_pannerNode.get())
dopplerRate = m_pannerNode->dopplerRate();
double sampleRateFactor = 1.0;
if (buffer())
sampleRateFactor = buffer()->sampleRate() / sampleRate();
double basePitchRate = playbackRate()->value();
double totalRate = dopplerRate * sampleRateFactor * basePitchRate;
totalRate = max(0.0, totalRate);
if (!totalRate)
totalRate = 1; totalRate = min(MaxRate, totalRate);
bool isTotalRateValid = !isnan(totalRate) && !isinf(totalRate);
ASSERT(isTotalRateValid);
if (!isTotalRateValid)
totalRate = 1.0;
return totalRate;
}
bool AudioBufferSourceNode::looping()
{
static bool firstTime = true;
if (firstTime && context() && context()->document()) {
context()->document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
firstTime = false;
}
return m_isLooping;
}
void AudioBufferSourceNode::setLooping(bool looping)
{
static bool firstTime = true;
if (firstTime && context() && context()->document()) {
context()->document()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
firstTime = false;
}
m_isLooping = looping;
}
bool AudioBufferSourceNode::propagatesSilence() const
{
return !isPlayingOrScheduled() || hasFinished() || !m_buffer;
}
}
#endif // ENABLE(WEB_AUDIO)