View on GitHub

clap-mml-render-tui

clap-mml-render-tui

clap-mml-render-tui

Overview

An MML TUI DAW (of sorts). Easily enjoy the rich sounds of Surge XT / Dexed / Vaporizer2 / Floe / Sforzando / Six Sines / TyrellN6 / TONE3000 / Dragonfly Reverb / Voyage Voyage / Shu / METAL-GTX with MML. Written in Rust.

Usage

Technology Stack

Setup

Please install Surge XT

winget install "Surge XT"

Install

cargo install --force --git https://github.com/cat2151/clap-mml-render-tui

Run

cmrt

You can input MML in the TUI screen and play around.

Play Server Implementation Details

Sound playback is handled by a separate process, the play server. The executable is determined by the following order, using the first one found:

  1. The full path specified by --play-server <PATH> (if the specified path does not exist, it will stop with an error without searching further)
  2. clap-mml-realtime-play-server in the same directory as cmrt
  3. The release build of the sibling repository (../clap-mml-play-server/target/release/)

PATH is not consulted. A debug build server is 4-5 times slower at pre-reading, causing playback to cut off at the beginning of measures. A warning will appear in the top-right corner of the screen when using a debug build or an executable of unknown origin.

cmrt --play-server "X:/projects/clap-mml-play-server/target/debug/clap-mml-realtime-play-server.exe"

Supported Audio Plugins

AI-Generated Documentation

From this point onwards, sections added by AI may be hard to read. These will be maintained periodically.

Keyboard Screen

Press v to navigate to the keyboard screen.

Chord Chart Screen

Press Ctrl+G then C to navigate to the chord chart screen.

This screen allows you to view and edit the ‘structure’ of a song’s chord progression. Moving the cursor will play the chord progression of that row. Traversing chords one by one within a row using h l will play only the pointed chord.

┌ Chord Chart ─────────────────────────────────────────────────────────────────┐
│ Key=C BPM120                                                                 │
│┌ Sections ───────────────────────┐┌ Arrangement ────────────────────────────┐│
││> 1 Intro    I-V                 ││  1 Intro    I-V                         ││
││  2 A        I-V-VIm-IV          ││  2 A        I-V-VIm-IV                  ││
││  3 B        IIm-V-I-VIm         ││> 3 A        I-V-VIm-IV                  ││
││                                 ││  4 B        IIm-V-I-VIm                 ││
│└─────────────────────────────────┘└─────────────────────────────────────────┘│
│ q:Exit ?:Help                                                                │
└──────────────────────────────────────────────────────────────────────────────┘

Here are the keybindings. Only q and ? are shown in the bottom line of the screen. The full list appears on screen by pressing ?.

Key Pane Action
Tab Common Pane movement (toggles Sections ⇔ Arrangement)
j k ↓ ↑ Common Cursor movement (row. The entire chord progression of the new row plays)
h l ← → Common Chord movement within a row (only the selected chord plays. Moves to the adjacent row at the end of a line)
PgUp PgDn Common Moves the cursor 10 rows
dd Common Deletes the cursor row (deleting in Sections also removes its references in Arrangement)
Alt+↑ Alt+↓ Common Moves the cursor row up / down
b Common Rewrites the Key / BPM in the header with single-line input
Shift+P Space Common Previews the section on the cursor row (stops if already playing)
x Common Selects an effect chain for the Chord timbre (Enter to confirm, Esc to discard. Does not apply to Bass timbre)
? Common Toggles help (closes with Esc as well)
q Common Exits the application
g Sections Adds a section by drawing from the chord progression catalog
r Sections Re-draws the progression of the cursor row, retaining its name
i Sections Edits the progression using a single-line MML overlay for the Chord Chart
n Sections Edits the name with single-line input
1〜9 Arrangement Inserts the section with that number after the cursor

The editing screen opened by i in Sections initializes with the current progression and places the cursor at the end. While inputting, the chord at the cursor position will auto-voice and play whenever it’s formed or changed. Ctrl+Space can play the entire progression with the same voicing. Unreadable input will not be played as alternative MML but can still be saved. Press Enter to confirm and save the progression (trimming leading/trailing whitespace), or Esc to discard changes.

Inside the editing screen, Ctrl+T opens the same timbre list as the regular MML overlay. The timbre doesn’t change by merely moving the candidate; Enter confirms, and Esc reverts to the original timbre. The Chord Chart timbre is global to the screen and saved to history.json separately from the regular Ctrl+P MML overlay timbre. Even if you discard progression edits with Esc after confirming a timbre, the confirmed timbre remains.

