PannerNode.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright (c) 2024, Luke Wilde <luke@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Bindings/Intrinsics.h>
  7. #include <LibWeb/WebAudio/AudioNode.h>
  8. #include <LibWeb/WebAudio/AudioParam.h>
  9. #include <LibWeb/WebAudio/BaseAudioContext.h>
  10. #include <LibWeb/WebAudio/PannerNode.h>
  11. namespace Web::WebAudio {
  12. GC_DEFINE_ALLOCATOR(PannerNode);
  13. PannerNode::~PannerNode() = default;
  14. WebIDL::ExceptionOr<GC::Ref<PannerNode>> PannerNode::create(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
  15. {
  16. return construct_impl(realm, context, options);
  17. }
  18. // https://webaudio.github.io/web-audio-api/#dom-pannernode-pannernode
  19. WebIDL::ExceptionOr<GC::Ref<PannerNode>> PannerNode::construct_impl(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
  20. {
  21. // https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance
  22. // A RangeError exception MUST be thrown if this is set to a negative value.
  23. if (options.ref_distance < 0.0)
  24. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "refDistance cannot be negative"sv };
  25. // https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor
  26. // A RangeError exception MUST be thrown if this is set to a negative value.
  27. if (options.rolloff_factor < 0.0)
  28. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "rolloffFactor cannot be negative"sv };
  29. // https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance
  30. // A RangeError exception MUST be thrown if this is set to a non-positive value.
  31. if (options.max_distance < 0.0)
  32. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "maxDistance cannot be negative"sv };
  33. // https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain
  34. // It is a linear value (not dB) in the range [0, 1]. An InvalidStateError MUST be thrown if the parameter is outside this range.
  35. if (options.cone_outer_gain < 0.0 || options.cone_outer_gain > 1.0)
  36. return WebIDL::InvalidStateError::create(realm, "coneOuterGain must be in the range of [0, 1]"_string);
  37. // Create the node and allocate memory
  38. auto node = realm.create<PannerNode>(realm, context, options);
  39. // Default options for channel count and interpretation
  40. // https://webaudio.github.io/web-audio-api/#PannerNode
  41. AudioNodeDefaultOptions default_options;
  42. default_options.channel_count_mode = Bindings::ChannelCountMode::ClampedMax;
  43. default_options.channel_interpretation = Bindings::ChannelInterpretation::Speakers;
  44. default_options.channel_count = 2;
  45. // FIXME: Set tail-time to maybe
  46. TRY(node->initialize_audio_node_options(options, default_options));
  47. return node;
  48. }
  49. PannerNode::PannerNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
  50. : AudioNode(realm, context)
  51. , m_panning_model(options.panning_model)
  52. , m_position_x(AudioParam::create(realm, options.position_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  53. , m_position_y(AudioParam::create(realm, options.position_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  54. , m_position_z(AudioParam::create(realm, options.position_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  55. , m_orientation_x(AudioParam::create(realm, options.orientation_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  56. , m_orientation_y(AudioParam::create(realm, options.orientation_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  57. , m_orientation_z(AudioParam::create(realm, options.orientation_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
  58. , m_distance_model(options.distance_model)
  59. , m_ref_distance(options.ref_distance)
  60. , m_max_distance(options.max_distance)
  61. , m_rolloff_factor(options.rolloff_factor)
  62. , m_cone_inner_angle(options.cone_inner_angle)
  63. , m_cone_outer_angle(options.cone_outer_angle)
  64. , m_cone_outer_gain(options.cone_outer_gain)
  65. {
  66. }
  67. void PannerNode::initialize(JS::Realm& realm)
  68. {
  69. Base::initialize(realm);
  70. WEB_SET_PROTOTYPE_FOR_INTERFACE(PannerNode);
  71. }
  72. void PannerNode::visit_edges(Cell::Visitor& visitor)
  73. {
  74. Base::visit_edges(visitor);
  75. visitor.visit(m_position_x);
  76. visitor.visit(m_position_y);
  77. visitor.visit(m_position_z);
  78. visitor.visit(m_orientation_x);
  79. visitor.visit(m_orientation_y);
  80. visitor.visit(m_orientation_z);
  81. }
  82. // https://webaudio.github.io/web-audio-api/#dom-pannernode-refdistance
  83. WebIDL::ExceptionOr<void> PannerNode::set_ref_distance(double value)
  84. {
  85. // A RangeError exception MUST be thrown if this is set to a negative value.
  86. if (value < 0.0)
  87. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "refDistance cannot be negative"sv };
  88. m_ref_distance = value;
  89. return {};
  90. }
  91. // https://webaudio.github.io/web-audio-api/#dom-pannernode-maxdistance
  92. WebIDL::ExceptionOr<void> PannerNode::set_max_distance(double value)
  93. {
  94. // A RangeError exception MUST be thrown if this is set to a non-positive value.
  95. if (value < 0.0)
  96. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "maxDistance cannot be negative"sv };
  97. m_max_distance = value;
  98. return {};
  99. }
  100. // https://webaudio.github.io/web-audio-api/#dom-pannernode-rollofffactor
  101. WebIDL::ExceptionOr<void> PannerNode::set_rolloff_factor(double value)
  102. {
  103. // A RangeError exception MUST be thrown if this is set to a negative value.
  104. if (value < 0.0)
  105. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "rolloffFactor cannot be negative"sv };
  106. m_rolloff_factor = value;
  107. return {};
  108. }
  109. // https://webaudio.github.io/web-audio-api/#dom-pannernode-coneoutergain
  110. WebIDL::ExceptionOr<void> PannerNode::set_cone_outer_gain(double value)
  111. {
  112. // It is a linear value (not dB) in the range [0, 1]. An InvalidStateError MUST be thrown if the parameter is outside this range.
  113. if (value < 0.0 || value > 1.0)
  114. return WebIDL::InvalidStateError::create(realm(), "coneOuterGain must be in the range of [0, 1]"_string);
  115. m_cone_outer_gain = value;
  116. return {};
  117. }
  118. // https://webaudio.github.io/web-audio-api/#dom-pannernode-setposition
  119. WebIDL::ExceptionOr<void> PannerNode::set_position(float x, float y, float z)
  120. {
  121. // This method is DEPRECATED. It is equivalent to setting positionX.value, positionY.value, and positionZ.value
  122. // attribute directly with the x, y and z parameters, respectively.
  123. // FIXME: Consequently, if any of the positionX, positionY, and positionZ AudioParams have an automation curve
  124. // set using setValueCurveAtTime() at the time this method is called, a NotSupportedError MUST be thrown.
  125. m_position_x->set_value(x);
  126. m_position_y->set_value(y);
  127. m_position_z->set_value(z);
  128. return {};
  129. }
  130. // https://webaudio.github.io/web-audio-api/#dom-pannernode-setorientation
  131. WebIDL::ExceptionOr<void> PannerNode::set_orientation(float x, float y, float z)
  132. {
  133. // This method is DEPRECATED. It is equivalent to setting orientationX.value, orientationY.value, and
  134. // orientationZ.value attribute directly, with the x, y and z parameters, respectively.
  135. // FIXME: Consequently, if any of the orientationX, orientationY, and orientationZ AudioParams have an automation
  136. // curve set using setValueCurveAtTime() at the time this method is called, a NotSupportedError MUST be thrown.
  137. m_orientation_x->set_value(x);
  138. m_orientation_y->set_value(y);
  139. m_orientation_z->set_value(z);
  140. return {};
  141. }
  142. // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode
  143. WebIDL::ExceptionOr<void> PannerNode::set_channel_count_mode(Bindings::ChannelCountMode mode)
  144. {
  145. if (mode == Bindings::ChannelCountMode::Max) {
  146. return WebIDL::NotSupportedError::create(realm(), "PannerNode does not support 'max' as channelCountMode."_string);
  147. }
  148. return AudioNode::set_channel_count_mode(mode);
  149. }
  150. // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount
  151. WebIDL::ExceptionOr<void> PannerNode::set_channel_count(WebIDL::UnsignedLong channel_count)
  152. {
  153. if (channel_count > 2) {
  154. return WebIDL::NotSupportedError::create(realm(), "PannerNode does not support channel count greater than 2"_string);
  155. }
  156. return AudioNode::set_channel_count(channel_count);
  157. }
  158. }