resid/
voice.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::envelope::EnvelopeGenerator;
9use super::wave::{Syncable, WaveformGenerator};
10use super::ChipModel;
11
12/// The waveform output range is 0x000 to 0xfff, so the "zero"
13/// level should ideally have been 0x800. In the measured chip, the
14/// waveform output "zero" level was found to be 0x380 (i.e. $d41b
15/// = 0x38) at 5.94V.
16const WAVE_ZERO: i32 = 0x0380;
17
18/// The envelope multiplying D/A converter introduces another DC
19/// offset. This is isolated by the following measurements:
20///
21/// * The "zero" output level of the mixer at full volume is 5.44V.
22/// * Routing one voice to the mixer at full volume yields
23///     6.75V at maximum voice output (wave = 0xfff, sustain = 0xf)
24///     5.94V at "zero" voice output  (wave = any,   sustain = 0x0)
25///     5.70V at minimum voice output (wave = 0x000, sustain = 0xf)
26/// * The DC offset of one voice is (5.94V - 5.44V) = 0.50V
27/// * The dynamic range of one voice is |6.75V - 5.70V| = 1.05V
28/// * The DC offset is thus 0.50V/1.05V ~ 1/2 of the dynamic range.
29///
30/// Note that by removing the DC offset, we get the following ranges for
31/// one voice:
32///     y > 0: (6.75V - 5.44V) - 0.50V =  0.81V
33///     y < 0: (5.70V - 5.44V) - 0.50V = -0.24V
34/// The scaling of the voice amplitude is not symmetric about y = 0;
35/// this follows from the DC level in the waveform output.
36const VOICE_DC: i32 = 0x800 * 0xff;
37
38#[derive(Clone, Copy)]
39pub struct Voice {
40    // Configuration
41    wave_zero: i32,
42    voice_dc: i32,
43    // Generators
44    pub envelope: EnvelopeGenerator,
45    pub wave: WaveformGenerator,
46}
47
48impl Voice {
49    pub fn new(chip_model: ChipModel) -> Self {
50        match chip_model {
51            ChipModel::Mos6581 => Voice {
52                wave_zero: WAVE_ZERO,
53                voice_dc: VOICE_DC,
54                envelope: EnvelopeGenerator::default(),
55                wave: WaveformGenerator::new(chip_model),
56            },
57            ChipModel::Mos8580 => Voice {
58                // No DC offsets in the MOS8580.
59                wave_zero: 0x800,
60                voice_dc: 0,
61                envelope: EnvelopeGenerator::default(),
62                wave: WaveformGenerator::new(chip_model),
63            },
64        }
65    }
66
67    pub fn set_control(&mut self, value: u8) {
68        self.envelope.set_control(value);
69        self.wave.set_control(value);
70    }
71
72    /// Amplitude modulated 20-bit waveform output.
73    /// Range [-2048*255, 2047*255].
74    #[inline]
75    pub fn output(&self, sync_source: Option<&WaveformGenerator>) -> i32 {
76        // Multiply oscillator output with envelope output.
77        (self.wave.output(sync_source) as i32 - self.wave_zero) * self.envelope.output() as i32
78            + self.voice_dc
79    }
80
81    pub fn reset(&mut self) {
82        self.envelope.reset();
83        self.wave.reset();
84    }
85}
86
87impl Syncable<&'_ Voice> {
88    pub fn output(&self) -> i32 {
89        self.main.output(Some(&self.sync_source.wave))
90    }
91}
92
93impl<'a> Syncable<&'a Voice> {
94    pub fn wave(self) -> Syncable<&'a WaveformGenerator> {
95        Syncable {
96            main: &self.main.wave,
97            sync_dest: &self.sync_dest.wave,
98            sync_source: &self.sync_source.wave,
99        }
100    }
101}
102
103impl<'a> Syncable<&'a mut Voice> {
104    pub fn wave(self) -> Syncable<&'a mut WaveformGenerator> {
105        Syncable {
106            main: &mut self.main.wave,
107            sync_dest: &mut self.sync_dest.wave,
108            sync_source: &mut self.sync_source.wave,
109        }
110    }
111}