1use 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
18pub const INTERNAL_CLOCK_DIVISOR: FTs = 16;
20pub const HOST_CLOCK_RATIO: FTs = 2;
22
23#[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#[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
76pub struct LogAmpLevels16<T>(PhantomData<T>);
79impl<T: Copy + FromSample<f32>> AmpLevels<T> for LogAmpLevels16<T> {
80 fn amp_level(level: u32) -> T {
81 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
96pub struct AyAmps<T>(PhantomData<T>);
98pub 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
115pub trait AyAudioFrame<B: Blep> {
117 fn render_ay_audio_frame<V: AmpLevels<B::SampleDelta>>(
122 &mut self,
123 blep: &mut B,
124 channels: [usize; 3]
125 );
126}
127
128#[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#[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#[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
180pub 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#[derive(Clone, Copy, Debug)]
192#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
193struct EnvelopeControl {
194 period: u16,
195 tick: u16,
196 cycle: u8,
198 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); 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#[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#[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#[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
412impl Ay3_891xAudio {
414 pub fn reset(&mut self) {
416 *self = Default::default()
417 }
418 #[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 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 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 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 #[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 #[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 #[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 #[inline]
622 pub fn get_noise_pitch(&self) -> u8 {
623 self.noise_control.period
624 }
625 #[inline]
636 pub fn get_mixer(&self) -> u8 {
637 self.mixer.0
638 }
639 #[inline]
641 pub fn get_envelope_level(&self) -> u8 {
642 self.env_control.get_level()
643 }
644 #[inline]
646 pub fn get_envelope_shape(&self) -> u8 {
647 self.env_control.get_shape()
648 }
649 #[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 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}