- Rust 100%
|
All checks were successful
- Exclude .idea, .forgejo, and deny.toml from the published tarball so only source, licenses, README, and the example ship. - Enable #![warn(missing_docs)] and document every public item: OplOperator/ OplPatch fields, OplBank methods, MidiMsg variants, TimedEvent/RenderOpts/ Pcm/StereoPcm fields, Error, and render(). cargo fmt/clippy/test/doc (with -D warnings) all clean. |
||
|---|---|---|
| .forgejo/workflows | ||
| .idea | ||
| examples | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| deny.toml | ||
| LICENSE-APACHE | ||
| LICENSE-MIT | ||
| README.md | ||
opl-audio
Pure-Rust OPL2 (Yamaha YM3812) and OPL3 (YMF262) FM song renderer.
Renders a list of timed MIDI events against a bank of AdLib FM patches to PCM
audio through an original YM3812 emulator. The crate is engine-agnostic: it
knows MIDI and FM synthesis — not any specific game, sound driver, or file
format. Callers load their own instrument bank (from a .bnk or anywhere else)
and feed timed events; the crate returns mono (OPL2) or stereo 18-channel
(OPL3) PCM.
#![forbid(unsafe_code)]- Single dependency:
thiserror - Register tables are derived from the published OPL2 formulas — no copyleft
emulator source is used, so the crate is cleanly licensable under
MIT OR Apache-2.0.
What it does
- OPL2 core — nine 2-operator channels, four waveforms, ADSR envelopes following the published rate table, key-scale rate/level, tremolo and vibrato LFOs.
- MIDI dynamics — note velocity, CC7 channel volume, CC11 expression, CC64 sustain pedal, ±2-semitone pitch bend, and channel-10 percussion (drum patches played as melodic voices keyed by note).
- OPL3 mode (
render_stereo) — eighteen 2-op channels sharing the same operator pipeline, with CC10 pan quantized to hard L/C/R via the per-channel output-enable bits.
Usage
use opl_audio::{MidiMsg, OplBank, RenderOpts, TimedEvent};
let mut bank = OplBank::new();
bank.insert_program(0, my_patch); // an OplPatch you built or loaded from a .bnk
let events = vec![
TimedEvent { delta_ticks: 0, msg: MidiMsg::NoteOn { ch: 0, note: 69, vel: 127 } },
TimedEvent { delta_ticks: 96, msg: MidiMsg::NoteOff { ch: 0, note: 69 } },
];
// division = MIDI ticks per quarter note, bpm = tempo
let pcm = opl_audio::render(&events, &bank, 96, 120, RenderOpts::default())?;
// pcm.samples: Vec<f32> in [-1.0, 1.0] at pcm.sample_rate (49716 Hz by default)
For stereo OPL3 output, call opl_audio::render_stereo(..), which returns a
StereoPcm { left, right }.
See examples/render_tone.rs for a complete,
data-free example:
cargo run --example render_tone
Provenance
Extracted from the dm2-tools Dungeon Master II toolchain, where it renders the game's DOS AdLib soundtrack. The per-crate commit history is preserved from that monorepo.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.