aym/backends/
precise.rs

1use crate::{AyMode, AymBackend, SoundChip, StereoSample, AY_REGISTER_COUNT};
2
3const TONE_CHANNELS: usize = 3;
4const DECIMATE_FACTOR: usize = 8;
5const FIR_SIZE: usize = 192;
6const DC_FILTER_SIZE: usize = 1024;
7
8#[derive(Default)]
9struct ToneChannel {
10    tone_period: u16,
11    tone_counter: u16,
12    tone: usize,
13    tone_off_bit: usize,
14    noise_off_bit: usize,
15    envelope_enabled: bool,
16    volume: usize,
17    pan_left: f64,
18    pan_right: f64,
19}
20
21#[derive(Default)]
22struct Interpolator {
23    c: [f64; 4],
24    y: [f64; 4],
25}
26
27struct DcFilter {
28    sum: f64,
29    delay: [f64; DC_FILTER_SIZE],
30}
31
32impl Default for DcFilter {
33    fn default() -> Self {
34        Self {
35            sum: 0.0,
36            delay: [0.0; DC_FILTER_SIZE],
37        }
38    }
39}
40
41/// Precise AY/YM sound chip generation backend.
42///
43/// Original code for this backend is derived from `ayumi` C library by Peter Sovietov
44/// Link to original repo: https://github.com/true-grue/ayumi
45///
46/// Uses f64 for computations.
47pub struct AymPrecise {
48    channels: [ToneChannel; TONE_CHANNELS],
49
50    noise_period: u16,
51    noise_counter: u16,
52    noise: usize,
53
54    envelope_counter: u16,
55    envelope_period: u16,
56    envelope_shape: usize,
57    envelope_segment: usize,
58    envelope: usize,
59
60    dac_table: &'static [f64; 32],
61    step: f64,
62    x: f64,
63    interpolator_left: Interpolator,
64    interpolator_right: Interpolator,
65
66    fir_left: [f64; FIR_SIZE * 2],
67    fir_right: [f64; FIR_SIZE * 2],
68    fir_index: usize,
69
70    dc_left: DcFilter,
71    dc_right: DcFilter,
72    dc_index: usize,
73
74    left: f64,
75    right: f64,
76
77    registers: [u8; AY_REGISTER_COUNT],
78    dc_filter: bool,
79}
80
81#[rustfmt::skip]
82const AY_DAC_TABLE: [f64; 32] = [
83    0.0, 0.0,
84    0.00999465934234, 0.00999465934234,
85    0.0144502937362, 0.0144502937362,
86    0.0210574502174, 0.0210574502174,
87    0.0307011520562, 0.0307011520562,
88    0.0455481803616, 0.0455481803616,
89    0.0644998855573, 0.0644998855573,
90    0.107362478065, 0.107362478065,
91    0.126588845655, 0.126588845655,
92    0.20498970016, 0.20498970016,
93    0.292210269322, 0.292210269322,
94    0.372838941024, 0.372838941024,
95    0.492530708782, 0.492530708782,
96    0.635324635691, 0.635324635691,
97    0.805584802014, 0.805584802014,
98    1.0, 1.0
99];
100
101#[rustfmt::skip]
102const YM_DAC_TABLE: [f64; 32] = [
103    0.0, 0.0,
104    0.00465400167849, 0.00772106507973,
105    0.0109559777218, 0.0139620050355,
106    0.0169985503929, 0.0200198367285,
107    0.024368657969, 0.029694056611,
108    0.0350652323186, 0.0403906309606,
109    0.0485389486534, 0.0583352407111,
110    0.0680552376593, 0.0777752346075,
111    0.0925154497597, 0.111085679408,
112    0.129747463188, 0.148485542077,
113    0.17666895552, 0.211551079576,
114    0.246387426566, 0.281101701381,
115    0.333730067903, 0.400427252613,
116    0.467383840696, 0.53443198291,
117    0.635172045472, 0.75800717174,
118    0.879926756695, 1.0
119];
120
121static ENVELOPES: [[fn(&mut AymPrecise); 2]; 16] = [
122    [AymPrecise::slide_down, AymPrecise::hold_bottom],
123    [AymPrecise::slide_down, AymPrecise::hold_bottom],
124    [AymPrecise::slide_down, AymPrecise::hold_bottom],
125    [AymPrecise::slide_down, AymPrecise::hold_bottom],
126    [AymPrecise::slide_up, AymPrecise::hold_bottom],
127    [AymPrecise::slide_up, AymPrecise::hold_bottom],
128    [AymPrecise::slide_up, AymPrecise::hold_bottom],
129    [AymPrecise::slide_up, AymPrecise::hold_bottom],
130    [AymPrecise::slide_down, AymPrecise::slide_down],
131    [AymPrecise::slide_down, AymPrecise::hold_bottom],
132    [AymPrecise::slide_down, AymPrecise::slide_up],
133    [AymPrecise::slide_down, AymPrecise::hold_top],
134    [AymPrecise::slide_up, AymPrecise::slide_up],
135    [AymPrecise::slide_up, AymPrecise::hold_top],
136    [AymPrecise::slide_up, AymPrecise::slide_down],
137    [AymPrecise::slide_up, AymPrecise::hold_bottom],
138];
139
140static ENVELOPE_RESET_TO_MAX: [[bool; 2]; 16] = [
141    [true, false],
142    [true, false],
143    [true, false],
144    [true, false],
145    [false, false],
146    [false, false],
147    [false, false],
148    [false, false],
149    [true, true],
150    [true, false],
151    [true, false],
152    [true, true],
153    [false, false],
154    [false, true],
155    [false, true],
156    [false, false],
157];
158
159impl AymPrecise {
160    fn new(is_ym: bool, clock_rate: f64, sample_rate: usize) -> Self {
161        let mut this = Self {
162            channels: Default::default(),
163            noise_period: 0,
164            noise_counter: 0,
165            noise: 0,
166            envelope_counter: 0,
167            envelope_period: 0,
168            envelope_shape: 0,
169            envelope_segment: 0,
170            envelope: 0,
171            dac_table: &AY_DAC_TABLE,
172            step: 0.0,
173            x: 0.0,
174            interpolator_left: Default::default(),
175            interpolator_right: Default::default(),
176            fir_left: [0.0; FIR_SIZE * 2],
177            fir_right: [0.0; FIR_SIZE * 2],
178            fir_index: 0,
179            dc_left: Default::default(),
180            dc_right: Default::default(),
181            dc_index: 0,
182            left: 0.0,
183            right: 0.0,
184            registers: [0; AY_REGISTER_COUNT],
185            dc_filter: false,
186        };
187
188        this.step = clock_rate / (sample_rate as f64 * 8f64 * DECIMATE_FACTOR as f64);
189        if is_ym {
190            this.dac_table = &YM_DAC_TABLE;
191        }
192        this.noise = 1;
193        this.set_envelope(1);
194        for i in 0..TONE_CHANNELS {
195            this.set_tone(i, 1);
196        }
197        this
198    }
199
200    fn set_pan(&mut self, index: usize, pan: f64, is_eqp: bool) {
201        if is_eqp {
202            self.channels[index].pan_left = libm::sqrt(1f64 - pan);
203            self.channels[index].pan_right = libm::sqrt(pan);
204        } else {
205            self.channels[index].pan_left = 1f64 - pan;
206            self.channels[index].pan_right = pan;
207        }
208    }
209
210    fn set_tone(&mut self, index: usize, period: u16) {
211        let period = period & 0xFFF;
212        self.channels[index].tone_period = (period == 0) as u16 | period;
213    }
214
215    fn set_noise(&mut self, period: u16) {
216        let period = period & 0x1F;
217        self.noise_period = (period == 0) as u16 | period;
218    }
219
220    fn set_mixer(
221        &mut self,
222        index: usize,
223        tone_enable: bool,
224        noise_enable: bool,
225        envelope_enabled: bool,
226    ) {
227        self.channels[index].tone_off_bit = (!tone_enable) as usize;
228        self.channels[index].noise_off_bit = (!noise_enable) as usize;
229        self.channels[index].envelope_enabled = envelope_enabled;
230    }
231
232    fn set_volume(&mut self, index: usize, volume: usize) {
233        self.channels[index].volume = volume & 0x0F;
234    }
235
236    fn set_envelope(&mut self, period: u16) {
237        self.envelope_period = (period == 0) as u16 | period;
238    }
239
240    fn set_envelope_shape(&mut self, shape: usize) {
241        self.envelope_shape = shape & 0x0F;
242        self.envelope_counter = 0;
243        self.envelope_segment = 0;
244        self.reset_segment();
245    }
246
247    fn process(&mut self) {
248        self.fir_index = (self.fir_index + 1) % (FIR_SIZE / DECIMATE_FACTOR - 1);
249        for i in (0..DECIMATE_FACTOR).rev() {
250            self.x += self.step;
251            if self.x >= 1.0 {
252                self.x -= 1.0;
253                self.interpolator_left.y[0] = self.interpolator_left.y[1];
254                self.interpolator_left.y[1] = self.interpolator_left.y[2];
255                self.interpolator_left.y[2] = self.interpolator_left.y[3];
256                self.interpolator_right.y[0] = self.interpolator_right.y[1];
257                self.interpolator_right.y[1] = self.interpolator_right.y[2];
258                self.interpolator_right.y[2] = self.interpolator_right.y[3];
259                self.update_mixer();
260                self.interpolator_left.y[3] = self.left;
261                self.interpolator_right.y[3] = self.right;
262                let y1 = self.interpolator_left.y[2] - self.interpolator_left.y[0];
263                self.interpolator_left.c[0] = 0.5 * self.interpolator_left.y[1]
264                    + 0.25 * (self.interpolator_left.y[0] + self.interpolator_left.y[2]);
265                self.interpolator_left.c[1] = 0.5 * y1;
266                self.interpolator_left.c[2] =
267                    0.25 * (self.interpolator_left.y[3] - self.interpolator_left.y[1] - y1);
268                let y1 = self.interpolator_right.y[2] - self.interpolator_right.y[0];
269                self.interpolator_right.c[0] = 0.5 * self.interpolator_right.y[1]
270                    + 0.25 * (self.interpolator_right.y[0] + self.interpolator_right.y[2]);
271                self.interpolator_right.c[1] = 0.5 * y1;
272                self.interpolator_right.c[2] =
273                    0.25 * (self.interpolator_right.y[3] - self.interpolator_right.y[1] - y1);
274            }
275            self.fir_left[FIR_SIZE - self.fir_index * DECIMATE_FACTOR..][i] =
276                (self.interpolator_left.c[2] * self.x + self.interpolator_left.c[1]) * self.x
277                    + self.interpolator_left.c[0];
278            self.fir_right[FIR_SIZE - self.fir_index * DECIMATE_FACTOR..][i] =
279                (self.interpolator_right.c[2] * self.x + self.interpolator_right.c[1]) * self.x
280                    + self.interpolator_right.c[0];
281        }
282        self.left = decimate(&mut self.fir_left[FIR_SIZE - self.fir_index * DECIMATE_FACTOR..]);
283        self.right = decimate(&mut self.fir_right[FIR_SIZE - self.fir_index * DECIMATE_FACTOR..]);
284    }
285
286    fn apply_dc_filter(&mut self) {
287        self.left = apply_dc_filter_for_sample(&mut self.dc_left, self.dc_index, self.left);
288        self.right = apply_dc_filter_for_sample(&mut self.dc_right, self.dc_index, self.right);
289        self.dc_index = (self.dc_index + 1) & (DC_FILTER_SIZE - 1);
290    }
291
292    fn slide_up(&mut self) {
293        if self.envelope == 31 {
294            self.envelope_segment ^= 1;
295            self.reset_segment();
296        } else {
297            self.envelope += 1;
298        }
299    }
300
301    fn slide_down(&mut self) {
302        if self.envelope == 0 {
303            self.envelope_segment ^= 1;
304            self.reset_segment();
305        } else {
306            self.envelope -= 1;
307        }
308    }
309
310    fn hold_top(&mut self) {}
311
312    fn hold_bottom(&mut self) {}
313
314    fn reset_segment(&mut self) {
315        if ENVELOPE_RESET_TO_MAX[self.envelope_shape][self.envelope_segment] {
316            self.envelope = 31;
317            return;
318        }
319        self.envelope = 0;
320    }
321
322    fn update_tone(&mut self, index: usize) -> usize {
323        let ch = &mut self.channels.as_mut()[index];
324        ch.tone_counter += 1;
325        if ch.tone_counter >= ch.tone_period {
326            ch.tone_counter = 0;
327            ch.tone ^= 1;
328        }
329
330        ch.tone
331    }
332
333    fn update_noise(&mut self) -> usize {
334        self.noise_counter += 1;
335        if self.noise_counter >= self.noise_period << 1 {
336            self.noise_counter = 0;
337            let bit0x3 = (self.noise ^ (self.noise >> 3)) & 1;
338            self.noise = (self.noise >> 1) | (bit0x3 << 16);
339        }
340
341        self.noise & 1
342    }
343
344    fn update_envelope(&mut self) -> usize {
345        self.envelope_counter += 1;
346        if self.envelope_counter >= self.envelope_period {
347            self.envelope_counter = 0;
348            ENVELOPES[self.envelope_shape][self.envelope_segment](self);
349        }
350        self.envelope
351    }
352
353    fn update_mixer(&mut self) {
354        let noise = self.update_noise();
355        let envelope = self.update_envelope();
356        self.left = 0.0;
357        self.right = 0.0;
358        for i in 0..TONE_CHANNELS {
359            let mut out = (self.update_tone(i) | self.channels[i].tone_off_bit)
360                & (noise | self.channels[i].noise_off_bit);
361            out *= if self.channels[i].envelope_enabled {
362                envelope
363            } else {
364                self.channels[i].volume * 2 + 1
365            };
366            assert!(out < 32);
367            self.left += self.dac_table[out] * self.channels[i].pan_left;
368            self.right += self.dac_table[out] * self.channels[i].pan_right;
369        }
370    }
371}
372
373#[allow(clippy::excessive_precision)]
374fn decimate(x: &mut [f64]) -> f64 {
375    assert!(x.len() >= FIR_SIZE);
376
377    let y = -0.0000046183113992051936 * (x[1] + x[191])
378        + -0.00001117761640887225 * (x[2] + x[190])
379        + -0.000018610264502005432 * (x[3] + x[189])
380        + -0.000025134586135631012 * (x[4] + x[188])
381        + -0.000028494281690666197 * (x[5] + x[187])
382        + -0.000026396828793275159 * (x[6] + x[186])
383        + -0.000017094212558802156 * (x[7] + x[185])
384        + 0.000023798193576966866 * (x[9] + x[183])
385        + 0.000051281160242202183 * (x[10] + x[182])
386        + 0.00007762197826243427 * (x[11] + x[181])
387        + 0.000096759426664120416 * (x[12] + x[180])
388        + 0.00010240229300393402 * (x[13] + x[179])
389        + 0.000089344614218077106 * (x[14] + x[178])
390        + 0.000054875700118949183 * (x[15] + x[177])
391        + -0.000069839082210680165 * (x[17] + x[175])
392        + -0.0001447966132360757 * (x[18] + x[174])
393        + -0.00021158452917708308 * (x[19] + x[173])
394        + -0.00025535069106550544 * (x[20] + x[172])
395        + -0.00026228714374322104 * (x[21] + x[171])
396        + -0.00022258805927027799 * (x[22] + x[170])
397        + -0.00013323230495695704 * (x[23] + x[169])
398        + 0.00016182578767055206 * (x[25] + x[167])
399        + 0.00032846175385096581 * (x[26] + x[166])
400        + 0.00047045611576184863 * (x[27] + x[165])
401        + 0.00055713851457530944 * (x[28] + x[164])
402        + 0.00056212565121518726 * (x[29] + x[163])
403        + 0.00046901918553962478 * (x[30] + x[162])
404        + 0.00027624866838952986 * (x[31] + x[161])
405        + -0.00032564179486838622 * (x[33] + x[159])
406        + -0.00065182310286710388 * (x[34] + x[158])
407        + -0.00092127787309319298 * (x[35] + x[157])
408        + -0.0010772534348943575 * (x[36] + x[156])
409        + -0.0010737727700273478 * (x[37] + x[155])
410        + -0.00088556645390392634 * (x[38] + x[154])
411        + -0.00051581896090765534 * (x[39] + x[153])
412        + 0.00059548767193795277 * (x[41] + x[151])
413        + 0.0011803558710661009 * (x[42] + x[150])
414        + 0.0016527320270369871 * (x[43] + x[149])
415        + 0.0019152679330965555 * (x[44] + x[148])
416        + 0.0018927324805381538 * (x[45] + x[147])
417        + 0.0015481870327877937 * (x[46] + x[146])
418        + 0.00089470695834941306 * (x[47] + x[145])
419        + -0.0010178225878206125 * (x[49] + x[143])
420        + -0.0020037400552054292 * (x[50] + x[142])
421        + -0.0027874356824117317 * (x[51] + x[141])
422        + -0.003210329988021943 * (x[52] + x[140])
423        + -0.0031540624117984395 * (x[53] + x[139])
424        + -0.0025657163651900345 * (x[54] + x[138])
425        + -0.0014750752642111449 * (x[55] + x[137])
426        + 0.0016624165446378462 * (x[57] + x[135])
427        + 0.0032591192839069179 * (x[58] + x[134])
428        + 0.0045165685815867747 * (x[59] + x[133])
429        + 0.0051838984346123896 * (x[60] + x[132])
430        + 0.0050774264697459933 * (x[61] + x[131])
431        + 0.0041192521414141585 * (x[62] + x[130])
432        + 0.0023628575417966491 * (x[63] + x[129])
433        + -0.0026543507866759182 * (x[65] + x[127])
434        + -0.0051990251084333425 * (x[66] + x[126])
435        + -0.0072020238234656924 * (x[67] + x[125])
436        + -0.0082672928192007358 * (x[68] + x[124])
437        + -0.0081033739572956287 * (x[69] + x[123])
438        + -0.006583111539570221 * (x[70] + x[122])
439        + -0.0037839040415292386 * (x[71] + x[121])
440        + 0.0042781252851152507 * (x[73] + x[119])
441        + 0.0084176358598320178 * (x[74] + x[118])
442        + 0.01172566057463055 * (x[75] + x[117])
443        + 0.013550476647788672 * (x[76] + x[116])
444        + 0.013388189369997496 * (x[77] + x[115])
445        + 0.010979501242341259 * (x[78] + x[114])
446        + 0.006381274941685413 * (x[79] + x[113])
447        + -0.007421229604153888 * (x[81] + x[111])
448        + -0.01486456304340213 * (x[82] + x[110])
449        + -0.021143584622178104 * (x[83] + x[109])
450        + -0.02504275058758609 * (x[84] + x[108])
451        + -0.025473530942547201 * (x[85] + x[107])
452        + -0.021627310017882196 * (x[86] + x[106])
453        + -0.013104323383225543 * (x[87] + x[105])
454        + 0.017065133989980476 * (x[89] + x[103])
455        + 0.036978919264451952 * (x[90] + x[102])
456        + 0.05823318062093958 * (x[91] + x[101])
457        + 0.079072012081405949 * (x[92] + x[100])
458        + 0.097675998716952317 * (x[93] + x[99])
459        + 0.11236045936950932 * (x[94] + x[98])
460        + 0.12176343577287731 * (x[95] + x[97])
461        + 0.125 * x[96];
462
463    let (src, dest) = x.split_at_mut(FIR_SIZE - DECIMATE_FACTOR);
464    dest[0..DECIMATE_FACTOR].copy_from_slice(&src[0..DECIMATE_FACTOR]);
465
466    y
467}
468
469fn apply_dc_filter_for_sample(dc: &mut DcFilter, index: usize, x: f64) -> f64 {
470    dc.sum += -dc.delay[index] + x;
471    dc.delay[index] = x;
472    x - dc.sum / DC_FILTER_SIZE as f64
473}
474
475impl AymPrecise {
476    /// Enabled dc filter for samples
477    pub fn enable_dc_filter(&mut self) {
478        self.dc_filter = true;
479    }
480}
481
482impl AymBackend for AymPrecise {
483    type SoundSample = f64;
484
485    fn new(chip: SoundChip, mode: AyMode, frequency: usize, sample_rate: usize) -> Self {
486        let mut ay = AymPrecise::new(matches!(chip, SoundChip::YM), frequency as f64, sample_rate);
487
488        let (pan_a, pan_b, pan_c) = match mode {
489            AyMode::Mono => (0.5, 0.5, 0.5),
490            AyMode::ABC => (0.0, 0.5, 1.0),
491            AyMode::ACB => (0.0, 1.0, 0.5),
492            AyMode::BAC => (0.5, 0.0, 1.0),
493            AyMode::BCA => (1.0, 0.0, 0.5),
494            AyMode::CAB => (0.5, 1.0, 0.0),
495            AyMode::CBA => (1.0, 0.5, 0.0),
496        };
497        ay.set_pan(0, pan_a, true);
498        ay.set_pan(1, pan_b, true);
499        ay.set_pan(2, pan_c, true);
500        ay
501    }
502
503    fn write_register(&mut self, address: u8, value: u8) {
504        if address as usize >= AY_REGISTER_COUNT {
505            return;
506        }
507
508        self.registers[address as usize] = value;
509
510        let r = self.registers;
511
512        match address {
513            0 | 1 => self.set_tone(0, u16::from_le_bytes([r[0], r[1] & 0x0f])),
514            2 | 3 => self.set_tone(1, u16::from_le_bytes([r[2], r[3] & 0x0f])),
515            4 | 5 => self.set_tone(2, u16::from_le_bytes([r[4], r[5] & 0x0f])),
516            6 => self.set_noise((r[6] & 0x1f) as u16),
517            7 => {
518                self.set_mixer(
519                    0,
520                    (r[7] & 0x01) == 0,
521                    (r[7] & 0x08) == 0,
522                    (r[8] & 0x10) != 0,
523                );
524                self.set_mixer(
525                    1,
526                    (r[7] & 0x02) == 0,
527                    (r[7] & 0x10) == 0,
528                    (r[9] & 0x10) != 0,
529                );
530                self.set_mixer(
531                    2,
532                    (r[7] & 0x04) == 0,
533                    (r[7] & 0x20) == 0,
534                    (r[10] & 0x10) != 0,
535                );
536            }
537            8 => {
538                self.set_mixer(
539                    0,
540                    (r[7] & 0x01) == 0,
541                    (r[7] & 0x08) == 0,
542                    (r[8] & 0x10) != 0,
543                );
544                self.set_volume(0, (r[8] & 0x0F) as usize);
545            }
546            9 => {
547                self.set_mixer(
548                    1,
549                    (r[7] & 0x02) == 0,
550                    (r[7] & 0x10) == 0,
551                    (r[9] & 0x10) != 0,
552                );
553                self.set_volume(1, (r[9] & 0x0F) as usize);
554            }
555            10 => {
556                self.set_mixer(
557                    2,
558                    (r[7] & 0x04) == 0,
559                    (r[7] & 0x20) == 0,
560                    (r[10] & 0x10) != 0,
561                );
562                self.set_volume(2, (r[10] & 0x0F) as usize);
563            }
564            11 | 12 => self.set_envelope(u16::from_le_bytes([r[11], r[12]])),
565            13 => self.set_envelope_shape((r[13] & 0x0F) as usize),
566            _ => unreachable!(),
567        }
568    }
569
570    fn next_sample(&mut self) -> StereoSample<Self::SoundSample> {
571        self.process();
572
573        if self.dc_filter {
574            self.apply_dc_filter();
575        }
576
577        StereoSample {
578            left: self.left,
579            right: self.right,
580        }
581    }
582}