// META: global=window,dedicatedworker test(t => { // 2 channels, 10 frames, interleaved f32. // Layout: [L0, R0, L1, R1, L2, R2, L3, R3, L4, R4, // L5, R5, L6, R6, L7, R7, L8, R8, L9, R9] const channels = 2; const frames = 10; const data = new Float32Array(channels * frames); for (let i = 0; i < frames; i++) { data[i * channels] = i * 0.1; // left data[i * channels + 1] = -(i * 0.1); // right } const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 'f32', }); t.add_cleanup(() => audioData.close()); const frameOffset = 3; const frameCount = 5; const dest = new Float32Array(channels * frameCount); audioData.copyTo(dest, { planeIndex: 0, format: 'f32', frameOffset: frameOffset, frameCount: frameCount, }); // Expected: frames 3 through 7, interleaved. for (let i = 0; i < frameCount; i++) { const srcFrame = frameOffset + i; const expectedL = srcFrame * 0.1; const expectedR = -(srcFrame * 0.1); assert_approx_equals(dest[i * channels], expectedL, 1e-6, `frame ${srcFrame} left channel`); assert_approx_equals(dest[i * channels + 1], expectedR, 1e-6, `frame ${srcFrame} right channel`); } }, 'copyTo interleaved-to-interleaved with non-zero frameOffset'); test(t => { // Same test with s16 to ensure it's not float-specific. const channels = 2; const frames = 10; const data = new Int16Array(channels * frames); for (let i = 0; i < frames; i++) { data[i * channels] = i * 1000; data[i * channels + 1] = -(i * 1000); } const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 's16', }); t.add_cleanup(() => audioData.close()); const frameOffset = 4; const frameCount = 3; const dest = new Int16Array(channels * frameCount); audioData.copyTo(dest, { planeIndex: 0, format: 's16', frameOffset: frameOffset, frameCount: frameCount, }); for (let i = 0; i < frameCount; i++) { const srcFrame = frameOffset + i; assert_equals(dest[i * channels], srcFrame * 1000, `frame ${srcFrame} left channel`); assert_equals(dest[i * channels + 1], -(srcFrame * 1000), `frame ${srcFrame} right channel`); } }, 'copyTo interleaved-to-interleaved s16 with non-zero frameOffset'); test(t => { // 3 channels, interleaved, to cover more than the stereo case. const channels = 3; const frames = 8; const data = new Float32Array(channels * frames); for (let i = 0; i < frames; i++) { for (let ch = 0; ch < channels; ch++) { data[i * channels + ch] = i + ch * 0.01; } } const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 'f32', }); t.add_cleanup(() => audioData.close()); const frameOffset = 2; const frameCount = 4; const dest = new Float32Array(channels * frameCount); audioData.copyTo(dest, { planeIndex: 0, format: 'f32', frameOffset: frameOffset, frameCount: frameCount, }); for (let i = 0; i < frameCount; i++) { const srcFrame = frameOffset + i; for (let ch = 0; ch < channels; ch++) { const expected = srcFrame + ch * 0.01; assert_approx_equals( dest[i * channels + ch], expected, 1e-6, `frame ${srcFrame} channel ${ch}`); } } }, 'copyTo interleaved-to-interleaved f32 with 3 channels and frameOffset'); test(t => { // 2 channels, 10 frames, planar f32. // Layout: [L0, L1, ..., L9, R0, R1, ..., R9] const channels = 2; const frames = 10; const data = new Float32Array(channels * frames); for (let ch = 0; ch < channels; ch++) { for (let i = 0; i < frames; i++) { data[ch * frames + i] = (ch + 1) * (i + 1) * 0.1; } } const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 'f32-planar', }); t.add_cleanup(() => audioData.close()); const frameOffset = 3; const frameCount = 5; const dest = new Float32Array(channels * frameCount); audioData.copyTo(dest, { planeIndex: 0, format: 'f32', frameOffset: frameOffset, frameCount: frameCount, }); // Expected: frames 3 through 7 from each channel, interleaved. for (let i = 0; i < frameCount; i++) { const srcFrame = frameOffset + i; for (let ch = 0; ch < channels; ch++) { const expected = (ch + 1) * (srcFrame + 1) * 0.1; assert_approx_equals( dest[i * channels + ch], expected, 1e-5, `frame ${srcFrame} channel ${ch}`); } } }, 'copyTo planar-to-interleaved with non-zero frameOffset'); test(t => { // 3 channels, planar s16. const channels = 3; const frames = 8; const data = new Int16Array(channels * frames); for (let ch = 0; ch < channels; ch++) { for (let i = 0; i < frames; i++) { data[ch * frames + i] = (ch + 1) * 1000 + i; } } const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 's16-planar', }); t.add_cleanup(() => audioData.close()); const frameOffset = 2; const frameCount = 4; const dest = new Int16Array(channels * frameCount); audioData.copyTo(dest, { planeIndex: 0, format: 's16', frameOffset: frameOffset, frameCount: frameCount, }); for (let i = 0; i < frameCount; i++) { const srcFrame = frameOffset + i; for (let ch = 0; ch < channels; ch++) { const expected = (ch + 1) * 1000 + srcFrame; assert_equals( dest[i * channels + ch], expected, `frame ${srcFrame} channel ${ch}`); } } }, 'copyTo planar-to-interleaved s16 with 3 channels and frameOffset'); test(t => { // Verify frameOffset=0 still works correctly for planar-to-interleaved // (baseline sanity check). const channels = 2; const frames = 4; const data = new Float32Array([ 1.0, 2.0, 3.0, 4.0, // channel 0 5.0, 6.0, 7.0, 8.0, // channel 1 ]); const audioData = new AudioData({ timestamp: 0, data: data, numberOfFrames: frames, numberOfChannels: channels, sampleRate: 44100, format: 'f32-planar', }); t.add_cleanup(() => audioData.close()); const dest = new Float32Array(channels * frames); audioData.copyTo(dest, {planeIndex: 0, format: 'f32'}); // Expected interleaved: [1, 5, 2, 6, 3, 7, 4, 8] const expected = new Float32Array([1, 5, 2, 6, 3, 7, 4, 8]); assert_array_equals(dest, expected, 'planar-to-interleaved with frameOffset=0'); }, 'copyTo planar-to-interleaved baseline with frameOffset=0');