@newgameplusinc/odyssey-audio-video-sdk-dev 1.0.257 → 1.0.259
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} +13 -41
- 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 +455 -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} +31 -42
- 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 +30 -4
- 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 -272
- package/dist/SpatialAudioManager.js +0 -1537
- package/dist/types.d.ts +0 -73
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pan Calculations
|
|
3
|
+
*
|
|
4
|
+
* Functions for calculating stereo panning based on listener/source positions
|
|
5
|
+
*/
|
|
6
|
+
import { Position, ListenerRight } from '../../types/position';
|
|
7
|
+
/**
|
|
8
|
+
* Panning values for left/right channels
|
|
9
|
+
*/
|
|
10
|
+
export interface Panning {
|
|
11
|
+
left: number;
|
|
12
|
+
right: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Calculate listener's right-ear vector from yaw rotation
|
|
16
|
+
*
|
|
17
|
+
* COORDINATE SYSTEM: X=right, Y=up, Z=forward (datum at 0,0,0)
|
|
18
|
+
*
|
|
19
|
+
* Rotation convention (right-hand rule around Y-axis):
|
|
20
|
+
* yaw = 0° → facing +Z (forward), right ear points to +X
|
|
21
|
+
* yaw = 90° → facing +X (right), right ear points to -Z
|
|
22
|
+
* yaw = 180° → facing -Z (backward), right ear points to -X
|
|
23
|
+
* yaw = 270° → facing -X (left), right ear points to +Z
|
|
24
|
+
*
|
|
25
|
+
* @param yawDegrees Yaw rotation in degrees
|
|
26
|
+
* @returns Unit vector pointing to listener's right ear
|
|
27
|
+
*/
|
|
28
|
+
export declare function calculateListenerRight(yawDegrees: number): ListenerRight;
|
|
29
|
+
/**
|
|
30
|
+
* Calculate pan value from listener and source positions
|
|
31
|
+
*
|
|
32
|
+
* Uses dot product projection onto listener's right-ear axis:
|
|
33
|
+
* dxLocal = vecToSource · listenerRight
|
|
34
|
+
*
|
|
35
|
+
* @param listenerPos Listener position
|
|
36
|
+
* @param sourcePos Sound source position
|
|
37
|
+
* @param listenerRight Listener's right-ear unit vector
|
|
38
|
+
* @returns Pan value from -1 (full left) to +1 (full right)
|
|
39
|
+
*/
|
|
40
|
+
export declare function calculatePanFromPositions(listenerPos: Position, sourcePos: Position, listenerRight: ListenerRight): number;
|
|
41
|
+
/**
|
|
42
|
+
* Convert pan value (-1 to +1) to left/right channel percentages
|
|
43
|
+
*
|
|
44
|
+
* @param pan Pan value from -1 (full left) to +1 (full right)
|
|
45
|
+
* @param dxLocal Optional dxLocal value (unused, for compatibility)
|
|
46
|
+
* @returns Panning percentages for left and right channels
|
|
47
|
+
*/
|
|
48
|
+
export declare function panValueToPanning(pan: number, dxLocal?: number): Panning;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pan Calculations
|
|
4
|
+
*
|
|
5
|
+
* Functions for calculating stereo panning based on listener/source positions
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.calculateListenerRight = calculateListenerRight;
|
|
9
|
+
exports.calculatePanFromPositions = calculatePanFromPositions;
|
|
10
|
+
exports.panValueToPanning = panValueToPanning;
|
|
11
|
+
/**
|
|
12
|
+
* Calculate listener's right-ear vector from yaw rotation
|
|
13
|
+
*
|
|
14
|
+
* COORDINATE SYSTEM: X=right, Y=up, Z=forward (datum at 0,0,0)
|
|
15
|
+
*
|
|
16
|
+
* Rotation convention (right-hand rule around Y-axis):
|
|
17
|
+
* yaw = 0° → facing +Z (forward), right ear points to +X
|
|
18
|
+
* yaw = 90° → facing +X (right), right ear points to -Z
|
|
19
|
+
* yaw = 180° → facing -Z (backward), right ear points to -X
|
|
20
|
+
* yaw = 270° → facing -X (left), right ear points to +Z
|
|
21
|
+
*
|
|
22
|
+
* @param yawDegrees Yaw rotation in degrees
|
|
23
|
+
* @returns Unit vector pointing to listener's right ear
|
|
24
|
+
*/
|
|
25
|
+
function calculateListenerRight(yawDegrees) {
|
|
26
|
+
const yawRad = (yawDegrees * Math.PI) / 180;
|
|
27
|
+
// Right ear vector - CLOCKWISE rotation (standard game engine convention)
|
|
28
|
+
return {
|
|
29
|
+
x: Math.cos(yawRad),
|
|
30
|
+
z: -Math.sin(yawRad), // NEGATIVE for clockwise rotation
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Calculate pan value from listener and source positions
|
|
35
|
+
*
|
|
36
|
+
* Uses dot product projection onto listener's right-ear axis:
|
|
37
|
+
* dxLocal = vecToSource · listenerRight
|
|
38
|
+
*
|
|
39
|
+
* @param listenerPos Listener position
|
|
40
|
+
* @param sourcePos Sound source position
|
|
41
|
+
* @param listenerRight Listener's right-ear unit vector
|
|
42
|
+
* @returns Pan value from -1 (full left) to +1 (full right)
|
|
43
|
+
*/
|
|
44
|
+
function calculatePanFromPositions(listenerPos, sourcePos, listenerRight) {
|
|
45
|
+
// Calculate relative vector (speaker relative to listener)
|
|
46
|
+
const vecToSource = {
|
|
47
|
+
x: sourcePos.x - listenerPos.x,
|
|
48
|
+
z: sourcePos.z - listenerPos.z,
|
|
49
|
+
};
|
|
50
|
+
// Project onto listener's right-ear axis using dot product
|
|
51
|
+
// Positive = sound is to the RIGHT of listener
|
|
52
|
+
// Negative = sound is to the LEFT of listener
|
|
53
|
+
const dxLocal = vecToSource.x * listenerRight.x + vecToSource.z * listenerRight.z;
|
|
54
|
+
// Calculate dzLocal (forward/back component)
|
|
55
|
+
// For CLOCKWISE rotation: Forward = right rotated 90° CW: (x,z) -> (-z,x)
|
|
56
|
+
const listenerForward = { x: -listenerRight.z, z: listenerRight.x };
|
|
57
|
+
const dzLocal = vecToSource.x * listenerForward.x + vecToSource.z * listenerForward.z;
|
|
58
|
+
// TRUE 360° SPATIAL AUDIO PANNING
|
|
59
|
+
// Calculate angle from listener to source using atan2
|
|
60
|
+
const angleToSource = Math.atan2(dxLocal, dzLocal); // Radians: -π to +π
|
|
61
|
+
const rawPanValue = Math.sin(angleToSource); // -1 to +1
|
|
62
|
+
return rawPanValue;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Convert pan value (-1 to +1) to left/right channel percentages
|
|
66
|
+
*
|
|
67
|
+
* @param pan Pan value from -1 (full left) to +1 (full right)
|
|
68
|
+
* @param dxLocal Optional dxLocal value (unused, for compatibility)
|
|
69
|
+
* @returns Panning percentages for left and right channels
|
|
70
|
+
*/
|
|
71
|
+
function panValueToPanning(pan, dxLocal) {
|
|
72
|
+
const clamped = Math.max(-1, Math.min(1, pan));
|
|
73
|
+
// Map pan to asymmetric gains while keeping center at 100/100
|
|
74
|
+
const left = 100 * (1 - Math.max(0, clamped));
|
|
75
|
+
const right = 100 * (1 + Math.min(0, clamped));
|
|
76
|
+
return {
|
|
77
|
+
left: Math.max(0, Math.min(100, left)),
|
|
78
|
+
right: Math.max(0, Math.min(100, right)),
|
|
79
|
+
};
|
|
80
|
+
}
|
package/package.json
CHANGED