use hound;
use lewton;
use alto::{Alto, Context, DeviceObject, OutputDevice, Buffer, StaticSource, SourceState, Mono, Stereo};
use alto::Source as AltoSource;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::Arc;
use bit_set::BitSet;
use keeshond_datapack::{ReadSeek, DataId, DataStore, DataObject, DataError, DataPreparer, PreparedStore, PreparedStoreError, DataMultistore};
use keeshond_datapack::source::Source;
const NUM_SOURCES : usize = 32;
#[derive(Debug, Fail)]
pub enum AudioError
{
#[fail(display = "OpenAL failed: {}", _0)]
AlError(String),
#[fail(display = "Failed to load resource: {}", _0)]
LoadResourceFailed(String)
}
pub struct Sound
{
frequency : u32,
channels : u32,
samples : Vec<i16>
}
impl Sound
{
fn from_wav(reader: Box<dyn ReadSeek>) -> Result<Self, DataError>
{
let mut wav_reader = try_or_else!(hound::WavReader::new(reader),
|error| { Err(DataError::BadData(format!("{}", error))) });
let frequency = wav_reader.spec().sample_rate;
let channels = wav_reader.spec().channels as u32;
let mut samples = Vec::new();
let mut sample_iter = wav_reader.samples::<i16>();
loop
{
if let Some(packet_res) = sample_iter.next()
{
match packet_res
{
Ok(packet) =>
{
samples.push(packet);
},
Err(error) =>
{
return Err(DataError::BadData(format!("{}", error)));
}
}
}
else
{
break;
}
}
Ok(Sound
{
frequency,
channels,
samples
})
}
fn from_ogg(reader: Box<dyn ReadSeek>) -> Result<Self, DataError>
{
let mut ogg_reader = try_or_else!(lewton::inside_ogg::OggStreamReader::new(reader),
|error| { Err(DataError::BadData(format!("{}", error))) });
let frequency = ogg_reader.ident_hdr.audio_sample_rate;
let channels = ogg_reader.ident_hdr.audio_channels as u32;
let mut samples = Vec::new();
loop
{
let packet_opt = try_or_else!(ogg_reader.read_dec_packet_itl(),
|error| { Err(DataError::BadData(format!("{}", error))) });
if let Some(mut packet) = packet_opt
{
samples.append(&mut packet);
}
else
{
break;
}
}
Ok(Sound
{
frequency,
channels,
samples
})
}
}
impl DataObject for Sound
{
fn folder_name() -> &'static str where Self : Sized
{
"sounds"
}
fn want_file(pathname : &str) -> bool where Self : Sized
{
pathname.ends_with(".wav") || pathname.ends_with(".ogg")
}
fn from_io(reader: Box<dyn ReadSeek>, full_pathname : &str, _source : &mut Box<dyn Source>) -> Result<Self, DataError> where Self : Sized
{
if full_pathname.ends_with(".wav")
{
return Sound::from_wav(reader);
}
else if full_pathname.ends_with(".ogg")
{
return Sound::from_ogg(reader);
}
Err(DataError::BadData("Unknown audio format".into()))
}
}
struct AlSoundHandle
{
buffer : Arc<Buffer>
}
struct AlSoundPreparer
{
context : Rc<RefCell<Context>>
}
impl AlSoundPreparer
{
fn new(context : Rc<RefCell<Context>>) -> AlSoundPreparer
{
AlSoundPreparer
{
context
}
}
}
impl DataPreparer<Sound, AlSoundHandle> for AlSoundPreparer
{
fn prepare(&mut self, data : &mut Sound, _id : DataId) -> AlSoundHandle
{
let context_ref : &Context = &self.context.borrow();
let buffer_raw;
match data.channels
{
1 =>
{
buffer_raw = context_ref.new_buffer::<Mono<i16>, _>(&data.samples, data.frequency as i32).expect("OpenAL buffer creation failed");
},
2 =>
{
buffer_raw = context_ref.new_buffer::<Stereo<i16>, _>(&data.samples, data.frequency as i32).expect("OpenAL buffer creation failed");
},
_ =>
{
panic!("OpenAL sound with unexpected number of channels");
}
}
let buffer = Arc::new(buffer_raw);
data.samples = Vec::new();
AlSoundHandle
{
buffer
}
}
fn unprepare(&mut self, _prepared : &mut AlSoundHandle, _id : DataId)
{
}
}
pub trait Audio
{
fn unit_scale(&self) -> f32;
fn set_unit_scale(&mut self, scale : f32);
fn listener_position(&self) -> (f32, f32);
fn set_listener_position(&mut self, x : f32, y : f32);
fn play_sound(&mut self, sound_id : DataId) -> Option<Voice>;
fn play_sound_with(&mut self, sound_id : DataId, voice_info : &VoiceInfo) -> Option<Voice>;
fn stop_voice(&mut self, voice : &Voice) -> bool;
fn voice(&self, voice : &Voice) -> Option<VoiceInfo>;
fn set_voice(&mut self, voice : &Voice, voice_info : &VoiceInfo) -> bool;
fn max_voices(&self) -> usize;
fn sync_sound_store(&mut self, sound_store : &mut DataStore<Sound>) -> Result<(), PreparedStoreError>;
}
#[derive(Debug)]
pub struct Voice
{
pub slot : usize,
pub generation : u64
}
#[derive(Clone)]
pub struct VoiceInfo
{
pub volume : f32,
pub pitch : f32,
pub position : (f32, f32),
pub distance : f32,
pub rolloff_start : f32,
pub local : bool,
pub looping : bool
}
impl Default for VoiceInfo
{
fn default() -> VoiceInfo
{
VoiceInfo
{
volume : 1.0,
pitch : 1.0,
position : (0.0, 0.0),
distance : 1.0,
rolloff_start : 0.0,
local : true,
looping : false
}
}
}
pub struct NullAudio
{
}
impl NullAudio
{
pub fn new() -> NullAudio
{
NullAudio {}
}
}
impl Audio for NullAudio
{
#[allow(unused_variables)]
fn unit_scale(&self) -> f32 { 1.0 }
#[allow(unused_variables)]
fn set_unit_scale(&mut self, scale : f32) {}
#[allow(unused_variables)]
fn listener_position(&self) -> (f32, f32) { (0.0, 0.0) }
#[allow(unused_variables)]
fn set_listener_position(&mut self, x : f32, y : f32) {}
#[allow(unused_variables)]
fn play_sound(&mut self, sound_id : DataId) -> Option<Voice>
{
Some(Voice { slot : 0, generation : 0 })
}
#[allow(unused_variables)]
fn play_sound_with(&mut self, sound_id : DataId, voice_info : &VoiceInfo) -> Option<Voice>
{
Some(Voice { slot : 0, generation : 0 })
}
#[allow(unused_variables)]
fn stop_voice(&mut self, voice : &Voice) -> bool { true }
#[allow(unused_variables)]
fn voice(&self, voice : &Voice) -> Option<VoiceInfo> { None }
#[allow(unused_variables)]
fn set_voice(&mut self, voice : &Voice, voice_info : &VoiceInfo) -> bool { true }
#[allow(unused_variables)]
fn max_voices(&self) -> usize { 0 }
#[allow(unused_variables)]
fn sync_sound_store(&mut self, sound_store : &mut DataStore<Sound>) -> Result<(), PreparedStoreError>
{
Ok(())
}
}
pub struct AlAudio
{
#[allow(dead_code)]
alto : Alto,
#[allow(dead_code)]
device : OutputDevice,
#[allow(dead_code)]
context : Rc<RefCell<Context>>,
unit_scale : f32,
scale_multiplier : f32,
listener_position : (f32, f32),
sources : Vec<StaticSource>,
voice_generations : Vec<u64>,
voice_sounds : Vec<DataId>,
voice_pending : BitSet<u32>,
voice_info : Vec<VoiceInfo>,
al_sound_store : PreparedStore<Sound, AlSoundHandle>
}
impl AlAudio
{
pub fn new(resources : &mut DataMultistore) -> Result<AlAudio, AudioError>
{
let alto = try_or_else!(Alto::load_default(),
|error| Err(AudioError::AlError(format!("{}", error))));
let device = try_or_else!(alto.open(None),
|error| Err(AudioError::AlError(format!("{}", error))));
match device.specifier()
{
Some(specifier) =>
{
info!("OpenAL device: {}", specifier.to_str().unwrap_or("unknown"));
},
None => {}
}
let context_raw = try_or_else!(device.new_context(None),
|error| Err(AudioError::AlError(format!("{}", error))));
let mut sources = Vec::new();
let mut voice_generations = Vec::new();
let mut voice_sounds = Vec::new();
let mut voice_info = Vec::new();
for _ in 0..NUM_SOURCES
{
let source = try_or_else!(context_raw.new_static_source(),
|error| Err(AudioError::AlError(format!("{}", error))));
sources.push(source);
voice_generations.push(0);
voice_sounds.push(0);
voice_info.push(VoiceInfo::default());
}
context_raw.set_distance_model(alto::DistanceModel::InverseClamped);
let context = Rc::new(RefCell::new(context_raw));
let sound_data_preparer = Box::new(AlSoundPreparer::new(context.clone()));
let al_sound_store = try_or_else!(PreparedStore::new(&mut resources.store_mut::<Sound>(),
sound_data_preparer),
|error| Err(AudioError::LoadResourceFailed(format!("Failed to create OpenAL sound handle store: {}", error))));
Ok(AlAudio
{
alto,
device,
context,
unit_scale : 1.0,
scale_multiplier : 1.0,
listener_position : (0.0, 0.0),
sources,
voice_generations,
voice_sounds,
voice_pending : BitSet::with_capacity(NUM_SOURCES),
voice_info,
al_sound_store
})
}
fn slot_playing(&self, slot : usize) -> bool
{
if slot >= self.sources.len()
{
return false;
}
let state = self.sources[slot].state();
state == SourceState::Playing || state == SourceState::Paused || self.voice_pending.contains(slot)
}
fn find_voice(&mut self) -> Option<Voice>
{
for i in 0..self.sources.len()
{
if !self.slot_playing(i)
{
self.voice_generations[i] += 1;
let voice = Voice
{
slot : i,
generation : self.voice_generations[i]
};
return Some(voice);
}
}
None
}
fn set_voice_internal(&mut self, source_id : usize, voice_info : &VoiceInfo)
{
let context_ref : &Context = &self.context.borrow();
let _defer_lock = context_ref.defer_updates();
let source = &mut self.sources[source_id];
source.set_relative(voice_info.local);
let (mut pos_x, mut pos_y) = voice_info.position;
pos_x *= self.scale_multiplier;
pos_y *= self.scale_multiplier;
if let Err(error) = source.set_position([pos_x, pos_y, -1.0])
{
warn!("OpenAL source error: {}", error);
}
source.set_looping(voice_info.looping);
if let Err(error) = source.set_gain(voice_info.volume)
{
warn!("OpenAL source error: {}", error);
}
if let Err(error) = source.set_pitch(voice_info.pitch)
{
warn!("OpenAL source error: {}", error);
}
if let Err(error) = source.set_reference_distance(voice_info.rolloff_start + 1.0)
{
warn!("OpenAL source error: {}", error);
}
let distance_multiplier;
if voice_info.distance > 0.0 && voice_info.distance.is_normal()
{
distance_multiplier = 1.0 / voice_info.distance;
}
else
{
distance_multiplier = 1.0;
}
if let Err(error) = source.set_rolloff_factor(distance_multiplier)
{
warn!("OpenAL source error: {}", error);
}
self.voice_info[source_id] = voice_info.clone();
}
fn set_buffer_internal(&mut self, slot : usize)
{
let sound_id = self.voice_sounds[slot];
if let Some(data) = self.al_sound_store.get_mut(sound_id)
{
let source = &mut self.sources[slot];
source.stop();
let buffer_result = source.set_buffer(data.buffer.clone());
match buffer_result
{
Ok(_) =>
{
self.sources[slot].play();
},
Err(error) =>
{
warn!("OpenAL source bind failed: {}", error);
}
}
}
}
}
impl Audio for AlAudio
{
fn unit_scale(&self) -> f32
{
self.unit_scale
}
fn set_unit_scale(&mut self, scale : f32)
{
if self.unit_scale == scale
{
return;
}
let context_ref : &Context = &self.context.borrow();
let _defer_lock = context_ref.defer_updates();
self.unit_scale = scale;
if scale > 0.0 && scale.is_normal()
{
self.scale_multiplier = 1.0 / scale;
}
else
{
self.scale_multiplier = 1.0;
}
let mut warned_yet = false;
for i in 0..self.voice_info.len()
{
let (mut pos_x, mut pos_y) = self.voice_info[i].position;
pos_x *= self.scale_multiplier;
pos_y *= self.scale_multiplier;
let source = &mut self.sources[i];
if let Err(error) = source.set_position([pos_x, pos_y, -1.0])
{
if !warned_yet
{
warn!("OpenAL source error: {}", error);
warned_yet = true;
}
}
}
let (mut listener_x, mut listener_y) = self.listener_position;
listener_x *= self.scale_multiplier;
listener_y *= self.scale_multiplier;
if let Err(error) = context_ref.set_position([listener_x, listener_y, 0.0])
{
warn!("OpenAL source error: {}", error);
}
}
fn listener_position(&self) -> (f32, f32)
{
self.listener_position
}
fn set_listener_position(&mut self, x : f32, y : f32)
{
self.listener_position = (x, y);
let listener_x = x * self.scale_multiplier;
let listener_y = y * self.scale_multiplier;
let context_ref : &Context = &self.context.borrow();
if let Err(error) = context_ref.set_position([listener_x, listener_y, 0.0])
{
warn!("OpenAL source error: {}", error);
}
}
fn play_sound(&mut self, sound_id : DataId) -> Option<Voice>
{
self.play_sound_with(sound_id, &VoiceInfo::default())
}
fn play_sound_with(&mut self, sound_id : DataId, voice_info : &VoiceInfo) -> Option<Voice>
{
let voice_result = self.find_voice();
if let Some(voice) = &voice_result
{
self.voice_sounds[voice.slot] = sound_id;
self.set_voice_internal(voice.slot, voice_info);
if self.al_sound_store.get_mut(sound_id).is_some()
{
self.set_buffer_internal(voice.slot);
}
else
{
self.voice_pending.insert(voice.slot);
}
}
voice_result
}
fn stop_voice(&mut self, voice : &Voice) -> bool
{
if voice.slot >= self.voice_generations.len()
{
return false;
}
if self.voice_generations[voice.slot] != voice.generation
{
return false;
}
if self.slot_playing(voice.slot)
{
let source = &mut self.sources[voice.slot];
source.stop();
self.voice_pending.remove(voice.slot);
return true;
}
false
}
fn set_voice(&mut self, voice : &Voice, voice_info : &VoiceInfo) -> bool
{
if voice.slot >= self.voice_generations.len()
{
return false;
}
if self.voice_generations[voice.slot] != voice.generation
{
return false;
}
self.set_voice_internal(voice.slot, voice_info);
true
}
fn voice(&self, voice : &Voice) -> Option<VoiceInfo>
{
if voice.slot >= self.voice_generations.len()
{
return None;
}
if self.voice_generations[voice.slot] != voice.generation
{
return None;
}
if self.slot_playing(voice.slot)
{
return Some(self.voice_info[voice.slot].clone());
}
None
}
fn max_voices(&self) -> usize
{
self.voice_info.len()
}
fn sync_sound_store(&mut self, sound_store : &mut DataStore<Sound>) -> Result<(), PreparedStoreError>
{
let result = self.al_sound_store.sync(sound_store);
for slot in 0..self.voice_pending.len()
{
if self.voice_pending.contains(slot)
{
self.set_buffer_internal(slot);
}
}
self.voice_pending.clear();
result
}
}