waveform-MCP

waveform-MCP

An MCP server that gives an LLM agent control over Tracktion Waveform, enabling it to compose, mix, and render songs through natural language commands.

Category
访问服务器

README

Waveform MCP

An MCP server that gives an LLM agent control over Tracktion Waveform. Ask Claude to write a song, balance a mix, or render to MP3 — and watch Waveform do it.

Synthwave arrangement built end-to-end via the MCP A 64-bar synthwave instrumental composed via MCP tool calls — drums, bass, two pads, counter, arp, lead. Section markers, tempo automation, sidechain pump, plate reverb, clip-level fades, full master chain.


What this gives you

  • 141 MCP tools across edit lifecycle, tracks/buses, MIDI, audio clips, plugins, automation, music theory, mix balance, composition primitives, drum pattern library, progression generator, voice leading, arrangement planner, audio analysis (LUFS / tempo / key detection), reference song database, mastering chain templates, MIDI export, chord chart export, A/B snapshots, tool-call history, render, loop library, VST discovery, schema capture, and Waveform UI control
  • Three composerscompose_lofi_track, compose_synthwave_track, compose_rainstorm
  • compose_variations to spin off N seeded variations of any composer
  • A music-theory knowledge layer — scales, chord progressions, cadences, song forms, voice-leading rules, mix-balance reference levels per genre, reference song database
  • Verified round-trip between the in-memory model and Waveform's .tracktionedit XML
  • Clip-level fades, gain, offset, automation curves — proven primitives the LLM can use to iterate musically
  • Reliable workflow loopcompose → write → reload via File → Revert to saved → listen → tweak

Status

Working end-to-end on Windows + Waveform 13. macOS / Linux paths exist for content discovery (presets, loop library, VST list) but Windows-only UI control via UIA / pywinauto for now.

Built and tested through ~30 hours of human-in-the-loop iteration with Claude. Both composers have been rendered to MP3 multiple times and the user has signed off on the resulting tracks.


Quick start

Install

cd "C:\path\to\waveform MCP"
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e .

Wire to Claude (Code, Desktop, or any MCP client)

~/.claude.json or your client's MCP config:

{
  "mcpServers": {
    "waveform": {
      "command": "waveform-mcp"
    }
  }
}

Try it

Open Waveform, then ask Claude:

  "Use compose_synthwave_track to make a synthwave song,
   save it to my Documents/Waveform folder, and reload it
   in Waveform so I can hear it."

The LLM calls compose_synthwave_trackwaveform_revert_to_saved and you press play. Then iterate: "turn down the bass" → the LLM updates MIX_BALANCE["synthwave"]["bass"] and reloads.


Architecture

Four layers, each with a clear contract:

┌──────────────────────────────────────────────────────────────┐
│  LLM (Claude / any MCP client)                               │
└────────────────────────────┬─────────────────────────────────┘
                             │ MCP stdio
┌────────────────────────────▼─────────────────────────────────┐
│  MCP server (server.py) — 107 tools                          │
└────────────────────────────┬─────────────────────────────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
      ┌──────────────┐ ┌──────────┐ ┌──────────────┐
      │ Edit model   │ │ Knowledge│ │  Waveform    │
      │ (in-memory)  │ │  (data)  │ │  UI control  │
      ├──────────────┤ ├──────────┤ ├──────────────┤
      │ Tracks       │ │ Scales   │ │ pywinauto +  │
      │ Clips        │ │ Chords   │ │ UIA + ffmpeg │
      │ Notes        │ │ Forms    │ │              │
      │ Plugins      │ │ Mix      │ │ Menu invoke  │
      │ Automation   │ │ Velocity │ │ Revert       │
      │ Markers      │ │ Rhythm   │ │ Render→MP3   │
      └──────┬───────┘ └──────────┘ └──────┬───────┘
             │                             │
             ▼                             ▼
   ┌──────────────────┐          ┌────────────────────┐
   │ xml_writer.py    │          │ Waveform 13        │
   │ xml_reader.py    │ ◀──────▶ │ (the running app)  │
   │ ↓ .tracktionedit │          └────────────────────┘
   └──────────────────┘

Key design choice: The model is not a 1:1 of the Tracktion ValueTree — it's the shape the LLM wants to work with, projected onto the XML on save and projected back on load. This makes tools simple (audio_clip_import(track_id, file_path, start_beats, length_beats, fade_in_beats, ...)) instead of forcing the LLM to think in JUCE-internals.


