@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.258 → 1.0.260
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -1
- package/dist/audio/AudioNodeFactory.d.ts +130 -0
- package/dist/audio/AudioNodeFactory.js +158 -0
- package/dist/audio/AudioPipeline.d.ts +89 -0
- package/dist/audio/AudioPipeline.js +138 -0
- package/dist/{MLNoiseSuppressor.d.ts → audio/MLNoiseSuppressor.d.ts} +7 -7
- package/dist/{MLNoiseSuppressor.js → audio/MLNoiseSuppressor.js} +19 -52
- package/dist/audio/index.d.ts +6 -0
- package/dist/audio/index.js +22 -0
- package/dist/channels/huddle/HuddleChannel.d.ts +87 -0
- package/dist/channels/huddle/HuddleChannel.js +152 -0
- package/dist/channels/huddle/HuddleTypes.d.ts +85 -0
- package/dist/channels/huddle/HuddleTypes.js +25 -0
- package/dist/channels/huddle/index.d.ts +5 -0
- package/dist/channels/huddle/index.js +21 -0
- package/dist/channels/index.d.ts +5 -0
- package/dist/channels/index.js +21 -0
- package/dist/channels/spatial/SpatialAudioChannel.d.ts +144 -0
- package/dist/channels/spatial/SpatialAudioChannel.js +476 -0
- package/dist/channels/spatial/SpatialAudioTypes.d.ts +85 -0
- package/dist/channels/spatial/SpatialAudioTypes.js +42 -0
- package/dist/channels/spatial/index.d.ts +5 -0
- package/dist/channels/spatial/index.js +21 -0
- package/dist/{EventManager.d.ts → core/EventManager.d.ts} +4 -2
- package/dist/{EventManager.js → core/EventManager.js} +5 -3
- package/dist/{MediasoupManager.d.ts → core/MediasoupManager.d.ts} +10 -4
- package/dist/{MediasoupManager.js → core/MediasoupManager.js} +56 -44
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +21 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +43 -9
- package/dist/sdk/index.d.ts +36 -0
- package/dist/sdk/index.js +121 -0
- package/dist/types/events.d.ts +154 -0
- package/dist/{types.js → types/events.js} +3 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +23 -0
- package/dist/types/participant.d.ts +65 -0
- package/dist/types/participant.js +5 -0
- package/dist/types/position.d.ts +47 -0
- package/dist/types/position.js +9 -0
- package/dist/types/room.d.ts +82 -0
- package/dist/types/room.js +5 -0
- package/dist/utils/audio/clarity-score.d.ts +33 -0
- package/dist/utils/audio/clarity-score.js +81 -0
- package/dist/utils/audio/index.d.ts +5 -0
- package/dist/utils/audio/index.js +21 -0
- package/dist/utils/audio/voice-filter.d.ts +30 -0
- package/dist/utils/audio/voice-filter.js +70 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/position/coordinates.d.ts +37 -0
- package/dist/utils/position/coordinates.js +61 -0
- package/dist/utils/position/index.d.ts +6 -0
- package/dist/utils/position/index.js +22 -0
- package/dist/utils/position/normalize.d.ts +37 -0
- package/dist/utils/position/normalize.js +78 -0
- package/dist/utils/position/snap.d.ts +51 -0
- package/dist/utils/position/snap.js +81 -0
- package/dist/utils/smoothing/gain-smoothing.d.ts +45 -0
- package/dist/utils/smoothing/gain-smoothing.js +77 -0
- package/dist/utils/smoothing/index.d.ts +5 -0
- package/dist/utils/smoothing/index.js +21 -0
- package/dist/utils/smoothing/pan-smoothing.d.ts +43 -0
- package/dist/utils/smoothing/pan-smoothing.js +85 -0
- package/dist/utils/spatial/angle-calc.d.ts +24 -0
- package/dist/utils/spatial/angle-calc.js +69 -0
- package/dist/utils/spatial/distance-calc.d.ts +33 -0
- package/dist/utils/spatial/distance-calc.js +48 -0
- package/dist/utils/spatial/gain-calc.d.ts +37 -0
- package/dist/utils/spatial/gain-calc.js +52 -0
- package/dist/utils/spatial/head-position.d.ts +32 -0
- package/dist/utils/spatial/head-position.js +76 -0
- package/dist/utils/spatial/index.d.ts +9 -0
- package/dist/utils/spatial/index.js +25 -0
- package/dist/utils/spatial/listener-calc.d.ts +28 -0
- package/dist/utils/spatial/listener-calc.js +74 -0
- package/dist/utils/spatial/pan-calc.d.ts +48 -0
- package/dist/utils/spatial/pan-calc.js +80 -0
- package/package.json +1 -1
- package/dist/SpatialAudioManager.d.ts +0 -271
- package/dist/SpatialAudioManager.js +0 -1512
- package/dist/types.d.ts +0 -73
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Smoothing utilities - Re-exports all smoothing functions
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./pan-smoothing"), exports);
|
|
21
|
+
__exportStar(require("./gain-smoothing"), exports);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pan Smoothing
|
|
3
|
+
*
|
|
4
|
+
* Classes and functions for smoothing stereo panning values
|
|
5
|
+
* to prevent audio jitter and rapid left/right jumping
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Pan smoother configuration
|
|
9
|
+
*/
|
|
10
|
+
export interface PanSmootherOptions {
|
|
11
|
+
smoothingFactor?: number;
|
|
12
|
+
changeThreshold?: number;
|
|
13
|
+
centerDeadZone?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default pan smoother options
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_PAN_SMOOTHER_OPTIONS: Required<PanSmootherOptions>;
|
|
19
|
+
/**
|
|
20
|
+
* Pan smoother class
|
|
21
|
+
* Smooths pan values using EMA with dead-zone and threshold
|
|
22
|
+
*/
|
|
23
|
+
export declare class PanSmoother {
|
|
24
|
+
private smoothedValues;
|
|
25
|
+
private options;
|
|
26
|
+
constructor(options?: PanSmootherOptions);
|
|
27
|
+
/**
|
|
28
|
+
* Smooth a pan value for a participant
|
|
29
|
+
*
|
|
30
|
+
* @param participantId Participant ID
|
|
31
|
+
* @param newPanValue New pan value (-1 to +1)
|
|
32
|
+
* @returns Smoothed pan value
|
|
33
|
+
*/
|
|
34
|
+
smooth(participantId: string, newPanValue: number): number;
|
|
35
|
+
/**
|
|
36
|
+
* Clear smoothed value for a participant
|
|
37
|
+
*/
|
|
38
|
+
clear(participantId: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all smoothed values
|
|
41
|
+
*/
|
|
42
|
+
clearAll(): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pan Smoothing
|
|
4
|
+
*
|
|
5
|
+
* Classes and functions for smoothing stereo panning values
|
|
6
|
+
* to prevent audio jitter and rapid left/right jumping
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PanSmoother = exports.DEFAULT_PAN_SMOOTHER_OPTIONS = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Default pan smoother options
|
|
12
|
+
*/
|
|
13
|
+
exports.DEFAULT_PAN_SMOOTHER_OPTIONS = {
|
|
14
|
+
smoothingFactor: 0.35,
|
|
15
|
+
changeThreshold: 0.02,
|
|
16
|
+
centerDeadZone: 0.03,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Pan smoother class
|
|
20
|
+
* Smooths pan values using EMA with dead-zone and threshold
|
|
21
|
+
*/
|
|
22
|
+
class PanSmoother {
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.smoothedValues = new Map();
|
|
25
|
+
this.options = { ...exports.DEFAULT_PAN_SMOOTHER_OPTIONS, ...options };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Smooth a pan value for a participant
|
|
29
|
+
*
|
|
30
|
+
* @param participantId Participant ID
|
|
31
|
+
* @param newPanValue New pan value (-1 to +1)
|
|
32
|
+
* @returns Smoothed pan value
|
|
33
|
+
*/
|
|
34
|
+
smooth(participantId, newPanValue) {
|
|
35
|
+
const previousPan = this.smoothedValues.get(participantId);
|
|
36
|
+
// DEAD-ZONE: If new pan is very close to center, snap to exactly 0
|
|
37
|
+
let targetPan = newPanValue;
|
|
38
|
+
if (Math.abs(newPanValue) < this.options.centerDeadZone) {
|
|
39
|
+
targetPan = 0;
|
|
40
|
+
}
|
|
41
|
+
// If no previous value, initialize with current value
|
|
42
|
+
if (previousPan === undefined) {
|
|
43
|
+
this.smoothedValues.set(participantId, targetPan);
|
|
44
|
+
return targetPan;
|
|
45
|
+
}
|
|
46
|
+
// Calculate the change from previous pan
|
|
47
|
+
const panChange = Math.abs(targetPan - previousPan);
|
|
48
|
+
// If change is below threshold, keep previous value (prevents micro-jitter)
|
|
49
|
+
if (panChange < this.options.changeThreshold) {
|
|
50
|
+
return previousPan;
|
|
51
|
+
}
|
|
52
|
+
// Determine effective smoothing factor based on change magnitude
|
|
53
|
+
let effectiveSmoothingFactor = 0.7; // High smoothing = slow but stable
|
|
54
|
+
const signFlipped = (previousPan > 0 && targetPan < 0) || (previousPan < 0 && targetPan > 0);
|
|
55
|
+
const bothNearCenter = Math.abs(previousPan) < 0.2 && Math.abs(targetPan) < 0.2;
|
|
56
|
+
if (bothNearCenter) {
|
|
57
|
+
// Near center - use moderate smoothing
|
|
58
|
+
effectiveSmoothingFactor = 0.5;
|
|
59
|
+
}
|
|
60
|
+
else if (signFlipped && panChange > 1.0) {
|
|
61
|
+
// Full flip - likely jitter, use HEAVY smoothing
|
|
62
|
+
effectiveSmoothingFactor = 0.85;
|
|
63
|
+
}
|
|
64
|
+
// Apply exponential moving average smoothing
|
|
65
|
+
const smoothedPan = previousPan * effectiveSmoothingFactor + targetPan * (1 - effectiveSmoothingFactor);
|
|
66
|
+
// Apply dead-zone to final smoothed value
|
|
67
|
+
const finalPan = Math.abs(smoothedPan) < this.options.centerDeadZone ? 0 : smoothedPan;
|
|
68
|
+
// Store for next update
|
|
69
|
+
this.smoothedValues.set(participantId, finalPan);
|
|
70
|
+
return finalPan;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear smoothed value for a participant
|
|
74
|
+
*/
|
|
75
|
+
clear(participantId) {
|
|
76
|
+
this.smoothedValues.delete(participantId);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear all smoothed values
|
|
80
|
+
*/
|
|
81
|
+
clearAll() {
|
|
82
|
+
this.smoothedValues.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.PanSmoother = PanSmoother;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angle Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for computing angles between positions
|
|
5
|
+
*/
|
|
6
|
+
import { Position } from '../../types/position';
|
|
7
|
+
/**
|
|
8
|
+
* Compute azimuth angle from listener to source
|
|
9
|
+
*
|
|
10
|
+
* @param listener Listener position
|
|
11
|
+
* @param source Source position
|
|
12
|
+
* @returns Angle in degrees (0-360), where 0° = forward (+Z), 90° = right (+X)
|
|
13
|
+
*/
|
|
14
|
+
export declare function computeAzimuthFromPositions(listener: Position, source: Position): number;
|
|
15
|
+
/**
|
|
16
|
+
* Calculate angle between listener and sound source in degrees
|
|
17
|
+
* Takes into account listener's forward direction
|
|
18
|
+
*
|
|
19
|
+
* @param listenerPos Listener position
|
|
20
|
+
* @param sourcePos Source position
|
|
21
|
+
* @param listenerForward Listener's forward direction vector
|
|
22
|
+
* @returns Angle in degrees (0-360): 0° = front, 90° = right, 180° = back, 270° = left
|
|
23
|
+
*/
|
|
24
|
+
export declare function calculateAngleToSource(listenerPos: Position, sourcePos: Position, listenerForward: Position): number;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Angle Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for computing angles between positions
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.computeAzimuthFromPositions = computeAzimuthFromPositions;
|
|
9
|
+
exports.calculateAngleToSource = calculateAngleToSource;
|
|
10
|
+
/**
|
|
11
|
+
* Compute azimuth angle from listener to source
|
|
12
|
+
*
|
|
13
|
+
* @param listener Listener position
|
|
14
|
+
* @param source Source position
|
|
15
|
+
* @returns Angle in degrees (0-360), where 0° = forward (+Z), 90° = right (+X)
|
|
16
|
+
*/
|
|
17
|
+
function computeAzimuthFromPositions(listener, source) {
|
|
18
|
+
const vx = source.x - listener.x;
|
|
19
|
+
const vz = source.z - listener.z;
|
|
20
|
+
const angleRad = Math.atan2(vx, vz); // 0° = forward (Z+), 90° = right (X+)
|
|
21
|
+
const deg = (angleRad * 180) / Math.PI;
|
|
22
|
+
const normalized = (deg + 360) % 360;
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate angle between listener and sound source in degrees
|
|
27
|
+
* Takes into account listener's forward direction
|
|
28
|
+
*
|
|
29
|
+
* @param listenerPos Listener position
|
|
30
|
+
* @param sourcePos Source position
|
|
31
|
+
* @param listenerForward Listener's forward direction vector
|
|
32
|
+
* @returns Angle in degrees (0-360): 0° = front, 90° = right, 180° = back, 270° = left
|
|
33
|
+
*/
|
|
34
|
+
function calculateAngleToSource(listenerPos, sourcePos, listenerForward) {
|
|
35
|
+
// Use X/Z as the horizontal plane for 360° panning
|
|
36
|
+
const vx = sourcePos.x - listenerPos.x;
|
|
37
|
+
const vz = sourcePos.z - listenerPos.z;
|
|
38
|
+
// Project listener forward onto X/Z plane
|
|
39
|
+
let fx = listenerForward.x;
|
|
40
|
+
let fz = listenerForward.z;
|
|
41
|
+
const fLen = Math.hypot(fx, fz);
|
|
42
|
+
if (fLen < 1e-4) {
|
|
43
|
+
// Fallback: if forward is nearly vertical/invalid, assume world-forward
|
|
44
|
+
fx = 0;
|
|
45
|
+
fz = 1;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
fx /= fLen;
|
|
49
|
+
fz /= fLen;
|
|
50
|
+
}
|
|
51
|
+
const vLen = Math.hypot(vx, vz);
|
|
52
|
+
if (vLen < 1e-4) {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
const nx = vx / vLen;
|
|
56
|
+
const nz = vz / vLen;
|
|
57
|
+
// Signed angle from forward->source
|
|
58
|
+
const dot = fx * nx + fz * nz;
|
|
59
|
+
const cross = fx * nz - fz * nx;
|
|
60
|
+
let radians = Math.atan2(cross, dot);
|
|
61
|
+
// atan2 gives [-π, π]; map to [0, 2π)
|
|
62
|
+
if (radians < 0) {
|
|
63
|
+
radians += Math.PI * 2;
|
|
64
|
+
}
|
|
65
|
+
const degrees = (radians * 180) / Math.PI;
|
|
66
|
+
// Convert to convention where +90 is to the right
|
|
67
|
+
const angle = (360 - degrees) % 360;
|
|
68
|
+
return angle;
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distance Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for calculating distances between positions in 3D space
|
|
5
|
+
*/
|
|
6
|
+
import { Position } from '../../types/position';
|
|
7
|
+
/**
|
|
8
|
+
* Calculate 3D Euclidean distance between two positions
|
|
9
|
+
* distance = √(Δx² + Δy² + Δz²)
|
|
10
|
+
*
|
|
11
|
+
* @param a First position
|
|
12
|
+
* @param b Second position
|
|
13
|
+
* @returns Distance in the same units as input positions
|
|
14
|
+
*/
|
|
15
|
+
export declare function getDistanceBetween(a: Position, b: Position): number;
|
|
16
|
+
/**
|
|
17
|
+
* Calculate horizontal distance (XZ plane only, ignoring Y/height)
|
|
18
|
+
* Useful for spatial audio where vertical distance matters less
|
|
19
|
+
*
|
|
20
|
+
* @param a First position
|
|
21
|
+
* @param b Second position
|
|
22
|
+
* @returns Horizontal distance
|
|
23
|
+
*/
|
|
24
|
+
export declare function getHorizontalDistance(a: Position, b: Position): number;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a position is within a certain range of another
|
|
27
|
+
*
|
|
28
|
+
* @param a First position
|
|
29
|
+
* @param b Second position
|
|
30
|
+
* @param maxDistance Maximum allowed distance
|
|
31
|
+
* @returns True if within range
|
|
32
|
+
*/
|
|
33
|
+
export declare function isWithinRange(a: Position, b: Position, maxDistance: number): boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Distance Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for calculating distances between positions in 3D space
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.getDistanceBetween = getDistanceBetween;
|
|
9
|
+
exports.getHorizontalDistance = getHorizontalDistance;
|
|
10
|
+
exports.isWithinRange = isWithinRange;
|
|
11
|
+
/**
|
|
12
|
+
* Calculate 3D Euclidean distance between two positions
|
|
13
|
+
* distance = √(Δx² + Δy² + Δz²)
|
|
14
|
+
*
|
|
15
|
+
* @param a First position
|
|
16
|
+
* @param b Second position
|
|
17
|
+
* @returns Distance in the same units as input positions
|
|
18
|
+
*/
|
|
19
|
+
function getDistanceBetween(a, b) {
|
|
20
|
+
const dx = b.x - a.x;
|
|
21
|
+
const dy = b.y - a.y;
|
|
22
|
+
const dz = b.z - a.z;
|
|
23
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate horizontal distance (XZ plane only, ignoring Y/height)
|
|
27
|
+
* Useful for spatial audio where vertical distance matters less
|
|
28
|
+
*
|
|
29
|
+
* @param a First position
|
|
30
|
+
* @param b Second position
|
|
31
|
+
* @returns Horizontal distance
|
|
32
|
+
*/
|
|
33
|
+
function getHorizontalDistance(a, b) {
|
|
34
|
+
const dx = b.x - a.x;
|
|
35
|
+
const dz = b.z - a.z;
|
|
36
|
+
return Math.sqrt(dx * dx + dz * dz);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a position is within a certain range of another
|
|
40
|
+
*
|
|
41
|
+
* @param a First position
|
|
42
|
+
* @param b Second position
|
|
43
|
+
* @param maxDistance Maximum allowed distance
|
|
44
|
+
* @returns True if within range
|
|
45
|
+
*/
|
|
46
|
+
function isWithinRange(a, b, maxDistance) {
|
|
47
|
+
return getDistanceBetween(a, b) <= maxDistance;
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gain Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for calculating audio gain based on distance
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Gain calculation configuration
|
|
8
|
+
*/
|
|
9
|
+
export interface GainConfig {
|
|
10
|
+
minDistance?: number;
|
|
11
|
+
maxDistance?: number;
|
|
12
|
+
minGain?: number;
|
|
13
|
+
falloffRate?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default gain configuration
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_GAIN_CONFIG: Required<GainConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Calculate gain based on distance using inverse-square law
|
|
21
|
+
*
|
|
22
|
+
* Uses inverse-square law (realistic sound propagation) with floor:
|
|
23
|
+
* Gain = 1 / (1 + k * distance²)
|
|
24
|
+
*
|
|
25
|
+
* Distance → Gain mapping (with defaults):
|
|
26
|
+
* - 0-1m → 100% (full volume, very close)
|
|
27
|
+
* - 2m → ~80%
|
|
28
|
+
* - 3m → ~55%
|
|
29
|
+
* - 5m → ~30%
|
|
30
|
+
* - 8m → ~15%
|
|
31
|
+
* - 10m+ → 5% (minimum, barely audible)
|
|
32
|
+
*
|
|
33
|
+
* @param distance Distance in meters
|
|
34
|
+
* @param config Optional gain configuration
|
|
35
|
+
* @returns Gain as percentage (0-100)
|
|
36
|
+
*/
|
|
37
|
+
export declare function calculateLogarithmicGain(distance: number, config?: GainConfig): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gain Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for calculating audio gain based on distance
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DEFAULT_GAIN_CONFIG = void 0;
|
|
9
|
+
exports.calculateLogarithmicGain = calculateLogarithmicGain;
|
|
10
|
+
/**
|
|
11
|
+
* Default gain configuration
|
|
12
|
+
*/
|
|
13
|
+
exports.DEFAULT_GAIN_CONFIG = {
|
|
14
|
+
minDistance: 1.0, // Full volume at 1m or closer
|
|
15
|
+
maxDistance: 15.0, // Cannot hear beyond 15m
|
|
16
|
+
minGain: 5, // Minimum 5% at far distances
|
|
17
|
+
falloffRate: 0.15, // Controls how fast volume drops
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Calculate gain based on distance using inverse-square law
|
|
21
|
+
*
|
|
22
|
+
* Uses inverse-square law (realistic sound propagation) with floor:
|
|
23
|
+
* Gain = 1 / (1 + k * distance²)
|
|
24
|
+
*
|
|
25
|
+
* Distance → Gain mapping (with defaults):
|
|
26
|
+
* - 0-1m → 100% (full volume, very close)
|
|
27
|
+
* - 2m → ~80%
|
|
28
|
+
* - 3m → ~55%
|
|
29
|
+
* - 5m → ~30%
|
|
30
|
+
* - 8m → ~15%
|
|
31
|
+
* - 10m+ → 5% (minimum, barely audible)
|
|
32
|
+
*
|
|
33
|
+
* @param distance Distance in meters
|
|
34
|
+
* @param config Optional gain configuration
|
|
35
|
+
* @returns Gain as percentage (0-100)
|
|
36
|
+
*/
|
|
37
|
+
function calculateLogarithmicGain(distance, config = {}) {
|
|
38
|
+
const { minDistance, maxDistance, minGain, falloffRate, } = { ...exports.DEFAULT_GAIN_CONFIG, ...config };
|
|
39
|
+
// Beyond max distance = minimum gain
|
|
40
|
+
if (distance >= maxDistance)
|
|
41
|
+
return minGain;
|
|
42
|
+
// Full volume within minimum distance
|
|
43
|
+
if (distance <= minDistance)
|
|
44
|
+
return 100;
|
|
45
|
+
// Inverse square falloff: gain = 1 / (1 + k * d²)
|
|
46
|
+
// This models realistic sound propagation in air
|
|
47
|
+
const effectiveDistance = distance - minDistance;
|
|
48
|
+
const attenuation = 1 / (1 + falloffRate * effectiveDistance * effectiveDistance);
|
|
49
|
+
// Scale to percentage range: minGain to 100
|
|
50
|
+
const gain = minGain + attenuation * (100 - minGain);
|
|
51
|
+
return Math.round(gain);
|
|
52
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Head Position Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for computing head/mouth position from body position
|
|
5
|
+
*/
|
|
6
|
+
import { Position } from '../../types/position';
|
|
7
|
+
/**
|
|
8
|
+
* Compute estimated head/mouth position from body position
|
|
9
|
+
* Body position is typically at feet/base - add head height offset
|
|
10
|
+
*
|
|
11
|
+
* @param bodyPosition Body position (typically feet/base)
|
|
12
|
+
* @param headHeight Height offset to add (default: 1.6m)
|
|
13
|
+
* @returns Estimated head position
|
|
14
|
+
*/
|
|
15
|
+
export declare function computeHeadPosition(bodyPosition: Position, headHeight?: number): Position;
|
|
16
|
+
/**
|
|
17
|
+
* Parse body height string to meters
|
|
18
|
+
* Handles various formats: "170", "170cm", "1.7m", etc.
|
|
19
|
+
*
|
|
20
|
+
* @param bodyHeightStr Body height string from participant data
|
|
21
|
+
* @param fallback Fallback height in meters (default: 1.6)
|
|
22
|
+
* @returns Height in meters
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseBodyHeight(bodyHeightStr?: string, fallback?: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* Get vector from listener to target position
|
|
27
|
+
*
|
|
28
|
+
* @param listenerPosition Listener position
|
|
29
|
+
* @param targetPosition Target position
|
|
30
|
+
* @returns Vector from listener to target
|
|
31
|
+
*/
|
|
32
|
+
export declare function getVectorFromListener(listenerPosition: Position, targetPosition: Position): Position;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Head Position Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for computing head/mouth position from body position
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.computeHeadPosition = computeHeadPosition;
|
|
9
|
+
exports.parseBodyHeight = parseBodyHeight;
|
|
10
|
+
exports.getVectorFromListener = getVectorFromListener;
|
|
11
|
+
/**
|
|
12
|
+
* Default head height offset in meters
|
|
13
|
+
* Average human head height above body origin
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_HEAD_HEIGHT = 1.6;
|
|
16
|
+
/**
|
|
17
|
+
* Compute estimated head/mouth position from body position
|
|
18
|
+
* Body position is typically at feet/base - add head height offset
|
|
19
|
+
*
|
|
20
|
+
* @param bodyPosition Body position (typically feet/base)
|
|
21
|
+
* @param headHeight Height offset to add (default: 1.6m)
|
|
22
|
+
* @returns Estimated head position
|
|
23
|
+
*/
|
|
24
|
+
function computeHeadPosition(bodyPosition, headHeight = DEFAULT_HEAD_HEIGHT) {
|
|
25
|
+
return {
|
|
26
|
+
x: bodyPosition.x,
|
|
27
|
+
y: bodyPosition.y + headHeight,
|
|
28
|
+
z: bodyPosition.z,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse body height string to meters
|
|
33
|
+
* Handles various formats: "170", "170cm", "1.7m", etc.
|
|
34
|
+
*
|
|
35
|
+
* @param bodyHeightStr Body height string from participant data
|
|
36
|
+
* @param fallback Fallback height in meters (default: 1.6)
|
|
37
|
+
* @returns Height in meters
|
|
38
|
+
*/
|
|
39
|
+
function parseBodyHeight(bodyHeightStr, fallback = 1.6) {
|
|
40
|
+
if (!bodyHeightStr)
|
|
41
|
+
return fallback;
|
|
42
|
+
const str = bodyHeightStr.toLowerCase().trim();
|
|
43
|
+
// Try parsing as number with optional unit
|
|
44
|
+
const numMatch = str.match(/^([\d.]+)\s*(cm|m)?$/);
|
|
45
|
+
if (numMatch) {
|
|
46
|
+
const value = parseFloat(numMatch[1]);
|
|
47
|
+
const unit = numMatch[2];
|
|
48
|
+
if (isNaN(value))
|
|
49
|
+
return fallback;
|
|
50
|
+
if (unit === 'm') {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
else if (unit === 'cm' || value > 10) {
|
|
54
|
+
// Assume centimeters if > 10 or explicitly cm
|
|
55
|
+
return value / 100;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return fallback;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get vector from listener to target position
|
|
65
|
+
*
|
|
66
|
+
* @param listenerPosition Listener position
|
|
67
|
+
* @param targetPosition Target position
|
|
68
|
+
* @returns Vector from listener to target
|
|
69
|
+
*/
|
|
70
|
+
function getVectorFromListener(listenerPosition, targetPosition) {
|
|
71
|
+
return {
|
|
72
|
+
x: targetPosition.x - listenerPosition.x,
|
|
73
|
+
y: targetPosition.y - listenerPosition.y,
|
|
74
|
+
z: targetPosition.z - listenerPosition.z,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spatial utilities - Re-exports all spatial calculation functions
|
|
3
|
+
*/
|
|
4
|
+
export * from './distance-calc';
|
|
5
|
+
export * from './gain-calc';
|
|
6
|
+
export * from './pan-calc';
|
|
7
|
+
export * from './head-position';
|
|
8
|
+
export * from './listener-calc';
|
|
9
|
+
export * from './angle-calc';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Spatial utilities - Re-exports all spatial calculation functions
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./distance-calc"), exports);
|
|
21
|
+
__exportStar(require("./gain-calc"), exports);
|
|
22
|
+
__exportStar(require("./pan-calc"), exports);
|
|
23
|
+
__exportStar(require("./head-position"), exports);
|
|
24
|
+
__exportStar(require("./listener-calc"), exports);
|
|
25
|
+
__exportStar(require("./angle-calc"), exports);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listener Orientation Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for computing listener orientation vectors
|
|
5
|
+
*/
|
|
6
|
+
import { Position, ListenerRight, Rotation } from '../../types/position';
|
|
7
|
+
/**
|
|
8
|
+
* Full listener orientation (forward and up vectors)
|
|
9
|
+
*/
|
|
10
|
+
export interface ListenerOrientation {
|
|
11
|
+
forward: Position;
|
|
12
|
+
up: Position;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Calculate listener's right-ear vector from rotation
|
|
16
|
+
*
|
|
17
|
+
* @param rot Rotation in degrees (pitch, yaw, roll)
|
|
18
|
+
* @returns Right-ear unit vector (XZ plane)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getListenerRightFromRotation(rot?: Rotation): ListenerRight;
|
|
21
|
+
/**
|
|
22
|
+
* Calculate full listener orientation from camera vectors
|
|
23
|
+
*
|
|
24
|
+
* @param cameraPos Camera position
|
|
25
|
+
* @param lookAtPos Look-at target position
|
|
26
|
+
* @returns Forward and up vectors for Web Audio API
|
|
27
|
+
*/
|
|
28
|
+
export declare function calculateListenerOrientation(cameraPos: Position, lookAtPos: Position): ListenerOrientation | null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Listener Orientation Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for computing listener orientation vectors
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.getListenerRightFromRotation = getListenerRightFromRotation;
|
|
9
|
+
exports.calculateListenerOrientation = calculateListenerOrientation;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate listener's right-ear vector from rotation
|
|
12
|
+
*
|
|
13
|
+
* @param rot Rotation in degrees (pitch, yaw, roll)
|
|
14
|
+
* @returns Right-ear unit vector (XZ plane)
|
|
15
|
+
*/
|
|
16
|
+
function getListenerRightFromRotation(rot) {
|
|
17
|
+
if (rot && typeof rot.y === 'number') {
|
|
18
|
+
const yawRad = (rot.y * Math.PI) / 180;
|
|
19
|
+
// Right ear vector - CLOCKWISE rotation (standard game engine convention)
|
|
20
|
+
// At yaw=0: facing +Z, right ear at +X → (1, 0)
|
|
21
|
+
// At yaw=90: facing +X, right ear at -Z → (0, -1)
|
|
22
|
+
return {
|
|
23
|
+
x: Math.cos(yawRad),
|
|
24
|
+
z: -Math.sin(yawRad),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Fallback: assume facing forward with right ear to +X
|
|
28
|
+
return { x: 1, z: 0 };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Calculate full listener orientation from camera vectors
|
|
32
|
+
*
|
|
33
|
+
* @param cameraPos Camera position
|
|
34
|
+
* @param lookAtPos Look-at target position
|
|
35
|
+
* @returns Forward and up vectors for Web Audio API
|
|
36
|
+
*/
|
|
37
|
+
function calculateListenerOrientation(cameraPos, lookAtPos) {
|
|
38
|
+
// Calculate forward vector (from camera to look-at point)
|
|
39
|
+
const forwardX = lookAtPos.x - cameraPos.x;
|
|
40
|
+
const forwardY = lookAtPos.y - cameraPos.y;
|
|
41
|
+
const forwardZ = lookAtPos.z - cameraPos.z;
|
|
42
|
+
// Normalize forward vector
|
|
43
|
+
const forwardLen = Math.sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
|
|
44
|
+
if (forwardLen < 0.001) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const fwdX = forwardX / forwardLen;
|
|
48
|
+
const fwdY = forwardY / forwardLen;
|
|
49
|
+
const fwdZ = forwardZ / forwardLen;
|
|
50
|
+
// Calculate right vector (cross product of world up and forward)
|
|
51
|
+
const worldUp = { x: 0, y: 1, z: 0 };
|
|
52
|
+
const rightX = worldUp.y * fwdZ - worldUp.z * fwdY;
|
|
53
|
+
const rightY = worldUp.z * fwdX - worldUp.x * fwdZ;
|
|
54
|
+
const rightZ = worldUp.x * fwdY - worldUp.y * fwdX;
|
|
55
|
+
const rightLen = Math.sqrt(rightX * rightX + rightY * rightY + rightZ * rightZ);
|
|
56
|
+
if (rightLen < 0.001) {
|
|
57
|
+
// Forward is parallel to world up, use fallback
|
|
58
|
+
return {
|
|
59
|
+
forward: { x: fwdX, y: fwdY, z: fwdZ },
|
|
60
|
+
up: { x: 0, y: 1, z: 0 },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const rX = rightX / rightLen;
|
|
64
|
+
const rY = rightY / rightLen;
|
|
65
|
+
const rZ = rightZ / rightLen;
|
|
66
|
+
// Calculate true up vector (cross product of forward and right)
|
|
67
|
+
const upX = fwdY * rZ - fwdZ * rY;
|
|
68
|
+
const upY = fwdZ * rX - fwdX * rZ;
|
|
69
|
+
const upZ = fwdX * rY - fwdY * rX;
|
|
70
|
+
return {
|
|
71
|
+
forward: { x: fwdX, y: fwdY, z: fwdZ },
|
|
72
|
+
up: { x: upX, y: upY, z: upZ },
|
|
73
|
+
};
|
|
74
|
+
}
|