@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.
Files changed (83) hide show
  1. package/README.md +45 -1
  2. package/dist/audio/AudioNodeFactory.d.ts +130 -0
  3. package/dist/audio/AudioNodeFactory.js +158 -0
  4. package/dist/audio/AudioPipeline.d.ts +89 -0
  5. package/dist/audio/AudioPipeline.js +138 -0
  6. package/dist/{MLNoiseSuppressor.d.ts → audio/MLNoiseSuppressor.d.ts} +7 -7
  7. package/dist/{MLNoiseSuppressor.js → audio/MLNoiseSuppressor.js} +19 -52
  8. package/dist/audio/index.d.ts +6 -0
  9. package/dist/audio/index.js +22 -0
  10. package/dist/channels/huddle/HuddleChannel.d.ts +87 -0
  11. package/dist/channels/huddle/HuddleChannel.js +152 -0
  12. package/dist/channels/huddle/HuddleTypes.d.ts +85 -0
  13. package/dist/channels/huddle/HuddleTypes.js +25 -0
  14. package/dist/channels/huddle/index.d.ts +5 -0
  15. package/dist/channels/huddle/index.js +21 -0
  16. package/dist/channels/index.d.ts +5 -0
  17. package/dist/channels/index.js +21 -0
  18. package/dist/channels/spatial/SpatialAudioChannel.d.ts +144 -0
  19. package/dist/channels/spatial/SpatialAudioChannel.js +476 -0
  20. package/dist/channels/spatial/SpatialAudioTypes.d.ts +85 -0
  21. package/dist/channels/spatial/SpatialAudioTypes.js +42 -0
  22. package/dist/channels/spatial/index.d.ts +5 -0
  23. package/dist/channels/spatial/index.js +21 -0
  24. package/dist/{EventManager.d.ts → core/EventManager.d.ts} +4 -2
  25. package/dist/{EventManager.js → core/EventManager.js} +5 -3
  26. package/dist/{MediasoupManager.d.ts → core/MediasoupManager.d.ts} +10 -4
  27. package/dist/{MediasoupManager.js → core/MediasoupManager.js} +56 -44
  28. package/dist/core/index.d.ts +5 -0
  29. package/dist/core/index.js +21 -0
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +43 -9
  32. package/dist/sdk/index.d.ts +36 -0
  33. package/dist/sdk/index.js +121 -0
  34. package/dist/types/events.d.ts +154 -0
  35. package/dist/{types.js → types/events.js} +3 -0
  36. package/dist/types/index.d.ts +7 -0
  37. package/dist/types/index.js +23 -0
  38. package/dist/types/participant.d.ts +65 -0
  39. package/dist/types/participant.js +5 -0
  40. package/dist/types/position.d.ts +47 -0
  41. package/dist/types/position.js +9 -0
  42. package/dist/types/room.d.ts +82 -0
  43. package/dist/types/room.js +5 -0
  44. package/dist/utils/audio/clarity-score.d.ts +33 -0
  45. package/dist/utils/audio/clarity-score.js +81 -0
  46. package/dist/utils/audio/index.d.ts +5 -0
  47. package/dist/utils/audio/index.js +21 -0
  48. package/dist/utils/audio/voice-filter.d.ts +30 -0
  49. package/dist/utils/audio/voice-filter.js +70 -0
  50. package/dist/utils/index.d.ts +7 -0
  51. package/dist/utils/index.js +23 -0
  52. package/dist/utils/position/coordinates.d.ts +37 -0
  53. package/dist/utils/position/coordinates.js +61 -0
  54. package/dist/utils/position/index.d.ts +6 -0
  55. package/dist/utils/position/index.js +22 -0
  56. package/dist/utils/position/normalize.d.ts +37 -0
  57. package/dist/utils/position/normalize.js +78 -0
  58. package/dist/utils/position/snap.d.ts +51 -0
  59. package/dist/utils/position/snap.js +81 -0
  60. package/dist/utils/smoothing/gain-smoothing.d.ts +45 -0
  61. package/dist/utils/smoothing/gain-smoothing.js +77 -0
  62. package/dist/utils/smoothing/index.d.ts +5 -0
  63. package/dist/utils/smoothing/index.js +21 -0
  64. package/dist/utils/smoothing/pan-smoothing.d.ts +43 -0
  65. package/dist/utils/smoothing/pan-smoothing.js +85 -0
  66. package/dist/utils/spatial/angle-calc.d.ts +24 -0
  67. package/dist/utils/spatial/angle-calc.js +69 -0
  68. package/dist/utils/spatial/distance-calc.d.ts +33 -0
  69. package/dist/utils/spatial/distance-calc.js +48 -0
  70. package/dist/utils/spatial/gain-calc.d.ts +37 -0
  71. package/dist/utils/spatial/gain-calc.js +52 -0
  72. package/dist/utils/spatial/head-position.d.ts +32 -0
  73. package/dist/utils/spatial/head-position.js +76 -0
  74. package/dist/utils/spatial/index.d.ts +9 -0
  75. package/dist/utils/spatial/index.js +25 -0
  76. package/dist/utils/spatial/listener-calc.d.ts +28 -0
  77. package/dist/utils/spatial/listener-calc.js +74 -0
  78. package/dist/utils/spatial/pan-calc.d.ts +48 -0
  79. package/dist/utils/spatial/pan-calc.js +80 -0
  80. package/package.json +1 -1
  81. package/dist/SpatialAudioManager.d.ts +0 -271
  82. package/dist/SpatialAudioManager.js +0 -1512
  83. 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
+ }