Changeset 269108 in webkit
- Timestamp:
- Oct 28, 2020 10:40:03 AM (21 months ago)
- Location:
- trunk
- Files:
-
- 12 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/imported/w3c/ChangeLog (modified) (1 diff)
-
LayoutTests/imported/w3c/web-platform-tests/webaudio/the-audio-api/the-convolvernode-interface/transferred-buffer-output-expected.txt (modified) (1 diff)
-
LayoutTests/webaudio/audiobuffer-neuter-expected.txt (modified) (1 diff)
-
LayoutTests/webaudio/audiobuffer-neuter.html (modified) (4 diffs)
-
LayoutTests/webaudio/resources/audio-testing.js (modified) (1 diff)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/Modules/webaudio/AudioBuffer.cpp (modified) (11 diffs)
-
Source/WebCore/Modules/webaudio/AudioBuffer.h (modified) (4 diffs)
-
Source/WebCore/Modules/webaudio/AudioBuffer.idl (modified) (1 diff)
-
Source/WebCore/Modules/webaudio/ConvolverNode.cpp (modified) (1 diff)
-
Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r269104 r269108 1 2020-10-28 Chris Dumez <cdumez@apple.com> 2 3 AudioBuffer channels should be neuterable / detachable 4 https://bugs.webkit.org/show_bug.cgi?id=218286 5 6 Reviewed by Eric Carlson. 7 8 * webaudio/audiobuffer-neuter-expected.txt: 9 * webaudio/audiobuffer-neuter.html: 10 Extend layout test coverage. I have verified that this new version of the test is fully passing in 11 Gecko. In Blink, the initial checks all pass but it then crashes during rendering. 12 13 * webaudio/resources/audio-testing.js: 14 (createConstantBuffer): 15 Improve createConstantBuffer() so that it can construct buffers with multiple channels. 16 1 17 2020-10-28 Philippe Normand <pnormand@igalia.com> 2 18 -
trunk/LayoutTests/imported/w3c/ChangeLog
r269081 r269108 1 2020-10-28 Chris Dumez <cdumez@apple.com> 2 3 AudioBuffer channels should be neuterable / detachable 4 https://bugs.webkit.org/show_bug.cgi?id=218286 5 6 Reviewed by Eric Carlson. 7 8 Rebaseline WPT test that is now passing. This test is also passing in Firefox. It is crashing in Blink. 9 10 * web-platform-tests/webaudio/the-audio-api/the-convolvernode-interface/transferred-buffer-output-expected.txt: 11 1 12 2020-10-27 Chris Dumez <cdumez@apple.com> 2 13 -
trunk/LayoutTests/imported/w3c/web-platform-tests/webaudio/the-audio-api/the-convolvernode-interface/transferred-buffer-output-expected.txt
r268685 r269108 4 4 PASS Audit report 5 5 PASS > [Test Convolver with transferred buffer] Output should be all zeroes 6 FAIL X Convolver channel 0 output[0:1279]: Expected 0 for all values but found 1280 unexpected values: 7 Index Actual 8 [0] 1 9 [1] 2 10 [2] 3 11 [3] 4 12 ...and 1276 more errors. assert_true: expected true got false 13 FAIL X Convolver channel 1 output[0:1279]: Expected 0 for all values but found 1280 unexpected values: 14 Index Actual 15 [0] 2 16 [1] 4 17 [2] 6 18 [3] 8 19 ...and 1276 more errors. assert_true: expected true got false 20 FAIL < [Test Convolver with transferred buffer] 2 out of 2 assertions were failed. assert_true: expected true got false 21 FAIL # AUDIT TASK RUNNER FINISHED: 1 out of 1 tasks were failed. assert_true: expected true got false 6 PASS Convolver channel 0 output[0:1279] contains only the constant 0. 7 PASS Convolver channel 1 output[0:1279] contains only the constant 0. 8 PASS < [Test Convolver with transferred buffer] All assertions passed. (total 2 assertions) 9 PASS # AUDIT TASK RUNNER FINISHED: 1 tasks ran successfully. 22 10 -
trunk/LayoutTests/webaudio/audiobuffer-neuter-expected.txt
r217243 r269108 1 Tests that neutered AudioBuffers do not produce garbage 1 Tests that neutered AudioBuffers produce silence 2 PASS sourceBuffer.length is numberOfFrames 3 PASS sourceBuffer.getChannelData(0).length is 0 4 PASS sourceBuffer.getChannelData(0) === sourceBuffer.getChannelData(0) is true 5 PASS sourceBuffer.getChannelData(0) === originalFirstChannel is true 6 PASS sourceBuffer.getChannelData(1).length is numberOfFrames 7 PASS sourceBuffer.getChannelData(1) === sourceBuffer.getChannelData(1) is true 8 PASS sourceBuffer.getChannelData(1) === originalSecondChannel is true 2 9 PASS Output matches expectations. 3 10 PASS successfullyParsed is true -
trunk/LayoutTests/webaudio/audiobuffer-neuter.html
r267504 r269108 15 15 jsTestIsAsync = true; 16 16 17 var numberOfFrames = sampleRate * lengthInSeconds; 18 context = new OfflineAudioContext(1, numberOfFrames, sampleRate); 19 sourceBuffer = createConstantBuffer(context, numberOfFrames, 0.5); 17 numberOfFrames = sampleRate * lengthInSeconds; 18 context = new OfflineAudioContext(2, numberOfFrames, sampleRate); 19 sourceBuffer = createConstantBuffer(context, numberOfFrames, [0.5, 0.25]); 20 originalFirstChannel = sourceBuffer.getChannelData(0); 21 originalSecondChannel = sourceBuffer.getChannelData(1); 20 22 var data = sourceBuffer.getChannelData(0).buffer; 21 23 … … 33 35 worker.onmessage = workerReply; 34 36 worker.postMessage(data, [data]); 37 38 // The length of the AudioBuffer should be unchanged, even if some of its audio channels buffers 39 // have been detached. 40 shouldBe("sourceBuffer.length", "numberOfFrames"); 41 42 // getChannelData() should return a 0-length array if the channel buffer has been detached. 43 shouldBe("sourceBuffer.getChannelData(0).length", "0"); 44 shouldBeTrue("sourceBuffer.getChannelData(0) === sourceBuffer.getChannelData(0)"); 45 shouldBeTrue("sourceBuffer.getChannelData(0) === originalFirstChannel"); 46 47 // getChannelData() should return accurate channel data for non detached channels. 48 shouldBe("sourceBuffer.getChannelData(1).length", "numberOfFrames"); 49 shouldBeTrue("sourceBuffer.getChannelData(1) === sourceBuffer.getChannelData(1)"); 50 shouldBeTrue("sourceBuffer.getChannelData(1) === originalSecondChannel"); 51 secondChannel = sourceBuffer.getChannelData(1); 52 for (var i = 0; i < numberOfFrames; ++i) { 53 if (secondChannel[i] != 0.25) { 54 testFailed("Second channel contained unexpected value: " + secondChannel[i]); 55 break; 56 } 57 } 35 58 } 36 59 … … 47 70 var renderedBuffer = event.renderedBuffer; 48 71 var numberOfFrames = sampleRate * lengthInSeconds; 49 var expectedBuffer = createConstantBuffer(context, numberOfFrames, 0.5); 72 // We expect silence on both channels. 73 var expectedBuffer = createConstantBuffer(context, numberOfFrames, [0, 0]); 50 74 51 var renderedData = renderedBuffer.getChannelData(0); 52 var expectedData = expectedBuffer.getChannelData(0); 75 for (let channelIndex = 0; channelIndex < 2; channelIndex++) { 76 var renderedData = renderedBuffer.getChannelData(channelIndex); 77 var expectedData = expectedBuffer.getChannelData(channelIndex); 53 78 54 for (var i = 0; i < numberOfFrames; ++i) { 55 if (expectedData[i] != renderedData[i]) { 56 testFailed('expected: ' + expectedData[i] + ' actual: ' + renderedData[i]); 57 finishJSTest(); 79 for (var i = 0; i < numberOfFrames; ++i) { 80 if (expectedData[i] != renderedData[i]) { 81 testFailed('Channel ' + channelIndex + ' - expected: ' + expectedData[i] + ' actual: ' + renderedData[i]); 82 finishJSTest(); 83 } 58 84 } 59 85 } … … 66 92 </head> 67 93 <body onload="runTest()"> 68 <div>Tests that neutered AudioBuffers do not produce garbage</div>94 <div>Tests that neutered AudioBuffers produce silence</div> 69 95 <div id="console"></div> 70 96 </body> -
trunk/LayoutTests/webaudio/resources/audio-testing.js
r205065 r269108 130 130 // Create a buffer of the given length having a constant value. 131 131 function createConstantBuffer(context, sampleFrameLength, constantValue) { 132 var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate); 133 var n = audioBuffer.length; 134 var dataL = audioBuffer.getChannelData(0); 135 136 for (var i = 0; i < n; ++i) 137 dataL[i] = constantValue; 132 let channels; 133 let values; 134 135 if (typeof constantValue === 'number') { 136 channels = 1; 137 values = [constantValue]; 138 } else { 139 channels = constantValue.length; 140 values = constantValue; 141 } 142 143 let audioBuffer = context.createBuffer(channels, sampleFrameLength, context.sampleRate); 144 let n = audioBuffer.length; 145 146 for (let c = 0; c < channels; ++c) { 147 let data = audioBuffer.getChannelData(c); 148 for (let i = 0; i < n; ++i) 149 data[i] = values[c]; 150 } 138 151 139 152 return audioBuffer; -
trunk/Source/WebCore/ChangeLog
r269104 r269108 1 2020-10-28 Chris Dumez <cdumez@apple.com> 2 3 AudioBuffer channels should be neuterable / detachable 4 https://bugs.webkit.org/show_bug.cgi?id=218286 5 6 Reviewed by Eric Carlson. 7 8 AudioBuffer channels should be neuterable / detachable: 9 - https://webaudio.github.io/web-audio-api/#acquire-the-content 10 11 When the one of the channels' buffers gets detached/neutered, the array returned by 12 getChannelData(0) is neutered and thus has zero length. Internally though, if 13 channelData() gets called and ANY of the channels' buffers are detached, we return 14 a new empty array, as per the specification. This makes sure we end up with silence 15 on all channels when any of the channels gets detached, which is consistent with the 16 specification and Firefox (Blink seems to crash). 17 18 To make the AudioBuffer API less error-prone when used natively, I updated length() 19 and duration() to return 0 whenever any of the channels is detached. This is safer 20 since channelData() returns 0-length arrays in this case. The Web API keeps returning 21 the original values though so they rely on new originalLength() / originalDuration() 22 getters. 23 24 No new tests, rebaselined / updated existing tests. 25 26 * Modules/webaudio/AudioBuffer.cpp: 27 (WebCore::AudioBuffer::create): 28 (WebCore::AudioBuffer::AudioBuffer): 29 (WebCore::AudioBuffer::invalidate): 30 (WebCore::AudioBuffer::channelData): 31 (WebCore::AudioBuffer::zero): 32 (WebCore::AudioBuffer::hasDetachedChannelBuffer const): 33 * Modules/webaudio/AudioBuffer.h: 34 (WebCore::AudioBuffer::originalLength const): 35 (WebCore::AudioBuffer::originalDuration const): 36 (WebCore::AudioBuffer::length const): 37 (WebCore::AudioBuffer::duration const): 38 * Modules/webaudio/AudioBuffer.idl: 39 * Modules/webaudio/ConvolverNode.cpp: 40 (WebCore::ConvolverNode::setBuffer): 41 * Modules/webaudio/ScriptProcessorNode.cpp: 42 (WebCore::ScriptProcessorNode::initialize): 43 1 44 2020-10-28 Philippe Normand <pnormand@igalia.com> 2 45 -
trunk/Source/WebCore/Modules/webaudio/AudioBuffer.cpp
r269081 r269108 41 41 namespace WebCore { 42 42 43 RefPtr<AudioBuffer> AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate )43 RefPtr<AudioBuffer> AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, LegacyPreventNeutering preventNeutering) 44 44 { 45 45 if (!BaseAudioContext::isSupportedSampleRate(sampleRate) || !numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels() || !numberOfFrames) 46 46 return nullptr; 47 47 48 auto buffer = adoptRef(*new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate ));49 if (!buffer-> m_length)48 auto buffer = adoptRef(*new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate, preventNeutering)); 49 if (!buffer->originalLength()) 50 50 return nullptr; 51 51 … … 68 68 69 69 auto buffer = adoptRef(*new AudioBuffer(options.numberOfChannels, options.length, options.sampleRate)); 70 if (!buffer-> length())70 if (!buffer->originalLength()) 71 71 return Exception { NotSupportedError, "Channel was not able to be created."_s }; 72 72 … … 82 82 } 83 83 84 AudioBuffer::AudioBuffer(unsigned numberOfChannels, size_t length, float sampleRate )84 AudioBuffer::AudioBuffer(unsigned numberOfChannels, size_t length, float sampleRate, LegacyPreventNeutering preventNeutering) 85 85 : m_sampleRate(sampleRate) 86 , m_ length(length)86 , m_originalLength(length) 87 87 { 88 88 m_channels.reserveCapacity(numberOfChannels); 89 89 90 90 for (unsigned i = 0; i < numberOfChannels; ++i) { 91 auto channelDataArray = Float32Array::tryCreate(m_ length);91 auto channelDataArray = Float32Array::tryCreate(m_originalLength); 92 92 if (!channelDataArray) { 93 93 invalidate(); … … 95 95 } 96 96 97 channelDataArray->setNeuterable(false); 97 if (preventNeutering == LegacyPreventNeutering::Yes) 98 channelDataArray->setNeuterable(false); 99 98 100 m_channels.append(WTFMove(channelDataArray)); 99 101 } … … 103 105 AudioBuffer::AudioBuffer(AudioBus& bus) 104 106 : m_sampleRate(bus.sampleRate()) 105 , m_ length(bus.length())107 , m_originalLength(bus.length()) 106 108 { 107 109 // Copy audio data from the bus to the Float32Arrays we manage. … … 109 111 m_channels.reserveCapacity(numberOfChannels); 110 112 for (unsigned i = 0; i < numberOfChannels; ++i) { 111 auto channelDataArray = Float32Array::tryCreate(m_ length);113 auto channelDataArray = Float32Array::tryCreate(m_originalLength); 112 114 if (!channelDataArray) { 113 115 invalidate(); … … 115 117 } 116 118 117 channelDataArray->setNeuterable(false); 118 channelDataArray->setRange(bus.channel(i)->data(), m_length, 0); 119 channelDataArray->setRange(bus.channel(i)->data(), m_originalLength, 0); 119 120 m_channels.append(WTFMove(channelDataArray)); 120 121 } … … 125 126 { 126 127 releaseMemory(); 127 m_ length = 0;128 m_originalLength = 0; 128 129 } 129 130 … … 160 161 } 161 162 162 Float32Array*AudioBuffer::channelData(unsigned channelIndex)163 RefPtr<Float32Array> AudioBuffer::channelData(unsigned channelIndex) 163 164 { 164 165 if (channelIndex >= m_channels.size()) 165 166 return nullptr; 166 return m_channels[channelIndex].get(); 167 if (hasDetachedChannelBuffer()) 168 return Float32Array::create(0); 169 return m_channels[channelIndex].copyRef(); 167 170 } 168 171 … … 226 229 { 227 230 for (auto& channel : m_channels) 228 channel->zero Range(0, length());231 channel->zeroFill(); 229 232 } 230 233 … … 243 246 } 244 247 248 bool AudioBuffer::hasDetachedChannelBuffer() const 249 { 250 for (auto& channel : m_channels) { 251 if (channel->isNeutered()) 252 return true; 253 } 254 return false; 255 } 256 245 257 } // namespace WebCore 246 258 -
trunk/Source/WebCore/Modules/webaudio/AudioBuffer.h
r269081 r269108 42 42 43 43 class AudioBuffer : public RefCounted<AudioBuffer> { 44 public: 45 static RefPtr<AudioBuffer> create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate); 44 public: 45 enum class LegacyPreventNeutering : bool { No, Yes }; 46 static RefPtr<AudioBuffer> create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, LegacyPreventNeutering = LegacyPreventNeutering::No); 46 47 static ExceptionOr<Ref<AudioBuffer>> create(const AudioBufferOptions&); 47 48 // Returns nullptr if data is not a valid audio file. … … 49 50 50 51 // Format 51 size_t length() const { return m_length; } 52 size_t originalLength() const { return m_originalLength; } 53 double originalDuration() const { return originalLength() / static_cast<double>(sampleRate()); } 54 float sampleRate() const { return m_sampleRate; } 55 56 // The following function may start returning 0 if any of the underlying channel buffers gets detached. 57 size_t length() const { return hasDetachedChannelBuffer() ? 0 : m_originalLength; } 52 58 double duration() const { return length() / static_cast<double>(sampleRate()); } 53 float sampleRate() const { return m_sampleRate; }54 59 55 60 // Channel data access … … 58 63 ExceptionOr<void> copyFromChannel(Ref<Float32Array>&&, unsigned channelNumber, unsigned bufferOffset); 59 64 ExceptionOr<void> copyToChannel(Ref<Float32Array>&&, unsigned channelNumber, unsigned startInChannel); 60 Float32Array* channelData(unsigned channelIndex); 65 66 // Native channel data access. 67 RefPtr<Float32Array> channelData(unsigned channelIndex); 61 68 void zero(); 62 69 … … 71 78 72 79 private: 73 AudioBuffer(unsigned numberOfChannels, size_t length, float sampleRate );80 AudioBuffer(unsigned numberOfChannels, size_t length, float sampleRate, LegacyPreventNeutering = LegacyPreventNeutering::No); 74 81 explicit AudioBuffer(AudioBus&); 75 82 76 83 void invalidate(); 77 84 85 bool hasDetachedChannelBuffer() const; 86 78 87 float m_sampleRate; 79 88 mutable Lock m_channelsLock; 80 size_t m_ length;89 size_t m_originalLength; 81 90 Vector<RefPtr<Float32Array>> m_channels; 82 91 Vector<JSValueInWrappedObject> m_channelWrappers; -
trunk/Source/WebCore/Modules/webaudio/AudioBuffer.idl
r269081 r269108 36 36 ] interface AudioBuffer { 37 37 [EnabledBySetting=ModernUnprefixedWebAudio] constructor(AudioBufferOptions options); 38 readonly attribute unsigned long length; // in sample-frames39 readonly attribute double duration; // in seconds38 [ImplementedAs=originalLength] readonly attribute unsigned long length; // in sample-frames 39 [ImplementedAs=originalDuration] readonly attribute double duration; // in seconds 40 40 readonly attribute float sampleRate; // in sample-frames per second 41 41 readonly attribute unsigned long numberOfChannels; -
trunk/Source/WebCore/Modules/webaudio/ConvolverNode.cpp
r268812 r269108 131 131 return Exception { NotSupportedError, "Buffer should have 1, 2 or 4 channels"_s }; 132 132 133 if (!bufferLength)134 return Exception { NotSupportedError, "Buffer length cannot be 0"_s };135 136 133 // Wrap the AudioBuffer by an AudioBus. It's an efficient pointer set and not a memcpy(). 137 134 // This memory is simply used in the Reverb constructor and no reference to it is kept for later use in that class. -
trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp
r268728 r269108 91 91 // These AudioBuffers will be directly accessed in the main thread by JavaScript. 92 92 for (unsigned i = 0; i < 2; ++i) { 93 auto inputBuffer = m_numberOfInputChannels ? AudioBuffer::create(m_numberOfInputChannels, bufferSize(), sampleRate) : 0; 94 auto outputBuffer = m_numberOfOutputChannels ? AudioBuffer::create(m_numberOfOutputChannels, bufferSize(), sampleRate) : 0; 93 // We prevent neutering the AudioBuffers here since we pass those to JS and reuse them. 94 auto inputBuffer = m_numberOfInputChannels ? AudioBuffer::create(m_numberOfInputChannels, bufferSize(), sampleRate, AudioBuffer::LegacyPreventNeutering::Yes) : 0; 95 auto outputBuffer = m_numberOfOutputChannels ? AudioBuffer::create(m_numberOfOutputChannels, bufferSize(), sampleRate, AudioBuffer::LegacyPreventNeutering::Yes) : 0; 95 96 96 97 m_inputBuffers.append(inputBuffer);
Note: See TracChangeset
for help on using the changeset viewer.