mirror of
https://github.com/transistorfet/moa.git
synced 2025-04-01 07:33:14 +00:00
Fixed a bug in the frequency setting code for ym2612
It was previously only updating the frequency if the A0 registers were written last, but now it works the way the rate code does, using the cached register values to set the frequency whenever a register is written to. It also stores the fnumber and block in the operator which I guess would be needed eventually if I want to save and restore state.
This commit is contained in:
parent
dcc3c97dd6
commit
03561f1427
@ -380,3 +380,10 @@ General Work
|
||||
changing from a sine wave generator to a square wave generator made it go from being muddled and
|
||||
mute sounding to sounding crisp and much closer to what it's supposed to sound like
|
||||
|
||||
- in order to match the way I did the rates, I modified the frequency setting code to store the
|
||||
fnumber and block in the operators themselves, and used the cached registers to update the values
|
||||
whenever either of the sets of registers was updated. It previously only updated the frequency if
|
||||
the A0 registers were written, so it was multiple octaves lower than it should have been
|
||||
- now it actually sounds pretty close for the high pitch tones, but the base tones are completely
|
||||
missing
|
||||
|
||||
|
@ -251,7 +251,9 @@ struct Operator {
|
||||
#[allow(dead_code)]
|
||||
debug_name: String,
|
||||
wave: SquareWave,
|
||||
frequency: f32,
|
||||
block: u8,
|
||||
fnumber: u16,
|
||||
detune: u8,
|
||||
multiplier: f32,
|
||||
envelope: EnvelopeGenerator,
|
||||
}
|
||||
@ -261,7 +263,9 @@ impl Operator {
|
||||
Self {
|
||||
debug_name: debug_name.clone(),
|
||||
wave: SquareWave::new(400.0, sample_rate),
|
||||
frequency: 400.0,
|
||||
block: 0,
|
||||
fnumber: 0,
|
||||
detune: 0,
|
||||
multiplier: 1.0,
|
||||
envelope: EnvelopeGenerator::new(debug_name),
|
||||
}
|
||||
@ -271,12 +275,17 @@ impl Operator {
|
||||
self.wave.reset();
|
||||
}
|
||||
|
||||
fn set_frequency(&mut self, frequency: f32) {
|
||||
self.frequency = frequency;
|
||||
fn set_block_and_fnumber(&mut self, block: u8, fnumber: u16) {
|
||||
self.block = block;
|
||||
self.fnumber = fnumber;
|
||||
}
|
||||
|
||||
fn set_multiplier(&mut self, _frequency: f32, multiplier: f32) {
|
||||
self.multiplier = multiplier;
|
||||
fn set_detune(&mut self, detune: u8) {
|
||||
self.detune = detune;
|
||||
}
|
||||
|
||||
fn set_multiplier(&mut self, multiplier: u16) {
|
||||
self.multiplier = if multiplier == 0 { 0.5 } else { multiplier as f32 };
|
||||
}
|
||||
|
||||
fn set_total_level(&mut self, level: u16) {
|
||||
@ -296,7 +305,8 @@ impl Operator {
|
||||
}
|
||||
|
||||
fn get_sample(&mut self, modulator: f32, envelope_clock: EnvelopeClock) -> f32 {
|
||||
self.wave.set_frequency((self.frequency * self.multiplier) + modulator);
|
||||
let frequency = fnumber_to_frequency(self.block, self.fnumber);
|
||||
self.wave.set_frequency((frequency * self.multiplier) as f32 + modulator);
|
||||
let sample = self.wave.next().unwrap();
|
||||
|
||||
self.envelope.update_envelope(envelope_clock);
|
||||
@ -306,6 +316,10 @@ impl Operator {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fnumber_to_frequency(block: u8, fnumber: u16) -> f32 {
|
||||
(fnumber as f32 * 0.0264) * 2_u32.pow(block as u32) as f32
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Channel {
|
||||
@ -332,13 +346,6 @@ impl Channel {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_frequency(&mut self, frequency: f32) {
|
||||
self.base_frequency = frequency;
|
||||
for operator in self.operators.iter_mut() {
|
||||
operator.set_frequency(frequency);
|
||||
}
|
||||
}
|
||||
|
||||
fn change_key_state(&mut self, fm_clock: FmClock, key: u8) {
|
||||
self.next_key_clock = fm_clock;
|
||||
self.next_key_state = key;
|
||||
@ -454,7 +461,6 @@ pub struct Ym2612 {
|
||||
fm_clock_period: ClockDuration,
|
||||
envelope_clock_period: ClockDuration,
|
||||
channels: Vec<Channel>,
|
||||
channel_frequencies: [(u8, u16); CHANNELS],
|
||||
dac: Dac,
|
||||
|
||||
timer_a_enable: bool,
|
||||
@ -487,7 +493,6 @@ impl Ym2612 {
|
||||
fm_clock_period,
|
||||
envelope_clock_period,
|
||||
channels: (0..CHANNELS).map(|i| Channel::new(format!("ch {}", i), sample_rate)).collect(),
|
||||
channel_frequencies: [(0, 0); CHANNELS],
|
||||
|
||||
dac: Dac::default(),
|
||||
|
||||
@ -559,6 +564,9 @@ impl Ym2612 {
|
||||
0x27 => {
|
||||
//if (data >> 5) & 0x1 {
|
||||
// self.timer_b
|
||||
if data >> 6 == 0x01 {
|
||||
warn!("{}: ch 3 special mode requested, but not implemented", DEV_NAME);
|
||||
}
|
||||
},
|
||||
|
||||
0x28 => {
|
||||
@ -588,10 +596,8 @@ impl Ym2612 {
|
||||
|
||||
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;
|
||||
debug!("{}: channel {} operator {} set to multiplier {}", DEV_NAME, ch + 1, op + 1, multiplier);
|
||||
self.channels[ch].operators[op].set_multiplier(frequency, multiplier)
|
||||
self.channels[ch].operators[op].set_detune((data & 0xF0) >> 4);
|
||||
self.channels[ch].operators[op].set_multiplier((data & 0x0F) as u16);
|
||||
},
|
||||
|
||||
reg if is_reg_range(reg, 0x40) => {
|
||||
@ -613,18 +619,11 @@ impl Ym2612 {
|
||||
},
|
||||
|
||||
reg if (0xA0..=0xA2).contains(®) => {
|
||||
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]);
|
||||
debug!("{}: channel {} set to frequency {}", DEV_NAME, ch + 1, frequency);
|
||||
self.channels[ch].set_frequency(frequency);
|
||||
self.update_fnumber(bank, reg & 0x0F);
|
||||
},
|
||||
|
||||
reg if (0xA4..=0xA6).contains(®) => {
|
||||
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;
|
||||
self.update_fnumber(bank, reg & 0x0F);
|
||||
},
|
||||
|
||||
reg if (0xB0..=0xB2).contains(®) => {
|
||||
@ -648,9 +647,18 @@ impl Ym2612 {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_rates(&mut self, bank: u8, reg: u8) {
|
||||
let index = bank as usize * 256 + reg as usize;
|
||||
let (ch, op) = get_ch_op(bank, reg);
|
||||
fn update_fnumber(&mut self, bank: u8, lower_reg: u8) {
|
||||
let index = bank as usize * 256 + lower_reg as usize;
|
||||
let block = (self.registers[0xA4 + index] & 0x38) >> 3;
|
||||
let fnumber = ((self.registers[0xA4 + index] as u16 & 0x07) << 8) | self.registers[0xA0 + index] as u16;
|
||||
|
||||
let (ch, op) = get_ch_op(bank, lower_reg);
|
||||
self.channels[ch].operators[op].set_block_and_fnumber(block, fnumber);
|
||||
}
|
||||
|
||||
fn update_rates(&mut self, bank: u8, lower_reg: u8) {
|
||||
let index = bank as usize * 256 + lower_reg as usize;
|
||||
let (ch, op) = get_ch_op(bank, lower_reg);
|
||||
let keycode = self.registers[0xA0 + get_ch_index(ch)] >> 1;
|
||||
let rate_scaling = self.registers[0x50 + index] & 0xC0 >> 6;
|
||||
|
||||
@ -670,11 +678,6 @@ impl Ym2612 {
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn calculate_rate(rate: u8, rate_scaling: u8, keycode: u8) -> usize {
|
||||
let scale = match rate_scaling {
|
||||
|
2
todo.txt
2
todo.txt
@ -27,6 +27,8 @@
|
||||
the cost of having more events on the queue when re-scheduling. There needs to be a mechanism to avoid the event queue ballooning due to
|
||||
an error
|
||||
|
||||
* add ability to serialize/deserialize state into something, so it can be restored... (maybe not worth it though)
|
||||
|
||||
* can you refactor the update timeout to put it in rust? Would that make it faster? (the tricky part is the closure)
|
||||
* re-enable sound on webassembly, see if it works (it does not. Very lagged and jittery with what sounds like repeated frames)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user