Commit ccfa5609 authored by Raymond Toy's avatar Raymond Toy Committed by Commit Bot

If AudioWorklet AudioParam is constant, make the array have length 1

If the AudioParam is constant over a rendering quantum, we're allowed
to set the parameter value array to have a length one to indicate that
the audio param is constant.

Make this so if the audio param is truly constant.

Bug: 814796
Test: audioworklet-audioparam-size.https.html
Change-Id: Ib6e5805dbeb418ad3c202c9c79adc87bc93dad4a
Reviewed-on: https://chromium-review.googlesource.com/1097977Reviewed-by: default avatarHongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#573985}
parent de8b0995
<!doctype html>
<html>
<head>
<title>
Test AudioParam Array Size
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit.js"></script>
<script src="/webaudio/resources/audit-util.js"></script>
</head>
<body>
<script>
let audit = Audit.createTaskRunner();
let filePath = 'processors/param-size-processor.js';
let context;
// Use a power of two so there's no roundoff computing times from frames.
let sampleRate = 16384;
// Sets up AudioWorklet and OfflineAudioContext.
audit.define('Initializing AudioWorklet and Context', (task, should) => {
should(() => {
context = new OfflineAudioContext(
1, 10 * RENDER_QUANTUM_FRAMES, sampleRate);
}, 'Creating offline context for testing').notThrow();
should(
context.audioWorklet.addModule(filePath), 'Creating test worklet')
.beResolved()
.then(() => {
task.done();
});
});
audit.define('Verify Size of AudioParam Arrays', (task, should) => {
let node = new AudioWorkletNode(context, 'param-size');
let nodeParam = node.parameters.get('param');
node.connect(context.destination);
let renderQuantumDuration = RENDER_QUANTUM_FRAMES / context.sampleRate;
// Set up some automations, after one render quantum. We want the first
// render not to have any automations, just to be sure we handle that
// case correctly.
context.suspend(renderQuantumDuration)
.then(() => {
let now = context.currentTime;
// Establish the first automation event.
nodeParam.setValueAtTime(1, now);
// The second render should be constant
nodeParam.setValueAtTime(0, now + renderQuantumDuration);
// The third render and part of the fourth is a linear ramp
nodeParam.linearRampToValueAtTime(
1, now + 2.5 * renderQuantumDuration);
// Everything afterwards should be constant.
})
.then(() => context.resume());
context.startRendering()
.then(renderedBuffer => {
let data = renderedBuffer.getChannelData(0);
// The very first render quantum should be constant, so the array
// has length 1.
should(
data.slice(0, RENDER_QUANTUM_FRAMES),
'Render quantum 0: array size')
.beConstantValueOf(1);
should(
data.slice(RENDER_QUANTUM_FRAMES, 2 * RENDER_QUANTUM_FRAMES),
'Render quantum 1: array size')
.beConstantValueOf(1);
should(
data.slice(
2 * RENDER_QUANTUM_FRAMES, 4 * RENDER_QUANTUM_FRAMES),
'Render quantum 2-3: array size')
.beConstantValueOf(RENDER_QUANTUM_FRAMES);
should(
data.slice(4 * RENDER_QUANTUM_FRAMES),
'Remaining renders: array size')
.beConstantValueOf(1);
})
.then(() => task.done());
});
audit.run();
</script>
</body>
</html>
......@@ -22,8 +22,13 @@ class GainProcessor extends AudioWorkletProcessor {
for (let channel = 0; channel < input.length; ++channel) {
let inputChannel = input[channel];
let outputChannel = output[channel];
for (let i = 0; i < inputChannel.length; ++i)
outputChannel[i] = inputChannel[i] * gain[i];
if (gain.length === 1) {
for (let i = 0; i < inputChannel.length; ++i)
outputChannel[i] = inputChannel[i] * gain[0];
} else {
for (let i = 0; i < inputChannel.length; ++i)
outputChannel[i] = inputChannel[i] * gain[i];
}
}
return true;
......
/**
* @class ParamSizeProcessor
* @extends AudioWorkletProcessor
*
* This processor is a source node which basically outputs the size of the
* AudioParam array for each render quantum.
*/
class ParamSizeProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{name: 'param'}];
}
constructor() {
super();
}
process(inputs, outputs, parameters) {
let output = outputs[0];
let param = parameters.param;
for (let channel = 0; channel < output.length; ++channel) {
output[channel].fill(param.length);
}
return true;
}
}
registerProcessor('param-size', ParamSizeProcessor);
......@@ -289,10 +289,22 @@ bool AudioWorkletGlobalScope::Process(
for (const auto& param : *param_value_map) {
const String& param_name = param.key;
const AudioFloatArray* param_array = param.value.get();
// If the AudioParam is constant, then the param array should have length 1.
// Manually check to see if the parameter is truly constant.
unsigned array_size = 1;
for (unsigned k = 1; k < param_array->size(); ++k) {
if (param_array->Data()[k] != param_array->Data()[0]) {
array_size = param_array->size();
break;
}
}
v8::Local<v8::ArrayBuffer> array_buffer =
v8::ArrayBuffer::New(isolate, param_array->size() * sizeof(float));
v8::ArrayBuffer::New(isolate, array_size * sizeof(float));
v8::Local<v8::Float32Array> float32_array =
v8::Float32Array::New(array_buffer, 0, param_array->size());
v8::Float32Array::New(array_buffer, 0, array_size);
bool success;
if (!param_values
->CreateDataProperty(current_context,
......@@ -302,8 +314,7 @@ bool AudioWorkletGlobalScope::Process(
return false;
}
const v8::ArrayBuffer::Contents& contents = array_buffer->GetContents();
memcpy(contents.Data(), param_array->Data(),
param_array->size() * sizeof(float));
memcpy(contents.Data(), param_array->Data(), array_size * sizeof(float));
}
v8::Local<v8::Value> argv[] = {inputs, outputs, param_values};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment