1use crate::{filesystem, Context, GameResult};
7use std::collections::HashMap;
8
9#[cfg(all(feature = "audio", not(target_os = "ios")))]
10use quad_snd::{AudioContext as QuadSndContext, Sound as QuadSndSound};
11
12#[cfg(all(feature = "audio", not(target_os = "ios")))]
13pub use quad_snd::PlaySoundParams;
14
15#[cfg(any(not(feature = "audio"), target_os = "ios"))]
16mod dummy_audio {
17 use crate::audio::PlaySoundParams;
18
19 pub struct AudioContext {}
20
21 impl AudioContext {
22 pub fn new() -> AudioContext {
23 AudioContext {}
24 }
25
26 pub fn pause(&mut self) {}
27
28 pub fn resume(&mut self) {}
29 }
30
31 pub struct Sound {}
32
33 impl Sound {
34 pub fn load(_ctx: &mut AudioContext, _data: &[u8]) -> Sound {
35 Sound {}
36 }
37
38 pub fn is_loaded(&self) -> bool {
39 true
40 }
41
42 pub fn play(&mut self, _ctx: &mut AudioContext, _params: PlaySoundParams) {}
43
44 pub fn stop(&mut self, _ctx: &mut AudioContext) {}
45
46 pub fn set_volume(&mut self, _ctx: &mut AudioContext, _volume: f32) {}
47 }
48}
49
50#[cfg(any(not(feature = "audio"), target_os = "ios"))]
51use dummy_audio::{AudioContext as QuadSndContext, Sound as QuadSndSound};
52
53#[cfg(any(not(feature = "audio"), target_os = "ios"))]
54pub struct PlaySoundParams {
55 pub looped: bool,
56 pub volume: f32,
57}
58
59pub struct AudioContext {
60 native_ctx: QuadSndContext,
61 sounds: HashMap<usize, QuadSndSound>,
62 id: usize,
63}
64
65impl AudioContext {
66 pub fn new() -> AudioContext {
67 AudioContext {
68 native_ctx: QuadSndContext::new(),
69 sounds: HashMap::new(),
70 id: 0,
71 }
72 }
73
74 #[cfg(target_os = "android")]
75 pub fn pause(&mut self) {
76 self.native_ctx.pause()
77 }
78
79 #[cfg(target_os = "android")]
80 pub fn resume(&mut self) {
81 self.native_ctx.resume()
82 }
83}
84
85impl Default for AudioContext {
86 fn default() -> Self {
87 AudioContext::new()
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct Sound(usize);
93
94pub struct Source {
95 sound: Sound,
96 params: PlaySoundParams,
97}
98
99impl Source {
100 pub fn new(ctx: &mut Context, path: &str) -> GameResult<Source> {
104 use std::io::Read;
105
106 let mut file = filesystem::open(ctx, path)?;
107
108 let mut bytes = vec![];
109 file.bytes.read_to_end(&mut bytes)?;
110
111 Self::from_bytes(ctx, bytes.as_slice())
112 }
113
114 pub fn from_bytes(ctx: &mut Context, bytes: &[u8]) -> GameResult<Source> {
118 let sound = QuadSndSound::load(&mut ctx.audio_context.native_ctx, bytes);
119
120 #[cfg(target_arch = "wasm32")]
122 while sound.is_loaded() {
123 std::thread::yield_now();
124 }
125
126 let id = ctx.audio_context.id;
127 ctx.audio_context.sounds.insert(id, sound);
128 ctx.audio_context.id += 1;
129 Ok(Source {
130 sound: Sound(id),
131 params: PlaySoundParams::default(),
132 })
133 }
134
135 pub fn play(&self, ctx: &mut Context) -> GameResult<()> {
136 let ctx = &mut ctx.audio_context;
137 let sound = &mut ctx.sounds.get_mut(&self.sound.0).unwrap();
138
139 let params = PlaySoundParams {
140 looped: self.params.looped,
141 volume: self.params.volume,
142 };
143 sound.play(&mut ctx.native_ctx, params);
144 Ok(())
145 }
146
147 pub fn stop(&self, ctx: &mut Context) -> GameResult {
148 let ctx = &mut ctx.audio_context;
149 let sound = &mut ctx.sounds.get_mut(&self.sound.0).unwrap();
150
151 sound.stop(&mut ctx.native_ctx);
152 Ok(())
153 }
154
155 pub fn set_volume(&mut self, ctx: &mut Context, volume: f32) -> GameResult<()> {
156 let ctx = &mut ctx.audio_context;
157 self.params.volume = volume;
158 let sound = &mut ctx.sounds.get_mut(&self.sound.0).unwrap();
159
160 sound.set_volume(&mut ctx.native_ctx, volume);
161 Ok(())
162 }
163
164 pub fn volume(&self) -> f32 {
165 self.params.volume
166 }
167
168 pub fn set_repeat(&mut self, repeat: bool) {
169 self.params.looped = repeat;
170 }
171
172 pub fn repeat(&self) -> bool {
173 self.params.looped
174 }
175}