moont 0.9.2

Roland CM-32L synthesizer emulator
Documentation

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.