@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.
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} +13 -41
  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 +455 -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} +31 -42
  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 +30 -4
  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 -272
  82. package/dist/SpatialAudioManager.js +0 -1537
  83. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newgameplusinc/odyssey-audio-video-sdk-dev",
3
- "version": "1.0.257",
3
+ "version": "1.0.259",
4
4
  "description": "Odyssey Spatial Audio & Video SDK using MediaSoup for real-time communication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",