fix: expose physical aliasing and delayed arrivals
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import { maximumAcousticDelaySeconds } from "./environment.js";
|
||||
import { ceilSampleIndex } from "./math.js";
|
||||
|
||||
function copyPosition(position) {
|
||||
if (!Array.isArray(position) || position.length !== 3 || !position.every(Number.isFinite)) {
|
||||
throw new TypeError("source position must be a finite [x, y, z] array.");
|
||||
@@ -65,7 +68,7 @@ export function sceneAtTime(scene, time) {
|
||||
* Approximate a moving scene as stationary during [startTime, endTime] by
|
||||
* sampling every trajectory at the window midpoint.
|
||||
*/
|
||||
export function sampleQuasiStaticWindow(scene, startTime, endTime) {
|
||||
export function sampleQuasiStaticWindow(scene, startTime, endTime, options = {}) {
|
||||
requireFiniteTime(startTime, "startTime");
|
||||
requireFiniteTime(endTime, "endTime");
|
||||
if (startTime < 0 || endTime <= startTime) {
|
||||
@@ -90,42 +93,77 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime) {
|
||||
// boundary. Adjacent windows therefore share an exact boundary without a
|
||||
// duplicated or omitted sample, even when their requested times are not
|
||||
// integer multiples of the sample period.
|
||||
const startSample = Math.ceil(startTime * sampleRate);
|
||||
const endSample = Math.ceil(endTime * sampleRate);
|
||||
const startSample = ceilSampleIndex(startTime, sampleRate);
|
||||
const endSample = ceilSampleIndex(endTime, sampleRate);
|
||||
if (endSample <= startSample) {
|
||||
throw new RangeError("window must contain at least one sample.");
|
||||
}
|
||||
const alignedStartTime = startSample / sampleRate;
|
||||
const alignedEndTime = endSample / sampleRate;
|
||||
const sampleTime = (startSample + endSample) / (2 * sampleRate);
|
||||
const sampledScene = sceneAtTime(scene, sampleTime);
|
||||
let maximumPropagationDelaySeconds = options.maximumPropagationDelaySeconds;
|
||||
if (maximumPropagationDelaySeconds === undefined) {
|
||||
maximumPropagationDelaySeconds = options.arrayDefinition
|
||||
? maximumAcousticDelaySeconds({
|
||||
scene: sampledScene,
|
||||
arrayDefinition: options.arrayDefinition,
|
||||
referenceDistance: options.referenceDistance ?? 1,
|
||||
minimumDistance: options.minimumDistance ?? 0.01,
|
||||
})
|
||||
: 0;
|
||||
}
|
||||
if (
|
||||
!Number.isFinite(maximumPropagationDelaySeconds) ||
|
||||
maximumPropagationDelaySeconds < 0
|
||||
) {
|
||||
throw new RangeError("maximumPropagationDelaySeconds must be finite and non-negative.");
|
||||
}
|
||||
const preRollSamples = ceilSampleIndex(maximumPropagationDelaySeconds, sampleRate);
|
||||
const renderStartSample = Math.max(0, startSample - preRollSamples);
|
||||
const sources = [];
|
||||
const sourceOffsets = [];
|
||||
|
||||
for (const source of scene.sources) {
|
||||
for (let sourceIndex = 0; sourceIndex < scene.sources.length; sourceIndex += 1) {
|
||||
const source = scene.sources[sourceIndex];
|
||||
const signalStartTime = source.startTime ?? 0;
|
||||
const signalDuration = source.duration ?? scene.duration;
|
||||
const signalStartSample = Math.ceil(signalStartTime * sampleRate);
|
||||
const signalEndSample = Math.ceil((signalStartTime + signalDuration) * sampleRate);
|
||||
const activeStartSample = Math.max(startSample, signalStartSample);
|
||||
const signalStartSample = ceilSampleIndex(signalStartTime, sampleRate);
|
||||
const signalEndSample = ceilSampleIndex(signalStartTime + signalDuration, sampleRate);
|
||||
const activeStartSample = Math.max(renderStartSample, signalStartSample);
|
||||
const activeEndSample = Math.min(endSample, signalEndSample);
|
||||
if (activeEndSample <= activeStartSample) continue;
|
||||
|
||||
sources.push({
|
||||
...source,
|
||||
position: sourcePositionAtTime(source, sampleTime),
|
||||
startTime: (activeStartSample - startSample) / sampleRate,
|
||||
position: sampledScene.sources[sourceIndex].position,
|
||||
startTime: (activeStartSample - renderStartSample) / sampleRate,
|
||||
duration: (activeEndSample - activeStartSample) / sampleRate,
|
||||
});
|
||||
sourceOffsets.push({ signalStartTime, signalStartSample });
|
||||
sourceOffsets.push({
|
||||
signalStartTime,
|
||||
signalStartSample,
|
||||
signalDuration,
|
||||
});
|
||||
}
|
||||
|
||||
const ambient = Array.isArray(scene.ambient) ? scene.ambient.slice() : scene.ambient;
|
||||
const signalOffsets = {
|
||||
sceneSampleOffset: startSample,
|
||||
sceneSampleOffset: renderStartSample,
|
||||
cropStartSample: startSample - renderStartSample,
|
||||
cropSampleCount: endSample - startSample,
|
||||
maximumPropagationDelaySeconds,
|
||||
sourcePositionSampleTime: sampleTime,
|
||||
windowingModel: "quasi-static-midpoint-propagation-preroll",
|
||||
sources: sourceOffsets,
|
||||
ambient: (scene.ambient ?? []).map(() => ({ signalStartTime: 0, signalStartSample: 0 })),
|
||||
ambient: (scene.ambient ?? []).map(() => ({
|
||||
signalStartTime: 0,
|
||||
signalStartSample: 0,
|
||||
signalDuration: scene.duration,
|
||||
})),
|
||||
};
|
||||
const duration = (endSample - startSample) / sampleRate;
|
||||
const renderDuration = (endSample - renderStartSample) / sampleRate;
|
||||
return {
|
||||
startTime: alignedStartTime,
|
||||
endTime: alignedEndTime,
|
||||
@@ -134,10 +172,14 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime) {
|
||||
endSample,
|
||||
sampleCount: endSample - startSample,
|
||||
duration,
|
||||
renderStartSample,
|
||||
renderDuration,
|
||||
preRollSamples: startSample - renderStartSample,
|
||||
maximumPropagationDelaySeconds,
|
||||
signalOffsets,
|
||||
scene: {
|
||||
...scene,
|
||||
duration,
|
||||
duration: renderDuration,
|
||||
sources,
|
||||
...(Array.isArray(scene.ambient) ? { ambient } : {}),
|
||||
},
|
||||
@@ -170,7 +212,7 @@ export function createQuasiStaticWindows(scene, options = {}) {
|
||||
const windows = [];
|
||||
for (let windowStart = startTime; windowStart < endTime; windowStart += hopDuration) {
|
||||
const windowEnd = Math.min(windowStart + windowDuration, endTime);
|
||||
windows.push(sampleQuasiStaticWindow(scene, windowStart, windowEnd));
|
||||
windows.push(sampleQuasiStaticWindow(scene, windowStart, windowEnd, options));
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user