From 5f38c4012c17d7e6c41bcec5c9dedf6daa140b21 Mon Sep 17 00:00:00 2001 From: Thomas Guilbert Date: Sat, 17 Jul 2021 09:49:24 +0000 Subject: [PATCH] Bug 1719923 [wpt PR 29629] - Add AudioData::copyTo(), a=testonly Automatic update from web-platform-tests Add AudioData::copyTo() This CL updates AudioData according to the latest WebCodecs spec. It adds an allocationSize() and a copyTo() method to AudioData. WPTs will be included in future CLs, when AudioDataInit is updated to use a BufferSource instead of an AudioBuffer. Bug: 1205281 Change-Id: I97a2ed6ac094e373671262d3ba4089d150d94446 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3011779 Commit-Queue: Thomas Guilbert Reviewed-by: Dan Sanders Reviewed-by: Jeremy Roman Cr-Commit-Position: refs/heads/master@{#900655} -- wpt-commits: 3ecb64d20dd5ddfaa067183d58dafc75a7fc6614 wpt-pr: 29629 --- .../webcodecs/audio-data-serialization.any.js | 44 +++--- .../tests/webcodecs/audio-data.any.js | 62 +++----- .../tests/webcodecs/audio-encoder.any.js | 134 ++++++++++++------ 3 files changed, 137 insertions(+), 103 deletions(-) diff --git a/testing/web-platform/tests/webcodecs/audio-data-serialization.any.js b/testing/web-platform/tests/webcodecs/audio-data-serialization.any.js index 3d42aec8edf1..0af59e9e4014 100644 --- a/testing/web-platform/tests/webcodecs/audio-data-serialization.any.js +++ b/testing/web-platform/tests/webcodecs/audio-data-serialization.any.js @@ -17,55 +17,59 @@ function createDefaultAudioData() { } async_test(t => { - let localData = createDefaultAudioData(); + let originalData = createDefaultAudioData(); let channel = new MessageChannel(); let localPort = channel.port1; let externalPort = channel.port2; externalPort.onmessage = t.step_func((e) => { - let externalData = e.data; - let buffer = externalData.buffer; - // We should have a valid deserialized buffer. - assert_true(buffer != undefined || buffer != null); - assert_equals(buffer.numberOfChannels, - localData.buffer.numberOfChannels, "numberOfChannels"); + let newData = e.data; - for (var channel = 0; channel < buffer.numberOfChannels; channel++) { - // This gives us the actual array that contains the data - var dest_array = buffer.getChannelData(channel); - var source_array = localData.buffer.getChannelData(channel); - for (var i = 0; i < dest_array.length; i+=10) { - assert_equals(dest_array[i], source_array[i], + // We should have a valid deserialized buffer. + assert_equals(newData.numberOfFrames, defaultInit.frames, 'numberOfFrames'); + assert_equals( + newData.numberOfChannels, defaultInit.channels, 'numberOfChannels'); + assert_equals(newData.sampleRate, defaultInit.sampleRate, 'sampleRate'); + + const originalData_copyDest = new Float32Array(defaultInit.frames); + const newData_copyDest = new Float32Array(defaultInit.frames); + + for (var channel = 0; channel < defaultInit.channels; channel++) { + originalData.copyTo(originalData_copyDest, { planeIndex: channel}); + newData.copyTo(newData_copyDest, { planeIndex: channel}); + + for (var i = 0; i < newData_copyDest.length; i+=10) { + assert_equals(newData_copyDest[i], originalData_copyDest[i], "data (ch=" + channel + ", i=" + i + ")"); } } - externalData.close(); + newData.close(); externalPort.postMessage("Done"); }) localPort.onmessage = t.step_func_done((e) => { - assert_true(localData.buffer != null); - localData.close(); + assert_equals(originalData.numberOfFrames, defaultInit.frames); + originalData.close(); }) - localPort.postMessage(localData); + localPort.postMessage(originalData); }, 'Verify closing AudioData does not propagate accross contexts.'); async_test(t => { - let localData = createDefaultAudioData(); + let data = createDefaultAudioData(); let channel = new MessageChannel(); let localPort = channel.port1; localPort.onmessage = t.unreached_func(); - localData.close(); + data.close(); assert_throws_dom("DataCloneError", () => { - localPort.postMessage(localData); + localPort.postMessage(data); }); t.done(); diff --git a/testing/web-platform/tests/webcodecs/audio-data.any.js b/testing/web-platform/tests/webcodecs/audio-data.any.js index e196cb8d8e89..02d4f8fdfb0e 100644 --- a/testing/web-platform/tests/webcodecs/audio-data.any.js +++ b/testing/web-platform/tests/webcodecs/audio-data.any.js @@ -29,10 +29,10 @@ test(t => { let data = new AudioData(audioDataInit); assert_equals(data.timestamp, defaultInit.timestamp, 'timestamp'); - assert_equals(data.buffer.length, defaultInit.frames, 'frames'); - assert_equals( - data.buffer.numberOfChannels, defaultInit.channels, 'channels'); - assert_equals(data.buffer.sampleRate, defaultInit.sampleRate, 'sampleRate'); + assert_equals(data.numberOfFrames, defaultInit.frames, 'frames'); + assert_equals(data.numberOfChannels, defaultInit.channels, 'channels'); + assert_equals(data.sampleRate, defaultInit.sampleRate, 'sampleRate'); + assert_equals(data.format, "FLTP", 'format'); assert_throws_js( TypeError, () => {let data = new AudioData({buffer: localBuffer})}, @@ -51,60 +51,36 @@ test(t => { // Verify the parameters match. assert_equals(data.timestamp, clone.timestamp, 'timestamp'); - assert_equals(data.buffer.length, clone.buffer.length, 'frames'); + assert_equals(data.numberOfFrames, clone.numberOfFrames, 'frames'); assert_equals( - data.buffer.numberOfChannels, clone.buffer.numberOfChannels, 'channels'); - assert_equals(data.buffer.sampleRate, clone.buffer.sampleRate, 'sampleRate'); + data.numberOfChannels, clone.numberOfChannels, 'channels'); + assert_equals(data.sampleRate, clone.sampleRate, 'sampleRate'); + assert_equals(data.format, clone.format, 'format'); + + const data_copyDest = new Float32Array(defaultInit.frames); + const clone_copyDest = new Float32Array(defaultInit.frames); // Verify the data matches. - for (var channel = 0; channel < data.buffer.numberOfChannels; channel++) { - var orig_ch = data.buffer.getChannelData(channel); - var cloned_ch = clone.buffer.getChannelData(channel); + for (var channel = 0; channel < defaultInit.channels; channel++) { + data.copyTo(data_copyDest, {planeIndex: channel}); + clone.copyTo(clone_copyDest, {planeIndex: channel}); - assert_array_equals(orig_ch, cloned_ch, 'Cloned data ch=' + channel); + assert_array_equals( + data_copyDest, clone_copyDest, 'Cloned data ch=' + channel); } // Verify closing the original data doesn't close the clone. data.close(); - assert_equals(data.buffer, null, 'data.buffer (closed)'); - assert_not_equals(clone.buffer, null, 'clone.buffer (not closed)'); + assert_equals(data.numberOfFrames, 0, 'data.buffer (closed)'); + assert_not_equals(clone.numberOfFrames, 0, 'clone.buffer (not closed)'); clone.close(); - assert_equals(clone.buffer, null, 'clone.buffer (closed)'); + assert_equals(clone.numberOfFrames, 0, 'clone.buffer (closed)'); // Verify closing a closed AudioData does not throw. data.close(); }, 'Verify closing and cloning AudioData'); -test(t => { - let data = createDefaultAudioData(); - - // Get a copy of the original data. - let pre_modification_clone = data.clone(); - - for (var channel = 0; channel < data.buffer.numberOfChannels; channel++) { - var orig_ch = data.buffer.getChannelData(channel); - - // Flip the polarity of the original data's buffer. - for (let i = 0; i < orig_ch.length; ++i) { - orig_ch.buffer[i] = -orig_ch.buffer[i]; - } - } - - // The data in 'data' should have been snapshotted internally, and - // despite changes to data.buffer, post_modification_clone should not contain - // modified data. - let post_modification_clone = data.clone(); - - // Verify the data matches. - for (var channel = 0; channel < data.buffer.numberOfChannels; channel++) { - var pre_ch = pre_modification_clone.buffer.getChannelData(channel); - var post_ch = post_modification_clone.buffer.getChannelData(channel); - - assert_array_equals(pre_ch, post_ch, 'Cloned data ch=' + channel); - } -}, 'Verify AudioData is snapshotted and internally immutable'); - test(t => { let data = make_audio_data( -10, defaultInit.channels, defaultInit.sampleRate, defaultInit.frames); diff --git a/testing/web-platform/tests/webcodecs/audio-encoder.any.js b/testing/web-platform/tests/webcodecs/audio-encoder.any.js index da120a3ddea6..c8d924faf305 100644 --- a/testing/web-platform/tests/webcodecs/audio-encoder.any.js +++ b/testing/web-platform/tests/webcodecs/audio-encoder.any.js @@ -2,32 +2,69 @@ // META: script=/webcodecs/utils.js // Merge all audio buffers into a new big one with all the data. -function join_buffers(buffers) { - assert_greater_than_equal(buffers.length, 0); - let total_length = 0; - let base_buffer = buffers[0]; - for (const buffer of buffers) { - assert_not_equals(buffer, null); - assert_equals(buffer.sampleRate, base_buffer.sampleRate); - assert_equals(buffer.numberOfChannels, base_buffer.numberOfChannels); - total_length += buffer.length; +function join_audio_data(audio_data_array) { + assert_greater_than_equal(audio_data_array.length, 0); + let total_frames = 0; + let base_buffer = audio_data_array[0]; + for (const data of audio_data_array) { + assert_not_equals(data, null); + assert_equals(data.sampleRate, base_buffer.sampleRate); + assert_equals(data.numberOfChannels, base_buffer.numberOfChannels); + assert_equals(data.format, base_buffer.format); + total_frames += data.numberOfFrames; } - let result = new AudioBuffer({ - length: total_length, - numberOfChannels: base_buffer.numberOfChannels, - sampleRate: base_buffer.sampleRate - }); + assert_true(base_buffer.format == 'FLT' || base_buffer.format == 'FLTP'); - for (let i = 0; i < base_buffer.numberOfChannels; i++) { - let channel = result.getChannelData(i); - let position = 0; - for (const buffer of buffers) { - channel.set(buffer.getChannelData(i), position); - position += buffer.length; + if (base_buffer.format == 'FLT') + return join_interleaved_data(audio_data_array, total_frames); + + // The format is 'FLTP'. + return join_planar_data(audio_data_array, total_frames); +} + +function join_interleaved_data(audio_data_array, total_frames) { + let base_data = audio_data_array[0]; + let channels = base_data.numberOfChannels; + let total_samples = total_frames * channels; + + let result = new Float32Array(total_samples); + + let copy_dest = new Float32Array(base_data.numberOfFrames * channels); + + // Copy all the interleaved data. + let position = 0; + for (const data of audio_data_array) { + let samples = data.numberOfFrames * channels; + if (copy_dest.length < samples) + copy_dest = new Float32Array(samples); + + data.copyTo(copy_dest, {planeIndex: 0}); + result.set(copy_dest, position); + position += samples; + } + + assert_equals(position, total_samples); + + return result; +} + +function join_planar_data(audio_data_array, total_frames) { + let base_frames = audio_data_array[0].numberOfFrames; + let channels = audio_data_array[0].numberOfChannels; + let result = new Float32Array(total_frames*channels); + let copyDest = new Float32Array(base_frames); + + // Merge all samples and lay them out according to the FLTP memory layout. + let position = 0; + for (let ch = 0; ch < channels; ch++) { + for (const data of audio_data_array) { + data.copyTo(copyDest, { planeIndex: ch}); + result.set(copyDest, position); + position += data.numberOfFrames; } - assert_equals(position, total_length); } + assert_equals(position, total_frames * channels); return result; } @@ -167,9 +204,9 @@ function channelNumberVariationTests() { let length = sample_rate / 10; let data1 = make_audio_data(ts, channels, sample_rate, length); - ts += Math.floor(data1.buffer.duration / 1000000); + ts += Math.floor(data1.duration / 1000000); let data2 = make_audio_data(ts, channels, sample_rate, length); - ts += Math.floor(data2.buffer.duration / 1000000); + ts += Math.floor(data2.duration / 1000000); let bad_data = make_audio_data(ts, channels + 1, sample_rate, length); promise_test(async t => @@ -193,9 +230,9 @@ function sampleRateVariationTests() { let length = sample_rate / 10; let data1 = make_audio_data(ts, channels, sample_rate, length); - ts += Math.floor(data1.buffer.duration / 1000000); + ts += Math.floor(data1.duration / 1000000); let data2 = make_audio_data(ts, channels, sample_rate, length); - ts += Math.floor(data2.buffer.duration / 1000000); + ts += Math.floor(data2.duration / 1000000); let bad_data = make_audio_data(ts, channels, sample_rate + 333, length); promise_test(async t => @@ -255,28 +292,45 @@ promise_test(async t => { decoder.close(); - let total_input = join_buffers(input_data.map(f => f.buffer)); - let total_output = join_buffers(output_data.map(f => f.buffer)); - assert_equals(total_output.numberOfChannels, 2); - assert_equals(total_output.sampleRate, sample_rate); + let total_input = join_audio_data(input_data); + let frames_per_plane = total_input.length / config.numberOfChannels; + + let total_output = join_audio_data(output_data); + + let base_input = input_data[0]; + let base_output = output_data[0]; + + // TODO: Convert formats to simplify conversions, once + // https://github.com/w3c/webcodecs/issues/232 is resolved. + assert_equals(base_input.format, "FLTP"); + assert_equals(base_output.format, "FLT"); + + assert_equals(base_output.numberOfChannels, config.numberOfChannels); + assert_equals(base_output.sampleRate, sample_rate); // Output can be slightly longer that the input due to padding assert_greater_than_equal(total_output.length, total_input.length); - assert_greater_than_equal(total_output.duration, total_duration_s); - assert_approx_equals(total_output.duration, total_duration_s, 0.1); // Compare waveform before and after encoding - for (let channel = 0; channel < total_input.numberOfChannels; channel++) { - let input_data = total_input.getChannelData(channel); - let output_data = total_output.getChannelData(channel); - for (let i = 0; i < total_input.length; i += 10) { + for (let channel = 0; channel < base_input.numberOfChannels; channel++) { + + let plane_start = channel * frames_per_plane; + let input_plane = total_input.slice( + plane_start, plane_start + frames_per_plane); + + for (let i = 0; i < base_input.numberOfFrames; i += 10) { + // Instead of de-interleaving the data, directly look into |total_output| + // for the sample we are interested in. + let ouput_index = i * base_input.numberOfChannels + channel; + // Checking only every 10th sample to save test time in slow // configurations like MSAN etc. - assert_approx_equals(input_data[i], output_data[i], 0.5, - "Difference between input and output is too large." - + " index: " + i - + " input: " + input_data[i] - + " output: " + output_data[i]); + assert_approx_equals( + input_plane[i], total_output[ouput_index], 0.5, + 'Difference between input and output is too large.' + + ' index: ' + i + ' channel: ' + channel + + ' input: ' + input_plane[i] + + ' output: ' + total_output[ouput_index]); } }