2
* Copyright (C) 2010, Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
13
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
#include "AudioBufferSourceNode.h"
31
#include "AudioContext.h"
32
#include "AudioNodeOutput.h"
33
#include "AudioUtilities.h"
34
#include "FloatConversion.h"
35
#include "ScriptCallStack.h"
36
#include "ScriptExecutionContext.h"
38
#include <wtf/MainThread.h>
39
#include <wtf/MathExtras.h>
45
const double DefaultGrainDuration = 0.020; // 20ms
47
// Arbitrary upper limit on playback rate.
48
// Higher than expected rates can be useful when playing back oversampled buffers
49
// to minimize linear interpolation aliasing.
50
const double MaxRate = 1024;
52
PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, float sampleRate)
54
return adoptRef(new AudioBufferSourceNode(context, sampleRate));
57
AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sampleRate)
58
: AudioScheduledSourceNode(context, sampleRate)
63
, m_virtualReadIndex(0)
66
, m_grainDuration(DefaultGrainDuration)
70
setNodeType(NodeTypeAudioBufferSource);
72
m_gain = AudioGain::create(context, "gain", 1.0, 0.0, 1.0);
73
m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, 0.0, MaxRate);
75
// Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer.
76
addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
81
AudioBufferSourceNode::~AudioBufferSourceNode()
87
void AudioBufferSourceNode::process(size_t framesToProcess)
89
AudioBus* outputBus = output(0)->bus();
91
if (!isInitialized()) {
96
// The audio thread can't block on this lock, so we call tryLock() instead.
97
MutexTryLocker tryLocker(m_processLock);
98
if (tryLocker.locked()) {
104
size_t quantumFrameOffset;
105
size_t bufferFramesToProcess;
107
updateSchedulingInfo(framesToProcess,
110
bufferFramesToProcess);
112
if (!bufferFramesToProcess) {
117
for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
118
m_destinationChannels[i] = outputBus->channel(i)->mutableData();
120
// Render by reading directly from the buffer.
121
if (!renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess)) {
126
// Apply the gain (in-place) to the output bus.
127
float totalGain = gain()->value() * m_buffer->gain();
128
outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
129
outputBus->clearSilentFlag();
131
// Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
136
// Returns true if we're finished.
137
bool AudioBufferSourceNode::renderSilenceAndFinishIfNotLooping(AudioBus*, unsigned index, size_t framesToProcess)
140
// If we're not looping, then stop playing when we get to the end.
142
if (framesToProcess > 0) {
143
// We're not looping and we've reached the end of the sample data, but we still need to provide more output,
144
// so generate silence for the remaining.
145
for (unsigned i = 0; i < numberOfChannels(); ++i)
146
memset(m_destinationChannels[i] + index, 0, sizeof(float) * framesToProcess);
155
bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destinationFrameOffset, size_t numberOfFrames)
157
ASSERT(context()->isAudioThread());
159
// Basic sanity checking
162
if (!bus || !buffer())
165
unsigned numberOfChannels = this->numberOfChannels();
166
unsigned busNumberOfChannels = bus->numberOfChannels();
168
bool channelCountGood = numberOfChannels && numberOfChannels == busNumberOfChannels;
169
ASSERT(channelCountGood);
170
if (!channelCountGood)
173
// Sanity check destinationFrameOffset, numberOfFrames.
174
size_t destinationLength = bus->length();
176
bool isLengthGood = destinationLength <= 4096 && numberOfFrames <= 4096;
177
ASSERT(isLengthGood);
181
bool isOffsetGood = destinationFrameOffset <= destinationLength && destinationFrameOffset + numberOfFrames <= destinationLength;
182
ASSERT(isOffsetGood);
186
// Potentially zero out initial frames leading up to the offset.
187
if (destinationFrameOffset) {
188
for (unsigned i = 0; i < numberOfChannels; ++i)
189
memset(m_destinationChannels[i], 0, sizeof(float) * destinationFrameOffset);
192
// Offset the pointers to the correct offset frame.
193
unsigned writeIndex = destinationFrameOffset;
195
size_t bufferLength = buffer()->length();
196
double bufferSampleRate = buffer()->sampleRate();
198
// Avoid converting from time to sample-frames twice by computing
199
// the grain end time first before computing the sample frame.
200
unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength;
202
// This is a HACK to allow for HRTF tail-time - avoids glitch at end.
203
// FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
204
// https://bugs.webkit.org/show_bug.cgi?id=77224
208
// Do some sanity checking.
209
if (endFrame > bufferLength)
210
endFrame = bufferLength;
211
if (m_virtualReadIndex >= endFrame)
212
m_virtualReadIndex = 0; // reset to start
214
// If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEnd == 0 implies
215
// that we should use the entire buffer as the loop, otherwise use the loop values in m_loopStart and m_loopEnd.
216
double virtualEndFrame = endFrame;
217
double virtualDeltaFrames = endFrame;
219
if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && m_loopEnd > 0 && m_loopStart < m_loopEnd) {
220
// Convert from seconds to sample-frames.
221
double loopStartFrame = m_loopStart * buffer()->sampleRate();
222
double loopEndFrame = m_loopEnd * buffer()->sampleRate();
224
virtualEndFrame = min(loopEndFrame, virtualEndFrame);
225
virtualDeltaFrames = virtualEndFrame - loopStartFrame;
229
double pitchRate = totalPitchRate();
231
// Sanity check that our playback rate isn't larger than the loop size.
232
if (pitchRate >= virtualDeltaFrames)
236
double virtualReadIndex = m_virtualReadIndex;
238
// Render loop - reading from the source buffer to the destination using linear interpolation.
239
int framesToProcess = numberOfFrames;
241
const float** sourceChannels = m_sourceChannels.get();
242
float** destinationChannels = m_destinationChannels.get();
244
// Optimize for the very common case of playing back with pitchRate == 1.
245
// We can avoid the linear interpolation.
246
if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex)
247
&& virtualDeltaFrames == floor(virtualDeltaFrames)
248
&& virtualEndFrame == floor(virtualEndFrame)) {
249
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
250
unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames);
251
endFrame = static_cast<unsigned>(virtualEndFrame);
252
while (framesToProcess > 0) {
253
int framesToEnd = endFrame - readIndex;
254
int framesThisTime = min(framesToProcess, framesToEnd);
255
framesThisTime = max(0, framesThisTime);
257
for (unsigned i = 0; i < numberOfChannels; ++i)
258
memcpy(destinationChannels[i] + writeIndex, sourceChannels[i] + readIndex, sizeof(float) * framesThisTime);
260
writeIndex += framesThisTime;
261
readIndex += framesThisTime;
262
framesToProcess -= framesThisTime;
265
if (readIndex >= endFrame) {
266
readIndex -= deltaFrames;
267
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
271
virtualReadIndex = readIndex;
273
while (framesToProcess--) {
274
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
275
double interpolationFactor = virtualReadIndex - readIndex;
277
// For linear interpolation we need the next sample-frame too.
278
unsigned readIndex2 = readIndex + 1;
279
if (readIndex2 >= bufferLength) {
281
// Make sure to wrap around at the end of the buffer.
282
readIndex2 = static_cast<unsigned>(virtualReadIndex + 1 - virtualDeltaFrames);
284
readIndex2 = readIndex;
287
// Final sanity check on buffer access.
288
// FIXME: as an optimization, try to get rid of this inner-loop check and put assertions and guards before the loop.
289
if (readIndex >= bufferLength || readIndex2 >= bufferLength)
292
// Linear interpolation.
293
for (unsigned i = 0; i < numberOfChannels; ++i) {
294
float* destination = destinationChannels[i];
295
const float* source = sourceChannels[i];
297
double sample1 = source[readIndex];
298
double sample2 = source[readIndex2];
299
double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
301
destination[writeIndex] = narrowPrecisionToFloat(sample);
305
virtualReadIndex += pitchRate;
307
// Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
308
if (virtualReadIndex >= virtualEndFrame) {
309
virtualReadIndex -= virtualDeltaFrames;
310
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
316
bus->clearSilentFlag();
318
m_virtualReadIndex = virtualReadIndex;
324
void AudioBufferSourceNode::reset()
326
m_virtualReadIndex = 0;
327
m_lastGain = gain()->value();
330
bool AudioBufferSourceNode::setBuffer(AudioBuffer* buffer)
332
ASSERT(isMainThread());
334
// The context must be locked since changing the buffer can re-configure the number of channels that are output.
335
AudioContext::AutoLocker contextLocker(context());
337
// This synchronizes with process().
338
MutexLocker processLocker(m_processLock);
341
// Do any necesssary re-configuration to the buffer's number of channels.
342
unsigned numberOfChannels = buffer->numberOfChannels();
344
if (numberOfChannels > AudioContext::maxNumberOfChannels())
347
output(0)->setNumberOfChannels(numberOfChannels);
349
m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
350
m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
352
for (unsigned i = 0; i < numberOfChannels; ++i)
353
m_sourceChannels[i] = buffer->getChannelData(i)->data();
356
m_virtualReadIndex = 0;
362
unsigned AudioBufferSourceNode::numberOfChannels()
364
return output(0)->numberOfChannels();
367
void AudioBufferSourceNode::startGrain(double when, double grainOffset)
369
// Duration of 0 has special value, meaning calculate based on the entire buffer's duration.
370
startGrain(when, grainOffset, 0);
373
void AudioBufferSourceNode::startGrain(double when, double grainOffset, double grainDuration)
375
ASSERT(isMainThread());
377
if (m_playbackState != UNSCHEDULED_STATE)
383
// Do sanity checking of grain parameters versus buffer size.
384
double bufferDuration = buffer()->duration();
386
grainOffset = max(0.0, grainOffset);
387
grainOffset = min(bufferDuration, grainOffset);
388
m_grainOffset = grainOffset;
390
// Handle default/unspecified duration.
391
double maxDuration = bufferDuration - grainOffset;
393
grainDuration = maxDuration;
395
grainDuration = max(0.0, grainDuration);
396
grainDuration = min(maxDuration, grainDuration);
397
m_grainDuration = grainDuration;
402
// We call timeToSampleFrame here since at playbackRate == 1 we don't want to go through linear interpolation
403
// at a sub-sample position since it will degrade the quality.
404
// When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer.
405
// Since playbackRate == 1 is very common, it's worth considering quality.
406
m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
408
m_playbackState = SCHEDULED_STATE;
411
#if ENABLE(LEGACY_WEB_AUDIO)
412
void AudioBufferSourceNode::noteGrainOn(double when, double grainOffset, double grainDuration)
414
startGrain(when, grainOffset, grainDuration);
418
double AudioBufferSourceNode::totalPitchRate()
420
double dopplerRate = 1.0;
422
dopplerRate = m_pannerNode->dopplerRate();
424
// Incorporate buffer's sample-rate versus AudioContext's sample-rate.
425
// Normally it's not an issue because buffers are loaded at the AudioContext's sample-rate, but we can handle it in any case.
426
double sampleRateFactor = 1.0;
428
sampleRateFactor = buffer()->sampleRate() / sampleRate();
430
double basePitchRate = playbackRate()->value();
432
double totalRate = dopplerRate * sampleRateFactor * basePitchRate;
434
// Sanity check the total rate. It's very important that the resampler not get any bad rate values.
435
totalRate = max(0.0, totalRate);
437
totalRate = 1; // zero rate is considered illegal
438
totalRate = min(MaxRate, totalRate);
440
bool isTotalRateValid = !isnan(totalRate) && !isinf(totalRate);
441
ASSERT(isTotalRateValid);
442
if (!isTotalRateValid)
448
bool AudioBufferSourceNode::looping()
450
static bool firstTime = true;
451
if (firstTime && context() && context()->scriptExecutionContext()) {
452
context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
459
void AudioBufferSourceNode::setLooping(bool looping)
461
static bool firstTime = true;
462
if (firstTime && context() && context()->scriptExecutionContext()) {
463
context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
467
m_isLooping = looping;
470
bool AudioBufferSourceNode::propagatesSilence() const
472
return !isPlayingOrScheduled() || hasFinished() || !m_buffer;
475
void AudioBufferSourceNode::setPannerNode(PannerNode* pannerNode)
477
if (m_pannerNode != pannerNode && !hasFinished()) {
479
pannerNode->ref(AudioNode::RefTypeConnection);
481
m_pannerNode->deref(AudioNode::RefTypeConnection);
483
m_pannerNode = pannerNode;
487
void AudioBufferSourceNode::clearPannerNode()
490
m_pannerNode->deref(AudioNode::RefTypeConnection);
495
void AudioBufferSourceNode::finish()
498
ASSERT(!m_pannerNode);
499
AudioScheduledSourceNode::finish();
502
} // namespace WebCore
504
#endif // ENABLE(WEB_AUDIO)