Skip to content

Commit

Permalink
Bug 1644847 [wpt PR 24093] - Avoid memory allocation in AudioWorkletP…
Browse files Browse the repository at this point in the history
…rocessor.process(), a=testonly

Automatic update from web-platform-tests
Avoid memory allocation in AudioWorkletProcessor.process()

Based on the spec [1], the current implementation of
AudioWorkletProcessor needs to create a new data container
(i.e. WebIDL sequence<>) for input, output, and param arrays.

With the new spec change [2], this CL changes the overall design
of the audio processing callback:

1. Moves the processing call from AudioWorkletGlobalScope to
   AudioWorkletProcessor object.
2. AudioWorkletProcessor now keeps the data container within
   the object and allocate memory when it is needed.

The preliminary benchmark shows the sizable improvement in the
audio stream quality. The glitch score
(= buffer underrun/total callback) is improved by ~9x in the
low-tier machine. [3]

This is an API change [4], but the real world impact would be
negligible because there's no functionality change.

[1]: https://webaudio.github.io/web-audio-api/#dom-audioworkletprocessor-process-inputs-outputs-parameters-inputs
[2]: WebAudio/web-audio-api#1933 (comment)
[3]: https://bugs.chromium.org/p/chromium/issues/detail?id=1086665#c2
[4]: https://chromestatus.com/feature/5647541725036544

Bug: 1071085, 1086665
Change-Id: I3e664754973d4d86649d38c1807c6b9d7830fb96
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2218702
Reviewed-by: Raymond Toy <rtoy@chromium.org>
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#779052}

--

wpt-commits: 48a5574b7e1a3d7d952a4b9fe84b049375773e42
wpt-pr: 24093
  • Loading branch information
hoch authored and moz-wptsync-bot committed Jun 23, 2020
1 parent b74dcce commit 1330974
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>
<html>
<head>
<title>
Test given arrays within AudioWorkletProcessor.process() method
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webaudio/resources/audit.js"></script>
</head>

<body>
<script>
const audit = Audit.createTaskRunner();
const filePath = 'processors/array-check-processor.js';
const context = new AudioContext();

// Test if the incoming arrays are frozen as expected.
audit.define('check-frozen-array', (task, should) => {
context.audioWorklet.addModule(filePath).then(() => {
const workletNode =
new AudioWorkletNode(context, 'array-frozen-processor');
workletNode.port.onmessage = (message) => {
const actual = message.data;
should(actual.isInputFrozen, '|inputs| is frozen').beTrue();
should(actual.isOutputFrozen, '|outputs| is frozen').beTrue();
task.done();
};
});
});

// The incoming arrays should not be transferred, but the associated
// ArrayBuffers can be transferred. See the `array-transfer-processor`
// definition for the details.
audit.define('transfer-frozen-array', (task, should) => {
const sourceNode = new ConstantSourceNode(context);
const workletNode =
new AudioWorkletNode(context, 'array-transfer-processor');
workletNode.port.onmessage = (message) => {
const actual = message.data;
if (actual.type === 'assertion')
should(actual.success, actual.message).beTrue();
if (actual.done)
task.done();
};
sourceNode.connect(workletNode);
sourceNode.start();
});

audit.run();
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* @class ArrayFrozenProcessor
* @extends AudioWorkletProcessor
*/
class ArrayFrozenProcessor extends AudioWorkletProcessor {
constructor() {
super();
this._messageSent = false;
}

process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];

if (!this._messageSent) {
this.port.postMessage({
inputLength: input.length,
isInputFrozen: Object.isFrozen(inputs) && Object.isFrozen(input),
outputLength: output.length,
isOutputFrozen: Object.isFrozen(outputs) && Object.isFrozen(output)
});
this._messageSent = true;
}

return false;
}
}

/**
* @class ArrayTransferProcessor
* @extends AudioWorkletProcessor
*/
class ArrayTransferProcessor extends AudioWorkletProcessor {
constructor() {
super();
this._messageSent = false;
}

process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];

if (!this._messageSent) {
try {
// Transferring Array objects should NOT work.
this.port.postMessage({
inputs, input, inputChannel: input[0],
outputs, output, outputChannel: output[0]
}, [inputs, input, inputs[0], outputs, output, output[0]]);
// Hence, the following must NOT be reached.
this.port.postMessage({
type: 'assertion',
success: false,
message: 'Transferring inputs/outputs, an individual input/output ' +
'array, or a channel Float32Array MUST fail, but succeeded.'
});
} catch (error) {
this.port.postMessage({
type: 'assertion',
success: true,
message: 'Transferring inputs/outputs, an individual input/output ' +
'array, or a channel Float32Array is not allowed as expected.'
});
}

try {
// Transferring ArrayBuffers should work.
this.port.postMessage(
{inputChannel: input[0], outputChannel: output[0]},
[input[0].buffer, output[0].buffer]);
this.port.postMessage({
type: 'assertion',
success: true,
message: 'Transferring ArrayBuffers was successful as expected.'
});
} catch (error) {
// This must NOT be reached.
this.port.postMessage({
type: 'assertion',
success: false,
message: 'Transferring ArrayBuffers unexpectedly failed.'
});
}

this.port.postMessage({done: true});
this._messageSent = true;
}

return false;
}
}

registerProcessor('array-frozen-processor', ArrayFrozenProcessor);
registerProcessor('array-transfer-processor', ArrayTransferProcessor);

0 comments on commit 1330974

Please sign in to comment.