aboutsummaryrefslogblamecommitdiffstats
path: root/src/main.rs
blob: e60ee45d862c1bf3d1009bf41e8b7398fb99e1d9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                         
                                                                              


























                                                                                    






                                       
                                        
                                        



                                
                                                







                                               
                                                 





















                                                                           



















                                                        






                                                                


                                                                           


                                                     


                                                                              











                                                             
// trriggy - a trigger/drum replacer in Rust
// Copyright (C) 2018  Harald Eilertsen <haraldei@anduin.net>
//
// 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 <https://www.gnu.org/licenses/>.

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<i16>,
}

impl Trigger {
    pub fn new<T: Read>(samples: hound::WavReader<T>) -> Trigger {
        Trigger::default().read_samples(samples)
    }

    fn read_samples<T: Read>(mut self, mut reader: hound::WavReader<T>) -> 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<R: Read>(spec: &hound::WavSpec, r: &hound::WavReader<R>) {
    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::<i16>();
    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();
}