WebAudio: limit output level to 0db
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Oct 2012 15:51:25 +0000 (15:51 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Oct 2012 15:51:25 +0000 (15:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=95792
<rdar://problem/11966135>

Reviewed by Chris Rogers.

.:

Add a manual test to determine that output volume has been limited to 0db.

* ManualTests/webaudio/limit-level-0db.html: Added.

Source/WebCore:

Clamp the output buffer data to the range of [-1,1], which limits
output volume to 0db. This ensures that malicious or poorly-written
pages will not be able to blow through the system volume limit by
creating >0db buffers and effects.

No new tests; added ManualTests/webaudio/limit-level-0db.html.

Clamp the output vector to values of [-1,1]:
* platform/audio/mac/AudioDestinationMac.cpp:
(WebCore::AudioDestinationMac::render):

Add a VectorMath wrapper for vDSP_clip to provide accelerated vector threshold operations:
* platform/audio/VectorMath.h:
* platform/audio/VectorMath.cpp:
(VectorMath):
(WebCore::VectorMath::vclip):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@131459 268f45cc-cd09-0410-ab3c-d52691b4dbfc

ChangeLog
ManualTests/webaudio/limit-level-0db.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/audio/VectorMath.cpp
Source/WebCore/platform/audio/VectorMath.h
Source/WebCore/platform/audio/mac/AudioDestinationMac.cpp

index 59c83a9824e55ede32ffd4262771981b9ebdce06..56c945760c7f649b580b23ed74e1230a8f7ba482 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2012-10-15  Jer Noble  <jer.noble@apple.com>
+
+        WebAudio: limit output level to 0db
+        https://bugs.webkit.org/show_bug.cgi?id=95792
+        <rdar://problem/11966135>
+
+        Reviewed by Chris Rogers.
+
+        Add a manual test to determine that output volume has been limited to 0db.
+
+        * ManualTests/webaudio/limit-level-0db.html: Added.
+
 2012-10-16  Simon Hausmann  <simon.hausmann@digia.com>
 
         [Qt] Fix support for silent builds
diff --git a/ManualTests/webaudio/limit-level-0db.html b/ManualTests/webaudio/limit-level-0db.html
new file mode 100644 (file)
index 0000000..426103b
--- /dev/null
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Web Audio Sample</title>
+    <script>
+        var context = new webkitAudioContext();
+        var gainNode = context.createGainNode();
+        gainNode.connect(context.destination);
+        gainNode.gain.value = 2;
+
+        var frequency = 1000;
+        var duration = 1000;
+    
+        function playNormal() {
+            var oscillator = context.createOscillator();
+            oscillator.frequency.value = frequency;
+            oscillator.type = 0;
+            oscillator.connect(context.destination);
+            oscillator.noteOn(0);
+            setTimeout(function(){ oscillator.disconnect(); }, duration);
+        }
+        
+        function playLoud() {
+            var oscillator = context.createOscillator();
+            oscillator.frequency.value = frequency;
+            oscillator.type = 0;
+            oscillator.connect(gainNode);
+            oscillator.noteOn(0);
+            setTimeout(function(){ oscillator.disconnect(); }, duration);
+        }
+    </script>
+</head>
+<body>
+    <p>
+        This tests that output audio is clamped to 0db maximum.  Press each button below in turn.  The apparent volume for both should be equal, even though the second button plays with 2x gain of the first. The resulting waveforms are not equal, however, as the second button audio is clipped to a square wave shape.
+    </p>
+    <button onclick="playNormal()">normal volume</button><button onclick="playLoud()">double volume</button>
+</body>
+</html>
index 6c2602b276e61feda96824ae103fefa79b3b7f0a..98009221cbbab7246393482d3fba1aa0d7b914fd 100644 (file)
@@ -1,3 +1,28 @@
+2012-10-15  Jer Noble  <jer.noble@apple.com>
+
+        WebAudio: limit output level to 0db
+        https://bugs.webkit.org/show_bug.cgi?id=95792
+        <rdar://problem/11966135>
+
+        Reviewed by Chris Rogers.
+
+        Clamp the output buffer data to the range of [-1,1], which limits
+        output volume to 0db. This ensures that malicious or poorly-written
+        pages will not be able to blow through the system volume limit by
+        creating >0db buffers and effects.
+
+        No new tests; added ManualTests/webaudio/limit-level-0db.html.
+
+        Clamp the output vector to values of [-1,1]:
+        * platform/audio/mac/AudioDestinationMac.cpp:
+        (WebCore::AudioDestinationMac::render):
+
+        Add a VectorMath wrapper for vDSP_clip to provide accelerated vector threshold operations:
+        * platform/audio/VectorMath.h:
+        * platform/audio/VectorMath.cpp:
+        (VectorMath):
+        (WebCore::VectorMath::vclip):
+
 2012-10-15  Vsevolod Vlasov  <vsevik@chromium.org>
 
         Web Inspector: Extract domain specific editing handling logic from UISourceCode (step 2).
index 3457995fad2cf1438f8683ce535260943b5e8acc..bb20ceee62e4bbfe0f02047e99a92e899b00ccfc 100644 (file)
@@ -111,6 +111,11 @@ void vsvesq(const float* sourceP, int sourceStride, float* sumP, size_t framesTo
 {
     vDSP_svesq(const_cast<float*>(sourceP), sourceStride, sumP, framesToProcess);
 }
+
+void vclip(const float* sourceP, int sourceStride, const float* lowThresholdP, const float* highThresholdP, float* destP, int destStride, size_t framesToProcess)
+{
+    vDSP_vclip(const_cast<float*>(sourceP), sourceStride, const_cast<float*>(lowThresholdP), const_cast<float*>(highThresholdP), destP, destStride, framesToProcess);
+}
 #else
 
 void vsma(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess)
@@ -642,6 +647,22 @@ void vmaxmgv(const float* sourceP, int sourceStride, float* maxP, size_t framesT
     ASSERT(maxP);
     *maxP = max;
 }
+
+void vclip(const float* sourceP, int sourceStride, const float* lowThresholdP, const float* highThresholdP, float* destP, int destStride, size_t framesToProcess)
+{
+    int n = framesToProcess;
+    float lowThreshold = *lowThresholdP;
+    float highThreshold = *highThresholdP;
+
+    // FIXME: Optimize for SSE2.
+    // FIXME: Optimize for NEON.
+    while (n--) {
+        *destP = std::max(std::min(*sourceP, highThreshold), lowThreshold);
+        sourceP += sourceStride;
+        destP += destStride;
+    }
+}
+
 #endif // OS(DARWIN)
 
 } // namespace VectorMath
index 7022519204106706dd93c3bf85c8bd2096087a02..863bc4be75ed17bfb7b9e717ca8a39b04e114df0 100644 (file)
@@ -49,6 +49,9 @@ void vmul(const float* source1P, int sourceStride1, const float* source2P, int s
 // Multiplies two complex vectors.
 void zvmul(const float* real1P, const float* imag1P, const float* real2P, const float* imag2P, float* realDestP, float* imagDestP, size_t framesToProcess);
 
+// Copies elements while clipping values to the threshold inputs.
+void vclip(const float* sourceP, int sourceStride, const float* lowThresholdP, const float* highThresholdP, float* destP, int destStride, size_t framesToProcess);
+
 } // namespace VectorMath
 
 } // namespace WebCore
index 95d2eb33e443991fe9fe7de61f516275b77076fc..88bccf9f275a306bf38d72711f3d48a70f1fa1da 100644 (file)
 
 #include "AudioIOCallback.h"
 #include "FloatConversion.h"
+#include "VectorMath.h"
 #include <CoreAudio/AudioHardware.h>
 
 namespace WebCore {
 
 const int kBufferSize = 128;
+const float kLowThreshold = -1;
+const float kHighThreshold = 1;
 
 // Factory method: Mac-implementation
 PassOwnPtr<AudioDestination> AudioDestination::create(AudioIOCallback& callback, float sampleRate)
@@ -158,6 +161,12 @@ OSStatus AudioDestinationMac::render(UInt32 numberOfFrames, AudioBufferList* ioD
     // FIXME: Add support for local/live audio input.
     m_callback.render(0, &m_renderBus, numberOfFrames);
 
+    // Clamp values at 0db (i.e., [-1.0, 1.0])
+    for (unsigned i = 0; i < m_renderBus.numberOfChannels(); ++i) {
+        AudioChannel* channel = m_renderBus.channel(i);
+        VectorMath::vclip(channel->data(), 1, &kLowThreshold, &kHighThreshold, channel->mutableData(), 1, numberOfFrames);
+    }
+
     return noErr;
 }