nesbox_utils/
audio.rs

1use bevy::{prelude::*, utils::HashSet};
2
3use crate::assets::AssetsResource;
4
5#[derive(Debug, Clone, Eq, PartialEq, Hash)]
6pub struct Audio {
7    pub name: String,
8    pub offset: usize,
9    pub cycle: bool,
10    pub volume: u32,
11}
12
13impl Default for Audio {
14    fn default() -> Self {
15        Self {
16            name: String::new(),
17            offset: 0,
18            cycle: false,
19            volume: 100,
20        }
21    }
22}
23
24#[derive(Resource, Default, Clone)]
25pub struct AudioResource {
26    out_buf: Vec<f32>,
27    current_audios: HashSet<Audio>,
28    enabled: bool,
29}
30
31impl AudioResource {
32    pub fn sound(&self) -> bool {
33        self.enabled
34    }
35
36    pub fn set_sound(&mut self, enabled: bool) {
37        self.enabled = enabled;
38    }
39
40    pub fn frame(&self) -> &[f32] {
41        &self.out_buf
42    }
43
44    pub fn clear(&mut self) {
45        self.current_audios.clear();
46    }
47
48    pub fn remove(&mut self, audio: &Audio) {
49        self.current_audios.remove(audio);
50    }
51
52    pub fn play(&mut self, name: &str) -> &Audio {
53        self.current_audios.get_or_insert(Audio {
54            name: name.into(),
55            ..default()
56        })
57    }
58
59    pub fn play_audio(&mut self, audio: Audio) -> &Audio {
60        self.current_audios.get_or_insert(audio)
61    }
62}
63
64#[derive(Debug)]
65pub struct AudioPlugin {
66    sample_rate: usize,
67    fps: usize,
68}
69
70impl Default for AudioPlugin {
71    fn default() -> Self {
72        Self {
73            sample_rate: 44100,
74            fps: 60,
75        }
76    }
77}
78
79impl AudioPlugin {
80    fn out(mut audio_resource: ResMut<AudioResource>, assets_resource: Res<AssetsResource>) {
81        let len = audio_resource.out_buf.len();
82        let enabled = audio_resource.enabled;
83        let mut out_buf = vec![0.; len];
84        let mut new_set = HashSet::new();
85
86        let mut append = |b: &[f32], volume: u32| {
87            if enabled {
88                let v = (volume.min(100) as f32) / 100.;
89                for (i, b_val) in b.iter().enumerate() {
90                    out_buf[i] += b_val * v;
91                }
92            }
93        };
94
95        for mut audio in audio_resource.current_audios.drain() {
96            if let Some(buf) = assets_resource.get_audio(&audio.name) {
97                if audio.offset + len < buf.len() {
98                    append(&buf[audio.offset..audio.offset + len], audio.volume);
99                    audio.offset += len;
100                    new_set.insert_unique_unchecked(audio);
101                } else if audio.cycle {
102                    append(&buf[0..len], audio.volume);
103                    audio.offset = len;
104                    new_set.insert_unique_unchecked(audio);
105                }
106            }
107        }
108        audio_resource.current_audios = new_set;
109        audio_resource.out_buf = out_buf;
110    }
111}
112
113impl Plugin for AudioPlugin {
114    fn build(&self, app: &mut App) {
115        app.insert_resource(AudioResource {
116            out_buf: vec![0.; self.sample_rate / self.fps],
117            ..default()
118        })
119        .add_system(Self::out.in_base_set(CoreSet::Last));
120    }
121}