mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
LibWeb/WebAudio: Implement PannerNode
Required by https://scottts.itch.io/different-strokes
This commit is contained in:
parent
27a654c739
commit
4f691c2410
Notes:
github-actions[bot]
2024-12-17 12:39:30 +00:00
Author: https://github.com/Lubrsi Commit: https://github.com/LadybirdBrowser/ladybird/commit/4f691c2410f Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2942 Reviewed-by: https://github.com/gmta ✅
7 changed files with 356 additions and 0 deletions
|
@ -787,6 +787,7 @@ set(SOURCES
|
|||
WebAudio/GainNode.cpp
|
||||
WebAudio/OfflineAudioContext.cpp
|
||||
WebAudio/OscillatorNode.cpp
|
||||
WebAudio/PannerNode.cpp
|
||||
WebAudio/PeriodicWave.cpp
|
||||
WebDriver/Actions.cpp
|
||||
WebDriver/Capabilities.cpp
|
||||
|
|
|
@ -822,6 +822,7 @@ class DynamicsCompressorNode;
|
|||
class GainNode;
|
||||
class OfflineAudioContext;
|
||||
class OscillatorNode;
|
||||
class PannerNode;
|
||||
class PeriodicWave;
|
||||
|
||||
enum class AudioContextState;
|
||||
|
|
166
Libraries/LibWeb/WebAudio/PannerNode.cpp
Normal file
166
Libraries/LibWeb/WebAudio/PannerNode.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/WebAudio/AudioNode.h>
|
||||
#include <LibWeb/WebAudio/AudioParam.h>
|
||||
#include <LibWeb/WebAudio/BaseAudioContext.h>
|
||||
#include <LibWeb/WebAudio/PannerNode.h>
|
||||
|
||||
namespace Web::WebAudio {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PannerNode);
|
||||
|
||||
PannerNode::~PannerNode() = default;
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ref<PannerNode>> PannerNode::create(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
|
||||
{
|
||||
return construct_impl(realm, context, options);
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-pannernode
|
||||
WebIDL::ExceptionOr<GC::Ref<PannerNode>> PannerNode::construct_impl(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
|
||||
{
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance
|
||||
// A RangeError exception MUST be thrown if this is set to a negative value.
|
||||
if (options.ref_distance < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "refDistance cannot be negative"sv };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor
|
||||
// A RangeError exception MUST be thrown if this is set to a negative value.
|
||||
if (options.rolloff_factor < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "rolloffFactor cannot be negative"sv };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance
|
||||
// A RangeError exception MUST be thrown if this is set to a non-positive value.
|
||||
if (options.max_distance < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "maxDistance cannot be negative"sv };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain
|
||||
// It is a linear value (not dB) in the range [0, 1]. An InvalidStateError MUST be thrown if the parameter is outside this range.
|
||||
if (options.cone_outer_gain < 0.0 || options.cone_outer_gain > 1.0)
|
||||
return WebIDL::InvalidStateError::create(realm, "coneOuterGain must be in the range of [0, 1]"_string);
|
||||
|
||||
// Create the node and allocate memory
|
||||
auto node = realm.create<PannerNode>(realm, context, options);
|
||||
|
||||
// Default options for channel count and interpretation
|
||||
// https://webaudio.github.io/web-audio-api/#PannerNode
|
||||
AudioNodeDefaultOptions default_options;
|
||||
default_options.channel_count_mode = Bindings::ChannelCountMode::ClampedMax;
|
||||
default_options.channel_interpretation = Bindings::ChannelInterpretation::Speakers;
|
||||
default_options.channel_count = 2;
|
||||
// FIXME: Set tail-time to maybe
|
||||
|
||||
TRY(node->initialize_audio_node_options(options, default_options));
|
||||
return node;
|
||||
}
|
||||
|
||||
PannerNode::PannerNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
|
||||
: AudioNode(realm, context)
|
||||
, m_position_x(AudioParam::create(realm, options.position_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_position_y(AudioParam::create(realm, options.position_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_position_z(AudioParam::create(realm, options.position_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_orientation_x(AudioParam::create(realm, options.orientation_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_orientation_y(AudioParam::create(realm, options.orientation_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_orientation_z(AudioParam::create(realm, options.orientation_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
|
||||
, m_ref_distance(options.ref_distance)
|
||||
, m_max_distance(options.max_distance)
|
||||
, m_rolloff_factor(options.rolloff_factor)
|
||||
, m_cone_inner_angle(options.cone_inner_angle)
|
||||
, m_cone_outer_angle(options.cone_outer_angle)
|
||||
, m_cone_outer_gain(options.cone_outer_gain)
|
||||
{
|
||||
}
|
||||
|
||||
void PannerNode::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(PannerNode);
|
||||
}
|
||||
|
||||
void PannerNode::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_position_x);
|
||||
visitor.visit(m_position_y);
|
||||
visitor.visit(m_position_z);
|
||||
visitor.visit(m_orientation_x);
|
||||
visitor.visit(m_orientation_y);
|
||||
visitor.visit(m_orientation_z);
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_ref_distance(double value)
|
||||
{
|
||||
// A RangeError exception MUST be thrown if this is set to a negative value.
|
||||
if (value < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "refDistance cannot be negative"sv };
|
||||
|
||||
m_ref_distance = value;
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_max_distance(double value)
|
||||
{
|
||||
// A RangeError exception MUST be thrown if this is set to a non-positive value.
|
||||
if (value < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "maxDistance cannot be negative"sv };
|
||||
|
||||
m_max_distance = value;
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_rolloff_factor(double value)
|
||||
{
|
||||
// A RangeError exception MUST be thrown if this is set to a negative value.
|
||||
if (value < 0.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "rolloffFactor cannot be negative"sv };
|
||||
|
||||
m_rolloff_factor = value;
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_cone_outer_gain(double value)
|
||||
{
|
||||
// It is a linear value (not dB) in the range [0, 1]. An InvalidStateError MUST be thrown if the parameter is outside this range.
|
||||
if (value < 0.0 || value > 1.0)
|
||||
return WebIDL::InvalidStateError::create(realm(), "coneOuterGain must be in the range of [0, 1]"_string);
|
||||
|
||||
m_cone_outer_gain = value;
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-setposition
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_position(float x, float y, float z)
|
||||
{
|
||||
// This method is DEPRECATED. It is equivalent to setting positionX.value, positionY.value, and positionZ.value
|
||||
// attribute directly with the x, y and z parameters, respectively.
|
||||
// FIXME: Consequently, if any of the positionX, positionY, and positionZ AudioParams have an automation curve
|
||||
// set using setValueCurveAtTime() at the time this method is called, a NotSupportedError MUST be thrown.
|
||||
m_position_x->set_value(x);
|
||||
m_position_y->set_value(y);
|
||||
m_position_z->set_value(z);
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-setorientation
|
||||
WebIDL::ExceptionOr<void> PannerNode::set_orientation(float x, float y, float z)
|
||||
{
|
||||
// This method is DEPRECATED. It is equivalent to setting orientationX.value, orientationY.value, and
|
||||
// orientationZ.value attribute directly, with the x, y and z parameters, respectively.
|
||||
// FIXME: Consequently, if any of the orientationX, orientationY, and orientationZ AudioParams have an automation
|
||||
// curve set using setValueCurveAtTime() at the time this method is called, a NotSupportedError MUST be thrown.
|
||||
m_orientation_x->set_value(x);
|
||||
m_orientation_y->set_value(y);
|
||||
m_orientation_z->set_value(z);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
130
Libraries/LibWeb/WebAudio/PannerNode.h
Normal file
130
Libraries/LibWeb/WebAudio/PannerNode.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/PannerNodePrototype.h>
|
||||
#include <LibWeb/WebAudio/AudioNode.h>
|
||||
|
||||
namespace Web::WebAudio {
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#PannerOptions
|
||||
struct PannerOptions : AudioNodeOptions {
|
||||
Bindings::PanningModelType panning_model { Bindings::PanningModelType::Equalpower };
|
||||
Bindings::DistanceModelType distance_model { Bindings::DistanceModelType::Inverse };
|
||||
float position_x { 0.0f };
|
||||
float position_y { 0.0f };
|
||||
float position_z { 0.0f };
|
||||
float orientation_x { 1.0f };
|
||||
float orientation_y { 0.0f };
|
||||
float orientation_z { 0.0f };
|
||||
double ref_distance { 1.0 };
|
||||
double max_distance { 10000.0 };
|
||||
double rolloff_factor { 1.0 };
|
||||
double cone_inner_angle { 360.0 };
|
||||
double cone_outer_angle { 360.0 };
|
||||
double cone_outer_gain { 0.0 };
|
||||
};
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#PannerNode
|
||||
class PannerNode final : public AudioNode {
|
||||
WEB_PLATFORM_OBJECT(PannerNode, AudioNode);
|
||||
GC_DECLARE_ALLOCATOR(PannerNode);
|
||||
|
||||
public:
|
||||
virtual ~PannerNode() override;
|
||||
|
||||
static WebIDL::ExceptionOr<GC::Ref<PannerNode>> create(JS::Realm&, GC::Ref<BaseAudioContext>, PannerOptions const& = {});
|
||||
static WebIDL::ExceptionOr<GC::Ref<PannerNode>> construct_impl(JS::Realm&, GC::Ref<BaseAudioContext>, PannerOptions const& = {});
|
||||
|
||||
WebIDL::UnsignedLong number_of_inputs() override { return 1; }
|
||||
WebIDL::UnsignedLong number_of_outputs() override { return 1; }
|
||||
|
||||
GC::Ref<AudioParam const> position_x() const { return m_position_x; }
|
||||
GC::Ref<AudioParam const> position_y() const { return m_position_y; }
|
||||
GC::Ref<AudioParam const> position_z() const { return m_position_z; }
|
||||
GC::Ref<AudioParam const> orientation_x() const { return m_orientation_x; }
|
||||
GC::Ref<AudioParam const> orientation_y() const { return m_orientation_y; }
|
||||
GC::Ref<AudioParam const> orientation_z() const { return m_orientation_z; }
|
||||
|
||||
Bindings::PanningModelType panning_model() const { return m_panning_model; }
|
||||
void set_panning_model(Bindings::PanningModelType value) { m_panning_model = value; }
|
||||
|
||||
Bindings::DistanceModelType distance_model() const { return m_distance_model; }
|
||||
void set_distance_model(Bindings::DistanceModelType value) { m_distance_model = value; }
|
||||
|
||||
double ref_distance() const { return m_ref_distance; }
|
||||
WebIDL::ExceptionOr<void> set_ref_distance(double);
|
||||
|
||||
double max_distance() const { return m_max_distance; }
|
||||
WebIDL::ExceptionOr<void> set_max_distance(double);
|
||||
|
||||
double rolloff_factor() const { return m_rolloff_factor; }
|
||||
WebIDL::ExceptionOr<void> set_rolloff_factor(double);
|
||||
|
||||
double cone_inner_angle() const { return m_cone_inner_angle; }
|
||||
void set_cone_inner_angle(double value) { m_cone_inner_angle = value; }
|
||||
|
||||
double cone_outer_angle() const { return m_cone_outer_angle; }
|
||||
void set_cone_outer_angle(double value) { m_cone_outer_angle = value; }
|
||||
|
||||
double cone_outer_gain() const { return m_cone_outer_gain; }
|
||||
WebIDL::ExceptionOr<void> set_cone_outer_gain(double);
|
||||
|
||||
WebIDL::ExceptionOr<void> set_position(float x, float y, float z);
|
||||
WebIDL::ExceptionOr<void> set_orientation(float x, float y, float z);
|
||||
|
||||
protected:
|
||||
PannerNode(JS::Realm&, GC::Ref<BaseAudioContext>, PannerOptions const& = {});
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-panningmodel
|
||||
Bindings::PanningModelType m_panning_model { Bindings::PanningModelType::Equalpower };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-positionx
|
||||
GC::Ref<AudioParam> m_position_x;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-positiony
|
||||
GC::Ref<AudioParam> m_position_y;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-positionz
|
||||
GC::Ref<AudioParam> m_position_z;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-orientationx
|
||||
GC::Ref<AudioParam> m_orientation_x;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-orientationy
|
||||
GC::Ref<AudioParam> m_orientation_y;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-orientationz
|
||||
GC::Ref<AudioParam> m_orientation_z;
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-distancemodel
|
||||
Bindings::DistanceModelType m_distance_model { Bindings::DistanceModelType::Inverse };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance
|
||||
double m_ref_distance { 1.0 };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance
|
||||
double m_max_distance { 10000.0 };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor
|
||||
double m_rolloff_factor { 1.0 };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-coneinnerangle
|
||||
double m_cone_inner_angle { 360.0 };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-coneouterangle
|
||||
double m_cone_outer_angle { 360.0 };
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain
|
||||
double m_cone_outer_gain { 0.0 };
|
||||
};
|
||||
|
||||
}
|
56
Libraries/LibWeb/WebAudio/PannerNode.idl
Normal file
56
Libraries/LibWeb/WebAudio/PannerNode.idl
Normal file
|
@ -0,0 +1,56 @@
|
|||
#import <WebAudio/AudioNode.idl>
|
||||
#import <WebAudio/AudioParam.idl>
|
||||
#import <WebAudio/BaseAudioContext.idl>
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#enumdef-panningmodeltype
|
||||
enum PanningModelType {
|
||||
"equalpower",
|
||||
"HRTF"
|
||||
};
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#enumdef-distancemodeltype
|
||||
enum DistanceModelType {
|
||||
"linear",
|
||||
"inverse",
|
||||
"exponential"
|
||||
};
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#PannerOptions
|
||||
dictionary PannerOptions : AudioNodeOptions {
|
||||
PanningModelType panningModel = "equalpower";
|
||||
DistanceModelType distanceModel = "inverse";
|
||||
float positionX = 0;
|
||||
float positionY = 0;
|
||||
float positionZ = 0;
|
||||
float orientationX = 1;
|
||||
float orientationY = 0;
|
||||
float orientationZ = 0;
|
||||
double refDistance = 1;
|
||||
double maxDistance = 10000;
|
||||
double rolloffFactor = 1;
|
||||
double coneInnerAngle = 360;
|
||||
double coneOuterAngle = 360;
|
||||
double coneOuterGain = 0;
|
||||
};
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#PannerNode
|
||||
[Exposed=Window]
|
||||
interface PannerNode : AudioNode {
|
||||
constructor(BaseAudioContext context, optional PannerOptions options = {});
|
||||
attribute PanningModelType panningModel;
|
||||
readonly attribute AudioParam positionX;
|
||||
readonly attribute AudioParam positionY;
|
||||
readonly attribute AudioParam positionZ;
|
||||
readonly attribute AudioParam orientationX;
|
||||
readonly attribute AudioParam orientationY;
|
||||
readonly attribute AudioParam orientationZ;
|
||||
attribute DistanceModelType distanceModel;
|
||||
attribute double refDistance;
|
||||
attribute double maxDistance;
|
||||
attribute double rolloffFactor;
|
||||
attribute double coneInnerAngle;
|
||||
attribute double coneOuterAngle;
|
||||
attribute double coneOuterGain;
|
||||
undefined setPosition(float x, float y, float z);
|
||||
undefined setOrientation(float x, float y, float z);
|
||||
};
|
|
@ -368,6 +368,7 @@ libweb_js_bindings(WebAudio/GainNode)
|
|||
libweb_js_bindings(WebAudio/ChannelMergerNode)
|
||||
libweb_js_bindings(WebAudio/OfflineAudioContext)
|
||||
libweb_js_bindings(WebAudio/OscillatorNode)
|
||||
libweb_js_bindings(WebAudio/PannerNode)
|
||||
libweb_js_bindings(WebAudio/PeriodicWave)
|
||||
libweb_js_bindings(WebGL/WebGL2RenderingContext)
|
||||
libweb_js_bindings(WebGL/WebGLActiveInfo)
|
||||
|
|
|
@ -261,6 +261,7 @@ OfflineAudioContext
|
|||
Option
|
||||
OscillatorNode
|
||||
PageTransitionEvent
|
||||
PannerNode
|
||||
Path2D
|
||||
Performance
|
||||
PerformanceEntry
|
||||
|
|
Loading…
Add table
Reference in a new issue