dsp.rsannotateddsp.rssource1157 lines · 40.4 KB · raw
1//! SNES S-DSP, responsible for audio playback
2
3mod interpolate;
4mod tables;
5
6use crate::apu::AudioRam;
7use crate::apu::dsp::interpolate::InterpolateArgs;
8use bincode::{Decode, Encode};
9use jgenesis_common::num::{GetBit, U16Ext};
10use snes_config::AudioInterpolationMode;
11use std::array;
12use std::ops::Index;
13
14const BRR_BLOCK_LEN: u16 = 9;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
17enum EnvelopeMode {
18    Adsr,
19    #[default]
20    Gain,
21}
22
23impl EnvelopeMode {
24    fn from_bit(bit: bool) -> Self {
25        if bit { Self::Adsr } else { Self::Gain }
26    }
27
28    fn to_bit(self) -> bool {
29        self == Self::Adsr
30    }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
34enum GainMode {
35    #[default]
36    Direct,
37    Custom,
38}
39
40impl GainMode {
41    fn from_bit(bit: bool) -> Self {
42        if bit { Self::Custom } else { Self::Direct }
43    }
44
45    fn to_bit(self) -> bool {
46        self == Self::Custom
47    }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
51enum EnvelopePhase {
52    Attack,
53    Decay,
54    Sustain,
55    #[default]
56    Release,
57}
58
59const BRR_BUFFER_LEN: usize = 12;
60
61#[derive(Debug, Clone, Default, Encode, Decode)]
62struct BrrRingBuffer {
63    buffer: [i16; BRR_BUFFER_LEN],
64    fill_idx: usize,
65    sample_idx: usize,
66}
67
68impl BrrRingBuffer {
69    fn reset(&mut self) {
70        self.fill_idx = 0;
71        self.sample_idx = 0;
72    }
73
74    fn write(&mut self, sample: i16) {
75        self.buffer[self.fill_idx] = sample;
76        self.fill_idx = (self.fill_idx + 1) % BRR_BUFFER_LEN;
77    }
78
79    fn shift_sample_idx(&mut self) {
80        self.sample_idx = (self.sample_idx + 4) % BRR_BUFFER_LEN;
81    }
82
83    fn last_two_written_samples(&self) -> (i16, i16) {
84        if self.fill_idx == 0 {
85            (self.buffer[BRR_BUFFER_LEN - 2], self.buffer[BRR_BUFFER_LEN - 1])
86        } else if self.fill_idx == 1 {
87            (self.buffer[BRR_BUFFER_LEN - 1], self.buffer[0])
88        } else {
89            (self.buffer[self.fill_idx - 2], self.buffer[self.fill_idx - 1])
90        }
91    }
92}
93
94impl Index<u16> for BrrRingBuffer {
95    type Output = i16;
96
97    fn index(&self, index: u16) -> &Self::Output {
98        let buffer_idx = (self.sample_idx + index as usize) % BRR_BUFFER_LEN;
99        &self.buffer[buffer_idx]
100    }
101}
102
103#[derive(Debug, Clone, Encode, Decode)]
104struct Voice {
105    audio_interpolation: AudioInterpolationMode,
106    // Registers
107    instrument_number: u8,
108    sample_rate: u16,
109    pitch_modulation_enabled: bool,
110    envelope_mode: EnvelopeMode,
111    attack_rate: u8,
112    decay_rate: u8,
113    sustain_rate: u8,
114    sustain_level: u8,
115    gain_mode: GainMode,
116    // Meaning of gain value varies based on gain mode
117    gain_value: u8,
118    volume_l: i8,
119    volume_r: i8,
120    keyed_on: bool,
121    keyed_off: bool,
122    output_noise: bool,
123    // Stored in full here to preserve the highest 2 bits which are R/W
124    last_pitch_h_write: u8,
125    // State
126    brr_block_address: u16,
127    brr_buffer: BrrRingBuffer,
128    brr_decoder_idx: u16,
129    pitch_counter: u16,
130    envelope_level: u16,
131    clipped_envelope_value: u16,
132    envelope_phase: EnvelopePhase,
133    current_sample: i16,
134    restart_pending: bool,
135    restart_delay_remaining: u8,
136    end_flag_seen: bool,
137}
138
139impl Voice {
140    fn new(audio_interpolation: AudioInterpolationMode) -> Self {
141        Self {
142            audio_interpolation,
143            instrument_number: 0,
144            sample_rate: 0,
145            pitch_modulation_enabled: false,
146            envelope_mode: EnvelopeMode::default(),
147            attack_rate: 0,
148            decay_rate: 0,
149            sustain_rate: 0,
150            sustain_level: 0,
151            gain_mode: GainMode::default(),
152            gain_value: 0,
153            volume_l: 0,
154            volume_r: 0,
155            keyed_on: false,
156            keyed_off: false,
157            output_noise: false,
158            last_pitch_h_write: 0,
159            brr_block_address: 0,
160            brr_buffer: BrrRingBuffer::default(),
161            brr_decoder_idx: 0,
162            pitch_counter: 0,
163            envelope_level: 0,
164            clipped_envelope_value: 0,
165            envelope_phase: EnvelopePhase::default(),
166            current_sample: 0,
167            restart_pending: false,
168            restart_delay_remaining: 0,
169            end_flag_seen: false,
170        }
171    }
172
173    fn write_pitch_low(&mut self, value: u8) {
174        self.sample_rate.set_lsb(value);
175    }
176
177    fn write_pitch_high(&mut self, value: u8) {
178        // Sample rate is 14 bits; drop the highest 2
179        self.sample_rate.set_msb(value & 0x3F);
180
181        // Preserve original value for register reads
182        self.last_pitch_h_write = value;
183    }
184
185    fn write_adsr_low(&mut self, value: u8) {
186        // 4-bit attack rate, 3-bit decay rate, and ADSR/Gain select bit
187        self.attack_rate = value & 0x0F;
188        self.decay_rate = (value >> 4) & 0x07;
189        self.envelope_mode = EnvelopeMode::from_bit(value.bit(7));
190
191        log::trace!(
192            "  Attack rate: {:02X}, Decay rate: {:02X}, Envelope Mode: {:?}",
193            self.attack_rate,
194            self.decay_rate,
195            self.envelope_mode
196        );
197    }
198
199    fn read_adsr_low(&self) -> u8 {
200        self.attack_rate | (self.decay_rate << 4) | (u8::from(self.envelope_mode.to_bit()) << 7)
201    }
202
203    fn write_adsr_high(&mut self, value: u8) {
204        // 5-bit sustain rate, 3-bit sustain level
205        self.sustain_rate = value & 0x1F;
206        self.sustain_level = value >> 5;
207
208        log::trace!(
209            "  Sustain rate: {:02X}, Sustain level: {:02X}",
210            self.sustain_rate,
211            self.sustain_level
212        );
213    }
214
215    fn read_adsr_high(&self) -> u8 {
216        self.sustain_rate | (self.sustain_level << 5)
217    }
218
219    fn write_gain(&mut self, value: u8) {
220        self.gain_mode = GainMode::from_bit(value.bit(7));
221        self.gain_value = value & 0x7F;
222
223        log::trace!("  Gain mode: {:?}, Gain value: {:02X}", self.gain_mode, self.gain_value);
224    }
225
226    fn read_gain(&self) -> u8 {
227        self.gain_value | (u8::from(self.gain_mode.to_bit()) << 7)
228    }
229
230    fn read_envelope(&self) -> u8 {
231        // VxENVX reads return the highest 7 bits of the 11-bit envelope value
232        (self.envelope_level >> 4) as u8
233    }
234
235    fn read_output(&self) -> u8 {
236        // VxOUTX reads return the highest 8 bits of the 15-bit sample value (pre-volume)
237        (self.current_sample >> 7) as u8
238    }
239
240    fn write_key_on(&mut self, key_on: bool) {
241        self.keyed_on = key_on;
242
243        // Keying on immediately moves envelope to attack, resets envelope value, and restarts the channel
244        if key_on {
245            self.envelope_phase = EnvelopePhase::Attack;
246            self.envelope_level = 0;
247            self.restart_pending = true;
248        }
249    }
250
251    fn write_key_off(&mut self, key_off: bool) {
252        self.keyed_off = key_off;
253
254        // Keying off immediately moves envelope to release (but does not modify envelope value or other state)
255        if key_off {
256            self.envelope_phase = EnvelopePhase::Release;
257        }
258    }
259
260    fn soft_reset(&mut self) {
261        // Soft reset keys off the voice and immediately sets envelope to 0
262        self.write_key_off(true);
263        self.envelope_level = 0;
264    }
265
266    fn clock(
267        &mut self,
268        registers: &DspRegisters,
269        audio_ram: &AudioRam,
270        prev_voice_sample: i16,
271        noise_generator_output: i16,
272    ) {
273        if self.restart_pending {
274            self.restart_pending = false;
275            self.restart(registers, audio_ram);
276        }
277
278        if self.restart_delay_remaining != 0 {
279            // Output empty sample
280            self.current_sample = 0;
281
282            // After 2 clocks, prevent the channel from starting if key off or soft reset is set
283            if self.restart_delay_remaining <= 3 && (self.keyed_off || registers.soft_reset) {
284                self.envelope_phase = EnvelopePhase::Release;
285            }
286
287            self.restart_delay_remaining -= 1;
288            if self.restart_delay_remaining == 0 {
289                // Reset BRR decoder state and decode the first 2 groups
290                // Actual hardware decodes 3 groups here, but decoding 2 simplifies the ring buffer handling
291                self.brr_buffer.reset();
292                self.brr_decoder_idx = 0;
293
294                for _ in 0..2 {
295                    self.decode_brr_group(registers.sample_table_address, audio_ram);
296                }
297            }
298
299            return;
300        }
301
302        let interpolated_sample = if self.output_noise {
303            // Turning on noise for a voice replaces the output with the noise generator output,
304            // but envelope is still used and all of the BRR decoding continues to run in the
305            // background
306            noise_generator_output
307        } else {
308            // Bits 12-15 of pitch counter are used as the sample index
309            let sample_idx = self.pitch_counter >> 12;
310            let args = InterpolateArgs {
311                pitch_counter: self.pitch_counter,
312                oldest: self.brr_buffer[sample_idx],
313                older: self.brr_buffer[sample_idx + 1],
314                old: self.brr_buffer[sample_idx + 2],
315                sample: self.brr_buffer[sample_idx + 3],
316            };
317
318            match self.audio_interpolation {
319                AudioInterpolationMode::Gaussian => interpolate::gaussian(args),
320                AudioInterpolationMode::Hermite => interpolate::hermite(args),
321            }
322        };
323
324        // TODO do this after multiplying by sample?
325        self.clock_envelope(registers.global_counter);
326
327        // Apply 11-bit envelope; still a signed 15-bit sample
328        let sample =
329            ((i32::from(interpolated_sample) * i32::from(self.envelope_level)) >> 11) as i16;
330        self.current_sample = sample;
331
332        self.pitch_counter += self.sample_rate;
333        if self.pitch_modulation_enabled && !self.output_noise {
334            // Adjust pitch based on previous voice's output
335            let modulation_rate =
336                ((i32::from(prev_voice_sample) >> 5) * i32::from(self.sample_rate)) >> 10;
337            self.pitch_counter =
338                self.pitch_counter.wrapping_add_signed(modulation_rate as i16).clamp(0, 0x7FFF);
339        }
340
341        if self.pitch_counter >= 0x4000 {
342            self.pitch_counter -= 0x4000;
343            self.decode_brr_group(registers.sample_table_address, audio_ram);
344            self.brr_buffer.shift_sample_idx();
345        }
346    }
347
348    fn restart(&mut self, registers: &DspRegisters, audio_ram: &AudioRam) {
349        // Table address is located at DIR + 4*instrument
350        let table_addr = registers
351            .sample_table_address
352            .wrapping_add(u16::from(self.instrument_number) << 2) as usize;
353
354        // First 2 bytes of table entry hold start address
355        let start_addr = u16::from_le_bytes([audio_ram[table_addr], audio_ram[table_addr + 1]]);
356        self.brr_block_address = start_addr;
357        self.pitch_counter = 0;
358
359        // Each voice outputs 5 empty samples after a restart
360        self.restart_delay_remaining = 5;
361
362        self.end_flag_seen = false;
363    }
364
365    // Decode a group of 4 BRR samples, advancing to the next BRR block or looping if necessary
366    fn decode_brr_group(&mut self, sample_table_address: u16, audio_ram: &AudioRam) {
367        if self.brr_decoder_idx == 16 {
368            // Advance to next block
369            let prev_block_header = audio_ram[self.brr_block_address as usize];
370            let prev_block_end_flag = prev_block_header.bit(0);
371            if prev_block_end_flag {
372                self.end_flag_seen = true;
373
374                // Jump to loop address
375                let table_addr = sample_table_address
376                    .wrapping_add(u16::from(self.instrument_number) << 2)
377                    as usize;
378                let loop_addr =
379                    u16::from_le_bytes([audio_ram[table_addr + 2], audio_ram[table_addr + 3]]);
380                self.brr_block_address = loop_addr;
381            } else {
382                self.brr_block_address = self.brr_block_address.wrapping_add(BRR_BLOCK_LEN);
383            }
384
385            self.brr_decoder_idx = 0;
386        }
387
388        let header = audio_ram[self.brr_block_address as usize];
389        let shift = header >> 4;
390        let filter = (header >> 2) & 0x03;
391        let loop_flag = header.bit(1);
392        let end_flag = header.bit(0);
393
394        // Immediately mute channel after decoding a header with end set and loop clear
395        if end_flag && !loop_flag {
396            self.envelope_phase = EnvelopePhase::Release;
397            self.envelope_level = 0;
398        }
399
400        let mut brr_nibbles = [0_i8; 4];
401        let decoder_idx = self.brr_decoder_idx;
402        for i in 0..2 {
403            let sample_addr = self.brr_block_address.wrapping_add(1 + (decoder_idx >> 1) + i);
404            let sample_pair = audio_ram[sample_addr as usize];
405
406            // High nibble stores the first sample, low nibble stores the second sample
407            // Treat both as signed 4-bit integers
408            let first_sample = (sample_pair as i8) >> 4;
409            let second_sample = ((sample_pair as i8) << 4) >> 4;
410            brr_nibbles[(2 * i) as usize] = first_sample;
411            brr_nibbles[(2 * i + 1) as usize] = second_sample;
412        }
413        self.brr_decoder_idx += 4;
414
415        let (mut older_sample, mut old_sample) = self.brr_buffer.last_two_written_samples();
416        for brr_nibble in brr_nibbles {
417            let shifted = apply_brr_shift(brr_nibble, shift);
418            let brr_sample = apply_brr_filter(shifted, filter, old_sample, older_sample);
419            self.brr_buffer.write(brr_sample);
420
421            older_sample = old_sample;
422            old_sample = brr_sample;
423        }
424    }
425
426    fn clock_envelope(&mut self, global_counter: u16) {
427        if self.envelope_phase == EnvelopePhase::Release {
428            // Release ignores other settings and decrements envelope value by 8 every sample
429            self.envelope_level = self.envelope_level.saturating_sub(8);
430            self.clipped_envelope_value = self.envelope_level.wrapping_sub(8) & 0x7FF;
431            return;
432        }
433
434        // ADSR transitions are checked every sample, even if envelope is in Gain mode
435        if self.envelope_phase == EnvelopePhase::Attack && self.envelope_level >= 0x7E0 {
436            self.envelope_phase = EnvelopePhase::Decay;
437        }
438
439        if self.envelope_phase == EnvelopePhase::Decay {
440            let sustain_level = u16::from(self.sustain_level + 1) << 8;
441            if self.envelope_level <= sustain_level {
442                self.envelope_phase = EnvelopePhase::Sustain;
443            }
444        }
445
446        let current_value: i32 = self.envelope_level.into();
447        let (rate, step) = match (self.envelope_mode, self.gain_mode) {
448            (EnvelopeMode::Gain, GainMode::Direct) => {
449                // Force envelope level to 16*N
450                let target_value = i32::from(self.gain_value) << 4;
451                if current_value == target_value {
452                    (0, 0)
453                } else {
454                    (31, target_value - current_value)
455                }
456            }
457            (EnvelopeMode::Gain, GainMode::Custom) => {
458                let rate = self.gain_value & 0x1F;
459                let step = match self.gain_value & 0x60 {
460                    0x00 => {
461                        // Linear decrease (fixed decrement by 32)
462                        -32
463                    }
464                    0x20 => {
465                        // Exponential decrease
466                        compute_exp_decay(current_value)
467                    }
468                    0x40 => {
469                        // Linear increase (fixed increment by 32)
470                        32
471                    }
472                    0x60 => {
473                        // Bent increase (uses clipped value rather than clamped value)
474                        if self.clipped_envelope_value < 0x600 { 32 } else { 8 }
475                    }
476                    _ => unreachable!("value & 0x60 is always one of the above values"),
477                };
478
479                (rate, step)
480            }
481            (EnvelopeMode::Adsr, _) => match self.envelope_phase {
482                EnvelopePhase::Attack => {
483                    let rate = (self.attack_rate << 1) | 0x01;
484                    let step = if rate == 31 { 1024 } else { 32 };
485                    (rate, step)
486                }
487                EnvelopePhase::Decay => {
488                    let rate = 0x10 | (self.decay_rate << 1);
489                    let step = compute_exp_decay(current_value);
490                    (rate, step)
491                }
492                EnvelopePhase::Sustain => {
493                    let step = compute_exp_decay(current_value);
494                    (self.sustain_rate, step)
495                }
496                EnvelopePhase::Release => (31, -8),
497            },
498        };
499
500        if rate != 0
501            && (global_counter + tables::ENVELOPE_OFFSET[rate as usize])
502                .is_multiple_of(tables::ENVELOPE_RATE[rate as usize])
503        {
504            let new_value = current_value + step;
505            self.envelope_level = new_value.clamp(0, 0x7FF) as u16;
506            self.clipped_envelope_value = (new_value as u16) & 0x7FF;
507        }
508    }
509}
510
511fn apply_brr_shift(nibble: i8, shift: u8) -> i16 {
512    match shift {
513        0 => (nibble >> 1).into(),
514        1..=12 => i16::from(nibble) << (shift - 1),
515        13..=15 => {
516            // "Invalid" shift values; resulting sample will always be either 0 or -2048 ($F800)
517            if nibble < 0 { -2048 } else { 0 }
518        }
519        _ => panic!("invalid BRR shift value: {shift}"),
520    }
521}
522
523fn apply_brr_filter(sample: i16, filter: u8, old: i16, older: i16) -> i16 {
524    // Do math in 32 bits to avoid overflows
525    let sample: i32 = sample.into();
526    let old: i32 = old.into();
527    let older: i32 = older.into();
528
529    let filtered = match filter {
530        // no filter
531        0 => sample,
532        // sample + 0.9375 * old
533        1 => sample + old + (-old >> 4),
534        // sample + 1.90625 * old - 0.9375 * older
535        2 => sample + (old << 1) + (-(3 * old) >> 5) - older + (older >> 4),
536        // sample + 1.796875 * old - 0.8125 * older
537        3 => sample + (old << 1) + (-(13 * old) >> 6) - older + ((3 * older) >> 4),
538        _ => panic!("invalid BRR filter value: {filter}"),
539    };
540
541    // Clamp to 16 bits
542    let clamped = filtered.clamp(i16::MIN.into(), i16::MAX.into()) as i16;
543
544    // Clip to 15 bits
545    (clamped << 1) >> 1
546}
547
548fn compute_exp_decay(current_value: i32) -> i32 {
549    -(((current_value - 1) >> 8) + 1)
550}
551
552#[derive(Debug, Clone, Encode, Decode)]
553struct NoiseGenerator {
554    output: i16,
555}
556
557impl NoiseGenerator {
558    fn new() -> Self {
559        Self { output: i16::MIN >> 1 }
560    }
561
562    fn clock(&mut self, noise_frequency: u8, global_counter: u16) {
563        // Noise generator uses the same rate/offset tables as the envelopes
564        let rate = noise_frequency as usize;
565        if rate != 0
566            && (global_counter + tables::ENVELOPE_OFFSET[rate])
567                .is_multiple_of(tables::ENVELOPE_RATE[rate])
568        {
569            let new_bit = self.output.bit(0) ^ self.output.bit(1);
570            self.output = ((self.output >> 1) & 0x3FFF) | (i16::from(new_bit) << 14);
571
572            // Clip to 15 bits
573            self.output = (self.output << 1) >> 1;
574        }
575    }
576}
577
578#[derive(Debug, Clone, Encode, Decode)]
579struct EchoFilter {
580    echo_enabled: [bool; 8],
581    buffer_start_address: u16,
582    buffer_current_offset: u16,
583    buffer_samples_remaining: u16,
584    buffer_size_samples: u16,
585    volume_l: i8,
586    volume_r: i8,
587    feedback_volume: i8,
588    fir_coefficients: [i8; 8],
589    sample_buffer_l: [i16; 8],
590    sample_buffer_r: [i16; 8],
591    sample_buffer_idx: usize,
592    // Stored because the highest 4 bits are unused R/W
593    last_edl_write: u8,
594}
595
596impl EchoFilter {
597    fn new() -> Self {
598        Self {
599            echo_enabled: [false; 8],
600            buffer_start_address: 0,
601            buffer_current_offset: 0,
602            buffer_samples_remaining: 1,
603            buffer_size_samples: 1,
604            volume_l: 0,
605            volume_r: 0,
606            feedback_volume: 0,
607            fir_coefficients: [0; 8],
608            sample_buffer_l: [0; 8],
609            sample_buffer_r: [0; 8],
610            sample_buffer_idx: 0,
611            last_edl_write: 0,
612        }
613    }
614
615    fn write_echo_enabled(&mut self, eon: u8) {
616        for i in 0..8 {
617            self.echo_enabled[i] = eon.bit(i as u8);
618        }
619    }
620
621    fn read_echo_enabled(&self) -> u8 {
622        (0..8).map(|i| u8::from(self.echo_enabled[i]) << i).reduce(|a, b| a | b).unwrap()
623    }
624
625    fn write_echo_buffer_size(&mut self, edl: u8) {
626        self.buffer_size_samples = match edl & 0x0F {
627            0 => 1,
628            edl => u16::from(edl) << 9,
629        };
630
631        self.last_edl_write = edl;
632    }
633
634    fn do_filter(
635        &mut self,
636        echo_buffer_writes_enabled: bool,
637        audio_ram: &mut AudioRam,
638        voice_samples_l: &[i32; 8],
639        voice_samples_r: &[i32; 8],
640    ) -> (i32, i32) {
641        let current_buffer_addr =
642            self.buffer_start_address.wrapping_add(self.buffer_current_offset);
643        self.sample_buffer_l[self.sample_buffer_idx] =
644            read_echo_sample(audio_ram, current_buffer_addr);
645        self.sample_buffer_r[self.sample_buffer_idx] =
646            read_echo_sample(audio_ram, current_buffer_addr.wrapping_add(2));
647
648        // Add the 7 older samples with 16-bit wrapping behavior
649        let mut fir_sample_l: i32 = 0;
650        let mut fir_sample_r: i32 = 0;
651        for i in 0..7 {
652            let coefficient: i32 = self.fir_coefficients[i].into();
653
654            let buffer_idx = self.sample_buffer_idx.wrapping_add(i + 1) & 0x07;
655            let sample_l: i32 = self.sample_buffer_l[buffer_idx].into();
656            let sample_r: i32 = self.sample_buffer_r[buffer_idx].into();
657
658            fir_sample_l += (coefficient * sample_l) >> 6;
659            fir_sample_r += (coefficient * sample_r) >> 6;
660        }
661
662        // Clip to 16 bits before adding the newest sample
663        fir_sample_l = (fir_sample_l as i16).into();
664        fir_sample_r = (fir_sample_r as i16).into();
665
666        // Add in the newest sample and clamp to 16 bits
667        fir_sample_l += (i32::from(self.fir_coefficients[7])
668            * i32::from(self.sample_buffer_l[self.sample_buffer_idx]))
669            >> 6;
670        fir_sample_r += (i32::from(self.fir_coefficients[7])
671            * i32::from(self.sample_buffer_r[self.sample_buffer_idx]))
672            >> 6;
673
674        let mut fir_sample_l = fir_sample_l.clamp(i16::MIN.into(), i16::MAX.into());
675        let mut fir_sample_r = fir_sample_r.clamp(i16::MIN.into(), i16::MAX.into());
676
677        // Force the lowest bit clear in FIR sample
678        fir_sample_l &= !1;
679        fir_sample_r &= !1;
680
681        if echo_buffer_writes_enabled {
682            self.write_to_echo_buffer(
683                audio_ram,
684                voice_samples_l,
685                voice_samples_r,
686                fir_sample_l,
687                fir_sample_r,
688            );
689        }
690
691        self.sample_buffer_idx = (self.sample_buffer_idx + 1) & 0x07;
692
693        self.buffer_samples_remaining -= 1;
694        if self.buffer_samples_remaining == 0 {
695            self.buffer_current_offset = 0;
696            self.buffer_samples_remaining = self.buffer_size_samples;
697        } else {
698            self.buffer_current_offset = self.buffer_current_offset.wrapping_add(4);
699        }
700
701        let echo_out_l = (fir_sample_l * i32::from(self.volume_l)) >> 7;
702        let echo_out_r = (fir_sample_r * i32::from(self.volume_r)) >> 7;
703
704        (echo_out_l, echo_out_r)
705    }
706
707    fn write_to_echo_buffer(
708        &self,
709        audio_ram: &mut AudioRam,
710        voice_samples_l: &[i32; 8],
711        voice_samples_r: &[i32; 8],
712        fir_sample_l: i32,
713        fir_sample_r: i32,
714    ) {
715        let mut echo_voice_sum_l: i32 = 0;
716        let mut echo_voice_sum_r: i32 = 0;
717        for i in (0..8).filter(|&i| self.echo_enabled[i]) {
718            echo_voice_sum_l += voice_samples_l[i];
719            echo_voice_sum_r += voice_samples_r[i];
720
721            echo_voice_sum_l = echo_voice_sum_l.clamp(i16::MIN.into(), i16::MAX.into());
722            echo_voice_sum_r = echo_voice_sum_r.clamp(i16::MIN.into(), i16::MAX.into());
723        }
724
725        let echo_feedback_l = (fir_sample_l * i32::from(self.feedback_volume)) >> 7;
726        let echo_feedback_r = (fir_sample_r * i32::from(self.feedback_volume)) >> 7;
727
728        // Force the lowest bit clear before writing back to echo buffer
729        let echo_sample_l =
730            (echo_voice_sum_l + echo_feedback_l).clamp(i16::MIN.into(), i16::MAX.into()) & !1;
731        let echo_sample_r =
732            (echo_voice_sum_r + echo_feedback_r).clamp(i16::MIN.into(), i16::MAX.into()) & !1;
733
734        let current_buffer_addr =
735            self.buffer_start_address.wrapping_add(self.buffer_current_offset);
736        write_echo_sample(audio_ram, current_buffer_addr, echo_sample_l as i16);
737        write_echo_sample(audio_ram, current_buffer_addr.wrapping_add(2), echo_sample_r as i16);
738    }
739}
740
741fn read_echo_sample(audio_ram: &AudioRam, address: u16) -> i16 {
742    let lsb = audio_ram[address as usize];
743    let msb = audio_ram[address.wrapping_add(1) as usize];
744    i16::from_le_bytes([lsb, msb]) >> 1
745}
746
747fn write_echo_sample(audio_ram: &mut AudioRam, address: u16, value: i16) {
748    let [value_lsb, value_msb] = value.to_le_bytes();
749    audio_ram[address as usize] = value_lsb;
750    audio_ram[address.wrapping_add(1) as usize] = value_msb;
751}
752
753#[derive(Debug, Clone, Encode, Decode)]
754struct DspRegisters {
755    sample_table_address: u16,
756    master_volume_l: i8,
757    master_volume_r: i8,
758    noise_frequency: u8,
759    echo_buffer_writes_enabled: bool,
760    mute_amplifier: bool,
761    soft_reset: bool,
762    global_counter: u16,
763    // Unused R/W registers
764    unused_xa_registers: [u8; 8],
765    unused_xb_registers: [u8; 8],
766    unused_xe_registers: [u8; 8],
767    unused_1d_register: u8,
768}
769
770impl DspRegisters {
771    fn new() -> Self {
772        Self {
773            sample_table_address: 0,
774            master_volume_l: 0,
775            master_volume_r: 0,
776            noise_frequency: 0,
777            echo_buffer_writes_enabled: false,
778            mute_amplifier: true,
779            soft_reset: true,
780            global_counter: 0,
781            unused_xa_registers: [0; 8],
782            unused_xb_registers: [0; 8],
783            unused_xe_registers: [0; 8],
784            unused_1d_register: 0,
785        }
786    }
787
788    fn write_flg(&mut self, value: u8) {
789        self.noise_frequency = value & 0x1F;
790        self.echo_buffer_writes_enabled = !value.bit(5);
791        self.mute_amplifier = value.bit(6);
792        self.soft_reset = value.bit(7);
793
794        log::trace!("  Noise frequency: {:02X}", self.noise_frequency);
795        log::trace!("  Echo buffer writes enabled: {}", self.echo_buffer_writes_enabled);
796        log::trace!("  Mute amplifier: {}", self.mute_amplifier);
797        log::trace!("  Soft reset: {}", self.soft_reset);
798    }
799
800    fn read_flg(&self) -> u8 {
801        self.noise_frequency
802            | (u8::from(!self.echo_buffer_writes_enabled) << 5)
803            | (u8::from(self.mute_amplifier) << 6)
804            | (u8::from(self.soft_reset) << 7)
805    }
806}
807
808#[derive(Debug, Clone, Encode, Decode)]
809pub struct AudioDsp {
810    voices: [Voice; 8],
811    registers: DspRegisters,
812    noise_generator: NoiseGenerator,
813    echo_filter: EchoFilter,
814    register_address: u8,
815}
816
817impl AudioDsp {
818    pub fn new(audio_interpolation: AudioInterpolationMode) -> Self {
819        Self {
820            voices: array::from_fn(|_| Voice::new(audio_interpolation)),
821            registers: DspRegisters::new(),
822            noise_generator: NoiseGenerator::new(),
823            echo_filter: EchoFilter::new(),
824            register_address: 0,
825        }
826    }
827
828    pub fn read_address(&self) -> u8 {
829        self.register_address
830    }
831
832    pub fn write_address(&mut self, address: u8) {
833        self.register_address = address;
834    }
835
836    pub fn read_register(&self) -> u8 {
837        log::trace!("DSP register read: {:02X}", self.register_address);
838
839        // Addresses $80-$FF mirror $00-$7F
840        let address = self.register_address & 0x7F;
841
842        // High nibble of register address encodes the voice
843        let voice = (address >> 4) as usize;
844
845        match address & 0x0F {
846            0x00 => self.voices[voice].volume_l as u8,
847            0x01 => self.voices[voice].volume_r as u8,
848            0x02 => self.voices[voice].sample_rate.lsb(),
849            0x03 => self.voices[voice].last_pitch_h_write,
850            0x04 => self.voices[voice].instrument_number,
851            0x05 => self.voices[voice].read_adsr_low(),
852            0x06 => self.voices[voice].read_adsr_high(),
853            0x07 => self.voices[voice].read_gain(),
854            0x08 => self.voices[voice].read_envelope(),
855            0x09 => self.voices[voice].read_output(),
856            0x0A => self.registers.unused_xa_registers[voice],
857            0x0B => self.registers.unused_xb_registers[voice],
858            0x0E => self.registers.unused_xe_registers[voice],
859            0x0F => self.echo_filter.fir_coefficients[voice] as u8,
860            // $xC and $xD registers are not voice-specific
861            0x0C | 0x0D => match address {
862                0x0C => self.registers.master_volume_l as u8,
863                0x1C => self.registers.master_volume_r as u8,
864                0x2C => self.echo_filter.volume_l as u8,
865                0x3C => self.echo_filter.volume_r as u8,
866                0x4C => {
867                    // Key on flags
868                    (0..8)
869                        .map(|voice| u8::from(self.voices[voice].keyed_on) << voice)
870                        .reduce(|a, b| a | b)
871                        .unwrap()
872                }
873                0x5C => {
874                    // Key off flags
875                    (0..8)
876                        .map(|voice| u8::from(self.voices[voice].keyed_off) << voice)
877                        .reduce(|a, b| a | b)
878                        .unwrap()
879                }
880                0x6C => self.registers.read_flg(),
881                0x7C => {
882                    // ENDX (end flags)
883                    (0..8)
884                        .map(|voice| u8::from(self.voices[voice].end_flag_seen) << voice)
885                        .reduce(|a, b| a | b)
886                        .unwrap()
887                }
888                0x0D => self.echo_filter.feedback_volume as u8,
889                0x1D => self.registers.unused_1d_register,
890                0x2D => {
891                    // Pitch modulation enable flags (voices 1-7 only)
892                    (1..8)
893                        .map(|voice| u8::from(self.voices[voice].pitch_modulation_enabled) << voice)
894                        .reduce(|a, b| a | b)
895                        .unwrap()
896                }
897                0x3D => {
898                    // Output noise flags
899                    (0..8)
900                        .map(|voice| u8::from(self.voices[voice].output_noise) << voice)
901                        .reduce(|a, b| a | b)
902                        .unwrap()
903                }
904                0x4D => self.echo_filter.read_echo_enabled(),
905                0x5D => (self.registers.sample_table_address >> 8) as u8,
906                0x6D => (self.echo_filter.buffer_start_address >> 8) as u8,
907                0x7D => self.echo_filter.last_edl_write,
908                _ => unreachable!("all $xC and $xD addresses covered"),
909            },
910            _ => unreachable!("all addresses <= $7F covered"),
911        }
912    }
913
914    pub fn write_register(&mut self, value: u8) {
915        // Addresses $80-$FF are not writable
916        if self.register_address >= 0x80 {
917            return;
918        }
919
920        log::trace!("DSP register write: {:02X}: {value:02X}", self.register_address);
921
922        // High nibble of register address encodes the voice
923        let voice = (self.register_address >> 4) as usize;
924
925        match self.register_address & 0x0F {
926            0x00 => {
927                self.voices[voice].volume_l = value as i8;
928                log::trace!("  Voice {voice} volume L: {value:02X}");
929            }
930            0x01 => {
931                self.voices[voice].volume_r = value as i8;
932                log::trace!("  Voice {voice} volume R: {value:02X}");
933            }
934            0x02 => {
935                self.voices[voice].write_pitch_low(value);
936                log::trace!("  Voice {voice} sample rate: {:04X}", self.voices[voice].sample_rate);
937            }
938            0x03 => {
939                self.voices[voice].write_pitch_high(value);
940                log::trace!("  Voice {voice} sample rate: {:04X}", self.voices[voice].sample_rate);
941            }
942            0x04 => {
943                self.voices[voice].instrument_number = value;
944                log::trace!("  Voice {voice} instrument number: {value:02X}");
945            }
946            0x05 => {
947                log::trace!("  Voice {voice} ADSR low write: {value:02X}");
948                self.voices[voice].write_adsr_low(value);
949            }
950            0x06 => {
951                log::trace!("  Voice {voice} ADSR high write: {value:02X}");
952                self.voices[voice].write_adsr_high(value);
953            }
954            0x07 => {
955                log::trace!("  Voice {voice} gain write: {value:02X}");
956                self.voices[voice].write_gain(value);
957            }
958            0x08 | 0x09 => {
959                // Current envelope value and current output value
960                // These are technically writable, although the DSP overwrites them at 32000 Hz
961                // TODO implement these
962            }
963            0x0A => {
964                self.registers.unused_xa_registers[voice] = value;
965            }
966            0x0B => {
967                self.registers.unused_xb_registers[voice] = value;
968            }
969            0x0E => {
970                self.registers.unused_xe_registers[voice] = value;
971            }
972            0x0F => {
973                self.echo_filter.fir_coefficients[voice] = value as i8;
974            }
975            // $xC and $xD registers are not voice-specific
976            0x0C | 0x0D => match self.register_address {
977                0x0C => {
978                    self.registers.master_volume_l = value as i8;
979                    log::trace!("  Master volume L: {value:02X}");
980                }
981                0x1C => {
982                    self.registers.master_volume_r = value as i8;
983                    log::trace!("  Master volume R: {value:02X}");
984                }
985                0x2C => {
986                    self.echo_filter.volume_l = value as i8;
987                    log::trace!("  Echo volume L: {}", self.echo_filter.volume_l);
988                }
989                0x3C => {
990                    self.echo_filter.volume_r = value as i8;
991                    log::trace!("  Echo volume R: {}", self.echo_filter.volume_r);
992                }
993                0x4C => {
994                    // Key on flags
995                    for voice in 0..8 {
996                        self.voices[voice].write_key_on(value.bit(voice as u8));
997                    }
998                    log::trace!("  Key on: {value:02X}");
999                }
1000                0x5C => {
1001                    // Key off flags
1002                    for voice in 0..8 {
1003                        self.voices[voice].write_key_off(value.bit(voice as u8));
1004                    }
1005                    log::trace!("  Key off: {value:02X}");
1006                }
1007                0x6C => {
1008                    self.registers.write_flg(value);
1009
1010                    // Apply soft reset immediately if set
1011                    if self.registers.soft_reset {
1012                        for voice in &mut self.voices {
1013                            voice.soft_reset();
1014                        }
1015                    }
1016                }
1017                0x7C => {
1018                    // ENDX; writing any value clears all of the flags
1019                    for voice in &mut self.voices {
1020                        voice.end_flag_seen = false;
1021                    }
1022                }
1023                0x0D => {
1024                    self.echo_filter.feedback_volume = value as i8;
1025
1026                    log::trace!("  Echo feedback volume: {}", self.echo_filter.feedback_volume);
1027                }
1028                0x1D => {
1029                    self.registers.unused_1d_register = value;
1030                }
1031                0x2D => {
1032                    // Pitch modulation enable flags (voices 1-7 only)
1033                    for voice in 1..8 {
1034                        self.voices[voice].pitch_modulation_enabled = value.bit(voice as u8);
1035                    }
1036                    log::trace!("  Pitch modulation enabled: {value:02X}");
1037                }
1038                0x3D => {
1039                    // Output noise flags
1040                    for voice in 0..8 {
1041                        self.voices[voice].output_noise = value.bit(voice as u8);
1042                    }
1043                    log::trace!("  Output noise: {value:02X}");
1044                }
1045                0x4D => {
1046                    self.echo_filter.write_echo_enabled(value);
1047
1048                    log::trace!("  Echo enabled: {value:02X}");
1049                }
1050                0x5D => {
1051                    // Sample table address is in 256-byte steps
1052                    self.registers.sample_table_address = u16::from_le_bytes([0x00, value]);
1053                    log::trace!(
1054                        "  Sample table address: {:04X}",
1055                        self.registers.sample_table_address
1056                    );
1057                }
1058                0x6D => {
1059                    // Echo ring buffer address is in 256-byte steps
1060                    self.echo_filter.buffer_start_address = u16::from_le_bytes([0x00, value]);
1061                    log::trace!(
1062                        "Echo buffer start address: {:04X}",
1063                        self.echo_filter.buffer_start_address
1064                    );
1065                }
1066                0x7D => {
1067                    self.echo_filter.write_echo_buffer_size(value);
1068                    log::trace!("  Echo buffer size: {value:02X}");
1069                }
1070                _ => unreachable!("all $xC and $xD addresses covered"),
1071            },
1072            _ => unreachable!("all addresses <= $7F covered"),
1073        }
1074    }
1075
1076    #[must_use]
1077    pub fn clock(&mut self, audio_ram: &mut AudioRam) -> (i16, i16) {
1078        if self.registers.global_counter == 0 {
1079            self.registers.global_counter = 0x77FF;
1080        } else {
1081            self.registers.global_counter -= 1;
1082        }
1083
1084        self.noise_generator.clock(self.registers.noise_frequency, self.registers.global_counter);
1085
1086        for i in 0..8 {
1087            let prev_voice_output = if i != 0 { self.voices[i - 1].current_sample } else { 0 };
1088            self.voices[i].clock(
1089                &self.registers,
1090                audio_ram,
1091                prev_voice_output,
1092                self.noise_generator.output,
1093            );
1094        }
1095
1096        self.sample(audio_ram)
1097    }
1098
1099    fn sample(&mut self, audio_ram: &mut AudioRam) -> (i16, i16) {
1100        let mut voice_samples_l = [0; 8];
1101        let mut voice_samples_r = [0; 8];
1102        let mut voice_sum_l = 0_i32;
1103        let mut voice_sum_r = 0_i32;
1104        for (i, voice) in self.voices.iter().enumerate() {
1105            let voice_sample_l = (i32::from(voice.current_sample) * i32::from(voice.volume_l)) >> 6;
1106            let voice_sample_r = (i32::from(voice.current_sample) * i32::from(voice.volume_r)) >> 6;
1107
1108            voice_samples_l[i] = voice_sample_l;
1109            voice_samples_r[i] = voice_sample_r;
1110
1111            voice_sum_l += voice_sample_l;
1112            voice_sum_r += voice_sample_r;
1113
1114            voice_sum_l = voice_sum_l.clamp(i16::MIN.into(), i16::MAX.into());
1115            voice_sum_r = voice_sum_r.clamp(i16::MIN.into(), i16::MAX.into());
1116        }
1117
1118        voice_sum_l = (voice_sum_l * i32::from(self.registers.master_volume_l)) >> 7;
1119        voice_sum_l = voice_sum_l.clamp(i16::MIN.into(), i16::MAX.into());
1120
1121        voice_sum_r = (voice_sum_r * i32::from(self.registers.master_volume_r)) >> 7;
1122        voice_sum_r = voice_sum_r.clamp(i16::MIN.into(), i16::MAX.into());
1123
1124        let (echo_l, echo_r) = self.echo_filter.do_filter(
1125            self.registers.echo_buffer_writes_enabled,
1126            audio_ram,
1127            &voice_samples_l,
1128            &voice_samples_r,
1129        );
1130
1131        let (out_l, out_r) = if !self.registers.mute_amplifier {
1132            let out_l = (voice_sum_l + echo_l).clamp(i16::MIN.into(), i16::MAX.into());
1133            let out_r = (voice_sum_r + echo_r).clamp(i16::MIN.into(), i16::MAX.into());
1134            (out_l, out_r)
1135        } else {
1136            // All processing continues while muted, but the DSP outputs silence
1137            (0, 0)
1138        };
1139
1140        ((out_l as i16) ^ !0, (out_r as i16) ^ !0)
1141    }
1142
1143    pub fn reset(&mut self) {
1144        // Set soft reset flag, mute amplifier, and block echo buffer writes
1145        self.registers.write_flg(0xE0);
1146
1147        for voice in &mut self.voices {
1148            voice.soft_reset();
1149        }
1150    }
1151
1152    pub fn update_audio_interpolation(&mut self, audio_interpolation: AudioInterpolationMode) {
1153        for voice in &mut self.voices {
1154            voice.audio_interpolation = audio_interpolation;
1155        }
1156    }
1157}