It auto-saves with each edit. The save location is clap-mml-render-tui/history/chord_chart.json under the configuration directory (on Windows, it’s %LOCALAPPDATA%\clap-mml-render-tui\history\chord_chart.json). Only one song is saved at a time. If the save file is missing or unreadable, when the screen is first opened, it performs a single g-like drawing to start with one section (if drawing fails, it remains empty).

The chord progression catalog used for g / r drawing is fetched from the network. There will be a wait only for the first time when the cache is not yet available (wait time is logged in log.txt as chord-chart: event=catalog-first-load elapsed_ms=...). If it fails to fetch, ‘No chord progression data’ will appear at the bottom of the screen.

Effect Chain in DAW Screen

In NORMAL mode of the DAW screen, placing the cursor on a performance track and pressing x opens the EFFECT CHAIN overlay for that track. You can insert any number of factory presets from TONE3000 / Surge XT Effects / Dragonfly Reverb in series after the instrument (timbre).

Key Action
x Opens the EFFECT CHAIN overlay for the cursor track (invalid on chord or conductor rows)
j k Moves between chain stages
a Opens the add overlay. All factory presets for all effects are listed in one column; select with j k, Enter to add to the end, ESC to return
dd Deletes the cursor stage
Enter Writes back to the init column’s JSON and closes (the track’s cache WAV is re-rendered)
ESC Discards changes and closes

In the Guitar Articulation screen (Ctrl+G → E), x also opens the same EFFECT CHAIN overlay. You can insert a guitar amp (press a then select Amp Simulator kind, TONE3000) or other effects after METAL-GTX and preview them with articulated performance whenever you change stages. Confirming with Enter applies the chain to subsequent b and space performances. The chain disappears when the application exits.

Configuration

config.toml is automatically created on first launch. Its location is under the OS standard configuration directory.

In TUI / DAW NORMAL mode, pressing e opens config.toml in an editor. After closing the editor, restart the application.

Here is an example configuration.

# [REQUIRED] CLAP plugin to use
plugin_path = 'C:\Program Files\Common Files\CLAP\Surge Synth Team\Surge XT.clap'

# Editor candidates to open config.toml (tried in order from left)
editors = ["fresh", "zed", "code", "edit", "nano", "vim"]

input_midi   = "input.mid"

# output_midi and output_wav are automatically saved under
# clap-mml-render-tui/phrase/ or clap-mml-render-tui/daw/ in the configuration directory.
# The following values are used internally.
output_midi = "output.mid"
output_wav  = "output.wav"

sample_rate = 48000
buffer_size = 512

# Offline rendering is performed by a render-server child process.
# Concurrent execution count (1-16) - port - startup command (searches for executable if empty)
offline_render_server_workers = 4
offline_render_server_port = 42153
offline_render_server_command = ""

# Realtime playback backend ("cache_player" / "play_server")
realtime_audio_backend = "cache_player"
realtime_play_server_port = 42154

# Whether to autoplay on startup
# Notepad mode: immediately plays the current line. DAW mode: starts playback from the beginning of the song (measure 0).
autoplay_on_startup = true

# List of directories to search for in the WAV loop browser
loop_dirs = []

# List of categories that can be assigned to WAV loop directories
loop_categories = ["guitar", "drum", "bass", "spoken", "sequence"]

# Only write if you want to change Surge XT's default values
[plugins."Surge XT"]
patches_dirs = [
  'C:\ProgramData\Surge XT\patches_factory',
  'C:\ProgramData\Surge XT\patches_3rdparty',
]

The configuration items are as follows.

Item Default Value Description
plugins."Surge XT".plugin_path OS-specific Surge XT CLAP standard path Path if Surge XT is installed in a non-standard location.
editors ["fresh", "zed", "code", "edit", "nano", "vim"] Editor candidates to try in order from left.
input_midi input.mid Internal input MIDI file name.
output_midi output.mid Internal output MIDI file name.
output_wav output.wav Internal output WAV file name.
sample_rate 48000 Sample rate for rendering.
buffer_size 512 Buffer size for rendering.
offline_render_server_workers 4 Number of concurrent offline rendering (render-server) executions.
offline_render_server_port 42153 Localhost port for the render-server.
offline_render_server_command Empty string Startup command for the render-server. If empty, it searches for the executable.
realtime_audio_backend cache_player Real-time playback execution target (cache_player / play_server).
realtime_play_server_port 42154 Localhost port for the play_server.
autoplay_on_startup true Whether to autoplay immediately after startup.
plugins."Surge XT".patches_dirs OS-specific Surge XT patches standard directory List of directories to search for Surge XT timbres.
loop_dirs [] List of directories to search for in the WAV loop browser. After changing, run cmrt scan-loops.
loop_categories ["guitar", "drum", "bass", "spoken", "sequence"] List of categories that can be assigned to loop directories. The key for the category overlay is determined from unused English letters within the category name.

