diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 90132af9401..21409cc74c1 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -764,6 +764,7 @@ set(SOURCES WebAudio/AudioScheduledSourceNode.cpp WebAudio/BaseAudioContext.cpp WebAudio/BiquadFilterNode.cpp + WebAudio/ChannelMergerNode.cpp WebAudio/DynamicsCompressorNode.cpp WebAudio/GainNode.cpp WebAudio/OfflineAudioContext.cpp diff --git a/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp b/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp index 44cf2d86c8f..11326d7b584 100644 --- a/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp +++ b/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,15 @@ WebIDL::ExceptionOr> BaseAudioContext::create_buf return AudioBufferSourceNode::create(realm(), *this); } +// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createchannelmerger +WebIDL::ExceptionOr> BaseAudioContext::create_channel_merger(WebIDL::UnsignedLong number_of_inputs) +{ + ChannelMergerOptions options; + options.number_of_inputs = number_of_inputs; + + return ChannelMergerNode::create(realm(), *this, options); +} + // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createoscillator WebIDL::ExceptionOr> BaseAudioContext::create_oscillator() { diff --git a/Libraries/LibWeb/WebAudio/BaseAudioContext.h b/Libraries/LibWeb/WebAudio/BaseAudioContext.h index 207122f2846..9487e94b36d 100644 --- a/Libraries/LibWeb/WebAudio/BaseAudioContext.h +++ b/Libraries/LibWeb/WebAudio/BaseAudioContext.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace Web::WebAudio { @@ -57,6 +58,7 @@ public: WebIDL::ExceptionOr> create_biquad_filter(); WebIDL::ExceptionOr> create_buffer(WebIDL::UnsignedLong number_of_channels, WebIDL::UnsignedLong length, float sample_rate); WebIDL::ExceptionOr> create_buffer_source(); + WebIDL::ExceptionOr> create_channel_merger(WebIDL::UnsignedLong number_of_inputs); WebIDL::ExceptionOr> create_oscillator(); WebIDL::ExceptionOr> create_dynamics_compressor(); WebIDL::ExceptionOr> create_gain(); diff --git a/Libraries/LibWeb/WebAudio/BaseAudioContext.idl b/Libraries/LibWeb/WebAudio/BaseAudioContext.idl index 79b887fc50e..0153136015d 100644 --- a/Libraries/LibWeb/WebAudio/BaseAudioContext.idl +++ b/Libraries/LibWeb/WebAudio/BaseAudioContext.idl @@ -4,6 +4,7 @@ #import #import #import +#import #import #import #import @@ -32,7 +33,7 @@ interface BaseAudioContext : EventTarget { BiquadFilterNode createBiquadFilter (); AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long length, float sampleRate); AudioBufferSourceNode createBufferSource (); - [FIXME] ChannelMergerNode createChannelMerger (optional unsigned long numberOfInputs = 6); + ChannelMergerNode createChannelMerger (optional unsigned long numberOfInputs = 6); [FIXME] ChannelSplitterNode createChannelSplitter (optional unsigned long numberOfOutputs = 6); [FIXME] ConstantSourceNode createConstantSource (); [FIXME] ConvolverNode createConvolver (); diff --git a/Libraries/LibWeb/WebAudio/ChannelMergerNode.cpp b/Libraries/LibWeb/WebAudio/ChannelMergerNode.cpp new file mode 100644 index 00000000000..0e278a9d423 --- /dev/null +++ b/Libraries/LibWeb/WebAudio/ChannelMergerNode.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::WebAudio { + +GC_DEFINE_ALLOCATOR(ChannelMergerNode); + +ChannelMergerNode::ChannelMergerNode(JS::Realm& realm, GC::Ref context, ChannelMergerOptions const& options) + : AudioNode(realm, context) + , m_number_of_inputs(options.number_of_inputs) +{ +} + +ChannelMergerNode::~ChannelMergerNode() = default; + +WebIDL::ExceptionOr> ChannelMergerNode::create(JS::Realm& realm, GC::Ref context, ChannelMergerOptions const& options) +{ + return construct_impl(realm, context, options); +} + +WebIDL::ExceptionOr> ChannelMergerNode::construct_impl(JS::Realm& realm, GC::Ref context, ChannelMergerOptions const& options) +{ + // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createchannelmerger + // An IndexSizeError exception MUST be thrown if numberOfInputs is less than 1 or is greater + // than the number of supported channels. + if (options.number_of_inputs < 1 || options.number_of_inputs > BaseAudioContext::MAX_NUMBER_OF_CHANNELS) + return WebIDL::IndexSizeError::create(realm, "Invalid number of inputs"_string); + + auto node = realm.create(realm, context, options); + + // Default options for channel count and interpretation + // https://webaudio.github.io/web-audio-api/#BiquadFilterNode + AudioNodeDefaultOptions default_options; + default_options.channel_count_mode = Bindings::ChannelCountMode::Explicit; + default_options.channel_interpretation = Bindings::ChannelInterpretation::Speakers; + default_options.channel_count = 1; + // FIXME: Set tail-time to no + + TRY(node->initialize_audio_node_options(options, default_options)); + + return node; +} + +// https://webaudio.github.io/web-audio-api/#audionode-channelcount-constraints +WebIDL::ExceptionOr ChannelMergerNode::set_channel_count(WebIDL::UnsignedLong channel_count) +{ + // The channel count cannot be changed, and an InvalidStateError exception MUST be thrown for + // any attempt to change the value. + if (channel_count != 1) + return WebIDL::InvalidStateError::create(realm(), "Channel count cannot be changed"_string); + + return Base::set_channel_count(channel_count); +} + +WebIDL::ExceptionOr ChannelMergerNode::set_channel_count_mode(Bindings::ChannelCountMode channel_count_mode) +{ + // 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 cannot be changed"_string); + + return Base::set_channel_count_mode(channel_count_mode); +} + +} diff --git a/Libraries/LibWeb/WebAudio/ChannelMergerNode.h b/Libraries/LibWeb/WebAudio/ChannelMergerNode.h new file mode 100644 index 00000000000..1de8ea0287e --- /dev/null +++ b/Libraries/LibWeb/WebAudio/ChannelMergerNode.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::WebAudio { + +// https://webaudio.github.io/web-audio-api/#ChannelMergerOptions +struct ChannelMergerOptions : AudioNodeOptions { + WebIDL::UnsignedLong number_of_inputs { 6 }; +}; + +// https://webaudio.github.io/web-audio-api/#ChannelMergerNode +class ChannelMergerNode final : public AudioNode { + WEB_PLATFORM_OBJECT(ChannelMergerNode, AudioNode); + GC_DECLARE_ALLOCATOR(ChannelMergerNode); + +public: + virtual ~ChannelMergerNode() override; + + static WebIDL::ExceptionOr> create(JS::Realm&, GC::Ref, ChannelMergerOptions const& = {}); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, GC::Ref, ChannelMergerOptions const& = {}); + + WebIDL::UnsignedLong number_of_inputs() override { return m_number_of_inputs; } + WebIDL::UnsignedLong number_of_outputs() override { return 1; } + + // ^AudioNode + virtual WebIDL::ExceptionOr set_channel_count(WebIDL::UnsignedLong) override; + virtual WebIDL::ExceptionOr set_channel_count_mode(Bindings::ChannelCountMode) override; + +private: + ChannelMergerNode(JS::Realm&, GC::Ref, ChannelMergerOptions const&); + + WebIDL::UnsignedLong m_number_of_inputs; +}; + +} diff --git a/Libraries/LibWeb/WebAudio/ChannelMergerNode.idl b/Libraries/LibWeb/WebAudio/ChannelMergerNode.idl new file mode 100644 index 00000000000..d170c3c3edc --- /dev/null +++ b/Libraries/LibWeb/WebAudio/ChannelMergerNode.idl @@ -0,0 +1,13 @@ +#import +#import + +// https://webaudio.github.io/web-audio-api/#ChannelMergerNode +[Exposed=Window] +interface ChannelMergerNode : AudioNode { + constructor (BaseAudioContext context, optional ChannelMergerOptions options = {}); +}; + +// https://webaudio.github.io/web-audio-api/#ChannelMergerOptions +dictionary ChannelMergerOptions : AudioNodeOptions { + unsigned long numberOfInputs = 6; +}; diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index ff569332c56..52599970883 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -358,6 +358,7 @@ libweb_js_bindings(WebAudio/BaseAudioContext) libweb_js_bindings(WebAudio/BiquadFilterNode) libweb_js_bindings(WebAudio/DynamicsCompressorNode) libweb_js_bindings(WebAudio/GainNode) +libweb_js_bindings(WebAudio/ChannelMergerNode) libweb_js_bindings(WebAudio/OfflineAudioContext) libweb_js_bindings(WebAudio/OscillatorNode) libweb_js_bindings(WebAudio/PeriodicWave) diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni index 0cc4e709de4..d2c6461acb4 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni @@ -368,6 +368,7 @@ standard_idl_files = [ "//Userland/Libraries/LibWeb/WebAudio/BiquadFilterNode.idl", "//Userland/Libraries/LibWeb/WebAudio/DynamicsCompressorNode.idl", "//Userland/Libraries/LibWeb/WebAudio/GainNode.idl", + "//Userland/Libraries/LibWeb/WebAudio/ChannelMergerNode.idl", "//Userland/Libraries/LibWeb/WebAudio/OfflineAudioContext.idl", "//Userland/Libraries/LibWeb/WebAudio/OscillatorNode.idl", "//Userland/Libraries/LibWeb/WebAudio/PeriodicWave.idl", diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index c14b2d34e64..489142cd435 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -56,6 +56,7 @@ CSSTransition CanvasGradient CanvasPattern CanvasRenderingContext2D +ChannelMergerNode CharacterData Clipboard ClipboardEvent