perf: stream propagation working sets
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import { maximumAcousticDelaySeconds } from "./environment.js";
|
||||
import {
|
||||
DEFAULT_MAXIMUM_ACOUSTIC_PATH_EVALUATIONS,
|
||||
selectAcousticPathSummary,
|
||||
summarizeAcousticPaths,
|
||||
} from "./environment.js";
|
||||
import { ceilSampleIndex } from "./math.js";
|
||||
import { effectiveSignalDurationSeconds } from "./signal.js";
|
||||
|
||||
function copyPosition(position) {
|
||||
if (!Array.isArray(position) || position.length !== 3 || !position.every(Number.isFinite)) {
|
||||
@@ -102,16 +107,20 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime, options = {})
|
||||
const alignedEndTime = endSample / sampleRate;
|
||||
const sampleTime = (startSample + endSample) / (2 * sampleRate);
|
||||
const sampledScene = sceneAtTime(scene, sampleTime);
|
||||
const pathSummary = options.arrayDefinition
|
||||
? summarizeAcousticPaths({
|
||||
scene: sampledScene,
|
||||
arrayDefinition: options.arrayDefinition,
|
||||
referenceDistance: options.referenceDistance ?? 1,
|
||||
minimumDistance: options.minimumDistance ?? 0.01,
|
||||
maximumPathEvaluations:
|
||||
options.maximumPathEvaluations ??
|
||||
DEFAULT_MAXIMUM_ACOUSTIC_PATH_EVALUATIONS,
|
||||
})
|
||||
: undefined;
|
||||
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;
|
||||
maximumPropagationDelaySeconds = pathSummary?.maximumDelaySeconds ?? 0;
|
||||
}
|
||||
if (
|
||||
!Number.isFinite(maximumPropagationDelaySeconds) ||
|
||||
@@ -119,42 +128,110 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime, options = {})
|
||||
) {
|
||||
throw new RangeError("maximumPropagationDelaySeconds must be finite and non-negative.");
|
||||
}
|
||||
const preRollSamples = ceilSampleIndex(maximumPropagationDelaySeconds, sampleRate);
|
||||
const requestedPreRollSamples = ceilSampleIndex(
|
||||
maximumPropagationDelaySeconds,
|
||||
sampleRate,
|
||||
);
|
||||
const requiredPreRollSamples = pathSummary
|
||||
? ceilSampleIndex(pathSummary.maximumDelaySeconds, sampleRate)
|
||||
: 0;
|
||||
if (pathSummary && requestedPreRollSamples < requiredPreRollSamples) {
|
||||
throw new RangeError(
|
||||
"maximumPropagationDelaySeconds is shorter than an active acoustic path delay.",
|
||||
);
|
||||
}
|
||||
const renderMaximumPropagationDelaySeconds = pathSummary
|
||||
? pathSummary.sources.reduce(
|
||||
(maximum, sourceSummary, sourceIndex) =>
|
||||
sampledScene.sources[sourceIndex].signal.type === "tone"
|
||||
? maximum
|
||||
: Math.max(maximum, sourceSummary.maximumDelaySeconds),
|
||||
0,
|
||||
)
|
||||
: maximumPropagationDelaySeconds;
|
||||
const preRollSamples = ceilSampleIndex(
|
||||
renderMaximumPropagationDelaySeconds,
|
||||
sampleRate,
|
||||
);
|
||||
const renderStartSample = Math.max(0, startSample - preRollSamples);
|
||||
const renderDuration = (endSample - renderStartSample) / sampleRate;
|
||||
const sources = [];
|
||||
const sourceOffsets = [];
|
||||
const sourceIndexes = [];
|
||||
|
||||
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 signalDuration = effectiveSignalDurationSeconds(
|
||||
source,
|
||||
scene,
|
||||
options.assetBuffers,
|
||||
);
|
||||
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;
|
||||
const sourceMaximumDelaySeconds =
|
||||
pathSummary?.sources[sourceIndex]?.maximumDelaySeconds ??
|
||||
maximumPropagationDelaySeconds;
|
||||
const latestPossibleArrivalEndSample = ceilSampleIndex(
|
||||
signalStartTime + signalDuration + sourceMaximumDelaySeconds,
|
||||
sampleRate,
|
||||
);
|
||||
const analyticTailMayOverlap =
|
||||
source.signal.type === "tone" &&
|
||||
signalStartSample < endSample &&
|
||||
latestPossibleArrivalEndSample > startSample;
|
||||
if (activeEndSample <= activeStartSample && !analyticTailMayOverlap) continue;
|
||||
const hasEmissionInRender = activeEndSample > activeStartSample;
|
||||
|
||||
sources.push({
|
||||
...source,
|
||||
position: sampledScene.sources[sourceIndex].position,
|
||||
startTime: (activeStartSample - renderStartSample) / sampleRate,
|
||||
duration: (activeEndSample - activeStartSample) / sampleRate,
|
||||
startTime: hasEmissionInRender
|
||||
? (activeStartSample - renderStartSample) / sampleRate
|
||||
: 0,
|
||||
duration: hasEmissionInRender
|
||||
? (activeEndSample - activeStartSample) / sampleRate
|
||||
: renderDuration,
|
||||
});
|
||||
sourceOffsets.push({
|
||||
signalStartTime,
|
||||
signalStartSample,
|
||||
signalDuration,
|
||||
});
|
||||
sourceIndexes.push(sourceIndex);
|
||||
}
|
||||
|
||||
const ambient = Array.isArray(scene.ambient) ? scene.ambient.slice() : scene.ambient;
|
||||
const windowScene = {
|
||||
...scene,
|
||||
duration: renderDuration,
|
||||
sources,
|
||||
...(Array.isArray(scene.ambient) ? { ambient } : {}),
|
||||
};
|
||||
const selectedPathSummary = pathSummary
|
||||
? selectAcousticPathSummary({
|
||||
summary: pathSummary,
|
||||
sourceIndexes,
|
||||
sourceScene: sampledScene,
|
||||
scene: windowScene,
|
||||
arrayDefinition: options.arrayDefinition,
|
||||
referenceDistance: options.referenceDistance ?? 1,
|
||||
minimumDistance: options.minimumDistance ?? 0.01,
|
||||
})
|
||||
: undefined;
|
||||
const signalOffsets = {
|
||||
sceneSampleOffset: renderStartSample,
|
||||
cropStartSample: startSample - renderStartSample,
|
||||
cropSampleCount: endSample - startSample,
|
||||
maximumPropagationDelaySeconds,
|
||||
renderMaximumPropagationDelaySeconds,
|
||||
sourcePositionSampleTime: sampleTime,
|
||||
windowingModel: "quasi-static-midpoint-propagation-preroll",
|
||||
...(selectedPathSummary
|
||||
? { acousticPathSummary: selectedPathSummary }
|
||||
: {}),
|
||||
sources: sourceOffsets,
|
||||
ambient: (scene.ambient ?? []).map(() => ({
|
||||
signalStartTime: 0,
|
||||
@@ -163,7 +240,6 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime, options = {})
|
||||
})),
|
||||
};
|
||||
const duration = (endSample - startSample) / sampleRate;
|
||||
const renderDuration = (endSample - renderStartSample) / sampleRate;
|
||||
return {
|
||||
startTime: alignedStartTime,
|
||||
endTime: alignedEndTime,
|
||||
@@ -176,13 +252,9 @@ export function sampleQuasiStaticWindow(scene, startTime, endTime, options = {})
|
||||
renderDuration,
|
||||
preRollSamples: startSample - renderStartSample,
|
||||
maximumPropagationDelaySeconds,
|
||||
renderMaximumPropagationDelaySeconds,
|
||||
signalOffsets,
|
||||
scene: {
|
||||
...scene,
|
||||
duration: renderDuration,
|
||||
sources,
|
||||
...(Array.isArray(scene.ambient) ? { ambient } : {}),
|
||||
},
|
||||
scene: windowScene,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user