#![feature(pattern)]
use fmod::{self, ChannelControl};
use vec_map::VecMap;
use rs_utils;
use signal_utils::{pcm, generator, Generator};
use fmod_utils::*;
const MAX_CHANNELS : u16 = 1024; const INITFLAGS : fmod::system::Initflags =
fmod::system::Initflags::_3D_RIGHTHANDED;
const SOUNDFONT : &'static str = "data/gm.dls";
const SAMPLE_WAV : sampler::Id = sampler::Id (0);
const SONG_MID : music::Id = music::Id (0);
const VOICE_WAV : usize = 0;
const VOICE_MID : usize = 1;
const VOICE_NOISE : usize = 2;
#[allow(unused_macros)]
macro_rules! show {
($e:expr) => { println!("{}: {:?}", stringify!($e), $e); }
}
fn noise_gen (system : &mut fmod::System, generator : &mut Generator)
-> fmod::Sound
{
const SECONDS : f64 = 10.0;
let mut pcm_data = vec![0i16; (SECONDS * 44100.0) as usize];
generator.generate (&mut pcm_data[..]);
let pcm_data = pcm::trim_zeros (
pcm::normalize_peak (pcm_data.as_mut_slice(), std::i16::MAX));
println!("noise len: {}", pcm_data.len());
system.create_sound_from_pcm_default (pcm_data).unwrap()
}
fn main() {
println!("fmod-utils example main...");
let opts = clap::Command::new ("Fmod Utils Example App")
.arg (clap::Arg::new ("wav-file")
.short ('w')
.value_name ("WAV_FILE")
.help ("Sample wav file"))
.arg (clap::Arg::new ("mid-file")
.short ('m')
.value_name ("MID_FILE")
.help ("Midi song file"))
.arg (clap::Arg::new ("dump-info")
.short ('d')
.help ("Write info files to ./dump"))
.get_matches();
let wav_file = opts.get_one ("wav-file")
.unwrap_or (&"data/bite.wav".to_string()).clone();
let mid_file = opts.get_one ("mid-file")
.unwrap_or (&"data/gshop.mid".to_string()).clone();
let dumpinfo = opts.contains_id ("dump-info");
println!("using soundfont: {}", SOUNDFONT);
let mut audition = {
use std::iter::FromIterator;
let system = fmod::System::new (None, MAX_CHANNELS, INITFLAGS).unwrap();
let mut audition = Audition::with_system (system);
audition.load_samples (
VecMap::from_iter (vec![wav_file].into_iter().enumerate()));
audition.load_songs_midi (
VecMap::from_iter (vec![(mid_file, SOUNDFONT.into())]
.into_iter().enumerate())
);
audition.voices.insert (VOICE_WAV, Voice::default());
audition.voices.insert (VOICE_MID, Voice::default());
audition.voices.insert (VOICE_NOISE, Voice::default());
audition
};
println!("...fmod system initialized...");
println!("reverb max instances: {}", fmod::dsp::REVERB_MAXINSTANCES);
let mut reverb = false;
let mut preset = fmod::reverb3d::Presets::Generic;
const REVERB_INSTANCE : i32 = 0;
audition.system.set_reverb_properties (REVERB_INSTANCE, &fmod::reverb3d::OFF)
.unwrap();
println!("...fmod system reverb properties set...");
let listener_attributes = fmod::ListenerAttributes {
pos: [0.0, 0.0, 2.0],
vel: [0.0, 0.0, 0.0],
forward: [0.0, 0.0, -1.0],
up: [0.0, 1.0, 0.0]
};
const LISTENER_ID : i32 = 0;
audition.system.set_3d_listener_attributes (LISTENER_ID, &listener_attributes)
.unwrap();
println!("...fmod system 3d listener attributes set...");
const DSP_UNIT_ECHO : i32 = 1;
let mut dsp_echo = audition.system.create_dsp_by_type (fmod::dsp::Type::Echo)
.unwrap();
dsp_echo.set_parameter_float (fmod::dsp::Echo::Delay as i32, 100.0).unwrap();
dsp_echo.set_parameter_float (fmod::dsp::Echo::Feedback as i32, 70.0).unwrap();
dsp_echo.set_parameter_float (fmod::dsp::Echo::DryLevel as i32, 0.0).unwrap();
dsp_echo.set_parameter_float (fmod::dsp::Echo::WetLevel as i32, 0.0).unwrap();
dsp_echo.set_bypass (true).unwrap();
const DSP_UNIT_LOWPASS : i32 = 2;
let mut dsp_lowpass = audition.system
.create_dsp_by_type (fmod::dsp::Type::Lowpass).unwrap();
dsp_lowpass.set_parameter_float (fmod::dsp::Lowpass::Cutoff as i32, 360.0)
.unwrap();
dsp_lowpass.set_bypass (true).unwrap();
let mut master_channel_group = audition.system.get_master_channel_group()
.unwrap();
master_channel_group.add_dsp (DSP_UNIT_ECHO, &mut dsp_echo).unwrap();
master_channel_group.add_dsp (DSP_UNIT_LOWPASS, &mut dsp_lowpass).unwrap();
println!("...fmod dsps created...");
println!("...generating noise...");
let mut generator = {
let kind = generator::Noise::Brown { intensity: 410 }.into();
let freq = 44100.0;
Generator::new (kind, freq)
};
let mut sound_noise = noise_gen (&mut audition.system, &mut generator);
audition.system.update().unwrap();
println!("readline loop...");
println!("commands:\n \
'play' -- play the sample wav\n \
'demo' -- demo that pans a repeating sample from left to right\n \
'midi' -- play the midi song file\n \
'noise' -- loop generated noise\n \
'regen' -- regenerate noise data\n \
'stop' -- stop looping noise and midi song playback\n \
'echo' -- toggle echo dsp\n \
'reverb' -- toggle 3D reverb\n \
'lowpass' -- toggle lowpass filter\n \
'next' -- choose the next reverb preset\n \
'quit' -- quit");
'readline_loop: loop {
use std::io::{self, Write};
print!(" > ");
let _ = io::stdout().flush();
let mut s = String::new();
let _ = io::stdin().read_line (&mut s);
if !s.trim_end().is_empty() {
let word_ct = s.as_str().split_whitespace().count();
let mut words = s.as_str().split_whitespace();
#[allow(unused_assignments)]
let mut handled = true;
match word_ct {
0 => unreachable!("ERROR: zero words in server input readline parse"),
1 => {
let command = words.next().unwrap();
use std::str::pattern::Pattern;
if command.is_prefix_of ("quit") {
break 'readline_loop;
} else if command.is_prefix_of ("play") {
audition.voices[VOICE_WAV].play (
audition.sampler.get_mut (SAMPLE_WAV).unwrap(),
None);
} else if command.is_prefix_of ("midi") {
audition.voices[VOICE_MID].play (
audition.music.get_mut (SONG_MID).unwrap(),
None);
} else if command.is_prefix_of ("noise") {
audition.voices[VOICE_NOISE].loop_ (&mut sound_noise, None);
} else if command.is_prefix_of ("regen") {
sound_noise = noise_gen (&mut audition.system, &mut generator);
if let Some (true) = audition.voices[VOICE_NOISE].is_playing() {
audition.voices[VOICE_NOISE].loop_ (&mut sound_noise, None);
}
} else if command.is_prefix_of ("stop") {
let _ = audition.voices[VOICE_MID].stop();
let _ = audition.voices[VOICE_NOISE].stop();
} else if command.is_prefix_of ("echo") {
if dsp_echo.get_bypass().unwrap() {
dsp_echo.set_bypass (false).unwrap();
} else {
dsp_echo.set_bypass (true).unwrap();
}
} else if command.is_prefix_of ("reverb") {
if reverb {
audition.system.set_reverb_properties (
REVERB_INSTANCE, &fmod::reverb3d::OFF
).unwrap();
reverb = false;
} else {
audition.system.set_reverb_properties (
REVERB_INSTANCE, &preset.into()
).unwrap();
reverb = true;
}
} else if command.is_prefix_of ("lowpass") {
if dsp_lowpass.get_bypass().unwrap() {
dsp_lowpass.set_bypass (false).unwrap();
} else {
dsp_lowpass.set_bypass (true).unwrap();
}
} else if command.is_prefix_of ("next") {
let i = preset as u32 + 1;
if i >= fmod::reverb3d::Presets::MAX as u32 {
preset = fmod::reverb3d::Presets::Generic;
} else {
use fmod::FromPrimitive;
preset = fmod::reverb3d::Presets::from_u32 (i).unwrap();
}
if reverb {
audition.system.set_reverb_properties (
REVERB_INSTANCE, &preset.into()
).unwrap();
}
show!(preset);
} else if command.is_prefix_of ("demo") {
let mut position = [0.0, 0.0, -2.0];
let velocity = [20.0, 0.0, 0.0];
for i in 0..101 {
let x = -100.0 + 2.0*(i as f32);
position[0] = x;
let mode = fmod::Mode::LOOP_OFF | fmod::Mode::_3D;
let mut channel = audition.sampler
.cue (SAMPLE_WAV, Some (mode), None);
channel.set_3d_attributes (position, velocity).unwrap();
channel.set_paused (false).unwrap();
audition.system.update().unwrap();
std::thread::sleep (std::time::Duration::from_millis (100));
}
} else {
handled = false;
}
},
_ => handled = false
} if !handled {
println!("unrecognized command: \"{}\"", s.trim());
}
} } println!("...readline loop");
if dumpinfo {
let file_path = std::path::Path::new ("dump/system-info");
let (file_path, mut file) =
rs_utils::file::file_new_append_incremental (file_path).unwrap();
println!("{:<40} {:<40?}", "...dumping system info to:",
file_path.to_str().unwrap());
info::write_info_system (&mut file, &audition.system).unwrap();
drop (file);
let file_path = std::path::Path::new ("dump/sound-info");
let (file_path, mut file) =
rs_utils::file::file_new_append_incremental (file_path).unwrap();
println!("{:<40} {:<40?}", "...dumping sound info to:",
file_path.to_str().unwrap());
info::write_info_sound (&mut file, &audition.sampler
.get (SAMPLE_WAV).unwrap()).unwrap();
drop (file);
let file_path = std::path::Path::new ("dump/channel-group-info");
let (file_path, mut file) =
rs_utils::file::file_new_append_incremental (file_path).unwrap();
println!("{:<40} {:<40?}", "...dumping master channel group info to:",
file_path.to_str().unwrap());
info::write_info_channel_group (&mut file, &master_channel_group).unwrap();
drop (file);
}
println!("...fmod-utils example main");
}