mirror of
https://github.com/transistorfet/moa.git
synced 2025-03-04 08:28:56 +00:00
Added ym2612 total level and start of envelope implementation
This commit is contained in:
parent
4dc4b6ad22
commit
10ef61784a
@ -158,3 +158,8 @@ impl Iterator for SkewedSquareWave {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn db_to_gain(db: f32) -> f32 {
|
||||
(10.0_f32).powf(db / 20.0)
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ use crate::devices::TransmutableBox;
|
||||
|
||||
|
||||
pub struct InterruptController {
|
||||
target: Option<TransmutableBox>,
|
||||
interrupts: Vec<(bool, u8)>,
|
||||
highest: u8,
|
||||
}
|
||||
@ -12,7 +11,6 @@ pub struct InterruptController {
|
||||
impl Default for InterruptController {
|
||||
fn default() -> InterruptController {
|
||||
InterruptController {
|
||||
target: None,
|
||||
interrupts: vec![(false, 0); 7],
|
||||
highest: 0,
|
||||
}
|
||||
@ -20,15 +18,6 @@ impl Default for InterruptController {
|
||||
}
|
||||
|
||||
impl InterruptController {
|
||||
pub fn set_target(&mut self, dev: TransmutableBox) -> Result<(), Error> {
|
||||
if self.target.is_some() {
|
||||
return Err(Error::new("Interruptable device already set, and interrupt controller only supports one receiver"));
|
||||
}
|
||||
|
||||
self.target = Some(dev);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
|
||||
self.interrupts[priority as usize].0 = state;
|
||||
self.interrupts[priority as usize].1 = number;
|
||||
|
@ -74,7 +74,6 @@ impl System {
|
||||
}
|
||||
|
||||
pub fn add_interruptable_device(&mut self, name: &str, device: TransmutableBox) -> Result<(), Error> {
|
||||
self.interrupt_controller.borrow_mut().set_target(device.clone())?;
|
||||
self.try_queue_device(device.clone());
|
||||
self.devices.insert(name.to_string(), device);
|
||||
Ok(())
|
||||
|
@ -8,14 +8,14 @@ use moa_core::host::audio::{SquareWave};
|
||||
const DEV_NAME: &str = "sn76489";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToneGenerator {
|
||||
struct ToneGenerator {
|
||||
on: bool,
|
||||
attenuation: f32,
|
||||
wave: SquareWave,
|
||||
}
|
||||
|
||||
impl ToneGenerator {
|
||||
pub fn new(sample_rate: usize) -> Self {
|
||||
fn new(sample_rate: usize) -> Self {
|
||||
Self {
|
||||
on: false,
|
||||
attenuation: 0.0,
|
||||
@ -23,7 +23,7 @@ impl ToneGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_attenuation(&mut self, attenuation: u8) {
|
||||
fn set_attenuation(&mut self, attenuation: u8) {
|
||||
if attenuation == 0x0F {
|
||||
self.on = false;
|
||||
} else {
|
||||
@ -33,20 +33,20 @@ impl ToneGenerator {
|
||||
info!("set attenuation to {} {}", self.attenuation, self.on);
|
||||
}
|
||||
|
||||
pub fn set_counter(&mut self, count: usize) {
|
||||
fn set_counter(&mut self, count: usize) {
|
||||
let frequency = 3_579_545.0 / (count as f32 * 32.0);
|
||||
self.wave.set_frequency(frequency);
|
||||
info!("set frequency to {}", frequency);
|
||||
}
|
||||
|
||||
pub fn get_sample(&mut self) -> f32 {
|
||||
fn get_sample(&mut self) -> f32 {
|
||||
self.wave.next().unwrap() / (self.attenuation + 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NoiseGenerator {
|
||||
struct NoiseGenerator {
|
||||
on: bool,
|
||||
attenuation: f32,
|
||||
}
|
||||
@ -61,7 +61,7 @@ impl Default for NoiseGenerator {
|
||||
}
|
||||
|
||||
impl NoiseGenerator {
|
||||
pub fn set_attenuation(&mut self, attenuation: u8) {
|
||||
fn set_attenuation(&mut self, attenuation: u8) {
|
||||
if attenuation == 0x0F {
|
||||
self.on = false;
|
||||
} else {
|
||||
@ -71,13 +71,13 @@ impl NoiseGenerator {
|
||||
info!("set attenuation to {} {}", self.attenuation, self.on);
|
||||
}
|
||||
|
||||
pub fn set_control(&mut self, _bits: u8) {
|
||||
fn set_control(&mut self, _bits: u8) {
|
||||
//let frequency = 3_579_545.0 / (count as f32 * 32.0);
|
||||
//self.wave.set_frequency(frequency);
|
||||
//debug!("set frequency to {}", frequency);
|
||||
}
|
||||
|
||||
pub fn get_sample(&mut self) -> f32 {
|
||||
fn get_sample(&mut self) -> f32 {
|
||||
// TODO this isn't implemented yet
|
||||
0.0
|
||||
}
|
||||
@ -86,18 +86,20 @@ impl NoiseGenerator {
|
||||
|
||||
|
||||
pub struct Sn76489 {
|
||||
pub first_byte: Option<u8>,
|
||||
pub source: Box<dyn Audio>,
|
||||
pub tones: Vec<ToneGenerator>,
|
||||
pub noise: NoiseGenerator,
|
||||
clock_frequency: u32,
|
||||
first_byte: Option<u8>,
|
||||
source: Box<dyn Audio>,
|
||||
tones: Vec<ToneGenerator>,
|
||||
noise: NoiseGenerator,
|
||||
}
|
||||
|
||||
impl Sn76489 {
|
||||
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
|
||||
pub fn create<H: Host>(host: &mut H, clock_frequency: u32) -> Result<Self, Error> {
|
||||
let source = host.create_audio_source()?;
|
||||
let sample_rate = source.samples_per_second();
|
||||
|
||||
Ok(Self {
|
||||
clock_frequency,
|
||||
first_byte: None,
|
||||
source,
|
||||
tones: vec![ToneGenerator::new(sample_rate); 3],
|
||||
|
@ -3,16 +3,24 @@ use std::num::NonZeroU8;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use moa_core::{debug, warn};
|
||||
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::{System, Error, Clock, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{Host, Audio};
|
||||
use moa_core::host::audio::{SineWave};
|
||||
use moa_core::host::audio::{SineWave, db_to_gain};
|
||||
|
||||
const DEV_NAME: &str = "ym2612";
|
||||
|
||||
const CHANNELS: usize = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum OperatorAlgorithm {
|
||||
enum EnvelopeState {
|
||||
Attack,
|
||||
Decay1,
|
||||
Decay2,
|
||||
Release,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum OperatorAlgorithm {
|
||||
A0,
|
||||
A1,
|
||||
A2,
|
||||
@ -25,148 +33,283 @@ pub enum OperatorAlgorithm {
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Operator {
|
||||
pub wave: SineWave,
|
||||
pub frequency: f32,
|
||||
pub multiplier: f32,
|
||||
struct Operator {
|
||||
wave: SineWave,
|
||||
frequency: f32,
|
||||
multiplier: f32,
|
||||
total_level: f32,
|
||||
|
||||
attack_rate: usize,
|
||||
first_decay_rate: usize,
|
||||
first_decay_level: usize,
|
||||
second_decay_rate: usize,
|
||||
release_rate: usize,
|
||||
|
||||
envelope_state: EnvelopeState,
|
||||
last_event: Clock,
|
||||
envelope_gain: f32,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn new(sample_rate: usize) -> Self {
|
||||
fn new(sample_rate: usize) -> Self {
|
||||
Self {
|
||||
wave: SineWave::new(400.0, sample_rate),
|
||||
frequency: 400.0,
|
||||
multiplier: 1.0,
|
||||
total_level: 0.0,
|
||||
|
||||
attack_rate: 0,
|
||||
first_decay_rate: 0,
|
||||
first_decay_level: 0,
|
||||
second_decay_rate: 0,
|
||||
release_rate: 0,
|
||||
|
||||
envelope_state: EnvelopeState::Attack,
|
||||
last_event: 0,
|
||||
envelope_gain: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_frequency(&mut self, frequency: f32) {
|
||||
fn set_frequency(&mut self, frequency: f32) {
|
||||
self.frequency = frequency;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
fn set_total_level(&mut self, db: f32) {
|
||||
self.total_level = db_to_gain(db);
|
||||
}
|
||||
|
||||
fn set_attack_rate(&mut self, rate: usize) {
|
||||
self.attack_rate = rate;
|
||||
}
|
||||
|
||||
fn set_first_decay_rate(&mut self, rate: usize) {
|
||||
self.first_decay_rate = rate;
|
||||
}
|
||||
|
||||
fn set_first_decay_level(&mut self, rate: usize) {
|
||||
self.first_decay_level = rate;
|
||||
}
|
||||
|
||||
fn set_second_decay_rate(&mut self, rate: usize) {
|
||||
self.second_decay_rate = rate;
|
||||
}
|
||||
|
||||
fn set_release_rate(&mut self, rate: usize) {
|
||||
self.release_rate = rate;
|
||||
}
|
||||
|
||||
fn notify_state_change(&mut self, state: bool, event_clock: Clock) {
|
||||
self.last_event = event_clock;
|
||||
if state {
|
||||
self.envelope_state = EnvelopeState::Attack;
|
||||
self.envelope_gain = 0.0;
|
||||
} else {
|
||||
self.envelope_state = EnvelopeState::Release;
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.wave.reset();
|
||||
}
|
||||
|
||||
pub fn set_multiplier(&mut self, _frequency: f32, multiplier: f32) {
|
||||
fn set_multiplier(&mut self, _frequency: f32, multiplier: f32) {
|
||||
self.multiplier = multiplier;
|
||||
}
|
||||
|
||||
pub fn get_sample(&mut self, modulator: f32) -> f32 {
|
||||
// TODO this would need to take into account the volume and envelope
|
||||
fn get_sample(&mut self, modulator: f32, event_clock: Clock) -> f32 {
|
||||
self.wave.set_frequency((self.frequency * self.multiplier) + modulator);
|
||||
self.wave.next().unwrap()
|
||||
let sample = self.wave.next().unwrap();
|
||||
|
||||
/*
|
||||
let time_since_last = event_clock - self.last_event;
|
||||
match self.envelope_state {
|
||||
EnvelopeState::Attack => {
|
||||
let gain = rate_to_gain(self.attack_rate, time_since_last).min(self.total_level);
|
||||
if gain == self.total_level {
|
||||
self.envelope_state = EnvelopeState::Decay1;
|
||||
}
|
||||
sample / gain
|
||||
},
|
||||
EnvelopeState::Decay1 => {
|
||||
let gain = (self.total_level - rate_to_gain(self.first_decay_rate, time_since_last)).max(self.total_level / 2.0);
|
||||
if gain == self.total_level / 2.0 {
|
||||
self.envelope_state = EnvelopeState::Decay2;
|
||||
}
|
||||
sample / gain
|
||||
},
|
||||
EnvelopeState::Decay2 => {
|
||||
let gain = (self.total_level / 2.0 - rate_to_gain(self.second_decay_rate, time_since_last)).max(0.0);
|
||||
sample / gain
|
||||
},
|
||||
EnvelopeState::Release => {
|
||||
let gain = (self.total_level / 2.0 - rate_to_gain(self.release_rate, time_since_last)).max(0.0);
|
||||
sample / gain
|
||||
},
|
||||
}
|
||||
*/
|
||||
sample
|
||||
}
|
||||
}
|
||||
|
||||
fn rate_to_gain(rate: usize, event_clock: Clock) -> f32 {
|
||||
event_clock as f32 * rate as f32
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Channel {
|
||||
pub operators: Vec<Operator>,
|
||||
pub on: u8,
|
||||
pub base_frequency: f32,
|
||||
pub algorithm: OperatorAlgorithm,
|
||||
struct Channel {
|
||||
operators: Vec<Operator>,
|
||||
on_state: u8,
|
||||
next_on_state: u8,
|
||||
base_frequency: f32,
|
||||
algorithm: OperatorAlgorithm,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn new(sample_rate: usize) -> Self {
|
||||
fn new(sample_rate: usize) -> Self {
|
||||
Self {
|
||||
operators: vec![Operator::new(sample_rate); 4],
|
||||
on: 0,
|
||||
on_state: 0,
|
||||
next_on_state: 0,
|
||||
base_frequency: 0.0,
|
||||
algorithm: OperatorAlgorithm::A0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_frequency(&mut self, frequency: f32) {
|
||||
fn set_frequency(&mut self, frequency: f32) {
|
||||
self.base_frequency = frequency;
|
||||
for operator in self.operators.iter_mut() {
|
||||
operator.set_frequency(frequency);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
fn reset(&mut self) {
|
||||
for operator in self.operators.iter_mut() {
|
||||
operator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sample(&mut self) -> f32 {
|
||||
fn get_sample(&mut self, event_clock: Clock) -> f32 {
|
||||
if self.on_state != self.next_on_state {
|
||||
self.on_state = self.next_on_state;
|
||||
for (i, operator) in self.operators.iter_mut().enumerate() {
|
||||
operator.notify_state_change(((self.on_state >> i) & 0x01) != 0, event_clock);
|
||||
}
|
||||
}
|
||||
|
||||
if self.on_state != 0 {
|
||||
self.get_algorithm_sample(event_clock)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn get_algorithm_sample(&mut self, event_clock: Clock) -> f32 {
|
||||
match self.algorithm {
|
||||
OperatorAlgorithm::A0 => {
|
||||
let modulator0 = self.operators[0].get_sample(0.0);
|
||||
let modulator1 = self.operators[1].get_sample(modulator0);
|
||||
let modulator2 = self.operators[2].get_sample(modulator1);
|
||||
self.operators[3].get_sample(modulator2)
|
||||
let modulator0 = self.operators[0].get_sample(0.0, event_clock);
|
||||
let modulator1 = self.operators[1].get_sample(modulator0, event_clock);
|
||||
let modulator2 = self.operators[2].get_sample(modulator1, event_clock);
|
||||
self.operators[3].get_sample(modulator2, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A1 => {
|
||||
let sample1 = self.operators[0].get_sample(0.0) + self.operators[1].get_sample(0.0);
|
||||
let sample2 = self.operators[2].get_sample(sample1);
|
||||
self.operators[3].get_sample(sample2)
|
||||
let sample1 = self.operators[0].get_sample(0.0, event_clock) + self.operators[1].get_sample(0.0, event_clock);
|
||||
let sample2 = self.operators[2].get_sample(sample1, event_clock);
|
||||
self.operators[3].get_sample(sample2, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A2 => {
|
||||
let sample1 = self.operators[1].get_sample(0.0);
|
||||
let sample2 = self.operators[2].get_sample(sample1);
|
||||
let sample3 = self.operators[0].get_sample(0.0) + sample2;
|
||||
self.operators[3].get_sample(sample3)
|
||||
let sample1 = self.operators[1].get_sample(0.0, event_clock);
|
||||
let sample2 = self.operators[2].get_sample(sample1, event_clock);
|
||||
let sample3 = self.operators[0].get_sample(0.0, event_clock) + sample2;
|
||||
self.operators[3].get_sample(sample3, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A3 => {
|
||||
let sample1 = self.operators[0].get_sample(0.0);
|
||||
let sample2 = self.operators[1].get_sample(sample1);
|
||||
let sample3 = self.operators[2].get_sample(0.0);
|
||||
self.operators[3].get_sample(sample2 + sample3)
|
||||
let sample1 = self.operators[0].get_sample(0.0, event_clock);
|
||||
let sample2 = self.operators[1].get_sample(sample1, event_clock);
|
||||
let sample3 = self.operators[2].get_sample(0.0, event_clock);
|
||||
self.operators[3].get_sample(sample2 + sample3, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A4 => {
|
||||
let sample1 = self.operators[0].get_sample(0.0);
|
||||
let sample2 = self.operators[1].get_sample(sample1);
|
||||
let sample3 = self.operators[2].get_sample(0.0);
|
||||
let sample4 = self.operators[3].get_sample(sample3);
|
||||
let sample1 = self.operators[0].get_sample(0.0, event_clock);
|
||||
let sample2 = self.operators[1].get_sample(sample1, event_clock);
|
||||
let sample3 = self.operators[2].get_sample(0.0, event_clock);
|
||||
let sample4 = self.operators[3].get_sample(sample3, event_clock);
|
||||
sample2 + sample4
|
||||
},
|
||||
OperatorAlgorithm::A5 => {
|
||||
let sample1 = self.operators[0].get_sample(0.0);
|
||||
self.operators[1].get_sample(sample1) + self.operators[2].get_sample(sample1) + self.operators[3].get_sample(sample1)
|
||||
let sample1 = self.operators[0].get_sample(0.0, event_clock);
|
||||
self.operators[1].get_sample(sample1, event_clock) + self.operators[2].get_sample(sample1, event_clock) + self.operators[3].get_sample(sample1, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A6 => {
|
||||
let sample1 = self.operators[0].get_sample(0.0);
|
||||
let sample2 = self.operators[1].get_sample(sample1);
|
||||
sample2 + self.operators[2].get_sample(0.0) + self.operators[3].get_sample(0.0)
|
||||
let sample1 = self.operators[0].get_sample(0.0, event_clock);
|
||||
let sample2 = self.operators[1].get_sample(sample1, event_clock);
|
||||
sample2 + self.operators[2].get_sample(0.0, event_clock) + self.operators[3].get_sample(0.0, event_clock)
|
||||
},
|
||||
OperatorAlgorithm::A7 => {
|
||||
self.operators[0].get_sample(0.0)
|
||||
+ self.operators[1].get_sample(0.0)
|
||||
+ self.operators[2].get_sample(0.0)
|
||||
+ self.operators[3].get_sample(0.0)
|
||||
self.operators[0].get_sample(0.0, event_clock)
|
||||
+ self.operators[1].get_sample(0.0, event_clock)
|
||||
+ self.operators[2].get_sample(0.0, event_clock)
|
||||
+ self.operators[3].get_sample(0.0, event_clock)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Dac {
|
||||
enabled: bool,
|
||||
samples: VecDeque<f32>,
|
||||
}
|
||||
|
||||
impl Default for Dac {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
samples: VecDeque::with_capacity(100),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dac {
|
||||
fn add_sample(&mut self, sample: f32) {
|
||||
self.samples.push_back(sample);
|
||||
}
|
||||
|
||||
fn get_sample(&mut self) -> f32 {
|
||||
if let Some(data) = self.samples.pop_front() {
|
||||
data
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Ym2612 {
|
||||
pub source: Box<dyn Audio>,
|
||||
pub selected_reg_0: Option<NonZeroU8>,
|
||||
pub selected_reg_1: Option<NonZeroU8>,
|
||||
source: Box<dyn Audio>,
|
||||
selected_reg_0: Option<NonZeroU8>,
|
||||
selected_reg_1: Option<NonZeroU8>,
|
||||
|
||||
pub channels: Vec<Channel>,
|
||||
pub channel_frequencies: [(u8, u16); CHANNELS],
|
||||
clock_frequency: u32,
|
||||
event_clock_period: ClockElapsed,
|
||||
channels: Vec<Channel>,
|
||||
channel_frequencies: [(u8, u16); CHANNELS],
|
||||
dac: Dac,
|
||||
|
||||
pub dac_enabled: bool,
|
||||
pub dac: VecDeque<u8>,
|
||||
timer_a_enable: bool,
|
||||
timer_a: u16,
|
||||
timer_a_current: u16,
|
||||
timer_a_overflow: bool,
|
||||
|
||||
pub timer_a_enable: bool,
|
||||
pub timer_a: u16,
|
||||
pub timer_a_current: u16,
|
||||
pub timer_a_overflow: bool,
|
||||
timer_b_enable: bool,
|
||||
timer_b: u8,
|
||||
timer_b_current: u8,
|
||||
timer_b_overflow: bool,
|
||||
|
||||
pub timer_b_enable: bool,
|
||||
pub timer_b: u8,
|
||||
pub timer_b_current: u8,
|
||||
pub timer_b_overflow: bool,
|
||||
registers: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Ym2612 {
|
||||
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
|
||||
pub fn create<H: Host>(host: &mut H, clock_frequency: u32) -> Result<Self, Error> {
|
||||
let source = host.create_audio_source()?;
|
||||
let sample_rate = source.samples_per_second();
|
||||
Ok(Self {
|
||||
@ -174,11 +317,12 @@ impl Ym2612 {
|
||||
selected_reg_0: None,
|
||||
selected_reg_1: None,
|
||||
|
||||
clock_frequency,
|
||||
event_clock_period: 3 * 144 * 1_000_000_000 / clock_frequency as ClockElapsed,
|
||||
channels: vec![Channel::new(sample_rate); 8],
|
||||
channel_frequencies: [(0, 0); CHANNELS],
|
||||
|
||||
dac_enabled: false,
|
||||
dac: VecDeque::with_capacity(100),
|
||||
dac: Dac::default(),
|
||||
|
||||
timer_a_enable: false,
|
||||
timer_a: 0,
|
||||
@ -189,10 +333,15 @@ impl Ym2612 {
|
||||
timer_b: 0,
|
||||
timer_b_current: 0,
|
||||
timer_b_overflow: false,
|
||||
|
||||
registers: vec![0; 512],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_register(&mut self, bank: usize, reg: usize, data: u8) {
|
||||
pub fn set_register(&mut self, bank: u8, reg: u8, data: u8) {
|
||||
// Keep a copy for debugging purposes, and if the original values are needed
|
||||
self.registers[bank as usize * 256 + reg as usize] = data;
|
||||
|
||||
//warn!("{}: set reg {}{:x} to {:x}", DEV_NAME, bank, reg, data);
|
||||
match reg {
|
||||
0x24 => {
|
||||
@ -211,23 +360,23 @@ impl Ym2612 {
|
||||
|
||||
0x28 => {
|
||||
let ch = (data as usize) & 0x07;
|
||||
self.channels[ch].on = data >> 4;
|
||||
self.channels[ch].next_on_state = data >> 4;
|
||||
self.channels[ch].reset();
|
||||
},
|
||||
|
||||
0x2a => {
|
||||
if self.dac_enabled {
|
||||
if self.dac.enabled {
|
||||
for _ in 0..3 {
|
||||
self.dac.push_back(data);
|
||||
self.dac.add_sample(((data as f32 - 128.0) / 255.0) * 2.0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
0x2b => {
|
||||
self.dac_enabled = data & 0x80 != 0;
|
||||
self.dac.enabled = data & 0x80 != 0;
|
||||
},
|
||||
|
||||
reg if (reg & 0xF0) == 0x30 => {
|
||||
reg if is_reg_range(reg, 0x30) => {
|
||||
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;
|
||||
@ -235,8 +384,24 @@ impl Ym2612 {
|
||||
self.channels[ch].operators[op].set_multiplier(frequency, multiplier)
|
||||
},
|
||||
|
||||
reg if is_reg_range(reg, 0x40) => {
|
||||
let (ch, op) = get_ch_op(bank, reg);
|
||||
// 0-127 is the attenuation, where 0 is the highest volume and 127 is the lowest, in 0.75 dB intervals
|
||||
self.channels[ch].operators[op].set_total_level((data & 0x7F) as f32 * 0.75);
|
||||
},
|
||||
|
||||
reg if is_reg_range(reg, 0x50)
|
||||
|| is_reg_range(reg, 0x60)
|
||||
|| is_reg_range(reg, 0x70)
|
||||
|| is_reg_range(reg, 0x80)
|
||||
|| is_reg_range(reg, 0x90)
|
||||
=> {
|
||||
let (ch, op) = get_ch_op(bank, reg);
|
||||
self.update_rates(ch, op);
|
||||
},
|
||||
|
||||
reg if (0xA0..=0xA2).contains(®) => {
|
||||
let ch = (reg & 0x07) + (bank * 3);
|
||||
let ch = get_ch(bank, reg);
|
||||
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF00) | data as u16;
|
||||
|
||||
let frequency = fnumber_to_frequency(self.channel_frequencies[ch]);
|
||||
@ -245,13 +410,13 @@ impl Ym2612 {
|
||||
},
|
||||
|
||||
reg if (0xA4..=0xA6).contains(®) => {
|
||||
let ch = (reg & 0x07) - 4 + (bank * 3);
|
||||
let ch = ((reg as usize) & 0x07) - 4 + ((bank as usize) * 3);
|
||||
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF) | ((data as u16) & 0x07) << 8;
|
||||
self.channel_frequencies[ch].0 = (data & 0x38) >> 3;
|
||||
},
|
||||
|
||||
reg if (0xB0..=0xB2).contains(®) => {
|
||||
let ch = (reg & 0x07) + (bank * 3);
|
||||
let ch = get_ch(bank, reg);
|
||||
self.channels[ch].algorithm = match data & 0x07 {
|
||||
0 => OperatorAlgorithm::A0,
|
||||
1 => OperatorAlgorithm::A1,
|
||||
@ -270,59 +435,101 @@ impl Ym2612 {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update_rates(&mut self, ch: usize, op: usize) {
|
||||
let index = get_index(ch, op);
|
||||
let keycode = self.registers[0xA0 + get_ch_index(ch)] >> 1;
|
||||
let rate_scaling = self.registers[0x50 + index] & 0xC0 >> 6;
|
||||
let attack_rate = self.registers[0x50 + index] & 0x1F;
|
||||
let first_decay_rate = self.registers[0x60 + index] & 0x1F;
|
||||
let first_decay_level = (self.registers[0x80 + index] & 0x0F) >> 4;
|
||||
let second_decay_rate = self.registers[0x70 + index] & 0x1F;
|
||||
let release_rate = self.registers[0x80 + index] & 0x0F;
|
||||
|
||||
self.channels[ch].operators[op].set_attack_rate(calculate_rate(attack_rate, rate_scaling, keycode));
|
||||
self.channels[ch].operators[op].set_first_decay_rate(calculate_rate(first_decay_rate, rate_scaling, keycode));
|
||||
self.channels[ch].operators[op].set_first_decay_level(calculate_rate(first_decay_level, rate_scaling, keycode));
|
||||
self.channels[ch].operators[op].set_second_decay_rate(calculate_rate(second_decay_rate, rate_scaling, keycode));
|
||||
self.channels[ch].operators[op].set_release_rate(calculate_rate(release_rate, rate_scaling, keycode));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fnumber_to_frequency(fnumber: (u8, u16)) -> f32 {
|
||||
#[inline]
|
||||
fn fnumber_to_frequency(fnumber: (u8, u16)) -> f32 {
|
||||
(fnumber.1 as f32 * 0.0264) * 2_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;
|
||||
#[inline]
|
||||
fn calculate_rate(rate: u8, rate_scaling: u8, keycode: u8) -> usize {
|
||||
let scale = match rate_scaling {
|
||||
0 => 8,
|
||||
1 => 4,
|
||||
2 => 2,
|
||||
3 => 1,
|
||||
_ => 8, // this shouldn't be possible
|
||||
};
|
||||
|
||||
(2 * rate as usize + (keycode as usize / scale)).min(64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_reg_range(reg: u8, base: u8) -> bool {
|
||||
// There is no 4th channel in each of the groupings
|
||||
reg >= base && reg <= base + 0x0F && (reg & 0x03) != 0x03
|
||||
}
|
||||
|
||||
/// Get the channel and operator to target with a given register number
|
||||
/// and bank number. Bank 0 refers to operators for channels 1-3, and
|
||||
/// bank 1 refers to operators for channels 4-6.
|
||||
#[inline]
|
||||
fn get_ch_op(bank: u8, reg: u8) -> (usize, usize) {
|
||||
let ch = ((reg as usize) & 0x03) + ((bank as usize) * 3);
|
||||
let op = ((reg as usize) & 0x0C) >> 2;
|
||||
(ch, op)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_index(ch: usize, op: usize) -> usize {
|
||||
let (bank, ch_l) = if ch < 3 { (0, ch) } else { (1, ch - 3) };
|
||||
(bank << 8) | op << 2 | ch
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_ch(bank: u8, reg: u8) -> usize {
|
||||
((reg as usize) & 0x07) + ((bank as usize) * 3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_ch_index(ch: usize) -> usize {
|
||||
if ch < 3 {
|
||||
ch
|
||||
} else {
|
||||
0x100 + ch - 3
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// you could assume that you should produce (sample_rate / 1000) samples
|
||||
|
||||
//if self.sine.frequency < 2000.0 {
|
||||
// self.sine.frequency += 1.0;
|
||||
//}
|
||||
|
||||
//let rate = self.source.samples_per_second();
|
||||
//self.source.write_samples(rate / 1000, &mut self.sine);
|
||||
//println!("{}", self.sine.frequency);
|
||||
|
||||
//if self.on {
|
||||
// let rate = self.source.samples_per_second();
|
||||
// self.source.write_samples(rate / 1000, &mut self.sine);
|
||||
//}
|
||||
|
||||
let rate = self.source.samples_per_second();
|
||||
let available = self.source.space_available();
|
||||
let samples = if available < rate / 1000 { available } else { rate / 1000 };
|
||||
let nanos_per_sample = 1_000_000_000 / rate;
|
||||
|
||||
//if self.source.space_available() >= samples {
|
||||
let mut buffer = vec![0.0; samples];
|
||||
for buffered_sample in buffer.iter_mut().take(samples) {
|
||||
for (i, buffered_sample) in buffer.iter_mut().enumerate().take(samples) {
|
||||
let event_clock = (system.clock + (i * nanos_per_sample) as Clock) / self.event_clock_period;
|
||||
let mut sample = 0.0;
|
||||
|
||||
for ch in 0..6 {
|
||||
if self.channels[ch].on != 0 {
|
||||
sample += self.channels[ch].get_sample();
|
||||
}
|
||||
sample += self.channels[ch].get_sample(event_clock);
|
||||
}
|
||||
|
||||
if self.dac_enabled {
|
||||
if let Some(data) = self.dac.pop_front() {
|
||||
sample += ((data as f32 - 128.0) / 255.0) * 2.0;
|
||||
}
|
||||
} else if self.channels[6].on != 0 {
|
||||
sample += self.channels[6].get_sample();
|
||||
if self.dac.enabled {
|
||||
sample += self.dac.get_sample();
|
||||
} else {
|
||||
sample += self.channels[6].get_sample(event_clock);
|
||||
}
|
||||
|
||||
*buffered_sample = sample.clamp(-1.0, 1.0);
|
||||
@ -361,7 +568,7 @@ impl Addressable for Ym2612 {
|
||||
},
|
||||
1 => {
|
||||
if let Some(reg) = self.selected_reg_0 {
|
||||
self.set_register(0, reg.get() as usize, data[0]);
|
||||
self.set_register(0, reg.get(), data[0]);
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
@ -369,7 +576,7 @@ impl Addressable for Ym2612 {
|
||||
},
|
||||
3 => {
|
||||
if let Some(reg) = self.selected_reg_1 {
|
||||
self.set_register(1, reg.get() as usize, data[0]);
|
||||
self.set_register(1, reg.get(), data[0]);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
@ -70,8 +70,8 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
||||
// Build the Coprocessor's Bus
|
||||
let bank_register = Signal::new(0);
|
||||
let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000]));
|
||||
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host)?);
|
||||
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host)?);
|
||||
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, 7_670_454)?);
|
||||
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host, 3_579_545)?);
|
||||
let coproc_register = wrap_transmutable(CoprocessorBankRegister::new(bank_register.clone()));
|
||||
let coproc_area = wrap_transmutable(CoprocessorBankArea::new(bank_register, system.bus.clone()));
|
||||
|
||||
|
7
todo.txt
7
todo.txt
@ -1,4 +1,11 @@
|
||||
|
||||
* the interrupt controller stuff is really not good. It should be more like busport, and connected to a device at startup (eg. create
|
||||
interrupt controller, then create objects that use that controller and pass in values, maybe an option so that the controller doesn't
|
||||
have to be hooked up, meaning hardware interrupts would not be used.
|
||||
|
||||
* along with the interrupt stuff, I'm kind of thinking of packaging things a bit differently, like using a tuple struct for the rc refcell
|
||||
transmutable abstraction, so that you can avoid the need for explicit borrows
|
||||
|
||||
* improve performance
|
||||
* should it be possible to reschedule multiple events at different intervals to reduce the times a given step function is called? Some have
|
||||
multiple clocks, or multiple things at different clocks, and making them each an event would mean they could be smaller and faster, but at
|
||||
|
Loading…
x
Reference in New Issue
Block a user