feat: add realistic portable localization core
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { dbToLinear, distance3 } from "./math.js";
|
||||
import {
|
||||
addFrequencyDomainDelay,
|
||||
addFrequencyDomainPaths,
|
||||
createDelayWorkspace,
|
||||
nextPowerOfTwo,
|
||||
prepareDelaySpectrum,
|
||||
} from "./fractional-delay.js";
|
||||
import { buildAcousticPaths, maximumAcousticDelaySeconds } from "./environment.js";
|
||||
import { renderSignal } from "./signal.js";
|
||||
|
||||
export function propagationDelaySeconds(sourcePosition, receiverPosition, speedOfSound) {
|
||||
@@ -27,15 +29,13 @@ export function synthesizeMicrophoneSignals(options) {
|
||||
} = options;
|
||||
const sampleCount = Math.ceil(scene.duration * scene.sampleRate);
|
||||
const retainedBufferCount = arrayDefinition.microphones.length + scene.sources.length + 1;
|
||||
let maximumDelaySamples = 0;
|
||||
for (const source of scene.sources) {
|
||||
for (const microphone of arrayDefinition.microphones) {
|
||||
maximumDelaySamples = Math.max(
|
||||
maximumDelaySamples,
|
||||
(distance3(source.position, microphone.position) / scene.speedOfSound) * scene.sampleRate,
|
||||
);
|
||||
}
|
||||
}
|
||||
const maximumDelaySamples =
|
||||
maximumAcousticDelaySeconds({
|
||||
scene,
|
||||
arrayDefinition,
|
||||
referenceDistance,
|
||||
minimumDistance,
|
||||
}) * scene.sampleRate;
|
||||
const delayFftSize = nextPowerOfTwo(sampleCount + Math.ceil(maximumDelaySamples) + 2);
|
||||
const delayWorkingBytes = delayFftSize * Float64Array.BYTES_PER_ELEMENT * 4;
|
||||
const estimatedBufferBytes =
|
||||
@@ -56,22 +56,49 @@ export function synthesizeMicrophoneSignals(options) {
|
||||
}),
|
||||
}));
|
||||
|
||||
const acousticPathCounts = {};
|
||||
let acousticPathCount = 0;
|
||||
renderedSources.forEach(({ definition, samples }) => {
|
||||
const distances = arrayDefinition.microphones.map((microphone) => distance3(definition.position, microphone.position));
|
||||
const maximumDelaySamples = (Math.max(...distances) / scene.speedOfSound) * scene.sampleRate;
|
||||
const spectrum = prepareDelaySpectrum(samples, maximumDelaySamples);
|
||||
const microphonePaths = arrayDefinition.microphones.map((microphone) =>
|
||||
buildAcousticPaths({
|
||||
scene,
|
||||
sourcePosition: definition.position,
|
||||
receiverPosition: microphone.position,
|
||||
sourceId: definition.id,
|
||||
receiverId: microphone.id,
|
||||
referenceDistance,
|
||||
minimumDistance,
|
||||
}),
|
||||
);
|
||||
let sourceMaximumDelaySamples = 0;
|
||||
for (const paths of microphonePaths) {
|
||||
for (const path of paths) {
|
||||
sourceMaximumDelaySamples = Math.max(
|
||||
sourceMaximumDelaySamples,
|
||||
path.delaySeconds * scene.sampleRate,
|
||||
);
|
||||
}
|
||||
}
|
||||
const spectrum = prepareDelaySpectrum(samples, sourceMaximumDelaySamples);
|
||||
const workspace = createDelayWorkspace(spectrum.fftSize);
|
||||
arrayDefinition.microphones.forEach((microphone, microphoneIndex) => {
|
||||
const distance = distances[microphoneIndex];
|
||||
const delaySamples = (distance / scene.speedOfSound) * scene.sampleRate;
|
||||
const paths = microphonePaths[microphoneIndex];
|
||||
for (const path of paths) {
|
||||
acousticPathCount += 1;
|
||||
acousticPathCounts[path.kind] = (acousticPathCounts[path.kind] ?? 0) + 1;
|
||||
}
|
||||
const sensitivity = dbToLinear(microphone.sensitivityDb ?? 0);
|
||||
const amplitude = sphericalAttenuation(distance, referenceDistance, minimumDistance) * sensitivity;
|
||||
addFrequencyDomainDelay({
|
||||
addFrequencyDomainPaths({
|
||||
output: channels[microphoneIndex],
|
||||
spectrum,
|
||||
delaySamples,
|
||||
gain: amplitude,
|
||||
paths: paths.map((path) => ({
|
||||
delaySamples: path.delaySeconds * scene.sampleRate,
|
||||
gain: path.gain,
|
||||
})),
|
||||
gain: sensitivity,
|
||||
phaseOffsetDeg: microphone.phaseOffsetDeg ?? 0,
|
||||
frequencyResponse: microphone.frequencyResponse,
|
||||
sampleRate: scene.sampleRate,
|
||||
workspace,
|
||||
});
|
||||
});
|
||||
@@ -93,6 +120,8 @@ export function synthesizeMicrophoneSignals(options) {
|
||||
delaySamples: 0,
|
||||
gain: dbToLinear(microphone.sensitivityDb ?? 0),
|
||||
phaseOffsetDeg: microphone.phaseOffsetDeg ?? 0,
|
||||
frequencyResponse: microphone.frequencyResponse,
|
||||
sampleRate: scene.sampleRate,
|
||||
workspace: createDelayWorkspace(spectrum.fftSize),
|
||||
});
|
||||
});
|
||||
@@ -127,5 +156,8 @@ export function synthesizeMicrophoneSignals(options) {
|
||||
appliedScale,
|
||||
clippedSampleCount,
|
||||
estimatedBufferBytes,
|
||||
acousticModel: scene.acoustics?.model ?? "free-field",
|
||||
acousticPathCount,
|
||||
acousticPathCounts,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user