Added frequency setting to ym2612

This commit is contained in:
transistor 2022-01-16 21:42:07 -08:00
parent 1413225463
commit 8cf8c07082
7 changed files with 195 additions and 23 deletions

View File

@ -129,3 +129,7 @@ number by the milliseconds per frame, increasing or decreasing the gameplay
clock relative to the frontend's update loop. Setting it to 0.5 slows the game clock relative to the frontend's update loop. Setting it to 0.5 slows the game
down to half speed and setting it to 2 doubles the speed. down to half speed and setting it to 2 doubles the speed.
The `-a` or `--disable-audio` option will prevent the audio device from being
created, so no audio will be played (although it will still be simulated by any
devices that simulate it).

View File

@ -122,9 +122,9 @@ impl AudioSource {
} }
} }
pub fn fill_with(&mut self, samples: usize, iter: &mut dyn Iterator<Item=f32>) { pub fn fill_with(&mut self, buffer: &[f32]) {
for _ in 0..samples { for sample in buffer.iter() {
let sample = 0.25 * iter.next().unwrap(); let sample = 0.25 * *sample;
self.buffer.insert(sample); self.buffer.insert(sample);
self.buffer.insert(sample); self.buffer.insert(sample);
if self.buffer.is_full() { if self.buffer.is_full() {
@ -156,8 +156,8 @@ impl Audio for AudioSource {
self.sample_rate self.sample_rate
} }
fn write_samples(&mut self, samples: usize, iter: &mut dyn Iterator<Item=f32>) { fn write_samples(&mut self, buffer: &[f32]) {
self.fill_with(samples, iter); self.fill_with(buffer);
} }
} }

View File

@ -32,6 +32,7 @@ pub fn new(name: &str) -> App {
.arg("-t, --threaded 'Run the simulation in a separate thread'") .arg("-t, --threaded 'Run the simulation in a separate thread'")
.arg("-x, --speed=[] 'Adjust the speed of the simulation'") .arg("-x, --speed=[] 'Adjust the speed of the simulation'")
.arg("-d, --debugger 'Start the debugger before running machine'") .arg("-d, --debugger 'Start the debugger before running machine'")
.arg("-a, --disable-audio 'Disable audio output'")
} }
pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static { pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
@ -152,7 +153,8 @@ pub struct MiniFrontend {
pub window: Option<Box<dyn WindowUpdater>>, pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>, pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>, pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub audio: AudioOutput, pub audio: Option<AudioOutput>,
pub mixer: HostData<AudioMixer>,
} }
impl MiniFrontend { impl MiniFrontend {
@ -163,7 +165,8 @@ impl MiniFrontend {
window, window,
controller, controller,
keyboard, keyboard,
audio: AudioOutput::create_audio_output(mixer), audio: None,
mixer: mixer,
} }
} }
@ -172,6 +175,10 @@ impl MiniFrontend {
system.as_mut().map(|system| system.enable_debugging()); system.as_mut().map(|system| system.enable_debugging());
} }
if matches.occurrences_of("disable-audio") <= 0 {
self.audio = Some(AudioOutput::create_audio_output(self.mixer.clone()));
}
let mut options = minifb::WindowOptions::default(); let mut options = minifb::WindowOptions::default();
options.scale = match matches.value_of("scale").map(|s| u8::from_str_radix(s, 10).unwrap()) { options.scale = match matches.value_of("scale").map(|s| u8::from_str_radix(s, 10).unwrap()) {
Some(1) => minifb::Scale::X1, Some(1) => minifb::Scale::X1,

View File

@ -37,7 +37,6 @@ pub trait Tty {
pub trait WindowUpdater: Send { pub trait WindowUpdater: Send {
fn get_size(&mut self) -> (u32, u32); fn get_size(&mut self) -> (u32, u32);
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]); fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
//fn update_frame(&mut self, draw_buffer: &mut dyn FnMut(u32, u32, &[u32]));
} }
pub trait ControllerUpdater: Send { pub trait ControllerUpdater: Send {
@ -50,8 +49,7 @@ pub trait KeyboardUpdater: Send {
pub trait Audio { pub trait Audio {
fn samples_per_second(&self) -> usize; fn samples_per_second(&self) -> usize;
fn write_samples(&mut self, samples: usize, iter: &mut Iterator<Item=f32>); fn write_samples(&mut self, buffer: &[f32]);
//fn write_samples(&mut self, buffer: &[f32]);
} }
pub trait BlitableSurface { pub trait BlitableSurface {

View File

@ -199,7 +199,7 @@ impl Addressable for GenesisControllers {
impl Steppable for GenesisControllers { impl Steppable for GenesisControllers {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> { fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let duration = 100_00; // Update every 100us let duration = 100_000; // Update every 100us
self.reset_timer += duration; self.reset_timer += duration;
if self.reset_timer >= 1_500_000 { if self.reset_timer >= 1_500_000 {

View File

@ -4,28 +4,56 @@ use std::num::NonZeroU8;
use crate::error::Error; use crate::error::Error;
use crate::system::System; use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable}; use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
use crate::host::audio::{SquareWave}; use crate::host::audio::{SineWave};
use crate::host::traits::{Host, Audio}; use crate::host::traits::{Host, Audio};
const DEV_NAME: &'static str = "ym2612"; const DEV_NAME: &'static str = "ym2612";
const CHANNELS: usize = 8;
#[derive(Copy, Clone, Debug)]
pub enum OperatorAlgorithm {
A0,
A1,
A2,
A3,
A4,
A5,
A6,
A7,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Operator { pub struct Operator {
pub wave: SquareWave, pub wave: SineWave,
pub multiplier: f32,
} }
impl Operator { impl Operator {
pub fn new(sample_rate: usize) -> Self { pub fn new(sample_rate: usize) -> Self {
Self { Self {
wave: SquareWave::new(400.0, sample_rate) wave: SineWave::new(400.0, sample_rate),
multiplier: 1.0,
} }
} }
pub fn set_frequency(&mut self, frequency: f32) {
self.wave.frequency = frequency * self.multiplier;
}
pub fn set_multiplier(&mut self, frequency: f32, multiplier: f32) {
self.multiplier = multiplier;
self.set_frequency(frequency);
}
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Channel { pub struct Channel {
pub operators: Vec<Operator>, pub operators: Vec<Operator>,
pub on: u8, pub on: u8,
pub base_frequency: f32,
pub algorithm: OperatorAlgorithm,
} }
impl Channel { impl Channel {
@ -33,6 +61,66 @@ impl Channel {
Self { Self {
operators: vec![Operator::new(sample_rate); 4], operators: vec![Operator::new(sample_rate); 4],
on: 0, on: 0,
base_frequency: 0.0,
algorithm: OperatorAlgorithm::A0,
}
}
pub fn set_frequency(&mut self, frequency: f32) {
self.base_frequency = frequency;
for operator in self.operators.iter_mut() {
operator.set_frequency(frequency);
}
}
pub fn get_sample(&mut self) -> f32 {
match self.algorithm {
OperatorAlgorithm::A0 => {
let mut sample = 0.0;
sample *= self.operators[0].wave.next().unwrap();
sample *= self.operators[1].wave.next().unwrap();
sample *= self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
sample
},
OperatorAlgorithm::A1 => {
let mut sample = self.operators[0].wave.next().unwrap() + self.operators[1].wave.next().unwrap();
sample *= self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
sample
},
OperatorAlgorithm::A2 => {
let mut sample = self.operators[0].wave.next().unwrap() + (self.operators[1].wave.next().unwrap() * self.operators[2].wave.next().unwrap());
sample *= self.operators[3].wave.next().unwrap();
sample
},
OperatorAlgorithm::A3 => {
let mut sample = (self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap()) + self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
sample
},
OperatorAlgorithm::A4 => {
let sample1 = self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap();
let sample2 = self.operators[2].wave.next().unwrap() * self.operators[3].wave.next().unwrap();
sample1 + sample2
},
OperatorAlgorithm::A5 => {
let sample1 = self.operators[0].wave.next().unwrap();
let sample2 = self.operators[1].wave.next().unwrap() + self.operators[2].wave.next().unwrap() + self.operators[3].wave.next().unwrap();
sample1 * sample2
},
OperatorAlgorithm::A6 => {
let sample1 = self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap();
sample1 + self.operators[2].wave.next().unwrap() + self.operators[3].wave.next().unwrap()
},
OperatorAlgorithm::A7 => {
let mut sample = 0.0;
sample += self.operators[0].wave.next().unwrap();
sample += self.operators[1].wave.next().unwrap();
sample += self.operators[2].wave.next().unwrap();
sample += self.operators[3].wave.next().unwrap();
sample / 4.0
},
} }
} }
} }
@ -41,9 +129,11 @@ impl Channel {
pub struct Ym2612 { pub struct Ym2612 {
pub source: Box<dyn Audio>, pub source: Box<dyn Audio>,
pub selected_reg: Option<NonZeroU8>, pub selected_reg_0: Option<NonZeroU8>,
pub selected_reg_1: Option<NonZeroU8>,
pub channels: Vec<Channel>, pub channels: Vec<Channel>,
pub channel_frequencies: [(u8, u16); CHANNELS],
} }
impl Ym2612 { impl Ym2612 {
@ -52,12 +142,14 @@ impl Ym2612 {
let sample_rate = source.samples_per_second(); let sample_rate = source.samples_per_second();
Ok(Self { Ok(Self {
source, source,
selected_reg: None, selected_reg_0: None,
selected_reg_1: None,
channels: vec![Channel::new(sample_rate); 8], channels: vec![Channel::new(sample_rate); 8],
channel_frequencies: [(0, 0); CHANNELS],
}) })
} }
pub fn set_register(&mut self, bank: u8, reg: usize, data: u8) { pub fn set_register(&mut self, bank: usize, reg: usize, data: u8) {
match reg { match reg {
0x28 => { 0x28 => {
let ch = (data as usize) & 0x07; let ch = (data as usize) & 0x07;
@ -65,13 +157,55 @@ impl Ym2612 {
println!("Note: {}: {:x}", ch, self.channels[ch].on); println!("Note: {}: {:x}", ch, self.channels[ch].on);
}, },
0x30 => { 0x30 => {
let _op = if bank == 0 { 0 } else { 3 }; let (ch, op) = get_ch_op(bank, reg);
} let multiplier = if data == 0 { 0.5 } else { (data & 0x0F) as f32 };
let frequency = self.channels[ch].base_frequency;
self.channels[ch].operators[op].set_multiplier(frequency, multiplier)
},
0xA4 | 0xA5 | 0xA6 => {
let ch = (reg & 0x07) - 4 + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF) | ((data as u16) & 0x07) << 8;
self.channel_frequencies[ch].0 = (data & 0x1C) >> 3;
},
0xA0 | 0xA1 | 0xA2 => {
let ch = (reg & 0x07) + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF00) | data as u16;
let frequency = fnumber_to_frequency(self.channel_frequencies[ch]);
self.channels[ch].set_frequency(frequency);
},
0xB0 | 0xB1 | 0xB2 => {
let ch = (reg & 0x07) + (bank * 3);
self.channels[ch].algorithm = match data & 0x07 {
0 => OperatorAlgorithm::A0,
1 => OperatorAlgorithm::A1,
2 => OperatorAlgorithm::A2,
3 => OperatorAlgorithm::A3,
4 => OperatorAlgorithm::A4,
5 => OperatorAlgorithm::A5,
6 => OperatorAlgorithm::A6,
7 => OperatorAlgorithm::A7,
_ => OperatorAlgorithm::A0,
};
},
_ => warning!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data), _ => warning!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data),
} }
} }
} }
#[inline(always)]
pub fn fnumber_to_frequency(fnumber: (u8, u16)) -> f32 {
(fnumber.1 as f32 * 0.0264) * (2 as u32).pow(fnumber.0 as u32) as f32
}
#[inline(always)]
pub fn get_ch_op(bank: usize, reg: usize) -> (usize, usize) {
let ch = (reg & 0x03) + (bank * 3);
let op = (reg & 0xC0) >> 2;
(ch, op)
}
impl Steppable for Ym2612 { impl Steppable for Ym2612 {
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> { fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
// TODO since you expect this step function to be called every 1ms of simulated time // TODO since you expect this step function to be called every 1ms of simulated time
@ -89,6 +223,26 @@ impl Steppable for Ym2612 {
// let rate = self.source.samples_per_second(); // let rate = self.source.samples_per_second();
// self.source.write_samples(rate / 1000, &mut self.sine); // self.source.write_samples(rate / 1000, &mut self.sine);
//} //}
let rate = self.source.samples_per_second() / 1000;
let mut buffer = vec![0.0; rate];
for i in 0..rate {
let mut sample = 0.0;
let mut count = 0;
for ch in 0..7 {
if self.channels[ch].on != 0 {
sample += self.channels[ch].get_sample();
count += 1;
}
}
if count > 0 {
buffer[i] = sample / count as f32;
}
}
self.source.write_samples(&buffer);
Ok(1_000_000) // Every 1ms of simulated time Ok(1_000_000) // Every 1ms of simulated time
} }
} }
@ -116,12 +270,19 @@ impl Addressable for Ym2612 {
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr { match addr {
0 => { 0 => {
self.selected_reg = NonZeroU8::new(data[0]); self.selected_reg_0 = NonZeroU8::new(data[0]);
}, },
1 => { 1 => {
match self.selected_reg { if let Some(reg) = self.selected_reg_0 {
None => {}, self.set_register(0, reg.get() as usize, data[0]);
Some(reg) => self.set_register(0, reg.get() as usize, data[0]), }
},
2 => {
self.selected_reg_1 = NonZeroU8::new(data[0]);
},
3 => {
if let Some(reg) = self.selected_reg_1 {
self.set_register(1, reg.get() as usize, data[0]);
} }
}, },
_ => { _ => {

View File

@ -1,4 +1,6 @@
* make it possible to compile without audio support (minifb frontend requires it atm)
* I need some better function for dealing with memory, like a function that copies data with a loop, or allows offset reading of * I need some better function for dealing with memory, like a function that copies data with a loop, or allows offset reading of
a fixed piece of data..., the trick is what function are the most common. You can use generics a fixed piece of data..., the trick is what function are the most common. You can use generics