mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
LibWeb: Add barebones BaseAudioContext.decodeAudioData()
Implement just enough steps to get https://zty.pe/ working! :^) (cherry picked from commit 14b2e5849d4b82aec6900253f6f035a9b356bad3)
This commit is contained in:
parent
4e0c54fa61
commit
fb8870d710
3 changed files with 130 additions and 3 deletions
|
@ -9,6 +9,8 @@
|
|||
#include <LibWeb/Bindings/BaseAudioContextPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/WebAudio/AudioBuffer.h>
|
||||
#include <LibWeb/WebAudio/AudioBufferSourceNode.h>
|
||||
#include <LibWeb/WebAudio/AudioDestinationNode.h>
|
||||
|
@ -17,6 +19,8 @@
|
|||
#include <LibWeb/WebAudio/DynamicsCompressorNode.h>
|
||||
#include <LibWeb/WebAudio/GainNode.h>
|
||||
#include <LibWeb/WebAudio/OscillatorNode.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
namespace Web::WebAudio {
|
||||
|
||||
|
@ -120,4 +124,122 @@ void BaseAudioContext::queue_a_media_element_task(JS::NonnullGCPtr<JS::HeapFunct
|
|||
HTML::main_thread_event_loop().task_queue().add(task);
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
|
||||
JS::NonnullGCPtr<JS::Promise> BaseAudioContext::decode_audio_data(JS::Handle<WebIDL::BufferSource> audio_data, JS::GCPtr<WebIDL::CallbackType> success_callback, JS::GCPtr<WebIDL::CallbackType> error_callback)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: When decodeAudioData is called, the following steps MUST be performed on the control thread:
|
||||
|
||||
// 1. If this's relevant global object's associated Document is not fully active then return a
|
||||
// promise rejected with "InvalidStateError" DOMException.
|
||||
auto const& associated_document = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).associated_document();
|
||||
if (!associated_document.is_fully_active()) {
|
||||
auto error = WebIDL::InvalidStateError::create(realm, "The document is not fully active."_string);
|
||||
return WebIDL::create_rejected_promise_from_exception(realm, error);
|
||||
}
|
||||
|
||||
// 2. Let promise be a new Promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// FIXME: 3. If audioData is detached, execute the following steps:
|
||||
if (true) {
|
||||
// FIXME: 3.1. Append promise to [[pending promises]].
|
||||
|
||||
// FIXME: 3.2. Detach the audioData ArrayBuffer. If this operations throws, jump to the step 3.
|
||||
|
||||
// 3.3. Queue a decoding operation to be performed on another thread.
|
||||
queue_a_decoding_operation(promise, move(audio_data), success_callback, error_callback);
|
||||
}
|
||||
|
||||
// 4. Else, execute the following error steps:
|
||||
else {
|
||||
// 4.1. Let error be a DataCloneError.
|
||||
auto error = WebIDL::DataCloneError::create(realm, "Audio data is not detached."_string);
|
||||
|
||||
// 4.2. Reject promise with error, and remove it from [[pending promises]].
|
||||
WebIDL::reject_promise(realm, promise, error);
|
||||
|
||||
// 4.3. Queue a media element task to invoke errorCallback with error.
|
||||
if (error_callback) {
|
||||
queue_a_media_element_task(JS::create_heap_function(heap(), [&realm, error_callback, error] {
|
||||
auto completion = WebIDL::invoke_callback(*error_callback, {}, error);
|
||||
if (completion.is_abrupt())
|
||||
HTML::report_exception(completion, realm);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Return promise.
|
||||
return verify_cast<JS::Promise>(*promise->promise());
|
||||
}
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
|
||||
void BaseAudioContext::queue_a_decoding_operation(JS::NonnullGCPtr<JS::PromiseCapability> promise, [[maybe_unused]] JS::Handle<WebIDL::BufferSource> audio_data, JS::GCPtr<WebIDL::CallbackType> success_callback, JS::GCPtr<WebIDL::CallbackType> error_callback)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: When queuing a decoding operation to be performed on another thread, the following steps
|
||||
// MUST happen on a thread that is not the control thread nor the rendering thread, called
|
||||
// the decoding thread.
|
||||
|
||||
// 1. Let can decode be a boolean flag, initially set to true.
|
||||
auto can_decode { true };
|
||||
|
||||
// FIXME: 2. Attempt to determine the MIME type of audioData, using MIME Sniffing § 6.2 Matching an
|
||||
// audio or video type pattern. If the audio or video type pattern matching algorithm returns
|
||||
// undefined, set can decode to false.
|
||||
|
||||
// 3. If can decode is true,
|
||||
if (can_decode) {
|
||||
// FIXME: attempt to decode the encoded audioData into linear PCM. In case of
|
||||
// failure, set can decode to false.
|
||||
|
||||
// FIXME: If the media byte-stream contains multiple audio tracks, only decode the first track to linear pcm.
|
||||
}
|
||||
|
||||
// 4. If can decode is false,
|
||||
if (!can_decode) {
|
||||
// queue a media element task to execute the following steps:
|
||||
queue_a_media_element_task(JS::create_heap_function(heap(), [&realm, promise, error_callback] {
|
||||
// 4.1. Let error be a DOMException whose name is EncodingError.
|
||||
auto error = WebIDL::EncodingError::create(realm, "Unable to decode."_string);
|
||||
|
||||
// 4.1.2. Reject promise with error,
|
||||
WebIDL::reject_promise(realm, promise, error);
|
||||
// FIXME: and remove it from [[pending promises]].
|
||||
|
||||
// 4.2. If errorCallback is not missing, invoke errorCallback with error.
|
||||
if (error_callback) {
|
||||
auto completion = WebIDL::invoke_callback(*error_callback, {}, error);
|
||||
if (completion.is_abrupt())
|
||||
HTML::report_exception(completion, realm);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 5. Otherwise:
|
||||
else {
|
||||
// FIXME: 5.1. Take the result, representing the decoded linear PCM audio data, and resample it to the
|
||||
// sample-rate of the BaseAudioContext if it is different from the sample-rate of
|
||||
// audioData.
|
||||
|
||||
// FIXME: 5.2. queue a media element task to execute the following steps:
|
||||
|
||||
// FIXME: 5.2.1. Let buffer be an AudioBuffer containing the final result (after possibly performing
|
||||
// sample-rate conversion).
|
||||
auto buffer = MUST(create_buffer(2, 1, 44100));
|
||||
|
||||
// 5.2.2. Resolve promise with buffer.
|
||||
WebIDL::resolve_promise(realm, promise, buffer);
|
||||
|
||||
// 5.2.3. If successCallback is not missing, invoke successCallback with buffer.
|
||||
if (success_callback) {
|
||||
auto completion = WebIDL::invoke_callback(*success_callback, {}, buffer);
|
||||
if (completion.is_abrupt())
|
||||
HTML::report_exception(completion, realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
WebIDL::ExceptionOr<JS::NonnullGCPtr<DynamicsCompressorNode>> create_dynamics_compressor();
|
||||
JS::NonnullGCPtr<GainNode> create_gain();
|
||||
|
||||
JS::NonnullGCPtr<JS::Promise> decode_audio_data(JS::Handle<WebIDL::BufferSource>, JS::GCPtr<WebIDL::CallbackType>, JS::GCPtr<WebIDL::CallbackType>);
|
||||
|
||||
protected:
|
||||
explicit BaseAudioContext(JS::Realm&, float m_sample_rate = 0);
|
||||
|
||||
|
@ -70,6 +72,8 @@ protected:
|
|||
JS::NonnullGCPtr<AudioDestinationNode> m_destination;
|
||||
|
||||
private:
|
||||
void queue_a_decoding_operation(JS::NonnullGCPtr<JS::PromiseCapability>, JS::Handle<WebIDL::BufferSource>, JS::GCPtr<WebIDL::CallbackType>, JS::GCPtr<WebIDL::CallbackType>);
|
||||
|
||||
float m_sample_rate { 0 };
|
||||
double m_current_time { 0 };
|
||||
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
#import <WebAudio/DynamicsCompressorNode.idl>
|
||||
#import <WebAudio/GainNode.idl>
|
||||
#import <WebAudio/OscillatorNode.idl>
|
||||
#import <WebIDL/DOMException.idl>
|
||||
|
||||
// https://www.w3.org/TR/webaudio/#enumdef-audiocontextstate
|
||||
enum AudioContextState { "suspended", "running", "closed" };
|
||||
|
||||
// FIXME: callback DecodeErrorCallback = undefined (DOMException error);
|
||||
callback DecodeErrorCallback = undefined (DOMException error);
|
||||
|
||||
// FIXME: callback DecodeSuccessCallback = undefined (AudioBuffer decodedData);
|
||||
callback DecodeSuccessCallback = undefined (AudioBuffer decodedData);
|
||||
|
||||
// https://webaudio.github.io/web-audio-api/#BaseAudioContext
|
||||
[Exposed=Window]
|
||||
|
@ -45,5 +46,5 @@ interface BaseAudioContext : EventTarget {
|
|||
[FIXME] StereoPannerNode createStereoPanner ();
|
||||
[FIXME] WaveShaperNode createWaveShaper ();
|
||||
|
||||
[FIXME] Promise<AudioBuffer> decodeAudioData (ArrayBuffer audioData, optional DecodeSuccessCallback? successCallback, optional DecodeErrorCallback? errorCallback);
|
||||
Promise<AudioBuffer> decodeAudioData (ArrayBuffer audioData, optional DecodeSuccessCallback? successCallback, optional DecodeErrorCallback? errorCallback);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue