use std::rc::Rc;
use std::cell::RefCell;
use std::time::Duration;
use reverb_effect::ReverbEffect;
use internal::OpenAlData;
use sound_data;use sound_data::{SoundData};
use openal::{ffi, al};
use states::State;
use states::State::{Initial, Playing, Paused, Stopped};
use audio_controller::AudioController;
use audio_tags::{AudioTags, Tags};
pub struct Sound {
al_source: u32,
sound_data: Rc<RefCell<SoundData>>
}
impl Sound {
pub fn new(path: &str) -> Result<Sound, String> {
check_openal_context!(Err("Invalid OpenAL context.".into()));
let sound_data = match SoundData::new(path) {
Ok(data) => Rc::new(RefCell::new(data)),
Err(err) => {
return Err(format!("Error creating sound data: {}", err));
}
};
Sound::new_with_data(sound_data)
}
pub fn new_with_data(sound_data: Rc<RefCell<SoundData>>) -> Result<Sound, String> {
check_openal_context!(Err("Invalid OpenAL context.".into()));
let mut source_id = 0;
al::alGenSources(1, &mut source_id);
al::alSourcei(source_id,
ffi::AL_BUFFER,
sound_data::get_buffer(&*sound_data
.borrow_mut()) as i32);
if let Some(err) = al::openal_has_error() {
return Err(format!("Internal OpenAL error: {}", err));
};
Ok(Sound {
al_source: source_id,
sound_data: sound_data
})
}
pub fn get_datas(&self) -> Rc<RefCell<SoundData>> {
self.sound_data.clone()
}
pub fn set_datas(&mut self, sound_data: Rc<RefCell<SoundData>>) {
check_openal_context!(());
if self.is_playing() {
return;
}
al::alSourcei(self.al_source,
ffi::AL_BUFFER,
sound_data::get_buffer(&*sound_data
.borrow()) as i32);
self.sound_data = sound_data
}
pub fn set_air_absorption_factor(&mut self, factor: f32) {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_AIR_ABSORPTION_FACTOR, factor);
}
pub fn get_air_absorption_factor(&mut self) -> f32 {
check_openal_context!(0.);
let mut factor = 0.0;
al::alGetSourcef(self.al_source, ffi::AL_AIR_ABSORPTION_FACTOR, &mut factor);
factor
}
pub fn set_velocity(&mut self, velocity: [f32; 3]) -> () {
check_openal_context!(());
al::alSourcefv(self.al_source, ffi::AL_VELOCITY, &velocity[0]);
}
pub fn get_velocity(&self) -> [f32; 3] {
check_openal_context!([0.0; 3]);
let mut velocity : [f32; 3] = [0.0; 3];
al::alGetSourcefv(self.al_source, ffi::AL_VELOCITY, &mut velocity[0]);
velocity
}
}
impl AudioTags for Sound {
fn get_tags(&self) -> Tags {
(*self.sound_data).borrow().get_tags().clone()
}
}
impl AudioController for Sound {
fn play(&mut self) -> () {
check_openal_context!(());
al::alSourcePlay(self.al_source);
match al::openal_has_error() {
None => {},
Some(err) => println!("{}", err)
}
}
fn pause(&mut self) -> () {
check_openal_context!(());
al::alSourcePause(self.al_source)
}
fn stop(&mut self) -> () {
check_openal_context!(());
al::alSourceStop(self.al_source)
}
fn connect(&mut self, reverb_effect: &Option<ReverbEffect>) {
check_openal_context!(());
match reverb_effect {
Some(reverb_effect) => {
al::alSource3i(self.al_source, ffi::AL_AUXILIARY_SEND_FILTER, reverb_effect.slot() as i32, 0, ffi::AL_FILTER_NULL);
},
None => {
al::alSource3i(self.al_source, ffi::AL_AUXILIARY_SEND_FILTER, ffi::AL_EFFECTSLOT_NULL, 0, ffi::AL_FILTER_NULL);
}
}
}
fn is_playing(&self) -> bool {
match self.get_state() {
Playing => true,
_ => false
}
}
fn get_state(&self) -> State {
check_openal_context!(Initial);
let mut state : i32 = 0;
al::alGetSourcei(self.al_source, ffi::AL_SOURCE_STATE, &mut state);
match state {
ffi::AL_INITIAL => Initial,
ffi::AL_PLAYING => Playing,
ffi::AL_PAUSED => Paused,
ffi::AL_STOPPED => Stopped,
_ => panic!(format!("AL_SOURCE_STATE == {}", state))
}
}
fn set_volume(&mut self, volume: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_GAIN, volume);
}
fn get_volume(&self) -> f32 {
check_openal_context!(0.);
let mut volume : f32 = 0.;
al::alGetSourcef(self.al_source, ffi::AL_GAIN, &mut volume);
volume
}
fn set_min_volume(&mut self, min_volume: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_MIN_GAIN, min_volume);
}
fn get_min_volume(&self) -> f32 {
check_openal_context!(0.);
let mut volume : f32 = 0.;
al::alGetSourcef(self.al_source, ffi::AL_MIN_GAIN, &mut volume);
volume
}
fn set_max_volume(&mut self, max_volume: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_MAX_GAIN, max_volume);
}
fn get_max_volume(&self) -> f32 {
check_openal_context!(0.);
let mut volume : f32 = 0.;
al::alGetSourcef(self.al_source, ffi::AL_MAX_GAIN, &mut volume);
volume
}
fn set_looping(&mut self, looping: bool) -> () {
check_openal_context!(());
match looping {
true => al::alSourcei(self.al_source,
ffi::AL_LOOPING,
ffi::ALC_TRUE as i32),
false => al::alSourcei(self.al_source,
ffi::AL_LOOPING,
ffi::ALC_FALSE as i32)
};
}
fn is_looping(&self) -> bool {
check_openal_context!(false);
let mut boolean = 0;
al::alGetSourcei(self.al_source, ffi::AL_LOOPING, &mut boolean);
match boolean as _ {
ffi::ALC_TRUE => true,
ffi::ALC_FALSE => false,
_ => unreachable!()
}
}
fn set_pitch(&mut self, pitch: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_PITCH, pitch)
}
fn get_pitch(&self) -> f32 {
check_openal_context!(0.);
let mut pitch = 0.;
al::alGetSourcef(self.al_source, ffi::AL_PITCH, &mut pitch);
pitch
}
fn set_relative(&mut self, relative: bool) -> () {
check_openal_context!(());
match relative {
true => al::alSourcei(self.al_source,
ffi::AL_SOURCE_RELATIVE,
ffi::ALC_TRUE as i32),
false => al::alSourcei(self.al_source,
ffi::AL_SOURCE_RELATIVE,
ffi::ALC_FALSE as i32)
};
}
fn is_relative(&mut self) -> bool {
check_openal_context!(false);
let mut boolean = 0;
al::alGetSourcei(self.al_source, ffi::AL_SOURCE_RELATIVE, &mut boolean);
match boolean as _ {
ffi::ALC_TRUE => true,
ffi::ALC_FALSE => false,
_ => unreachable!()
}
}
fn set_position(&mut self, position: [f32; 3]) -> () {
check_openal_context!(());
al::alSourcefv(self.al_source, ffi::AL_POSITION, &position[0]);
}
fn get_position(&self) -> [f32; 3] {
check_openal_context!([0.; 3]);
let mut position : [f32; 3] = [0.; 3];
al::alGetSourcefv(self.al_source, ffi::AL_POSITION, &mut position[0]);
position
}
fn set_direction(&mut self, direction: [f32; 3]) -> () {
check_openal_context!(());
al::alSourcefv(self.al_source, ffi::AL_DIRECTION, &direction[0]);
}
fn get_direction(&self) -> [f32; 3] {
check_openal_context!([0.; 3]);
let mut direction : [f32; 3] = [0.; 3];
al::alGetSourcefv(self.al_source, ffi::AL_DIRECTION, &mut direction[0]);
direction
}
fn set_max_distance(&mut self, max_distance: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_MAX_DISTANCE, max_distance);
}
fn get_max_distance(&self) -> f32 {
check_openal_context!(0.);
let mut max_distance = 0.;
al::alGetSourcef(self.al_source,
ffi::AL_MAX_DISTANCE,
&mut max_distance);
max_distance
}
fn set_reference_distance(&mut self, ref_distance: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_REFERENCE_DISTANCE, ref_distance);
}
fn get_reference_distance(&self) -> f32 {
check_openal_context!(1.);
let mut ref_distance = 0.;
al::alGetSourcef(self.al_source,
ffi::AL_REFERENCE_DISTANCE,
&mut ref_distance);
ref_distance
}
fn set_attenuation(&mut self, attenuation: f32) -> () {
check_openal_context!(());
al::alSourcef(self.al_source, ffi::AL_ROLLOFF_FACTOR, attenuation);
}
fn get_attenuation(&self) -> f32 {
check_openal_context!(1.);
let mut attenuation = 0.;
al::alGetSourcef(self.al_source,
ffi::AL_ROLLOFF_FACTOR,
&mut attenuation);
attenuation
}
fn set_direct_channel(&mut self, enabled: bool) -> () {
if OpenAlData::direct_channel_capable() {
let value = match enabled {
true => ffi::AL_TRUE,
false => ffi::AL_FALSE,
};
al::alSourcei(self.al_source, ffi::AL_DIRECT_CHANNELS_SOFT, value as i32);
}
}
fn get_direct_channel(&self) -> bool {
match OpenAlData::direct_channel_capable() {
true => {
let mut boolean = 0;
al::alGetSourcei(self.al_source,
ffi::AL_DIRECT_CHANNELS_SOFT,
&mut boolean);
match boolean as _ {
ffi::ALC_TRUE => true,
ffi::ALC_FALSE => false,
_ => unreachable!()
}
},
false => false,
}
}
fn get_duration(&self) -> Duration {
let data = self.sound_data.borrow();
let snd_info = sound_data::get_sndinfo(&data);
let frames = snd_info.frames as u64;
let sample_rate = snd_info.samplerate as u64;
let seconds = frames / sample_rate;
let nanoseconds = frames % sample_rate * 1_000_000_000 / sample_rate;
Duration::new(seconds, nanoseconds as u32)
}
}
impl Drop for Sound {
fn drop(&mut self) -> () {
unsafe {
ffi::alDeleteSources(1, &mut self.al_source);
}
}
}
#[cfg(test)]
mod test {
#![allow(non_snake_case)]
use sound::Sound;
use states::State::{Playing, Paused, Stopped};
use audio_controller::AudioController;
#[test]
#[ignore]
fn sound_create_OK() -> () {
let snd = Sound::new("res/shot.wav");
assert!(snd.is_ok());
}
#[test]
#[ignore]
fn sound_create_FAIL() -> () {
let snd = Sound::new("toto.wav");
assert!(snd.is_err());
}
#[test]
#[ignore]
fn sound_play_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.play();
assert_eq!(snd.get_state() as i32, Playing as i32);
snd.stop();
}
#[test]
#[ignore]
fn sound_pause_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.play();
snd.pause();
assert_eq!(snd.get_state() as i32, Paused as i32);
snd.stop();
}
#[test]
#[ignore]
fn sound_stop_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.play();
snd.stop();
assert_eq!(snd.get_state() as i32, Stopped as i32);
snd.stop();
}
#[test]
#[ignore]
fn sound_is_playing_TRUE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.play();
assert_eq!(snd.is_playing(), true);
snd.stop();
}
#[test]
#[ignore]
fn sound_is_playing_FALSE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
assert_eq!(snd.is_playing(), false);
snd.stop();
}
#[test]
#[ignore]
fn sound_set_volume_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_volume(0.7);
assert_eq!(snd.get_volume(), 0.7);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_volume_low_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_volume(-1.);
assert_eq!(snd.get_volume(), -1.);
}
#[test]
#[ignore]
fn sound_set_min_volume_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_min_volume(0.1);
assert_eq!(snd.get_min_volume(), 0.1);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_min_volume_high_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_min_volume(10.9);
assert_eq!(snd.get_min_volume(), 10.9);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_min_volume_low_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_min_volume(-1.);
assert_eq!(snd.get_min_volume(), -1.);
}
#[test]
#[ignore]
fn sound_set_max_volume_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_max_volume(0.9);
assert_eq!(snd.get_max_volume(), 0.9);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_max_volume_high_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_max_volume(10.9);
assert_eq!(snd.get_max_volume(), 10.9);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_max_volume_low_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_max_volume(-1.);
assert_eq!(snd.get_max_volume(), -1.);
}
#[test]
#[ignore]
fn sound_is_looping_TRUE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_looping(true);
assert_eq!(snd.is_looping(), true);
}
#[test]
#[ignore]
fn sound_is_looping_FALSE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_looping(false);
assert_eq!(snd.is_looping(), false);
}
#[test]
#[ignore]
fn sound_set_pitch_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_pitch(1.5);
assert_eq!(snd.get_pitch(), 1.5);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_pitch_too_low_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_pitch(-1.);
assert_eq!(snd.get_pitch(), -1.);
}
#[test]
#[ignore]
fn sound_set_relative_TRUE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_relative(true);
assert_eq!(snd.is_relative(), true);
}
#[test]
#[ignore]
fn sound_set_relative_FALSE() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_relative(false);
assert_eq!(snd.is_relative(), false);
}
#[test]
#[ignore]
fn sound_set_position_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_position([50f32, 150f32, 250f32]);
let res = snd.get_position();
assert_eq!([res[0], res[1], res[2]], [50f32, 150f32, 250f32]);
}
#[test]
#[ignore]
fn sound_set_direction_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_direction([50f32, 150f32, 250f32]);
let res = snd.get_direction();
assert_eq!([res[0], res[1], res[2]], [50f32, 150f32, 250f32]);
}
#[test]
#[ignore]
fn sound_set_max_distance_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_max_distance(70.);
assert_eq!(snd.get_max_distance(), 70.);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_max_distance_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_max_distance(-1.);
assert_eq!(snd.get_max_distance(), -1.);
}
#[test]
#[ignore]
fn sound_set_reference_distance_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_reference_distance(70.);
assert_eq!(snd.get_reference_distance(), 70.);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_reference_distance_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_reference_distance(-1.);
assert_eq!(snd.get_reference_distance(), -1.);
}
#[test]
#[ignore]
fn sound_set_attenuation_OK() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_attenuation(0.5f32);
assert_eq!(snd.get_attenuation(), 0.5f32);
}
#[test]
#[ignore]
#[should_panic]
fn sound_set_attenuation_FAIL() -> () {
let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound");
snd.set_attenuation(-1.);
assert_eq!(snd.get_attenuation(), -1.);
}
}