use super::wave_program::{self, ProgramState};
use super::{
shift_semitones_down, shift_semitones_up, SidFx, CTRL_GATE, SEMI_DEN, SEMI_NUM, VIB_GATE_FRAMES,
};
#[inline]
fn shift_semitones_signed(base: u32, semis: i8) -> u32 {
if semis >= 0 {
shift_semitones_up(base, semis as u8)
} else {
shift_semitones_down(base, (-(semis as i16)) as u8)
}
}
pub(crate) struct FxState {
pub base_milli_hz: u32,
pub gated: bool,
pub note_frame: u32,
pub arp_phase: u32,
pub drum_hi: u8,
pub gate_off_ctrl: u8,
pub skydive_semis: u8,
pub interp_ctr: u8,
pub interp_up: bool,
pub vib_delay: u8,
pub prog: ProgramState,
}
#[inline]
pub(crate) fn freq_reg(milli_hz: u32, clock_hz: u32) -> u16 {
(((milli_hz as u64) << 24) / (clock_hz as u64 * 1000)).min(65535) as u16
}
pub(crate) fn advance_pingpong(ctr: &mut u8, up: &mut bool, depth: u8, hold_top: bool) {
if *up {
if *ctr >= depth {
*up = false;
if !hold_top {
*ctr = depth.saturating_sub(1);
}
} else {
*ctr += 1;
}
} else if *ctr == 0 {
*up = true;
*ctr = 1;
} else {
*ctr -= 1;
}
}
fn sounding_note(fx: &SidFx, s: &FxState, frame: u32, base: u32, skydive_semis: u8) -> u32 {
if fx.arp_len == 0 {
return base;
}
let phase = if fx.arp_per_note { s.arp_phase } else { frame };
let off = fx.arp_steps[(phase % fx.arp_len.min(3) as u32) as usize];
let from = if fx.arp_follow_climb && fx.skydive_climb && skydive_semis != 0 {
shift_semitones_up(base, skydive_semis)
} else {
base
};
shift_semitones_signed(from, off)
}
pub(crate) struct Modulated {
pub reg: u16,
pub ctrl: Option<u8>,
pub freq_hi_only: bool,
pub ctrl_masked: bool,
}
pub(crate) fn modulate(
s: &mut FxState,
fx: &SidFx,
frame: u32,
speed: u8,
clock_hz: u32,
) -> Modulated {
let base = s.base_milli_hz;
let gated = s.gated;
let note_frame = s.note_frame;
let drum_hi0 = s.drum_hi;
let gate_off_ctrl = s.gate_off_ctrl;
let skydive_semis = s.skydive_semis;
let mut reg = freq_reg(base, clock_hz);
let vib_ok = !fx.vib_len_gate || (gated && note_frame >= VIB_GATE_FRAMES);
if fx.vibrato && fx.vib_depth != 0 && vib_ok && fx.vib_len_gate {
let depth = fx.vib_depth as i64;
let period = (2 * depth) as u32;
let p = (frame % period) as i64;
let phase = if p <= depth { p } else { period as i64 - p };
let semitone = (base as i64) * SEMI_NUM / SEMI_DEN;
let step = semitone >> (fx.vib_div as u32).min(63);
let delta = (phase - depth / 2) * step;
reg = freq_reg((base as i64 + delta).max(0) as u32, clock_hz);
} else if fx.vibrato && fx.vib_depth != 0 && vib_ok {
let mut osc = (frame & 7) as i64;
if osc > 3 {
osc ^= 7;
}
if fx.vib_tempvdif_reg != 0 {
let base_reg = freq_reg(base, clock_hz) as i64;
reg = (base_reg + fx.vib_tempvdif_reg as i64 * osc).clamp(0, 0xFFFF) as u16;
} else {
let shift = ((fx.vib_depth as u32) >> 2).min(8) + 1;
let semitone = (base as i64) * SEMI_NUM / SEMI_DEN;
let delta = (semitone >> shift) * osc;
reg = freq_reg((base as i64 + delta).max(0) as u32, clock_hz);
}
}
let mut ctrl_override = None;
let mut new_drum_hi = drum_hi0;
if fx.drum && gated && drum_hi0 != 0 {
if note_frame < speed as u32 {
if !fx.drum_no_freq_slide {
reg = (reg & 0x00FF) | ((drum_hi0 as u16) << 8);
}
ctrl_override = Some(0x80);
} else {
if !fx.drum_no_freq_slide {
new_drum_hi = drum_hi0 - 1;
reg = (reg & 0x00FF) | ((drum_hi0 as u16) << 8);
}
ctrl_override = Some(if gate_off_ctrl == 0 {
0x80
} else {
gate_off_ctrl
});
}
}
if fx.skydive_add != 0
&& (gated || fx.skydive_when == 0)
&& (frame & 1 != 0)
&& new_drum_hi != 0
{
reg = (reg & 0x00FF) | ((new_drum_hi as u16) << 8);
let delta = (fx.skydive_add as i32) >> 8;
new_drum_hi = (new_drum_hi as i32 + delta).clamp(0, 255) as u8;
}
if fx.skydive_climb && skydive_semis != 0 {
reg = freq_reg(shift_semitones_up(base, skydive_semis), clock_hz);
}
let mut interp_applied = false;
if s.vib_delay > 0 {
s.vib_delay -= 1;
} else if fx.interp_vib && fx.interp_half_depth > 0 {
let centre = if fx.interp_flat != 0 {
sounding_note(fx, s, frame, base, skydive_semis)
} else {
base
};
interp_applied = fx.interp_flat != 0;
let base_reg = freq_reg(centre, clock_hz) as i32;
let down1 = freq_reg(shift_semitones_down(centre, 1), clock_hz) as i32;
let step = (base_reg - down1) >> fx.interp_shift.min(31);
let half = (fx.interp_half_depth >> 1) as i32;
let depth = fx.interp_half_depth;
if fx.interp_flat != 0 {
advance_pingpong(&mut s.interp_ctr, &mut s.interp_up, depth, true);
}
let ctr = if note_frame >= fx.interp_flat as u32 {
s.interp_ctr as i32
} else {
0
};
reg = (base_reg - half * step + ctr * step).clamp(0, 0xFFFF) as u16;
if fx.interp_flat != 0 {
} else if s.interp_up {
if s.interp_ctr >= depth {
s.interp_up = false;
s.interp_ctr = depth.saturating_sub(1);
} else {
s.interp_ctr += 1;
}
} else if s.interp_ctr == 0 {
s.interp_up = true;
s.interp_ctr = 1;
} else {
s.interp_ctr -= 1;
}
}
if fx.arp {
let n = if frame & 1 == 0 {
base
} else {
base.saturating_mul(2)
};
reg = freq_reg(n, clock_hz);
}
if fx.arp_len != 0 && !interp_applied {
let phase = if fx.arp_per_note { s.arp_phase } else { frame };
let off = fx.arp_steps[(phase % fx.arp_len.min(3) as u32) as usize];
let from = if fx.arp_follow_climb && fx.skydive_climb && skydive_semis != 0 {
shift_semitones_up(base, skydive_semis)
} else {
base
};
reg = freq_reg(shift_semitones_signed(from, off), clock_hz);
}
if fx.arp2_reg != 0 && note_frame & 1 == 0 {
reg = fx.arp2_reg;
}
if fx.wave_alt_ctrl != 0 && gated {
ctrl_override = Some(if note_frame & 1 == 1 {
gate_off_ctrl | CTRL_GATE
} else {
fx.wave_alt_ctrl
});
}
let mut freq_hi_only = false;
let mut ctrl_masked = false;
if let Some(out) = wave_program::step(&fx.program, &mut s.prog) {
ctrl_override = Some(out.ctrl);
ctrl_masked = out.masked;
match out.pitch {
wave_program::ProgramPitch::FixedHi(hi) => {
reg = (reg & 0x00FF) | ((hi as u16) << 8);
freq_hi_only = true;
}
wave_program::ProgramPitch::Bend => {
reg = (reg as i32 - s.prog.bend_acc as i32).clamp(0, 0xFFFF) as u16;
}
wave_program::ProgramPitch::Released => {}
}
}
if new_drum_hi != drum_hi0 {
s.drum_hi = new_drum_hi;
}
Modulated {
reg,
ctrl: ctrl_override,
freq_hi_only,
ctrl_masked,
}
}