mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 09:39:39 +00:00
LibWeb: Add BaseAudioContext.create_periodic_wave()
factory method
This commit is contained in:
parent
f90b79fa1f
commit
a6f04506e4
Notes:
github-actions[bot]
2025-01-03 13:55:11 +00:00
Author: https://github.com/tcl3
Commit: a6f04506e4
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3104
5 changed files with 179 additions and 1 deletions
|
@ -134,6 +134,17 @@ WebIDL::ExceptionOr<GC::Ref<PannerNode>> BaseAudioContext::create_panner()
|
||||||
return PannerNode::create(realm(), *this);
|
return PannerNode::create(realm(), *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebIDL::ExceptionOr<GC::Ref<PeriodicWave>> BaseAudioContext::create_periodic_wave(Vector<float> const& real, Vector<float> const& imag, Optional<PeriodicWaveConstraints> const& constraints)
|
||||||
|
{
|
||||||
|
PeriodicWaveOptions options;
|
||||||
|
options.real = real;
|
||||||
|
options.imag = imag;
|
||||||
|
if (constraints.has_value())
|
||||||
|
options.disable_normalization = constraints->disable_normalization;
|
||||||
|
|
||||||
|
return PeriodicWave::construct_impl(realm(), *this, options);
|
||||||
|
}
|
||||||
|
|
||||||
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer
|
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer
|
||||||
WebIDL::ExceptionOr<void> BaseAudioContext::verify_audio_options_inside_nominal_range(JS::Realm& realm, WebIDL::UnsignedLong number_of_channels, WebIDL::UnsignedLong length, float sample_rate)
|
WebIDL::ExceptionOr<void> BaseAudioContext::verify_audio_options_inside_nominal_range(JS::Realm& realm, WebIDL::UnsignedLong number_of_channels, WebIDL::UnsignedLong length, float sample_rate)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibWeb/WebAudio/ChannelMergerNode.h>
|
#include <LibWeb/WebAudio/ChannelMergerNode.h>
|
||||||
#include <LibWeb/WebAudio/ChannelSplitterNode.h>
|
#include <LibWeb/WebAudio/ChannelSplitterNode.h>
|
||||||
#include <LibWeb/WebAudio/ConstantSourceNode.h>
|
#include <LibWeb/WebAudio/ConstantSourceNode.h>
|
||||||
|
#include <LibWeb/WebAudio/PeriodicWave.h>
|
||||||
#include <LibWeb/WebIDL/Types.h>
|
#include <LibWeb/WebIDL/Types.h>
|
||||||
|
|
||||||
namespace Web::WebAudio {
|
namespace Web::WebAudio {
|
||||||
|
@ -67,6 +68,7 @@ public:
|
||||||
WebIDL::ExceptionOr<GC::Ref<DynamicsCompressorNode>> create_dynamics_compressor();
|
WebIDL::ExceptionOr<GC::Ref<DynamicsCompressorNode>> create_dynamics_compressor();
|
||||||
WebIDL::ExceptionOr<GC::Ref<GainNode>> create_gain();
|
WebIDL::ExceptionOr<GC::Ref<GainNode>> create_gain();
|
||||||
WebIDL::ExceptionOr<GC::Ref<PannerNode>> create_panner();
|
WebIDL::ExceptionOr<GC::Ref<PannerNode>> create_panner();
|
||||||
|
WebIDL::ExceptionOr<GC::Ref<PeriodicWave>> create_periodic_wave(Vector<float> const& real, Vector<float> const& imag, Optional<PeriodicWaveConstraints> const& constraints = {});
|
||||||
|
|
||||||
GC::Ref<WebIDL::Promise> decode_audio_data(GC::Root<WebIDL::BufferSource>, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
|
GC::Ref<WebIDL::Promise> decode_audio_data(GC::Root<WebIDL::BufferSource>, GC::Ptr<WebIDL::CallbackType>, GC::Ptr<WebIDL::CallbackType>);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ interface BaseAudioContext : EventTarget {
|
||||||
[FIXME] IIRFilterNode createIIRFilter (sequence<double> feedforward, sequence<double> feedback);
|
[FIXME] IIRFilterNode createIIRFilter (sequence<double> feedforward, sequence<double> feedback);
|
||||||
OscillatorNode createOscillator();
|
OscillatorNode createOscillator();
|
||||||
PannerNode createPanner();
|
PannerNode createPanner();
|
||||||
[FIXME] PeriodicWave createPeriodicWave (sequence<float> real, sequence<float> imag, optional PeriodicWaveConstraints constraints = {});
|
PeriodicWave createPeriodicWave (sequence<float> real, sequence<float> imag, optional PeriodicWaveConstraints constraints = {});
|
||||||
[FIXME] ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0, optional unsigned long numberOfInputChannels = 2, optional unsigned long numberOfOutputChannels = 2);
|
[FIXME] ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0, optional unsigned long numberOfInputChannels = 2, optional unsigned long numberOfOutputChannels = 2);
|
||||||
[FIXME] StereoPannerNode createStereoPanner ();
|
[FIXME] StereoPannerNode createStereoPanner ();
|
||||||
[FIXME] WaveShaperNode createWaveShaper ();
|
[FIXME] WaveShaperNode createWaveShaper ();
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 29 tests
|
||||||
|
|
||||||
|
28 Pass
|
||||||
|
1 Fail
|
||||||
|
Pass # AUDIT TASK RUNNER STARTED.
|
||||||
|
Pass Executing "create with factory method"
|
||||||
|
Pass Executing "different length with factory method"
|
||||||
|
Pass Executing "too small with factory method"
|
||||||
|
Pass Executing "create with constructor"
|
||||||
|
Pass Executing "different length with constructor"
|
||||||
|
Pass Executing "too small with constructor"
|
||||||
|
Fail Executing "output test"
|
||||||
|
Pass Audit report
|
||||||
|
Pass > [create with factory method]
|
||||||
|
Pass context.createPeriodicWave(new Float32Array(8192), new Float32Array(8192)) did not throw an exception.
|
||||||
|
Pass < [create with factory method] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [different length with factory method]
|
||||||
|
Pass context.createPeriodicWave(new Float32Array(512), new Float32Array(4)) threw IndexSizeError: "Real and imaginary arrays must have the same length and contain at least 2 elements".
|
||||||
|
Pass < [different length with factory method] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [too small with factory method]
|
||||||
|
Pass context.createPeriodicWave(new Float32Array(1), new Float32Array(1)) threw IndexSizeError: "Real and imaginary arrays must have the same length and contain at least 2 elements".
|
||||||
|
Pass < [too small with factory method] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [create with constructor]
|
||||||
|
Pass new PeriodicWave(context, { real : new Float32Array(8192), imag : new Float32Array(8192) }) did not throw an exception.
|
||||||
|
Pass < [create with constructor] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [different length with constructor]
|
||||||
|
Pass new PeriodicWave(context, { real : new Float32Array(8192), imag : new Float32Array(4) }) threw IndexSizeError: "Real and imaginary arrays must have the same length and contain at least 2 elements".
|
||||||
|
Pass < [different length with constructor] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [too small with constructor]
|
||||||
|
Pass new PeriodicWave(context, { real : new Float32Array(1), imag : new Float32Array(1) }) threw IndexSizeError: "Real and imaginary arrays must have the same length and contain at least 2 elements".
|
||||||
|
Pass < [too small with constructor] All assertions passed. (total 1 assertions)
|
||||||
|
Pass > [output test]
|
||||||
|
Pass # AUDIT TASK RUNNER FINISHED: 7 tasks ran successfully.
|
|
@ -0,0 +1,130 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
Test Constructor: PeriodicWave
|
||||||
|
</title>
|
||||||
|
<script src="../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../../webaudio/resources/audit-util.js"></script>
|
||||||
|
<script src="../../../webaudio/resources/audit.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script id="layout-test-code">
|
||||||
|
// real and imag are used in separate PeriodicWaves to make their peak values
|
||||||
|
// easy to determine.
|
||||||
|
const realMax = 99;
|
||||||
|
var real = new Float32Array(realMax + 1);
|
||||||
|
real[1] = 2.0; // fundamental
|
||||||
|
real[realMax] = 3.0;
|
||||||
|
const realPeak = real[1] + real[realMax];
|
||||||
|
const realFundamental = 19.0;
|
||||||
|
var imag = new Float32Array(4);
|
||||||
|
imag[0] = 6.0; // should be ignored.
|
||||||
|
imag[3] = 0.5;
|
||||||
|
const imagPeak = imag[3];
|
||||||
|
const imagFundamental = 551.0;
|
||||||
|
|
||||||
|
const testLength = 8192;
|
||||||
|
let context = new AudioContext();
|
||||||
|
|
||||||
|
let audit = Audit.createTaskRunner();
|
||||||
|
|
||||||
|
// Create with the factory method
|
||||||
|
|
||||||
|
audit.define('create with factory method', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
context.createPeriodicWave(new Float32Array(testLength), new Float32Array(testLength));
|
||||||
|
}, 'context.createPeriodicWave(new Float32Array(' + testLength + '), ' +
|
||||||
|
'new Float32Array(' + testLength + '))').notThrow();
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.define('different length with factory method', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
context.createPeriodicWave(new Float32Array(512), new Float32Array(4));
|
||||||
|
}, 'context.createPeriodicWave(new Float32Array(512), ' +
|
||||||
|
'new Float32Array(4))').throw(DOMException, "IndexSizeError");
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.define('too small with factory method', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
context.createPeriodicWave(new Float32Array(1), new Float32Array(1));
|
||||||
|
}, 'context.createPeriodicWave(new Float32Array(1), ' +
|
||||||
|
'new Float32Array(1))').throw(DOMException, "IndexSizeError");
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create with the constructor
|
||||||
|
|
||||||
|
audit.define('create with constructor', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
new PeriodicWave(context, { real: new Float32Array(testLength), imag: new Float32Array(testLength) });
|
||||||
|
}, 'new PeriodicWave(context, { real : new Float32Array(' + testLength + '), ' +
|
||||||
|
'imag : new Float32Array(' + testLength + ') })').notThrow();
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.define('different length with constructor', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
new PeriodicWave(context, { real: new Float32Array(testLength), imag: new Float32Array(4) });
|
||||||
|
}, 'new PeriodicWave(context, { real : new Float32Array(' + testLength + '), ' +
|
||||||
|
'imag : new Float32Array(4) })').throw(DOMException, "IndexSizeError");
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.define('too small with constructor', (task, should) => {
|
||||||
|
should(() => {
|
||||||
|
new PeriodicWave(context, { real: new Float32Array(1), imag: new Float32Array(1) });
|
||||||
|
}, 'new PeriodicWave(context, { real : new Float32Array(1), ' +
|
||||||
|
'imag : new Float32Array(1) })').throw(DOMException, "IndexSizeError");
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.define('output test', (task, should) => {
|
||||||
|
let context = new OfflineAudioContext(2, testLength, 44100);
|
||||||
|
// Create the expected output buffer
|
||||||
|
let expectations = context.createBuffer(2, testLength, context.sampleRate);
|
||||||
|
for (var i = 0; i < expectations.length; ++i) {
|
||||||
|
|
||||||
|
expectations.getChannelData(0)[i] = 1.0 / realPeak *
|
||||||
|
(real[1] * Math.cos(2 * Math.PI * realFundamental * i /
|
||||||
|
context.sampleRate) +
|
||||||
|
real[realMax] * Math.cos(2 * Math.PI * realMax * realFundamental * i /
|
||||||
|
context.sampleRate));
|
||||||
|
|
||||||
|
expectations.getChannelData(1)[i] = 1.0 / imagPeak *
|
||||||
|
imag[3] * Math.sin(2 * Math.PI * 3 * imagFundamental * i /
|
||||||
|
context.sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the real output buffer
|
||||||
|
let merger = context.createChannelMerger();
|
||||||
|
|
||||||
|
let osc1 = context.createOscillator();
|
||||||
|
let osc2 = context.createOscillator();
|
||||||
|
|
||||||
|
osc1.setPeriodicWave(context.createPeriodicWave(
|
||||||
|
real, new Float32Array(real.length)));
|
||||||
|
osc2.setPeriodicWave(context.createPeriodicWave(
|
||||||
|
new Float32Array(imag.length), imag));
|
||||||
|
osc1.frequency.value = realFundamental;
|
||||||
|
osc2.frequency.value = imagFundamental;
|
||||||
|
|
||||||
|
osc1.start();
|
||||||
|
osc2.start();
|
||||||
|
|
||||||
|
osc1.connect(merger, 0, 0);
|
||||||
|
osc2.connect(merger, 0, 1);
|
||||||
|
|
||||||
|
context.startRendering().then(reality => {
|
||||||
|
should(reality, 'rendering PeriodicWave').beEqualToArray(expectations);
|
||||||
|
task.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
audit.run();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue