dbsdk_rs/
sounddriver.rs

1use std::{convert::TryInto, mem::{size_of, transmute}, cell::RefCell, sync::{Weak, RwLock}, sync::Arc, io::{Read, Seek}};
2
3use byteorder::{LittleEndian, ReadBytesExt};
4
5use crate::{audio::{VOICE_COUNT, AudioSample, get_voice_state, queue_stop_voice, get_time, queue_start_voice, queue_set_voice_param_f, AudioVoiceParam, queue_set_voice_param_i}, math::{Vector3, Quaternion}, io::FileStream};
6
7#[derive(Clone, Copy)]
8pub enum AttenuationType {
9    None,
10    InverseDistance,
11    Linear,
12    ExponentialDistance,
13}
14
15#[derive(Clone, Copy)]
16struct SoundVoice {
17    slot: i32,
18    priority: u8,
19    _is_playing: bool,
20    id: u32,
21    play_time: f64,
22}
23
24pub struct SoundEmitter {
25    pub is_valid: bool,
26    pub priority: u8,
27    pub looping: bool,
28    pub reverb: bool,
29    pub is_3d: bool,
30    pub atten_type: AttenuationType,
31    pub atten_min_dist: f32,
32    pub atten_max_dist: f32,
33    pub atten_rolloff: f32,
34    pub position: Vector3,
35    pub volume: f32,
36    pub pitch: f32,
37    pub pan: f32,
38    sample: Arc<AudioSample>,
39    id: u32,
40    voice: Option<i32>,
41}
42
43struct WavHeader {
44    riff: [u8;4],
45    _overall_size: u32,
46    wave: [u8;4],
47}
48
49impl WavHeader {
50    pub fn read(fs: &mut FileStream) -> WavHeader {
51        let mut riff: [u8;4] = [0;4];
52        fs.read_exact(&mut riff).expect("Failed reading header");
53        let overall_size = fs.read_u32::<LittleEndian>().expect("Failed reading header");
54        let mut wave: [u8;4] = [0;4];
55        fs.read_exact(&mut wave).expect("Failed reading header");
56
57        return WavHeader { riff: riff, _overall_size: overall_size, wave: wave };
58    }
59}
60
61struct WavHeaderFormat {
62    fmt_chunk_marker: [u8;4],
63    length_of_fmt: u32,
64    format_type: u16,
65    channels: u16,
66    samplerate: u32,
67    _byterate: u32,
68    block_align: u16,
69    bits_per_sample: u16,
70}
71
72impl WavHeaderFormat {
73    pub fn read(fs: &mut FileStream) -> WavHeaderFormat {
74        let mut fmt_chunk_marker: [u8;4] = [0;4];
75        fs.read_exact(&mut fmt_chunk_marker).expect("Failed reading header");
76        let length_of_fmt = fs.read_u32::<LittleEndian>().expect("Failed reading header");
77        let format_type = fs.read_u16::<LittleEndian>().expect("Failed reading header");
78        let channels = fs.read_u16::<LittleEndian>().expect("Failed reading header");
79        let samplerate = fs.read_u32::<LittleEndian>().expect("Failed reading header");
80        let byterate = fs.read_u32::<LittleEndian>().expect("Failed reading header");
81        let block_align = fs.read_u16::<LittleEndian>().expect("Failed reading header");
82        let bits_per_sample = fs.read_u16::<LittleEndian>().expect("Failed reading header");
83
84        return WavHeaderFormat {
85            fmt_chunk_marker: fmt_chunk_marker,
86            length_of_fmt: length_of_fmt,
87            format_type: format_type,
88            channels: channels,
89            samplerate: samplerate,
90            _byterate: byterate,
91            block_align: block_align,
92            bits_per_sample: bits_per_sample,
93        };
94    }
95}
96
97struct WavChunkHeader {
98    id: [u8;4],
99    chunk_size: u32,
100}
101
102impl WavChunkHeader {
103    pub fn read(fs: &mut FileStream) -> WavChunkHeader {
104        let mut id: [u8;4] = [0;4];
105        fs.read_exact(&mut id).expect("Failed reading header");
106        let chunk_size = fs.read_u32::<LittleEndian>().expect("Failed reading header");
107
108        return WavChunkHeader {
109            id: id,
110            chunk_size: chunk_size
111        };
112    }
113}
114
115pub struct SoundDriver {
116    max_voices: usize,
117    voices: [SoundVoice;VOICE_COUNT],
118    emitters: Vec<Arc<RwLock<SoundEmitter>>>,
119    listener_position: Vector3,
120    listener_orientation: Quaternion,
121    search_offset: usize,
122}
123
124impl SoundDriver {
125    /// Construct a new instance of the SoundDriver
126    pub fn new(max_voices: usize) -> SoundDriver {
127        assert!(max_voices <= VOICE_COUNT, "Cannot init sound driver with more than {} voices", VOICE_COUNT);
128
129        let mut driver = SoundDriver {
130            max_voices: max_voices,
131            voices: [
132                SoundVoice {
133                    slot: 0,
134                    priority: 255,
135                    _is_playing: false,
136                    id: 0,
137                    play_time: 0.0
138                }; VOICE_COUNT
139            ],
140            emitters: Vec::new(),
141            listener_position: Vector3::zero(),
142            listener_orientation: Quaternion::identity(),
143            search_offset: 0,
144        };
145
146        for i in 0..VOICE_COUNT {
147            driver.voices[i].slot = i.try_into().unwrap();
148        }
149
150        return driver;
151    }
152
153    fn allocate_voice(voices: &mut [SoundVoice], search_offset: &mut usize, max_voice: usize, priority: u8) -> Option<usize> {
154        // a little silly but:
155        // voice stealing scheme can sometimes steal voices too early because we have to schedule playback in advance
156        // a simple round-robin search offset helps alleviate this
157
158        let mut ret: Option<usize> = None;
159
160        for i in 0..max_voice {
161            let idx = (i + *search_offset) % max_voice;
162            let voice = &voices[idx];
163
164            if !voice._is_playing && !get_voice_state(voice.slot) {
165                ret = Some(idx);
166                break;
167            } else {
168                match ret {
169                    Some(r) => {
170                        let rv = &voices[r];
171                        if voice.play_time < rv.play_time && voice.priority >= priority {
172                            ret = Some(idx);
173                        }
174                    },
175                    None => {
176                        ret = Some(idx);
177                    }
178                };
179            }
180        }
181
182        *search_offset = (*search_offset + 1) % max_voice;
183
184        match ret {
185            Some(r) => {
186                let rv = &mut voices[r];
187                rv.priority = priority;
188            },
189            None => {
190            }
191        }
192
193        return ret;
194    }
195   
196    fn assign_hw_voice(listener_position: &Vector3, listener_orientation: &Quaternion, voices: &mut [SoundVoice], search_offset: &mut usize, max_voice: usize, emitter: &mut SoundEmitter) {
197        let voice = SoundDriver::allocate_voice(voices, search_offset, max_voice, emitter.priority);
198
199        if voice.is_some() {
200            let idx = voice.unwrap();
201            let t = get_time();
202            voices[idx].play_time = t;
203            voices[idx].id += 1;
204            emitter.voice = Some(idx.try_into().unwrap());
205            emitter.id = voices[idx].id;
206
207            SoundDriver::update_voice(listener_position, listener_orientation, &voices[idx], emitter);
208            queue_start_voice(idx.try_into().unwrap(), t);
209        }
210    }
211
212    fn calc_3d(listener_position: &Vector3, listener_orientation: &Quaternion, position: &Vector3, atten_type: AttenuationType, atten_min_dist: f32, atten_max_dist: f32, atten_rolloff: f32) -> (f32, f32) {
213        // calculate gain from distance
214        let dist = Vector3::distance(position, listener_position).clamp(atten_min_dist, atten_max_dist);
215        let gain = match atten_type {
216            AttenuationType::Linear => {
217                1.0 - atten_rolloff * (dist - atten_min_dist) / (atten_max_dist - atten_min_dist)
218            }
219            AttenuationType::InverseDistance => {
220                atten_min_dist / (atten_min_dist + atten_rolloff * (dist - atten_min_dist))
221            }
222            AttenuationType::ExponentialDistance => {
223                (dist / atten_min_dist).powf(-atten_rolloff)
224            }
225            AttenuationType::None => {
226                1.0
227            }
228        };
229
230        // calculate pan
231        let mut local_pos = *position - *listener_position;
232        let mut rot = *listener_orientation;
233        rot.invert();
234        local_pos = rot * local_pos;
235        local_pos.normalize();
236
237        let pan = local_pos.x;
238
239        return (gain, pan);
240    }
241
242    fn update_voice(listener_position: &Vector3, listener_orientation: &Quaternion, voice: &SoundVoice, emitter: &mut SoundEmitter) {
243        if emitter.id == voice.id {
244            let t = get_time();
245            let mut gain = emitter.volume;
246            let mut pan = emitter.pan;
247
248            if emitter.is_3d {
249                let (gain3d, pan3d) = SoundDriver::calc_3d(listener_position, listener_orientation, &emitter.position, 
250                    emitter.atten_type, emitter.atten_min_dist, emitter.atten_max_dist, emitter.atten_rolloff);
251
252                gain *= gain3d;
253                pan = pan3d;
254            }
255
256            let voice_slot = TryInto::<i32>::try_into(voice.slot).unwrap();
257            queue_set_voice_param_i(voice_slot, AudioVoiceParam::SampleData, emitter.sample.handle, t);
258            queue_set_voice_param_i(voice_slot, AudioVoiceParam::Samplerate, emitter.sample.samplerate, t);
259            queue_set_voice_param_i(voice_slot, AudioVoiceParam::LoopEnabled, if emitter.looping { 1 } else { 0 }, t);
260            queue_set_voice_param_i(voice_slot, AudioVoiceParam::LoopStart, 0, t);
261            queue_set_voice_param_i(voice_slot, AudioVoiceParam::LoopEnd, 0, t);
262            queue_set_voice_param_i(voice_slot, AudioVoiceParam::Reverb, if emitter.reverb { 1 } else { 0 }, t);
263            queue_set_voice_param_f(voice_slot, AudioVoiceParam::Volume, gain, t);
264            queue_set_voice_param_f(voice_slot, AudioVoiceParam::Detune, 0.0, t);
265            queue_set_voice_param_f(voice_slot, AudioVoiceParam::Pitch, emitter.pitch, t);
266            queue_set_voice_param_f(voice_slot, AudioVoiceParam::Pan, pan, t);
267            queue_set_voice_param_f(voice_slot, AudioVoiceParam::FadeInDuration, 0.0, t);
268            queue_set_voice_param_f(voice_slot, AudioVoiceParam::FadeOutDuration, 0.0, t);
269        } else {
270            // something may have stolen this emitter's voice
271            emitter.voice = None;
272        }
273    }
274
275    /// Update internal sound logic
276    pub fn update(&mut self) {
277        let emitters = self.emitters.as_mut_slice();
278        for emitter_rc in emitters {
279            let voice = {
280                emitter_rc.read().unwrap().voice
281            };
282            {
283                let mut emref = emitter_rc.write().unwrap();
284                match voice {
285                    Some(v) => {
286                        SoundDriver::update_voice(&self.listener_position, &self.listener_orientation, &self.voices[TryInto::<usize>::try_into(v).unwrap()], &mut emref);
287                    },
288                    None => {
289                        if emref.looping {
290                            SoundDriver::assign_hw_voice(&self.listener_position, &self.listener_orientation, &mut self.voices, &mut self.search_offset, self.max_voices, &mut emref);
291                        }
292                    }
293                }
294            }
295            {
296                let mut emref = emitter_rc.write().unwrap();
297                // for non-looping sounds: if the voice stops playing, or the sound's voice has been stolen, just stop emitter and remove from list
298                if !emref.looping && (!voice.is_some() || !get_voice_state(voice.unwrap().try_into().unwrap())) {
299                    emref.is_valid = false;
300                }
301            }
302        }
303
304        // remove any emitters which are no longer valid
305        self.emitters.retain(|x| {
306            x.read().unwrap().is_valid
307        });
308    }
309
310    /// Set the listener position & orientation
311    pub fn set_listener(&mut self, position: Vector3, orientation: Quaternion) {
312        self.listener_position = position;
313        self.listener_orientation = orientation;
314    }
315
316    /// Start playing a sound effect and return a handle to it
317    pub fn play(&mut self, priority: u8, sample: &Arc<AudioSample>, looping: bool, reverb: bool, volume: f32, pitch: f32, pan: f32) -> Weak<RwLock<SoundEmitter>> {
318        let mut emitter = SoundEmitter {
319            is_valid: true,
320            priority: priority,
321            looping: looping,
322            reverb: reverb,
323            is_3d: false,
324            atten_type: AttenuationType::None,
325            atten_min_dist: 0.0,
326            atten_max_dist: 0.0,
327            atten_rolloff: 0.0,
328            position: Vector3::zero(),
329            volume: volume,
330            pitch: pitch,
331            pan: pan,
332            sample: sample.clone(),
333            id: 0,
334            voice: None
335        };
336        SoundDriver::assign_hw_voice(&self.listener_position, &self.listener_orientation, &mut self.voices, &mut self.search_offset, self.max_voices, &mut emitter);
337        
338        let rc = Arc::new(RwLock::new(emitter));
339        let wr = Arc::downgrade(&rc);
340        self.emitters.push(rc);
341
342        return wr;
343    }
344
345    /// Start playing a 3D sound effect and return a handle to it
346    pub fn play_3d(&mut self, priority: u8, sample: &Arc<AudioSample>, looping: bool, reverb: bool, volume: f32, pitch: f32,
347        position: Vector3, atten_type: AttenuationType, atten_min_dist: f32, atten_max_dist: f32, atten_rolloff: f32) -> Weak<RwLock<SoundEmitter>> {
348        let mut emitter = SoundEmitter {
349            is_valid: true,
350            priority: priority,
351            looping: looping,
352            reverb: reverb,
353            is_3d: true,
354            atten_type: atten_type,
355            atten_min_dist: atten_min_dist,
356            atten_max_dist: atten_max_dist,
357            atten_rolloff: atten_rolloff,
358            position: position,
359            volume: volume,
360            pitch: pitch,
361            pan: 0.0,
362            sample: sample.clone(),
363            id: 0,
364            voice: None
365        };
366        SoundDriver::assign_hw_voice(&self.listener_position, &self.listener_orientation, &mut self.voices, &mut self.search_offset, self.max_voices, &mut emitter);
367        
368        let rc = Arc::new(RwLock::new(emitter));
369        let wr = Arc::downgrade(&rc);
370        self.emitters.push(rc);
371
372        return wr;
373    }
374
375    /// Stop the playing emitter
376    pub fn stop(&mut self, emitter_ref: Weak<RefCell<SoundEmitter>>) {
377        let rc = emitter_ref.upgrade();
378        if !rc.is_some() {
379            return;
380        }
381
382        let em = rc.unwrap();
383        let mut emitter = em.borrow_mut();
384
385        if !emitter.is_valid { return; }
386
387        if emitter.voice.is_some() {
388            let voiceid = TryInto::<usize>::try_into(emitter.voice.unwrap()).unwrap();
389            let voice: &mut SoundVoice = &mut self.voices[voiceid];
390            if voice.id == emitter.id {
391                voice.priority = 255;
392                queue_stop_voice(voice.slot.try_into().unwrap(), 0.0);
393            }
394        }
395
396        emitter.is_valid = false;
397    }
398}
399
400/// Load a wav file, returning an audio sample handle (supported encodings are unsigned 8-bit, signed 16-bit, and IMA ADPCM)
401pub fn load_wav(file: &mut FileStream) -> Result<AudioSample,()> {
402    let header = WavHeader::read(file);
403
404    // check riff string
405    let riff = match std::str::from_utf8(&header.riff) {
406        Ok(v) => { v },
407        Err(_) => { return Err(()); }
408    };
409
410    if riff != "RIFF" {
411        return Err(());
412    }
413
414    // check wav string
415    let wave = match std::str::from_utf8(&header.wave) {
416        Ok(v) => { v },
417        Err(_) => { return Err(()); }
418    };
419
420    if wave != "WAVE" {
421        return Err(());
422    }
423
424    let fmt_header = WavHeaderFormat::read(file);
425
426    // check fmt string
427
428    let fmt_str = match std::str::from_utf8(&fmt_header.fmt_chunk_marker) {
429        Ok(v) => { v },
430        Err(_) => { return Err(()); }
431    };
432
433    if fmt_str != "fmt " {
434        return Err(());
435    }
436
437    if fmt_header.channels != 1 {
438        return Err(());
439    }
440
441    // skip over header data
442    let fmt_header_size: usize = fmt_header.length_of_fmt.try_into().unwrap();
443    let header_size: usize = size_of::<WavHeader>() + fmt_header_size + 8;
444
445    match file.seek(std::io::SeekFrom::Start(header_size.try_into().unwrap())) {
446        Ok(_) => {  },
447        Err(_) => { return Err(()); }
448    }
449
450    let mut data_found = false;
451    let mut chunk_header: WavChunkHeader = WavChunkHeader { id: [0;4], chunk_size: 0 };
452
453    while !file.end_of_file() {
454        chunk_header = WavChunkHeader::read(file);
455
456        let chunk_id = match std::str::from_utf8(&chunk_header.id) {
457            Ok(v) => { v },
458            Err(_) => { return Err(()); }
459        };
460
461        if chunk_id == "data" {
462            data_found = true;
463            break;
464        } else {
465            // skip chunk data
466            if file.seek(std::io::SeekFrom::Current(chunk_header.chunk_size.try_into().unwrap())).is_err() {
467                return Err(());
468            }
469        }
470    }
471
472    if !data_found {
473        return Err(());
474    }
475
476    if fmt_header.format_type == 1 && fmt_header.bits_per_sample == 8 {
477        // unsigned 8-bit PCM
478        let mut pcm8: Vec<u8> = vec![0;chunk_header.chunk_size.try_into().unwrap()];
479        match file.read(pcm8.as_mut_slice()) {
480            Ok(_) => {},
481            Err(_) => { return Err(()); }
482        };
483
484        // convert from unsigned 0 .. 255 to signed -128 .. 127
485        for i in 0..pcm8.len() {
486            pcm8[i] = pcm8[i].wrapping_sub(128);
487        }
488
489        let sample_handle = unsafe { AudioSample::create_s8(transmute(pcm8.as_slice()), 
490            fmt_header.samplerate.try_into().unwrap())? };
491
492        return Ok(sample_handle);
493    } else if fmt_header.format_type == 1 && fmt_header.bits_per_sample == 16 {
494        // signed 16-bit PCM
495        let mut pcm16: Vec<u8> = vec![0, chunk_header.chunk_size.try_into().unwrap()];
496        match file.read(pcm16.as_mut_slice()) {
497            Ok(_) => {},
498            Err(_) => { return Err(()); }
499        };
500
501        let sample_handle = unsafe { AudioSample::create_s16(transmute(pcm16.as_slice()), 
502            fmt_header.samplerate.try_into().unwrap())? };
503        
504        return Ok(sample_handle);
505    } else if fmt_header.format_type == 0x11 {
506        // IMA ADPCM
507        let mut adpcm: Vec<u8> = vec![0, chunk_header.chunk_size.try_into().unwrap()];
508        match file.read(adpcm.as_mut_slice()) {
509            Ok(_) => {},
510            Err(_) => { return Err(()); }
511        }
512
513        let sample_handle = AudioSample::create_adpcm(adpcm.as_slice(), 
514            fmt_header.block_align.try_into().unwrap(), 
515            fmt_header.samplerate.try_into().unwrap())?;
516
517        return Ok(sample_handle);
518    }
519
520    return Err(());
521}