ktrl 0.1.8

A Supercharged Keyboard Programming Daemon
use serde::Deserialize;

use enum_iterator::IntoEnumIterator;
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;

use rodio;
use rodio::Source;
use std::convert::AsRef;

//---------------------------------------------------

struct SoundImpl(Arc<Vec<u8>>);

impl AsRef<[u8]> for SoundImpl {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl SoundImpl {
    fn load(path: &Path) -> io::Result<SoundImpl> {
        let mut buf = Vec::new();
        let mut file = File::open(path)?;
        file.read_to_end(&mut buf)?;
        Ok(SoundImpl(Arc::new(buf)))
    }
    fn cursor(self: &Self) -> io::Cursor<SoundImpl> {
        io::Cursor::new(SoundImpl(self.0.clone()))
    }
    fn decoder(self: &Self) -> rodio::Decoder<io::Cursor<SoundImpl>> {
        rodio::Decoder::new(self.cursor()).unwrap()
    }
}

//---------------------------------------------------

lazy_static::lazy_static! {
    static ref KSOUND_FILENAMES: HashMap<KSnd, &'static str> = {
        [
            (KSnd::Click1, "click1.wav"),
            (KSnd::Click2, "click2.wav"),
            (KSnd::Sticky, "sticky.wav"),
            (KSnd::Error, "error.wav"),
        ].iter().cloned().collect()
    };
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, IntoEnumIterator, Deserialize)]
pub enum KSnd {
    Click1,
    Click2,
    Sticky,
    Error,
}

pub struct Dj {
    dev: rodio::Device,
    ksnds: HashMap<KSnd, SoundImpl>,
    custom_snds: HashMap<String, SoundImpl>,
}

impl Dj {
    fn make_ksnds(assets_path: &Path) -> HashMap<KSnd, SoundImpl> {
        let snds_dir = Path::new(assets_path).join("sounds");
        let mut out: HashMap<KSnd, SoundImpl> = HashMap::new();
        for snd in KSnd::into_enum_iter() {
            let path = snds_dir.join(&KSOUND_FILENAMES[&snd]);
            out.insert(snd, SoundImpl::load(&path).unwrap());
        }
        out
    }

    pub fn new(assets_path: &Path) -> Self {
        let dev = rodio::default_output_device().expect("Failed to open the default sound device");
        let ksnds = Self::make_ksnds(assets_path);
        Self {
            dev,
            ksnds,
            custom_snds: HashMap::new(),
        }
    }

    pub fn play(&self, snd: KSnd) {
        let snd = &self.ksnds[&snd];
        rodio::play_raw(&self.dev, snd.decoder().convert_samples());
    }

    pub fn play_custom(&mut self, path: &String) {
        if !self.custom_snds.contains_key(path) {
            let _path = Path::new(path);
            self.custom_snds
                .insert(path.clone(), SoundImpl::load(&_path).unwrap());
        }

        let snd = &self.custom_snds[path];
        rodio::play_raw(&self.dev, snd.decoder().convert_samples());
    }
}