1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#![warn(nonstandard_style, rust_2018_idioms, future_incompatible)]
use bit_set::BitSet;

/// An interface for one "Voice"
/// in a synth.
pub trait Voice {
    type Configuration: ?Sized;
    fn note_on(&mut self, _: &Self::Configuration, note_number: u8, velocity: u8);
    fn note_off(&mut self, _: &Self::Configuration, velocity: u8);

    /// Returning false here indicates
    /// that the voice is ready for a new note
    /// without audible glitch - i.e.
    /// release is done.
    /// It's legal to return false before
    /// receiving a note_off, but if that
    /// happens a note_off will not be sent.
    fn is_running(&self) -> bool;
}

pub struct Manager {
    num_voices: usize,
    used_voices: Vec<(usize, u8)>,
    free_voices: BitSet,
}

impl Manager {
    pub fn new(num_voices: usize) -> Manager {
        Manager {
            num_voices,
            used_voices: Vec::with_capacity(num_voices),
            free_voices: BitSet::with_capacity(num_voices),
        }
    }

    pub fn note_on<C: Default, V: Voice<Configuration = C>>(
        &mut self,
        voices: &mut [V],
        note_number: u8,
        velocity: u8,
    ) {
        self.note_on_with_config(voices, &V::Configuration::default(), note_number, velocity);
    }

    pub fn note_off<C: Default, V: Voice<Configuration = C>>(
        &mut self,
        voices: &mut [V],
        note_number: u8,
        velocity: u8,
    ) {
        self.note_off_with_config(voices, &V::Configuration::default(), note_number, velocity);
    }

    /// Send a note on to the relevant voice.
    pub fn note_on_with_config<V: Voice>(
        &mut self,
        voices: &mut [V],
        config: &V::Configuration,
        note_number: u8,
        velocity: u8,
    ) {
        assert_eq!(voices.iter().len(), self.num_voices);

        // first, free up any used_voices that are no longer
        // running.  This could be from something like a drum
        // hit that finished itself before a note off.
        self.used_voices
            .retain(|voice_note| voices[voice_note.0].is_running());

        for (i, v) in voices.iter().enumerate() {
            if !v.is_running() {
                self.free_voices.insert(i);
            }
        }

        // get the first free voice.  If there isn't one, drop it.
        let first_free_voice = self.free_voices.iter().next();
        if let Some(i) = first_free_voice {
            self.free_voices.remove(&i);
            voices[i].note_on(config, note_number, velocity);
            self.used_voices.push((i, note_number));
        }
    }

    /// Send a note off to the relevant voice.
    pub fn note_off_with_config<V: Voice>(
        &mut self,
        voices: &mut [V],
        config: &V::Configuration,
        note_number: u8,
        velocity: u8,
    ) {
        assert_eq!(voices.iter().len(), self.num_voices);
        self.used_voices.retain(|voice_note| {
            if voice_note.1 != note_number {
                true
            } else {
                if voices[voice_note.0].is_running() {
                    voices[voice_note.0].note_off(config, velocity);
                }
                false
            }
        });
    }
}