psg/
lib.rs

1//! The PSG crate provides a fast and highly precise emulation of the [General Instruments
2//! AY-3-8910](https://en.wikipedia.org/wiki/General_Instrument_AY-3-8910) Programmable Sound
3//! Generator chip, as well as its most popular clone, the Yamaha YM2149.
4//!
5//! These PSG chips were used in some of the most popular home computers in the 1980s and early
6//! 1990s, such as the MSX family, the Sinclair ZX Spectrum, and the Atari ST.
7//!
8//! This particular implementation of the PSG chip was specifically built for use in emulation and
9//! music production (e.g. tracker) software, and comes with many useful extras to aid in writing
10//! such applications. Examples of these are math functions for easy period/frequency conversion,
11//! conversion to/from MIDI note numbers, and APIs for directly setting register values, as well as
12//! APIs that expose every individual property of the PSG chip.
13//!
14//! The crate is based on the excellent Ayumi by Peter Sovietov and includes several bug-fixes and
15//! improvements, while still being sample-accurate when compared to the original implementation.
16//!
17//! To get started, simply initialize a new [`PSG`] struct, set some registers, and start rendering in
18//! a loop:
19//!
20//! ```
21//! # use psg::PSG;
22//! // Initialize a new PSG with a clock rate of an MSX machine and a sampling rate of 44100 Hz.
23//! let mut psg = PSG::new(1789772.5, 44100)?;
24//!
25//! // Set some registers.
26//! let channel = psg.channel_mut(0);
27//! channel.set_period(100);
28//! channel.set_amplitude(8);
29//! channel.set_tone_disabled(false);
30//!
31//! // Render a second of audio.
32//! for _ in 0..44100 {
33//!     let (left, right) = psg.render();
34//!
35//!     // Do something useful with the samples here, such as writing to a file or playing on an
36//!     // audio device.
37//! }
38//! # Ok::<(), psg::Error>(())
39//! ```
40//!
41//! For more detailed information on how to use the crate, please have a look at the [`PSG`]
42//! struct, which is the workhorse of the crate.
43
44mod channel;
45mod dc_filter;
46mod decimator;
47mod envelope_generator;
48mod error;
49mod interpolator;
50mod noise_generator;
51
52pub mod math;
53
54pub use channel::Channel;
55pub use envelope_generator::EnvelopeGenerator;
56pub use error::Error;
57pub use noise_generator::NoiseGenerator;
58
59use decimator::{DECIMATE_FACTOR, Decimator, FIR_SIZE};
60use dc_filter::DCFilter;
61use interpolator::Interpolator;
62
63/// Digital-to-analog amplitude conversion table for the AY-3-8910. Internally, amplitudes are
64/// represented as 5-bit values. The AY only has 16 amplitude levels. This table therefore contains
65/// quantized values.
66const AY_DAC_TABLE: [f64; 32] = [
67    0.0,             0.0,             0.00999465934234, 0.00999465934234,
68    0.0144502937362, 0.0144502937362, 0.0210574502174,  0.0210574502174,
69    0.0307011520562, 0.0307011520562, 0.0455481803616,  0.0455481803616,
70    0.0644998855573, 0.0644998855573, 0.107362478065,   0.107362478065,
71    0.126588845655,  0.126588845655,  0.20498970016,    0.20498970016,
72    0.292210269322,  0.292210269322,  0.372838941024,   0.372838941024,
73    0.492530708782,  0.492530708782,  0.635324635691,   0.635324635691,
74    0.805584802014,  0.805584802014,  1.0,              1.0
75];
76
77/// Digital-to-analog amplitude conversion table for the YM2149, utilizing the full 5-bit dynamic
78/// range. Note that the PSG registers only support setting the amplitude as a 4-bit value, and
79/// that a value of 0 always represents a mute channel. Only the envelope generator uses 5-bit
80/// amplitudes.
81const YM_DAC_TABLE: [f64; 32] = [
82    0.0,             0.0,             0.00465400167849, 0.00772106507973,
83    0.0109559777218, 0.0139620050355, 0.0169985503929,  0.0200198367285,
84    0.024368657969,  0.029694056611,  0.0350652323186,  0.0403906309606,
85    0.0485389486534, 0.0583352407111, 0.0680552376593,  0.0777752346075,
86    0.0925154497597, 0.111085679408,  0.129747463188,   0.148485542077,
87    0.17666895552,   0.211551079576,  0.246387426566,   0.281101701381,
88    0.333730067903,  0.400427252613,  0.467383840696,   0.53443198291,
89    0.635172045472,  0.75800717174,   0.879926756695,   1.0
90];
91
92/// An enumeration of the various chip variants supported by the PSG struct.
93pub enum ChipType {
94    /// The original General Instrument AY-3-8910.
95    AY,
96
97    /// The Yamaha YM2149. In all respects identical to the AY-3-8910, except for the envelope
98    /// generator, which has double the resolution in its digital-to-analog converter, resulting in
99    /// smoother envelopes.
100    YM
101}
102
103impl ChipType {
104    /// Return a reference to the digital-to-analog amplitude conversion table for the current chip
105    /// type.
106    fn log2lin_table(&self) -> &'static [f64; 32] {
107        match self {
108            ChipType::AY => &AY_DAC_TABLE,
109            ChipType::YM => &YM_DAC_TABLE
110        }
111    }
112}
113
114/// The programmable sound generator (PSG). This struct is the workhorse of the crate and
115/// contains all state to fully emulate the selected chip, which can either be the original General
116/// Instrument AY-3-8912 or the Yamaha YM2149.
117///
118/// To get a proper audio signal, instantiate the struct with a sample rate of your choice, and a
119/// suitable chip clock rate. Here are some common clock rates:
120///
121///  - Amstrad CPC: 1 MHz
122///  - Atari ST: 2 MHz
123///  - MSX: 1.7897725 MHz
124///  - Oric-1: 1 MHz
125///  - ZX Spectrum: 1.7734 MHz
126///
127/// The base period unit used by the tone generators is the period of a clock cycle multiplied by
128/// 16 (the PSG contains a 16x frequency divider). Therefore, with a clock rate of 1 MHz tones with
129/// frequencies between 15.26 Hz and 31.25 kHz can be obtained, although in practice this would be
130/// limited by half the sampling frequency (the Nyquist frequency). Setting tone frequencies beyond
131/// this limit will produce aliasing and is not recommended.
132///
133/// The noise and envelope periods works similar, but instead of oscillating these components will
134/// produce a new value after every period.
135///
136/// Note that there are envelope shapes that have a repeating pattern (sawtooth and triangle
137/// waveforms) and that it is possible to set the envelope period to such a low value that its
138/// frequency falls into the audible range. This is the so-called "buzzer" effect and can be used
139/// to create timbres that vastly differ from the usual square wave and noise sounds. The effect
140/// works best when using the YM2149 chip type, as it has double the dynamic range in the envelope
141/// generator.
142pub struct PSG {
143    channels: [Channel; 3],
144    noise_generator: NoiseGenerator,
145    envelope_generator: EnvelopeGenerator,
146
147    log2lin_table: &'static [f64; 32],
148
149    // Clock signal
150    x: f64,
151    step: f64,
152
153    // Interpolators
154    left_interpolator: Interpolator,
155    right_interpolator: Interpolator,
156
157    // Decimators (anti-alias filters)
158    left_decimator: Decimator,
159    right_decimator: Decimator,
160    decimator_index: usize,
161
162    // DC filter
163    dc_filter: DCFilter
164}
165
166impl PSG {
167    /// Initialize a new PSG struct using the specified clock and sample rates.
168    ///
169    /// There is an upper bound to the clock rate that can be used for a given sample rate. This
170    /// upper limit can be computed by multiplying the sample rate by 128. Providing a clock rate
171    /// higher than this will return an error. For a 44100 Hz sample rate the highest supported
172    /// clock rate is 5.6448 MHz, well above the most popular PSG clock rates.
173    ///
174    /// By default the PSG is configured to emulate a Yamaha YM2149, but this can be changed
175    /// afterwards by calling [`set_chip_type`](Self::set_chip_type).
176    pub fn new(clock_rate: f64, sample_rate: u32) -> Result<Self, Error> {
177        // First compute the step value to determine if it is within bounds
178        let step = clock_rate / (sample_rate as f64 * 8.0 * DECIMATE_FACTOR as f64);
179
180        if step >= 1.0 {
181            return Err(Error::ClockRateTooHigh);
182        }
183
184        Ok(Self {
185            channels: [Channel::new(), Channel::new(), Channel::new()],
186            noise_generator: NoiseGenerator::new(),
187            envelope_generator: EnvelopeGenerator::new(),
188
189            log2lin_table: ChipType::YM.log2lin_table(),
190
191            x: 0.0,
192            step,
193
194            left_interpolator: Interpolator::new(),
195            right_interpolator: Interpolator::new(),
196
197            left_decimator: Decimator::new(),
198            right_decimator: Decimator::new(),
199            decimator_index: 0,
200
201            dc_filter: DCFilter::new()
202        })
203    }
204
205    /// Set the PSG chip type to the specified type.
206    ///
207    /// This only affects the envelope generator resolution, which is higher for the Yamaha YM2149.
208    pub fn set_chip_type(&mut self, chip_type: ChipType) {
209        self.log2lin_table = chip_type.log2lin_table();
210    }
211
212    /// Render the next PSG clock tick.
213    ///
214    /// Returns a tuple containing the left channel as the first element and the right channel as
215    /// the second.
216    fn render_tick(&mut self) -> (f64, f64) {
217        let noise = self.noise_generator.render();
218        let envelope = self.envelope_generator.render();
219
220        self.channels.iter_mut().fold((0.0, 0.0), |(left, right), channel| {
221            let mut level = (channel.render() | channel.tone_off as u8) & (noise | channel.noise_off as u8);
222
223            level *= if channel.envelope_on {
224                envelope
225            } else {
226                channel.amplitude * 2 + 1
227            };
228
229            let amplitude = self.log2lin_table[level as usize];
230
231            (left + amplitude * channel.pan_left, right + amplitude * channel.pan_right)
232        })
233    }
234
235    /// Render the next frame.
236    ///
237    /// Returns a tuple containing the left channel as the first element and the right channel as
238    /// the second.
239    pub fn render(&mut self) -> (f64, f64) {
240        let decimator_start = FIR_SIZE - self.decimator_index * DECIMATE_FACTOR;
241
242        // modulo 23
243        self.decimator_index = (self.decimator_index + 1) % (FIR_SIZE / DECIMATE_FACTOR - 1);
244
245        // Fill decimator buffers in reverse
246        // TODO: Since the filter is symmetrical, does this matter?
247        for offset in (0..DECIMATE_FACTOR).rev() {
248            self.x += self.step;
249
250            if self.x >= 1.0 {
251                self.x -= 1.0;
252
253                let (left, right) = self.render_tick();
254
255                self.left_interpolator.feed(left);
256                self.right_interpolator.feed(right);
257            }
258
259            self.left_decimator.buffer[decimator_start + offset] = self.left_interpolator.interpolate(self.x);
260            self.right_decimator.buffer[decimator_start + offset] = self.right_interpolator.interpolate(self.x);
261        }
262
263        self.dc_filter.render(
264            self.left_decimator.render(decimator_start),
265            self.right_decimator.render(decimator_start)
266        )
267    }
268
269    /// Return a reference to the specified channel number's [`Channel`] struct.
270    ///
271    /// The channel number must be smaller than 3.
272    pub fn channel(&self, index: u8) -> &Channel {
273        &self.channels[index as usize]
274    }
275
276    /// Return a mutable reference to the specified channel number's [`Channel`] struct.
277    ///
278    /// The channel number must be smaller than 3.
279    pub fn channel_mut(&mut self, index: u8) -> &mut Channel {
280        &mut self.channels[index as usize]
281    }
282
283    /// Return a reference to the PSG's noise generator.
284    pub fn noise_generator(&self) -> &NoiseGenerator {
285        &self.noise_generator
286    }
287
288    /// Return a mutable reference to the PSG's noise generator.
289    pub fn noise_generator_mut(&mut self) -> &mut NoiseGenerator {
290        &mut self.noise_generator
291    }
292
293    /// Return a reference to the PSG's envelope generator.
294    pub fn envelope_generator(&self) -> &EnvelopeGenerator {
295        &self.envelope_generator
296    }
297
298    /// Return a mutable reference to the PSG's envelope generator.
299    pub fn envelope_generator_mut(&mut self) -> &mut EnvelopeGenerator {
300        &mut self.envelope_generator
301    }
302
303    /// Set a channel's tone period to a value between 1 and 4095 inclusive.
304    ///
305    /// Smaller values are set to 1, larger values are wrapped. The channel number must be smaller
306    /// than 3.
307    pub fn set_tone_period(&mut self, channel: u8, period: u16) {
308        self.channels[channel as usize].set_period(period);
309    }
310
311    /// Set a channel's amplitude to a value between 0 and 15 inclusive.
312    ///
313    /// Larger values are wrapped. The channel number must be smaller than 3.
314    pub fn set_amplitude(&mut self, channel: u8, amplitude: u8) {
315        self.channels[channel as usize].set_amplitude(amplitude);
316    }
317
318    /// Set a channel's tone disable flag.
319    ///
320    /// The channel number must be smaller than 3.
321    pub fn set_tone_disabled(&mut self, channel: u8, disabled: bool) {
322        self.channels[channel as usize].set_tone_disabled(disabled);
323    }
324
325    /// Set a channel's noise disable flag.
326    ///
327    /// The channel number must be smaller than 3.
328    pub fn set_noise_disabled(&mut self, channel: u8, disabled: bool) {
329        self.channels[channel as usize].set_noise_disabled(disabled);
330    }
331
332    /// Set a channel's envelope enable flag.
333    ///
334    /// The channel number must be smaller than 3.
335    pub fn set_envelope_enabled(&mut self, channel: u8, enabled: bool) {
336        self.channels[channel as usize].set_envelope_enabled(enabled);
337    }
338
339    /// Set the noise generator's period to a value between 1 and 31 inclusive.
340    ///
341    /// Smaller values are set to 1, larger values are wrapped.
342    pub fn set_noise_period(&mut self, period: u8) {
343        self.noise_generator.set_period(period);
344    }
345
346    /// Set the PSG's mixer register value.
347    ///
348    /// The mixer value is an 8-bit number consisting of the following bits:
349    ///
350    /// Bit 0: Channel A tone enable (0 to enable, 1 to disable) \
351    /// Bit 1: Channel B tone enable (0 to enable, 1 to disable) \
352    /// Bit 2: Channel C tone enable (0 to enable, 1 to disable) \
353    /// Bit 3: Channel A noise enable (0 to enable, 1 to disable) \
354    /// Bit 4: Channel B noise enable (0 to enable, 1 to disable) \
355    /// Bit 5: Channel C noise enable (0 to enable, 1 to disable) \
356    /// Bit 6: GPIO In/out A toggle (ignored in this implementation) \
357    /// Bit 7: GPIO In/out B toggle (ignored in this implementation)
358    pub fn set_mixer(&mut self, mixer: u8) {
359        self.channels[0].set_tone_disabled(mixer & 0x01 != 0);
360        self.channels[1].set_tone_disabled(mixer & 0x02 != 0);
361        self.channels[2].set_tone_disabled(mixer & 0x04 != 0);
362        self.channels[0].set_noise_disabled(mixer & 0x08 != 0);
363        self.channels[1].set_noise_disabled(mixer & 0x10 != 0);
364        self.channels[2].set_noise_disabled(mixer & 0x20 != 0);
365
366        // Note: the GPIO bits are ignored
367    }
368
369    /// Set the envelope generator period to a value between 1 and 65535 inclusive.
370    ///
371    /// Lower values are set to 1.
372    pub fn set_envelope_period(&mut self, period: u16) {
373        self.envelope_generator.set_period(period);
374    }
375
376    /// Set shape to a value between 0 and 15 inclusive.
377    ///
378    /// Higher values are wrapped.
379    pub fn set_envelope_shape(&mut self, shape: u8) {
380        self.envelope_generator.set_shape(shape);
381    }
382
383    /// Set a PSG register to the provided value.
384    ///
385    /// This function is particularly useful when writing emulators, as it provides a convenient
386    /// API to the PSG's address/data ports that is easy to map from machine code.
387    ///
388    /// For an exact specification of the register numbers and their accepted values, please refer
389    /// to the AY-3-8910 or YM2149 datasheets. Note that the AY-3-8910 datasheet uses octal numbers
390    /// when referring to register numbers.
391    ///
392    /// Please note that the GPIO registers (14 and 15) are ignored in this implementation, and
393    /// that writing to any register number higher than 15 will have no effect.
394    pub fn set_register(&mut self, register: u8, value: u8) {
395        // Note: the AY-3-8910 datasheet uses octal register numbers. The YM2149 datasheet uses
396        // decimal numbers.
397        match register {
398            0 => self.channels[0].set_period_lsb(value),
399            1 => self.channels[0].set_period_msb(value),
400            2 => self.channels[1].set_period_lsb(value),
401            3 => self.channels[1].set_period_msb(value),
402            4 => self.channels[2].set_period_lsb(value),
403            5 => self.channels[2].set_period_msb(value),
404            6 => self.noise_generator.set_period(value),
405            7 => self.set_mixer(value),
406            8 => self.channels[0].set_amplitude_and_envelope_enabled(value),
407            9 => self.channels[1].set_amplitude_and_envelope_enabled(value),
408            10 => self.channels[2].set_amplitude_and_envelope_enabled(value),
409            11 => self.envelope_generator.set_period_lsb(value),
410            12 => self.envelope_generator.set_period_msb(value),
411            13 => self.envelope_generator.set_shape(value),
412            14 => (), // GPIO port A data store is ignored here
413            15 => (), // GPIO port B data store is ignored here
414            _ => ()
415        }
416    }
417}