audio: Calculate sound transform values using 30-bit integers
The fix in #5218 wasn't sufficient; 30-bit arithmetic should be used along all the way when calculating an effective sound transform. For example, a sound transform composited by volumes `-0x80000000` and `25` should end up as effectively 0, whereas previously it would have been calculated as `-0x80000000 * 25 / 100 = -0x20000000`, which is a 30-bit integer that hasn't been truncated. Fixes #5655.
This commit is contained in:
parent
e2b821e92f
commit
b32ec3cf6c
|
@ -580,21 +580,12 @@ impl From<display_object::SoundTransform> for SoundTransform {
|
||||||
fn from(other: display_object::SoundTransform) -> Self {
|
fn from(other: display_object::SoundTransform) -> Self {
|
||||||
const SCALE: f32 = display_object::SoundTransform::MAX_VOLUME.pow(2) as f32;
|
const SCALE: f32 = display_object::SoundTransform::MAX_VOLUME.pow(2) as f32;
|
||||||
|
|
||||||
// It seems like Flash stores sound transform values in 30-bit unsigned integers:
|
// The volume multiplication wraps around at `u32::MAX`.
|
||||||
// * Negative values are equivalent to their absolute value.
|
|
||||||
// * Specifically, 0x40000000, -0x40000000 and -0x80000000 are equivalent to zero.
|
|
||||||
// * The volume multiplication wraps around at `u32::MAX`.
|
|
||||||
let left_to_left = (other.left_to_left << 2) >> 2;
|
|
||||||
let left_to_right = (other.left_to_right << 2) >> 2;
|
|
||||||
let right_to_left = (other.right_to_left << 2) >> 2;
|
|
||||||
let right_to_right = (other.right_to_right << 2) >> 2;
|
|
||||||
let volume = (other.volume << 2) >> 2;
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
left_to_left: left_to_left.wrapping_mul(volume) as f32 / SCALE,
|
left_to_left: other.left_to_left.wrapping_mul(other.volume) as f32 / SCALE,
|
||||||
left_to_right: left_to_right.wrapping_mul(volume) as f32 / SCALE,
|
left_to_right: other.left_to_right.wrapping_mul(other.volume) as f32 / SCALE,
|
||||||
right_to_left: right_to_left.wrapping_mul(volume) as f32 / SCALE,
|
right_to_left: other.right_to_left.wrapping_mul(other.volume) as f32 / SCALE,
|
||||||
right_to_right: right_to_right.wrapping_mul(volume) as f32 / SCALE,
|
right_to_right: other.right_to_right.wrapping_mul(other.volume) as f32 / SCALE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1554,11 +1554,17 @@ impl SoundTransform {
|
||||||
|
|
||||||
/// Applies another SoundTransform on top of this SoundTransform.
|
/// Applies another SoundTransform on top of this SoundTransform.
|
||||||
pub fn concat(&mut self, other: &SoundTransform) {
|
pub fn concat(&mut self, other: &SoundTransform) {
|
||||||
|
const MAX_VOLUME: i64 = SoundTransform::MAX_VOLUME as i64;
|
||||||
|
|
||||||
|
// It seems like Flash masks the results below to 30-bit integers:
|
||||||
|
// * Negative values are equivalent to their absolute value (their sign bit is unset).
|
||||||
|
// * Specifically, 0x40000000, -0x40000000 and -0x80000000 are equivalent to zero.
|
||||||
|
const MASK: i32 = (1 << 30) - 1;
|
||||||
|
|
||||||
|
self.volume = (i64::from(self.volume) * i64::from(other.volume) / MAX_VOLUME) as i32 & MASK;
|
||||||
|
|
||||||
// This is a 2x2 matrix multiply between the transforms.
|
// This is a 2x2 matrix multiply between the transforms.
|
||||||
// Done with integer math to match Flash behavior.
|
// Done with integer math to match Flash behavior.
|
||||||
const MAX_VOLUME: i64 = SoundTransform::MAX_VOLUME as i64;
|
|
||||||
self.volume = (i64::from(self.volume) * i64::from(other.volume) / MAX_VOLUME) as i32;
|
|
||||||
|
|
||||||
let ll0: i64 = self.left_to_left.into();
|
let ll0: i64 = self.left_to_left.into();
|
||||||
let lr0: i64 = self.left_to_right.into();
|
let lr0: i64 = self.left_to_right.into();
|
||||||
let rl0: i64 = self.right_to_left.into();
|
let rl0: i64 = self.right_to_left.into();
|
||||||
|
@ -1567,10 +1573,10 @@ impl SoundTransform {
|
||||||
let lr1: i64 = other.left_to_right.into();
|
let lr1: i64 = other.left_to_right.into();
|
||||||
let rl1: i64 = other.right_to_left.into();
|
let rl1: i64 = other.right_to_left.into();
|
||||||
let rr1: i64 = other.right_to_right.into();
|
let rr1: i64 = other.right_to_right.into();
|
||||||
self.left_to_left = ((ll0 * ll1 + rl0 * lr1) / MAX_VOLUME) as i32;
|
self.left_to_left = ((ll0 * ll1 + rl0 * lr1) / MAX_VOLUME) as i32 & MASK;
|
||||||
self.left_to_right = ((lr0 * ll1 + rr0 * lr1) / MAX_VOLUME) as i32;
|
self.left_to_right = ((lr0 * ll1 + rr0 * lr1) / MAX_VOLUME) as i32 & MASK;
|
||||||
self.right_to_left = ((ll0 * rl1 + rl0 * rr1) / MAX_VOLUME) as i32;
|
self.right_to_left = ((ll0 * rl1 + rl0 * rr1) / MAX_VOLUME) as i32 & MASK;
|
||||||
self.right_to_right = ((lr0 * rl1 + rr0 * rr1) / MAX_VOLUME) as i32;
|
self.right_to_right = ((lr0 * rl1 + rr0 * rr1) / MAX_VOLUME) as i32 & MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pan of this transform.
|
/// Returns the pan of this transform.
|
||||||
|
|
Loading…
Reference in New Issue