diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp
index bd84ad92f0d..c1be2836bb7 100644
--- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp
+++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp
@@ -35,6 +35,7 @@ static bool is_platform_object(Type const& type)
"AnimationTimeline"sv,
"Attr"sv,
"AudioBuffer"sv,
+ "AudioListener"sv,
"AudioNode"sv,
"AudioParam"sv,
"AudioScheduledSourceNode"sv,
diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/WebAudio/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/WebAudio/BUILD.gn
index 1a70a024896..39f505d38ec 100644
--- a/Meta/gn/secondary/Userland/Libraries/LibWeb/WebAudio/BUILD.gn
+++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/WebAudio/BUILD.gn
@@ -12,6 +12,8 @@ source_set("WebAudio") {
"AudioContext.h",
"AudioDestinationNode.cpp",
"AudioDestinationNode.h",
+ "AudioListener.cpp",
+ "AudioListener.h",
"AudioNode.cpp",
"AudioNode.h",
"AudioParam.cpp",
diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni
index 8fd08a979dc..af50c3a93f7 100644
--- a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni
+++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni
@@ -351,6 +351,7 @@ standard_idl_files = [
"//Userland/Libraries/LibWeb/WebAudio/AudioBufferSourceNode.idl",
"//Userland/Libraries/LibWeb/WebAudio/AudioContext.idl",
"//Userland/Libraries/LibWeb/WebAudio/AudioDestinationNode.idl",
+ "//Userland/Libraries/LibWeb/WebAudio/AudioListener.idl",
"//Userland/Libraries/LibWeb/WebAudio/AudioNode.idl",
"//Userland/Libraries/LibWeb/WebAudio/AudioParam.idl",
"//Userland/Libraries/LibWeb/WebAudio/AudioScheduledSourceNode.idl",
diff --git a/Tests/LibWeb/Text/expected/WebAudio/AudioListener.txt b/Tests/LibWeb/Text/expected/WebAudio/AudioListener.txt
new file mode 100644
index 00000000000..357cf09e3ce
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/WebAudio/AudioListener.txt
@@ -0,0 +1,18 @@
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: -1, default: -1, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 1, default: 1, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 0, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: -1, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 2.5, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: -3, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 4, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: -5, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 6, default: -1, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 7, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: -8, default: 1, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
+[object AudioParam] current: 9, default: 0, min: -3.4028234663852886e+38, max: 3.4028234663852886e+38, rate: a-rate
diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt
index f7fc93d2f55..97f1c98cb87 100644
--- a/Tests/LibWeb/Text/expected/all-window-properties.txt
+++ b/Tests/LibWeb/Text/expected/all-window-properties.txt
@@ -15,6 +15,7 @@ AudioBuffer
AudioBufferSourceNode
AudioContext
AudioDestinationNode
+AudioListener
AudioNode
AudioParam
AudioScheduledSourceNode
diff --git a/Tests/LibWeb/Text/input/WebAudio/AudioListener.html b/Tests/LibWeb/Text/input/WebAudio/AudioListener.html
new file mode 100644
index 00000000000..eb126cf938a
--- /dev/null
+++ b/Tests/LibWeb/Text/input/WebAudio/AudioListener.html
@@ -0,0 +1,29 @@
+
+
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index 2fb44f7ad3e..91492f7696a 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -725,6 +725,7 @@ set(SOURCES
WebAudio/AudioBufferSourceNode.cpp
WebAudio/AudioContext.cpp
WebAudio/AudioDestinationNode.cpp
+ WebAudio/AudioListener.cpp
WebAudio/AudioNode.cpp
WebAudio/AudioParam.cpp
WebAudio/AudioScheduledSourceNode.cpp
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index c6707ca033f..53d1ac812fe 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -768,6 +768,7 @@ class AudioBuffer;
class AudioBufferSourceNode;
class AudioContext;
class AudioDestinationNode;
+class AudioListener;
class AudioNode;
class AudioParam;
class AudioScheduledSourceNode;
diff --git a/Userland/Libraries/LibWeb/WebAudio/AudioListener.cpp b/Userland/Libraries/LibWeb/WebAudio/AudioListener.cpp
new file mode 100644
index 00000000000..2df7ce2f565
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAudio/AudioListener.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024, Jelle Raaijmakers
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include
+#include
+#include
+
+namespace Web::WebAudio {
+
+JS_DEFINE_ALLOCATOR(AudioListener);
+
+AudioListener::AudioListener(JS::Realm& realm)
+ : Bindings::PlatformObject(realm)
+ , m_forward_x(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_forward_y(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_forward_z(AudioParam::create(realm, -1.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_position_x(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_position_y(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_position_z(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_up_x(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_up_y(AudioParam::create(realm, 1.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+ , m_up_z(AudioParam::create(realm, 0.f, NumericLimits::lowest(), NumericLimits::max(), Bindings::AutomationRate::ARate))
+{
+}
+
+JS::NonnullGCPtr AudioListener::create(JS::Realm& realm)
+{
+ return realm.vm().heap().allocate(realm, realm);
+}
+
+AudioListener::~AudioListener() = default;
+
+// https://webaudio.github.io/web-audio-api/#dom-audiolistener-setposition
+WebIDL::ExceptionOr AudioListener::set_position(float x, float y, float z)
+{
+ // This method is DEPRECATED. It is equivalent to setting positionX.value, positionY.value, and
+ // positionZ.value directly with the given x, y, and z values, respectively.
+
+ // FIXME: Consequently, any of the positionX, positionY, and positionZ AudioParams for this
+ // AudioListener 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-audiolistener-setorientation
+WebIDL::ExceptionOr AudioListener::set_orientation(float x, float y, float z, float x_up, float y_up, float z_up)
+{
+ // This method is DEPRECATED. It is equivalent to setting forwardX.value, forwardY.value,
+ // forwardZ.value, upX.value, upY.value, and upZ.value directly with the given x, y, z, xUp,
+ // yUp, and zUp values, respectively.
+
+ // FIXME: Consequently, if any of the forwardX, forwardY, forwardZ, upX, upY and upZ
+ // AudioParams have an automation curve set using setValueCurveAtTime() at the time this
+ // method is called, a NotSupportedError MUST be thrown.
+
+ m_forward_x->set_value(x);
+ m_forward_y->set_value(y);
+ m_forward_z->set_value(z);
+ m_up_x->set_value(x_up);
+ m_up_y->set_value(y_up);
+ m_up_z->set_value(z_up);
+
+ return {};
+}
+
+void AudioListener::initialize(JS::Realm& realm)
+{
+ Base::initialize(realm);
+ WEB_SET_PROTOTYPE_FOR_INTERFACE(AudioListener);
+}
+
+void AudioListener::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_forward_x);
+ visitor.visit(m_forward_y);
+ visitor.visit(m_forward_z);
+ visitor.visit(m_position_x);
+ visitor.visit(m_position_y);
+ visitor.visit(m_position_z);
+ visitor.visit(m_up_x);
+ visitor.visit(m_up_y);
+ visitor.visit(m_up_z);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAudio/AudioListener.h b/Userland/Libraries/LibWeb/WebAudio/AudioListener.h
new file mode 100644
index 00000000000..ba4a361097a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAudio/AudioListener.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024, Jelle Raaijmakers
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Web::WebAudio {
+
+// https://webaudio.github.io/web-audio-api/#AudioListener
+class AudioListener final : public Bindings::PlatformObject {
+ WEB_PLATFORM_OBJECT(AudioListener, Bindings::PlatformObject);
+ JS_DECLARE_ALLOCATOR(AudioListener);
+
+public:
+ static JS::NonnullGCPtr create(JS::Realm&);
+ virtual ~AudioListener() override;
+
+ JS::NonnullGCPtr forward_x() const { return m_forward_x; }
+ JS::NonnullGCPtr forward_y() const { return m_forward_y; }
+ JS::NonnullGCPtr forward_z() const { return m_forward_z; }
+ JS::NonnullGCPtr position_x() const { return m_position_x; }
+ JS::NonnullGCPtr position_y() const { return m_position_y; }
+ JS::NonnullGCPtr position_z() const { return m_position_z; }
+ JS::NonnullGCPtr up_x() const { return m_up_x; }
+ JS::NonnullGCPtr up_y() const { return m_up_y; }
+ JS::NonnullGCPtr up_z() const { return m_up_z; }
+
+ WebIDL::ExceptionOr set_position(float x, float y, float z);
+ WebIDL::ExceptionOr set_orientation(float x, float y, float z, float x_up, float y_up, float z_up);
+
+private:
+ explicit AudioListener(JS::Realm&);
+
+ virtual void initialize(JS::Realm&) override;
+ virtual void visit_edges(Cell::Visitor&) override;
+
+ JS::NonnullGCPtr m_forward_x;
+ JS::NonnullGCPtr m_forward_y;
+ JS::NonnullGCPtr m_forward_z;
+ JS::NonnullGCPtr m_position_x;
+ JS::NonnullGCPtr m_position_y;
+ JS::NonnullGCPtr m_position_z;
+ JS::NonnullGCPtr m_up_x;
+ JS::NonnullGCPtr m_up_y;
+ JS::NonnullGCPtr m_up_z;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAudio/AudioListener.idl b/Userland/Libraries/LibWeb/WebAudio/AudioListener.idl
new file mode 100644
index 00000000000..3afa47fea5d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAudio/AudioListener.idl
@@ -0,0 +1,15 @@
+// https://webaudio.github.io/web-audio-api/#AudioListener
+[Exposed=Window]
+interface AudioListener {
+ readonly attribute AudioParam positionX;
+ readonly attribute AudioParam positionY;
+ readonly attribute AudioParam positionZ;
+ readonly attribute AudioParam forwardX;
+ readonly attribute AudioParam forwardY;
+ readonly attribute AudioParam forwardZ;
+ readonly attribute AudioParam upX;
+ readonly attribute AudioParam upY;
+ readonly attribute AudioParam upZ;
+ undefined setPosition (float x, float y, float z);
+ undefined setOrientation (float x, float y, float z, float xUp, float yUp, float zUp);
+};
diff --git a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp
index 49c0c368cc1..ddd6451573a 100644
--- a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp
+++ b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp
@@ -28,6 +28,7 @@ BaseAudioContext::BaseAudioContext(JS::Realm& realm, float sample_rate)
: DOM::EventTarget(realm)
, m_destination(AudioDestinationNode::construct_impl(realm, *this))
, m_sample_rate(sample_rate)
+ , m_listener(AudioListener::create(realm))
{
}
@@ -44,6 +45,7 @@ void BaseAudioContext::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_destination);
visitor.visit(m_pending_promises);
+ visitor.visit(m_listener);
}
void BaseAudioContext::set_onstatechange(WebIDL::CallbackType* event_handler)
diff --git a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.h b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.h
index c216a7c24ee..27b10eb9cf5 100644
--- a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.h
+++ b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.h
@@ -10,6 +10,7 @@
#include
#include
+#include
#include
#include
@@ -38,6 +39,7 @@ public:
JS::NonnullGCPtr destination() const { return m_destination; }
float sample_rate() const { return m_sample_rate; }
double current_time() const { return m_current_time; }
+ JS::NonnullGCPtr listener() const { return m_listener; }
Bindings::AudioContextState state() const { return m_control_thread_state; }
// https://webaudio.github.io/web-audio-api/#--nyquist-frequency
@@ -78,6 +80,8 @@ private:
float m_sample_rate { 0 };
double m_current_time { 0 };
+ JS::NonnullGCPtr m_listener;
+
Bindings::AudioContextState m_control_thread_state = Bindings::AudioContextState::Suspended;
Bindings::AudioContextState m_rendering_thread_state = Bindings::AudioContextState::Suspended;
diff --git a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.idl b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.idl
index e5acc435e55..79b887fc50e 100644
--- a/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.idl
+++ b/Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.idl
@@ -3,6 +3,7 @@
#import
#import
#import
+#import
#import
#import
#import
@@ -21,7 +22,7 @@ interface BaseAudioContext : EventTarget {
readonly attribute AudioDestinationNode destination;
readonly attribute float sampleRate;
readonly attribute double currentTime;
- [FIXME] readonly attribute AudioListener listener;
+ readonly attribute AudioListener listener;
readonly attribute AudioContextState state;
// FIXME: [SameObject, SecureContext]
[FIXME] readonly attribute AudioWorklet audioWorklet;
diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake
index 7e0abf596ff..e7cc62c322a 100644
--- a/Userland/Libraries/LibWeb/idl_files.cmake
+++ b/Userland/Libraries/LibWeb/idl_files.cmake
@@ -337,6 +337,7 @@ libweb_js_bindings(WebAudio/AudioBuffer)
libweb_js_bindings(WebAudio/AudioBufferSourceNode)
libweb_js_bindings(WebAudio/AudioContext)
libweb_js_bindings(WebAudio/AudioDestinationNode)
+libweb_js_bindings(WebAudio/AudioListener)
libweb_js_bindings(WebAudio/AudioNode)
libweb_js_bindings(WebAudio/AudioParam)
libweb_js_bindings(WebAudio/AudioScheduledSourceNode)