OS-specific default plugin_path values are as follows.

OS-specific default patches_dirs values are as follows.

Fixed Default Plugin and Multiple Plugins

The default plugin for lines without a specified timbre is Surge XT fixed. There is no switching via active_plugin. Other plugins like Dexed are added to a mixed catalog via [plugins.<Name>] and used on lines where the timbre is explicitly stated.

The contents of the built-in profiles are as follows, with paths set to OS-specific standard installation locations.

Name plugin_id patches_dirs Usage-specific categories
Surge XT org.surge-synth-team.surge-xt OS-specific default values from the table above Surge XT category names
Dexed com.digital-suburban.dexed Dexed cartridge location (Windows: %APPDATA%\DigitalSuburban\Dexed\Cartridges) All empty (= no filtering)
Vaporizer2 com.vastdynamics.VAST2 Presets under registry (HKLM\SOFTWARE\VAST Dynamics\Vaporizer2\Settings’s InstallPath). patches_dirs in config is ignored Vaporizer2 category names (e.g., Pad / Bass / Arpeggio)
Six Sines org.baconpaul.six-sines %LOCALAPPDATA%\clap-mml-render-tui\vendor-patches\six-sines-factory (factory timbre acquisition location). patches_dirs in config is ignored All empty (= no filtering)
TyrellN6 com.u-he.TyrellN6 Presets\TyrellN6 under registry (HKCU\Software\u-he\TyrellN6’s DataPath). patches_dirs in config is ignored All empty (= no filtering)

Names are matched ignoring differences in case, spaces, and underscores (Dexed / dexed, Surge XT / surge_xt / SurgeXT are all treated as the same). You only write [plugins.<Name>] if you are installing in a non-standard location, supplementing timbre locations, or using a plugin not built-in. Only the written items will override built-in values, so if you only want to change Surge XT’s path, a single plugin_path line within that table is sufficient.

# Path only replaced. plugin_id and patches_dirs remain built-in values.
[plugins."Surge XT"]
plugin_path = 'D:\my\clap\Surge XT.clap'

# For plugins not built-in, specify everything.
[plugins.my_synth]
plugin_path   = 'D:\my\clap\MySynth.clap'
patches_dirs = ['D:\my\patches']
Item Description
plugins.<Name>.plugin_path Path to that plugin.
plugins.<Name>.plugin_id Expected CLAP plugin ID. Can be omitted.
plugins.<Name>.patches_dirs Location for that plugin’s timbres. To clear built-in values, write patches_dirs = [].
plugins.<Name>.<Usage>_patch_categories / <Role>_patch_keywords Filtering for usage-specific patch auto-selection. Seven key names can be written (chord_patch_categories / bass_patch_categories / arpeggio_patch_categories / drum_patch_categories / kick_patch_keywords / snare_patch_keywords / hihat_patch_keywords). Only the written items take effect for that specific plugin. If omitted, the plugin’s default value (Surge XT uses category names, others use “no filtering”) is used.

All offline rendering is performed via the render-server. The TUI side (cmrt.exe) does not load CLAP plugins; it sends MML to 127.0.0.1:<offline_render_server_port>/render and receives WAVs. If the connection to the render-server fails, cmrt launches a child process and retries once after restarting it in case of a communication error. If offline_render_server_command is empty, the child process executable is searched for in the order of ‘same directory as cmrt.exe -> sibling repo clap-mml-play-server’s release build’, and PATH is not consulted.

Update Command

cmrt update

Server Mode

cmrt --server

CLI Mode

cmrt cde
cmrt CM7

Patch Roles Command

cmrt patch-roles
cmrt patch-roles --config C:\tmp\try.toml

Render MML Command

cmrt render-mml --patch "AR Accent Arp.vvp"
cmrt render-mml --config C:\tmp\try.toml --out-dir C:\tmp\wav --patch "PD Juno Dream Pad.vvp" --poly-check

Breaking Changes

Future Plans

Concept Notes

Out of Scope