use crate::channel::Channel;
use crate::midi_observer::MidiEvent;
use crate::triggerkeep::TRIGGER_KEEP_PERIOD;
use crate::voice_pool::VoicePool;
use alloc::{vec, vec::Vec};
use xmrs::fixed::fixed::Q15;
use xmrs::fixed::units::{Amplification, SampleRate, Volume};
use xmrs::prelude::*;
pub const DEFAULT_VOICE_POOL_CAPACITY: usize = 256;
pub struct Voices<'a> {
sample_rate: SampleRate,
channel: Vec<Channel<'a>>,
pool: VoicePool<'a>,
global_volume: Volume,
amplification: Amplification,
volume_slide_speed: Q15,
volume_slide_fine: bool,
row_has_global_volume_slide: bool,
pending_midi_events: Vec<(usize, MidiEvent)>,
}
impl<'a> Voices<'a> {
pub(crate) fn new_with_voice_pool_capacity(
module: &'a Module,
sample_rate: u32,
initial_tempo: usize,
voice_pool_capacity: usize,
) -> Self {
let num_channels = module.get_num_channels();
let sr = SampleRate::from_hz(sample_rate.max(1));
let mut channels = vec![Channel::new(module, sr, initial_tempo); num_channels];
for (i, ch) in channels.iter_mut().enumerate() {
if let Some(d) = module.channel_defaults.get(i) {
if let Some(p) = d.panning {
ch.set_initial_panning(p);
}
if let Some(v) = d.volume {
ch.set_initial_channel_volume(v);
}
if d.muted {
ch.set_initial_muted(true);
}
if d.surround {
ch.set_initial_surround(true);
}
}
}
for (i, ch) in channels.iter_mut().enumerate() {
ch.reseed_rng(0xA5A5_0000 | (i as u32 + 1));
ch.set_track_index(i);
}
Self {
sample_rate: sr,
channel: channels,
pool: VoicePool::new(voice_pool_capacity),
global_volume: Volume::FULL,
amplification: Amplification::UNITY,
volume_slide_speed: Q15::ZERO,
volume_slide_fine: true,
row_has_global_volume_slide: false,
pending_midi_events: Vec::new(),
}
}
pub fn global_volume(&self) -> Volume {
self.global_volume
}
pub fn set_global_volume(&mut self, v: Volume) {
self.global_volume = v;
}
#[doc(hidden)]
pub fn global_volume_q(&self) -> Volume {
self.global_volume
}
#[doc(hidden)]
#[allow(dead_code)]
pub fn set_global_volume_q(&mut self, v: Volume) {
self.global_volume = v;
}
pub fn amplification(&self) -> Amplification {
self.amplification
}
pub fn set_amplification(&mut self, a: Amplification) {
self.amplification = a;
}
#[doc(hidden)]
pub fn amplification_q(&self) -> Amplification {
self.amplification
}
#[doc(hidden)]
#[allow(dead_code)]
pub fn set_amplification_q(&mut self, a: Amplification) {
self.amplification = a;
}
pub fn sample_rate(&self) -> SampleRate {
self.sample_rate
}
#[doc(hidden)]
#[allow(dead_code)]
pub fn sample_rate_q(&self) -> SampleRate {
self.sample_rate
}
pub fn num_channels(&self) -> usize {
self.channel.len()
}
pub fn set_mute_channel(&mut self, channel_num: usize, mute: bool) {
if channel_num < self.channel.len() {
self.channel[channel_num].muted = mute;
}
}
pub fn mute_all(&mut self, mute: bool) {
for c in &mut self.channel {
c.muted = mute;
}
}
pub(crate) fn set_tempo(&mut self, tempo: usize) {
for ch in &mut self.channel {
ch.set_tempo(tempo);
}
}
pub(crate) fn reset_for_goto(&mut self) {
self.global_volume = Volume::FULL;
let pool = &mut self.pool;
for ch in &mut self.channel {
ch.clear_ghosts(pool);
ch.trigger_pitch(TRIGGER_KEEP_PERIOD, pool);
}
}
pub(crate) fn process_row(&mut self, cells: &[TrackUnit]) {
self.row_has_global_volume_slide = cells.iter().any(|cell| cell.has_global_volume_slide());
self.pending_midi_events.clear();
let n = self.channel.len().min(cells.len());
for (i, cell) in cells.iter().enumerate().take(n) {
self.channel[i].tick0(cell, &mut self.pool);
self.apply_row_global_effects(i, cell);
}
}
fn apply_row_global_effects(&mut self, ch_index: usize, cell: &TrackUnit) {
for gfx in &cell.global_effects {
match gfx {
GlobalEffect::Volume(volume) => {
self.global_volume = *volume;
}
GlobalEffect::VolumeSlide { speed: s, fine: f } => {
if *f {
self.global_volume = self.global_volume.with_tremolo(*s);
}
self.volume_slide_speed = *s;
self.volume_slide_fine = *f;
}
GlobalEffect::MidiMacro(macro_type) => {
if let Some(ch) = self.channel.get_mut(ch_index) {
ch.apply_midi_macro(
macro_type.clone(),
ch_index,
&mut self.pending_midi_events,
&mut self.pool,
);
}
}
_ => {}
}
}
}
pub(crate) fn drain_midi_events(&mut self) -> alloc::vec::Drain<'_, (usize, MidiEvent)> {
self.pending_midi_events.drain(..)
}
pub(crate) fn process_tick(&mut self, current_tick: usize) {
let pool = &mut self.pool;
for ch in &mut self.channel {
ch.tick(current_tick, pool);
}
if self.row_has_global_volume_slide && !self.volume_slide_fine {
self.global_volume = self.global_volume.with_tremolo(self.volume_slide_speed);
}
}
pub(crate) fn mix(&mut self, per_channel_out: Option<&mut [(i16, i16)]>) -> (i32, i32) {
let mut left: i32 = 0;
let mut right: i32 = 0;
let pool = &mut self.pool;
match per_channel_out {
None => {
for ch in &mut self.channel {
if let Some((l, r)) = ch.next_sample(pool) {
if !ch.is_muted() {
l.accumulate_into(&mut left);
r.accumulate_into(&mut right);
}
}
}
}
Some(buf) => {
for (idx, ch) in self.channel.iter_mut().enumerate() {
let val = match ch.next_sample(pool) {
Some((l, r)) if !ch.is_muted() => {
l.accumulate_into(&mut left);
r.accumulate_into(&mut right);
(l.as_q15_i16(), r.as_q15_i16())
}
_ => (0, 0),
};
if idx < buf.len() {
buf[idx] = val;
}
}
}
}
(left, right)
}
pub(crate) fn apply_final_gain(&self, sum: (i32, i32)) -> (i16, i16) {
let gv = self.global_volume.as_q15_i32() as i64; let amp = self.amplification.as_q4_12_i32() as i64;
let g_q27 = gv.max(0) * amp.max(0);
let bias: i64 = 1 << 26;
let apply = |s: i32| -> i16 {
let prod: i64 = (s as i64).wrapping_mul(g_q27);
let r = if prod >= 0 {
(prod + bias) >> 27
} else {
-(((-prod) + bias) >> 27)
};
r.clamp(i16::MIN as i64, i16::MAX as i64) as i16
};
(apply(sum.0), apply(sum.1))
}
#[inline]
pub(crate) fn saturate_to_i16(sum: (i32, i32)) -> (i16, i16) {
(
sum.0.clamp(i16::MIN as i32, i16::MAX as i32) as i16,
sum.1.clamp(i16::MIN as i32, i16::MAX as i32) as i16,
)
}
pub(crate) fn samples_from_channels(&mut self) -> Vec<(i16, i16)> {
let pool = &mut self.pool;
self.channel
.iter_mut()
.map(|ch| match ch.next_sample(pool) {
Some((l, r)) if !ch.is_muted() => (l.as_q15_i16(), r.as_q15_i16()),
_ => (0, 0),
})
.collect()
}
}