2024-07-21 15:24:25 +03:00
/*
* Copyright ( c ) 2024 , Bar Yemini < bar . ye651 @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibWeb/Bindings/AudioScheduledSourceNodePrototype.h>
# include <LibWeb/Bindings/Intrinsics.h>
2024-07-27 17:19:38 +03:00
# include <LibWeb/WebAudio/AudioBuffer.h>
2024-07-21 15:24:25 +03:00
# include <LibWeb/WebAudio/AudioBufferSourceNode.h>
2024-07-27 17:19:38 +03:00
# include <LibWeb/WebAudio/AudioParam.h>
2024-07-21 15:24:25 +03:00
# include <LibWeb/WebAudio/AudioScheduledSourceNode.h>
namespace Web : : WebAudio {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( AudioBufferSourceNode ) ;
2024-07-21 15:24:25 +03:00
2024-11-15 04:01:23 +13:00
AudioBufferSourceNode : : AudioBufferSourceNode ( JS : : Realm & realm , GC : : Ref < BaseAudioContext > context , AudioBufferSourceOptions const & options )
2024-07-21 15:24:25 +03:00
: AudioScheduledSourceNode ( realm , context )
2024-07-27 17:19:38 +03:00
, m_buffer ( options . buffer )
2025-01-19 03:47:18 +00:00
, m_playback_rate ( AudioParam : : create ( realm , context , options . playback_rate , NumericLimits < float > : : lowest ( ) , NumericLimits < float > : : max ( ) , Bindings : : AutomationRate : : KRate , AudioParam : : FixedAutomationRate : : Yes ) )
, m_detune ( AudioParam : : create ( realm , context , options . detune , NumericLimits < float > : : lowest ( ) , NumericLimits < float > : : max ( ) , Bindings : : AutomationRate : : KRate , AudioParam : : FixedAutomationRate : : Yes ) )
2024-07-27 17:19:38 +03:00
, m_loop ( options . loop )
, m_loop_start ( options . loop_start )
, m_loop_end ( options . loop_end )
2024-07-21 15:24:25 +03:00
{
}
AudioBufferSourceNode : : ~ AudioBufferSourceNode ( ) = default ;
2024-07-27 17:19:38 +03:00
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < void > AudioBufferSourceNode : : set_buffer ( GC : : Ptr < AudioBuffer > buffer )
2024-07-27 17:19:38 +03:00
{
2025-01-02 01:09:07 +00:00
// 1. Let new buffer be the AudioBuffer or null value to be assigned to buffer.
auto new_buffer = buffer ;
// 2. If new buffer is not null and [[buffer set]] is true, throw an InvalidStateError and abort these steps.
if ( new_buffer & & m_buffer_set )
2025-08-07 19:31:52 -04:00
return WebIDL : : InvalidStateError : : create ( realm ( ) , " Buffer has already been set " _utf16 ) ;
2025-01-02 01:09:07 +00:00
// 3. If new buffer is not null, set [[buffer set]] to true.
if ( new_buffer )
m_buffer_set = true ;
// 4. Assign new buffer to the buffer attribute.
m_buffer = new_buffer ;
// FIXME: 5. If start() has previously been called on this node, perform the operation acquire the content on buffer.
2024-07-27 17:19:38 +03:00
return { } ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-buffer
2024-11-15 04:01:23 +13:00
GC : : Ptr < AudioBuffer > AudioBufferSourceNode : : buffer ( ) const
2024-07-27 17:19:38 +03:00
{
return m_buffer ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-playbackrate
2024-11-15 04:01:23 +13:00
GC : : Ref < AudioParam > AudioBufferSourceNode : : playback_rate ( ) const
2024-07-27 17:19:38 +03:00
{
return m_playback_rate ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-detune
2024-11-15 04:01:23 +13:00
GC : : Ref < AudioParam > AudioBufferSourceNode : : detune ( ) const
2024-07-27 17:19:38 +03:00
{
return m_detune ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
WebIDL : : ExceptionOr < void > AudioBufferSourceNode : : set_loop ( bool loop )
{
m_loop = loop ;
return { } ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loop
bool AudioBufferSourceNode : : loop ( ) const
{
return m_loop ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
WebIDL : : ExceptionOr < void > AudioBufferSourceNode : : set_loop_start ( double loop_start )
{
m_loop_start = loop_start ;
return { } ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopstart
double AudioBufferSourceNode : : loop_start ( ) const
{
return m_loop_start ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
WebIDL : : ExceptionOr < void > AudioBufferSourceNode : : set_loop_end ( double loop_end )
{
m_loop_end = loop_end ;
return { } ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-loopend
double AudioBufferSourceNode : : loop_end ( ) const
{
return m_loop_end ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start`
WebIDL : : ExceptionOr < void > AudioBufferSourceNode : : start ( Optional < double > when , Optional < double > offset , Optional < double > duration )
{
2025-01-09 23:47:30 +00:00
// 1. If this AudioBufferSourceNode internal slot [[source started]] is true, an InvalidStateError exception MUST be thrown.
if ( source_started ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : InvalidStateError : : create ( realm ( ) , " AudioBufferSourceNode has already been started " _utf16 ) ;
2025-01-09 23:47:30 +00:00
// 2. Check for any errors that must be thrown due to parameter constraints described below. If any exception is thrown during this step, abort those steps.
// A RangeError exception MUST be thrown if when is negative.
if ( when . has_value ( ) & & when . value ( ) < 0 )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " when must not be negative " sv } ;
// A RangeError exception MUST be thrown if offset is negative
if ( offset . has_value ( ) & & offset . value ( ) < 0 )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " offset must not be negative " sv } ;
// A RangeError exception MUST be thrown if duration is negative.
if ( duration . has_value ( ) & & duration . value ( ) < 0 )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " duration must not be negative " sv } ;
// 3. Set the internal slot [[source started]] on this AudioBufferSourceNode to true.
set_source_started ( true ) ;
// FIXME: 4. Queue a control message to start the AudioBufferSourceNode, including the parameter values in the message.
// FIXME: 5. Acquire the contents of the buffer if the buffer has been set.
// FIXME: 6. Send a control message to the associated AudioContext to start running its rendering thread only when all the following conditions are met:
2024-07-27 17:19:38 +03:00
dbgln ( " FIXME: Implement AudioBufferSourceNode::start(when, offset, duration) " ) ;
return { } ;
}
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < AudioBufferSourceNode > > AudioBufferSourceNode : : create ( JS : : Realm & realm , GC : : Ref < BaseAudioContext > context , AudioBufferSourceOptions const & options )
2024-07-21 15:24:25 +03:00
{
return construct_impl ( realm , context , options ) ;
}
// https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-audiobuffersourcenode
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < AudioBufferSourceNode > > AudioBufferSourceNode : : construct_impl ( JS : : Realm & realm , GC : : Ref < BaseAudioContext > context , AudioBufferSourceOptions const & options )
2024-07-21 15:24:25 +03:00
{
// When the constructor is called with a BaseAudioContext c and an option object option, the user agent
// MUST initialize the AudioNode this, with context and options as arguments.
2024-11-14 05:50:17 +13:00
auto node = realm . create < AudioBufferSourceNode > ( realm , context , options ) ;
2025-01-04 02:21:16 +00:00
// Default options for channel count and interpretation
// https://webaudio.github.io/web-audio-api/#AudioBufferSourceNode
AudioNodeDefaultOptions default_options ;
default_options . channel_count = 2 ;
default_options . channel_count_mode = Bindings : : ChannelCountMode : : Max ;
default_options . channel_interpretation = Bindings : : ChannelInterpretation : : Speakers ;
// FIXME: Set tail-time to no
TRY ( node - > initialize_audio_node_options ( options , default_options ) ) ;
2024-07-21 15:24:25 +03:00
return node ;
}
void AudioBufferSourceNode : : initialize ( JS : : Realm & realm )
{
WEB_SET_PROTOTYPE_FOR_INTERFACE ( AudioBufferSourceNode ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2024-07-21 15:24:25 +03:00
}
void AudioBufferSourceNode : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-07-27 17:19:38 +03:00
visitor . visit ( m_buffer ) ;
visitor . visit ( m_playback_rate ) ;
visitor . visit ( m_detune ) ;
2024-07-21 15:24:25 +03:00
}
}