rg3d_sound/renderer/
mod.rs1#![allow(clippy::float_cmp)]
9
10use crate::{
11 context::DistanceModel,
12 listener::Listener,
13 math,
14 renderer::hrtf::HrtfRenderer,
15 source::{generic::GenericSource, SoundSource},
16};
17use rg3d_core::visitor::{Visit, VisitResult, Visitor};
18
19pub mod hrtf;
20
21#[allow(clippy::large_enum_variant)]
25#[derive(Debug, Clone)]
26pub enum Renderer {
27 Default,
29
30 HrtfRenderer(HrtfRenderer),
33}
34
35impl Renderer {
36 fn id(&self) -> u32 {
37 match self {
38 Renderer::Default => 0,
39 Renderer::HrtfRenderer(_) => 1,
40 }
41 }
42
43 fn from_id(id: u32) -> Result<Self, String> {
44 match id {
45 0 => Ok(Self::Default),
46 1 => Ok(Self::HrtfRenderer(HrtfRenderer::default())),
47 _ => Err(format!("Invalid renderer id {}!", id)),
48 }
49 }
50}
51
52impl Default for Renderer {
53 fn default() -> Self {
54 Self::Default
55 }
56}
57
58fn render_with_params(
59 source: &mut GenericSource,
60 left_gain: f32,
61 right_gain: f32,
62 mix_buffer: &mut [(f32, f32)],
63) {
64 let last_left_gain = *source.last_left_gain.get_or_insert(left_gain);
65 let last_right_gain = *source.last_right_gain.get_or_insert(right_gain);
66
67 if last_left_gain != left_gain || last_right_gain != right_gain {
68 let step = 1.0 / mix_buffer.len() as f32;
69 let mut t = 0.0;
70 for ((out_left, out_right), &(raw_left, raw_right)) in
71 mix_buffer.iter_mut().zip(source.frame_samples())
72 {
73 *out_left += math::lerpf(last_left_gain, left_gain, t) * raw_left;
76 *out_right += math::lerpf(last_right_gain, right_gain, t) * raw_right;
77
78 t += step;
79 }
80 } else {
81 for ((out_left, out_right), &(raw_left, raw_right)) in
82 mix_buffer.iter_mut().zip(source.frame_samples())
83 {
84 *out_left += left_gain * raw_left;
86 *out_right += right_gain * raw_right;
87 }
88 }
89}
90
91pub(in crate) fn render_source_default(
92 source: &mut SoundSource,
93 listener: &Listener,
94 distance_model: DistanceModel,
95 mix_buffer: &mut [(f32, f32)],
96) {
97 match source {
98 SoundSource::Generic(generic) => {
99 let gain = generic.gain();
100 let panning = generic.panning();
101 let left_gain = gain * (1.0 + panning);
102 let right_gain = gain * (1.0 - panning);
103 render_with_params(generic, left_gain, right_gain, mix_buffer);
104 generic.last_left_gain = Some(left_gain);
105 generic.last_right_gain = Some(right_gain);
106 }
107 SoundSource::Spatial(spatial) => {
108 let distance_gain = spatial.get_distance_gain(listener, distance_model);
109 let panning = spatial.get_panning(listener);
110 let gain = distance_gain * spatial.generic().gain();
111 let left_gain = gain * (1.0 + panning);
112 let right_gain = gain * (1.0 - panning);
113 render_with_params(spatial.generic_mut(), left_gain, right_gain, mix_buffer);
114 spatial.generic_mut().last_left_gain = Some(left_gain);
115 spatial.generic_mut().last_right_gain = Some(right_gain);
116 }
117 }
118}
119
120impl Visit for Renderer {
121 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
122 visitor.enter_region(name)?;
123
124 let mut id = self.id();
125 id.visit("Id", visitor)?;
126 if visitor.is_reading() {
127 *self = Self::from_id(id)?;
128 }
129 match self {
130 Renderer::Default => {}
131 Renderer::HrtfRenderer(hrtf) => {
132 hrtf.visit("Hrtf", visitor)?;
133 }
134 }
135
136 visitor.leave_region()
137 }
138}