Expand description
§oplon
A no_std, integer-only OPL2 / OPL3 (Yamaha YM3812 / YMF262) FM
synthesis core, with a clean-room ROM set generated entirely at compile time
(no float, no hand-pasted magic tables).
The chip powers up as an OPL2 (9 channels, mono, waveforms 0-3) and becomes
a full OPL3 — 18 channels, stereo panning, waveforms 4-7 — once OPL3 mode is
enabled (port-1 register 0x05, or Opl2::set_opl3_enabled), exactly like
the real hardware. So OPL2 code paths are unchanged while OPL3 streams get
the extra features (including 4-operator mode).
§Properties
no_std, zero heap allocation, zero floating-point, fully deterministic.- Up to 18 two-operator FM channels (two banks) + the OPL rhythm mode.
- Register-level interface (
Opl2::write_reg) and a structured note/patch interface (Opl2::set_patch,Opl2::key_on, …). - Operators run at the chip’s native rate (
clock / 72); the output is 4-point-cubic-resampled to your sample rate, so the chip’s own aliasing matches the silicon. - Log-sine / exp ROMs and the envelope-increment table are generated
from their defining hardware formulas in integer
const fn, verified within ±1 LSB of the canonical values.
§Minimal usage (register level — what a VGM/DRO player needs)
use oplon::Opl2;
let mut opl = Opl2::new(44_100); // output sample rate
// Program a simple carrier tone on channel 0 (raw AdLib registers):
opl.write_reg(0x20, 0x01); // op0 mult = 1
opl.write_reg(0x40, 0x00); // op0 total level = loud
opl.write_reg(0x60, 0xF0); // op0 fast attack
opl.write_reg(0x80, 0x00); // op0 sustain/release
opl.write_reg(0xA0, 0x98); // F-number low
opl.write_reg(0xB0, 0x31); // key-on + block 4 + F-number high
for _ in 0..44_100 {
let (left, right): (i32, i32) = opl.render_frame();
// feed `left`/`right` to your DAC or audio buffer
let _ = (left, right);
}§Scope
2-operator voices on both register banks (18 channels), OPL3 stereo panning
(register 0xC0 bits 4/5), the 8 OPL3 waveforms, and 4-operator linking
(port-1 register 0x04, the four connection algorithms).
§Clean-room & attribution
This crate is MIT-licensed. The OPL emulators it is validated against are
not: Schism Tracker’s player/fmopl3.c (a ymf262 core) is GPL-2.0+,
© Jarek Burczynski, and Nuked-OPL3 is LGPL. No code or data table is
copied from either. Every constant here is independently generated from
the documented hardware behaviour and integer math: the log-sin/exp ROMs
from their defining formulas, the envelope increments from the documented
(4+lo)·2^rate_hi rate law, the tremolo/vibrato LFOs from their documented
triangle/8-step shapes, and the MUL/KSL/key-scale values from the
Yamaha YM3812/YMF262 datasheet. Those references are consulted only as
behavioural references (to know what the silicon does); the
implementation — structure, code and tables — is original.
Structs§
- Channel
Patch - A full 2-operator channel patch.
- OpPatch
- One operator’s decoded patch fields.
- Opl2
- An OPL2/OPL3 chip: up to 18 two-operator channels summed to stereo.
- Opl3
- Alias for
Opl2: the same chip type, which becomes a full OPL3 (18 channels, stereo, waveforms 4-7) onceOpl2::set_opl3_enabled— or the port-10x05register — turns OPL3 mode on. An OPL2/OPL3 chip: up to 18 two-operator channels summed to stereo.
Constants§
- OPL2_
CHANNELS - Number of 2-operator channels on an OPL2 (YM3812).
- OPL3_
CHANNELS - Number of 2-operator channels on an OPL3 (YMF262): two banks of 9.
- OPL_
NATIVE_ RATE - Native OPL sample rate for the canonical clock (
YM3812_CLOCK / 72, equal to the OPL3YMF262_CLOCK / 288). F-numbers are computed against the chip’s native rate; the resampler then converts to the caller’s output rate. - YM3812_
CLOCK - The canonical YM3812 (OPL2) master clock (Hz). The native operator rate is
clock / 72. - YMF262_
CLOCK - The canonical YMF262 (OPL3) master clock (Hz), 4× the OPL2 clock. The native
operator rate is
clock / 288— the same 49716 Hz, since the YMF262 has 4× the operators to service per sample.