ym2149_ay_replayer/
lib.rs1#![warn(missing_docs)]
9
10pub mod error;
11pub mod format;
12mod machine;
13mod parser;
14pub mod player;
15
16pub use crate::error::{AyError, Result};
17pub use crate::format::{AyBlock, AyFile, AyHeader, AyPoints, AySong, AySongData};
18pub use crate::parser::load_ay;
19pub use crate::player::{AyMetadata, AyPlayer, CPC_UNSUPPORTED_MSG};
20
21pub use ym2149_common::{ChiptunePlayer, PlaybackMetadata, PlaybackState};
23
24#[allow(deprecated)]
26pub use crate::player::AyPlaybackState;
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31
32 const SPACE_MADNESS: &[u8] = include_bytes!(concat!(
33 env!("CARGO_MANIFEST_DIR"),
34 "/../../examples/ay/SpaceMadness.AY"
35 ));
36 const SHORT_MODULE: &[u8] = include_bytes!(concat!(
37 env!("CARGO_MANIFEST_DIR"),
38 "/../../examples/ay/Short.ay"
39 ));
40
41 #[test]
42 fn parse_space_madness_metadata() {
43 let ay = load_ay(SPACE_MADNESS).expect("failed to parse Space Madness");
44 assert_eq!(ay.header.author, "Alberto Gonzales");
45 assert!(
46 ay.header.misc.starts_with("Unreleased Track"),
47 "unexpected misc '{}'",
48 ay.header.misc
49 );
50 assert_eq!(ay.header.song_count, 1);
51 assert_eq!(ay.songs.len(), 1);
52
53 let song = &ay.songs[0];
54 println!("blocks loaded:");
55 for block in &song.data.blocks {
56 println!(" 0x{:04x} ({} bytes)", block.address, block.length);
57 }
58 let cwd = std::env::current_dir().unwrap();
59 println!("cwd: {}", cwd.display());
60 std::fs::write("impact_block.bin", &song.data.blocks[0].data).unwrap();
61 assert_eq!(song.name, "Space Madness");
62 assert_eq!(song.data.channel_map, [0, 1, 2, 3]);
63 assert_eq!(song.data.song_length_50hz, 0);
64 let points = song.data.points.as_ref().expect("points missing");
65 assert_eq!(points.stack, 0x0000);
66 assert_eq!(points.init, 0x801a);
67 assert_eq!(points.interrupt, 0x80ab);
68 assert!(!song.data.blocks.is_empty());
69 assert_eq!(song.data.blocks[0].address, 0x8000);
70 }
71
72 #[test]
73 fn parse_short_module_song_list() {
74 let ay = load_ay(SHORT_MODULE).expect("failed to parse Short.ay");
75 assert_eq!(ay.header.author, "Ivan Roshin, Moscow, 15.09.2000");
76 assert!(
77 ay.header.misc.starts_with("Non-standard note table"),
78 "unexpected misc '{}'",
79 ay.header.misc
80 );
81 assert_eq!(ay.header.song_count, ay.songs.len() as u8);
82 assert_eq!(ay.songs.len(), 1);
83
84 let song = &ay.songs[0];
85 assert_eq!(song.name, "Short module for PHAT0 (ABC)");
86 assert!(
87 song.data.song_length_50hz > 0,
88 "expected non-zero length metadata"
89 );
90 assert!(
91 song.data.blocks.iter().all(|blk| !blk.data.is_empty()),
92 "every block should have data payload"
93 );
94 }
95
96 #[test]
97 fn ay_player_generates_audio() {
98 let (mut player, meta) =
99 crate::player::AyPlayer::load_from_bytes(SPACE_MADNESS, 0).unwrap();
100 assert_eq!(meta.song_name, "Space Madness");
101 player.play().unwrap();
102 let samples = player.generate_samples(1_764);
103 assert_eq!(samples.len(), 1_764);
104 assert!(
106 samples.iter().any(|s| s.abs() > f32::EPSILON),
107 "expected waveform data"
108 );
109 }
110}