OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010, Google Inc. All rights reserved. | 2 * Copyright (C) 2010, Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 23 matching lines...) Expand all Loading... |
34 #include "platform/audio/AudioUtilities.h" | 34 #include "platform/audio/AudioUtilities.h" |
35 #include "wtf/MathExtras.h" | 35 #include "wtf/MathExtras.h" |
36 #include "wtf/PtrUtil.h" | 36 #include "wtf/PtrUtil.h" |
37 #include <algorithm> | 37 #include <algorithm> |
38 | 38 |
39 namespace blink { | 39 namespace blink { |
40 | 40 |
41 const double DefaultGrainDuration = 0.020; // 20ms | 41 const double DefaultGrainDuration = 0.020; // 20ms |
42 | 42 |
43 // Arbitrary upper limit on playback rate. | 43 // Arbitrary upper limit on playback rate. |
44 // Higher than expected rates can be useful when playing back oversampled buffer
s | 44 // Higher than expected rates can be useful when playing back oversampled |
45 // to minimize linear interpolation aliasing. | 45 // buffers to minimize linear interpolation aliasing. |
46 const double MaxRate = 1024; | 46 const double MaxRate = 1024; |
47 | 47 |
48 // Number of extra frames to use when determining if a source node can be stoppe
d. This should be | 48 // Number of extra frames to use when determining if a source node can be |
49 // at least one rendering quantum, but we add one more quantum for good measure.
This doesn't need | 49 // stopped. This should be at least one rendering quantum, but we add one more |
50 // to be extra precise, just more than one rendering quantum. See |handleStoppa
bleSourceNode()|. | 50 // quantum for good measure. This doesn't need to be extra precise, just more |
51 // FIXME: Expose the rendering quantum somehow instead of hardwiring a value her
e. | 51 // than one rendering quantum. See |handleStoppableSourceNode()|. |
| 52 // FIXME: Expose the rendering quantum somehow instead of hardwiring a value |
| 53 // here. |
52 const int kExtraStopFrames = 256; | 54 const int kExtraStopFrames = 256; |
53 | 55 |
54 AudioBufferSourceHandler::AudioBufferSourceHandler( | 56 AudioBufferSourceHandler::AudioBufferSourceHandler( |
55 AudioNode& node, | 57 AudioNode& node, |
56 float sampleRate, | 58 float sampleRate, |
57 AudioParamHandler& playbackRate, | 59 AudioParamHandler& playbackRate, |
58 AudioParamHandler& detune) | 60 AudioParamHandler& detune) |
59 : AudioScheduledSourceHandler(NodeTypeAudioBufferSource, node, sampleRate), | 61 : AudioScheduledSourceHandler(NodeTypeAudioBufferSource, node, sampleRate), |
60 m_buffer(nullptr), | 62 m_buffer(nullptr), |
61 m_playbackRate(playbackRate), | 63 m_playbackRate(playbackRate), |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 } | 100 } |
99 | 101 |
100 // The audio thread can't block on this lock, so we call tryLock() instead. | 102 // The audio thread can't block on this lock, so we call tryLock() instead. |
101 MutexTryLocker tryLocker(m_processLock); | 103 MutexTryLocker tryLocker(m_processLock); |
102 if (tryLocker.locked()) { | 104 if (tryLocker.locked()) { |
103 if (!buffer()) { | 105 if (!buffer()) { |
104 outputBus->zero(); | 106 outputBus->zero(); |
105 return; | 107 return; |
106 } | 108 } |
107 | 109 |
108 // After calling setBuffer() with a buffer having a different number of chan
nels, there can in rare cases be a slight delay | 110 // After calling setBuffer() with a buffer having a different number of |
109 // before the output bus is updated to the new number of channels because of
use of tryLocks() in the context's updating system. | 111 // channels, there can in rare cases be a slight delay before the output bus |
110 // In this case, if the the buffer has just been changed and we're not quite
ready yet, then just output silence. | 112 // is updated to the new number of channels because of use of tryLocks() in |
| 113 // the context's updating system. In this case, if the the buffer has just |
| 114 // been changed and we're not quite ready yet, then just output silence. |
111 if (numberOfChannels() != buffer()->numberOfChannels()) { | 115 if (numberOfChannels() != buffer()->numberOfChannels()) { |
112 outputBus->zero(); | 116 outputBus->zero(); |
113 return; | 117 return; |
114 } | 118 } |
115 | 119 |
116 size_t quantumFrameOffset; | 120 size_t quantumFrameOffset; |
117 size_t bufferFramesToProcess; | 121 size_t bufferFramesToProcess; |
118 | 122 |
119 updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, | 123 updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, |
120 bufferFramesToProcess); | 124 bufferFramesToProcess); |
121 | 125 |
122 if (!bufferFramesToProcess) { | 126 if (!bufferFramesToProcess) { |
123 outputBus->zero(); | 127 outputBus->zero(); |
124 return; | 128 return; |
125 } | 129 } |
126 | 130 |
127 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) | 131 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) |
128 m_destinationChannels[i] = outputBus->channel(i)->mutableData(); | 132 m_destinationChannels[i] = outputBus->channel(i)->mutableData(); |
129 | 133 |
130 // Render by reading directly from the buffer. | 134 // Render by reading directly from the buffer. |
131 if (!renderFromBuffer(outputBus, quantumFrameOffset, | 135 if (!renderFromBuffer(outputBus, quantumFrameOffset, |
132 bufferFramesToProcess)) { | 136 bufferFramesToProcess)) { |
133 outputBus->zero(); | 137 outputBus->zero(); |
134 return; | 138 return; |
135 } | 139 } |
136 | 140 |
137 outputBus->clearSilentFlag(); | 141 outputBus->clearSilentFlag(); |
138 } else { | 142 } else { |
139 // Too bad - the tryLock() failed. We must be in the middle of changing buf
fers and were already outputting silence anyway. | 143 // Too bad - the tryLock() failed. We must be in the middle of changing |
| 144 // buffers and were already outputting silence anyway. |
140 outputBus->zero(); | 145 outputBus->zero(); |
141 } | 146 } |
142 } | 147 } |
143 | 148 |
144 // Returns true if we're finished. | 149 // Returns true if we're finished. |
145 bool AudioBufferSourceHandler::renderSilenceAndFinishIfNotLooping( | 150 bool AudioBufferSourceHandler::renderSilenceAndFinishIfNotLooping( |
146 AudioBus*, | 151 AudioBus*, |
147 unsigned index, | 152 unsigned index, |
148 size_t framesToProcess) { | 153 size_t framesToProcess) { |
149 if (!loop()) { | 154 if (!loop()) { |
150 // If we're not looping, then stop playing when we get to the end. | 155 // If we're not looping, then stop playing when we get to the end. |
151 | 156 |
152 if (framesToProcess > 0) { | 157 if (framesToProcess > 0) { |
153 // We're not looping and we've reached the end of the sample data, but we
still need to provide more output, | 158 // We're not looping and we've reached the end of the sample data, but we |
154 // so generate silence for the remaining. | 159 // still need to provide more output, so generate silence for the |
| 160 // remaining. |
155 for (unsigned i = 0; i < numberOfChannels(); ++i) | 161 for (unsigned i = 0; i < numberOfChannels(); ++i) |
156 memset(m_destinationChannels[i] + index, 0, | 162 memset(m_destinationChannels[i] + index, 0, |
157 sizeof(float) * framesToProcess); | 163 sizeof(float) * framesToProcess); |
158 } | 164 } |
159 | 165 |
160 finish(); | 166 finish(); |
161 return true; | 167 return true; |
162 } | 168 } |
163 return false; | 169 return false; |
164 } | 170 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 double bufferSampleRate = buffer()->sampleRate(); | 220 double bufferSampleRate = buffer()->sampleRate(); |
215 | 221 |
216 // Avoid converting from time to sample-frames twice by computing | 222 // Avoid converting from time to sample-frames twice by computing |
217 // the grain end time first before computing the sample frame. | 223 // the grain end time first before computing the sample frame. |
218 unsigned endFrame = | 224 unsigned endFrame = |
219 m_isGrain ? AudioUtilities::timeToSampleFrame( | 225 m_isGrain ? AudioUtilities::timeToSampleFrame( |
220 m_grainOffset + m_grainDuration, bufferSampleRate) | 226 m_grainOffset + m_grainDuration, bufferSampleRate) |
221 : bufferLength; | 227 : bufferLength; |
222 | 228 |
223 // This is a HACK to allow for HRTF tail-time - avoids glitch at end. | 229 // This is a HACK to allow for HRTF tail-time - avoids glitch at end. |
224 // FIXME: implement tailTime for each AudioNode for a more general solution to
this problem. | 230 // FIXME: implement tailTime for each AudioNode for a more general solution to |
225 // https://bugs.webkit.org/show_bug.cgi?id=77224 | 231 // this problem, https://bugs.webkit.org/show_bug.cgi?id=77224 |
226 if (m_isGrain) | 232 if (m_isGrain) |
227 endFrame += 512; | 233 endFrame += 512; |
228 | 234 |
229 // Do some sanity checking. | 235 // Do some sanity checking. |
230 if (endFrame > bufferLength) | 236 if (endFrame > bufferLength) |
231 endFrame = bufferLength; | 237 endFrame = bufferLength; |
232 | 238 |
233 // If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEn
d == 0 implies | 239 // If the .loop attribute is true, then values of |
234 // that we should use the entire buffer as the loop, otherwise use the loop va
lues in m_loopStart and m_loopEnd. | 240 // m_loopStart == 0 && m_loopEnd == 0 implies that we should use the entire |
| 241 // buffer as the loop, otherwise use the loop values in m_loopStart and |
| 242 // m_loopEnd. |
235 double virtualEndFrame = endFrame; | 243 double virtualEndFrame = endFrame; |
236 double virtualDeltaFrames = endFrame; | 244 double virtualDeltaFrames = endFrame; |
237 | 245 |
238 if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && | 246 if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && |
239 m_loopEnd > 0 && m_loopStart < m_loopEnd) { | 247 m_loopEnd > 0 && m_loopStart < m_loopEnd) { |
240 // Convert from seconds to sample-frames. | 248 // Convert from seconds to sample-frames. |
241 double loopStartFrame = m_loopStart * buffer()->sampleRate(); | 249 double loopStartFrame = m_loopStart * buffer()->sampleRate(); |
242 double loopEndFrame = m_loopEnd * buffer()->sampleRate(); | 250 double loopEndFrame = m_loopEnd * buffer()->sampleRate(); |
243 | 251 |
244 virtualEndFrame = std::min(loopEndFrame, virtualEndFrame); | 252 virtualEndFrame = std::min(loopEndFrame, virtualEndFrame); |
245 virtualDeltaFrames = virtualEndFrame - loopStartFrame; | 253 virtualDeltaFrames = virtualEndFrame - loopStartFrame; |
246 } | 254 } |
247 | 255 |
248 // If we're looping and the offset (virtualReadIndex) is past the end of the l
oop, wrap back to | 256 // If we're looping and the offset (virtualReadIndex) is past the end of the |
249 // the beginning of the loop. For other cases, nothing needs to be done. | 257 // loop, wrap back to the beginning of the loop. For other cases, nothing |
| 258 // needs to be done. |
250 if (loop() && m_virtualReadIndex >= virtualEndFrame) { | 259 if (loop() && m_virtualReadIndex >= virtualEndFrame) { |
251 m_virtualReadIndex = | 260 m_virtualReadIndex = |
252 (m_loopStart < 0) ? 0 : (m_loopStart * buffer()->sampleRate()); | 261 (m_loopStart < 0) ? 0 : (m_loopStart * buffer()->sampleRate()); |
253 m_virtualReadIndex = | 262 m_virtualReadIndex = |
254 std::min(m_virtualReadIndex, static_cast<double>(bufferLength - 1)); | 263 std::min(m_virtualReadIndex, static_cast<double>(bufferLength - 1)); |
255 } | 264 } |
256 | 265 |
257 double computedPlaybackRate = computePlaybackRate(); | 266 double computedPlaybackRate = computePlaybackRate(); |
258 | 267 |
259 // Sanity check that our playback rate isn't larger than the loop size. | 268 // Sanity check that our playback rate isn't larger than the loop size. |
260 if (computedPlaybackRate > virtualDeltaFrames) | 269 if (computedPlaybackRate > virtualDeltaFrames) |
261 return false; | 270 return false; |
262 | 271 |
263 // Get local copy. | 272 // Get local copy. |
264 double virtualReadIndex = m_virtualReadIndex; | 273 double virtualReadIndex = m_virtualReadIndex; |
265 | 274 |
266 // Render loop - reading from the source buffer to the destination using linea
r interpolation. | 275 // Render loop - reading from the source buffer to the destination using |
| 276 // linear interpolation. |
267 int framesToProcess = numberOfFrames; | 277 int framesToProcess = numberOfFrames; |
268 | 278 |
269 const float** sourceChannels = m_sourceChannels.get(); | 279 const float** sourceChannels = m_sourceChannels.get(); |
270 float** destinationChannels = m_destinationChannels.get(); | 280 float** destinationChannels = m_destinationChannels.get(); |
271 | 281 |
272 DCHECK_GE(virtualReadIndex, 0); | 282 DCHECK_GE(virtualReadIndex, 0); |
273 DCHECK_GE(virtualDeltaFrames, 0); | 283 DCHECK_GE(virtualDeltaFrames, 0); |
274 DCHECK_GE(virtualEndFrame, 0); | 284 DCHECK_GE(virtualEndFrame, 0); |
275 | 285 |
276 // Optimize for the very common case of playing back with computedPlaybackRate
== 1. | 286 // Optimize for the very common case of playing back with |
277 // We can avoid the linear interpolation. | 287 // computedPlaybackRate == 1. We can avoid the linear interpolation. |
278 if (computedPlaybackRate == 1 && | 288 if (computedPlaybackRate == 1 && |
279 virtualReadIndex == floor(virtualReadIndex) && | 289 virtualReadIndex == floor(virtualReadIndex) && |
280 virtualDeltaFrames == floor(virtualDeltaFrames) && | 290 virtualDeltaFrames == floor(virtualDeltaFrames) && |
281 virtualEndFrame == floor(virtualEndFrame)) { | 291 virtualEndFrame == floor(virtualEndFrame)) { |
282 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); | 292 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); |
283 unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames); | 293 unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames); |
284 endFrame = static_cast<unsigned>(virtualEndFrame); | 294 endFrame = static_cast<unsigned>(virtualEndFrame); |
285 while (framesToProcess > 0) { | 295 while (framesToProcess > 0) { |
286 int framesToEnd = endFrame - readIndex; | 296 int framesToEnd = endFrame - readIndex; |
287 int framesThisTime = std::min(framesToProcess, framesToEnd); | 297 int framesThisTime = std::min(framesToProcess, framesToEnd); |
288 framesThisTime = std::max(0, framesThisTime); | 298 framesThisTime = std::max(0, framesThisTime); |
289 | 299 |
290 DCHECK_LE(writeIndex + framesThisTime, destinationLength); | 300 DCHECK_LE(writeIndex + framesThisTime, destinationLength); |
291 DCHECK_LE(readIndex + framesThisTime, bufferLength); | 301 DCHECK_LE(readIndex + framesThisTime, bufferLength); |
292 | 302 |
293 for (unsigned i = 0; i < numberOfChannels; ++i) | 303 for (unsigned i = 0; i < numberOfChannels; ++i) |
294 memcpy(destinationChannels[i] + writeIndex, | 304 memcpy(destinationChannels[i] + writeIndex, |
295 sourceChannels[i] + readIndex, sizeof(float) * framesThisTime); | 305 sourceChannels[i] + readIndex, sizeof(float) * framesThisTime); |
296 | 306 |
297 writeIndex += framesThisTime; | 307 writeIndex += framesThisTime; |
298 readIndex += framesThisTime; | 308 readIndex += framesThisTime; |
299 framesToProcess -= framesThisTime; | 309 framesToProcess -= framesThisTime; |
300 | 310 |
301 // It can happen that framesThisTime is 0. DCHECK that we will actually ex
it the loop in | 311 // It can happen that framesThisTime is 0. DCHECK that we will actually |
302 // this case. framesThisTime is 0 only if readIndex >= endFrame; | 312 // exit the loop in this case. framesThisTime is 0 only if |
| 313 // readIndex >= endFrame; |
303 DCHECK(framesThisTime ? true : readIndex >= endFrame); | 314 DCHECK(framesThisTime ? true : readIndex >= endFrame); |
304 | 315 |
305 // Wrap-around. | 316 // Wrap-around. |
306 if (readIndex >= endFrame) { | 317 if (readIndex >= endFrame) { |
307 readIndex -= deltaFrames; | 318 readIndex -= deltaFrames; |
308 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, | 319 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, |
309 framesToProcess)) | 320 framesToProcess)) |
310 break; | 321 break; |
311 } | 322 } |
312 } | 323 } |
313 virtualReadIndex = readIndex; | 324 virtualReadIndex = readIndex; |
314 } else { | 325 } else { |
315 while (framesToProcess--) { | 326 while (framesToProcess--) { |
316 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); | 327 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); |
317 double interpolationFactor = virtualReadIndex - readIndex; | 328 double interpolationFactor = virtualReadIndex - readIndex; |
318 | 329 |
319 // For linear interpolation we need the next sample-frame too. | 330 // For linear interpolation we need the next sample-frame too. |
320 unsigned readIndex2 = readIndex + 1; | 331 unsigned readIndex2 = readIndex + 1; |
321 if (readIndex2 >= bufferLength) { | 332 if (readIndex2 >= bufferLength) { |
322 if (loop()) { | 333 if (loop()) { |
323 // Make sure to wrap around at the end of the buffer. | 334 // Make sure to wrap around at the end of the buffer. |
324 readIndex2 = | 335 readIndex2 = |
325 static_cast<unsigned>(virtualReadIndex + 1 - virtualDeltaFrames); | 336 static_cast<unsigned>(virtualReadIndex + 1 - virtualDeltaFrames); |
326 } else { | 337 } else { |
327 readIndex2 = readIndex; | 338 readIndex2 = readIndex; |
328 } | 339 } |
329 } | 340 } |
330 | 341 |
331 // Final sanity check on buffer access. | 342 // Final sanity check on buffer access. |
332 // FIXME: as an optimization, try to get rid of this inner-loop check and
put assertions and guards before the loop. | 343 // FIXME: as an optimization, try to get rid of this inner-loop check and |
| 344 // put assertions and guards before the loop. |
333 if (readIndex >= bufferLength || readIndex2 >= bufferLength) | 345 if (readIndex >= bufferLength || readIndex2 >= bufferLength) |
334 break; | 346 break; |
335 | 347 |
336 // Linear interpolation. | 348 // Linear interpolation. |
337 for (unsigned i = 0; i < numberOfChannels; ++i) { | 349 for (unsigned i = 0; i < numberOfChannels; ++i) { |
338 float* destination = destinationChannels[i]; | 350 float* destination = destinationChannels[i]; |
339 const float* source = sourceChannels[i]; | 351 const float* source = sourceChannels[i]; |
340 | 352 |
341 double sample1 = source[readIndex]; | 353 double sample1 = source[readIndex]; |
342 double sample2 = source[readIndex2]; | 354 double sample2 = source[readIndex2]; |
343 double sample = (1.0 - interpolationFactor) * sample1 + | 355 double sample = (1.0 - interpolationFactor) * sample1 + |
344 interpolationFactor * sample2; | 356 interpolationFactor * sample2; |
345 | 357 |
346 destination[writeIndex] = clampTo<float>(sample); | 358 destination[writeIndex] = clampTo<float>(sample); |
347 } | 359 } |
348 writeIndex++; | 360 writeIndex++; |
349 | 361 |
350 virtualReadIndex += computedPlaybackRate; | 362 virtualReadIndex += computedPlaybackRate; |
351 | 363 |
352 // Wrap-around, retaining sub-sample position since virtualReadIndex is fl
oating-point. | 364 // Wrap-around, retaining sub-sample position since virtualReadIndex is |
| 365 // floating-point. |
353 if (virtualReadIndex >= virtualEndFrame) { | 366 if (virtualReadIndex >= virtualEndFrame) { |
354 virtualReadIndex -= virtualDeltaFrames; | 367 virtualReadIndex -= virtualDeltaFrames; |
355 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, | 368 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, |
356 framesToProcess)) | 369 framesToProcess)) |
357 break; | 370 break; |
358 } | 371 } |
359 } | 372 } |
360 } | 373 } |
361 | 374 |
362 bus->clearSilentFlag(); | 375 bus->clearSilentFlag(); |
363 | 376 |
364 m_virtualReadIndex = virtualReadIndex; | 377 m_virtualReadIndex = virtualReadIndex; |
365 | 378 |
366 return true; | 379 return true; |
367 } | 380 } |
368 | 381 |
369 void AudioBufferSourceHandler::setBuffer(AudioBuffer* buffer, | 382 void AudioBufferSourceHandler::setBuffer(AudioBuffer* buffer, |
370 ExceptionState& exceptionState) { | 383 ExceptionState& exceptionState) { |
371 DCHECK(isMainThread()); | 384 DCHECK(isMainThread()); |
372 | 385 |
373 if (m_buffer) { | 386 if (m_buffer) { |
374 exceptionState.throwDOMException( | 387 exceptionState.throwDOMException( |
375 InvalidStateError, | 388 InvalidStateError, |
376 "Cannot set buffer after it has been already been set"); | 389 "Cannot set buffer after it has been already been set"); |
377 return; | 390 return; |
378 } | 391 } |
379 | 392 |
380 // The context must be locked since changing the buffer can re-configure the n
umber of channels that are output. | 393 // The context must be locked since changing the buffer can re-configure the |
| 394 // number of channels that are output. |
381 BaseAudioContext::AutoLocker contextLocker(context()); | 395 BaseAudioContext::AutoLocker contextLocker(context()); |
382 | 396 |
383 // This synchronizes with process(). | 397 // This synchronizes with process(). |
384 MutexLocker processLocker(m_processLock); | 398 MutexLocker processLocker(m_processLock); |
385 | 399 |
386 if (buffer) { | 400 if (buffer) { |
387 // Do any necesssary re-configuration to the buffer's number of channels. | 401 // Do any necesssary re-configuration to the buffer's number of channels. |
388 unsigned numberOfChannels = buffer->numberOfChannels(); | 402 unsigned numberOfChannels = buffer->numberOfChannels(); |
389 | 403 |
390 // This should not be possible since AudioBuffers can't be created with too
many channels | 404 // This should not be possible since AudioBuffers can't be created with too |
391 // either. | 405 // many channels either. |
392 if (numberOfChannels > BaseAudioContext::maxNumberOfChannels()) { | 406 if (numberOfChannels > BaseAudioContext::maxNumberOfChannels()) { |
393 exceptionState.throwDOMException( | 407 exceptionState.throwDOMException( |
394 NotSupportedError, ExceptionMessages::indexOutsideRange( | 408 NotSupportedError, ExceptionMessages::indexOutsideRange( |
395 "number of input channels", numberOfChannels, | 409 "number of input channels", numberOfChannels, |
396 1u, ExceptionMessages::InclusiveBound, | 410 1u, ExceptionMessages::InclusiveBound, |
397 BaseAudioContext::maxNumberOfChannels(), | 411 BaseAudioContext::maxNumberOfChannels(), |
398 ExceptionMessages::InclusiveBound)); | 412 ExceptionMessages::InclusiveBound)); |
399 return; | 413 return; |
400 } | 414 } |
401 | 415 |
402 output(0).setNumberOfChannels(numberOfChannels); | 416 output(0).setNumberOfChannels(numberOfChannels); |
403 | 417 |
404 m_sourceChannels = wrapArrayUnique(new const float*[numberOfChannels]); | 418 m_sourceChannels = wrapArrayUnique(new const float*[numberOfChannels]); |
405 m_destinationChannels = wrapArrayUnique(new float*[numberOfChannels]); | 419 m_destinationChannels = wrapArrayUnique(new float*[numberOfChannels]); |
406 | 420 |
407 for (unsigned i = 0; i < numberOfChannels; ++i) | 421 for (unsigned i = 0; i < numberOfChannels; ++i) |
408 m_sourceChannels[i] = buffer->getChannelData(i)->data(); | 422 m_sourceChannels[i] = buffer->getChannelData(i)->data(); |
409 | 423 |
410 // If this is a grain (as set by a previous call to start()), validate the g
rain parameters | 424 // If this is a grain (as set by a previous call to start()), validate the |
411 // now since it wasn't validated when start was called (because there was no
buffer then). | 425 // grain parameters now since it wasn't validated when start was called |
| 426 // (because there was no buffer then). |
412 if (m_isGrain) | 427 if (m_isGrain) |
413 clampGrainParameters(buffer); | 428 clampGrainParameters(buffer); |
414 } | 429 } |
415 | 430 |
416 m_virtualReadIndex = 0; | 431 m_virtualReadIndex = 0; |
417 m_buffer = buffer; | 432 m_buffer = buffer; |
418 } | 433 } |
419 | 434 |
420 unsigned AudioBufferSourceHandler::numberOfChannels() { | 435 unsigned AudioBufferSourceHandler::numberOfChannels() { |
421 return output(0).numberOfChannels(); | 436 return output(0).numberOfChannels(); |
422 } | 437 } |
423 | 438 |
424 void AudioBufferSourceHandler::clampGrainParameters(const AudioBuffer* buffer) { | 439 void AudioBufferSourceHandler::clampGrainParameters(const AudioBuffer* buffer) { |
425 DCHECK(buffer); | 440 DCHECK(buffer); |
426 | 441 |
427 // We have a buffer so we can clip the offset and duration to lie within the b
uffer. | 442 // We have a buffer so we can clip the offset and duration to lie within the |
| 443 // buffer. |
428 double bufferDuration = buffer->duration(); | 444 double bufferDuration = buffer->duration(); |
429 | 445 |
430 m_grainOffset = clampTo(m_grainOffset, 0.0, bufferDuration); | 446 m_grainOffset = clampTo(m_grainOffset, 0.0, bufferDuration); |
431 | 447 |
432 // If the duration was not explicitly given, use the buffer duration to set th
e grain | 448 // If the duration was not explicitly given, use the buffer duration to set |
433 // duration. Otherwise, we want to use the user-specified value, of course. | 449 // the grain duration. Otherwise, we want to use the user-specified value, of |
| 450 // course. |
434 if (!m_isDurationGiven) | 451 if (!m_isDurationGiven) |
435 m_grainDuration = bufferDuration - m_grainOffset; | 452 m_grainDuration = bufferDuration - m_grainOffset; |
436 | 453 |
437 if (m_isDurationGiven && loop()) { | 454 if (m_isDurationGiven && loop()) { |
438 // We're looping a grain with a grain duration specified. Schedule the loop
to stop after | 455 // We're looping a grain with a grain duration specified. Schedule the loop |
439 // grainDuration seconds after starting, possibly running the loop multiple
times if | 456 // to stop after grainDuration seconds after starting, possibly running the |
440 // grainDuration is larger than the buffer duration. The net effect is as if
the user called | 457 // loop multiple times if grainDuration is larger than the buffer duration. |
441 // stop(when + grainDuration). | 458 // The net effect is as if the user called stop(when + grainDuration). |
442 m_grainDuration = | 459 m_grainDuration = |
443 clampTo(m_grainDuration, 0.0, std::numeric_limits<double>::infinity()); | 460 clampTo(m_grainDuration, 0.0, std::numeric_limits<double>::infinity()); |
444 m_endTime = m_startTime + m_grainDuration; | 461 m_endTime = m_startTime + m_grainDuration; |
445 } else { | 462 } else { |
446 m_grainDuration = | 463 m_grainDuration = |
447 clampTo(m_grainDuration, 0.0, bufferDuration - m_grainOffset); | 464 clampTo(m_grainDuration, 0.0, bufferDuration - m_grainOffset); |
448 } | 465 } |
449 | 466 |
450 // We call timeToSampleFrame here since at playbackRate == 1 we don't want to
go through | 467 // We call timeToSampleFrame here since at playbackRate == 1 we don't want to |
451 // linear interpolation at a sub-sample position since it will degrade the qua
lity. When | 468 // go through linear interpolation at a sub-sample position since it will |
452 // aligned to the sample-frame the playback will be identical to the PCM data
stored in the | 469 // degrade the quality. When aligned to the sample-frame the playback will be |
453 // buffer. Since playbackRate == 1 is very common, it's worth considering qual
ity. | 470 // identical to the PCM data stored in the buffer. Since playbackRate == 1 is |
| 471 // very common, it's worth considering quality. |
454 m_virtualReadIndex = | 472 m_virtualReadIndex = |
455 AudioUtilities::timeToSampleFrame(m_grainOffset, buffer->sampleRate()); | 473 AudioUtilities::timeToSampleFrame(m_grainOffset, buffer->sampleRate()); |
456 } | 474 } |
457 | 475 |
458 void AudioBufferSourceHandler::start(double when, | 476 void AudioBufferSourceHandler::start(double when, |
459 ExceptionState& exceptionState) { | 477 ExceptionState& exceptionState) { |
460 AudioScheduledSourceHandler::start(when, exceptionState); | 478 AudioScheduledSourceHandler::start(when, exceptionState); |
461 } | 479 } |
462 | 480 |
463 void AudioBufferSourceHandler::start(double when, | 481 void AudioBufferSourceHandler::start(double when, |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 "duration", grainDuration, 0.0)); | 528 "duration", grainDuration, 0.0)); |
511 return; | 529 return; |
512 } | 530 } |
513 | 531 |
514 // The node is started. Add a reference to keep us alive so that audio | 532 // The node is started. Add a reference to keep us alive so that audio |
515 // will eventually get played even if Javascript should drop all references | 533 // will eventually get played even if Javascript should drop all references |
516 // to this node. The reference will get dropped when the source has finished | 534 // to this node. The reference will get dropped when the source has finished |
517 // playing. | 535 // playing. |
518 context()->notifySourceNodeStartedProcessing(node()); | 536 context()->notifySourceNodeStartedProcessing(node()); |
519 | 537 |
520 // This synchronizes with process(). updateSchedulingInfo will read some of th
e variables being | 538 // This synchronizes with process(). updateSchedulingInfo will read some of |
521 // set here. | 539 // the variables being set here. |
522 MutexLocker processLocker(m_processLock); | 540 MutexLocker processLocker(m_processLock); |
523 | 541 |
524 m_isDurationGiven = isDurationGiven; | 542 m_isDurationGiven = isDurationGiven; |
525 m_isGrain = true; | 543 m_isGrain = true; |
526 m_grainOffset = grainOffset; | 544 m_grainOffset = grainOffset; |
527 m_grainDuration = grainDuration; | 545 m_grainDuration = grainDuration; |
528 | 546 |
529 // If |when| < currentTime, the source must start now according to the spec. | 547 // If |when| < currentTime, the source must start now according to the spec. |
530 // So just set startTime to currentTime in this case to start the source now. | 548 // So just set startTime to currentTime in this case to start the source now. |
531 m_startTime = std::max(when, context()->currentTime()); | 549 m_startTime = std::max(when, context()->currentTime()); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 m_minPlaybackRate = std::min(finalPlaybackRate, m_minPlaybackRate); | 589 m_minPlaybackRate = std::min(finalPlaybackRate, m_minPlaybackRate); |
572 | 590 |
573 return finalPlaybackRate; | 591 return finalPlaybackRate; |
574 } | 592 } |
575 | 593 |
576 bool AudioBufferSourceHandler::propagatesSilence() const { | 594 bool AudioBufferSourceHandler::propagatesSilence() const { |
577 return !isPlayingOrScheduled() || hasFinished() || !m_buffer; | 595 return !isPlayingOrScheduled() || hasFinished() || !m_buffer; |
578 } | 596 } |
579 | 597 |
580 void AudioBufferSourceHandler::handleStoppableSourceNode() { | 598 void AudioBufferSourceHandler::handleStoppableSourceNode() { |
581 // If the source node is not looping, and we have a buffer, we can determine w
hen the source | 599 // If the source node is not looping, and we have a buffer, we can determine |
582 // would stop playing. This is intended to handle the (uncommon) scenario whe
re start() has | 600 // when the source would stop playing. This is intended to handle the |
583 // been called but is never connected to the destination (directly or indirect
ly). By stopping | 601 // (uncommon) scenario where start() has been called but is never connected to |
584 // the node, the node can be collected. Otherwise, the node will never get co
llected, leaking | 602 // the destination (directly or indirectly). By stopping the node, the node |
| 603 // can be collected. Otherwise, the node will never get collected, leaking |
585 // memory. | 604 // memory. |
586 // | 605 // |
587 // If looping was ever done (m_didSetLooping = true), give up. We can't easil
y determine how | 606 // If looping was ever done (m_didSetLooping = true), give up. We can't |
588 // long we looped so we don't know the actual duration thus far, so don't try
to do anything | 607 // easily determine how long we looped so we don't know the actual duration |
589 // fancy. | 608 // thus far, so don't try to do anything fancy. |
590 if (!m_didSetLooping && buffer() && isPlayingOrScheduled() && | 609 if (!m_didSetLooping && buffer() && isPlayingOrScheduled() && |
591 m_minPlaybackRate > 0) { | 610 m_minPlaybackRate > 0) { |
592 // Adjust the duration to include the playback rate. Only need to account fo
r rate < 1 | 611 // Adjust the duration to include the playback rate. Only need to account |
593 // which makes the sound last longer. For rate >= 1, the source stops soone
r, but that's | 612 // for rate < 1 which makes the sound last longer. For rate >= 1, the |
594 // ok. | 613 // source stops sooner, but that's ok. |
595 double actualDuration = buffer()->duration() / m_minPlaybackRate; | 614 double actualDuration = buffer()->duration() / m_minPlaybackRate; |
596 | 615 |
597 double stopTime = m_startTime + actualDuration; | 616 double stopTime = m_startTime + actualDuration; |
598 | 617 |
599 // See crbug.com/478301. If a source node is started via start(), the source
may not start | 618 // See crbug.com/478301. If a source node is started via start(), the source |
600 // at that time but one quantum (128 frames) later. But we compute the stop
time based on | 619 // may not start at that time but one quantum (128 frames) later. But we |
601 // the start time and the duration, so we end up stopping one quantum early.
Thus, add a | 620 // compute the stop time based on the start time and the duration, so we end |
602 // little extra time; we just need to stop the source sometime after it shou
ld have stopped | 621 // up stopping one quantum early. Thus, add a little extra time; we just |
603 // if it hadn't already. We don't need to be super precise on when to stop. | 622 // need to stop the source sometime after it should have stopped if it |
| 623 // hadn't already. We don't need to be super precise on when to stop. |
604 double extraStopTime = | 624 double extraStopTime = |
605 kExtraStopFrames / static_cast<double>(context()->sampleRate()); | 625 kExtraStopFrames / static_cast<double>(context()->sampleRate()); |
606 | 626 |
607 stopTime += extraStopTime; | 627 stopTime += extraStopTime; |
608 if (context()->currentTime() > stopTime) { | 628 if (context()->currentTime() > stopTime) { |
609 // The context time has passed the time when the source nodes should have
stopped | 629 // The context time has passed the time when the source nodes should have |
610 // playing. Stop the node now and deref it. (But don't run the onEnded eve
nt because the | 630 // stopped playing. Stop the node now and deref it. (But don't run the |
611 // source never actually played.) | 631 // onEnded event because the source never actually played.) |
612 finishWithoutOnEnded(); | 632 finishWithoutOnEnded(); |
613 } | 633 } |
614 } | 634 } |
615 } | 635 } |
616 | 636 |
617 // ---------------------------------------------------------------- | 637 // ---------------------------------------------------------------- |
618 AudioBufferSourceNode::AudioBufferSourceNode(BaseAudioContext& context) | 638 AudioBufferSourceNode::AudioBufferSourceNode(BaseAudioContext& context) |
619 : AudioScheduledSourceNode(context), | 639 : AudioScheduledSourceNode(context), |
620 m_playbackRate(AudioParam::create(context, | 640 m_playbackRate(AudioParam::create(context, |
621 ParamTypeAudioBufferSourcePlaybackRate, | 641 ParamTypeAudioBufferSourcePlaybackRate, |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
735 | 755 |
736 void AudioBufferSourceNode::start(double when, | 756 void AudioBufferSourceNode::start(double when, |
737 double grainOffset, | 757 double grainOffset, |
738 double grainDuration, | 758 double grainDuration, |
739 ExceptionState& exceptionState) { | 759 ExceptionState& exceptionState) { |
740 audioBufferSourceHandler().start(when, grainOffset, grainDuration, | 760 audioBufferSourceHandler().start(when, grainOffset, grainDuration, |
741 exceptionState); | 761 exceptionState); |
742 } | 762 } |
743 | 763 |
744 } // namespace blink | 764 } // namespace blink |
OLD | NEW |