use crate::generators::NUM_CHANNELS;
use crate::tables::{MASKS, YM2149_LOG_LEVELS};
pub const MAX_LEVEL: u32 = 10922;
const DIGIDRUM_MAX_AMPLITUDE: f32 = 4.0;
#[derive(Clone, Debug, Default)]
pub struct MixerConfig {
tone_mask: u32,
noise_mask: u32,
}
impl MixerConfig {
#[inline]
pub fn set_from_register(&mut self, value: u8) {
self.tone_mask = MASKS[(value & 0x07) as usize];
self.noise_mask = MASKS[((value >> 3) & 0x07) as usize];
}
#[inline]
pub fn compute_gate_mask(&self, tone_edges: u32, noise_mask: u32) -> u32 {
(tone_edges | self.tone_mask) & (noise_mask | self.noise_mask)
}
}
#[derive(Clone, Debug, Default)]
pub struct ChannelState {
pub muted: bool,
pub drum_override: Option<f32>,
pub last_output: f32,
pub last_amplitude: f32,
}
#[derive(Clone, Debug, Default)]
pub struct Mixer {
pub config: MixerConfig,
pub channels: [ChannelState; NUM_CHANNELS],
}
impl Mixer {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn compute_levels(
&self,
volume_regs: [u8; 3],
envelope_level: u32,
gate_mask: u32,
) -> (u32, u32) {
let mut levels: u32 = 0;
for (i, &vol_reg) in volume_regs.iter().enumerate() {
let level = if (vol_reg & 0x10) != 0 {
envelope_level
} else {
(vol_reg as u32) << 1
};
levels |= level << (i * 5);
}
(levels & gate_mask, levels)
}
#[inline]
pub fn compute_channel_output(
&mut self,
channel: usize,
level_index: u32,
ungated_level_index: u32,
half_amplitude: bool,
) -> u32 {
let state = &mut self.channels[channel];
let output = if state.muted {
0
} else if let Some(drum_sample) = state.drum_override {
((drum_sample / DIGIDRUM_MAX_AMPLITUDE * MAX_LEVEL as f32) as u32).min(MAX_LEVEL)
} else {
let base_level = YM2149_LOG_LEVELS[level_index as usize];
if half_amplitude {
base_level >> 1
} else {
base_level
}
};
if state.muted {
state.last_output = 0.0;
state.last_amplitude = 0.0;
} else if state.drum_override.is_some() {
state.last_output = (output as f32 / MAX_LEVEL as f32) * 2.0 - 1.0;
state.last_amplitude = 1.0;
} else {
let ungated_output = YM2149_LOG_LEVELS[ungated_level_index as usize];
let ungated_output = if half_amplitude {
ungated_output >> 1
} else {
ungated_output
};
let amplitude = ungated_output as f32 / MAX_LEVEL as f32;
state.last_amplitude = amplitude;
if ungated_level_index > 0 {
let gate_on = level_index == ungated_level_index;
state.last_output = if gate_on { amplitude } else { -amplitude };
} else {
state.last_output = 0.0;
}
}
output
}
#[inline]
pub fn channel_outputs(&self) -> (f32, f32, f32) {
(
self.channels[0].last_output,
self.channels[1].last_output,
self.channels[2].last_output,
)
}
#[inline]
pub fn set_mute(&mut self, channel: usize, muted: bool) {
if channel < NUM_CHANNELS {
self.channels[channel].muted = muted;
}
}
#[inline]
pub fn is_muted(&self, channel: usize) -> bool {
self.channels.get(channel).is_some_and(|c| c.muted)
}
#[inline]
pub fn set_drum_override(&mut self, channel: usize, sample: Option<f32>) {
if channel < NUM_CHANNELS {
self.channels[channel].drum_override = sample;
}
}
pub fn reset(&mut self) {
self.config = MixerConfig::default();
for channel in &mut self.channels {
channel.drum_override = None;
channel.last_output = 0.0;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mixer_config() {
let mut config = MixerConfig::default();
config.set_from_register(0x38);
assert_eq!(config.tone_mask, 0);
config.set_from_register(0x3F);
assert_eq!(config.tone_mask, MASKS[7]);
}
#[test]
fn test_channel_mute() {
let mut mixer = Mixer::new();
assert!(!mixer.is_muted(0));
mixer.set_mute(0, true);
assert!(mixer.is_muted(0));
assert!(!mixer.is_muted(1));
}
#[test]
fn test_drum_override() {
let mut mixer = Mixer::new();
mixer.set_drum_override(0, Some(128.0));
let output = mixer.compute_channel_output(0, 0, 0, false);
assert!(output > 0);
mixer.set_drum_override(0, None);
let output_normal = mixer.compute_channel_output(0, 0, 0, false);
assert!(output_normal < output);
}
}