mod hum_math;
mod hum_voice;
use super::hum_error::GenerateError;
static SAMPLE_RATE: u32 = 44_100;
pub fn run_commands(score_commands: Vec<(String, String)>) -> Result<Vec<f32>, GenerateError> {
let mut beats_per_second: f32 = 1.0;
let mut measure_index: i32 = -1;
let mut measure_greatest: i32 = -1;
let mut checkpoint_index: i32 = -1;
let mut time_signature: f32 = 1.0;
let mut beats_per_measure: f32 = 4.0;
let mut measure_duration = beats_per_measure / beats_per_second;
let mut timestamp_at_measure_start: f32 = 0.0;
let mut timestamp_offset_in_measure: f32 = 0.0;
let mut voice: String = "sine".to_string();
let mut track: Vec<f32> = Vec::new();
let mut note_frequencies = hum_math::get_standard_note_frequencies("sharps");
let note_frequencies_flats = hum_math::get_standard_note_frequencies("flats");
note_frequencies.extend(note_frequencies_flats);
for command in score_commands {
let verb = command.0;
let noun = command.1;
match verb.as_ref() {
"comment" => {
}
"tempo" => {
beats_per_second = noun.parse::<f32>().unwrap() / 60.0;
}
"time" => {
let time_signature_parts: Vec<&str> = noun.split("/").collect();
let numerator: f32 = time_signature_parts[0].parse::<f32>().unwrap();
let denominator: f32 = time_signature_parts[1].parse::<f32>().unwrap();
time_signature = numerator / denominator;
beats_per_measure = numerator;
measure_duration = beats_per_measure / beats_per_second;
}
"checkpoint" => {
checkpoint_index = measure_greatest + 1;
measure_index = measure_greatest;
}
"voice" => {
voice = noun;
}
"measure" => {
measure_index += 1;
timestamp_at_measure_start = measure_duration * (measure_index as f32);
timestamp_offset_in_measure = 0.0;
if measure_index > measure_greatest {
measure_greatest = measure_index;
}
}
"reset" => {
measure_index = checkpoint_index - 1;
}
_ => {
match note_frequencies.get(&verb[..]) {
Some(note_frequency) => {
let length_parts: Vec<&str> = noun.split("/").collect();
let length_numerator: f32 = length_parts[0].parse::<f32>().unwrap();
let mut length_denominator = length_parts[1].to_string();
let mut pluses = 0;
for (i, ch) in length_parts[1].chars().enumerate() {
if ch == '+' {
pluses += 1;
length_denominator.remove(i);
}
}
let length_denominator: f32 = length_denominator.parse::<f32>().unwrap();
let note_length_of_measure =
(length_numerator / length_denominator) / time_signature;
let mut note_duration = measure_duration * note_length_of_measure;
note_duration = note_duration + (pluses as f32) * (0.5 * note_duration);
let note_position =
timestamp_at_measure_start + timestamp_offset_in_measure;
add_note_to_track(
note_position, note_duration, note_frequency, &voice[..], &mut track, );
timestamp_offset_in_measure += note_duration;
}
None => {
return Err(GenerateError{message: format!("There is no note named {}", verb)});
}
}
}
}
}
Ok(track)
}
fn add_note_to_track(
position: f32, duration: f32, frequency: &f32, voice: &str, track: &mut Vec<f32>, ) {
let note = if frequency.is_nan() {
hum_math::generate_wave(&hum_voice::silence, frequency, duration)
} else {
match voice {
"square" => hum_math::generate_wave(&hum_voice::square, frequency, duration),
"sawtooth" => hum_math::generate_wave(&hum_voice::sawtooth, frequency, duration),
_ => hum_math::generate_wave(&hum_voice::sine, frequency, duration),
}
};
let sample_position = (position * (SAMPLE_RATE as f32)) as usize;
let sample_duration = (duration * (SAMPLE_RATE as f32)) as usize;
let extended_position = sample_position + sample_duration;
match track.len().checked_sub(extended_position) {
Some(_) => (),
None => {
let num_samples_to_add = extended_position - track.len();
track.extend(vec![0.0; num_samples_to_add]);
}
}
let volume = 0.05;
for i in 0..sample_duration {
track[sample_position + i] += note[i] * volume;
}
}