Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<html>
<head>
<title>
Test Basic Functionality of AudioBuffer.copyFromChannel and
AudioBuffer.copyToChannel
</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">
// Define utility routines.
// Initialize the AudioBuffer |buffer| with a ramp signal on each channel.
// The ramp starts at channel number + 1.
function initializeAudioBufferRamp(buffer) {
for (let c = 0; c < buffer.numberOfChannels; ++c) {
let d = buffer.getChannelData(c);
for (let k = 0; k < d.length; ++k) {
d[k] = k + c + 1;
}
}
}
// Create a Float32Array of length |length| and initialize the array to
// -1.
function createInitializedF32Array(length) {
let x = new Float32Array(length);
for (let k = 0; k < length; ++k) {
x[k] = -1;
}
return x;
}
// Create a Float32Array of length |length| that is initialized to be a
// ramp starting at 1.
function createFloat32RampArray(length) {
let x = new Float32Array(length);
for (let k = 0; k < x.length; ++k) {
x[k] = k + 1;
}
return x;
}
// Test that the array |x| is a ramp starting at value |start| of length
// |length|, starting at |startIndex| in the array. |startIndex| is
// optional and defaults to 0. Any other values must be -1.
function shouldBeRamp(
should, testName, x, startValue, length, startIndex) {
let k;
let startingIndex = startIndex || 0;
let expected = Array(x.length);
// Fill the expected array with the correct results.
// The initial part (if any) must be -1.
for (k = 0; k < startingIndex; ++k) {
expected[k] = -1;
}
// The second part should be a ramp starting with |startValue|
for (; k < startingIndex + length; ++k) {
expected[k] = startValue + k - startingIndex;
}
// The last part (if any) should be -1.
for (; k < x.length; ++k) {
expected[k] = -1;
}
should(x, testName, {numberOfArrayLog: 32}).beEqualToArray(expected);
}
let audit = Audit.createTaskRunner();
let context = new AudioContext();
// Temp array for testing exceptions for copyToChannel/copyFromChannel.
// The length is arbitrary.
let x = new Float32Array(8);
// Number of frames in the AudioBuffer for testing. This is pretty
// arbitrary so choose a fairly small value.
let bufferLength = 16;
// Number of channels in the AudioBuffer. Also arbitrary, but it should
// be greater than 1 for test coverage.
let numberOfChannels = 3;
// AudioBuffer that will be used for testing copyFrom and copyTo.
let buffer = context.createBuffer(
numberOfChannels, bufferLength, context.sampleRate);
let initialValues = Array(numberOfChannels);
// Initialize things
audit.define('initialize', (task, should) => {
// Initialize to -1.
initialValues.fill(-1);
should(initialValues, 'Initialized values').beConstantValueOf(-1)
task.done();
});
// Test that expected exceptions are signaled for copyFrom.
audit.define('copyFrom-exceptions', (task, should) => {
should(
AudioBuffer.prototype.copyFromChannel,
'AudioBuffer.prototype.copyFromChannel')
.exist();
should(
() => {
buffer = context.createBuffer(
numberOfChannels, bufferLength, context.sampleRate);
},
'0: buffer = context.createBuffer(' + numberOfChannels + ', ' +
bufferLength + ', context.sampleRate)')
.notThrow();
should(() => {
buffer.copyFromChannel(null, 0);
}, '1: buffer.copyFromChannel(null, 0)').throw(TypeError);
should(() => {
buffer.copyFromChannel(context, 0);
}, '2: buffer.copyFromChannel(context, 0)').throw(TypeError);
should(() => {
buffer.copyFromChannel(x, -1);
}, '3: buffer.copyFromChannel(x, -1)').throw(DOMException, 'IndexSizeError');
should(
() => {
buffer.copyFromChannel(x, numberOfChannels);
},
'4: buffer.copyFromChannel(x, ' + numberOfChannels + ')')
.throw(DOMException, 'IndexSizeError');
;
should(() => {
buffer.copyFromChannel(x, 0, -1);
}, '5: buffer.copyFromChannel(x, 0, -1)').notThrow();
should(
() => {
buffer.copyFromChannel(x, 0, bufferLength);
},
'6: buffer.copyFromChannel(x, 0, ' + bufferLength + ')')
.notThrow();
should(() => {
buffer.copyFromChannel(x, 3);
}, '7: buffer.copyFromChannel(x, 3)').throw(DOMException, 'IndexSizeError');
// WebAssembly.Memory's size is in multiples of 64 KiB
const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer);
should(
() => {
buffer.copyFromChannel(shared_buffer, 0);
},
'8: buffer.copyFromChannel(SharedArrayBuffer view, 0)')
.throw(TypeError);
should(
() => {
buffer.copyFromChannel(shared_buffer, 0, 0);
},
'9: buffer.copyFromChannel(SharedArrayBuffer view, 0, 0)')
.throw(TypeError);
task.done();
});
// Test that expected exceptions are signaled for copyTo.
audit.define('copyTo-exceptions', (task, should) => {
should(
AudioBuffer.prototype.copyToChannel,
'AudioBuffer.prototype.copyToChannel')
.exist();
should(() => {
buffer.copyToChannel(null, 0);
}, '0: buffer.copyToChannel(null, 0)').throw(TypeError);
should(() => {
buffer.copyToChannel(context, 0);
}, '1: buffer.copyToChannel(context, 0)').throw(TypeError);
should(() => {
buffer.copyToChannel(x, -1);
}, '2: buffer.copyToChannel(x, -1)').throw(DOMException, 'IndexSizeError');
should(
() => {
buffer.copyToChannel(x, numberOfChannels);
},
'3: buffer.copyToChannel(x, ' + numberOfChannels + ')')
.throw(DOMException, 'IndexSizeError');
should(() => {
buffer.copyToChannel(x, 0, -1);
}, '4: buffer.copyToChannel(x, 0, -1)').notThrow();
should(
() => {
buffer.copyToChannel(x, 0, bufferLength);
},
'5: buffer.copyToChannel(x, 0, ' + bufferLength + ')')
.notThrow();
should(() => {
buffer.copyToChannel(x, 3);
}, '6: buffer.copyToChannel(x, 3)').throw(DOMException, 'IndexSizeError');
// WebAssembly.Memory's size is in multiples of 64 KiB
const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer);
should(
() => {
buffer.copyToChannel(shared_buffer, 0);
},
'7: buffer.copyToChannel(SharedArrayBuffer view, 0)')
.throw(TypeError);
should(
() => {
buffer.copyToChannel(shared_buffer, 0, 0);
},
'8: buffer.copyToChannel(SharedArrayBuffer view, 0, 0)')
.throw(TypeError);
task.done();
});
// Test copyFromChannel
audit.define('copyFrom-validate', (task, should) => {
// Initialize the AudioBuffer to a ramp for testing copyFrom.
initializeAudioBufferRamp(buffer);
// Test copyFrom operation with a short destination array, filling the
// destination completely.
for (let c = 0; c < numberOfChannels; ++c) {
let dst8 = createInitializedF32Array(8);
buffer.copyFromChannel(dst8, c);
shouldBeRamp(
should, 'buffer.copyFromChannel(dst8, ' + c + ')', dst8, c + 1, 8)
}
// Test copyFrom operation with a short destination array using a
// non-zero start index that still fills the destination completely.
for (let c = 0; c < numberOfChannels; ++c) {
let dst8 = createInitializedF32Array(8);
buffer.copyFromChannel(dst8, c, 1);
shouldBeRamp(
should, 'buffer.copyFromChannel(dst8, ' + c + ', 1)', dst8, c + 2,
8)
}
// Test copyFrom operation with a short destination array using a
// non-zero start index that does not fill the destinatiom completely.
// The extra elements should be unchanged.
for (let c = 0; c < numberOfChannels; ++c) {
let dst8 = createInitializedF32Array(8);
let startInChannel = bufferLength - 5;
buffer.copyFromChannel(dst8, c, startInChannel);
shouldBeRamp(
should,
'buffer.copyFromChannel(dst8, ' + c + ', ' + startInChannel + ')',
dst8, c + 1 + startInChannel, bufferLength - startInChannel);
}
// Copy operation with the destination longer than the buffer, leaving
// the trailing elements of the destination untouched.
for (let c = 0; c < numberOfChannels; ++c) {
let dst26 = createInitializedF32Array(bufferLength + 10);
buffer.copyFromChannel(dst26, c);
shouldBeRamp(
should, 'buffer.copyFromChannel(dst26, ' + c + ')', dst26, c + 1,
bufferLength);
}
task.done();
});
// Test copyTo
audit.define('copyTo-validate', (task, should) => {
// Create a source consisting of a ramp starting at 1, longer than the
// AudioBuffer
let src = createFloat32RampArray(bufferLength + 10);
// Test copyTo with AudioBuffer shorter than Float32Array. The
// AudioBuffer should be completely filled with the Float32Array.
should(
() => {
buffer =
createConstantBuffer(context, bufferLength, initialValues);
},
'buffer = createConstantBuffer(context, ' + bufferLength + ', [' +
initialValues + '])')
.notThrow();
for (let c = 0; c < numberOfChannels; ++c) {
buffer.copyToChannel(src, c);
shouldBeRamp(
should, 'buffer.copyToChannel(src, ' + c + ')',
buffer.getChannelData(c), 1, bufferLength);
}
// Test copyTo with AudioBuffer longer than the Float32Array. The tail
// of the AudioBuffer should be unchanged.
buffer = createConstantBuffer(context, bufferLength, initialValues);
let src10 = createFloat32RampArray(10);
for (let c = 0; c < numberOfChannels; ++c) {
buffer.copyToChannel(src10, c);
shouldBeRamp(
should, 'buffer.copyToChannel(src10, ' + c + ')',
buffer.getChannelData(c), 1, 10);
}
// Test copyTo with non-default startInChannel. Part of the AudioBuffer
// should filled with the beginning and end sections untouched.
buffer = createConstantBuffer(context, bufferLength, initialValues);
for (let c = 0; c < numberOfChannels; ++c) {
let startInChannel = 5;
buffer.copyToChannel(src10, c, startInChannel);
shouldBeRamp(
should,
'buffer.copyToChannel(src10, ' + c + ', ' + startInChannel + ')',
buffer.getChannelData(c), 1, src10.length, startInChannel);
}
task.done();
});
audit.run();
</script>
</body>
</html>