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}