/* * Copyright (c) 2023, Luke Wilde * Copyright (c) 2024, Shannon Booth * Copyright (c) 2024, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Web::WebAudio { class AudioDestinationNode; // https://webaudio.github.io/web-audio-api/#BaseAudioContext class BaseAudioContext : public DOM::EventTarget { WEB_PLATFORM_OBJECT(BaseAudioContext, DOM::EventTarget); public: virtual ~BaseAudioContext() override; // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer-numberofchannels // > An implementation MUST support at least 32 channels. // Other browsers appear to only allow 32 channels - so let's limit ourselves to that too. static constexpr WebIDL::UnsignedLong MAX_NUMBER_OF_CHANNELS { 32 }; // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer-samplerate // > An implementation MUST support sample rates in at least the range 8000 to 96000. // This doesn't seem consistent between browsers. We use what firefox accepts from testing BaseAudioContext.createAudioBuffer. static constexpr float MIN_SAMPLE_RATE { 8000 }; static constexpr float MAX_SAMPLE_RATE { 192000 }; GC::Ref destination() const { return *m_destination; } float sample_rate() const { return m_sample_rate; } double current_time() const { return m_current_time; } GC::Ref listener() const { return m_listener; } Bindings::AudioContextState state() const { return m_control_thread_state; } // https://webaudio.github.io/web-audio-api/#--nyquist-frequency float nyquist_frequency() const { return m_sample_rate / 2; } void set_onstatechange(WebIDL::CallbackType*); WebIDL::CallbackType* onstatechange(); void set_sample_rate(float sample_rate) { m_sample_rate = sample_rate; } void set_control_state(Bindings::AudioContextState state) { m_control_thread_state = state; } void set_rendering_state(Bindings::AudioContextState state) { m_rendering_thread_state = state; } static WebIDL::ExceptionOr verify_audio_options_inside_nominal_range(JS::Realm&, float sample_rate); static WebIDL::ExceptionOr verify_audio_options_inside_nominal_range(JS::Realm&, WebIDL::UnsignedLong number_of_channels, WebIDL::UnsignedLong length, float sample_rate); WebIDL::ExceptionOr> create_analyser(); 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_constant_source(); WebIDL::ExceptionOr> create_channel_splitter(WebIDL::UnsignedLong number_of_outputs); WebIDL::ExceptionOr> create_delay(double max_delay_time = 1); WebIDL::ExceptionOr> create_oscillator(); WebIDL::ExceptionOr> create_dynamics_compressor(); WebIDL::ExceptionOr> create_gain(); WebIDL::ExceptionOr> create_panner(); WebIDL::ExceptionOr> create_periodic_wave(Vector const& real, Vector const& imag, Optional const& constraints = {}); GC::Ref decode_audio_data(GC::Root, GC::Ptr, GC::Ptr); protected: explicit BaseAudioContext(JS::Realm&, float m_sample_rate = 0); void queue_a_media_element_task(GC::Ref>); virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; GC::Ptr m_destination; Vector> m_pending_promises; private: void queue_a_decoding_operation(GC::Ref, GC::Root, GC::Ptr, GC::Ptr); float m_sample_rate { 0 }; double m_current_time { 0 }; GC::Ref m_listener; Bindings::AudioContextState m_control_thread_state = Bindings::AudioContextState::Suspended; Bindings::AudioContextState m_rendering_thread_state = Bindings::AudioContextState::Suspended; HTML::UniqueTaskSource m_media_element_event_task_source {}; }; }