1/* 2 * Copyright (C) 2010, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 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. 12 * 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. 23 */ 24 25#include "config.h" 26 27#if ENABLE(WEB_AUDIO) 28 29#include "AudioBufferSourceNode.h" 30 31#include "AudioContext.h" 32#include "AudioNodeOutput.h" 33#include "AudioUtilities.h" 34#include "FloatConversion.h" 35#include "ScriptCallStack.h" 36#include "ScriptController.h" 37#include "ScriptExecutionContext.h" 38#include <algorithm> 39#include <wtf/MainThread.h> 40#include <wtf/MathExtras.h> 41 42using namespace std; 43 44namespace WebCore { 45 46const double DefaultGrainDuration = 0.020; // 20ms 47 48// Arbitrary upper limit on playback rate. 49// Higher than expected rates can be useful when playing back oversampled buffers 50// to minimize linear interpolation aliasing. 51const double MaxRate = 1024; 52 53PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, float sampleRate) 54{ 55 return adoptRef(new AudioBufferSourceNode(context, sampleRate)); 56} 57 58AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sampleRate) 59 : AudioScheduledSourceNode(context, sampleRate) 60 , m_buffer(0) 61 , m_isLooping(false) 62 , m_loopStart(0) 63 , m_loopEnd(0) 64 , m_virtualReadIndex(0) 65 , m_isGrain(false) 66 , m_grainOffset(0.0) 67 , m_grainDuration(DefaultGrainDuration) 68 , m_lastGain(1.0) 69 , m_pannerNode(0) 70{ 71 setNodeType(NodeTypeAudioBufferSource); 72 73 m_gain = AudioParam::create(context, "gain", 1.0, 0.0, 1.0); 74 m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, 0.0, MaxRate); 75 76 // Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer. 77 addOutput(adoptPtr(new AudioNodeOutput(this, 1))); 78 79 initialize(); 80} 81 82AudioBufferSourceNode::~AudioBufferSourceNode() 83{ 84 clearPannerNode(); 85 uninitialize(); 86} 87 88void AudioBufferSourceNode::process(size_t framesToProcess) 89{ 90 AudioBus* outputBus = output(0)->bus(); 91 92 if (!isInitialized()) { 93 outputBus->zero(); 94 return; 95 } 96 97 // The audio thread can't block on this lock, so we call tryLock() instead. 98 MutexTryLocker tryLocker(m_processLock); 99 if (tryLocker.locked()) { 100 if (!buffer()) { 101 outputBus->zero(); 102 return; 103 } 104 105 // After calling setBuffer() with a buffer having a different number of channels, there can in rare cases be a slight delay 106 // before the output bus is updated to the new number of channels because of use of tryLocks() in the context's updating system. 107 // In this case, if the the buffer has just been changed and we're not quite ready yet, then just output silence. 108 if (numberOfChannels() != buffer()->numberOfChannels()) { 109 outputBus->zero(); 110 return; 111 } 112 113 size_t quantumFrameOffset; 114 size_t bufferFramesToProcess; 115 116 updateSchedulingInfo(framesToProcess, 117 outputBus, 118 quantumFrameOffset, 119 bufferFramesToProcess); 120 121 if (!bufferFramesToProcess) { 122 outputBus->zero(); 123 return; 124 } 125 126 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) 127 m_destinationChannels[i] = outputBus->channel(i)->mutableData(); 128 129 // Render by reading directly from the buffer. 130 if (!renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess)) { 131 outputBus->zero(); 132 return; 133 } 134 135 // Apply the gain (in-place) to the output bus. 136 float totalGain = gain()->value() * m_buffer->gain(); 137 outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); 138 outputBus->clearSilentFlag(); 139 } else { 140 // Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway. 141 outputBus->zero(); 142 } 143} 144 145// Returns true if we're finished. 146bool AudioBufferSourceNode::renderSilenceAndFinishIfNotLooping(AudioBus*, unsigned index, size_t framesToProcess) 147{ 148 if (!loop()) { 149 // If we're not looping, then stop playing when we get to the end. 150 151 if (framesToProcess > 0) { 152 // We're not looping and we've reached the end of the sample data, but we still need to provide more output, 153 // so generate silence for the remaining. 154 for (unsigned i = 0; i < numberOfChannels(); ++i) 155 memset(m_destinationChannels[i] + index, 0, sizeof(float) * framesToProcess); 156 } 157 158 finish(); 159 return true; 160 } 161 return false; 162} 163 164bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destinationFrameOffset, size_t numberOfFrames) 165{ 166 ASSERT(context()->isAudioThread()); 167 168 // Basic sanity checking 169 ASSERT(bus); 170 ASSERT(buffer()); 171 if (!bus || !buffer()) 172 return false; 173 174 unsigned numberOfChannels = this->numberOfChannels(); 175 unsigned busNumberOfChannels = bus->numberOfChannels(); 176 177 bool channelCountGood = numberOfChannels && numberOfChannels == busNumberOfChannels; 178 ASSERT(channelCountGood); 179 if (!channelCountGood) 180 return false; 181 182 // Sanity check destinationFrameOffset, numberOfFrames. 183 size_t destinationLength = bus->length(); 184 185 bool isLengthGood = destinationLength <= 4096 && numberOfFrames <= 4096; 186 ASSERT(isLengthGood); 187 if (!isLengthGood) 188 return false; 189 190 bool isOffsetGood = destinationFrameOffset <= destinationLength && destinationFrameOffset + numberOfFrames <= destinationLength; 191 ASSERT(isOffsetGood); 192 if (!isOffsetGood) 193 return false; 194 195 // Potentially zero out initial frames leading up to the offset. 196 if (destinationFrameOffset) { 197 for (unsigned i = 0; i < numberOfChannels; ++i) 198 memset(m_destinationChannels[i], 0, sizeof(float) * destinationFrameOffset); 199 } 200 201 // Offset the pointers to the correct offset frame. 202 unsigned writeIndex = destinationFrameOffset; 203 204 size_t bufferLength = buffer()->length(); 205 double bufferSampleRate = buffer()->sampleRate(); 206 207 // Avoid converting from time to sample-frames twice by computing 208 // the grain end time first before computing the sample frame. 209 unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength; 210 211 // This is a HACK to allow for HRTF tail-time - avoids glitch at end. 212 // FIXME: implement tailTime for each AudioNode for a more general solution to this problem. 213 // https://bugs.webkit.org/show_bug.cgi?id=77224 214 if (m_isGrain) 215 endFrame += 512; 216 217 // Do some sanity checking. 218 if (endFrame > bufferLength) 219 endFrame = bufferLength; 220 if (m_virtualReadIndex >= endFrame) 221 m_virtualReadIndex = 0; // reset to start 222 223 // If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEnd == 0 implies 224 // that we should use the entire buffer as the loop, otherwise use the loop values in m_loopStart and m_loopEnd. 225 double virtualEndFrame = endFrame; 226 double virtualDeltaFrames = endFrame; 227 228 if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && m_loopEnd > 0 && m_loopStart < m_loopEnd) { 229 // Convert from seconds to sample-frames. 230 double loopStartFrame = m_loopStart * buffer()->sampleRate(); 231 double loopEndFrame = m_loopEnd * buffer()->sampleRate(); 232 233 virtualEndFrame = min(loopEndFrame, virtualEndFrame); 234 virtualDeltaFrames = virtualEndFrame - loopStartFrame; 235 } 236 237 238 double pitchRate = totalPitchRate(); 239 240 // Sanity check that our playback rate isn't larger than the loop size. 241 if (pitchRate >= virtualDeltaFrames) 242 return false; 243 244 // Get local copy. 245 double virtualReadIndex = m_virtualReadIndex; 246 247 // Render loop - reading from the source buffer to the destination using linear interpolation. 248 int framesToProcess = numberOfFrames; 249 250 const float** sourceChannels = m_sourceChannels.get(); 251 float** destinationChannels = m_destinationChannels.get(); 252 253 // Optimize for the very common case of playing back with pitchRate == 1. 254 // We can avoid the linear interpolation. 255 if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex) 256 && virtualDeltaFrames == floor(virtualDeltaFrames) 257 && virtualEndFrame == floor(virtualEndFrame)) { 258 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); 259 unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames); 260 endFrame = static_cast<unsigned>(virtualEndFrame); 261 while (framesToProcess > 0) { 262 int framesToEnd = endFrame - readIndex; 263 int framesThisTime = min(framesToProcess, framesToEnd); 264 framesThisTime = max(0, framesThisTime); 265 266 for (unsigned i = 0; i < numberOfChannels; ++i) 267 memcpy(destinationChannels[i] + writeIndex, sourceChannels[i] + readIndex, sizeof(float) * framesThisTime); 268 269 writeIndex += framesThisTime; 270 readIndex += framesThisTime; 271 framesToProcess -= framesThisTime; 272 273 // Wrap-around. 274 if (readIndex >= endFrame) { 275 readIndex -= deltaFrames; 276 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) 277 break; 278 } 279 } 280 virtualReadIndex = readIndex; 281 } else { 282 while (framesToProcess--) { 283 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); 284 double interpolationFactor = virtualReadIndex - readIndex; 285 286 // For linear interpolation we need the next sample-frame too. 287 unsigned readIndex2 = readIndex + 1; 288 if (readIndex2 >= bufferLength) { 289 if (loop()) { 290 // Make sure to wrap around at the end of the buffer. 291 readIndex2 = static_cast<unsigned>(virtualReadIndex + 1 - virtualDeltaFrames); 292 } else 293 readIndex2 = readIndex; 294 } 295 296 // Final sanity check on buffer access. 297 // FIXME: as an optimization, try to get rid of this inner-loop check and put assertions and guards before the loop. 298 if (readIndex >= bufferLength || readIndex2 >= bufferLength) 299 break; 300 301 // Linear interpolation. 302 for (unsigned i = 0; i < numberOfChannels; ++i) { 303 float* destination = destinationChannels[i]; 304 const float* source = sourceChannels[i]; 305 306 double sample1 = source[readIndex]; 307 double sample2 = source[readIndex2]; 308 double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2; 309 310 destination[writeIndex] = narrowPrecisionToFloat(sample); 311 } 312 writeIndex++; 313 314 virtualReadIndex += pitchRate; 315 316 // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point. 317 if (virtualReadIndex >= virtualEndFrame) { 318 virtualReadIndex -= virtualDeltaFrames; 319 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) 320 break; 321 } 322 } 323 } 324 325 bus->clearSilentFlag(); 326 327 m_virtualReadIndex = virtualReadIndex; 328 329 return true; 330} 331 332 333void AudioBufferSourceNode::reset() 334{ 335 m_virtualReadIndex = 0; 336 m_lastGain = gain()->value(); 337} 338 339bool AudioBufferSourceNode::setBuffer(AudioBuffer* buffer) 340{ 341 ASSERT(isMainThread()); 342 343 // The context must be locked since changing the buffer can re-configure the number of channels that are output. 344 AudioContext::AutoLocker contextLocker(context()); 345 346 // This synchronizes with process(). 347 MutexLocker processLocker(m_processLock); 348 349 if (buffer) { 350 // Do any necesssary re-configuration to the buffer's number of channels. 351 unsigned numberOfChannels = buffer->numberOfChannels(); 352 353 if (numberOfChannels > AudioContext::maxNumberOfChannels()) 354 return false; 355 356 output(0)->setNumberOfChannels(numberOfChannels); 357 358 m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]); 359 m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]); 360 361 for (unsigned i = 0; i < numberOfChannels; ++i) 362 m_sourceChannels[i] = buffer->getChannelData(i)->data(); 363 } 364 365 m_virtualReadIndex = 0; 366 m_buffer = buffer; 367 368 return true; 369} 370 371unsigned AudioBufferSourceNode::numberOfChannels() 372{ 373 return output(0)->numberOfChannels(); 374} 375 376void AudioBufferSourceNode::startGrain(double when, double grainOffset) 377{ 378 // Duration of 0 has special value, meaning calculate based on the entire buffer's duration. 379 startGrain(when, grainOffset, 0); 380} 381 382void AudioBufferSourceNode::startGrain(double when, double grainOffset, double grainDuration) 383{ 384 ASSERT(isMainThread()); 385 386 if (ScriptController::processingUserGesture()) 387 context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction); 388 389 if (m_playbackState != UNSCHEDULED_STATE) 390 return; 391 392 if (!buffer()) 393 return; 394 395 // Do sanity checking of grain parameters versus buffer size. 396 double bufferDuration = buffer()->duration(); 397 398 grainOffset = max(0.0, grainOffset); 399 grainOffset = min(bufferDuration, grainOffset); 400 m_grainOffset = grainOffset; 401 402 // Handle default/unspecified duration. 403 double maxDuration = bufferDuration - grainOffset; 404 if (!grainDuration) 405 grainDuration = maxDuration; 406 407 grainDuration = max(0.0, grainDuration); 408 grainDuration = min(maxDuration, grainDuration); 409 m_grainDuration = grainDuration; 410 411 m_isGrain = true; 412 m_startTime = when; 413 414 // We call timeToSampleFrame here since at playbackRate == 1 we don't want to go through linear interpolation 415 // at a sub-sample position since it will degrade the quality. 416 // When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer. 417 // Since playbackRate == 1 is very common, it's worth considering quality. 418 m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate()); 419 420 m_playbackState = SCHEDULED_STATE; 421} 422 423#if ENABLE(LEGACY_WEB_AUDIO) 424void AudioBufferSourceNode::noteGrainOn(double when, double grainOffset, double grainDuration) 425{ 426 startGrain(when, grainOffset, grainDuration); 427} 428#endif 429 430double AudioBufferSourceNode::totalPitchRate() 431{ 432 double dopplerRate = 1.0; 433 if (m_pannerNode) 434 dopplerRate = m_pannerNode->dopplerRate(); 435 436 // Incorporate buffer's sample-rate versus AudioContext's sample-rate. 437 // Normally it's not an issue because buffers are loaded at the AudioContext's sample-rate, but we can handle it in any case. 438 double sampleRateFactor = 1.0; 439 if (buffer()) 440 sampleRateFactor = buffer()->sampleRate() / sampleRate(); 441 442 double basePitchRate = playbackRate()->value(); 443 444 double totalRate = dopplerRate * sampleRateFactor * basePitchRate; 445 446 // Sanity check the total rate. It's very important that the resampler not get any bad rate values. 447 totalRate = max(0.0, totalRate); 448 if (!totalRate) 449 totalRate = 1; // zero rate is considered illegal 450 totalRate = min(MaxRate, totalRate); 451 452 bool isTotalRateValid = !std::isnan(totalRate) && !std::isinf(totalRate); 453 ASSERT(isTotalRateValid); 454 if (!isTotalRateValid) 455 totalRate = 1.0; 456 457 return totalRate; 458} 459 460bool AudioBufferSourceNode::looping() 461{ 462 static bool firstTime = true; 463 if (firstTime && context() && context()->scriptExecutionContext()) { 464 context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead."); 465 firstTime = false; 466 } 467 468 return m_isLooping; 469} 470 471void AudioBufferSourceNode::setLooping(bool looping) 472{ 473 static bool firstTime = true; 474 if (firstTime && context() && context()->scriptExecutionContext()) { 475 context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead."); 476 firstTime = false; 477 } 478 479 m_isLooping = looping; 480} 481 482bool AudioBufferSourceNode::propagatesSilence() const 483{ 484 return !isPlayingOrScheduled() || hasFinished() || !m_buffer; 485} 486 487void AudioBufferSourceNode::setPannerNode(PannerNode* pannerNode) 488{ 489 if (m_pannerNode != pannerNode && !hasFinished()) { 490 if (pannerNode) 491 pannerNode->ref(AudioNode::RefTypeConnection); 492 if (m_pannerNode) 493 m_pannerNode->deref(AudioNode::RefTypeConnection); 494 495 m_pannerNode = pannerNode; 496 } 497} 498 499void AudioBufferSourceNode::clearPannerNode() 500{ 501 if (m_pannerNode) { 502 m_pannerNode->deref(AudioNode::RefTypeConnection); 503 m_pannerNode = 0; 504 } 505} 506 507void AudioBufferSourceNode::finish() 508{ 509 clearPannerNode(); 510 ASSERT(!m_pannerNode); 511 AudioScheduledSourceNode::finish(); 512} 513 514} // namespace WebCore 515 516#endif // ENABLE(WEB_AUDIO) 517