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}