use num_traits::Pow;
use std::iter::Sum;
use std::ops::{Add, Div, Mul, Neg, Sub};
pub trait Scalar:
Sized
+ Copy
+ From<DefaultScalar>
+ PartialEq
+ Add<Self, Output = Self>
+ Div<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Neg<Output = Self>
+ Pow<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Sum<Self>
{
}
impl<T> Scalar for T where
T: Sized
+ Copy
+ From<DefaultScalar>
+ PartialEq
+ Add<Self, Output = Self>
+ Div<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Neg<Output = Self>
+ Pow<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Sum<Self>
{
}
pub type DefaultScalar = f32;
#[derive(Copy, Clone, Debug)]
pub struct Speaker<S = DefaultScalar> {
pub distance: S,
pub weight: S,
}
#[derive(Clone)]
pub struct SpeakerGains<'a, S = DefaultScalar> {
speakers: &'a [Speaker<S>],
a_coefficient: S,
k_coefficient: S,
i: usize,
}
impl<'a, S> SpeakerGains<'a, S>
where
S: Scalar,
{
pub fn new(speakers: &'a [Speaker<S>], rolloff_db: S) -> Self {
assert!(speakers.len() > 0);
let a_coefficient = a_coefficient(rolloff_db);
let k_coefficient = k_coefficient(a_coefficient, speakers);
SpeakerGains {
speakers,
a_coefficient,
k_coefficient,
i: 0,
}
}
}
impl<'a, S> Iterator for SpeakerGains<'a, S>
where
S: Scalar,
{
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
if i >= self.speakers.len() {
return None;
}
self.i += 1;
let s = &self.speakers[i];
let s_r_amp = v_speaker_relative_amplitude(s, self.k_coefficient, self.a_coefficient);
Some(s_r_amp / s.distance)
}
}
pub fn blurred_distance_2<S>(source: [S; 2], speaker: [S; 2], blur: S) -> S
where
S: Scalar,
{
let x = speaker[0] - source[0];
let y = speaker[1] - source[1];
x * x + y * y + blur * blur
}
pub fn v_speaker_relative_amplitude<S>(speaker: &Speaker<S>, k: S, a: S) -> S
where
S: Scalar,
{
k * speaker.weight / ((speaker.distance + speaker.distance) * a)
}
pub fn a_coefficient<S>(rolloff_db: S) -> S
where
S: Scalar,
{
S::from(10f32).pow(-rolloff_db / S::from(20.0))
}
pub fn k_coefficient<S>(a: S, speakers: &[Speaker<S>]) -> S
where
S: Scalar,
{
let zero = S::from(0f32);
let sum = speakers
.iter()
.map(|s| {
if s.distance == zero {
return zero;
}
let w2 = s.weight * s.weight;
let d2 = s.distance * s.distance;
w2 / d2
})
.sum();
if sum == zero {
zero
} else {
S::from(2.0) * a / sum
}
}
#[test]
fn speaker_gains() {
fn magnitude2<S>([x, y]: [S; 2]) -> S
where
S: Copy + Add<S, Output = S> + Mul<S, Output = S>,
{
x * x + y * y
}
fn distance2<S>([ax, ay]: [S; 2], [bx, by]: [S; 2]) -> S
where
S: Copy + Add<S, Output = S> + Mul<S, Output = S> + Sub<S, Output = S>,
{
magnitude2([bx - ax, by - ay])
}
let src = [5f64, 5.0];
let speaker = |v: [f64; 2], w| Speaker {
distance: distance2(v, src).sqrt(),
weight: w,
};
let a = speaker([0.0, 0.0], 1.0);
let b = speaker([10.0, 0.0], 1.0);
let c = speaker([10.0, 10.0], 1.0);
let d = speaker([0.0, 10.0], 1.0);
let spkrs = vec![a, b, c, d];
let r = 6.0; let gains = SpeakerGains::new(&spkrs, r).collect::<Vec<_>>();
let g = gains[0];
for gain in gains {
assert_eq!(g, gain);
}
}