Skip to content

fix: spatial panning gain match to mono + click prevention - #66

Merged
popuz merged 7 commits into
mainfrom
fix/spatial-audio-panning-lower-volume
May 8, 2026
Merged

fix: spatial panning gain match to mono + click prevention#66
popuz merged 7 commits into
mainfrom
fix/spatial-audio-panning-lower-volume

Conversation

@popuz

@popuz popuz commented May 4, 2026

Copy link
Copy Markdown

Summary

Spatial audio panning fixes for voice chat:

  • Loudness: Spatial mode no longer attenuates the signal at center position. Both channels reach unity gain when the speaker is in front, matching Mono / Community Voice Chat loudness (was −3 dB due to equal-power 0.707 floor).
  • Clicks: Click artifacts on rapid camera rotation eliminated via a short gain ramp (~5 ms) at the start of each audio buffer. Ramp is skipped automatically when gain change is below audible threshold (saves CPU when source is stationary).

Companion PR: decentraland/unity-explorer#8610

What changed (technical)

  • Replaced equal-power `cos/sin` panning law with exponential ILD curve: `gainFar = exp(-α·|pan|)`, `gainNear = 1.0`
  • Default `α = 2.0` (`Strong` profile) → far ear at 90° ≈ −13 dB
  • Added click-free linear ramp at buffer start; auto-bypass via `Δgain` threshold
  • Inspector knobs added (build-irrelevant): `IldProfile` enum, `panningRampMs`, `gainDeltaThreshold`

@popuz popuz self-assigned this May 4, 2026
@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

🔗 Merge Alignment Reminder

If this PR targets main, please make sure the corresponding changes in unity-explorer are also ready to merge.

Both repos should be merged in coordination to avoid breaking changes. Do not merge one without the other being ready.

@popuz
popuz marked this pull request as ready for review May 5, 2026 11:42
@popuz
popuz requested a review from NickKhalow May 5, 2026 11:47
@popuz popuz changed the title switched to exp instead of cos/sin to get proper gain pan curve fix: spatial panning gain match to mono May 5, 2026
@popuz popuz changed the title fix: spatial panning gain match to mono fix: spatial panning gain match to mono + click prevention May 5, 2026
Subtle, // α=1.2, ≈ −8 dB at 90° — average across voice band
Moderate, // α=1.5, ≈ −10 dB at 90° — slightly emphasized
Strong, // α=2.0, ≈ −13 dB at 90° — high-freq end of natural ILD
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a method AsAlpha() to keep the locality

@popuz
popuz merged commit bdb26fb into main May 8, 2026
@popuz
popuz deleted the fix/spatial-audio-panning-lower-volume branch May 8, 2026 20:06
popuz added a commit to decentraland/unity-explorer that referenced this pull request May 8, 2026
…lls (#8610)

## Summary

Spatial audio panning fixes for voice chat:
- **Loudness:** Spatial mode no longer attenuates the signal at center position. Both channels reach unity gain when the speaker is in front, matching Mono / Community Voice Chat loudness (was −3 dB due to equal-power 0.707 floor).
- **Clicks:** Click artifacts on rapid camera rotation eliminated via a short gain ramp (~5 ms) at the start of each audio buffer. Ramp is skipped automatically when gain change is below audible threshold (saves CPU when source is stationary).
- **Smooth FP↔TP audio:** Removed the FirstPerson-only optimization in `NearbyAudioPositionSystem`. Reprojection is now mode-independent (always uses the player head as anchor), so the source position stays continuous through the Cinemachine FirstPerson↔ThirdPerson camera blend instead of jumping at the moment of mode switch.

Main logic change is in LiveKit PR: decentraland/client-sdk-unity#66

## What changed (technical)

- Replaced equal-power `cos/sin` panning law with exponential ILD curve: `gainFar = exp(-α·|pan|)`, `gainNear = 1.0`
- Default `α = 2.0` (`Strong` profile) → far ear at 90° ≈ −13 dB
- Added click-free linear ramp at buffer start; auto-bypass via `Δgain` threshold
- Inspector knobs added (build-irrelevant): `IldProfile` enum, `panningRampMs`, `gainDeltaThreshold`
- `NearbyAudioPositionSystem` no longer branches on `isFirstPerson`. Source position is always computed as `listener.position + (remoteHead − playerHead)` with `playerHead = PlayerComponent.CameraFocus.position`. In FirstPerson the camera and the head are nearly collocated, so the formula degenerates to the previous FP behavior; during the FP↔TP blend, both `listener.position` (camera) and the listener-local frame change smoothly, eliminating the audible jump that the previous mode-gated branch produced.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants