use std::thread::sleep;
use std::mem;
use std::thread;
use std::time::Duration;
use libc::c_void;
use std::vec::Vec;
use std::sync::mpsc::{channel, Sender, Receiver};
use reverb_effect::ReverbEffect;
use internal::OpenAlData;
use openal::{ffi, al};
use sndfile::{SndInfo, SndFile};
use sndfile::OpenMode::Read;
use sndfile::SeekMode::SeekSet;
use states::State;
use states::State::{Initial, Playing, Paused, Stopped};
use audio_controller::AudioController;
use audio_tags::{Tags, AudioTags, get_sound_tags};
pub struct Music {
al_source: u32,
al_buffers: [u32; 2],
file: Option<Box<SndFile>>,
file_infos: SndInfo,
sample_to_read: i32,
sample_format: i32,
sound_tags: Tags,
is_looping: bool,
looping_sender: Option<Sender<bool>>,
thread_handle: Option<thread::JoinHandle<()>>,
}
impl Music {
pub fn new(path: &str) -> Result<Music, String> {
check_openal_context!(Err("Invalid OpenAL context.".into()));
let file = match SndFile::new(path, Read) {
Ok(file) => Box::new(file),
Err(err) => {
return Err(format!("Error while loading music file: {}", err));
}
};
let infos = file.get_sndinfo();
let mut source_id = 0;
let mut buffer_ids = [0; 2];
al::alGenSources(1, &mut source_id);
al::alGenBuffers(2, &mut buffer_ids[0]);
let format = match al::get_channels_format(infos.channels) {
Some(fmt) => fmt,
None => {
return Err("Unrecognized music format.".into());
}
};
if let Some(err) = al::openal_has_error() {
return Err(format!("Internal OpenAL error: {}", err));
};
let sound_tags = get_sound_tags(&*file);
Ok(Music {
al_source: source_id,
al_buffers: buffer_ids,
file: Some(file),
file_infos: infos,
sample_to_read: 50000,
sample_format: format,
sound_tags: sound_tags,
is_looping: false,
looping_sender: None,
thread_handle: None,
})
}
fn process_music(&mut self) -> () {
let (chan, port) = channel();
let sample_t_r = self.sample_to_read;
let sample_rate = self.file_infos.samplerate;
let sample_format = self.sample_format;
let al_source = self.al_source;
let al_buffers = self.al_buffers;
let mut samples = vec![0i16; sample_t_r as usize];
let mut len = mem::size_of::<i16>() *
self.file.as_mut().unwrap().read_i16(&mut samples[..], sample_t_r as i64) as usize;
al::alBufferData(al_buffers[0],
sample_format,
samples.as_ptr() as *mut c_void,
len as i32,
sample_rate);
samples.clear();
len = mem::size_of::<i16>() *
self.file.as_mut().unwrap().read_i16(&mut samples[..], sample_t_r as i64) as usize;
al::alBufferData(al_buffers[1],
sample_format,
samples.as_ptr() as *mut c_void,
len as i32,
sample_rate);
al::alSourceQueueBuffers(al_source, 2, &al_buffers[0]);
al::alSourcePlay(al_source);
let (looping_sender, looping_receiver): (Sender<bool>, Receiver<bool>) = channel();
self.looping_sender = Some(looping_sender);
let is_looping_clone = self.is_looping.clone();
self.thread_handle = Some(thread::spawn(move|| {
match OpenAlData::check_al_context() {
Ok(_) => {},
Err(err) => { println!("{}", err);}
};
let mut file : SndFile = port.recv().ok().unwrap();
let mut samples = vec![0i16; sample_t_r as usize];
let mut status = ffi::AL_PLAYING;
let mut buffers_processed = 0;
let mut buf = 0;
let mut is_looping = is_looping_clone;
while status != ffi::AL_STOPPED {
sleep(Duration::from_millis(50));
if status == ffi::AL_PLAYING {
if let Ok(new_is_looping) = looping_receiver.try_recv() {
is_looping = new_is_looping;
}
al::alGetSourcei(al_source,
ffi::AL_BUFFERS_PROCESSED,
&mut buffers_processed);
if buffers_processed != 0 {
al::alSourceUnqueueBuffers(al_source, 1, &mut buf);
let read = file.read_i16(&mut samples[..], sample_t_r as i64);
if is_looping && read < sample_t_r as i64 {
let additional_read = sample_t_r as i64 - read;
file.seek(0, SeekSet);
file.read_i16(&mut samples[read as usize..], additional_read);
} else if read == 0 {
samples.clear();
}
al::alBufferData(buf,
sample_format,
samples.as_ptr() as *mut c_void,
samples.len() as i32 * mem::size_of::<i16>() as i32,
sample_rate);
al::alSourceQueueBuffers(al_source, 1, &buf);
}
}
status = al::alGetState(al_source);
}
al::alSourcei(al_source, ffi::AL_BUFFER, 0);
}));
let file = self.file.as_ref().unwrap().clone();
chan.send(*file);
}
}
impl AudioTags for Music {
fn get_tags(&self) -> Tags {
self.sound_tags.clone()
}
}
impl AudioController for Music {
fn play(&mut self) -> () {
check_openal_context!(());
match self.get_state() {
Paused => { al::alSourcePlay(self.al_source); return; },
_ => {
if self.is_playing() {
al::alSourceStop(self.al_source);
sleep(Duration::from_millis(50));
}
self.file.as_mut().unwrap().seek(0, SeekSet);
self.process_music();
}
}
}
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 state = al::alGetState(self.al_source);
match state {
ffi::AL_INITIAL => Initial,
ffi::AL_PLAYING => Playing,
ffi::AL_PAUSED => Paused,
ffi::AL_STOPPED => Stopped,
_ => unreachable!()
}
}
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) -> () {
if let Some(ref sender) = self.looping_sender {
sender.send(looping);
}
self.is_looping = looping;
}
fn is_looping(&self) -> bool {
self.is_looping
}
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 frames = self.file_infos.frames as u64;
let sample_rate = self.file_infos.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 Music {
fn drop(&mut self) -> () {
self.stop();
if let Some(handle) = self.thread_handle.take() {
handle.join();
}
unsafe {
al::alSourcei(self.al_source, ffi::AL_BUFFER, 0);
ffi::alDeleteBuffers(2, &mut self.al_buffers[0]);
ffi::alDeleteSources(1, &mut self.al_source);
}
}
}
#[cfg(test)]
mod test {
#![allow(non_snake_case)]
use music::Music;
use states::State::{Playing, Paused, Stopped};
use audio_controller::AudioController;
#[test]
#[ignore]
fn music_create_OK() -> () {
let msc = Music::new("res/shot.wav");
assert!(msc.is_ok());
}
#[test]
fn music_create_FAIL() -> () {
let msc = Music::new("toto.wav");
assert!(msc.is_err());
}
#[test]
#[ignore]
fn music_play_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.play();
assert_eq!(msc.get_state() as i32, Playing as i32);
msc.stop();
}
#[test]
#[ignore]
fn music_pause_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.play();
msc.pause();
assert_eq!(msc.get_state() as i32, Paused as i32);
msc.stop();
}
#[test]
#[ignore]
fn music_stop_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.play();
msc.stop();
assert_eq!(msc.get_state() as i32, Stopped as i32);
msc.stop();
}
#[test]
#[ignore]
fn music_is_playing_TRUE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.play();
assert_eq!(msc.is_playing(), true);
msc.stop();
}
#[test]
#[ignore]
fn music_is_playing_FALSE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
assert_eq!(msc.is_playing(), false);
msc.stop();
}
#[test]
#[ignore]
fn music_set_volume_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_volume(0.7);
assert_eq!(msc.get_volume(), 0.7);
}
#[test]
#[ignore]
fn music_set_min_volume_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_min_volume(0.1);
assert_eq!(msc.get_min_volume(), 0.1);
}
#[test]
#[ignore]
fn music_set_max_volume_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_max_volume(0.9);
assert_eq!(msc.get_max_volume(), 0.9);
}
#[test]
#[ignore]
fn music_is_looping_TRUE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_looping(true);
assert_eq!(msc.is_looping(), true);
}
#[test]
#[ignore]
fn music_is_looping_FALSE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_looping(false);
assert_eq!(msc.is_looping(), false);
}
#[test]
#[ignore]
fn music_set_pitch_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_pitch(1.5);
assert_eq!(msc.get_pitch(), 1.5);
}
#[test]
#[ignore]
fn music_set_relative_TRUE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_relative(true);
assert_eq!(msc.is_relative(), true);
}
#[test]
#[ignore]
fn music_set_relative_FALSE() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_relative(false);
assert_eq!(msc.is_relative(), false);
}
#[test]
#[ignore]
fn music_set_position_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_position([50., 150., 250.]);
let res = msc.get_position();
assert_eq!([res[0], res[1], res[2]], [50f32, 150f32, 250f32]);
}
#[test]
#[ignore]
fn music_set_direction_OK() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_direction([50., 150., 250.]);
let res = msc.get_direction();
assert_eq!([res[0], res[1], res[2]], [50f32, 150f32, 250f32]);
}
#[test]
#[ignore]
fn music_set_max_distance() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_max_distance(70.);
assert_eq!(msc.get_max_distance(), 70.);
}
#[test]
#[ignore]
fn music_set_reference_distance() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_reference_distance(70.);
assert_eq!(msc.get_reference_distance(), 70.);
}
#[test]
#[ignore]
fn music_set_attenuation() -> () {
let mut msc = Music::new("res/shot.wav").expect("Cannot create Music");
msc.set_attenuation(0.5f32);
println!("{}", &msc.get_attenuation());
assert_eq!(&msc.get_attenuation(), &0.5f32);
}
}