36 lines
1.5 KiB
Text
36 lines
1.5 KiB
Text
# place.ludic - where a sound is, heard from the listener. Not an inverse square, which is right for
|
|
# a point in free air and wrong for a valley full of trees: a shaped roll-off keeps a call audible
|
|
# across a meadow and gone across a lake
|
|
const TAU: float = 6.2831853
|
|
const HALF_TURN: float = 3.14159265
|
|
|
|
export function aud_gain_at(x: float, z: float, range: float) -> float {
|
|
let dx = x - AudioListener.x()
|
|
let dz = z - AudioListener.z()
|
|
let d = Math.sqrt(dx * dx + dz * dz)
|
|
if d > range or range <= 0.0 { return 0.0 }
|
|
let t = 1.0 - d / range
|
|
return Math.clamp(t * t, 0.0, 1.0)
|
|
}
|
|
|
|
# Which side, -1 left .. 1 right, from the listener's heading. Ahead and behind are both centred:
|
|
# that is all a two-speaker pan can honestly say.
|
|
export function aud_pan_at(x: float, z: float) -> float {
|
|
let dx = x - AudioListener.x()
|
|
let dz = z - AudioListener.z()
|
|
if dx * dx + dz * dz < 1.0 { return 0.0 }
|
|
let rel = wrap(Math.atan2(-dx, -dz) - AudioListener.yaw())
|
|
# forward for a yaw is (-sin, -cos), so a bearing to the LEFT is positive out of atan2
|
|
return Math.clamp(-Math.sin(rel) * 0.85, -1.0, 1.0)
|
|
}
|
|
|
|
# Air takes the top off a distant sound: a semitone lower at the edge of hearing is most of why a
|
|
# far call reads as far rather than as a quiet near one.
|
|
export function aud_pitch_at(gain: float) -> float { return 0.94 + gain * 0.09 }
|
|
|
|
function wrap(a: float) -> float {
|
|
var d = a
|
|
while d > HALF_TURN { d = d - TAU }
|
|
while d < -HALF_TURN { d = d + TAU }
|
|
return d
|
|
}
|