LibWeb: Use correct ranges for BiquadFilterNode parameters

This commit is contained in:
Tim Ledbetter 2025-01-04 01:47:52 +00:00 committed by Tim Ledbetter
commit e469c884d4
Notes: github-actions[bot] 2025-01-08 19:06:47 +00:00
4 changed files with 818 additions and 3 deletions

View file

@ -4,11 +4,13 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Math.h>
#include <LibWeb/Bindings/AudioParamPrototype.h> #include <LibWeb/Bindings/AudioParamPrototype.h>
#include <LibWeb/Bindings/BiquadFilterNodePrototype.h> #include <LibWeb/Bindings/BiquadFilterNodePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebAudio/AudioNode.h> #include <LibWeb/WebAudio/AudioNode.h>
#include <LibWeb/WebAudio/AudioParam.h> #include <LibWeb/WebAudio/AudioParam.h>
#include <LibWeb/WebAudio/BaseAudioContext.h>
#include <LibWeb/WebAudio/BiquadFilterNode.h> #include <LibWeb/WebAudio/BiquadFilterNode.h>
namespace Web::WebAudio { namespace Web::WebAudio {
@ -18,10 +20,10 @@ GC_DEFINE_ALLOCATOR(BiquadFilterNode);
BiquadFilterNode::BiquadFilterNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, BiquadFilterOptions const& options) BiquadFilterNode::BiquadFilterNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, BiquadFilterOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_type(options.type) , m_type(options.type)
, m_frequency(AudioParam::create(realm, options.frequency, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_frequency(AudioParam::create(realm, options.frequency, 0, context->nyquist_frequency(), Bindings::AutomationRate::ARate))
, m_detune(AudioParam::create(realm, options.detune, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_detune(AudioParam::create(realm, options.detune, -1200 * AK::log2(NumericLimits<float>::max()), 1200 * AK::log2(NumericLimits<float>::max()), Bindings::AutomationRate::ARate))
, m_q(AudioParam::create(realm, options.q, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_q(AudioParam::create(realm, options.q, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_gain(AudioParam::create(realm, options.gain, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_gain(AudioParam::create(realm, options.gain, NumericLimits<float>::lowest(), 40 * AK::log10(NumericLimits<float>::max()), Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -51,9 +51,17 @@ protected:
private: private:
Bindings::BiquadFilterType m_type { Bindings::BiquadFilterType::Lowpass }; Bindings::BiquadFilterType m_type { Bindings::BiquadFilterType::Lowpass };
// https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-frequency
GC::Ref<AudioParam> m_frequency; GC::Ref<AudioParam> m_frequency;
// https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-detune
GC::Ref<AudioParam> m_detune; GC::Ref<AudioParam> m_detune;
// https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-q
GC::Ref<AudioParam> m_q; GC::Ref<AudioParam> m_q;
// https://webaudio.github.io/web-audio-api/#dom-biquadfilternode-gain
GC::Ref<AudioParam> m_gain; GC::Ref<AudioParam> m_gain;
}; };

View file

@ -0,0 +1,308 @@
Harness status: OK
Found 302 tests
291 Pass
11 Fail
Pass # AUDIT TASK RUNNER STARTED.
Pass Executing "initialize"
Pass Executing "Offline createGain"
Pass Executing "Offline createDelay"
Pass Executing "Offline createBufferSource"
Fail Executing "Offline createStereoPanner"
Pass Executing "Offline createDynamicsCompressor"
Pass Executing "Offline createBiquadFilter"
Pass Executing "Offline createOscillator"
Pass Executing "Offline createPanner"
Pass Executing "Offline createConstantSource"
Pass Executing "Offline createBuffer"
Fail Executing "Offline createIIRFilter"
Fail Executing "Offline createWaveShaper"
Fail Executing "Offline createConvolver"
Fail Executing "Offline createAnalyser"
Fail Executing "Offline createScriptProcessor"
Pass Executing "Offline createPeriodicWave"
Pass Executing "Offline createChannelSplitter"
Pass Executing "Offline createChannelMerger"
Fail Executing "Online createMediaElementSource"
Fail Executing "Online createMediaStreamDestination"
Pass Executing "AudioListener"
Pass Executing "verifyTests"
Pass Executing "automation"
Pass Audit report
Pass > [initialize]
Pass Create offline context for tests did not throw an exception.
Pass Create online context for tests did not throw an exception.
Pass < [initialize] All assertions passed. (total 2 assertions)
Pass > [Offline createGain]
Pass GainNode.gain.minValue is equal to -3.4028234663852886e+38.
Pass GainNode.gain.maxValue is equal to 3.4028234663852886e+38.
Pass GainNode.gain.minValue = 42 is not equal to 42.
Pass GainNode.gain.minValue is read-only is equal to true.
Pass GainNode.gain.maxValue = 42 is not equal to 42.
Pass GainNode.gain.maxValue is read-only is equal to true.
Pass Nominal ranges for AudioParam(s) of GainNode are correct
Pass < [Offline createGain] All assertions passed. (total 7 assertions)
Pass > [Offline createDelay]
Pass DelayNode.delayTime.minValue is equal to 0.
Pass DelayNode.delayTime.maxValue is equal to 1.5.
Pass DelayNode.delayTime.minValue = 42 is not equal to 42.
Pass DelayNode.delayTime.minValue is read-only is equal to true.
Pass DelayNode.delayTime.maxValue = 42 is not equal to 42.
Pass DelayNode.delayTime.maxValue is read-only is equal to true.
Pass Set DelayNode.delayTime.value = -1 is equal to 0.
Pass Set DelayNode.delayTime.value = 4 is equal to 1.5.
Pass DelayNode.delayTime was clipped to lie within the nominal range is equal to true.
Pass Nominal ranges for AudioParam(s) of DelayNode are correct
Pass < [Offline createDelay] All assertions passed. (total 10 assertions)
Pass > [Offline createBufferSource]
Pass AudioBufferSourceNode.playbackRate.minValue is equal to -3.4028234663852886e+38.
Pass AudioBufferSourceNode.playbackRate.maxValue is equal to 3.4028234663852886e+38.
Pass AudioBufferSourceNode.playbackRate.minValue = 42 is not equal to 42.
Pass AudioBufferSourceNode.playbackRate.minValue is read-only is equal to true.
Pass AudioBufferSourceNode.playbackRate.maxValue = 42 is not equal to 42.
Pass AudioBufferSourceNode.playbackRate.maxValue is read-only is equal to true.
Pass AudioBufferSourceNode.detune.minValue is equal to -3.4028234663852886e+38.
Pass AudioBufferSourceNode.detune.maxValue is equal to 3.4028234663852886e+38.
Pass AudioBufferSourceNode.detune.minValue = 42 is not equal to 42.
Pass AudioBufferSourceNode.detune.minValue is read-only is equal to true.
Pass AudioBufferSourceNode.detune.maxValue = 42 is not equal to 42.
Pass AudioBufferSourceNode.detune.maxValue is read-only is equal to true.
Pass Nominal ranges for AudioParam(s) of AudioBufferSourceNode are correct
Pass < [Offline createBufferSource] All assertions passed. (total 13 assertions)
Pass > [Offline createStereoPanner]
Pass > [Offline createDynamicsCompressor]
Pass DynamicsCompressorNode.threshold.minValue is equal to -100.
Pass DynamicsCompressorNode.threshold.maxValue is equal to 0.
Pass DynamicsCompressorNode.threshold.minValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.threshold.minValue is read-only is equal to true.
Pass DynamicsCompressorNode.threshold.maxValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.threshold.maxValue is read-only is equal to true.
Pass Set DynamicsCompressorNode.threshold.value = -201 is equal to -100.
Pass Set DynamicsCompressorNode.threshold.value = 1 is equal to 0.
Pass DynamicsCompressorNode.threshold was clipped to lie within the nominal range is equal to true.
Pass DynamicsCompressorNode.knee.minValue is equal to 0.
Pass DynamicsCompressorNode.knee.maxValue is equal to 40.
Pass DynamicsCompressorNode.knee.minValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.knee.minValue is read-only is equal to true.
Pass DynamicsCompressorNode.knee.maxValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.knee.maxValue is read-only is equal to true.
Pass Set DynamicsCompressorNode.knee.value = -1 is equal to 0.
Pass Set DynamicsCompressorNode.knee.value = 81 is equal to 40.
Pass DynamicsCompressorNode.knee was clipped to lie within the nominal range is equal to true.
Pass DynamicsCompressorNode.ratio.minValue is equal to 1.
Pass DynamicsCompressorNode.ratio.maxValue is equal to 20.
Pass DynamicsCompressorNode.ratio.minValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.ratio.minValue is read-only is equal to true.
Pass DynamicsCompressorNode.ratio.maxValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.ratio.maxValue is read-only is equal to true.
Pass Set DynamicsCompressorNode.ratio.value = 1 is equal to 1.
Pass Set DynamicsCompressorNode.ratio.value = 41 is equal to 20.
Pass DynamicsCompressorNode.ratio was clipped to lie within the nominal range is equal to true.
Pass DynamicsCompressorNode.attack.minValue is equal to 0.
Pass DynamicsCompressorNode.attack.maxValue is equal to 1.
Pass DynamicsCompressorNode.attack.minValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.attack.minValue is read-only is equal to true.
Pass DynamicsCompressorNode.attack.maxValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.attack.maxValue is read-only is equal to true.
Pass Set DynamicsCompressorNode.attack.value = -1 is equal to 0.
Pass Set DynamicsCompressorNode.attack.value = 3 is equal to 1.
Pass DynamicsCompressorNode.attack was clipped to lie within the nominal range is equal to true.
Pass DynamicsCompressorNode.release.minValue is equal to 0.
Pass DynamicsCompressorNode.release.maxValue is equal to 1.
Pass DynamicsCompressorNode.release.minValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.release.minValue is read-only is equal to true.
Pass DynamicsCompressorNode.release.maxValue = 42 is not equal to 42.
Pass DynamicsCompressorNode.release.maxValue is read-only is equal to true.
Pass Set DynamicsCompressorNode.release.value = -1 is equal to 0.
Pass Set DynamicsCompressorNode.release.value = 3 is equal to 1.
Pass DynamicsCompressorNode.release was clipped to lie within the nominal range is equal to true.
Pass Nominal ranges for AudioParam(s) of DynamicsCompressorNode are correct
Pass < [Offline createDynamicsCompressor] All assertions passed. (total 46 assertions)
Pass > [Offline createBiquadFilter]
Pass BiquadFilterNode.frequency.minValue is equal to 0.
Pass BiquadFilterNode.frequency.maxValue is equal to 24000.
Pass BiquadFilterNode.frequency.minValue = 42 is not equal to 42.
Pass BiquadFilterNode.frequency.minValue is read-only is equal to true.
Pass BiquadFilterNode.frequency.maxValue = 42 is not equal to 42.
Pass BiquadFilterNode.frequency.maxValue is read-only is equal to true.
Pass Set BiquadFilterNode.frequency.value = -1 is equal to 0.
Pass Set BiquadFilterNode.frequency.value = 48001 is equal to 24000.
Pass BiquadFilterNode.frequency was clipped to lie within the nominal range is equal to true.
Pass BiquadFilterNode.detune.minValue is equal to -153600.
Pass BiquadFilterNode.detune.maxValue is equal to 153600.
Pass BiquadFilterNode.detune.minValue = 42 is not equal to 42.
Pass BiquadFilterNode.detune.minValue is read-only is equal to true.
Pass BiquadFilterNode.detune.maxValue = 42 is not equal to 42.
Pass BiquadFilterNode.detune.maxValue is read-only is equal to true.
Pass Set BiquadFilterNode.detune.value = -307201 is equal to -153600.
Pass Set BiquadFilterNode.detune.value = 307201 is equal to 153600.
Pass BiquadFilterNode.detune was clipped to lie within the nominal range is equal to true.
Pass BiquadFilterNode.Q.minValue is equal to -3.4028234663852886e+38.
Pass BiquadFilterNode.Q.maxValue is equal to 3.4028234663852886e+38.
Pass BiquadFilterNode.Q.minValue = 42 is not equal to 42.
Pass BiquadFilterNode.Q.minValue is read-only is equal to true.
Pass BiquadFilterNode.Q.maxValue = 42 is not equal to 42.
Pass BiquadFilterNode.Q.maxValue is read-only is equal to true.
Pass BiquadFilterNode.gain.minValue is equal to -3.4028234663852886e+38.
Pass BiquadFilterNode.gain.maxValue is equal to 1541.273681640625.
Pass BiquadFilterNode.gain.minValue = 42 is not equal to 42.
Pass BiquadFilterNode.gain.minValue is read-only is equal to true.
Pass BiquadFilterNode.gain.maxValue = 42 is not equal to 42.
Pass BiquadFilterNode.gain.maxValue is read-only is equal to true.
Pass Set BiquadFilterNode.gain.value = 3083.54736328125 is equal to 1541.273681640625.
Pass BiquadFilterNode.gain was clipped to lie within the nominal range is equal to true.
Pass Nominal ranges for AudioParam(s) of BiquadFilterNode are correct
Pass < [Offline createBiquadFilter] All assertions passed. (total 33 assertions)
Pass > [Offline createOscillator]
Pass OscillatorNode.frequency.minValue is equal to -24000.
Pass OscillatorNode.frequency.maxValue is equal to 24000.
Pass OscillatorNode.frequency.minValue = 42 is not equal to 42.
Pass OscillatorNode.frequency.minValue is read-only is equal to true.
Pass OscillatorNode.frequency.maxValue = 42 is not equal to 42.
Pass OscillatorNode.frequency.maxValue is read-only is equal to true.
Pass Set OscillatorNode.frequency.value = -48001 is equal to -24000.
Pass Set OscillatorNode.frequency.value = 48001 is equal to 24000.
Pass OscillatorNode.frequency was clipped to lie within the nominal range is equal to true.
Pass OscillatorNode.detune.minValue is equal to -153600.
Pass OscillatorNode.detune.maxValue is equal to 153600.
Pass OscillatorNode.detune.minValue = 42 is not equal to 42.
Pass OscillatorNode.detune.minValue is read-only is equal to true.
Pass OscillatorNode.detune.maxValue = 42 is not equal to 42.
Pass OscillatorNode.detune.maxValue is read-only is equal to true.
Pass Set OscillatorNode.detune.value = -307201 is equal to -153600.
Pass Set OscillatorNode.detune.value = 307201 is equal to 153600.
Pass OscillatorNode.detune was clipped to lie within the nominal range is equal to true.
Pass Nominal ranges for AudioParam(s) of OscillatorNode are correct
Pass < [Offline createOscillator] All assertions passed. (total 19 assertions)
Pass > [Offline createPanner]
Pass PannerNode.positionX.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.positionX.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.positionX.minValue = 42 is not equal to 42.
Pass PannerNode.positionX.minValue is read-only is equal to true.
Pass PannerNode.positionX.maxValue = 42 is not equal to 42.
Pass PannerNode.positionX.maxValue is read-only is equal to true.
Pass PannerNode.positionY.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.positionY.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.positionY.minValue = 42 is not equal to 42.
Pass PannerNode.positionY.minValue is read-only is equal to true.
Pass PannerNode.positionY.maxValue = 42 is not equal to 42.
Pass PannerNode.positionY.maxValue is read-only is equal to true.
Pass PannerNode.positionZ.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.positionZ.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.positionZ.minValue = 42 is not equal to 42.
Pass PannerNode.positionZ.minValue is read-only is equal to true.
Pass PannerNode.positionZ.maxValue = 42 is not equal to 42.
Pass PannerNode.positionZ.maxValue is read-only is equal to true.
Pass PannerNode.orientationX.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.orientationX.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.orientationX.minValue = 42 is not equal to 42.
Pass PannerNode.orientationX.minValue is read-only is equal to true.
Pass PannerNode.orientationX.maxValue = 42 is not equal to 42.
Pass PannerNode.orientationX.maxValue is read-only is equal to true.
Pass PannerNode.orientationY.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.orientationY.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.orientationY.minValue = 42 is not equal to 42.
Pass PannerNode.orientationY.minValue is read-only is equal to true.
Pass PannerNode.orientationY.maxValue = 42 is not equal to 42.
Pass PannerNode.orientationY.maxValue is read-only is equal to true.
Pass PannerNode.orientationZ.minValue is equal to -3.4028234663852886e+38.
Pass PannerNode.orientationZ.maxValue is equal to 3.4028234663852886e+38.
Pass PannerNode.orientationZ.minValue = 42 is not equal to 42.
Pass PannerNode.orientationZ.minValue is read-only is equal to true.
Pass PannerNode.orientationZ.maxValue = 42 is not equal to 42.
Pass PannerNode.orientationZ.maxValue is read-only is equal to true.
Pass Nominal ranges for AudioParam(s) of PannerNode are correct
Pass < [Offline createPanner] All assertions passed. (total 37 assertions)
Pass > [Offline createConstantSource]
Pass ConstantSourceNode.offset.minValue is equal to -3.4028234663852886e+38.
Pass ConstantSourceNode.offset.maxValue is equal to 3.4028234663852886e+38.
Pass ConstantSourceNode.offset.minValue = 42 is not equal to 42.
Pass ConstantSourceNode.offset.minValue is read-only is equal to true.
Pass ConstantSourceNode.offset.maxValue = 42 is not equal to 42.
Pass ConstantSourceNode.offset.maxValue is read-only is equal to true.
Pass Nominal ranges for AudioParam(s) of ConstantSourceNode are correct
Pass < [Offline createConstantSource] All assertions passed. (total 7 assertions)
Pass > [Offline createBuffer]
Pass AudioBuffer has no AudioParams as expected
Pass < [Offline createBuffer] All assertions passed. (total 1 assertions)
Pass > [Offline createIIRFilter]
Pass > [Offline createWaveShaper]
Pass > [Offline createConvolver]
Pass > [Offline createAnalyser]
Pass > [Offline createScriptProcessor]
Pass > [Offline createPeriodicWave]
Pass PeriodicWave has no AudioParams as expected
Pass < [Offline createPeriodicWave] All assertions passed. (total 1 assertions)
Pass > [Offline createChannelSplitter]
Pass ChannelSplitterNode has no AudioParams as expected
Pass < [Offline createChannelSplitter] All assertions passed. (total 1 assertions)
Pass > [Offline createChannelMerger]
Pass AudioNode has no AudioParams as expected
Pass < [Offline createChannelMerger] All assertions passed. (total 1 assertions)
Pass > [Online createMediaElementSource]
Pass > [Online createMediaStreamDestination]
Pass > [AudioListener]
Pass AudioListener.positionX.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.positionX.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.positionX.minValue = 42 is not equal to 42.
Pass AudioListener.positionX.minValue is read-only is equal to true.
Pass AudioListener.positionX.maxValue = 42 is not equal to 42.
Pass AudioListener.positionX.maxValue is read-only is equal to true.
Pass AudioListener.positionY.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.positionY.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.positionY.minValue = 42 is not equal to 42.
Pass AudioListener.positionY.minValue is read-only is equal to true.
Pass AudioListener.positionY.maxValue = 42 is not equal to 42.
Pass AudioListener.positionY.maxValue is read-only is equal to true.
Pass AudioListener.positionZ.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.positionZ.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.positionZ.minValue = 42 is not equal to 42.
Pass AudioListener.positionZ.minValue is read-only is equal to true.
Pass AudioListener.positionZ.maxValue = 42 is not equal to 42.
Pass AudioListener.positionZ.maxValue is read-only is equal to true.
Pass AudioListener.forwardX.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.forwardX.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.forwardX.minValue = 42 is not equal to 42.
Pass AudioListener.forwardX.minValue is read-only is equal to true.
Pass AudioListener.forwardX.maxValue = 42 is not equal to 42.
Pass AudioListener.forwardX.maxValue is read-only is equal to true.
Pass AudioListener.forwardY.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.forwardY.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.forwardY.minValue = 42 is not equal to 42.
Pass AudioListener.forwardY.minValue is read-only is equal to true.
Pass AudioListener.forwardY.maxValue = 42 is not equal to 42.
Pass AudioListener.forwardY.maxValue is read-only is equal to true.
Pass AudioListener.forwardZ.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.forwardZ.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.forwardZ.minValue = 42 is not equal to 42.
Pass AudioListener.forwardZ.minValue is read-only is equal to true.
Pass AudioListener.forwardZ.maxValue = 42 is not equal to 42.
Pass AudioListener.forwardZ.maxValue is read-only is equal to true.
Pass AudioListener.upX.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.upX.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.upX.minValue = 42 is not equal to 42.
Pass AudioListener.upX.minValue is read-only is equal to true.
Pass AudioListener.upX.maxValue = 42 is not equal to 42.
Pass AudioListener.upX.maxValue is read-only is equal to true.
Pass AudioListener.upY.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.upY.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.upY.minValue = 42 is not equal to 42.
Pass AudioListener.upY.minValue is read-only is equal to true.
Pass AudioListener.upY.maxValue = 42 is not equal to 42.
Pass AudioListener.upY.maxValue is read-only is equal to true.
Pass AudioListener.upZ.minValue is equal to -3.4028234663852886e+38.
Pass AudioListener.upZ.maxValue is equal to 3.4028234663852886e+38.
Pass AudioListener.upZ.minValue = 42 is not equal to 42.
Pass AudioListener.upZ.minValue is read-only is equal to true.
Pass AudioListener.upZ.maxValue = 42 is not equal to 42.
Pass AudioListener.upZ.maxValue is read-only is equal to true.
Pass Nominal ranges for AudioParam(s) of AudioListener are correct
Pass < [AudioListener] All assertions passed. (total 55 assertions)
Pass > [verifyTests]
Pass Number of nodes not tested : 0
Pass < [verifyTests] All assertions passed. (total 1 assertions)
Pass > [automation]
Fail X Test automations (check console logs) incorrectly threw NotSupportedError: "FIXME: Implement AudioParam::linear_ramp_to_value_at_time".
Fail < [automation] 1 out of 1 assertions were failed.
Fail # AUDIT TASK RUNNER FINISHED: 1 out of 24 tasks were failed.

View file

@ -0,0 +1,497 @@
<!DOCTYPE html>
<html>
<head>
<title>
Test AudioParam Nominal Range Values
</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../webaudio/resources/audit-util.js"></script>
<script src="../../../webaudio/resources/audit.js"></script>
</head>
<body>
<script id="layout-test-code">
// Some arbitrary sample rate for the offline context.
let sampleRate = 48000;
// The actual contexts to use. Generally use the offline context for
// testing except for the media nodes which require an AudioContext.
let offlineContext;
let audioContext;
// The set of all methods that we've tested for verifying that we tested
// all of the necessary objects.
let testedMethods = new Set();
// The most positive single float value (the value just before infinity).
// Be careful when changing this value! Javascript only uses double
// floats, so the value here should be the max single-float value,
// converted directly to a double-float value. This also depends on
// Javascript reading this value and producing the desired double-float
// value correctly.
let mostPositiveFloat = 3.4028234663852886e38;
let audit = Audit.createTaskRunner();
// Array describing the tests that should be run. |testOfflineConfigs| is
// for tests that can use an offline context. |testOnlineConfigs| is for
// tests that need to use an online context. Offline contexts are
// preferred when possible.
let testOfflineConfigs = [
{
// The name of the method to create the particular node to be tested.
creator: 'createGain',
// Any args to pass to the creator function.
args: [],
// The min/max limits for each AudioParam of the node. This is a
// dictionary whose keys are
// the names of each AudioParam in the node. Don't define this if the
// node doesn't have any
// AudioParam attributes.
limits: {
gain: {
// The expected min and max values for this AudioParam.
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat
}
}
},
{
creator: 'createDelay',
// Just specify a non-default value for the maximum delay so we can
// make sure the limits are
// set correctly.
args: [1.5],
limits: {delayTime: {minValue: 0, maxValue: 1.5}}
},
{
creator: 'createBufferSource',
args: [],
limits: {
playbackRate:
{minValue: -mostPositiveFloat, maxValue: mostPositiveFloat},
detune: {minValue: -mostPositiveFloat, maxValue: mostPositiveFloat}
}
},
{
creator: 'createStereoPanner',
args: [],
limits: {pan: {minValue: -1, maxValue: 1}}
},
{
creator: 'createDynamicsCompressor',
args: [],
// Do not set limits for reduction; it's currently an AudioParam but
// should be a float.
// So let the test fail for reduction. When reduction is changed,
// this test will then
// correctly pass.
limits: {
threshold: {minValue: -100, maxValue: 0},
knee: {minValue: 0, maxValue: 40},
ratio: {minValue: 1, maxValue: 20},
attack: {minValue: 0, maxValue: 1},
release: {minValue: 0, maxValue: 1}
}
},
{
creator: 'createBiquadFilter',
args: [],
limits: {
gain: {
minValue: -mostPositiveFloat,
// This complicated expression is used to get all the arithmetic
// to round to the correct single-precision float value for the
// desired max. This also assumes that the implication computes
// the limit as 40 * log10f(std::numeric_limits<float>::max()).
maxValue:
Math.fround(40 * Math.fround(Math.log10(mostPositiveFloat)))
},
Q: {minValue: -mostPositiveFloat, maxValue: mostPositiveFloat},
frequency: {minValue: 0, maxValue: sampleRate / 2},
detune: {
minValue: -Math.fround(1200 * Math.log2(mostPositiveFloat)),
maxValue: Math.fround(1200 * Math.log2(mostPositiveFloat))
}
}
},
{
creator: 'createOscillator',
args: [],
limits: {
frequency: {minValue: -sampleRate / 2, maxValue: sampleRate / 2},
detune: {
minValue: -Math.fround(1200 * Math.log2(mostPositiveFloat)),
maxValue: Math.fround(1200 * Math.log2(mostPositiveFloat))
}
}
},
{
creator: 'createPanner',
args: [],
limits: {
positionX: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
positionY: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
positionZ: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
orientationX: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
orientationY: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
orientationZ: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
}
},
},
{
creator: 'createConstantSource',
args: [],
limits: {
offset: {minValue: -mostPositiveFloat, maxValue: mostPositiveFloat}
}
},
// These nodes don't have AudioParams, but we want to test them anyway.
// Any arguments for the
// constructor are pretty much arbitrary; they just need to be valid.
{
creator: 'createBuffer',
args: [1, 1, sampleRate],
},
{creator: 'createIIRFilter', args: [[1, 2], [1, .9]]},
{
creator: 'createWaveShaper',
args: [],
},
{
creator: 'createConvolver',
args: [],
},
{
creator: 'createAnalyser',
args: [],
},
{
creator: 'createScriptProcessor',
args: [0],
},
{
creator: 'createPeriodicWave',
args: [Float32Array.from([0, 0]), Float32Array.from([1, 0])],
},
{
creator: 'createChannelSplitter',
args: [],
},
{
creator: 'createChannelMerger',
args: [],
},
];
let testOnlineConfigs = [
{creator: 'createMediaElementSource', args: [new Audio()]},
{creator: 'createMediaStreamDestination', args: []}
// Can't currently test MediaStreamSource because we're using an offline
// context.
];
// Create the contexts so we can use it in the following test.
audit.define('initialize', (task, should) => {
// Just any context so that we can create the nodes.
should(() => {
offlineContext = new OfflineAudioContext(1, 1, sampleRate);
}, 'Create offline context for tests').notThrow();
should(() => {
onlineContext = new AudioContext();
}, 'Create online context for tests').notThrow();
task.done();
});
// Create a task for each entry in testOfflineConfigs
for (let test in testOfflineConfigs) {
let config = testOfflineConfigs[test]
audit.define('Offline ' + config.creator, (function(c) {
return (task, should) => {
let node = offlineContext[c.creator](...c.args);
testLimits(should, c.creator, node, c.limits);
task.done();
};
})(config));
}
for (let test in testOnlineConfigs) {
let config = testOnlineConfigs[test]
audit.define('Online ' + config.creator, (function(c) {
return (task, should) => {
let node = onlineContext[c.creator](...c.args);
testLimits(should, c.creator, node, c.limits);
task.done();
};
})(config));
}
// Test the AudioListener params that were added for the automated Panner
audit.define('AudioListener', (task, should) => {
testLimits(should, '', offlineContext.listener, {
positionX: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
positionY: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
positionZ: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
forwardX: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
forwardY: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
forwardZ: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
upX: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
upY: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
},
upZ: {
minValue: -mostPositiveFloat,
maxValue: mostPositiveFloat,
}
});
task.done();
});
// Verify that we have tested all the create methods available on the
// context.
audit.define('verifyTests', (task, should) => {
let allNodes = new Set();
// Create the set of all "create" methods from the context.
for (let method in offlineContext) {
if (typeof offlineContext[method] === 'function' &&
method.substring(0, 6) === 'create') {
allNodes.add(method);
}
}
// Compute the difference between the set of all create methods on the
// context and the set of tests that we've run.
let diff = new Set([...allNodes].filter(x => !testedMethods.has(x)));
// Can't currently test a MediaStreamSourceNode, so remove it from the
// diff set.
diff.delete('createMediaStreamSource');
// It's a test failure if we didn't test all of the create methods in
// the context (except createMediaStreamSource, of course).
let output = [];
if (diff.size) {
for (let item of diff)
output.push(' ' + item.substring(6));
}
should(output.length === 0, 'Number of nodes not tested')
.message(': 0', ': ' + output);
task.done();
});
// Simple test of a few automation methods to verify we get warnings.
audit.define('automation', (task, should) => {
// Just use a DelayNode for testing because the audio param has finite
// limits.
should(() => {
let d = offlineContext.createDelay();
// The console output should have the warnings that we're interested
// in.
d.delayTime.setValueAtTime(-1, 0);
d.delayTime.linearRampToValueAtTime(2, 1);
d.delayTime.exponentialRampToValueAtTime(3, 2);
d.delayTime.setTargetAtTime(-1, 3, .1);
d.delayTime.setValueCurveAtTime(
Float32Array.from([.1, .2, 1.5, -1]), 4, .1);
}, 'Test automations (check console logs)').notThrow();
task.done();
});
audit.run();
// Is |object| an AudioParam? We determine this by checking the
// constructor name.
function isAudioParam(object) {
return object && object.constructor.name === 'AudioParam';
}
// Does |limitOptions| exist and does it have valid values for the
// expected min and max values?
function hasValidLimits(limitOptions) {
return limitOptions && (typeof limitOptions.minValue === 'number') &&
(typeof limitOptions.maxValue === 'number');
}
// Check the min and max values for the AudioParam attribute named
// |paramName| for the |node|. The expected limits is given by the
// dictionary |limits|. If some test fails, add the name of the failed
function validateAudioParamLimits(should, node, paramName, limits) {
let nodeName = node.constructor.name;
let parameter = node[paramName];
let prefix = nodeName + '.' + paramName;
let success = true;
if (hasValidLimits(limits[paramName])) {
// Verify that the min and max values for the parameter are correct.
let isCorrect = should(parameter.minValue, prefix + '.minValue')
.beEqualTo(limits[paramName].minValue);
isCorrect = should(parameter.maxValue, prefix + '.maxValue')
.beEqualTo(limits[paramName].maxValue) &&
isCorrect;
// Verify that the min and max attributes are read-only. |testValue|
// MUST be a number that can be represented exactly the same way as
// both a double and single float. A small integer works nicely.
const testValue = 42;
parameter.minValue = testValue;
let isReadOnly;
isReadOnly =
should(parameter.minValue, `${prefix}.minValue = ${testValue}`)
.notBeEqualTo(testValue);
should(isReadOnly, prefix + '.minValue is read-only').beEqualTo(true);
isCorrect = isReadOnly && isCorrect;
parameter.maxValue = testValue;
isReadOnly =
should(parameter.maxValue, `${prefix}.maxValue = ${testValue}`)
.notBeEqualTo(testValue);
should(isReadOnly, prefix + '.maxValue is read-only').beEqualTo(true);
isCorrect = isReadOnly && isCorrect;
// Now try to set the parameter outside the nominal range.
let newValue = 2 * limits[paramName].minValue - 1;
let isClipped = true;
let clippingTested = false;
// If the new value is beyond float the largest single-precision
// float, skip the test because Chrome throws an error.
if (newValue >= -mostPositiveFloat) {
parameter.value = newValue;
clippingTested = true;
isClipped =
should(
parameter.value, 'Set ' + prefix + '.value = ' + newValue)
.beEqualTo(parameter.minValue) &&
isClipped;
}
newValue = 2 * limits[paramName].maxValue + 1;
if (newValue <= mostPositiveFloat) {
parameter.value = newValue;
clippingTested = true;
isClipped =
should(
parameter.value, 'Set ' + prefix + '.value = ' + newValue)
.beEqualTo(parameter.maxValue) &&
isClipped;
}
if (clippingTested) {
should(
isClipped,
prefix + ' was clipped to lie within the nominal range')
.beEqualTo(true);
}
isCorrect = isCorrect && isClipped;
success = isCorrect && success;
} else {
// Test config didn't specify valid limits. Fail this test!
should(
clippingTested,
'Limits for ' + nodeName + '.' + paramName +
' were correctly defined')
.beEqualTo(false);
success = false;
}
return success;
}
// Test all of the AudioParams for |node| using the expected values in
// |limits|. |creatorName| is the name of the method to create the node,
// and is used to keep trakc of which tests we've run.
function testLimits(should, creatorName, node, limits) {
let nodeName = node.constructor.name;
testedMethods.add(creatorName);
let success = true;
// List of all of the AudioParams that were tested.
let audioParams = [];
// List of AudioParams that failed the test.
let incorrectParams = [];
// Look through all of the keys for the node and extract just the
// AudioParams
Object.keys(node.__proto__).forEach(function(paramName) {
if (isAudioParam(node[paramName])) {
audioParams.push(paramName);
let isValid = validateAudioParamLimits(
should, node, paramName, limits, incorrectParams);
if (!isValid)
incorrectParams.push(paramName);
success = isValid && success;
}
});
// Print an appropriate message depending on whether there were
// AudioParams defined or not.
if (audioParams.length) {
let message =
'Nominal ranges for AudioParam(s) of ' + node.constructor.name;
should(success, message)
.message('are correct', 'are incorrect for: ' + +incorrectParams);
return success;
} else {
should(!limits, nodeName)
.message(
'has no AudioParams as expected',
'has no AudioParams but test expected ' + limits);
}
}
</script>
</body>
</html>