Tool catalog

Edit lifecycle (edit.py)

edit_create · edit_open · edit_save · flush · edit_summary · edit_inspect · undo · narrate

Tracks + mix + bus routing (tracks.py)

track_add · track_remove · mix_set · mix_apply_reference · send_add · marker_add · tempo_set · key_set · bus_create · bus_route

mix_apply_reference(track_id, genre, role) looks up the dB target from a curated mix-balance table (MIX_BALANCE[genre][role]) — the "kick is the anchor, bass 5-6 dB under" reference distilled into a callable.

bus_create("Drum Bus") + bus_route(kick_track, drum_bus) lets you stack tracks onto a submix for shared compression / EQ.

MIDI (midi.py)

midi_clip_add · midi_notes_add · midi_notes_clear · midi_clip_quantize

Audio clips (audio.py, clips.py)

audio_clip_import (with gain_db, fade_in_beats, fade_out_beats, offset_in_source_beats) clip_list · clip_set · clip_move · clip_resize · clip_duplicate · clip_remove

Plugins (plugins.py, preset_library.py)

plugin_list · plugin_add · plugin_set_param · plugin_remove plugin_add_reverb (plate / natural / non-linear with sensible defaults) plugin_add_drum_kit (Sampler-backed, one SOUND per pad) plugin_add_modifier (LFO / envelope / sidechain — schema TBD on full Waveform support) plugin_discover (parses knownPluginList64.settings to list installed VSTs) EQ + compressor helpers: eq_high_pass · eq_low_pass · eq_tilt · compress_glue · compress_smash waveform_preset_list · waveform_preset_read · waveform_plugin_types

Composition primitives (melody.py, drum_patterns.py, composition.py)

melody_generate(scale, contour, density) — rule-based melody from contour shape (arch / descending / wave / question_answer / static) arp_pattern(chord, rate, direction, octaves) — arpeggio over a chord (up / down / up_down / random / octave_alternate) bassline_generate(roots, feel) — bass under a progression (pump / walking / half_time / sub / octave_jumps / dub) motif_develop(notes, transformation) — variations: transpose / invert / retrograde / augment / diminish / sequence_up / sequence_down drum_pattern(genre, role, density, length_bars) — 50+ named grooves indexed by (genre, role); synthwave, lofi, hip_hop, edm, house, techno, trap, dnb, rock, pop, ambient drum_pattern_list — show the catalog progression_generate(root, mode, length, end_cadence, style) — invent chord progressions; styles: pop / jazz / modal / synthwave / sad / uplifting voice_lead(prev_voicing, next_chord) — minimal-motion voice leading arrangement_plan(genre, target_seconds, energy_curve) — produce a section list with bars + role + energy 0..1; curves: slow_burn / immediate_hook / build_drop_build / call_response

Audio analysis (audio_analysis.py, audio_detect.py)

audio_loudness_lufs(file) — integrated LUFS, true peak, LRA via ffmpeg ebur128 audio_spectrum(file, bands=10) — log-spaced RMS-per-band frequency balance audio_compare(a, b) — diff LUFS / peak / per-band spectrum between two files audio_detect_tempo(file) — BPM via onset autocorrelation (~1% accuracy on clear loops) audio_detect_key(file) — root + mode via Krumhansl-Schmuckler chromagram

Reference songs (reference_songs.py)

reference_song_lookup(name) / reference_song_list — curated DB of 11 references across synthwave / pop / house / ambient / lofi (Drive (Kavinsky), Midnight City (M83), Sweet Dreams, Africa, In the Air Tonight, Strobe, Miami, Hide and Seek, Still Alive, generic chillhop, generic outrun) with BPM, key, form hint, energy curve, signature elements.

Mastering / mix workflow (workflow.py)

master_chain_apply(template) — drop-in chains: clean_pop / loud_edm / lofi_warm / cinematic_dynamic / podcast_voice / no_processing snapshot_save(name) / snapshot_recall(name) / snapshot_list — A/B compare versions of an Edit edit_transpose(semitones) — shift all MIDI notes edit_set_tempo(bpm) — change project tempo compose_variations(composer_name, prefix, count) — generate N seeded variations of any composer tool_history(last_n) — list recently-called tools (auto-recorded by the @op decorator) edit_save_stem(track_names, out_path) — save a stem-mix copy with named tracks soloed edit_export_chord_chart(out_path, format) — text chord chart from markers + key

