Changeset 179838 in webkit
- Timestamp:
- Feb 9, 2015 11:45:54 AM (9 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r179837 r179838 1 2015-02-09 Jer Noble <jer.noble@apple.com> 2 3 [WebAudio] AudioBufferSourceNodes should accurately play backwards if given a negative playbackRate. 4 https://bugs.webkit.org/show_bug.cgi?id=140955 5 6 Reviewed by Eric Carlson. 7 8 * webaudio/audiobuffersource-negative-playbackrate-expected.txt: Added. 9 * webaudio/audiobuffersource-negative-playbackrate-interpolated-expected.txt: Added. 10 * webaudio/audiobuffersource-negative-playbackrate-interpolated-loop-expected.txt: Added. 11 * webaudio/audiobuffersource-negative-playbackrate-interpolated-loop.html: Added. 12 * webaudio/audiobuffersource-negative-playbackrate-interpolated.html: 13 * webaudio/audiobuffersource-negative-playbackrate-loop-expected.txt: Added. 14 * webaudio/audiobuffersource-negative-playbackrate-loop.html: Added. 15 * webaudio/audiobuffersource-negative-playbackrate.html: 16 * webaudio/resources/audiobuffersource-testing.js: 17 (createRamp): 18 19 Get rid of extra HRTF padding as it's now unnecessary. 20 21 * webaudio/resources/note-grain-on-testing.js: 22 (createSignalBuffer): 23 (verifyStartAndEndFrames): 24 1 25 2015-02-09 David Kilzer <ddkilzer@apple.com> 2 26 -
trunk/LayoutTests/webaudio/resources/audiobuffersource-testing.js
r133101 r179838 6 6 for (var i = 0; i < sampleFrameLength; ++i) 7 7 channelData[i] = i; 8 9 return audioBuffer; 10 } 11 12 function createRamp(context, startValue, endValue, numberOfSamples) { 13 var audioBuffer = context.createBuffer(1, numberOfSamples, context.sampleRate); 14 var channelData = audioBuffer.getChannelData(0); 15 16 var delta = (endValue - startValue) / (numberOfSamples - 1); 17 var nextValue = startValue; 18 19 for (var i = 0; i < numberOfSamples; ++i) { 20 channelData[i] = nextValue; 21 nextValue += delta; 22 } 8 23 9 24 return audioBuffer; -
trunk/LayoutTests/webaudio/resources/note-grain-on-testing.js
r179638 r179838 1 1 var sampleRate = 44100.0; 2 3 // HRTF extra frames. This is a magic constant currently in4 // AudioBufferSourceNode::process that always extends the5 // duration by this number of samples. See bug 772246 // (https://bugs.webkit.org/show_bug.cgi?id=77224).7 var extraFramesHRTF = 512;8 2 9 3 // How many grains to play. … … 13 7 var duration = 0.01; 14 8 15 // Time step between the start of each grain. We need to add a little 16 // bit of silence so we can detect grain boundaries and also account 17 // for the extra frames for HRTF. 18 var timeStep = duration + .005 + extraFramesHRTF / sampleRate; 9 // Time step between the start of each grain. 10 var timeStep = duration + .005; 19 11 20 12 // Time step between the start for each grain. … … 34 26 // grain offsets and durations. Need to include the extra frames 35 27 // for HRTF. The additional 1 is for any round-off errors. 36 var signalLength = Math.floor(1 + extraFramesHRTF +sampleRate * (numberOfTests * grainOffsetStep + duration));28 var signalLength = Math.floor(1 + sampleRate * (numberOfTests * grainOffsetStep + duration)); 37 29 38 30 var buffer = context.createBuffer(2, signalLength, sampleRate); … … 136 128 // The end point is the duration, plus the extra frames 137 129 // for HRTF. 138 var expectedEnd = ex traFramesHRTF + expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate);130 var expectedEnd = expectedStart + grainLengthInSampleFrames(k * grainOffsetStep, duration, sampleRate); 139 131 140 132 if (startFrames[k] != expectedStart) { -
trunk/Source/WebCore/ChangeLog
r179829 r179838 1 2015-02-09 Jer Noble <jer.noble@apple.com> 2 3 [WebAudio] AudioBufferSourceNodes should accurately play backwards if given a negative playbackRate. 4 https://bugs.webkit.org/show_bug.cgi?id=140955 5 6 Reviewed by Eric Carlson. 7 8 Tests: webaudio/audiobuffersource-negative-playbackrate-interpolated.html 9 webaudio/audiobuffersource-negative-playbackrate.html 10 11 Add support for playing an AudioBufferSourceNode at a negative playbackRate. Change the meaning of 12 start() to set the initial playback position at the end of the play range if the rate of playback 13 is negtive. 14 15 * Modules/webaudio/AudioBufferSourceNode.cpp: 16 (WebCore::AudioBufferSourceNode::AudioBufferSourceNode): Allow the playbackRate AudioParam to range from [-32, 32]. 17 (WebCore::AudioBufferSourceNode::renderFromBuffer): Change variable names from "start" and "end" to "min" and "max" 18 for clarity. Add a non-interpolated and interpolated render step for negative playback. 19 (WebCore::AudioBufferSourceNode::start): Drive-by fix: default value of grainDuration is not 0.02. 20 (WebCore::AudioBufferSourceNode::startPlaying): Start playing at the end of the buffer for negative playback. 21 (WebCore::AudioBufferSourceNode::totalPitchRate): Allow the pitch to be negative. 22 1 23 2015-02-09 Darin Adler <darin@apple.com> 2 24 -
trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
r179638 r179838 71 71 72 72 m_gain = AudioParam::create(context, "gain", 1.0, 0.0, 1.0); 73 m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, 0.0, MaxRate);73 m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, -MaxRate, MaxRate); 74 74 75 75 // Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer. … … 201 201 size_t bufferLength = buffer()->length(); 202 202 double bufferSampleRate = buffer()->sampleRate(); 203 double pitchRate = totalPitchRate(); 204 bool reverse = pitchRate < 0; 203 205 204 206 // Avoid converting from time to sample-frames twice by computing 205 207 // the grain end time first before computing the sample frame. 206 unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength; 207 208 // This is a HACK to allow for HRTF tail-time - avoids glitch at end. 209 // FIXME: implement tailTime for each AudioNode for a more general solution to this problem. 210 // https://bugs.webkit.org/show_bug.cgi?id=77224 208 unsigned maxFrame; 211 209 if (m_isGrain) 212 endFrame += 512; 210 maxFrame = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate); 211 else 212 maxFrame = bufferLength; 213 213 214 214 // Do some sanity checking. 215 if (endFrame > bufferLength) 216 endFrame = bufferLength; 217 if (m_virtualReadIndex >= endFrame) 215 if (maxFrame > bufferLength) 216 maxFrame = bufferLength; 217 if (reverse && m_virtualReadIndex <= 0) 218 m_virtualReadIndex = maxFrame - 1; 219 else if (!reverse && m_virtualReadIndex >= maxFrame) 218 220 m_virtualReadIndex = 0; // reset to start 219 221 220 222 // If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEnd == 0 implies 221 223 // that we should use the entire buffer as the loop, otherwise use the loop values in m_loopStart and m_loopEnd. 222 double virtualEndFrame = endFrame; 223 double virtualDeltaFrames = endFrame; 224 double virtualMaxFrame = maxFrame; 225 double virtualMinFrame = 0; 226 double virtualDeltaFrames = maxFrame; 224 227 225 228 if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && m_loopEnd > 0 && m_loopStart < m_loopEnd) { 226 229 // Convert from seconds to sample-frames. 227 double loopStartFrame = m_loopStart * buffer()->sampleRate(); 228 double loopEndFrame = m_loopEnd * buffer()->sampleRate(); 229 230 virtualEndFrame = std::min(loopEndFrame, virtualEndFrame); 231 virtualDeltaFrames = virtualEndFrame - loopStartFrame; 232 } 233 234 235 double pitchRate = totalPitchRate(); 230 double loopMinFrame = m_loopStart * buffer()->sampleRate(); 231 double loopMaxFrame = m_loopEnd * buffer()->sampleRate(); 232 233 virtualMaxFrame = std::min(loopMaxFrame, virtualMaxFrame); 234 virtualMinFrame = std::max(loopMinFrame, virtualMinFrame); 235 virtualDeltaFrames = virtualMaxFrame - virtualMinFrame; 236 } 237 236 238 237 239 // Sanity check that our playback rate isn't larger than the loop size. 238 if ( pitchRate>= virtualDeltaFrames)240 if (fabs(pitchRate) >= virtualDeltaFrames) 239 241 return false; 240 242 … … 242 244 double virtualReadIndex = m_virtualReadIndex; 243 245 246 bool needsInterpolation = virtualReadIndex != floor(virtualReadIndex) 247 || virtualDeltaFrames != floor(virtualDeltaFrames) 248 || virtualMaxFrame != floor(virtualMaxFrame) 249 || virtualMinFrame != floor(virtualMinFrame); 250 244 251 // Render loop - reading from the source buffer to the destination using linear interpolation. 245 252 int framesToProcess = numberOfFrames; … … 250 257 // Optimize for the very common case of playing back with pitchRate == 1. 251 258 // We can avoid the linear interpolation. 252 if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex) 253 && virtualDeltaFrames == floor(virtualDeltaFrames) 254 && virtualEndFrame == floor(virtualEndFrame)) { 259 if (pitchRate == 1 && !needsInterpolation) { 255 260 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); 256 261 unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames); 257 endFrame = static_cast<unsigned>(virtualEndFrame);262 maxFrame = static_cast<unsigned>(virtualMaxFrame); 258 263 while (framesToProcess > 0) { 259 int framesToEnd = endFrame - readIndex;264 int framesToEnd = maxFrame - readIndex; 260 265 int framesThisTime = std::min(framesToProcess, framesToEnd); 261 266 framesThisTime = std::max(0, framesThisTime); … … 269 274 270 275 // Wrap-around. 271 if (readIndex >= endFrame) {276 if (readIndex >= maxFrame) { 272 277 readIndex -= deltaFrames; 273 278 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) … … 276 281 } 277 282 virtualReadIndex = readIndex; 283 } else if (pitchRate == -1 && !needsInterpolation) { 284 int readIndex = static_cast<int>(virtualReadIndex); 285 int deltaFrames = static_cast<int>(virtualDeltaFrames); 286 int minFrame = static_cast<int>(virtualMinFrame) - 1; 287 while (framesToProcess > 0) { 288 int framesToEnd = readIndex - minFrame; 289 int framesThisTime = std::min<int>(framesToProcess, framesToEnd); 290 framesThisTime = std::max<int>(0, framesThisTime); 291 292 while (framesThisTime--) { 293 for (unsigned i = 0; i < numberOfChannels; ++i) { 294 float* destination = destinationChannels[i]; 295 const float* source = sourceChannels[i]; 296 297 destination[writeIndex] = source[readIndex]; 298 } 299 300 ++writeIndex; 301 --readIndex; 302 --framesToProcess; 303 } 304 305 // Wrap-around. 306 if (readIndex <= minFrame) { 307 readIndex += deltaFrames; 308 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) 309 break; 310 } 311 } 312 virtualReadIndex = readIndex; 313 } else if (!pitchRate) { 314 unsigned readIndex = static_cast<unsigned>(virtualReadIndex); 315 316 for (unsigned i = 0; i < numberOfChannels; ++i) 317 std::fill_n(destinationChannels[i], framesToProcess, sourceChannels[i][readIndex]); 318 } else if (reverse) { 319 unsigned maxFrame = static_cast<unsigned>(virtualMaxFrame); 320 unsigned minFrame = static_cast<unsigned>(floorf(virtualMinFrame)); 321 322 while (framesToProcess--) { 323 unsigned readIndex = static_cast<unsigned>(floorf(virtualReadIndex)); 324 double interpolationFactor = virtualReadIndex - readIndex; 325 326 unsigned readIndex2 = readIndex + 1; 327 if (readIndex2 >= maxFrame) 328 readIndex2 = loop() ? minFrame : maxFrame - 1; 329 330 // Linear interpolation. 331 for (unsigned i = 0; i < numberOfChannels; ++i) { 332 float* destination = destinationChannels[i]; 333 const float* source = sourceChannels[i]; 334 335 double sample1 = source[readIndex]; 336 double sample2 = source[readIndex2]; 337 double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2; 338 339 destination[writeIndex] = narrowPrecisionToFloat(sample); 340 } 341 342 writeIndex++; 343 344 virtualReadIndex += pitchRate; 345 346 // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point. 347 if (virtualReadIndex < virtualMinFrame) { 348 virtualReadIndex += virtualDeltaFrames; 349 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) 350 break; 351 } 352 } 278 353 } else { 279 354 while (framesToProcess--) { … … 312 387 313 388 // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point. 314 if (virtualReadIndex >= virtual EndFrame) {389 if (virtualReadIndex >= virtualMaxFrame) { 315 390 virtualReadIndex -= virtualDeltaFrames; 316 391 if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess)) … … 383 458 void AudioBufferSourceNode::start(double when, double grainOffset, ExceptionCode& ec) 384 459 { 385 startPlaying(Partial, when, grainOffset, 0, ec);460 startPlaying(Partial, when, grainOffset, buffer() ? buffer()->duration() - grainOffset : 0, ec); 386 461 } 387 462 … … 432 507 } else { 433 508 m_grainOffset = 0.0; 434 m_grainDuration = DefaultGrainDuration;509 m_grainDuration = buffer()->duration(); 435 510 } 436 511 … … 441 516 // When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer. 442 517 // Since playbackRate == 1 is very common, it's worth considering quality. 443 m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate()); 518 if (totalPitchRate() < 0) 519 m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, buffer()->sampleRate()) - 1; 520 else 521 m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate()); 444 522 445 523 m_playbackState = SCHEDULED_STATE; … … 472 550 double totalRate = dopplerRate * sampleRateFactor * basePitchRate; 473 551 474 // Sanity check the total rate. It's very important that the resampler not get any bad rate values. 475 totalRate = std::max(0.0, totalRate); 476 if (!totalRate) 477 totalRate = 1; // zero rate is considered illegal 478 totalRate = std::min(MaxRate, totalRate); 552 totalRate = std::max(-MaxRate, std::min(MaxRate, totalRate)); 479 553 480 554 bool isTotalRateValid = !std::isnan(totalRate) && !std::isinf(totalRate);
Note: See TracChangeset
for help on using the changeset viewer.