extern crate sdl2;
use sdl2::mixer::{
self,
Channel,
Chunk,
Music as sdl_Music
};
use crate::base::BASE_DIR;
use super::config::Config;
const FREQUENCY: i32 = 44100;
const FORMAT_DATA: mixer::AudioFormat = mixer::DEFAULT_FORMAT;
const FORMAT_CHANNELS: i32 = 2;
const CHUNKSIZE: i32 = 1024;
const CHANNELS: i32 = 0x10;
pub const MUSICS_DIR: &str = "audio/music";
pub const SOUNDS_DIR: &str = "audio/sound";
pub const SPEECHES_DIR: &str = "audio/speech";
const MUSIC_FADE_IN_DELAY: i32 = 1000;
const SPEECH_FADE_OUT_DELAY: i32 = 200; const SPEECH_FADE_IN_DELAY: i32 = 200;
pub struct System {
mute: bool,
vol_sound: i32,
vol_speech: i32
}
pub struct Music {
sdl_music: sdl_Music<'static>
}
pub struct Sound {
chunk: Chunk,
channel: Option<Channel>
}
impl System {
pub fn init(config: &Config) -> Result<System, String> {
if !config.mute {
mixer::open_audio(FREQUENCY, FORMAT_DATA, FORMAT_CHANNELS, CHUNKSIZE)?; mixer::allocate_channels(CHANNELS);
sdl_Music::set_volume(config.vol_music * 128 / 100);
mixer::reserve_channels(1); }
Ok(System{mute: config.mute, vol_sound: config.vol_sound, vol_speech: config.vol_speech})
}
pub fn load_music(&self, filepath: impl ToString) -> Option<Music> {
if !self.mute {
let filepath = filepath.to_string();
match sdl_Music::from_file(format!("{}/{}/{}", BASE_DIR, MUSICS_DIR, filepath)) {
Ok(sdl_music) => Some(Music{sdl_music}),
Err(..) => None
}
} else {
None
}
}
pub fn play_music(&self, music: Option<&Music>) -> Result<(), String> {
if !self.mute {
match music {
Some(m) => {
m.sdl_music.fade_in(-1, MUSIC_FADE_IN_DELAY)?;
Ok(())
},
None => Err(String::from("no music to play"))
}
} else {
Ok(())
}
}
fn load_chunk(&self, base_dirpath: impl ToString, filepath: impl ToString, volume: i32) -> Option<Sound> {
if !self.mute {
let base_dirpath = base_dirpath.to_string();
let filepath = filepath.to_string();
match Chunk::from_file(format!("{}/{}/{}", BASE_DIR, base_dirpath, filepath)) {
Ok(mut chunk) => {
chunk.set_volume(volume * 128 / 100);
let channel = None;
Some(Sound{chunk, channel})
},
Err(..) => None
}
} else {
None
}
}
pub fn load_sound(&self, filepath: impl ToString) -> Option<Sound> {
self.load_chunk(SOUNDS_DIR, filepath, self.vol_sound)
}
pub fn play_sound(&self, sound: Option<&mut Sound>, loops: i32) -> Result<(), String> {
if !self.mute {
match sound {
Some(s) => {
s.channel = Some(sdl2::mixer::Channel::all().play(& s.chunk, loops)?);
Ok(())
},
None => Err(String::from("no sound to play"))
}
} else {
Ok(())
}
}
pub fn pause_sound(&self, sound: Option<&Sound>) -> Result<(), String> {
if !self.mute {
match sound {
Some(s) => {
match s.channel {
Some(Channel(chan)) => {
Channel(chan).pause();
Ok(())
}
None => Err(String::from("sound is not played via any channel to pause"))
}
}
None => Err(String::from("no sound to pause"))
}
} else {
Ok(())
}
}
pub fn resume_sound(&self, sound: Option<&Sound>) -> Result<(), String> {
if !self.mute {
match sound {
Some(s) => {
match s.channel {
Some(Channel(chan)) => {
Channel(chan).resume();
Ok(())
}
None => Err(String::from("sound is not played via any channel to resume"))
}
}
None => Err(String::from("no sound to resume"))
}
} else {
Ok(())
}
}
pub fn halt_sound(&self, sound: Option<&mut Sound>) -> Result<(), String> {
if !self.mute {
match sound {
Some(s) => {
match s.channel {
Some(Channel(chan)) => {
Channel(chan).halt();
s.channel = None;
Ok(())
}
None => Err(String::from("sound is not played via any channel to halt"))
}
}
None => Err(String::from("no sound to halt"))
}
} else {
Ok(())
}
}
pub fn load_speech(&self, filepath: impl ToString) -> Option<Sound> {
self.load_chunk(SPEECHES_DIR, filepath, self.vol_speech)
}
pub fn play_speech(&self, speech: Option<&Sound>) -> Result<(), String> {
if !self.mute {
let chan = Channel(0); chan.fade_out(SPEECH_FADE_OUT_DELAY);
match speech {
Some(s) => {
chan.fade_in(&s.chunk, 0, SPEECH_FADE_IN_DELAY)?;
Ok(())
},
None => Err(String::from("no speech to play"))
}
} else {
Ok(())
}
}
pub fn pause(&self) {
if !self.mute {
sdl_Music::pause();
Channel::all().pause();
Channel(0).pause();
}
}
pub fn resume(&self) {
if !self.mute {
sdl_Music::resume();
Channel::all().resume();
Channel(0).resume();
}
}
pub fn halt(&self) {
if !self.mute {
sdl_Music::halt();
Channel::all().halt();
Channel(0).halt();
}
}
pub fn shut(&mut self) -> Result<(), String> {
if !self.mute {
mixer::close_audio();
}
Ok(())
}
}