LibWeb: Add ChannelSplitterNode
interface
This commit is contained in:
parent
819178a49e
commit
e564d25ffb
Notes:
github-actions[bot]
2025-01-02 11:40:31 +00:00
Author: https://github.com/tcl3 Commit: https://github.com/LadybirdBrowser/ladybird/commit/e564d25ffb6 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3097 Reviewed-by: https://github.com/shannonbooth ✅
9 changed files with 315 additions and 1 deletions
Libraries/LibWeb
Tests/LibWeb/Text
expected
all-window-properties.txt
wpt-import/webaudio/the-audio-api/the-channelsplitternode-interface
input/wpt-import/webaudio/the-audio-api/the-channelsplitternode-interface
|
@ -793,6 +793,7 @@ set(SOURCES
|
|||
WebAudio/BaseAudioContext.cpp
|
||||
WebAudio/BiquadFilterNode.cpp
|
||||
WebAudio/ChannelMergerNode.cpp
|
||||
WebAudio/ChannelSplitterNode.cpp
|
||||
WebAudio/DynamicsCompressorNode.cpp
|
||||
WebAudio/GainNode.cpp
|
||||
WebAudio/OfflineAudioContext.cpp
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
|
||||
virtual WebIDL::ExceptionOr<void> set_channel_count_mode(Bindings::ChannelCountMode);
|
||||
Bindings::ChannelCountMode channel_count_mode();
|
||||
WebIDL::ExceptionOr<void> set_channel_interpretation(Bindings::ChannelInterpretation);
|
||||
virtual WebIDL::ExceptionOr<void> set_channel_interpretation(Bindings::ChannelInterpretation);
|
||||
Bindings::ChannelInterpretation channel_interpretation();
|
||||
|
||||
WebIDL::ExceptionOr<void> initialize_audio_node_options(AudioNodeOptions const& given_options, AudioNodeDefaultOptions const& default_options);
|
||||
|
|
87
Libraries/LibWeb/WebAudio/ChannelSplitterNode.cpp
Normal file
87
Libraries/LibWeb/WebAudio/ChannelSplitterNode.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/ChannelSplitterNodePrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/WebAudio/BaseAudioContext.h>
|
||||
#include <LibWeb/WebAudio/ChannelSplitterNode.h>
|
||||
|
||||
namespace Web::WebAudio {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(ChannelSplitterNode);
|
||||
|
||||
ChannelSplitterNode::ChannelSplitterNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, ChannelSplitterOptions const& options)
|
||||
: AudioNode(realm, context)
|
||||
, m_number_of_outputs(options.number_of_outputs)
|
||||
{
|
||||
}
|
||||
|
||||
ChannelSplitterNode::~ChannelSplitterNode() = default;
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ref<ChannelSplitterNode>> ChannelSplitterNode::create(JS::Realm& realm, GC::Ref<BaseAudioContext> context, ChannelSplitterOptions const& options)
|
||||
{
|
||||
return construct_impl(realm, context, options);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ref<ChannelSplitterNode>> ChannelSplitterNode::construct_impl(JS::Realm& realm, GC::Ref<BaseAudioContext> context, ChannelSplitterOptions const& options)
|
||||
{
|
||||
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createchannelsplitter
|
||||
// An IndexSizeError exception MUST be thrown if numberOfOutputs is less than 1 or is greater than the number of supported channels.
|
||||
if (options.number_of_outputs < 1 || options.number_of_outputs > BaseAudioContext::MAX_NUMBER_OF_CHANNELS)
|
||||
return WebIDL::IndexSizeError::create(realm, "Invalid number of outputs"_string);
|
||||
|
||||
auto node = realm.create<ChannelSplitterNode>(realm, context, options);
|
||||
|
||||
// Default options for channel count and interpretation
|
||||
// https://webaudio.github.io/web-audio-api/#ChannelSplitterNode
|
||||
AudioNodeDefaultOptions default_options;
|
||||
default_options.channel_count_mode = Bindings::ChannelCountMode::Explicit;
|
||||
default_options.channel_interpretation = Bindings::ChannelInterpretation::Discrete;
|
||||
default_options.channel_count = node->number_of_outputs();
|
||||
// FIXME: Set tail-time to no
|
||||
|
||||
TRY(node->initialize_audio_node_options(options, default_options));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void ChannelSplitterNode::initialize(JS::Realm& realm)
|
||||
{
|
||||
AudioNode::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(ChannelSplitterNode);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> ChannelSplitterNode::set_channel_count(WebIDL::UnsignedLong channel_count)
|
||||
{
|
||||
// https://webaudio.github.io/web-audio-api/#audionode-channelcount-constraints
|
||||
// The channel count cannot be changed, and an InvalidStateError exception MUST be thrown for any attempt to change the value.
|
||||
if (channel_count != m_number_of_outputs)
|
||||
return WebIDL::InvalidStateError::create(realm(), "Channel count must be equal to number of outputs"_string);
|
||||
|
||||
return AudioNode::set_channel_count(channel_count);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> ChannelSplitterNode::set_channel_count_mode(Bindings::ChannelCountMode channel_count_mode)
|
||||
{
|
||||
// https://webaudio.github.io/web-audio-api/#audionode-channelcountmode-constraints
|
||||
// The channel count mode cannot be changed from "explicit" and an InvalidStateError exception MUST be thrown for any attempt to change the value.
|
||||
if (channel_count_mode != Bindings::ChannelCountMode::Explicit)
|
||||
return WebIDL::InvalidStateError::create(realm(), "Channel count mode must be 'explicit'"_string);
|
||||
|
||||
return AudioNode::set_channel_count_mode(channel_count_mode);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> ChannelSplitterNode::set_channel_interpretation(Bindings::ChannelInterpretation channel_interpretation)
|
||||
{
|
||||
// https://webaudio.github.io/web-audio-api/#audionode-channelinterpretation-constraints
|
||||
// The channel intepretation can not be changed from "discrete" and a InvalidStateError exception MUST be thrown for any attempt to change the value.
|
||||
if (channel_interpretation != Bindings::ChannelInterpretation::Discrete)
|
||||
return WebIDL::InvalidStateError::create(realm(), "Channel interpretation must be 'discrete'"_string);
|
||||
|
||||
return AudioNode::set_channel_interpretation(channel_interpretation);
|
||||
}
|
||||
|
||||
}
|
44
Libraries/LibWeb/WebAudio/ChannelSplitterNode.h
Normal file
44
Libraries/LibWeb/WebAudio/ChannelSplitterNode.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/WebAudio/AudioNode.h>
|
||||
|
||||
namespace Web::WebAudio {
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#ChannelSplitterOptions
|
||||
struct ChannelSplitterOptions : AudioNodeOptions {
|
||||
WebIDL::UnsignedLong number_of_outputs { 6 };
|
||||
};
|
||||
|
||||
/// https://webaudio.github.io/web-audio-api/#ChannelSplitterNode
|
||||
class ChannelSplitterNode final : public AudioNode {
|
||||
WEB_PLATFORM_OBJECT(ChannelSplitterNode, AudioNode);
|
||||
GC_DECLARE_ALLOCATOR(ChannelSplitterNode);
|
||||
|
||||
public:
|
||||
virtual ~ChannelSplitterNode() override;
|
||||
|
||||
static WebIDL::ExceptionOr<GC::Ref<ChannelSplitterNode>> create(JS::Realm&, GC::Ref<BaseAudioContext>, ChannelSplitterOptions const& = {});
|
||||
static WebIDL::ExceptionOr<GC::Ref<ChannelSplitterNode>> construct_impl(JS::Realm&, GC::Ref<BaseAudioContext>, ChannelSplitterOptions const& = {});
|
||||
|
||||
virtual WebIDL::UnsignedLong number_of_inputs() override { return 1; }
|
||||
virtual WebIDL::UnsignedLong number_of_outputs() override { return m_number_of_outputs; }
|
||||
|
||||
virtual WebIDL::ExceptionOr<void> set_channel_count(WebIDL::UnsignedLong) override;
|
||||
virtual WebIDL::ExceptionOr<void> set_channel_count_mode(Bindings::ChannelCountMode) override;
|
||||
virtual WebIDL::ExceptionOr<void> set_channel_interpretation(Bindings::ChannelInterpretation) override;
|
||||
|
||||
private:
|
||||
ChannelSplitterNode(JS::Realm&, GC::Ref<BaseAudioContext>, ChannelSplitterOptions const&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
WebIDL::UnsignedLong m_number_of_outputs;
|
||||
};
|
||||
|
||||
}
|
13
Libraries/LibWeb/WebAudio/ChannelSplitterNode.idl
Normal file
13
Libraries/LibWeb/WebAudio/ChannelSplitterNode.idl
Normal file
|
@ -0,0 +1,13 @@
|
|||
#import <WebAudio/AudioNode.idl>
|
||||
#import <WebAudio/BaseAudioContext.idl>
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#ChannelSplitterNode
|
||||
[Exposed=Window]
|
||||
interface ChannelSplitterNode : AudioNode {
|
||||
constructor (BaseAudioContext context, optional ChannelSplitterOptions options = {});
|
||||
};
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#ChannelSplitterOptions
|
||||
dictionary ChannelSplitterOptions : AudioNodeOptions {
|
||||
unsigned long numberOfOutputs = 6;
|
||||
};
|
|
@ -370,6 +370,7 @@ libweb_js_bindings(WebAudio/BiquadFilterNode)
|
|||
libweb_js_bindings(WebAudio/DynamicsCompressorNode)
|
||||
libweb_js_bindings(WebAudio/GainNode)
|
||||
libweb_js_bindings(WebAudio/ChannelMergerNode)
|
||||
libweb_js_bindings(WebAudio/ChannelSplitterNode)
|
||||
libweb_js_bindings(WebAudio/OfflineAudioContext)
|
||||
libweb_js_bindings(WebAudio/OscillatorNode)
|
||||
libweb_js_bindings(WebAudio/PannerNode)
|
||||
|
|
|
@ -57,6 +57,7 @@ CanvasGradient
|
|||
CanvasPattern
|
||||
CanvasRenderingContext2D
|
||||
ChannelMergerNode
|
||||
ChannelSplitterNode
|
||||
CharacterData
|
||||
Clipboard
|
||||
ClipboardEvent
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 47 tests
|
||||
|
||||
47 Pass
|
||||
Pass # AUDIT TASK RUNNER STARTED.
|
||||
Pass Executing "initialize"
|
||||
Pass Executing "invalid constructor"
|
||||
Pass Executing "default constructor"
|
||||
Pass Executing "test AudioNodeOptions"
|
||||
Pass Executing "constructor options"
|
||||
Pass Audit report
|
||||
Pass > [initialize]
|
||||
Pass context = new OfflineAudioContext(...) did not throw an exception.
|
||||
Pass < [initialize] All assertions passed. (total 1 assertions)
|
||||
Pass > [invalid constructor]
|
||||
Pass new ChannelSplitterNode() threw TypeError: "ChannelSplitterNode() needs one argument".
|
||||
Pass new ChannelSplitterNode(1) threw TypeError: "Not an object of type BaseAudioContext".
|
||||
Pass new ChannelSplitterNode(context, 42) threw TypeError: "Not an object of type ChannelSplitterOptions".
|
||||
Pass < [invalid constructor] All assertions passed. (total 3 assertions)
|
||||
Pass > [default constructor]
|
||||
Pass node0 = new ChannelSplitterNode(context) did not throw an exception.
|
||||
Pass node0 instanceof ChannelSplitterNode is equal to true.
|
||||
Pass node0.numberOfInputs is equal to 1.
|
||||
Pass node0.numberOfOutputs is equal to 6.
|
||||
Pass node0.channelCount is equal to 6.
|
||||
Pass node0.channelCountMode is equal to explicit.
|
||||
Pass node0.channelInterpretation is equal to discrete.
|
||||
Pass < [default constructor] All assertions passed. (total 7 assertions)
|
||||
Pass > [test AudioNodeOptions]
|
||||
Pass new ChannelSplitterNode(c, {channelCount: 6}) did not throw an exception.
|
||||
Pass node.channelCount is equal to 6.
|
||||
Pass new ChannelSplitterNode(c, {channelCount: 7}) threw InvalidStateError: "Channel count must be equal to number of outputs".
|
||||
Pass (new ChannelSplitterNode(c, {channelCount: 6})).channelCount = 6 did not throw an exception.
|
||||
Pass new ChannelSplitterNode(c, {channelCountMode: "explicit"} did not throw an exception.
|
||||
Pass node.channelCountMode is equal to explicit.
|
||||
Pass new ChannelSplitterNode(c, {channelCountMode: "max"}) threw InvalidStateError: "Channel count mode must be 'explicit'".
|
||||
Pass new ChannelSplitterNode(c, {channelCountMode: "clamped-max"}) threw InvalidStateError: "Channel count mode must be 'explicit'".
|
||||
Pass (new ChannelSplitterNode(c, {channelCountMode: "explicit"})).channelCountMode = "explicit" did not throw an exception.
|
||||
Pass new ChannelSplitterNode(c, {channelInterpretation: "speakers"}) threw InvalidStateError: "Channel interpretation must be 'discrete'".
|
||||
Pass (new ChannelSplitterNode(c, {channelInterpretation: "discrete"})).channelInterpretation = "discrete" did not throw an exception.
|
||||
Pass < [test AudioNodeOptions] All assertions passed. (total 11 assertions)
|
||||
Pass > [constructor options]
|
||||
Pass node1 = new ChannelSplitterNode(context, {"numberOfInputs":3,"numberOfOutputs":9,"channelInterpretation":"discrete"}) did not throw an exception.
|
||||
Pass node1.numberOfInputs is equal to 1.
|
||||
Pass node1.numberOfOutputs is equal to 9.
|
||||
Pass node1.channelInterpretation is equal to discrete.
|
||||
Pass new ChannelSplitterNode(c, {"numberOfOutputs":99}) threw IndexSizeError: "Invalid number of outputs".
|
||||
Pass new ChannelSplitterNode(c, {"channelCount":3}) threw InvalidStateError: "Channel count must be equal to number of outputs".
|
||||
Pass new ChannelSplitterNode(c, {"channelCountMode":"max"}) threw InvalidStateError: "Channel count mode must be 'explicit'".
|
||||
Pass < [constructor options] All assertions passed. (total 7 assertions)
|
||||
Pass # AUDIT TASK RUNNER FINISHED: 5 tasks ran successfully.
|
|
@ -0,0 +1,115 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Test Constructor: ChannelSplitter
|
||||
</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>
|
||||
<script src="../../../webaudio/resources/audionodeoptions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script id="layout-test-code">
|
||||
let context;
|
||||
|
||||
let audit = Audit.createTaskRunner();
|
||||
|
||||
audit.define('initialize', (task, should) => {
|
||||
context = initializeContext(should);
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.define('invalid constructor', (task, should) => {
|
||||
testInvalidConstructor(should, 'ChannelSplitterNode', context);
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.define('default constructor', (task, should) => {
|
||||
testDefaultConstructor(should, 'ChannelSplitterNode', context, {
|
||||
prefix: 'node0',
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 6,
|
||||
channelCount: 6,
|
||||
channelCountMode: 'explicit',
|
||||
channelInterpretation: 'discrete'
|
||||
});
|
||||
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.define('test AudioNodeOptions', (task, should) => {
|
||||
testAudioNodeOptions(should, context, 'ChannelSplitterNode', {
|
||||
channelCount: {
|
||||
value: 6,
|
||||
isFixed: true,
|
||||
exceptionType: 'InvalidStateError'
|
||||
},
|
||||
channelCountMode: {
|
||||
value: 'explicit',
|
||||
isFixed: true,
|
||||
exceptionType: 'InvalidStateError'
|
||||
},
|
||||
channelInterpretation: {
|
||||
value: 'discrete',
|
||||
isFixed: true,
|
||||
exceptionType: 'InvalidStateError'
|
||||
},
|
||||
});
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.define('constructor options', (task, should) => {
|
||||
let node;
|
||||
let options = {
|
||||
numberOfInputs: 3,
|
||||
numberOfOutputs: 9,
|
||||
channelInterpretation: 'discrete'
|
||||
};
|
||||
|
||||
should(
|
||||
() => {
|
||||
node = new ChannelSplitterNode(context, options);
|
||||
},
|
||||
'node1 = new ChannelSplitterNode(context, ' +
|
||||
JSON.stringify(options) + ')')
|
||||
.notThrow();
|
||||
|
||||
should(node.numberOfInputs, 'node1.numberOfInputs').beEqualTo(1);
|
||||
should(node.numberOfOutputs, 'node1.numberOfOutputs')
|
||||
.beEqualTo(options.numberOfOutputs);
|
||||
should(node.channelInterpretation, 'node1.channelInterpretation')
|
||||
.beEqualTo(options.channelInterpretation);
|
||||
|
||||
options = {numberOfOutputs: 99};
|
||||
should(
|
||||
() => {
|
||||
node = new ChannelSplitterNode(context, options);
|
||||
},
|
||||
'new ChannelSplitterNode(c, ' + JSON.stringify(options) + ')')
|
||||
.throw(DOMException, 'IndexSizeError');
|
||||
|
||||
options = {channelCount: 3};
|
||||
should(
|
||||
() => {
|
||||
node = new ChannelSplitterNode(context, options);
|
||||
},
|
||||
'new ChannelSplitterNode(c, ' + JSON.stringify(options) + ')')
|
||||
.throw(DOMException, 'InvalidStateError');
|
||||
|
||||
options = {channelCountMode: 'max'};
|
||||
should(
|
||||
() => {
|
||||
node = new ChannelSplitterNode(context, options);
|
||||
},
|
||||
'new ChannelSplitterNode(c, ' + JSON.stringify(options) + ')')
|
||||
.throw(DOMException, 'InvalidStateError');
|
||||
|
||||
task.done();
|
||||
});
|
||||
|
||||
audit.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue