/opensource.apple.com/source/WebCore/WebCore-1298.37/webaudio/AudioBufferSourceNode.cpp
C++ | 480 lines | 375 code | 84 blank | 21 comment | 21 complexity | eb2a44ab956f32fd4b25175b48ce5395 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0, BSD-3-Clause, GPL-3.0, MPL-2.0, LGPL-2.0, LGPL-2.1, CC-BY-SA-3.0, IPL-1.0, ISC, AGPL-1.0, AGPL-3.0, JSON, Apache-2.0, 0BSD
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title>AudioBufferSourceNode.cpp</title>
- <style type="text/css">
- .enscript-comment { font-style: italic; color: rgb(178,34,34); }
- .enscript-function-name { font-weight: bold; color: rgb(0,0,255); }
- .enscript-variable-name { font-weight: bold; color: rgb(184,134,11); }
- .enscript-keyword { font-weight: bold; color: rgb(160,32,240); }
- .enscript-reference { font-weight: bold; color: rgb(95,158,160); }
- .enscript-string { font-weight: bold; color: rgb(188,143,143); }
- .enscript-builtin { font-weight: bold; color: rgb(218,112,214); }
- .enscript-type { font-weight: bold; color: rgb(34,139,34); }
- .enscript-highlight { text-decoration: underline; color: 0; }
- </style>
- </head>
- <body id="top">
- <h1 style="margin:8px;" id="f1">AudioBufferSourceNode.cpp <span style="font-weight: normal; font-size: 0.5em;">[<a href="?txt">plain text</a>]</span></h1>
- <hr/>
- <div></div>
- <pre>
- <span class="enscript-comment">/*
- * 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.
- */</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"config.h"</span>
- #<span class="enscript-reference">if</span> <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">WEB_AUDIO</span>)
- #<span class="enscript-reference">include</span> <span class="enscript-string">"AudioBufferSourceNode.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"AudioContext.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string">"AudioNodeOutput.h"</span>
- #<span class="enscript-reference">include</span> <span class="enscript-string"><algorithm></span>
- #<span class="enscript-reference">include</span> <span class="enscript-string"><wtf/MathExtras.h></span>
- using namespace std;
- namespace WebCore {
- <span class="enscript-type">const</span> <span class="enscript-type">double</span> DefaultGrainDuration = 0.020; <span class="enscript-comment">// 20ms
- </span>
- PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, <span class="enscript-type">double</span> sampleRate)
- {
- <span class="enscript-keyword">return</span> adoptRef(<span class="enscript-keyword">new</span> AudioBufferSourceNode(context, sampleRate));
- }
- <span class="enscript-function-name">AudioBufferSourceNode::AudioBufferSourceNode</span>(AudioContext* context, <span class="enscript-type">double</span> sampleRate)
- : AudioSourceNode(context, sampleRate)
- , m_buffer(0)
- , m_isPlaying(false)
- , m_isLooping(false)
- , m_hasFinished(false)
- , m_startTime(0.0)
- , m_schedulingFrameDelay(0)
- , m_readIndex(0)
- , m_isGrain(false)
- , m_grainOffset(0.0)
- , m_grainDuration(DefaultGrainDuration)
- , m_grainFrameCount(0)
- , m_lastGain(1.0)
- , m_pannerNode(0)
- {
- setType(NodeTypeAudioBufferSource);
- m_gain = AudioGain::create(<span class="enscript-string">"gain"</span>, 1.0, 0.0, 1.0);
- m_playbackRate = AudioParam::create(<span class="enscript-string">"playbackRate"</span>, 1.0, 0.0, AudioResampler::MaxRate);
- <span class="enscript-comment">// Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer.
- </span> addOutput(adoptPtr(<span class="enscript-keyword">new</span> AudioNodeOutput(<span class="enscript-keyword">this</span>, 1)));
- initialize();
- }
- <span class="enscript-function-name">AudioBufferSourceNode::~AudioBufferSourceNode</span>()
- {
- uninitialize();
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::process</span>(size_t framesToProcess)
- {
- AudioBus* outputBus = output(0)->bus();
- <span class="enscript-keyword">if</span> (!isInitialized()) {
- outputBus->zero();
- <span class="enscript-keyword">return</span>;
- }
- <span class="enscript-comment">// The audio thread can't block on this lock, so we call tryLock() instead.
- </span> <span class="enscript-comment">// Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return.
- </span> <span class="enscript-keyword">if</span> (m_processLock.tryLock()) {
- <span class="enscript-comment">// Check if it's time to start playing.
- </span> <span class="enscript-type">double</span> sampleRate = <span class="enscript-keyword">this</span>->sampleRate();
- <span class="enscript-type">double</span> pitchRate = totalPitchRate();
- <span class="enscript-type">double</span> quantumStartTime = context()->currentTime();
- <span class="enscript-type">double</span> quantumEndTime = quantumStartTime + framesToProcess / sampleRate;
- <span class="enscript-keyword">if</span> (!m_isPlaying || m_hasFinished || !buffer() || m_startTime >= quantumEndTime) {
- <span class="enscript-comment">// FIXME: can optimize here by propagating silent hint instead of forcing the whole chain to process silence.
- </span> outputBus->zero();
- m_processLock.unlock();
- <span class="enscript-keyword">return</span>;
- }
- <span class="enscript-comment">// Handle sample-accurate scheduling so that buffer playback will happen at a very precise time.
- </span> m_schedulingFrameDelay = 0;
- <span class="enscript-keyword">if</span> (m_startTime >= quantumStartTime) {
- <span class="enscript-comment">// m_schedulingFrameDelay is set here only the very first render quantum (because of above check: m_startTime >= quantumEndTime)
- </span> <span class="enscript-comment">// So: quantumStartTime <= m_startTime < quantumEndTime
- </span> ASSERT(m_startTime < quantumEndTime);
-
- <span class="enscript-type">double</span> startTimeInQuantum = m_startTime - quantumStartTime;
- <span class="enscript-type">double</span> startFrameInQuantum = startTimeInQuantum * sampleRate;
-
- <span class="enscript-comment">// m_schedulingFrameDelay is used in provideInput(), so factor in the current playback pitch rate.
- </span> m_schedulingFrameDelay = static_cast<<span class="enscript-type">int</span>>(pitchRate * startFrameInQuantum);
- }
- <span class="enscript-comment">// FIXME: optimization opportunity:
- </span> <span class="enscript-comment">// With a bit of work, it should be possible to avoid going through the resampler completely when the pitchRate == 1,
- </span> <span class="enscript-comment">// especially if the pitchRate has never deviated from 1 in the past.
- </span>
- <span class="enscript-comment">// Read the samples through the pitch resampler. Our provideInput() method will be called by the resampler.
- </span> m_resampler.setRate(pitchRate);
- m_resampler.process(<span class="enscript-keyword">this</span>, outputBus, framesToProcess);
- <span class="enscript-comment">// Apply the gain (in-place) to the output bus.
- </span> <span class="enscript-type">double</span> totalGain = gain()->value() * m_buffer->gain();
- outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
- m_processLock.unlock();
- } <span class="enscript-keyword">else</span> {
- <span class="enscript-comment">// Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
- </span> outputBus->zero();
- }
- }
- <span class="enscript-comment">// The resampler calls us back here to get the input samples from our buffer.
- </span><span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::provideInput</span>(AudioBus* bus, size_t numberOfFrames)
- {
- ASSERT(context()->isAudioThread());
-
- <span class="enscript-comment">// Basic sanity checking
- </span> ASSERT(bus);
- ASSERT(buffer());
- <span class="enscript-keyword">if</span> (!bus || !buffer())
- <span class="enscript-keyword">return</span>;
- <span class="enscript-type">unsigned</span> numberOfChannels = <span class="enscript-keyword">this</span>->numberOfChannels();
- <span class="enscript-type">unsigned</span> busNumberOfChannels = bus->numberOfChannels();
- <span class="enscript-comment">// FIXME: we can add support for sources with more than two channels, but this is not a common case.
- </span> <span class="enscript-type">bool</span> channelCountGood = numberOfChannels == busNumberOfChannels && (numberOfChannels == 1 || numberOfChannels == 2);
- ASSERT(channelCountGood);
- <span class="enscript-keyword">if</span> (!channelCountGood)
- <span class="enscript-keyword">return</span>;
- <span class="enscript-comment">// Get the destination pointers.
- </span> <span class="enscript-type">float</span>* destinationL = bus->channel(0)->data();
- ASSERT(destinationL);
- <span class="enscript-keyword">if</span> (!destinationL)
- <span class="enscript-keyword">return</span>;
- <span class="enscript-type">float</span>* destinationR = (numberOfChannels < 2) ? 0 : bus->channel(1)->data();
- size_t bufferLength = buffer()->length();
- <span class="enscript-type">double</span> bufferSampleRate = buffer()->sampleRate();
- <span class="enscript-comment">// Calculate the start and end frames in our buffer that we want to play.
- </span> <span class="enscript-comment">// If m_isGrain is true, then we will be playing a portion of the total buffer.
- </span> <span class="enscript-type">unsigned</span> startFrame = m_isGrain ? static_cast<<span class="enscript-type">unsigned</span>>(m_grainOffset * bufferSampleRate) : 0;
- <span class="enscript-type">unsigned</span> endFrame = m_isGrain ? static_cast<<span class="enscript-type">unsigned</span>>(startFrame + m_grainDuration * bufferSampleRate) : bufferLength;
- <span class="enscript-comment">// This is a HACK to allow for HRTF tail-time - avoids glitch at end.
- </span> <span class="enscript-comment">// FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
- </span> <span class="enscript-keyword">if</span> (m_isGrain)
- endFrame += 512;
- <span class="enscript-comment">// Do some sanity checking.
- </span> <span class="enscript-keyword">if</span> (startFrame >= bufferLength)
- startFrame = !bufferLength ? 0 : bufferLength - 1;
- <span class="enscript-keyword">if</span> (endFrame > bufferLength)
- endFrame = bufferLength;
- <span class="enscript-keyword">if</span> (m_readIndex >= endFrame)
- m_readIndex = startFrame; <span class="enscript-comment">// reset to start
- </span>
- <span class="enscript-type">int</span> framesToProcess = numberOfFrames;
- <span class="enscript-comment">// Handle sample-accurate scheduling so that we play the buffer at a very precise time.
- </span> <span class="enscript-comment">// m_schedulingFrameDelay will only be non-zero the very first time that provideInput() is called, which corresponds
- </span> <span class="enscript-comment">// with the very start of the buffer playback.
- </span> <span class="enscript-keyword">if</span> (m_schedulingFrameDelay > 0) {
- ASSERT(m_schedulingFrameDelay <= framesToProcess);
- <span class="enscript-keyword">if</span> (m_schedulingFrameDelay <= framesToProcess) {
- <span class="enscript-comment">// Generate silence for the initial portion of the destination.
- </span> memset(destinationL, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * m_schedulingFrameDelay);
- destinationL += m_schedulingFrameDelay;
- <span class="enscript-keyword">if</span> (destinationR) {
- memset(destinationR, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * m_schedulingFrameDelay);
- destinationR += m_schedulingFrameDelay;
- }
- <span class="enscript-comment">// Since we just generated silence for the initial portion, we have fewer frames to provide.
- </span> framesToProcess -= m_schedulingFrameDelay;
- }
- }
-
- <span class="enscript-comment">// We have to generate a certain number of output sample-frames, but we need to handle the case where we wrap around
- </span> <span class="enscript-comment">// from the end of the buffer to the start if playing back with looping and also the case where we simply reach the
- </span> <span class="enscript-comment">// end of the sample data, but haven't yet rendered numberOfFrames worth of output.
- </span> <span class="enscript-keyword">while</span> (framesToProcess > 0) {
- ASSERT(m_readIndex <= endFrame);
- <span class="enscript-keyword">if</span> (m_readIndex > endFrame)
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-comment">// Figure out how many frames we can process this time.
- </span> <span class="enscript-type">int</span> framesAvailable = endFrame - m_readIndex;
- <span class="enscript-type">int</span> framesThisTime = min(framesToProcess, framesAvailable);
-
- <span class="enscript-comment">// Create the destination bus for the part of the destination we're processing this time.
- </span> AudioBus currentDestinationBus(busNumberOfChannels, framesThisTime, false);
- currentDestinationBus.setChannelMemory(0, destinationL, framesThisTime);
- <span class="enscript-keyword">if</span> (busNumberOfChannels > 1)
- currentDestinationBus.setChannelMemory(1, destinationR, framesThisTime);
- <span class="enscript-comment">// Generate output from the buffer.
- </span> readFromBuffer(&currentDestinationBus, framesThisTime);
- <span class="enscript-comment">// Update the destination pointers.
- </span> destinationL += framesThisTime;
- <span class="enscript-keyword">if</span> (busNumberOfChannels > 1)
- destinationR += framesThisTime;
- framesToProcess -= framesThisTime;
- <span class="enscript-comment">// Handle the case where we reach the end of the part of the sample data we're supposed to play for the buffer.
- </span> <span class="enscript-keyword">if</span> (m_readIndex >= endFrame) {
- m_readIndex = startFrame;
- m_grainFrameCount = 0;
-
- <span class="enscript-keyword">if</span> (!looping()) {
- <span class="enscript-comment">// If we're not looping, then stop playing when we get to the end.
- </span> m_isPlaying = false;
- <span class="enscript-keyword">if</span> (framesToProcess > 0) {
- <span class="enscript-comment">// We're not looping and we've reached the end of the sample data, but we still need to provide more output,
- </span> <span class="enscript-comment">// so generate silence for the remaining.
- </span> memset(destinationL, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
- <span class="enscript-keyword">if</span> (destinationR)
- memset(destinationR, 0, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
- }
- <span class="enscript-keyword">if</span> (!m_hasFinished) {
- <span class="enscript-comment">// Let the context dereference this AudioNode.
- </span> context()->notifyNodeFinishedProcessing(<span class="enscript-keyword">this</span>);
- m_hasFinished = true;
- }
- <span class="enscript-keyword">return</span>;
- }
- }
- }
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::readFromBuffer</span>(AudioBus* destinationBus, size_t framesToProcess)
- {
- <span class="enscript-type">bool</span> isBusGood = destinationBus && destinationBus->length() == framesToProcess && destinationBus->numberOfChannels() == numberOfChannels();
- ASSERT(isBusGood);
- <span class="enscript-keyword">if</span> (!isBusGood)
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-type">unsigned</span> numberOfChannels = <span class="enscript-keyword">this</span>->numberOfChannels();
- <span class="enscript-comment">// FIXME: we can add support for sources with more than two channels, but this is not a common case.
- </span> <span class="enscript-type">bool</span> channelCountGood = numberOfChannels == 1 || numberOfChannels == 2;
- ASSERT(channelCountGood);
- <span class="enscript-keyword">if</span> (!channelCountGood)
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-comment">// Get pointers to the start of the sample buffer.
- </span> <span class="enscript-type">float</span>* sourceL = m_buffer->getChannelData(0)->data();
- <span class="enscript-type">float</span>* sourceR = m_buffer->numberOfChannels() == 2 ? m_buffer->getChannelData(1)->data() : 0;
- <span class="enscript-comment">// Sanity check buffer access.
- </span> <span class="enscript-type">bool</span> isSourceGood = sourceL && (numberOfChannels == 1 || sourceR) && m_readIndex + framesToProcess <= m_buffer->length();
- ASSERT(isSourceGood);
- <span class="enscript-keyword">if</span> (!isSourceGood)
- <span class="enscript-keyword">return</span>;
- <span class="enscript-comment">// Offset the pointers to the current read position in the sample buffer.
- </span> sourceL += m_readIndex;
- sourceR += m_readIndex;
- <span class="enscript-comment">// Get pointers to the destination.
- </span> <span class="enscript-type">float</span>* destinationL = destinationBus->channel(0)->data();
- <span class="enscript-type">float</span>* destinationR = numberOfChannels == 2 ? destinationBus->channel(1)->data() : 0;
- <span class="enscript-type">bool</span> isDestinationGood = destinationL && (numberOfChannels == 1 || destinationR);
- ASSERT(isDestinationGood);
- <span class="enscript-keyword">if</span> (!isDestinationGood)
- <span class="enscript-keyword">return</span>;
- <span class="enscript-keyword">if</span> (m_isGrain)
- readFromBufferWithGrainEnvelope(sourceL, sourceR, destinationL, destinationR, framesToProcess);
- <span class="enscript-keyword">else</span> {
- <span class="enscript-comment">// Simply copy the data from the source buffer to the destination.
- </span> memcpy(destinationL, sourceL, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
- <span class="enscript-keyword">if</span> (numberOfChannels == 2)
- memcpy(destinationR, sourceR, <span class="enscript-keyword">sizeof</span>(<span class="enscript-type">float</span>) * framesToProcess);
- }
- <span class="enscript-comment">// Advance the buffer's read index.
- </span> m_readIndex += framesToProcess;
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::readFromBufferWithGrainEnvelope</span>(<span class="enscript-type">float</span>* sourceL, <span class="enscript-type">float</span>* sourceR, <span class="enscript-type">float</span>* destinationL, <span class="enscript-type">float</span>* destinationR, size_t framesToProcess)
- {
- ASSERT(sourceL && destinationL);
- <span class="enscript-keyword">if</span> (!sourceL || !destinationL)
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-type">int</span> grainFrameLength = static_cast<<span class="enscript-type">int</span>>(m_grainDuration * m_buffer->sampleRate());
- <span class="enscript-type">bool</span> isStereo = sourceR && destinationR;
-
- <span class="enscript-type">int</span> n = framesToProcess;
- <span class="enscript-keyword">while</span> (n--) {
- <span class="enscript-comment">// Apply the grain envelope.
- </span> <span class="enscript-type">float</span> x = static_cast<<span class="enscript-type">float</span>>(m_grainFrameCount) / static_cast<<span class="enscript-type">float</span>>(grainFrameLength);
- m_grainFrameCount++;
- x = min(1.0f, x);
- <span class="enscript-type">float</span> grainEnvelope = sinf(piFloat * x);
-
- *destinationL++ = grainEnvelope * *sourceL++;
- <span class="enscript-keyword">if</span> (isStereo)
- *destinationR++ = grainEnvelope * *sourceR++;
- }
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::reset</span>()
- {
- m_resampler.reset();
- m_readIndex = 0;
- m_grainFrameCount = 0;
- m_lastGain = gain()->value();
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::setBuffer</span>(AudioBuffer* buffer)
- {
- ASSERT(isMainThread());
-
- <span class="enscript-comment">// The context must be locked since changing the buffer can re-configure the number of channels that are output.
- </span> <span class="enscript-reference">AudioContext</span>::AutoLocker contextLocker(context());
-
- <span class="enscript-comment">// This synchronizes with process().
- </span> MutexLocker processLocker(m_processLock);
-
- <span class="enscript-keyword">if</span> (buffer) {
- <span class="enscript-comment">// Do any necesssary re-configuration to the buffer's number of channels.
- </span> <span class="enscript-type">unsigned</span> numberOfChannels = buffer->numberOfChannels();
- m_resampler.configureChannels(numberOfChannels);
- output(0)->setNumberOfChannels(numberOfChannels);
- }
- m_readIndex = 0;
- m_buffer = buffer;
- }
- <span class="enscript-type">unsigned</span> <span class="enscript-function-name">AudioBufferSourceNode::numberOfChannels</span>()
- {
- <span class="enscript-keyword">return</span> output(0)->numberOfChannels();
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteOn</span>(<span class="enscript-type">double</span> when)
- {
- ASSERT(isMainThread());
- <span class="enscript-keyword">if</span> (m_isPlaying)
- <span class="enscript-keyword">return</span>;
- m_isGrain = false;
- m_startTime = when;
- m_readIndex = 0;
- m_isPlaying = true;
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteGrainOn</span>(<span class="enscript-type">double</span> when, <span class="enscript-type">double</span> grainOffset, <span class="enscript-type">double</span> grainDuration)
- {
- ASSERT(isMainThread());
- <span class="enscript-keyword">if</span> (m_isPlaying)
- <span class="enscript-keyword">return</span>;
- <span class="enscript-keyword">if</span> (!buffer())
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-comment">// Do sanity checking of grain parameters versus buffer size.
- </span> <span class="enscript-type">double</span> bufferDuration = buffer()->duration();
- <span class="enscript-keyword">if</span> (grainDuration > bufferDuration)
- <span class="enscript-keyword">return</span>; <span class="enscript-comment">// FIXME: maybe should throw exception - consider in specification.
- </span>
- <span class="enscript-type">double</span> maxGrainOffset = bufferDuration - grainDuration;
- maxGrainOffset = max(0.0, maxGrainOffset);
- grainOffset = max(0.0, grainOffset);
- grainOffset = min(maxGrainOffset, grainOffset);
- m_grainOffset = grainOffset;
- m_grainDuration = grainDuration;
- m_grainFrameCount = 0;
-
- m_isGrain = true;
- m_startTime = when;
- m_readIndex = static_cast<<span class="enscript-type">int</span>>(m_grainOffset * buffer()->sampleRate());
- m_isPlaying = true;
- }
- <span class="enscript-type">void</span> <span class="enscript-function-name">AudioBufferSourceNode::noteOff</span>(<span class="enscript-type">double</span>)
- {
- ASSERT(isMainThread());
- <span class="enscript-keyword">if</span> (!m_isPlaying)
- <span class="enscript-keyword">return</span>;
-
- <span class="enscript-comment">// FIXME: the "when" argument to this method is ignored.
- </span> m_isPlaying = false;
- m_readIndex = 0;
- }
- <span class="enscript-type">double</span> <span class="enscript-function-name">AudioBufferSourceNode::totalPitchRate</span>()
- {
- <span class="enscript-type">double</span> dopplerRate = 1.0;
- <span class="enscript-keyword">if</span> (m_pannerNode.get())
- dopplerRate = m_pannerNode->dopplerRate();
-
- <span class="enscript-comment">// Incorporate buffer's sample-rate versus AudioContext's sample-rate.
- </span> <span class="enscript-comment">// Normally it's not an issue because buffers are loaded at the AudioContext's sample-rate, but we can handle it in any case.
- </span> <span class="enscript-type">double</span> sampleRateFactor = 1.0;
- <span class="enscript-keyword">if</span> (buffer())
- sampleRateFactor = buffer()->sampleRate() / sampleRate();
-
- <span class="enscript-type">double</span> basePitchRate = playbackRate()->value();
- <span class="enscript-type">double</span> totalRate = dopplerRate * sampleRateFactor * basePitchRate;
- <span class="enscript-comment">// Sanity check the total rate. It's very important that the resampler not get any bad rate values.
- </span> totalRate = max(0.0, totalRate);
- totalRate = min(AudioResampler::MaxRate, totalRate);
-
- <span class="enscript-type">bool</span> isTotalRateValid = !isnan(totalRate) && !isinf(totalRate);
- ASSERT(isTotalRateValid);
- <span class="enscript-keyword">if</span> (!isTotalRateValid)
- totalRate = 1.0;
-
- <span class="enscript-keyword">return</span> totalRate;
- }
- } <span class="enscript-comment">// namespace WebCore
- </span>
- #<span class="enscript-reference">endif</span> // <span class="enscript-variable-name">ENABLE</span>(<span class="enscript-variable-name">WEB_AUDIO</span>)
- </pre>
- <hr />
- </body></html>