resid/
synth.rs

1// This file is part of resid-rs.
2// Copyright (c) 2017-2019 Sebastian Jastrzebski <sebby2k@gmail.com>. All rights reserved.
3// Portions (c) 2004 Dag Lem <resid@nimrod.no>
4// Licensed under the GPLv3. See LICENSE file in the project root for full license text.
5
6#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
7
8use super::external_filter::ExternalFilter;
9use super::filter::Filter;
10use super::sid::reg;
11use super::voice::Voice;
12use super::wave::Syncable;
13use super::ChipModel;
14
15const OUTPUT_RANGE: u32 = 1 << 16;
16const OUTPUT_HALF: i32 = (OUTPUT_RANGE >> 1) as i32;
17const SAMPLES_PER_OUTPUT: u32 = ((4095 * 255) >> 7) * 3 * 15 * 2 / OUTPUT_RANGE;
18
19#[derive(Clone, Copy)]
20pub struct Synth {
21    pub ext_filter: ExternalFilter,
22    pub filter: Filter,
23    pub voices: [Voice; 3],
24    pub ext_in: i32,
25}
26
27// slice::rotate_left is inefficient for small arrays:
28// https://github.com/rust-lang/rust/issues/89714
29fn rotate3<T>([a, b, c]: [T; 3], i: usize) -> [T; 3] {
30    match i {
31        0 => [a, b, c],
32        1 => [b, c, a],
33        2 => [c, a, b],
34        _ => panic!("index out of bounds"),
35    }
36}
37
38impl Synth {
39    pub fn new(chip_model: ChipModel) -> Self {
40        Synth {
41            ext_filter: ExternalFilter::new(chip_model),
42            filter: Filter::new(chip_model),
43            voices: [Voice::new(chip_model); 3],
44            ext_in: 0,
45        }
46    }
47
48    pub fn syncable_voice(&self, i: usize) -> Syncable<&'_ Voice> {
49        let [a, b, c] = &self.voices;
50        let [main, sync_dest, sync_source] = rotate3([a, b, c], i);
51        Syncable {
52            main,
53            sync_dest,
54            sync_source,
55        }
56    }
57
58    pub fn syncable_voice_mut(&mut self, i: usize) -> Syncable<&'_ mut Voice> {
59        let [a, b, c] = &mut self.voices;
60        let [main, sync_dest, sync_source] = rotate3([a, b, c], i);
61        Syncable {
62            main,
63            sync_dest,
64            sync_source,
65        }
66    }
67
68    pub fn clock(&mut self) {
69        // Clock amplitude modulators.
70        for i in 0..3 {
71            self.voices[i].envelope.clock();
72        }
73        // Clock oscillators.
74        for i in 0..3 {
75            self.voices[i].wave.clock();
76        }
77        // Synchronize oscillators.
78        for i in 0..3 {
79            self.syncable_voice_mut(i).wave().synchronize();
80        }
81        // Clock filter.
82        self.filter.clock(
83            self.syncable_voice(0).output(),
84            self.syncable_voice(1).output(),
85            self.syncable_voice(2).output(),
86            self.ext_in,
87        );
88        // Clock external filter.
89        self.ext_filter.clock(self.filter.output());
90    }
91
92    pub fn clock_delta(&mut self, delta: u32) {
93        // Clock amplitude modulators.
94        for i in 0..3 {
95            self.voices[i].envelope.clock_delta(delta);
96        }
97        let mut delta_osc = delta;
98        while delta_osc != 0 {
99            // Find minimum number of cycles to an oscillator accumulator MSB toggle.
100            // We have to clock on each MSB on / MSB off for hard sync to operate
101            // correctly.
102            let mut delta_min = delta_osc;
103            for i in 0..3 {
104                let wave = self.syncable_voice(i).wave();
105                // It is only necessary to clock on the MSB of an oscillator that is
106                // a sync source and has freq != 0.
107                if !(wave.sync_dest.get_sync() && wave.main.get_frequency() != 0) {
108                    continue;
109                }
110                let freq = wave.main.get_frequency() as u32;
111                let acc = wave.main.get_acc();
112                // Clock on MSB off if MSB is on, clock on MSB on if MSB is off.
113                let delta_acc = if acc & 0x0080_0000 != 0 {
114                    0x0100_0000 - acc
115                } else {
116                    0x0080_0000 - acc
117                };
118                let mut delta_next = delta_acc / freq;
119                if delta_acc % freq != 0 {
120                    delta_next += 1;
121                }
122                if delta_next < delta_min {
123                    delta_min = delta_next;
124                }
125            }
126            // Clock oscillators.
127            for i in 0..3 {
128                self.voices[i].wave.clock_delta(delta_min);
129            }
130            // Synchronize oscillators.
131            for i in 0..3 {
132                self.syncable_voice_mut(i).wave().synchronize();
133            }
134            delta_osc -= delta_min;
135        }
136        // Clock filter.
137        self.filter.clock_delta(
138            delta,
139            self.syncable_voice(0).output(),
140            self.syncable_voice(1).output(),
141            self.syncable_voice(2).output(),
142            self.ext_in,
143        );
144        // Clock external filter.
145        self.ext_filter.clock_delta(delta, self.filter.output());
146    }
147
148    pub fn output(&self) -> i16 {
149        // Read sample from audio output.
150        let sample = self.ext_filter.output() / SAMPLES_PER_OUTPUT as i32;
151        if sample >= OUTPUT_HALF {
152            (OUTPUT_HALF - 1) as i16
153        } else if sample < -OUTPUT_HALF {
154            (-OUTPUT_HALF) as i16
155        } else {
156            sample as i16
157        }
158    }
159
160    pub fn reset(&mut self) {
161        self.ext_filter.reset();
162        self.filter.reset();
163        for i in 0..3 {
164            self.voices[i].reset();
165        }
166        self.ext_in = 0;
167    }
168
169    pub fn read(&self, reg: u8, bus_value: u8) -> u8 {
170        match reg {
171            reg::POTX => 0xff,
172            reg::POTY => 0xff,
173            reg::OSC3 => self.syncable_voice(2).wave().read_osc(),
174            reg::ENV3 => self.voices[2].envelope.read_env(),
175            _ => bus_value,
176        }
177    }
178
179    pub fn write(&mut self, reg: u8, value: u8) {
180        match reg {
181            reg::FREQLO1 => self.voices[0].wave.set_frequency_lo(value),
182            reg::FREQHI1 => self.voices[0].wave.set_frequency_hi(value),
183            reg::PWLO1 => self.voices[0].wave.set_pulse_width_lo(value),
184            reg::PWHI1 => self.voices[0].wave.set_pulse_width_hi(value),
185            reg::CR1 => self.voices[0].set_control(value),
186            reg::AD1 => self.voices[0].envelope.set_attack_decay(value),
187            reg::SR1 => self.voices[0].envelope.set_sustain_release(value),
188            reg::FREQLO2 => self.voices[1].wave.set_frequency_lo(value),
189            reg::FREQHI2 => self.voices[1].wave.set_frequency_hi(value),
190            reg::PWLO2 => self.voices[1].wave.set_pulse_width_lo(value),
191            reg::PWHI2 => self.voices[1].wave.set_pulse_width_hi(value),
192            reg::CR2 => self.voices[1].set_control(value),
193            reg::AD2 => self.voices[1].envelope.set_attack_decay(value),
194            reg::SR2 => self.voices[1].envelope.set_sustain_release(value),
195            reg::FREQLO3 => self.voices[2].wave.set_frequency_lo(value),
196            reg::FREQHI3 => self.voices[2].wave.set_frequency_hi(value),
197            reg::PWLO3 => self.voices[2].wave.set_pulse_width_lo(value),
198            reg::PWHI3 => self.voices[2].wave.set_pulse_width_hi(value),
199            reg::CR3 => self.voices[2].set_control(value),
200            reg::AD3 => self.voices[2].envelope.set_attack_decay(value),
201            reg::SR3 => self.voices[2].envelope.set_sustain_release(value),
202            reg::FCLO => self.filter.set_fc_lo(value),
203            reg::FCHI => self.filter.set_fc_hi(value),
204            reg::RESFILT => self.filter.set_res_filt(value),
205            reg::MODVOL => self.filter.set_mode_vol(value),
206            _ => {}
207        }
208    }
209}