Skip to main content

Crate moont

Crate moont 

Source
Expand description

Roland CM-32L synthesizer emulator.

Renders 32kHz stereo PCM audio from MIDI input. no_std compatible (requires alloc). Based on Munt, with no external 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.

§Quick start

use moont::{Frame, Synth, cm32l};

let control = std::fs::read("CM32L_CONTROL.ROM").unwrap();
let pcm = std::fs::read("CM32L_PCM.ROM").unwrap();
let rom = cm32l::Rom::new(&control, &pcm).expect("invalid ROM");
let mut synth = cm32l::Device::new(rom);

// Note On: MIDI channel 2, note C4, max velocity.
synth.play_msg(0x7f3c91);

// Render 1 second of audio at 32 kHz.
let mut buf = vec![Frame(0, 0); 32000];
synth.render(&mut buf);

// Note Off: MIDI channel 2.
synth.play_msg(0x3c81);

§Loading ROMs

The CM-32L ROMs are not distributed with this crate. Load them at runtime with cm32l::Rom::new:

let control = std::fs::read("CM32L_CONTROL.ROM").unwrap();
let pcm = std::fs::read("CM32L_PCM.ROM").unwrap();
let rom = cm32l::Rom::new(&control, &pcm).expect("invalid ROM");

With the bundle-rom feature, ROMs are parsed at compile time and embedded in the binary, enabling cm32l::Rom::bundled(). Place CM32L_CONTROL.ROM and CM32L_PCM.ROM in rom/ inside the crate directory, or set the MOONT_ROM_DIR environment variable:

let rom = cm32l::Rom::bundled();

§General MIDI translation

The CM-32L uses its own instrument map. When the input is General MIDI, use cm32l::GmDevice to translate program changes, drum notes, and pan direction into CM-32L equivalents. GM defaults are automatically reapplied after any device reset:

use moont::{Synth, cm32l};

let control = std::fs::read("CM32L_CONTROL.ROM").unwrap();
let pcm = std::fs::read("CM32L_PCM.ROM").unwrap();
let mut synth =
    cm32l::GmDevice::new(cm32l::Rom::new(&control, &pcm).unwrap());
synth.play_msg(0x7f3c90);

§Type-safe MIDI messages

The Message type provides validated message construction as an alternative to raw u32 values:

use moont::Message;

let msg = Message::note_on(60, 100, 0).unwrap();
let packed: u32 = msg.try_into().unwrap();

§Features

FeatureDescription
bundle-romEmbed pre-parsed ROMs in the binary (enables cm32l::Rom::bundled())
tracingEmit tracing spans and events
CrateDescription
moont-renderRender .mid files to .wav
moont-liveReal-time ALSA MIDI sink
moont-webWebAssembly wrapper with Web Audio API

Modules§

cm32l
CM-32L-specific API surface.
gm
General MIDI to CM-32L translation.
param
Shared parameter types for instrument timbres, patches, and rhythm keys.
smf
Minimal Standard MIDI File (SMF) parser.

Structs§

CM32L
CM-32L synthesizer state.
Frame
Stereo PCM audio frame of two native-endian signed 16-bit samples.
Message
Validated MIDI message.

Enums§

ControlCommand
High-level runtime control commands for a synth instance.
ControlError
Error applying ControlCommand.
MidiError
Error returned when constructing or converting a Message message.

Constants§

SAMPLE_RATE
Audio frames (stereo samples) per second.

Traits§

Synth
Interface for MIDI synthesizers.