rodio/source/
spatial.rs

1use std::time::Duration;
2
3use crate::source::ChannelVolume;
4use crate::{Sample, Source};
5
6use super::SeekError;
7
8/// A simple spatial audio source. The underlying source is transformed to Mono
9/// and then played in stereo. The left and right channel's volume are amplified
10/// differently depending on the distance of the left and right ear to the source.
11#[derive(Clone)]
12pub struct Spatial<I>
13where
14    I: Source,
15    I::Item: Sample,
16{
17    input: ChannelVolume<I>,
18}
19
20fn dist_sq(a: [f32; 3], b: [f32; 3]) -> f32 {
21    a.iter()
22        .zip(b.iter())
23        .map(|(a, b)| (a - b) * (a - b))
24        .sum::<f32>()
25}
26
27impl<I> Spatial<I>
28where
29    I: Source,
30    I::Item: Sample,
31{
32    /// Builds a new `SpatialSink`, beginning playback on a stream.
33    pub fn new(
34        input: I,
35        emitter_position: [f32; 3],
36        left_ear: [f32; 3],
37        right_ear: [f32; 3],
38    ) -> Spatial<I>
39    where
40        I: Source,
41        I::Item: Sample,
42    {
43        let mut ret = Spatial {
44            input: ChannelVolume::new(input, vec![0.0, 0.0]),
45        };
46        ret.set_positions(emitter_position, left_ear, right_ear);
47        ret
48    }
49
50    /// Sets the position of the emitter and ears in the 3D world.
51    pub fn set_positions(
52        &mut self,
53        emitter_pos: [f32; 3],
54        left_ear: [f32; 3],
55        right_ear: [f32; 3],
56    ) {
57        debug_assert!(left_ear != right_ear);
58        let left_dist_sq = dist_sq(left_ear, emitter_pos);
59        let right_dist_sq = dist_sq(right_ear, emitter_pos);
60        let max_diff = dist_sq(left_ear, right_ear).sqrt();
61        let left_dist = left_dist_sq.sqrt();
62        let right_dist = right_dist_sq.sqrt();
63        let left_diff_modifier = (((left_dist - right_dist) / max_diff + 1.0) / 4.0 + 0.5).min(1.0);
64        let right_diff_modifier =
65            (((right_dist - left_dist) / max_diff + 1.0) / 4.0 + 0.5).min(1.0);
66        let left_dist_modifier = (1.0 / left_dist_sq).min(1.0);
67        let right_dist_modifier = (1.0 / right_dist_sq).min(1.0);
68        self.input
69            .set_volume(0, left_diff_modifier * left_dist_modifier);
70        self.input
71            .set_volume(1, right_diff_modifier * right_dist_modifier);
72    }
73}
74
75impl<I> Iterator for Spatial<I>
76where
77    I: Source,
78    I::Item: Sample,
79{
80    type Item = I::Item;
81
82    #[inline]
83    fn next(&mut self) -> Option<I::Item> {
84        self.input.next()
85    }
86
87    #[inline]
88    fn size_hint(&self) -> (usize, Option<usize>) {
89        self.input.size_hint()
90    }
91}
92
93impl<I> ExactSizeIterator for Spatial<I>
94where
95    I: Source + ExactSizeIterator,
96    I::Item: Sample,
97{
98}
99
100impl<I> Source for Spatial<I>
101where
102    I: Source,
103    I::Item: Sample,
104{
105    #[inline]
106    fn current_frame_len(&self) -> Option<usize> {
107        self.input.current_frame_len()
108    }
109
110    #[inline]
111    fn channels(&self) -> u16 {
112        self.input.channels()
113    }
114
115    #[inline]
116    fn sample_rate(&self) -> u32 {
117        self.input.sample_rate()
118    }
119
120    #[inline]
121    fn total_duration(&self) -> Option<Duration> {
122        self.input.total_duration()
123    }
124
125    #[inline]
126    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
127        self.input.try_seek(pos)
128    }
129}