spectrusty_peripherals/ay/
audio.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! The emulation of the AY-3-8910/8912/8913 sound generator.
9use core::num::NonZeroU16;
10use core::marker::PhantomData;
11
12#[cfg(feature = "snapshot")]
13use serde::{Serialize, Deserialize};
14
15use super::{AyRegister, AyRegChange};
16use spectrusty_core::audio::*;
17
18/// Internal clock divisor.
19pub const INTERNAL_CLOCK_DIVISOR: FTs = 16;
20/// Cpu clock ratio.
21pub const HOST_CLOCK_RATIO: FTs = 2;
22
23/// Amplitude levels for AY-3-891x.
24///
25/// These levels are closest to the specs.
26///
27/// Original comment from [game-music-emu]:
28/// ```text
29///    // With channels tied together and 1K resistor to ground (as datasheet recommends),
30///    // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step.
31/// ```
32/// [AyAmps] struct implements `AMPS` for [AmpLevels]. See also [FUSE_AMPS].
33///
34/// [game-music-emu]: https://bitbucket.org/mpyne/game-music-emu/src/013d4676c689dc49f363f99dcfb8b88f22278236/gme/Ay_Apu.cpp#lines-32
35#[allow(clippy::approx_constant,clippy::excessive_precision)]
36pub const AMPS: [f32;16] = [0.000_000, 0.007_813, 0.011_049, 0.015_625,
37                            0.022_097, 0.031_250, 0.044_194, 0.062_500,
38                            0.088_388, 0.125_000, 0.176_777, 0.250_000,
39                            0.353_553, 0.500_000, 0.707_107, 1.000_000];
40
41pub const AMPS_I32: [i32;16] = [0x0000_0000, 0x0100_0431, 0x016a_0db9, 0x01ff_ffff,
42                                0x02d4_1313, 0x03ff_ffff, 0x05a8_2627, 0x07ff_ffff,
43                                0x0b50_4c4f, 0x0fff_ffff, 0x16a0_a0ff, 0x1fff_ffff,
44                                0x2d41_397f, 0x3fff_ffff, 0x5a82_7b7f, 0x7fff_ffff];
45
46pub const AMPS_I16: [i16;16] = [0x0000, 0x0100, 0x016a, 0x01ff,
47                                0x02d4, 0x03ff, 0x05a8, 0x07ff,
48                                0x0b50, 0x0fff, 0x16a0, 0x1fff,
49                                0x2d40, 0x3fff, 0x5a81, 0x7fff];
50
51/// These AY-3-891x amplitude levels are being used in the ["Free Unix Spectrum Emulator"] emulator.
52///
53/// The original comment below:
54/// ```text
55///  /* AY output doesn't match the claimed levels; these levels are based
56///   * on the measurements posted to comp.sys.sinclair in Dec 2001 by
57///   * Matthew Westcott, adjusted as I described in a followup to his post,
58///   * then scaled to 0..0xffff.
59///   */
60/// ```
61/// These are more linear than [AMPS].
62/// [AyFuseAmps] struct implements `FUSE_AMPS` for [AmpLevels].
63///
64/// ["Free Unix Spectrum Emulator"]: http://fuse-emulator.sourceforge.net/
65#[allow(clippy::unreadable_literal,clippy::excessive_precision)]
66pub const FUSE_AMPS: [f32;16] = [0.000000000, 0.0137483785, 0.020462349, 0.029053178,
67                                 0.042343784, 0.0618448150, 0.084718090, 0.136903940,
68                                 0.169131000, 0.2646677500, 0.352712300, 0.449942770,
69                                 0.570382240, 0.6872816000, 0.848172700, 1.000000000];
70
71pub const FUSE_AMPS_I16: [i16;16] = [0x0000, 0x01c2, 0x029e, 0x03b8,
72                                     0x056b, 0x07ea, 0x0ad8, 0x1186,
73                                     0x15a6, 0x21e0, 0x2d25, 0x3997,
74                                     0x4902, 0x57f8, 0x6c90, 0x7fff];
75
76/// This may be used to calculate other levels, but I'd discourage from using it in the player
77/// as it uses expensive float calculations.
78pub struct LogAmpLevels16<T>(PhantomData<T>);
79impl<T: Copy + FromSample<f32>> AmpLevels<T> for LogAmpLevels16<T> {
80    fn amp_level(level: u32) -> T {
81        // as proposed by https://www.dr-lex.be/info-stuff/volumecontrols.html
82        const A: f32 = 3.1623e-3;
83        const B: f32 = 5.757;
84        let y: f32 = match level & 0xF {
85            0  => 0.0,
86            15 => 1.0,
87            l => {
88                let x = l as f32 / 15.0;
89                A * (B * x).exp()
90            }
91        };
92        T::from_sample(y)
93    }
94}
95
96/// A struct implementing [AmpLevels] for Ay-3-891x sound chip. See also [AMPS].
97pub struct AyAmps<T>(PhantomData<T>);
98/// A struct implementing alternative [AmpLevels] for Ay-3-891x sound chip. See also [FUSE_AMPS].
99pub struct AyFuseAmps<T>(PhantomData<T>);
100
101macro_rules! impl_ay_amp_levels {
102    ($([$name:ident, $ty:ty, $amps:ident]),*) => { $(
103        impl AmpLevels<$ty> for $name<$ty> {
104            #[inline(always)]
105            fn amp_level(level: u32) -> $ty {
106                $amps[(level & 15) as usize]
107            }
108        }
109    )* };
110}
111impl_ay_amp_levels!(
112    [AyAmps, f32, AMPS], [AyAmps, i32, AMPS_I32], [AyAmps, i16, AMPS_I16],
113    [AyFuseAmps, f32, FUSE_AMPS], [AyFuseAmps, i16, FUSE_AMPS_I16]);
114
115/// A trait for interfacing controllers to render square-wave audio pulses from an AY-3-891x emulator.
116pub trait AyAudioFrame<B: Blep> {
117    /// Renders square-wave pulses via [Blep] interface.
118    ///
119    /// Provide [AmpLevels] that can handle `level` values from 0 to 15 (4-bits).
120    /// `channels` - target [Blep] audio channels for `[A, B, C]` AY-3-891x channels.
121    fn render_ay_audio_frame<V: AmpLevels<B::SampleDelta>>(
122        &mut self,
123        blep: &mut B,
124        channels: [usize; 3]
125    );
126}
127
128/// Implements AY-3-8910/8912/8913 programmable sound generator.
129///
130/// For the implementation of I/O ports see [crate::ay].
131#[derive(Default, Clone, Debug)]
132#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
133#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
134pub struct Ay3_891xAudio {
135    current_ts: FTs,
136    last_levels: [u8; 3],
137    amp_levels: [AmpLevel; 3],
138    env_control: EnvelopeControl,
139    noise_control: NoiseControl,
140    tone_control: [ToneControl; 3],
141    mixer: Mixer,
142}
143
144/// A type for AY-3-891x amplitude level register values.
145#[derive(Default, Clone, Copy, Debug)]
146#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
147struct AmpLevel(u8);
148
149impl AmpLevel {
150    #[inline]
151    pub fn set(&mut self, level: u8) {
152        self.0 = level & 0x1F;
153    }
154    #[inline]
155    pub fn is_env_control(self) -> bool {
156        self.0 & 0x10 != 0
157    }
158}
159
160/// A type for AY-3-891x mixer controller register values.
161#[derive(Default, Clone, Copy, Debug)]
162#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
163struct Mixer(u8);
164
165impl Mixer {
166    #[inline]
167    pub fn has_tone(self) -> bool {
168        self.0 & 1 == 0
169    }
170    #[inline]
171    pub fn has_noise(self) -> bool {
172        self.0 & 8 == 0
173    }
174    #[inline]
175    pub fn next_chan(&mut self) {
176        self.0 >>= 1
177    }
178}
179
180// TODO: make bitflags
181pub const ENV_SHAPE_CONT_MASK:   u8 = 0b0000_1000;
182pub const ENV_SHAPE_ATTACK_MASK: u8 = 0b0000_0100;
183pub const ENV_SHAPE_ALT_MASK:    u8 = 0b0000_0010;
184pub const ENV_SHAPE_HOLD_MASK:   u8 = 0b0000_0001;
185const ENV_LEVEL_REV_MASK:    u8 = 0b1000_0000;
186const ENV_LEVEL_MOD_MASK:    u8 = 0b0100_0000;
187const ENV_LEVEL_MASK:        u8 = 0x0F;
188const ENV_CYCLE_MASK:        u8 = 0xF0;
189
190/// A type implementing AY-3-891x volume envelope progression.
191#[derive(Clone, Copy, Debug)]
192#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
193struct EnvelopeControl {
194    period: u16,
195    tick: u16,
196    // c c c c CT AT AL HO
197    cycle: u8,
198    // RV MD 0 0 v v v v
199    level: u8
200}
201
202impl Default for EnvelopeControl {
203    fn default() -> Self {
204        EnvelopeControl { period: 1, tick: 0, cycle: 0, level: 0 }
205    }
206}
207
208impl EnvelopeControl {
209    #[inline]
210    fn set_shape(&mut self, shape: u8) {
211        self.tick = 0;
212        self.cycle = shape & !ENV_CYCLE_MASK;
213        self.level = if shape & ENV_SHAPE_ATTACK_MASK != 0 {
214            ENV_LEVEL_MOD_MASK
215        }
216        else {
217            ENV_LEVEL_MOD_MASK|ENV_LEVEL_REV_MASK|ENV_LEVEL_MASK
218        }
219    }
220    #[inline]
221    fn set_period_fine(&mut self, perlo: u8) {
222        self.set_period(self.period & 0xFF00 | perlo as u16)
223    }
224
225    #[inline]
226    fn set_period_coarse(&mut self, perhi: u8) {
227        self.set_period(u16::from_le_bytes([self.period as u8, perhi]))
228    }
229    #[inline]
230    fn set_period(&mut self, mut period: u16) {
231        if period == 0 { period = 1 }
232        self.period = period;
233        if self.tick >= period {
234            self.tick %= period;
235        }
236    }
237    #[inline]
238    fn get_level(&self) -> u8 {
239        self.level & ENV_LEVEL_MASK
240    }
241    #[inline]
242    fn get_shape(&self) -> u8 {
243        self.cycle & !ENV_CYCLE_MASK
244    }
245    #[inline]
246    fn update_level(&mut self) -> u8 {
247        let EnvelopeControl { period, mut tick, mut level, .. } = *self;
248        if tick >= period {
249            tick -= period;
250
251            if level & ENV_LEVEL_MOD_MASK != 0 {
252                level = (level & !ENV_LEVEL_MASK) | (
253                    if level & ENV_LEVEL_REV_MASK == 0 {
254                        level.wrapping_add(1)
255                    }
256                    else {
257                        level.wrapping_sub(1)
258                    }
259                & ENV_LEVEL_MASK);
260
261                let cycle = self.cycle.wrapping_add(0x10); // 16 times
262                if cycle & ENV_CYCLE_MASK == 0 {
263                    if cycle & ENV_SHAPE_CONT_MASK == 0 {
264                        level = 0;
265                    }
266                    else if cycle & ENV_SHAPE_HOLD_MASK != 0 {
267                        if cycle & ENV_SHAPE_ALT_MASK == 0 {
268                            level ^= ENV_LEVEL_MOD_MASK|ENV_LEVEL_MASK;
269                        }
270                        else {
271                            level ^= ENV_LEVEL_MOD_MASK;
272                        }
273                    }
274                    else if cycle & ENV_SHAPE_ALT_MASK != 0 {
275                        level ^= ENV_LEVEL_REV_MASK|ENV_LEVEL_MASK;
276                    }
277                }
278                self.level = level;
279                self.cycle = cycle;
280            }
281        }
282        self.tick = tick.wrapping_add(1);
283        level & ENV_LEVEL_MASK
284    }
285}
286
287const NOISE_PERIOD_MASK: u8 = 0x1F;
288
289/// A type implementing AY-3-891x noise progression.
290#[derive(Clone, Copy, Debug)]
291#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
292struct NoiseControl {
293    rng: i32,
294    period: u8,
295    tick: u8,
296    low: bool,
297}
298
299impl Default for NoiseControl {
300    fn default() -> Self {
301        NoiseControl { rng: 1, period: 0, tick: 0, low: false }
302    }
303}
304
305impl NoiseControl {
306    #[inline]
307    fn set_period(&mut self, mut period: u8) {
308        period &= NOISE_PERIOD_MASK;
309        if period == 0 { period = 1 }
310        self.period = period;
311        if self.tick >= period {
312            self.tick %= period;
313        }
314    }
315
316    #[inline]
317    fn update_is_low(&mut self) -> bool {
318        let NoiseControl { mut rng, period, mut tick, mut low } = *self;
319        if tick >= period {
320            tick -= period;
321
322            if (rng + 1) & 2 != 0 {
323                low = !low;
324                self.low = low;
325            }
326            rng = (-(rng & 1) & 0x12000) ^ (rng >> 1);
327            self.rng = rng;
328        }
329        self.tick = tick.wrapping_add(1);
330        low
331    }
332}
333
334const TONE_GEN_MIN_THRESHOLD: u16 = 5;
335const TONE_PERIOD_MASK: u16 = 0xFFF;
336
337/// A type implementing AY-3-891x tone progression.
338#[derive(Default, Clone, Copy, Debug)]
339#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
340struct ToneControl {
341    period: u16,
342    tick: u16,
343    low: bool
344}
345
346
347impl ToneControl {
348    #[inline]
349    fn set_period_fine(&mut self, perlo: u8) {
350        self.set_period(self.period & 0xFF00 | perlo as u16)
351    }
352
353    #[inline]
354    fn set_period_coarse(&mut self, perhi: u8) {
355        self.set_period(u16::from_le_bytes([self.period as u8, perhi]))
356    }
357
358    #[inline]
359    fn set_period(&mut self, mut period: u16) {
360        period &= TONE_PERIOD_MASK;
361        if period == 0 { period = 1 }
362        self.period = period;
363        if self.tick >= period*2 {
364            self.tick %= period*2;
365        }
366    }
367
368    #[inline]
369    fn update_is_low(&mut self) -> bool {
370        let ToneControl { period, mut tick, mut low } = *self;
371        if period < TONE_GEN_MIN_THRESHOLD {
372            low = false;
373        }
374        else if tick >= period {
375            tick -= period;
376            low = !low;
377            self.low = low;
378        }
379        self.tick = tick.wrapping_add(2);
380        low
381    }
382}
383
384/// A type implementing timestamp iterator.
385#[derive(Clone, Copy, Debug)]
386struct Ticker {
387    current: FTs,
388    end_ts: FTs
389}
390
391impl Ticker {
392    const CLOCK_INCREASE: FTs = HOST_CLOCK_RATIO * INTERNAL_CLOCK_DIVISOR;
393    fn new(current: FTs, end_ts: FTs) -> Self {
394        Ticker { current, end_ts }
395    }
396}
397
398impl Iterator for Ticker {
399    type Item = FTs;
400    fn next(&mut self) -> Option<FTs> {
401        let res = self.current;
402        if res < self.end_ts {
403            self.current = res + Self::CLOCK_INCREASE;
404            Some(res)
405        }
406        else {
407            None
408        }
409    }
410}
411
412/// Use the [Default] trait to create instances of this struct.
413impl Ay3_891xAudio {
414    /// Resets the internal state to the one initialized with.
415    pub fn reset(&mut self) {
416        *self = Default::default()
417    }
418    /// Converts a tone frequency given in Hz to a closest 16-bit tone period register value.
419    ///
420    /// `clock_hz` AY-3-891x clock frequency in Hz. In ZX Spectrum it equals to CPU_HZ / 2.
421    /// Amstrad CPC has PSG clocked at 1 MHz. Atari ST at 2 MHz.
422    ///
423    /// Returns `None` if the result can't be properly represented by 16-bit unsigned integer or if
424    /// the result is `0`.
425    #[allow(clippy::float_cmp)]
426    pub fn freq_to_tone_period(clock_hz: f32, hz: f32) -> Option<NonZeroU16> {
427        let ftp = (clock_hz / (INTERNAL_CLOCK_DIVISOR as f32 * hz)).round();
428        let utp = ftp as u16;
429        if utp as f32 != ftp {
430            None
431        }
432        else {
433            NonZeroU16::new(utp)
434        }
435    }
436    /// Converts a 16-bit tone period register value to a tone frequency in Hz.
437    ///
438    /// `clock_hz` AY-3-891x clock frequency in Hz. In ZX Spectrum it equals to CPU_HZ / 2.
439    /// Amstrad CPC has PSG clocked at 1 MHz. Atari ST at 2 MHz.
440    pub fn tone_period_to_freq(clock_hz: f32, tp: u16) -> f32 {
441        clock_hz / (tp as f32 * INTERNAL_CLOCK_DIVISOR as f32)
442    }
443    /// Creates an iterator of tone periods for the AY-3-891x chip.
444    ///
445    /// `min_octave` A minimal octave number, 0-based (0 is the minimum).
446    /// `max_octave` A maximal octave number, 0-based (7 is the maximum).
447    /// `note_freqs` An array of tone frequencies (in Hz) in the 5th octave (0-based: 4).
448    ///  To generate frequencies you may want to use audio::music::equal_tempered_scale_note_freqs.
449    /// `clock_hz` The AY-3-891x clock frequency in Hz. Usually, it's CPU_HZ / 2.
450    ///
451    /// # Panics
452    /// Panics if any period can't be expressed by 16-bit unsigned integer.
453    pub fn tone_periods<I>(
454                clock_hz: f32,
455                min_octave: i32,
456                max_octave: i32,
457                note_freqs: I
458            ) -> impl IntoIterator<Item=u16>
459        where I: Clone + IntoIterator<Item=f32>
460    {
461        (min_octave..=max_octave).flat_map(move |octave| {
462          note_freqs.clone().into_iter().map(move |hz| {
463            let hz = hz * (2.0f32).powi(octave - 4);
464            Self::freq_to_tone_period(clock_hz, hz)
465                 .expect("frequency out of range")
466                 .get()
467          })
468        })
469    }
470    /// Renders square-wave audio pulses via the [Blep] interface while mutating the internal state.
471    ///
472    /// The internal state is being altered every [INTERNAL_CLOCK_DIVISOR] * [HOST_CLOCK_RATIO] Cpu
473    /// clock cycles until `end_ts` is reached. The internal cycle counter is then decremented by the
474    /// value of `frame_tstates` before returning from this method.
475    ///
476    /// Provide [AmpLevels] that can handle `level` values from 0 to 15 (4-bits).
477    ///
478    /// * `changes` should be ordered by `time` and recorded only with `time` < `end_ts`
479    ///   otherwise, some register changes may be lost - the iterator will be drained anyway.
480    /// * `end_ts` should be a value of an end of frame T-state counter value.
481    /// * `frame_tstates` should be a duration of a single frame in T-states.
482    /// * `channels` - indicate [Blep] audio channels for `[A, B, C]` AY channels.
483    pub fn render_audio<V,I,A>(&mut self,
484                changes: I,
485                blep: &mut A,
486                end_ts: FTs,
487                frame_tstates: FTs,
488                chans: [usize; 3]
489            )
490        where V: AmpLevels<A::SampleDelta>,
491              I: IntoIterator<Item=AyRegChange>,
492              A: Blep
493    {
494        let mut change_iter = changes.into_iter().peekable();
495        let mut ticker = Ticker::new(self.current_ts, end_ts);
496        let mut tone_levels: [u8; 3] = self.last_levels;
497        let mut vol_levels: [A::SampleDelta;3] = Default::default();
498
499        for (level, tgt_amp) in tone_levels.iter().copied()
500                                .zip(vol_levels.iter_mut()) {
501            *tgt_amp = V::amp_level(level.into());
502        }
503        for tick in &mut ticker {
504            while let Some(change) = change_iter.peek() {
505                if change.time <= tick {
506                    let AyRegChange { reg, val, .. } = change_iter.next().unwrap();
507                    self.update_register(reg, val);
508                }
509                else {
510                    break
511                }
512            }
513
514
515            let env_level = self.env_control.update_level();
516            let noise_low = self.noise_control.update_is_low();
517            let mut mixer = self.mixer;
518            for ((level, tone_control), tgt_lvl) in self.amp_levels.iter()
519                                                    .zip(self.tone_control.iter_mut())
520                                                        .zip(tone_levels.iter_mut()) {
521                *tgt_lvl = if (mixer.has_tone() && tone_control.update_is_low()) ||
522                   (mixer.has_noise() && noise_low) {
523                    0
524                }
525                else if level.is_env_control() {
526                    env_level
527                }
528                else {
529                    level.0
530                };
531                mixer.next_chan();
532            }
533
534            for (chan, (level, last_vol)) in chans.iter().copied()
535                                                  .zip(tone_levels.iter().copied()
536                                                  .zip(vol_levels.iter_mut())) {
537                let vol = V::amp_level(level.into());
538                if let Some(delta) = last_vol.sample_delta(vol) {
539                    blep.add_step(chan, tick, delta);
540                    *last_vol = vol;
541                }
542            }
543
544        }
545        for AyRegChange { reg, val, .. } in change_iter {
546            self.update_register(reg, val);
547        }
548
549        self.current_ts = ticker.current - frame_tstates;
550        self.last_levels = tone_levels;
551    }
552    /// Updates the value of one of the sound generator registers for the indicated `reg` register,
553    /// with the value given in `val`.
554    ///
555    /// This method can be used to instantly set the state of the sound generator without the need
556    /// to generate audio pulses.
557    #[inline]
558    pub fn update_register(&mut self, reg: AyRegister, val: u8) {
559        use AyRegister::*;
560        match reg {
561            ToneFineA|ToneFineB|ToneFineC => {
562                self.tone_control[usize::from(reg) >> 1].set_period_fine(val)
563            }
564            ToneCoarseA|ToneCoarseB|ToneCoarseC => {
565                self.tone_control[usize::from(reg) >> 1].set_period_coarse(val)
566            }
567            NoisePeriod => {
568                self.noise_control.set_period(val)
569            }
570            MixerControl => {
571                self.mixer = Mixer(val)
572            }
573            AmpLevelA|AmpLevelB|AmpLevelC => {
574                self.amp_levels[usize::from(reg) - 8].set(val)
575            }
576            EnvPerFine => {
577                self.env_control.set_period_fine(val)
578            }
579            EnvPerCoarse => {
580                self.env_control.set_period_coarse(val)
581            }
582            EnvShape => {
583                self.env_control.set_shape(val)
584            }
585            _ => ()
586        }
587    }
588    /// Returns the current tone periods of each channel.
589    ///
590    /// The period is in the range: [1, 4095].
591    #[inline]
592    pub fn get_tone_periods(&self) -> [u16;3] {
593        let mut periods = [0;3];
594        for (tone, tgt) in self.tone_control.iter().zip(periods.iter_mut()) {
595            *tgt = tone.period;
596        }
597        periods
598    }
599    /// Returns the current amplitude level of each channel.
600    ///
601    /// If the channel volume register's envelope bit is set, it returns the current envelope
602    /// level for that channel.
603    ///
604    /// The levels are in the range: [0, 15].
605    #[inline]
606    pub fn get_amp_levels(&self) -> [u8;3] {
607        let mut amps = [0;3];
608        for (level, tgt) in self.amp_levels.iter().zip(amps.iter_mut()) {
609            *tgt = if level.is_env_control() {
610                self.env_control.get_level()
611            }
612            else {
613                level.0
614            };
615        }
616        amps
617    }
618    /// Returns the current noise pitch.
619    ///
620    /// The pitch is in the range: [0, 31].
621    #[inline]
622    pub fn get_noise_pitch(&self) -> u8 {
623        self.noise_control.period
624    }
625    /// Returns the current value of the mixer register.
626    ///
627    /// ```text
628    /// t - tone bit: 0 tone enabled, 1 disabled
629    /// n - noise bit: 0 noise enabled, 1 disabled
630    ///
631    /// b7 b6 b5 b4 b3 b2 b1 b0 bit
632    /// -  -  n  n  n  t  t  t  value
633    /// -  -  C  B  A  C  B  A  channel
634    /// ```
635    #[inline]
636    pub fn get_mixer(&self) -> u8 {
637        self.mixer.0
638    }
639    /// Returns the current level of the envelope generator.
640    #[inline]
641    pub fn get_envelope_level(&self) -> u8 {
642        self.env_control.get_level()
643    }
644    /// Returns the envelope shape.
645    #[inline]
646    pub fn get_envelope_shape(&self) -> u8 {
647        self.env_control.get_shape()
648    }
649    /// Returns the envelope period.
650    ///
651    /// The period is in the range: [1, 65535].
652    #[inline]
653    pub fn get_envelope_period(&self) -> u16 {
654        self.env_control.period
655    }
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661
662    #[test]
663    fn ay_3_889x_tone_periods() {
664        use spectrusty_audio::music::*;
665        let clock_hz = 3_546_900.0/2.0f32;
666        let mut notes: Vec<u16> = Vec::new();
667        assert_eq!(252, Ay3_891xAudio::freq_to_tone_period(clock_hz, 440.0).unwrap().get());
668        assert_eq!(5, Ay3_891xAudio::freq_to_tone_period(clock_hz, 24000.0).unwrap().get());
669        assert_eq!(439.84375, Ay3_891xAudio::tone_period_to_freq(clock_hz, 252));
670        assert_eq!(22168.125, Ay3_891xAudio::tone_period_to_freq(clock_hz, 5));
671        notes.extend(Ay3_891xAudio::tone_periods(clock_hz, 0, 7, equal_tempered_scale_note_freqs(440.0, 0, 12)));
672        assert_eq!(
673            vec![4031, 3804, 3591, 3389, 3199, 3020, 2850, 2690, 2539, 2397, 2262, 2135,
674                 2015, 1902, 1795, 1695, 1600, 1510, 1425, 1345, 1270, 1198, 1131, 1068,
675                 1008,  951,  898,  847,  800,  755,  713,  673,  635,  599,  566,  534,
676                  504,  476,  449,  424,  400,  377,  356,  336,  317,  300,  283,  267,
677                  252,  238,  224,  212,  200,  189,  178,  168,  159,  150,  141,  133,
678                  126,  119,  112,  106,  100,   94,   89,   84,   79,   75,   71,   67,
679                   63,   59,   56,   53,   50,   47,   45,   42,   40,   37,   35,   33,
680                   31,   30,   28,   26,   25,   24,   22,   21,   20,   19,   18,   17], notes);
681    }
682
683    #[test]
684    fn ay_3_889x_env_works() {
685        // println!("Ay3_891xAudio {:?}", core::mem::size_of::<Ay3_891xAudio>());
686        let mut ay = Ay3_891xAudio::default();
687
688        for shape in [0, ENV_SHAPE_ALT_MASK,
689                         ENV_SHAPE_HOLD_MASK,
690                         ENV_SHAPE_ALT_MASK|ENV_SHAPE_HOLD_MASK,
691                         ENV_SHAPE_CONT_MASK|ENV_SHAPE_HOLD_MASK].iter().copied() {
692            ay.env_control.set_shape(shape);
693            assert_eq!(ay.env_control.tick, 0);
694            assert_eq!(ay.env_control.cycle, shape);
695            assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|ENV_LEVEL_MASK);
696            ay.env_control.set_period(0);
697            assert_eq!(ay.env_control.period, 1);
698            for exp_level in (0..=15).rev() {
699                assert_eq!(ay.env_control.update_level(), exp_level);
700                assert_eq!(ay.env_control.tick, 1);
701            }
702            for _ in 0..100 {
703                assert_eq!(ay.env_control.tick, 1);
704                assert_eq!(ay.env_control.update_level(), 0);
705            }
706        }
707
708        for shape in [0, ENV_SHAPE_ALT_MASK,
709                         ENV_SHAPE_HOLD_MASK,
710                         ENV_SHAPE_ALT_MASK|ENV_SHAPE_HOLD_MASK,
711                         ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK|ENV_SHAPE_ALT_MASK|ENV_SHAPE_HOLD_MASK
712                         ].iter().copied() {
713            ay.env_control.set_shape(shape|ENV_SHAPE_ATTACK_MASK);
714            assert_eq!(ay.env_control.tick, 0);
715            assert_eq!(ay.env_control.cycle, shape|ENV_SHAPE_ATTACK_MASK);
716            assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK);
717            ay.env_control.set_period(0);
718            assert_eq!(ay.env_control.period, 1);
719            for exp_level in 0..=15 {
720                assert_eq!(ay.env_control.update_level(), exp_level);
721                assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK|exp_level);
722                assert_eq!(ay.env_control.tick, 1);
723            }
724            for _ in 0..100 {
725                assert_eq!(ay.env_control.tick, 1);
726                assert_eq!(ay.env_control.update_level(), 0);
727                assert_eq!(ay.env_control.level, 0);
728            }
729        }
730
731        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK);
732        assert_eq!(ay.env_control.tick, 0);
733        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK);
734        assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|ENV_LEVEL_MASK);
735        ay.env_control.set_period(0);
736        assert_eq!(ay.env_control.period, 1);
737        for _ in 0..10 {
738            for exp_level in (0..=15).rev() {
739                assert_eq!(ay.env_control.update_level(), exp_level);
740                assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|exp_level);
741                assert_eq!(ay.env_control.tick, 1);
742            }
743        }
744
745        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK|ENV_SHAPE_ALT_MASK);
746        assert_eq!(ay.env_control.tick, 0);
747        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK|ENV_SHAPE_ALT_MASK);
748        assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|ENV_LEVEL_MASK);
749        ay.env_control.set_period(0);
750        assert_eq!(ay.env_control.period, 1);
751        for _ in 0..10 {
752            for exp_level in (0..=15).rev() {
753                assert_eq!(ay.env_control.update_level(), exp_level);
754                assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|exp_level);
755                assert_eq!(ay.env_control.tick, 1);
756            }
757            for exp_level in 0..=15 {
758                assert_eq!(ay.env_control.update_level(), exp_level);
759                assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK|exp_level);
760                assert_eq!(ay.env_control.tick, 1);
761            }
762        }
763
764        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK|ENV_SHAPE_ALT_MASK|ENV_SHAPE_HOLD_MASK);
765        assert_eq!(ay.env_control.tick, 0);
766        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK|ENV_SHAPE_ALT_MASK|ENV_SHAPE_HOLD_MASK);
767        assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|ENV_LEVEL_MASK);
768        ay.env_control.set_period(0);
769        assert_eq!(ay.env_control.period, 1);
770        for exp_level in (0..=15).rev() {
771            assert_eq!(ay.env_control.update_level(), exp_level);
772            assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|exp_level);
773            assert_eq!(ay.env_control.tick, 1);
774        }
775        for _ in 0..100 {
776            assert_eq!(ay.env_control.tick, 1);
777            assert_eq!(ay.env_control.update_level(), 15);
778            assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|15);
779        }
780
781        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK);
782        assert_eq!(ay.env_control.tick, 0);
783        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK);
784        assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK);
785        ay.env_control.set_period(0);
786        assert_eq!(ay.env_control.period, 1);
787        for _ in 0..10 {
788            for exp_level in 0..=15 {
789                assert_eq!(ay.env_control.update_level(), exp_level);
790                assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK|exp_level);
791                assert_eq!(ay.env_control.tick, 1);
792            }
793        }
794
795        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK|ENV_SHAPE_HOLD_MASK);
796        assert_eq!(ay.env_control.tick, 0);
797        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK|ENV_SHAPE_HOLD_MASK);
798        assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK);
799        ay.env_control.set_period(0);
800        assert_eq!(ay.env_control.period, 1);
801        for exp_level in 0..=15 {
802            assert_eq!(ay.env_control.update_level(), exp_level);
803            assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK|exp_level);
804            assert_eq!(ay.env_control.tick, 1);
805        }
806        for _ in 0..100 {
807            assert_eq!(ay.env_control.tick, 1);
808            assert_eq!(ay.env_control.update_level(), 15);
809            assert_eq!(ay.env_control.level, 15);
810        }
811
812        ay.env_control.set_shape(ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK|ENV_SHAPE_ALT_MASK);
813        assert_eq!(ay.env_control.tick, 0);
814        assert_eq!(ay.env_control.cycle, ENV_SHAPE_CONT_MASK|ENV_SHAPE_ATTACK_MASK|ENV_SHAPE_ALT_MASK);
815        assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK);
816        ay.env_control.set_period(0);
817        assert_eq!(ay.env_control.period, 1);
818        for _ in 0..10 {
819            for exp_level in 0..=15 {
820                assert_eq!(ay.env_control.update_level(), exp_level);
821                assert_eq!(ay.env_control.level, ENV_LEVEL_MOD_MASK|exp_level);
822                assert_eq!(ay.env_control.tick, 1);
823            }
824            for exp_level in (0..=15).rev() {
825                assert_eq!(ay.env_control.update_level(), exp_level);
826                assert_eq!(ay.env_control.level, ENV_LEVEL_REV_MASK|ENV_LEVEL_MOD_MASK|exp_level);
827                assert_eq!(ay.env_control.tick, 1);
828            }
829        }
830    }
831}