moont 0.9.2

Roland CM-32L synthesizer emulator
Documentation
# moont

Roland CM-32L synth emulator, based on [Munt](https://github.com/munt/munt/),
written in Rust with no code dependencies.

The CM-32L is in the [MT-32 family](https://en.wikipedia.org/wiki/Roland_MT-32)
of synthesizers. This hardware predates [General
MIDI](https://en.wikipedia.org/wiki/General_MIDI), but [many early 1990s PC
games](https://www.vogonswiki.com/index.php/List_of_MT-32-compatible_computer_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](https://gitlab.gnome.org/geoffhill/moont/-/issues).


## 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]https://crates.io/crates/moont-render | Render .mid files to .wav |
| [moont-live]https://crates.io/crates/moont-live | Real-time ALSA MIDI sink |
| [moont-web]https://crates.io/crates/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](https://gitlab.gnome.org/geoffhill/munt) 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](COPYING.LESSER.txt),
or (at your option) any later version.