// Create an Int16 array of the right length.
let small_data = new Int16Array(defaultInit.channels * defaultInit.frames);
let wrong_format_init = {...audio_data_init};
wrong_format_init.data = small_data;
// Creating `f32-planar` AudioData from Int16 from should throw.
assert_throws_js(TypeError, () => {
let data = new AudioData(wrong_format_init);
}, `AudioDataInit.data needs to be big enough`);
var members = [ 'timestamp', 'data', 'numberOfFrames', 'numberOfChannels', 'sampleRate', 'format',
];
for (const member of members) {
let incomplete_init = {...audio_data_init}; delete incomplete_init[member];
assert_throws_js(
TypeError, () => {let data = new AudioData(incomplete_init)}, 'AudioData requires \'' + member + '\'');
}
let invalid_init = {...audio_data_init};
invalid_init.numberOfFrames = 0
assert_throws_js(
TypeError, () => {let data = new AudioData(invalid_init)}, 'AudioData requires numberOfFrames > 0');
const data_copyDest = new Float32Array(defaultInit.frames); const clone_copyDest = new Float32Array(defaultInit.frames);
// Verify the data matches. for (var channel = 0; channel < defaultInit.channels; channel++) {
data.copyTo(data_copyDest, {planeIndex: channel});
clone.copyTo(clone_copyDest, {planeIndex: 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.numberOfFrames, 0, 'data.buffer (closed)');
assert_not_equals(clone.numberOfFrames, 0, 'clone.buffer (not closed)');
// Verify closing a closed AudioData does not throw.
data.close();
}, 'Verify closing and cloning AudioData');
test(t => {
let data = make_audio_data(
-10, defaultInit.channels, defaultInit.sampleRate, defaultInit.frames);
assert_equals(data.timestamp, -10, 'timestamp');
data.close();
}, 'Test we can construct AudioData with a negative timestamp.');
test(t => { var data = new Float32Array([0]);
let audio_data_init = {
timestamp: 0,
data: data,
numberOfFrames: 1,
numberOfChannels: 1,
sampleRate: 44100,
format: 'f32',
};
let audioData = new AudioData(audio_data_init);
assert_not_equals(data.length, 0, "Input data is copied when constructing an AudioData");
}, 'Test input array is copied on construction');
test(t => {
let audio_data_init = {
timestamp: 0,
data: new Float32Array([1,2,3,4,5,6,7,8]),
numberOfFrames: 4,
numberOfChannels: 2,
sampleRate: 44100,
format: 'f32',
};
let audioData = new AudioData(audio_data_init);
let dest = new Float32Array(8);
assert_throws_js(
RangeError, () => audioData.copyTo(dest, {planeIndex: 1}), 'copyTo from interleaved data with non-zero planeIndex throws');
audioData.close();
}, 'Test that copyTo throws if copying from interleaved with a non-zero planeIndex');
// Indices to pick a particular specific value in a specific sample-format const MIN = 0; // Minimum sample value, max amplitude const MAX = 1; // Maximum sample value, max amplitude const HALF = 2; // Half the maximum sample value, positive const NEGATIVE_HALF = 3; // Half the maximum sample value, negative const BIAS = 4; // Center of the range, silence const DISCRETE_STEPS = 5; // Number of different value for a type.
function pow2(p) { return 2 ** p;
} // Rounding operations for conversion, currently always floor (round towards // zero).
let r = Math.floor.bind(this);
const TEST_TEMPLATE = {
channels: 2,
frames: 5, // Each test is run with an element of the cartesian product of a pair of // elements of the set of type in [u8, s16, s32, f32] // For each test, this template is copied and the values replaced with the // appropriate values for this particular type. // For each test, copy this template and replace the number by the appropriate // number for this type
testInput: [MIN, BIAS, MAX, MIN, HALF, NEGATIVE_HALF, BIAS, MAX, BIAS, BIAS],
testInterleavedResult: [MIN, NEGATIVE_HALF, BIAS, BIAS, MAX, MAX, MIN, BIAS, HALF, BIAS],
testVectorInterleavedResult: [
[MIN, MAX, HALF, BIAS, BIAS],
[BIAS, MIN, NEGATIVE_HALF, MAX, BIAS],
],
testVectorPlanarResult: [
[MIN, BIAS, MAX, MIN, HALF],
[NEGATIVE_HALF, BIAS, MAX, BIAS, BIAS],
],
};
// This is the complex part: carefully select an acceptable error value // depending on various factors: expected destination value, source type, // destination type. This is designed to be strict but reachable with simple // sample format transformation (no dithering or complex transformation). function epsilon(expectedDestValue, sourceType, destType) { // Strict comparison if not converting if (sourceType == destType) { return 0.0;
} // There are three cases in which the maximum value cannot be reached, when // converting from a smaller integer sample type to a wider integer sample // type: // - u8 to s16 // - u8 to s32 // - s16 to u32 if (expectedDestValue == TEST_VALUES[destType][MAX]) { if (sourceType == "u8" && destType == "s16") { return expectedDestValue - 32511; // INT16_MAX - 2 << 7 + 1
} elseif (sourceType == "u8" && destType == "s32") { return expectedDestValue - 2130706432; // INT32_MAX - (2 << 23) + 1
} elseif (sourceType == "s16" && destType == "s32") { return expectedDestValue - 2147418112; // INT32_MAX - UINT16_MAX
}
} // Min and bias value are correctly mapped for all integer sample-types if (isInteger(sourceType) && isInteger(destType)) { if (expectedDestValue == TEST_VALUES[destType][MIN] ||
expectedDestValue == TEST_VALUES[destType][BIAS]) { return 0.0;
}
} // If converting from float32 to u8 or s16, allow choosing the rounding // direction. s32 has higher resolution than f32 in [-1.0,1.0] (24 bits of // mantissa) if (!isInteger(sourceType) && isInteger(destType) && destType != "s32") { return 1.0;
} // In all other cases, expect an accuracy that depends on the source type and // the destination type. // The resolution of the source type. var sourceResolution = TEST_VALUES[sourceType][DISCRETE_STEPS]; // The resolution of the destination type. var destResolution = TEST_VALUES[destType][DISCRETE_STEPS]; // Computations should be exact if going from high resolution to low resolution. if (sourceResolution > destResolution) { return 0.0;
} else { // Something that approaches the precision imbalance return destResolution / sourceResolution;
}
}
// Fill the template above with the values for a particular type function get_type_values(type) {
let cloned = structuredClone(TEST_TEMPLATE);
cloned.testInput = Array.from(
cloned.testInput,
idx => TEST_VALUES[type][idx]
);
cloned.testInterleavedResult = Array.from(
cloned.testInterleavedResult,
idx => TEST_VALUES[type][idx]
);
cloned.testVectorInterleavedResult = Array.from(
cloned.testVectorInterleavedResult,
c => { return Array.from(c, idx => { return TEST_VALUES[type][idx];
});
}
);
cloned.testVectorPlanarResult = Array.from(
cloned.testVectorPlanarResult,
c => { return Array.from(c, idx => { return TEST_VALUES[type][idx];
});
}
); return cloned;
}
function arrayTypeToType(array) { switch (array.constructor) { case Uint8Array: return"u8"; case Int16Array: return"s16"; case Int32Array: return"s32"; case Float32Array: return"f32"; default: throw"Unexpected";
}
}
function check_array_equality(values, expected, sourceType, message, assert_func) { if (values.length != expected.length) { throw"Array not of the same length";
} for (var i = 0; i < values.length; i++) { var eps = epsilon(expected[i], sourceType, arrayTypeToType(values));
assert_func(
Math.abs(expected[i] - values[i]) <= eps,
`Got ${values[i]} but expected result ${
expected[i]
} at index ${i} when converting from ${sourceType} to ${arrayTypeToType(
values
)}, epsilon ${eps}`
);
}
assert_func( true,
`${values} is equal to ${expected} when converting from ${sourceType} to ${arrayTypeToType(
values
)}`
);
}
function conversionTest(sourceType, destinationType) {
test(function (t) { var test = get_type_values(sourceType); var result = get_type_values(destinationType);
var sourceArrayCtor = typeToArrayType(sourceType); var destArrayCtor = typeToArrayType(destinationType);
let data = new AudioData({
timestamp: defaultInit.timestamp,
data: new sourceArrayCtor(test.testInput),
numberOfFrames: test.frames,
numberOfChannels: test.channels,
sampleRate: defaultInit.sampleRate,
format: sourceType,
});
// All conversions can be supported, but conversion of any type to f32-planar // MUST be supported. var assert_func = destinationType == "f32" ? assert_true : assert_implements_optional;
let dest = new destArrayCtor(data.numberOfFrames);
data.copyTo(dest, { planeIndex: 0, format: destinationType + "-planar" });
check_array_equality(
dest,
result.testVectorInterleavedResult[0],
sourceType, "interleaved channel 0",
assert_func
);
data.copyTo(dest, { planeIndex: 1, format: destinationType + "-planar" });
check_array_equality(
dest,
result.testVectorInterleavedResult[1],
sourceType, "interleaved channel 0",
assert_func
);
let destInterleaved = new destArrayCtor(data.numberOfFrames * data.numberOfChannels);
data.copyTo(destInterleaved, { planeIndex: 0, format: destinationType });
check_array_equality(
destInterleaved,
result.testInput,
sourceType, "copyTo from interleaved to interleaved (conversion only)",
assert_implements_optional
);
data = new AudioData({
timestamp: defaultInit.timestamp,
data: new sourceArrayCtor(test.testInput),
numberOfFrames: test.frames,
numberOfChannels: test.channels,
sampleRate: defaultInit.sampleRate,
format: sourceType + "-planar",
});
data.copyTo(dest, { planeIndex: 0, format: destinationType + "-planar" });
check_array_equality(
dest,
result.testVectorPlanarResult[0],
sourceType, "planar channel 0",
assert_func,
);
data.copyTo(dest, { planeIndex: 1, format: destinationType + "-planar" });
check_array_equality(
dest,
result.testVectorPlanarResult[1],
sourceType, "planar channel 1",
assert_func
); // Copy to interleaved from planar: all channels are copied
data.copyTo(destInterleaved, {planeIndex: 0, format: destinationType});
check_array_equality(
destInterleaved,
result.testInterleavedResult,
sourceType, "planar to interleaved",
assert_func
);
}, `Test conversion of ${sourceType} to ${destinationType}`);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.