use crate::envelope::Envelope;
use crate::voice::Voice;
use oscy::Oscillator;
pub struct Polyphony<O: Oscillator, E: Envelope, const N: usize> {
voices: [Voice<O, E>; N],
ages: [u64; N],
counter: u64,
steal_strategy: VoiceStealStrategy,
}
impl<O: Oscillator, E: Envelope, const N: usize> Polyphony<O, E, N> {
pub fn new(voices: [Voice<O, E>; N], steal_strategy: VoiceStealStrategy) -> Self {
Self {
voices,
ages: [0; N],
counter: 0,
steal_strategy,
}
}
pub fn from_factory<F>(steal_strategy: VoiceStealStrategy, mut factory: F) -> Self
where
F: FnMut() -> Voice<O, E>,
{
Self {
voices: std::array::from_fn(|_| factory()),
ages: [0; N],
counter: 0,
steal_strategy,
}
}
pub fn note_on(&mut self, midi_note: u8, velocity: f32) {
let voice_idx = self
.find_free_voice()
.unwrap_or_else(|| self.find_voice_to_steal());
self.counter += 1;
self.ages[voice_idx] = self.counter;
self.voices[voice_idx].note_on(midi_note, velocity);
}
pub fn note_off(&mut self, midi_note: u8) {
if let Some(voice) = self
.voices
.iter_mut()
.find(|v| v.note() == Some(midi_note))
{
voice.note_off();
}
}
pub fn next_sample(&mut self) -> f32 {
self.voices
.iter_mut()
.filter(|v| v.is_active())
.map(|v| v.next_sample())
.sum()
}
pub fn active_count(&self) -> usize {
self.voices.iter().filter(|v| v.is_active()).count()
}
pub fn reset(&mut self) {
for voice in &mut self.voices {
voice.reset();
}
self.ages = [0; N];
self.counter = 0;
}
pub fn voices(&self) -> &[Voice<O, E>; N] {
&self.voices
}
pub fn voices_mut(&mut self) -> &mut [Voice<O, E>; N] {
&mut self.voices
}
fn find_voice_to_steal(&self) -> usize {
match self.steal_strategy {
VoiceStealStrategy::Oldest => self
.ages
.iter()
.enumerate()
.min_by_key(|(_, age)| *age)
.map(|(idx, _)| idx)
.unwrap_or(0),
}
}
fn find_free_voice(&self) -> Option<usize> {
self.voices.iter().position(|v| !v.is_active())
}
pub const fn capacity(&self) -> usize {
N
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum VoiceStealStrategy {
#[default]
Oldest,
}