ym2149 0.1.1

Cycle-accurate YM2149 PSG emulator with real-time streaming audio output
Documentation

YM2149‑RS

Cycle‑accurate Yamaha YM2149 PSG (Atari ST) emulator in Rust, with optional YM file replay, real‑time streaming, and an experimental softsynth.

Highlights

  • Integer‑accurate YM2149 core (envelope, LFSR noise, mixer, color filter)
  • YM file replay: YM2 (Mad Max), YM3/YM3b, YM4, YM5, YM6
  • VBL‑synced replayer with YM5/YM6 effects (SID, Sync Buzzer)
  • Optional real‑time streaming (rodio) and experimental softsynth
  • Modular features: enable only what you need

YM File Replay

This crate can replay YM files (YM2–YM6) through a simple frame‑based replayer. You can render to a buffer or stream in real‑time. Effects encoded in YM5/YM6 are supported; YM2 Mad Max digi‑drums are handled as well.

Minimal example:

use ym2149::{replayer::PlaybackController, replayer::load_song};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = std::fs::read("examples/Scaven6.ym")?;
    let (mut player, _summary) = load_song(&data)?; // auto-detects YM version
    player.play()?;
    let samples = player.generate_samples(44_100);  // render ~1s at 44.1kHz
    println!("{} samples", samples.len());
    Ok(())
}

Real‑Time Streaming (replayer + streaming)

use ym2149::{replayer::PlaybackController, replayer::load_song};
use ym2149::streaming::{StreamConfig, RealtimePlayer, AudioDevice};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load YM data
    let data = std::fs::read("examples/Scaven6.ym")?;
    let (mut song, summary) = load_song(&data)?; // auto-detect YM2–YM6

    // Start song playback
    song.play()?;

    // Set up streaming
    let cfg = StreamConfig::low_latency(44_100);
    let stream = RealtimePlayer::new(cfg)?;
    let _dev = AudioDevice::new(cfg.sample_rate, cfg.channels, stream.get_buffer())?;

    // Push audio in small batches
    let mut batch = vec![0.0f32; 1024];
    let total = summary.samples_per_frame as usize * summary.frame_count as usize;
    let mut generated = 0usize;
    while generated < total {
        let to_gen = (total - generated).min(batch.len());
        let samples = song.generate_samples(to_gen);
        batch[..to_gen].copy_from_slice(&samples);
        stream.write_blocking(&batch[..to_gen]);
        generated += to_gen;
    }

    Ok(())
}

Quick Start

Add to your Cargo.toml (enable only what you need):

[dependencies]
ym2149 = { version = "0.1", features = ["emulator", "ym-format", "replayer", "streaming"] }
# add `"softsynth"` if you want the experimental synth engine

Core chip (generate samples)

use ym2149::ym2149::Ym2149;

let mut chip = Ym2149::new();
chip.write_register(0, 0x50); // A freq lo
chip.write_register(1, 0x00); // A freq hi
chip.write_register(8, 0x0F); // A amp
chip.clock();
let sample = chip.get_sample();

Streaming (feature: streaming)

use ym2149::streaming::{StreamConfig, RealtimePlayer, AudioDevice};

let cfg = StreamConfig::low_latency(44_100);
let player = RealtimePlayer::new(cfg)?;
let _dev = AudioDevice::new(cfg.sample_rate, cfg.channels, player.get_buffer())?;
// write samples into player.write_blocking(&samples)

CLI Player

The repository ships a ym2149 CLI that performs real-time playback with terminal visualization. The binary is only built when the streaming feature is enabled:

cargo run --features streaming -- examples/Scaven6.ym

To experiment with the experimental softsynth backend, add the softsynth feature and select it via the --chip flag:

cargo run --features "streaming softsynth" -- --chip softsynth examples/Ashtray.ym

Features

  • emulator (default): core YM2149 chip
  • ym-format (default): YM file parsing/loader
  • replayer (default): frame player + effects
  • streaming: rodio-powered audio output (opt-in)
  • softsynth: experimental synth engine (opt-in)
  • visualization (default): terminal visualization helpers

License

MIT — see LICENSE.