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}