MIDI export (midi_export.py)

edit_export_midi(out_path) — Standard MIDI File (format 1) with one track per Edit MIDI track, plus tempo + time-sig in track 0.

Automation (automation.py)

automation_add · automation_envelope · automation_clear · automation_list

Targets: pan and plugin/<plugin_id>/<param>. Volume target is disabled at the API level — Waveform's volume plugin doesn't honor our <AUTOMATIONCURVE> schema and silences the track. Use mix_set/mix_apply_reference for static levels and clip_set(fade_in_beats|fade_out_beats) for fades. (The MCP refuses target="volume" with a clear error pointing to alternatives.)

Music theory knowledge (music_theory.py, music_theory_data.py)

17 query tools: theory_scale · theory_modes · theory_diatonic_chords · theory_chord_progression · theory_cadences · theory_song_form · theory_section · theory_genre · theory_arrangement_layers · theory_velocity · theory_rhythm · theory_voice_leading_rules · theory_heuristics · theory_surprise_devices · theory_borrowed_chords · theory_mix_balance · theory_search

The data behind these:

  • 13 scales (major modes, harmonic minor, pentatonic, blues, etc.)
  • 25+ chord progressions (axis_pop, ii_V_I, andalusian, lament_bass, …)
  • Cadences, song forms, sections with role/density/dynamic profiles
  • 14 genres with typical BPM, key tendencies, instruments, hallmark progressions
  • Velocity / rhythm maps (swing ratios, accent bumps, ghost-note ranges)
  • 13 songwriting heuristics (rule-of-3, contrast-required, surprise quota, …)
  • 7 surprise devices (truck-driver modulation, deceptive cadence, …)
  • Mix balance reference table — 7 genres × 14 roles, fully annotated

Composers (composer.py)

  • compose_lofi_track — 32-bar lofi with drums, bass, keys, pad, melody, counter; section-aware velocity envelopes; tempo automation; lofi master chain
  • compose_synthwave_track — 64-bar synthwave with 7 tracks; 9-section form (intro/verse/chorus/verse/chorus/bridge/buildup/chorusFinal/outro); per-section bass feels (half-time / 8th-pump / walking); section-keyed arp themes; sidechain-style filter pump; section markers; clip fades
  • compose_rainstorm — ambient soundscape with rain + wind + lowpassed-distant thunder; per-clip gain randomization; offset trim; track FX

Render (render.py, waveform_workflows.py)

waveform_render_export · waveform_render_to_mp3 (uses bundled ffmpeg + libmp3lame)

Waveform UI control (waveform_workflows.py)

waveform_new_project · waveform_save · waveform_revert_to_saved (the iteration loop unlocker) · waveform_close_active_tab · waveform_active_tab · waveform_project_loaded · waveform_menu_invoke · waveform_add_track · waveform_select_track · waveform_insert_clip_on_track · waveform_build_skeleton

App lifecycle (waveform_app.py)

waveform_locate · waveform_status · waveform_launch · waveform_focus · waveform_quit · waveform_settings_dir

Loop library (loops.py)

loop_search (by tempo / bars / name) · loop_drop (auto-length, fit-to-tempo)

Schema capture (schema_capture.py)

schema_snapshot_current_edit · schema_diff_snapshots · schema_list_snapshots

Low-level UI / desktop (win_input.py, desktop.py)

18 primitives for window management, UIA inspection, key/click sending, screenshots.


Layout

