Imported from yousernaym/vm (
AGENTS.md). Install upstream withnpx skills add yousernaym/vm. Copyright stays with the author.
AGENTS.md
This file provides guidance for AI coding agents when working with code in this repository.
What this repository is
Visual Music (VM.exe) is a Windows desktop app that renders note-based music (MIDI, tracker
modules, SID) as real-time 3D visualizations and exports them to MKV video (including 360°). The app
code lives in VisualMusic/; everything under Dependencies/ is a git
submodule (a separate repo) that this solution builds and the app consumes.
Start here: the detailed app-level guide is VisualMusic/AGENTS.md — read it for the WPF app architecture, MVVM layout, rendering pipeline, undo/redo, and UI navigation.
Solution layout
Everything builds from one aggregate solution at the repo root: VisualMusic.sln. It contains the app,
the in-house submodules, the forked MonoGame, and the vendored C++ audio libraries, wired together with
project dependencies so a single build produces a runnable app.
| Project | Language | How VisualMusic uses it | Guide |
|---|---|---|---|
| VisualMusic | C# (net10.0-windows, WPF) | the app itself; assembly VM.exe |
VisualMusic/AGENTS.md |
| midiLib | C# (net10.0) | ProjectReference — MIDI parsing (Midi.Song) |
Dependencies/midiLib/AGENTS.md |
| MonoGame (WindowsDX) | C# (fork) | ProjectReference — 3D graphics framework |
Dependencies/MonoGame/AGENTS.md |
| Media | C++ → media.dll |
P/Invoke (VisualMusic/Media.cs) — FFmpeg video export + Media Foundation audio playback | Dependencies/Media/AGENTS.md |
| MidMix | C++ → MidMix.dll |
P/Invoke (VisualMusic/MidMix.cs) — Fluidsynth MIDI→WAV mixdown | Dependencies/MidMix/AGENTS.md |
| Remuxer | C# exe + C++ libRemuxer.dll |
launched as a child process (remuxer/remuxer.exe, see VisualMusic/Project.cs) — converts MOD/SID → MIDI+WAV |
Dependencies/Remuxer/AGENTS.md |
Data-flow in one line: MOD/SID are converted to MIDI by Remuxer, MIDI is parsed by midiLib into a
Midi.Song, MonoGame renders it, MidMix synthesizes MIDI audio, and Media plays audio back
and exports video.
Build
Full prerequisites and the exact vcpkg steps are in README.md. In short:
- Visual Studio 2026 with the .NET desktop and Desktop development with C++ workloads (plus the Spectre-mitigated MSVC v143 x64/x86 libs).
- vcpkg with
vcpkg integrate install. Fluidsynth andffmpeg[x264]are restored automatically via vcpkg manifest mode: Media and MidMix each ship avcpkg.json(pinning versions with abuiltin-baseline), and the first solution build installs them into a localvcpkg_installed/per submodule. - Clone with
--recurse-submodules— theDependencies/*repos must be present or the solution won't load. - Build
VisualMusic.sln(Debug/Release). Use the Any CPU solution platform (the only one); C++ projects still build as x64 under the hood. The first build auto-runsdotnet tool restore(the MonoGame content-builder tool, pinned in VisualMusic/.config/dotnet-tools.json).
Building from the command line.
This is a mixed C#/C++ solution, so you must build it with MSBuild, not dotnet build
(dotnet build cannot build the C++ .vcxproj projects). msbuild is usually not on PATH in a
plain shell — do not give up if msbuild "isn't found". Locate it with vswhere (always installed with
VS, at a fixed path) and call it by full path. From the repo root (d:\dev\vm), in PowerShell:
$msbuild = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" `
-latest -prerelease -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe"
& $msbuild d:\dev\vm\VisualMusic.sln /p:Configuration=Debug /p:Platform="Any CPU" /m /nologo
Notes:
- Pass the full path to the
.sln. The agent PowerShell shell does not reliably start in the repo root, so a bareVisualMusic.slnfails withMSB1009: Project file does not exist. Use the absolute pathd:\dev\vm\VisualMusic.sln(the Bash tool, by contrast, does persist its cwd at the repo root). - The solution platform is
Any CPU(the only supported solution platform). That still builds the C++ projects as x64 (via sln project mappings); native DLLs land in repo-rootx64\<Config>\. - Use
/p:Configuration=Releasefor a release build./menables parallel builds;/t:Rebuildforces a clean rebuild. - On this dev machine MSBuild also happens to be on
PATH(a VS Developer shell), so a baremsbuild VisualMusic.sln /p:Platform="Any CPU"may work too — but thevswhereform above is the reliable one for agents and fresh shells. - First build only: if you hit a missing-tool error, run
dotnet tool restorein VisualMusic/ first. - Reading the output: a successful build is ~126 KB and noisy with pre-existing, benign warnings
(MVVMTK0034 "should not be directly referenced", SYSLIB0003, CS0618, CS0168) plus a transient
VisualMusic_<hash>_wpftmp.csproj(WPF's markup-compile pass — not an error). To find real failures grep for": error "and excludeaka.ms//errors/(those substrings appear in warning help-links and cause false positives). Success indicators:VM.dll ->is emitted and the post-build native/remuxer copy target runs without errors.
Where the app output lands (sln vs csproj — easy to get wrong)
VisualMusic.slnwith/p:Configuration=<Config> /p:Platform="Any CPU": the app is Any CPU, so the runnable output is alwaysVisualMusic\bin\<Config>\net10.0-windows10.0.26100.0\— nox64\segment. (The C# assembly is platform-agnostic; the native DLLs copied in are still x64 fromx64\<Config>\.)- Building
VisualMusic.csprojdirectly: don't for a runnable app.CopyNativeOutputssoft-skips whenx64\<Config>\media.dll/MidMix.dllor a packaged Remuxer bin are missing (soVisualMusic.Testscan build the managed assembly alone withoutAdditionalProperties). Always build the.slnfor a completeVM.exe.
Stale copies of VM.exe/VM.dll can linger in old folders (e.g. bin\x64\Debug\ from a former project-x64
config). To confirm where a build landed, read the
VM.dll -> <path> line in the build log, or find the newest binary:
Get-ChildItem d:\dev\vm\VisualMusic\bin -Recurse -Filter VM.exe | Sort-Object LastWriteTime -Descending
How the pieces reach the app output (this part trips people up — it's spread across several project files):
- The C++ projects (Media, MidMix, libRemuxer + its vendored libs) build into the repo-root
x64\<Config>\(project platform is x64 even when the solution platform is Any CPU). - VisualMusic's
CopyNativeOutputstarget copiesx64\<Config>\*.dllinto the app output, and copies the entire Remuxer build into<app output>\remuxer\. - MonoGame
.fx/content is copied via theContent\items inVisualMusic.csproj.
Optional at runtime: place a soundfont.sf2 next to VM.exe for MIDI audio synthesis.
Working across submodules
Dependencies/midiLib, Media, MidMix, Remuxer, and MonoGame are independent git repos (see
.gitmodules); Remuxer itself nests libRemuxer. Edits inside a submodule are commits in
that repo — this repo only tracks the submodule commit pointer. MonoGame is a large upstream fork and
most of Remuxer/libRemuxer (openmpt, sidplayfp) is vendored third-party code: treat both as
upstream and change only what Visual Music requires.
Testing
Essential automated tests live in each first-party repo (xUnit for C#, GoogleTest for libRemuxer Song/FileFormat/FX fixtures).
Fixtures live in the deepest owning submodule (midiLib/test-files/, Remuxer/libRemuxer/test-files/,
Media/test-files/, MidMix/test-files/). VisualMusic.Tests copies each tree into
test-files/<owner>/ (PreserveNewest) so relative names cannot collide across submodules;
TestFiles.PathTo(owner, relative) resolves under the test assembly directory.
MonoGame is not covered. Remuxer.Tests still needs the sibling midiLib checkout to validate generated MIDI.
Module fixtures come in two sets — per-note effects (FX.XM / FX.S3M / FX.IT) and transport effects
(mod-transport/: position jump, pattern break, pattern delay, pattern loop, speed/tempo) — each a hand-authored
XM plus generated S3M / IT twins. Pattern and order layout for both lives in libRemuxer GoogleTest; MIDI conversion
coverage is Remuxer Integration (FxMidiTests, ModTransportMidiTests).
Unit tests (no native build required beyond what dotnet restores):
dotnet test D:\dev\vm\Dependencies\midiLib\midiLib.Tests\midiLib.Tests.csproj --nologo
dotnet test D:\dev\vm\Dependencies\Remuxer\Remuxer.Tests\Remuxer.Tests.csproj --filter "Category!=Integration" --nologo
dotnet test D:\dev\vm\VisualMusic\VisualMusic.Tests\VisualMusic.Tests.csproj --filter "Category!=Integration" --nologo
GoogleTest (links libopenmpt; first build also restores gtest for the x64-windows-static triplet).
Prefer VisualMusic.sln / Remuxer.sln so openmpt is built first; standalone needs libopenmpt x64 first:
$msbuild = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" `
-latest -prerelease -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe"
& $msbuild D:\dev\vm\Dependencies\Remuxer\libRemuxer\openmpt\build\vs2022win10\libopenmpt.vcxproj /p:Configuration=Debug /p:Platform=x64
& $msbuild D:\dev\vm\Dependencies\Remuxer\libRemuxer\tests\libRemuxer.Tests.vcxproj /p:Configuration=Debug /p:Platform=x64
& D:\dev\vm\Dependencies\Remuxer\libRemuxer\tests\x64\Debug\libRemuxer.Tests.exe
Integration tests (build VisualMusic.sln Debug|Any CPU first so media.dll / MidMix.dll / Remuxer.exe
exist and VisualMusic.Tests copies a complete native runtime beside the test assembly):
& $msbuild D:\dev\vm\VisualMusic.sln /p:Configuration=Debug /p:Platform="Any CPU" /m /nologo
dotnet test D:\dev\vm\VisualMusic\VisualMusic.Tests\VisualMusic.Tests.csproj --filter "Category=Integration" --nologo
dotnet test D:\dev\vm\Dependencies\Remuxer\Remuxer.Tests\Remuxer.Tests.csproj --filter "Category=Integration" --nologo
Manual UI checks (import, export, undo) remain useful; see VisualMusic/AGENTS.md.
