oplon 1.0.0

A no_std, integer-only OPL2/OPL3 (Yamaha YM3812/YMF262) FM synthesis core with a clean-room, compile-time-generated ROM set
Documentation
//! Render a short OPL2 tone to a raw PCM file — a dependency-free smoke test
//! of the core engine (no `player` feature needed).
//!
//! Usage: `cargo run --example render -- [out.pcm] [seconds]`
//! Output is raw **s16le stereo** at 44100 Hz (play with e.g.
//! `ffplay -f s16le -ar 44100 -ch_layout stereo out.pcm`).

#![forbid(unsafe_code)]
use std::io::Write;

use oplon::Opl2;

fn main() {
    let mut args = std::env::args().skip(1);
    let out_path = args.next().unwrap_or_else(|| "out.pcm".into());
    let seconds: f64 = args.next().and_then(|s| s.parse().ok()).unwrap_or(2.0);

    let sample_rate = 44_100u32;
    let mut opl = Opl2::new(sample_rate);

    // A simple FM voice on channel 0: quiet modulator, audible carrier.
    for (reg, val) in [
        (0x20u8, 0x21u8), // mod: EG-type sustaining, mult=1
        (0x23, 0x21),     // car: EG-type sustaining, mult=1
        (0x40, 0x10),     // mod: moderate level (adds a little colour)
        (0x43, 0x00),     // car: loud
        (0x60, 0xF2),     // mod: fast attack, slow decay
        (0x63, 0xF2),     // car
        (0x80, 0x05),     // mod: sustain/release
        (0x83, 0x05),     // car
        (0xC0, 0x06),     // FM, feedback 3
        (0xA0, 0x98),     // F-number low
        (0xB0, 0x31),     // key-on + block 4 + F-number high
    ] {
        opl.write_reg(reg, val);
    }

    let frames = (seconds * sample_rate as f64) as u64;
    let mut out = Vec::with_capacity(frames as usize * 4);
    for i in 0..frames {
        // Key-off for the last 25% so the release tail is audible.
        if i == frames * 3 / 4 {
            opl.write_reg(0xB0, 0x11); // clear key-on bit
        }
        let (l, r) = opl.render_frame();
        out.extend_from_slice(&(l.clamp(-32768, 32767) as i16).to_le_bytes());
        out.extend_from_slice(&(r.clamp(-32768, 32767) as i16).to_le_bytes());
    }

    let mut f = std::fs::File::create(&out_path).expect("create output");
    f.write_all(&out).expect("write output");
    eprintln!(
        "wrote {} ({:.1}s, s16le stereo @ {} Hz)",
        out_path, seconds, sample_rate
    );
}