use bbx_core::StackVec;
#[inline]
pub fn midi_note_to_frequency(note: u8) -> f32 {
440.0 * 2.0f32.powf((note as f32 - 69.0) / 12.0)
}
#[derive(Debug, Clone)]
pub struct VoiceState {
pub active_note: Option<u8>,
pub velocity: f32,
pub gate: bool,
pub frequency: f32,
note_stack: StackVec<(u8, u8), 16>,
}
impl VoiceState {
pub fn new() -> Self {
Self {
active_note: None,
velocity: 0.0,
gate: false,
frequency: 440.0,
note_stack: StackVec::new(),
}
}
pub fn note_on(&mut self, note: u8, velocity: u8) {
let vel_normalized = velocity as f32 / 127.0;
let _ = self.note_stack.push((note, velocity));
self.active_note = Some(note);
self.velocity = vel_normalized;
self.gate = true;
self.frequency = midi_note_to_frequency(note);
}
pub fn note_off(&mut self, note: u8) -> bool {
let mut i = 0;
while i < self.note_stack.len() {
if self.note_stack[i].0 == note {
for j in i..self.note_stack.len() - 1 {
self.note_stack[j] = self.note_stack[j + 1];
}
self.note_stack.pop();
} else {
i += 1;
}
}
if self.active_note == Some(note) {
if let Some(&(prev_note, prev_vel)) = self.note_stack.as_slice().last() {
self.active_note = Some(prev_note);
self.velocity = prev_vel as f32 / 127.0;
self.frequency = midi_note_to_frequency(prev_note);
false
} else {
self.active_note = None;
self.gate = false;
true
}
} else {
false
}
}
pub fn reset(&mut self) {
self.active_note = None;
self.velocity = 0.0;
self.gate = false;
self.frequency = 440.0;
self.note_stack.clear();
}
#[inline]
pub fn is_active(&self) -> bool {
self.active_note.is_some()
}
#[inline]
pub fn held_note_count(&self) -> usize {
self.note_stack.len()
}
}
impl Default for VoiceState {
fn default() -> Self {
Self::new()
}
}