moont
Roland CM-32L synth emulator, based on Munt, written in Rust with no code dependencies.
The CM-32L is in the MT-32 family of synthesizers. This hardware predates General MIDI, but many early 1990s PC games directly support these synths.
moont aims to be sample-accurate with Munt. It accomplishes this across a variety of input comparison tests with no known incompatibilities. Inputs for which moont output does not match Munt should be reported as a bug.
Usage
Add the moont crate as dependency in Cargo.toml:
[dependencies]
moont = "0.9.2"
Loading ROMs
This repository does not distribute the Roland CM-32L ROMs. In the default build configuration, the only option is to load them at runtime:
use moont::{CM32L, Rom, Synth};
let rom = Rom::new(&control_rom, &pcm_rom)?;
let mut synth = CM32L::new(rom);
For situations where redistribution of the ROMs is not a concern, the
bundle-rom feature flag avoids runtime loading:
use moont::{CM32L, Rom, Synth};
let mut synth = CM32L::new(Rom::bundled());
Place CM32L_CONTROL.ROM and CM32L_PCM.ROM in the rom/ directory inside
the moont crate (i.e. moont/rom/), or set the MOONT_ROM_DIR environment
variable to a directory containing them. The tools/download-roms.sh script
downloads them to the default location.
This feature goes beyond bundling ROM data as binary blobs: at build time, the
ROMs are validated, parsed, PCM-unscrambled and converted to Rust values. Rust
literals are generated, compiled into the library, and Rom::bundled() returns
a handle to this static generated data.
General MIDI Translation
When the input is General MIDI, use GmSynth to make it playable through
the CM-32L engine. It does the following:
-
Assigns parts 1-8 to MIDI channels 1-8 (rhythm on channel 10).
-
Sets pitch bend range to 2 semitones (the GM default) on all parts.
-
Translates program changes and drum notes to their CM-32L equivalents.
-
Reverses pan (CC#10) to match GM stereo convention.
-
Reapplies GM defaults after device reset.
use moont::{GmSynth, Rom, Synth};
let mut synth = GmSynth::new(Rom::new(&control_rom, &pcm_rom)?);
Rendering Audio
Use play_msg() or play_msg_at() to enqueue MIDI events, and render() to
advance the time and output 32kHz stereo samples to a buffer.
use moont::{Frame, Synth};
// 0x90: Note On, Channel 1.
// 0x3c: Note C-3 (Middle C).
// 0x7f: Velocity 127 (Max).
synth.play_msg(0x7f3c91);
// 1 second of output.
let mut buf = vec![Frame(0, 0); 32000];
synth.render(&mut buf);
// 0x81: Note Off, Channel 1.
// 0x3c: Note C-3 (Middle C).
synth.play_msg(0x3c81);
Munt Feature Comparison
moont makes major scope limitations compared to mt32emu:
- No support for alternate configurations or other hardware models.
- No C/C++ API, just Rust library crate.
- No display functionality.
- Very limited runtime state introspection.
moont behaves like this libmt32emu configuration:
- CM-32L hardware model.
- Compared to MT-32: 33 extra rhythm sounds.
- None of the MT-32 "quirks".
- 32kHz output sample rate (matching CM-32L).
- 4096 MIDI queue size.
- "Coarse" output mode (no upsampling).
- "Nice" DAC input mode.
- "Nice" amplitude ramp mode.
- "Nice" panning mode.
- "Nice" partial mixing mode.
Related Crates
| Crate | Description |
|---|---|
| moont-render | Render .mid files to .wav |
| moont-live | Real-time ALSA MIDI sink |
| moont-web | WebAssembly wrapper with Web Audio API |
Testing
Since the ROMs are not distributed, most tests are excluded from the published
crate. When built from source, the sample accurate comparison tests require a
patched Munt fork at ./munt in
the workspace root to make its behavior deterministic. It is built and linked
as a static library automatically.
License
moont is a derivative work of Munt. Both are distributed under LGPL 2.1+.
Copyright (C) 2021-2026 Geoff Hill geoff@geoffhill.org
Copyright (C) 2003-2026 Dean Beeler, Jerome Fisher, Sergey V. Mikayev
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.