use crate::chip::Ym2149;
use ym2149_common::Ym2149Backend;
const DEFAULT_SAMPLE_RATE: u32 = 44_100;
#[derive(Debug)]
pub struct PsgBank {
chips: Vec<Ym2149>,
frequencies: Vec<u32>,
scratch: Vec<f32>,
}
impl PsgBank {
pub fn new(count: usize, frequency: u32) -> Self {
assert!(count > 0, "PSG bank must have at least one chip");
let chips = (0..count)
.map(|_| Ym2149::with_clocks(frequency, DEFAULT_SAMPLE_RATE))
.collect();
let frequencies = vec![frequency; count];
Self {
chips,
frequencies,
scratch: Vec::new(),
}
}
pub fn new_with_frequencies(frequencies: Vec<u32>) -> Self {
assert!(
!frequencies.is_empty(),
"PSG bank must have at least one chip"
);
let chips = frequencies
.iter()
.map(|&freq| Ym2149::with_clocks(freq, DEFAULT_SAMPLE_RATE))
.collect();
Self {
chips,
frequencies,
scratch: Vec::new(),
}
}
#[inline]
pub fn psg_count(&self) -> usize {
self.chips.len()
}
#[inline]
pub fn channel_count(&self) -> usize {
self.psg_count() * 3
}
#[inline]
pub fn get_frequency(&self, psg_index: usize) -> u32 {
self.frequencies[psg_index]
}
#[inline]
pub fn get_chip(&self, psg_index: usize) -> &Ym2149 {
&self.chips[psg_index]
}
#[inline]
pub fn get_chip_mut(&mut self, psg_index: usize) -> &mut Ym2149 {
&mut self.chips[psg_index]
}
#[inline]
pub fn write_register(&mut self, psg_index: usize, register: u8, value: u8) {
self.chips[psg_index].write_register(register, value);
}
#[inline]
pub fn read_register(&self, psg_index: usize, register: u8) -> u8 {
self.chips[psg_index].read_register(register)
}
pub fn generate_samples_interleaved(&mut self, buffer: &mut [f32]) {
let psg_count = self.chips.len();
if psg_count == 0 {
buffer.fill(0.0);
return;
}
self.chips[0].generate_samples_into(buffer);
if psg_count > 1 {
if self.scratch.len() < buffer.len() {
self.scratch.resize(buffer.len(), 0.0);
}
let scratch = &mut self.scratch[..buffer.len()];
for chip in &mut self.chips[1..] {
chip.generate_samples_into(scratch);
for (out, sample) in buffer.iter_mut().zip(scratch.iter()) {
*out += *sample;
}
}
}
let scale = 1.0 / psg_count as f32;
for sample in buffer.iter_mut() {
*sample *= scale;
}
}
pub fn generate_samples_separate(&mut self, buffers: &mut [&mut [f32]]) {
assert_eq!(
buffers.len(),
self.psg_count(),
"Buffer count must match PSG count"
);
for (chip, buffer) in self.chips.iter_mut().zip(buffers.iter_mut()) {
chip.generate_samples_into(buffer);
}
}
pub fn reset(&mut self) {
for chip in &mut self.chips {
chip.reset();
}
}
pub fn dump_all_registers(&self) -> Vec<[u8; 16]> {
self.chips
.iter()
.map(|chip| chip.dump_registers())
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_psg_bank_creation() {
let bank = PsgBank::new(2, 2_000_000);
assert_eq!(bank.psg_count(), 2);
assert_eq!(bank.channel_count(), 6);
assert_eq!(bank.get_frequency(0), 2_000_000);
assert_eq!(bank.get_frequency(1), 2_000_000);
}
#[test]
fn test_psg_bank_mixed_frequencies() {
let bank = PsgBank::new_with_frequencies(vec![1_000_000, 2_000_000, 2_000_000]);
assert_eq!(bank.psg_count(), 3);
assert_eq!(bank.channel_count(), 9);
assert_eq!(bank.get_frequency(0), 1_000_000);
assert_eq!(bank.get_frequency(1), 2_000_000);
assert_eq!(bank.get_frequency(2), 2_000_000);
}
#[test]
fn test_write_read_register() {
let mut bank = PsgBank::new(2, 2_000_000);
bank.write_register(0, 0x08, 0x0F);
assert_eq!(bank.read_register(0, 0x08), 0x0F);
bank.write_register(1, 0x08, 0x0A);
assert_eq!(bank.read_register(1, 0x08), 0x0A);
assert_eq!(bank.read_register(0, 0x08), 0x0F);
}
#[test]
fn test_generate_samples_interleaved() {
let mut bank = PsgBank::new(2, 2_000_000);
for i in 0..2 {
bank.write_register(i, 0x07, 0x3E); bank.write_register(i, 0x00, 0x1C); bank.write_register(i, 0x01, 0x01); bank.write_register(i, 0x08, 0x0F); }
let mut buffer = vec![0.0f32; 882];
bank.generate_samples_interleaved(&mut buffer);
let has_signal = buffer.iter().any(|&s| s.abs() > 0.01);
assert!(has_signal, "Expected non-zero samples");
}
#[test]
fn test_generate_samples_separate() {
let mut bank = PsgBank::new(2, 2_000_000);
bank.write_register(0, 0x07, 0x3E);
bank.write_register(0, 0x00, 0x1C);
bank.write_register(0, 0x08, 0x0F);
bank.write_register(1, 0x07, 0x3E);
bank.write_register(1, 0x00, 0x2A); bank.write_register(1, 0x08, 0x0F);
let mut buffer0 = vec![0.0f32; 882];
let mut buffer1 = vec![0.0f32; 882];
let mut buffers = vec![&mut buffer0[..], &mut buffer1[..]];
bank.generate_samples_separate(&mut buffers);
assert!(buffer0.iter().any(|&s| s.abs() > 0.01));
assert!(buffer1.iter().any(|&s| s.abs() > 0.01));
}
#[test]
fn test_reset() {
let mut bank = PsgBank::new(2, 2_000_000);
bank.write_register(0, 0x08, 0x0F);
bank.write_register(1, 0x08, 0x0F);
bank.reset();
assert_eq!(bank.read_register(0, 0x08), 0x00);
assert_eq!(bank.read_register(1, 0x08), 0x00);
}
#[test]
#[should_panic(expected = "PSG bank must have at least one chip")]
fn test_empty_bank_panics() {
PsgBank::new(0, 2_000_000);
}
#[test]
#[should_panic(expected = "PSG bank must have at least one chip")]
fn test_empty_frequencies_panics() {
PsgBank::new_with_frequencies(vec![]);
}
}