// trriggy - a trigger/drum replacer in Rust // Copyright (C) 2018 Harald Eilertsen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use clap::{self, crate_name, crate_authors, crate_version, crate_description}; use hound; use std::io::Read; use std::iter::FromIterator; #[derive(Default)] struct Trigger { trigged: bool, playing: bool, acc: u32, sample_pos: usize, sample: Vec, } impl Trigger { pub fn new(samples: hound::WavReader) -> Trigger { Trigger::default().read_samples(samples) } fn read_samples(mut self, mut reader: hound::WavReader) -> Trigger { self.sample = Vec::from_iter(reader.samples().map(|s| s.unwrap())); self } pub fn process(&mut self, sample: i16) -> i16 { let mut res = 0i16; let abs = sample.abs() as u32; let div = if (abs > self.acc) { abs - self.acc } else { 0 }; self.acc = (self.acc + abs) / 2; if !self.trigged && div > 5000 { self.trigged = true; self.playing = true; self.sample_pos = 0; } else if self.trigged && self.acc < 250 { self.trigged = false; } if self.playing { res = self.sample[self.sample_pos]; self.sample_pos += 1; } if self.sample_pos == self.sample.len() { self.playing = false; } res } } fn print_wavspec(spec: &hound::WavSpec, r: &hound::WavReader) { println!(" channels: {}", spec.channels); println!(" samplerate: {}", spec.sample_rate); println!(" bits/sample: {}", spec.bits_per_sample); println!(" sample format: {}", match spec.sample_format { hound::SampleFormat::Int => "integer", hound::SampleFormat::Float => "floating point", }); println!(" duration: {} samples/{} sec/{} min", r.duration(), r.duration()/spec.sample_rate, r.duration()/(spec.sample_rate * 60)); } fn main() { let m = clap::app_from_crate!() .arg(clap::Arg::with_name("sample") .short("s") .long("sample") .value_name("SAMPLE_FILE") .help("The sample to replace when trigged.") .takes_value(true) .required(true)) .arg(clap::Arg::with_name("output") .short("o") .long("output") .value_name("OUTPUT_FILE") .help("Where to store the result.") .takes_value(true)) .arg(clap::Arg::with_name("INPUT") .help("The input file") .required(true)) .get_matches(); let filename = m.value_of("INPUT").unwrap(); let mut reader = hound::WavReader::open(&filename).unwrap(); println!("Reading file {}:", &filename); let spec = reader.spec(); print_wavspec(&spec, &reader); let sample_filename = m.value_of("sample").unwrap(); println!("Using sample file: {}", sample_filename); let mut sample_file = hound::WavReader::open(sample_filename).unwrap(); print_wavspec(&sample_file.spec(), &sample_file); let output_filename = m.value_of("output").unwrap_or("output.wav"); println!("Writing to output: {}", output_filename); let mut output = hound::WavWriter::create(output_filename, spec).unwrap(); let samples = reader.samples::(); let mut triggey = Trigger::new(sample_file); samples .map(|s| s.unwrap()) .for_each(|s| { output.write_sample(triggey.process(s)).unwrap(); }); output.finalize().unwrap(); }