{"id":51706389,"url":"https://github.com/1ajh/xm-2-wav","last_synced_at":"2026-07-16T16:04:24.830Z","repository":{"id":371045932,"uuid":"1298704183","full_name":"1ajh/xm-2-wav","owner":"1ajh","description":"Render FastTracker II .xm modules to lossless WAV at any fixed BPM and transposition — Windows, macOS \u0026 Linux.","archived":false,"fork":false,"pushed_at":"2026-07-13T00:10:56.000Z","size":1500,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-13T02:07:19.338Z","etag":null,"topics":["audio","audio-conversion","chiptune","cross-platform","fasttracker","libopenmpt","module-music","python","tkinter","tracker-music","wav","xm"],"latest_commit_sha":null,"homepage":"https://ajh.wtf","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1ajh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-12T23:56:49.000Z","updated_at":"2026-07-13T00:11:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/1ajh/xm-2-wav","commit_stats":null,"previous_names":["1ajh/xm-2-wav"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/1ajh/xm-2-wav","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1ajh%2Fxm-2-wav","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1ajh%2Fxm-2-wav/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1ajh%2Fxm-2-wav/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1ajh%2Fxm-2-wav/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1ajh","download_url":"https://codeload.github.com/1ajh/xm-2-wav/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1ajh%2Fxm-2-wav/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35549675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","audio-conversion","chiptune","cross-platform","fasttracker","libopenmpt","module-music","python","tkinter","tracker-music","wav","xm"],"created_at":"2026-07-16T16:04:24.109Z","updated_at":"2026-07-16T16:04:24.821Z","avatar_url":"https://github.com/1ajh.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XM-2-WAV\n\nA small cross-platform program (**Windows · macOS · Linux**) that imports FastTracker II\n`.xm` modules and renders them to **lossless WAV** at **any fixed BPM** and **any\ntransposition** — with an in-app **audio preview** so you can test transpositions before\ncommitting to a render.\n\n![XM-2-WAV](assets/ui_dark.png)\n\n- **Faithful rendering** — audio is produced by [libopenmpt](https://lib.openmpt.org/)\n  (the same engine used by OpenMPT and foobar2000), with the highest-quality 8-tap sinc\n  interpolation. Instruments, samples, envelopes and effects are reproduced exactly.\n- **Lossless output** — the default is **32-bit float WAV**, which stores the engine's\n  native output verbatim: no quantisation, no clipping, full dynamic range. 24-bit and\n  16-bit PCM are also available.\n- **Musically-lossless BPM \u0026 transpose** — applied by editing the `.xm` *score* before\n  rendering, not by DSP time-stretching or pitch-shifting. **No audio artifacts.**\n- **Preview before rendering** — play a short clip at the current transpose/BPM to A/B\n  different transpositions instantly.\n- **Dark mode** (default) and light mode.\n\n## Download \u0026 install\n\nSee **[INSTALL.md](INSTALL.md)** for full details. In short:\n\n- **Windows** — download and run `XM-2-WAV.exe` (self-contained, no install needed).\n- **macOS** — open `XM-2-WAV.dmg`, drag to Applications (first launch: right-click → Open).\n- **Linux** — `XM-2-WAV-x86_64.AppImage` (any distro), or `yay -S xm-2-wav` on Arch (AUR).\n- **Any OS with Python** — `pipx install \"xm-2-wav[preview]\"` (install libopenmpt + Tk first on macOS/Linux).\n\n## Using it\n\n1. **Add…** one or more `.xm` files.\n2. Tick **Fix BPM** and choose a value (or leave it off to keep the original tempo).\n3. Set **Transpose** in semitones (`+12` = up one octave).\n4. Click **▶ Play preview** to hear the result; tweak the transpose and preview again.\n5. Pick sample rate and WAV format (32-bit float is the lossless default).\n6. Choose an output folder (or leave blank to save next to each `.xm`).\n7. **Render WAV**.\n\nOutput files are named `‹song›_bpm‹N›_t‹±N›.wav`.\n\n### Command line (bonus)\n\nThe same executable also works headless when given arguments:\n\n```\nXM-2-WAV.exe song.xm --bpm 140 --transpose 3 --format float32\nXM-2-WAV.exe a.xm b.xm --bpm 120 --outdir renders\n```\n\nOptions: `--bpm N`, `--transpose N`, `--samplerate N`, `--format float32|pcm24|pcm16`,\n`--normalize`, `--outdir DIR`, `--out FILE`.\n\n## What \"fixed BPM\" means\n\nThe song is forced to a single constant BPM: the module's default-BPM field is rewritten\nand every in-song \"set BPM\" effect (`Fxx` with `xx ≥ 0x20`) is removed. Speed (ticks-per-row)\nautomation is preserved, since that is a separate parameter from BPM.\n\n## What \"transposition\" means\n\nEvery note event in every pattern is shifted by the chosen number of semitones (notes are\nclamped to the valid range). This is the exact operation a tracker performs when you\ntranspose a song — multi-sampled instruments follow their key maps correctly, and there is\nno resampling of already-rendered audio.\n\n## How it works\n\n```\n .xm bytes ──► xmwav.xmedit ──► edited .xm bytes ──► libopenmpt ──► float32 ──► WAV\n              (transpose notes,     (samples/instruments   (8-tap sinc)   (xmwav.wavio)\n               rewrite BPM)          untouched)\n```\n\n- `xmwav/xmedit.py`  — pure, dependency-free in-place `.xm` editor (transpose + fix BPM).\n- `xmwav/engine.py`  — `ctypes` wrapper that finds libopenmpt (bundled on Windows, system lib on macOS/Linux).\n- `xmwav/wavio.py`   — minimal float32 / 24-bit / 16-bit WAV writer/encoder.\n- `xmwav/player.py`  — cross-platform preview playback (`winsound` on Windows, `sounddevice` elsewhere).\n- `xmwav/theme.py`   — dark / light theming.\n- `xmwav/convert.py` — edit → render → write.\n- `xmwav/cli.py`     — CLI + GUI dispatcher (backs the `xm-2-wav` entry points).\n- `xmwav/app.py`     — Tkinter GUI.\n\nOn Windows the libopenmpt DLLs are bundled in `xmwav/libs/`. On macOS/Linux the app uses\nthe system libopenmpt (`brew install libopenmpt` / `pacman -S libopenmpt` / `apt install libopenmpt0`).\n\n## Building the downloads\n\nEach platform's build recipe lives in `packaging/`, and the GitHub Actions workflow\n`.github/workflows/build.yml` builds **all three** on real runners and cuts a Release on\nversion tags. See **[INSTALL.md](INSTALL.md#for-packagers--developers-building-the-downloads)**\nfor the per-OS commands. Quick reference:\n\n```bash\n# Windows\n./build.ps1\n# macOS\nbash packaging/macos/build_app.sh\n# Linux (AppImage)\nbash packaging/linux/build_appimage.sh\n# Python wheel/sdist\npython -m build\n```\n\nRun the GUI without building: `python xm_to_wav.py` (or `python -m xmwav`).\n\n## Tests\n\n```powershell\npython -m tests.selftest      # editor + engine: FFT-verified pitch, duration, WAV formats\npython -m tests.gui_smoke     # GUI widget wiring + theming\npython -m tests.gui_render    # GUI render worker end-to-end\n```\n\n`tests/selftest.py` synthesises a known module and checks, among other things, that\n`+12` semitones doubles the pitch, `+7` gives a perfect fifth, and doubling the BPM halves\nthe duration.\n\n## Credits\n\nDeveloped by **1ajh** ([ajh.wtf](https://ajh.wtf)), built with **Claude** (Anthropic).\n\nRendering by [libopenmpt](https://lib.openmpt.org/) (BSD license; see\n`xmwav/libs/LICENSE.libopenmpt.txt` and [NOTICE](NOTICE)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1ajh%2Fxm-2-wav","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1ajh%2Fxm-2-wav","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1ajh%2Fxm-2-wav/lists"}