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
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>) {
for _ in 0..samples {
let sample = 0.25 * iter.next().unwrap();
pub fn fill_with(&mut self, buffer: &[f32]) {
for sample in buffer.iter() {
let sample = 0.25 * *sample;
self.buffer.insert(sample);
self.buffer.insert(sample);
if self.buffer.is_full() {
@ -156,8 +156,8 @@ impl Audio for AudioSource {
self.sample_rate
}
fn write_samples(&mut self, samples: usize, iter: &mut dyn Iterator<Item=f32>) {
self.fill_with(samples, iter);
fn write_samples(&mut self, buffer: &[f32]) {
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("-x, --speed=[] 'Adjust the speed of the simulation'")
.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 {
@ -152,7 +153,8 @@ pub struct MiniFrontend {
pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub audio: AudioOutput,
pub audio: Option<AudioOutput>,
pub mixer: HostData<AudioMixer>,
}
impl MiniFrontend {
@ -163,7 +165,8 @@ impl MiniFrontend {
window,
controller,
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());
}
if matches.occurrences_of("disable-audio") <= 0 {
self.audio = Some(AudioOutput::create_audio_output(self.mixer.clone()));
}
let mut options = minifb::WindowOptions::default();
options.scale = match matches.value_of("scale").map(|s| u8::from_str_radix(s, 10).unwrap()) {
Some(1) => minifb::Scale::X1,

View File

@ -37,7 +37,6 @@ pub trait Tty {
pub trait WindowUpdater: Send {
fn get_size(&mut self) -> (u32, 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 {
@ -50,8 +49,7 @@ pub trait KeyboardUpdater: Send {
pub trait Audio {
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 {

View File

@ -199,7 +199,7 @@ impl Addressable for GenesisControllers {
impl Steppable for GenesisControllers {
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;
if self.reset_timer >= 1_500_000 {

View File

@ -4,28 +4,56 @@ use std::num::NonZeroU8;
use crate::error::Error;
use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
use crate::host::audio::{SquareWave};
use crate::host::audio::{SineWave};
use crate::host::traits::{Host, Audio};
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)]
pub struct Operator {
pub wave: SquareWave,
pub wave: SineWave,
pub multiplier: f32,
}
impl Operator {
pub fn new(sample_rate: usize) -> 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)]
pub struct Channel {
pub operators: Vec<Operator>,
pub on: u8,
pub base_frequency: f32,
pub algorithm: OperatorAlgorithm,
}
impl Channel {
@ -33,6 +61,66 @@ impl Channel {
Self {
operators: vec![Operator::new(sample_rate); 4],
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 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 channel_frequencies: [(u8, u16); CHANNELS],
}
impl Ym2612 {
@ -52,12 +142,14 @@ impl Ym2612 {
let sample_rate = source.samples_per_second();
Ok(Self {
source,
selected_reg: None,
selected_reg_0: None,
selected_reg_1: None,
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 {
0x28 => {
let ch = (data as usize) & 0x07;
@ -65,13 +157,55 @@ impl Ym2612 {
println!("Note: {}: {:x}", ch, self.channels[ch].on);
},
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),
}
}
}
#[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 {
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
// 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();
// 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
}
}
@ -116,12 +270,19 @@ impl Addressable for Ym2612 {
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
0 => {
self.selected_reg = NonZeroU8::new(data[0]);
self.selected_reg_0 = NonZeroU8::new(data[0]);
},
1 => {
match self.selected_reg {
None => {},
Some(reg) => self.set_register(0, reg.get() as usize, data[0]),
if let Some(reg) = self.selected_reg_0 {
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
a fixed piece of data..., the trick is what function are the most common. You can use generics