waveform-mcp/
├── src/waveform_mcp/
│   ├── server.py                  MCP server entry (stdio)
│   ├── model.py                   Edit / Track / Clip / Note / AutomationLane dataclasses
│   ├── xml_writer.py              Edit → .tracktionedit
│   ├── xml_reader.py              .tracktionedit → Edit
│   ├── audio_convert.py           ffmpeg-backed MP3→WAV cache for Sampler sources
│   ├── music_theory_data.py       SCALES, PROGRESSIONS, GENRES, MIX_BALANCE, ...
│   ├── events.py                  event bus + JSONL log
│   ├── diff.py                    Edit-diff for change events
│   ├── tools/
│   │   ├── edit.py                Edit lifecycle
│   │   ├── tracks.py              Tracks + mix balance
│   │   ├── midi.py                MIDI clips/notes
│   │   ├── audio.py               Audio clip import
│   │   ├── clips.py               Clip mutators (move, resize, duplicate, set)
│   │   ├── plugins.py             Plugin add + reverb / drum kit / modifier helpers
│   │   ├── automation.py          Automation lanes (pan + plugin params)
│   │   ├── preset_library.py      Factory preset browser
│   │   ├── loops.py               Loop library search + drop
│   │   ├── render.py              Render stubs
│   │   ├── waveform_app.py        App lifecycle
│   │   ├── waveform_workflows.py  UI workflows (revert, render-to-mp3, etc.)
│   │   ├── desktop.py             Generic desktop primitives
│   │   ├── win_input.py           Windows UIA + keystroke primitives
│   │   ├── schema_capture.py      Hand-fixture capture for schema reverse-engineering
│   │   ├── music_theory.py        Theory query tools
│   │   ├── composer.py            compose_lofi_track, compose_synthwave_track, compose_rainstorm
│   │   └── common.py              @op decorator (apply + diff + event)
│   └── preview/
│       ├── app.py                 FastAPI + websocket
│       └── static/                HTML / JS piano-roll
├── tests/
├── docs/
│   ├── img/synthwave_arrangement.png
│   ├── ARCHITECTURE.md
│   ├── EVENT_SCHEMA.md
│   └── EDIT_MODEL.md
├── pyproject.toml
└── README.md

The iteration loop that actually works

After many false starts, here's the loop that lets the LLM and the user collaborate on a track without restarting Waveform every cycle:

1. Compose / mutate            → composer.compose_*  or clip_set / mix_apply_reference
2. Save to disk                → edit_save / flush  (writes .tracktionedit)
3. Reload in Waveform          → waveform_revert_to_saved
                                  (File → Revert to saved state, auto-confirms popup)
4. User listens                → "turn the arp up"
5. Update MIX_BALANCE or run a clip mutator
6. → goto 2

The killer move was discovering Waveform's File → Revert to saved state menu item: it forces the open Edit to reload from disk, which is what makes external mutation visible without closing/reopening the project. waveform_revert_to_saved automates that path with retry.


Mix balance reference

mix_apply_reference reads from a curated table of "kick is the anchor; bass 5-6 dB under; lead similar to bass; pad/arp 6-9 dB under lead; ambience deepest" — distilled across genre tutorials, mastering blogs, and tuned-by-ear iterations:

MIX_BALANCE["synthwave"] = {
    "drums": -7, "kick": -6, "snare": -10, "hat": -16,
    "bass": -25, "sub_bass": -28,           # background-level texture
    "lead": -15, "pad": -19, "arp": -8,     # arp-driven mix
    "counter": -14, ...
}

Composers call tracks.mix_apply_reference({track_id, genre, role}) once per track. Tweak the table once, every composer rebalances.

Sources informing the table:


Known limitations

  • Track-volume AUTOMATIONCURVE is disabled. Waveform's volume plugin doesn't honor our curve schema and silences the affected track. The MCP refuses the target with a clear error and points to working alternatives (clip fades, multiple clips with per-clip gain, static mix_set).
  • Plugin modifier matrix is exploratory. plugin_add_modifier writes a generic modmatrix shape; needs a hand-edited fixture to confirm the per-plugin schema before LFO modulation works reliably for 4OSC and friends.
  • Headless render not built yet. waveform_render_to_mp3 drives Waveform's UI export — works, but requires Waveform to be running. A C++ helper linking tracktion_engine is the eventual fix.
  • Linux/macOS UI control absent. Content discovery (presets, loop library, VST list) is OS-aware; UI automation is Windows-only.

Building blocks for next iterations

  • Capture a real Waveform fixture for <AUTOMATIONCURVE paramID="volume"> so volume automation can be re-enabled
  • C++ headless render helper on tracktion_engine
  • Drum Sampler / Micro Drum Sampler real fixture (currently fall back to plain Sampler)
  • Sidechain modifier capture
  • Clip Launcher (v13) support
  • Linux UI control via xdotool/wmctrl once UIA is no longer the only path

License

GPL-3.0-or-later (matches tracktion_engine if/when the C++ render helper links to it).

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选