1use crate::{context::SoundContext, device};
8use rg3d_core::visitor::{Visit, VisitResult, Visitor};
9use std::sync::{Arc, Mutex};
10
11#[derive(Default)]
13pub struct SoundEngine {
14 contexts: Vec<SoundContext>,
15 master_gain: f32,
16}
17
18impl SoundEngine {
19 pub fn new() -> Arc<Mutex<Self>> {
23 let engine = Arc::new(Mutex::new(Self {
24 contexts: Default::default(),
25 master_gain: 1.0,
26 }));
27
28 device::run_device(4 * SoundContext::SAMPLES_PER_CHANNEL as u32, {
32 let state = engine.clone();
33 move |buf| {
34 if let Ok(mut state) = state.lock() {
35 state.render_inner(buf);
36 }
37 }
38 });
39
40 engine
41 }
42
43 pub fn without_device() -> Arc<Mutex<Self>> {
46 Arc::new(Mutex::new(Self {
47 contexts: Default::default(),
48 master_gain: 1.0,
49 }))
50 }
51
52 pub fn add_context(&mut self, context: SoundContext) {
55 self.contexts.push(context);
56 }
57
58 pub fn remove_context(&mut self, context: SoundContext) {
60 if let Some(position) = self.contexts.iter().position(|c| c == &context) {
61 self.contexts.remove(position);
62 }
63 }
64
65 pub fn has_context(&self, context: &SoundContext) -> bool {
67 self.contexts
68 .iter()
69 .any(|c| Arc::ptr_eq(c.state.as_ref().unwrap(), context.state.as_ref().unwrap()))
70 }
71
72 pub fn contexts(&self) -> &[SoundContext] {
74 &self.contexts
75 }
76
77 pub fn set_master_gain(&mut self, master_gain: f32) {
79 self.master_gain = master_gain;
80 }
81
82 pub fn master_gain(&self) -> f32 {
84 self.master_gain
85 }
86
87 pub fn render_buffer_len() -> usize {
89 SoundContext::SAMPLES_PER_CHANNEL
90 }
91
92 pub fn render(&mut self, buf: &mut [(f32, f32)]) {
101 buf.fill((0.0, 0.0));
102 self.render_inner(buf);
103 }
104
105 fn render_inner(&mut self, buf: &mut [(f32, f32)]) {
106 let master_gain = self.master_gain;
107 for context in self.contexts.iter_mut() {
108 context.state().render(master_gain, buf);
109 }
110 }
111}
112
113impl Visit for SoundEngine {
114 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
115 visitor.enter_region(name)?;
116
117 if visitor.is_reading() {
118 self.contexts.clear();
119 }
120
121 self.master_gain.visit("MasterGain", visitor)?;
122 self.contexts.visit("Contexts", visitor)?;
123
124 visitor.leave_region()
125 }
126}