polyphony 0.1.0

A library for handling polyphony in real-time audio applications.
Documentation
//! Library to facilitate generating different sounds at the same time (polyphony).
//!
//! Polyphony consists of different steps:
//!
//! 1. Classify how the event should be dispatched.
//!    How exactly it should be classified, is defined by the [`EventDispatchClass`] enum.
//!    The dispatching itself is done by a type that implements the [`EventDispatchClassifier`] trait.
//! 2. Next, using the classification, voices are assigned to the event.
//!    The assigned voice(s) are described by the [`VoiceAssignment`] enum.
//!    The [`VoiceAssigner`] trait defines this action.
//! 3. Then, the event can be dispatched by calling the [`dispatch`] method on the [`VoiceAssignment`].
#![cfg_attr(feature = "midi", doc ="\
# Example of using polyphony

The following example illustrates a plugin (or application) that has multiple voices that
correspond to different tones.

_Remark_ the example assumes the `polyphony` crate is compiled with the `midi` feature.

```
use polyphony::{Voice, EventDispatchClassifier, VoiceAssigner};
use polyphony::midi::{ToneIdentifier, RawMidiEventToneIdentifierDispatchClassifier};
use polyphony::simple_event_dispatching::SimpleVoiceState;
use polyphony::simple_event_dispatching::SimpleEventDispatcher;

struct MyVoice {
     // ...
}

impl MyVoice {
   fn handle_raw_midi_event(&mut self, event: &[u8; 3]) {
        // Here you typically change the state of the voice.
        unimplemented!();
   }

   fn render_buffer(&mut self, audio_buffer: &mut[f32]) {
       unimplemented!();
   }
}

impl Voice<SimpleVoiceState<ToneIdentifier>> for MyVoice {
    fn state(&self) -> SimpleVoiceState<ToneIdentifier> {
        // Let the event dispatcher know what state this voice is in.
        unimplemented!();
    }
}

struct MyPlugin {
    voices: Vec<MyVoice>,
    // ...
}

impl MyPlugin
{
    fn handle_event(&mut self, raw_midi_event: &[u8;3]) {
        let mut classifier = RawMidiEventToneIdentifierDispatchClassifier;
        let classification = classifier.classify(raw_midi_event);
        let mut dispatcher = SimpleEventDispatcher;
        let assignment = dispatcher.assign(classification, &mut self.voices);
        assignment.dispatch(raw_midi_event, &mut self.voices, MyVoice::handle_raw_midi_event);
    }

    fn render_buffer(&mut self, buffer: &mut [f32]) {
        for voice in self.voices.iter_mut() {
            voice.render_buffer(buffer);
        }
    }
}

```
")]
//! [`EventDispatchClass`]: ./enum.EventDispatchClass.html
//! [`EventDispatchClassifier`]: ./trait.EventDispatchClassifier.html
//! [`VoiceAssignment`]: ./enum.VoiceAssignment.html
//! [`VoiceAssigner`]: ./trait.VoiceAssigner.html
//! [`dispatch`]: enum.VoiceAssignment.html#method.dispatch

#[cfg(feature = "midi")]
extern crate midi_consts;
#[cfg(feature = "midi")]
pub mod midi;

pub mod simple_event_dispatching;

/// Describe how an event should be dispatched.
///
/// # Example.
/// Suppose events are dispatched based on the tone that is played.
/// Then `EventDispatchClass::VoiceSpecific(ToneIdentifier(60))` means that this event
/// should be dispatched to whatever voice is currently playing the tone with midi number 60
/// (central C on the piano).
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum EventDispatchClass<Identifier> {
    /// The event should be dispatched to all voices
    Broadcast,
    /// The event should be dispatched to a newly created voice with the given identifier
    AssignNewVoice(Identifier),
    /// The event should be dispatched to an already existing voice with the given identifier
    VoiceSpecific(Identifier),
}

/// Determine to what voices the event should be dispatched.
pub trait EventDispatchClassifier<Event> {
    /// The identifier used to identify a specific voice.
    type VoiceIdentifier: Eq + Copy;

    /// Classify how the event should be dispatched.
    fn classify(&self, event: &Event) -> EventDispatchClass<Self::VoiceIdentifier>;
}

/// Describe to what particular voice an event should be assigned.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum VoiceAssignment {
    /// This event should be assigned to no particular voice (i.e.: it should simply be dropped).
    None,
    /// This event should be assigned to all voices.
    All,
    /// This event should be assigned to the voice with the given index
    Some { index: usize },
}

impl VoiceAssignment {
    /// Dispatch an action to the assigned voices.
    pub fn dispatch<E, F, V>(self, event: E, voices: &mut [V], mut action: F)
    where
        E: Copy,
        F: FnMut(&mut V, E),
    {
        match self {
            VoiceAssignment::None => {}
            VoiceAssignment::Some { index } => {
                action(&mut voices[index], event);
            }
            VoiceAssignment::All => {
                for voice in voices.iter_mut() {
                    action(voice, event);
                }
            }
        }
    }
}

/// Define to which particular voice(s) the event should be assigned, based on the [`EventDispatchClass`].
///
/// This does the "voice stealing".
///
/// [`EventDispatchClass`]: ./enum.EventDispatchClass.html
pub trait VoiceAssigner<Voice, Identifier> {
    /// Define to which particular voice(s) the event should be assigned, based on the [`EventDispatchClass`].
    /// The implementation of this method may assume that for subsequent calls to it, always the
    /// same slice of voices is used.
    ///
    /// [`EventDispatchClass`]: ./enum.EventDispatchClass.html
    fn assign(
        &mut self,
        classifier: EventDispatchClass<Identifier>,
        voices: &mut [Voice],
    ) -> VoiceAssignment;
}

/// Trait used to facilitate implementing a [`VoiceAssigner`].
///
/// [`VoiceAssigner`]: ./trait.VoiceAssigner.html
pub trait VoiceAssignerHelper<Voice, Identifier> {
    fn find_active_voice(&mut self, identifier: Identifier, voices: &mut [Voice]) -> Option<usize>;

    fn find_new_voice(&mut self, identifier: Identifier, voices: &mut [Voice]) -> usize;
}

impl<Voice, Identifier, T> VoiceAssigner<Voice, Identifier> for T
where
    T: VoiceAssignerHelper<Voice, Identifier>,
{
    fn assign(
        &mut self,
        classifier: EventDispatchClass<Identifier>,
        voices: &mut [Voice],
    ) -> VoiceAssignment {
        match classifier {
            EventDispatchClass::Broadcast => VoiceAssignment::All,
            EventDispatchClass::VoiceSpecific(identifier) => {
                match self.find_active_voice(identifier, voices) {
                    Some(index) => VoiceAssignment::Some { index },
                    None => VoiceAssignment::None,
                }
            }
            EventDispatchClass::AssignNewVoice(identifier) => VoiceAssignment::Some {
                index: self.find_new_voice(identifier, voices),
            },
        }
    }
}

/// A trait used by some dispatchers.
pub trait Voice<State> {
    fn state(&self) -> State;
}