ym2149_sndh_replayer/lib.rs
1//! # ym2149-sndh-replayer
2//!
3//! SNDH file parser and Atari ST machine emulation for YM2149 chiptune playback.
4//!
5//! This crate provides playback support for SNDH files, a popular format for
6//! Atari ST chiptune music. It includes:
7//!
8//! - **SNDH Parser**: Parses SNDH file headers and metadata
9//! - **ICE Depacker**: Decompresses ICE! 2.4 packed SNDH files
10//! - **68000 CPU Emulation**: Via the `m68000` crate for executing SNDH drivers
11//! - **MFP68901 Timer Emulation**: For accurate timer-based effects (SID voice, etc.)
12//! - **Atari ST Machine**: Memory-mapped I/O emulation for YM2149 and timers
13//!
14//! ## Example
15//!
16//! ```rust,ignore
17//! use ym2149_sndh_replayer::{SndhPlayer, load_sndh};
18//! use ym2149_common::ChiptunePlayer;
19//!
20//! let data = std::fs::read("music.sndh")?;
21//! let mut player = load_sndh(&data, 44100)?;
22//!
23//! // Select subsong (1-based index)
24//! player.init_subsong(1)?;
25//! player.play();
26//!
27//! // Generate audio samples
28//! let mut buffer = vec![0.0f32; 882]; // ~50Hz at 44100 sample rate
29//! player.generate_samples_into(&mut buffer);
30//! ```
31//!
32//! ## SNDH Format
33//!
34//! SNDH is a standard format for Atari ST music that embeds the original
35//! 68000 replay code along with the music data. The format uses a simple
36//! header followed by executable code:
37//!
38//! - Entry point + 0: Initialize subsong (D0 = subsong number)
39//! - Entry point + 4: Exit/cleanup
40//! - Entry point + 8: Play one frame (called at player rate, typically 50Hz)
41//!
42//! Many SNDH files are ICE! packed for smaller file sizes.
43
44#![warn(missing_docs)]
45
46mod cpu_backend;
47mod error;
48mod ice;
49mod lmc1992;
50mod machine;
51mod mfp68901;
52mod parser;
53mod player;
54mod ste_dac;
55
56pub use error::{Result, SndhError};
57pub use ice::{ice_depack, is_ice_packed};
58pub use parser::{DmaSampleRate, SndhFile, SndhFlags, SndhMetadata, SubsongInfo};
59pub use player::SndhPlayer;
60
61// Re-export common traits for convenience
62pub use ym2149_common::{BasicMetadata, ChiptunePlayer, PlaybackMetadata, PlaybackState};
63
64/// Check if data appears to be SNDH format.
65///
66/// This performs a quick header check without fully parsing the file.
67/// It handles both raw SNDH and ICE!-packed SNDH files.
68///
69/// # Arguments
70///
71/// * `data` - Raw file data to check
72///
73/// # Returns
74///
75/// `true` if the data appears to be SNDH format.
76pub fn is_sndh_data(data: &[u8]) -> bool {
77 // Check for ICE! packed data first
78 if is_ice_packed(data) {
79 // For ICE! packed files, we can't easily check the header
80 // but ICE! is commonly used for SNDH, so we'll accept it
81 // The actual validation happens during parsing
82 return true;
83 }
84
85 // Check minimum size for SNDH header
86 if data.len() < 16 {
87 return false;
88 }
89
90 // Check for BRA instruction at offset 0
91 if data[0] != 0x60 {
92 return false;
93 }
94
95 // Check for SNDH magic at offset 12
96 &data[12..16] == b"SNDH"
97}
98
99/// Load an SNDH file and create a player.
100///
101/// This is the main entry point for playing SNDH files. It handles:
102/// - ICE! decompression if needed
103/// - SNDH header parsing
104/// - Atari ST machine initialization
105///
106/// # Arguments
107///
108/// * `data` - Raw SNDH file data (may be ICE! compressed)
109/// * `sample_rate` - Output sample rate (e.g., 44100)
110///
111/// # Returns
112///
113/// A configured `SndhPlayer` ready for playback.
114pub fn load_sndh(data: &[u8], sample_rate: u32) -> Result<SndhPlayer> {
115 SndhPlayer::new(data, sample_rate)
116}