Skip to main content

vinsynlib

The shared base of a family of terminal instrument browsers.

Nine sibling programs browse nine instruments over MIDI from a terminal: emorphed (E-mu Morpheus), ensqsqed (Ensoniq SQ-R Plus), eosed (E-mu E4XT), kwsed (Korg Wavestation SR), nanosyned (Alesis NanoSynth), p2ked (E-mu Proteus 2000), rxved (Roland XV-2020), s3ked (Akai S1000/S3000) and x5ded (Korg X5D/X5DR). Several of these repositories are not public yet, which is why only some of the names above are links.

A tenth project, k2kremote, is a remote for the Kurzweil K2000 rather than a browser, and depends on the same shared code.

They grew separately, which is the right way to learn what an instrument does and the wrong way to arrive at nine copies of the same code. This library is where the parts that are not the manufacturer's live, so that a change to one of them happens once instead of nine times.


What is in here, and what is not

In here: how a preference is remembered, where a favourite lives, what an error says, which key means what, how a port list is printed, how a legend wraps.

In each project: the wire format, the bank table, the meaning of a byte, the LCD glyphs.

The line is ours versus the manufacturer's. A MIDI port name is whatever ALSA reports, so the port listing is shared; which SysEx byte selects which bank is that instrument's business and stays put.

vinsynlib/
  config.py        the settings cache: port, channel, device ID, UI state
  favorites.py     the favourites database: SQLite, slots, ratings, tags
  keys.py          the keymap and the legend
  cli.py           the command line every tool builds the same way
  spec.py          that command line, as data: flags, commands, exit codes
  terms.py         the vocabulary: our words, and each tool's two
  midi.py          port listing, the shared error taxonomy, clean exit
  conformance.py   the checks a project runs against itself
  devchecks.py     checks that catch cross-project mistakes
  backup.py        the backup envelope: a versioned JSON container
  catalog.py       printed names, plus what has been read off the unit
  livenames.py     names read off the unit, kept between runs
  render.py        table and progress rendering, without Textual
  build.py         which build is running: version, commit, tag
  undo.py          the undo log: inspectable records, no threads or locks
  ui/hints.py      the legend widget
  ui/browser.py    BrowserApp, the AppProtocol implementation
  ui/protocol.py   the App contract screens depend on
  ui/work.py       worker infrastructure: busy gate, error set, idle-close
  ui/confirm.py    a generic yes/no confirmation modal
  ui/report.py     a scrollable block of text, as a modal
  ui/prompt.py     a one-line modal text prompt
  ui/modals.py     the shared modal screens: picker, search, report, rating

Why it exists

Nine projects each grew a config.py (about 190 lines, 90-97% identical), a favorites.py (about 620 lines, 85-100% identical), a KeyHints widget and a wrap_blocks helper. Measured across the family: roughly 6,300 lines of that were duplicates that need not exist.

Three of those copies had already drifted into bugs worth naming:

  • emorphed's LICENSE file opened with the words "nanosyned — Terminal browser for the E-mu Morpheus".
  • emorphed's mypy and deptry configuration still named the nano module, and one function imported nano.config -- nanosyned's package, which is not installed beside it. That function worked in the project it was copied from and raised ImportError the moment --config was used.
  • eosed wrote port names into config.toml without escaping them, so a port name containing a quote produced a file that is not TOML -- and its settings writer refuses to overwrite a file it cannot parse, so the cache never healed until somebody deleted it by hand.

None of these were visible in review. They were visible the moment there was a second copy to compare against, which is what this library is.


⚠️ Use at your own risk — back up first

vinsynlib is provided as is, with absolutely no warranty and no liability for data loss or hardware damage. You assume all risk. Full terms: DISCLAIMER.md.

Before you use this software, make good, current backups of all your files — and of any existing banks on your instrument and storage media. These tools can write to hardware and storage media; a mistake, a bug, or untested output could overwrite or corrupt data, or be rejected by hardware. Always test on a spare unit or emulator before connecting irreplaceable equipment.

Installing it, and who depends on it

This is a library, not a program. It is published on PyPI — the current release is 0.4.2 — so the ordinary way to depend on it is a dependency line:

vinsynlib>=0.4.2
python3 -m pip install vinsynlib

The library itself has no dependencies. The parts that need a terminal UI or a MIDI backend are behind the ui and midi extras:

python3 -m pip install "vinsynlib[ui,midi]"

Ten projects in the family depend on it: the nine browsers above and k2kremote. Each of them writes its own install steps out in its README, because a reader starts from the tool and rarely from here.

Working on the library itself. To develop against a checkout rather than the published release, put it beside the project that uses it and install it editable:

git-repos/
  vinsynlib/     <- this one
  x5ded/         <- a sibling, and the other tools beside it
cd ../x5ded                     # or any sibling
git clone https://github.com/lentferj/vinsynlib.git ../vinsynlib
python3 -m venv .venv
.venv/bin/pip install --no-deps -e ../vinsynlib   # --no-deps: its deps are theirs
.venv/bin/pip install -e .

For projects that configure [tool.uv.sources] pointing to this library, uv sync takes the path from there, so the checkout only has to exist and neither pip line is needed.

Using it

from vinsynlib.config import Settings

settings = Settings("x5ded")
channel = settings.load_channel()  # 0-15, or None; a TOML bool is None
settings.save_port("Midi Out: X5D")
from vinsynlib.favorites import Favorites

with Favorites(app_name="x5ded") as favourites:
    favourites.add("A", 7, name="Warm Pad", rating=4, tags="pad")
    favourites.toggle("A", 7)  # clears the flag, keeps the rating
from vinsynlib import cli

parser = cli.make_parser("x5cli", "Browse a Korg X5D's sounds.")
cli.add_common_arguments(parser, port=True, scan=True, channel=True)

Where the rules are written down

docs/UX-SPEC.md is the contract: terminology, the command line, the keys, the paths, the exit codes, and the reasoning behind the parts that are not obvious. It is the document to read before changing anything here, and the document to argue with.

Three things in it are worth repeating, because they are the parts a new contributor is most likely to get backwards:

The instrument's word is the instrument's. Each tool declares the word for a stored sound and for the area it lives in, once, and uses it consistently within itself. A Yamaha says PATCH; an E-mu says PRESET. The unit's word wins, because a tool that renames it has to translate, and a mistranslation is the difference between finding a sound and not.

Our words are the same everywhere. favourite, search, scan, dump, port: no tool invents a synonym for a concept the family owns.

config.toml is disposable; favorites.db is the user's own work. The first lives in the working directory and is safe to delete. The second lives in the platform data directory, is keyed on the slot rather than the name, and is the one file here worth backing up.

Development

$ uv venv && uv pip install -e ".[ui,midi]" && uv pip install \
      pytest pytest-cov mypy ruff vulture deptry detect-secrets pip-audit
$ make check

make check runs the whole gate in order and stops at the first failure: ruff, mypy in strict mode, the test suite with coverage, then vulture, deptry and pip-audit.


Support this project

vinsynlib is free software and always will be. Nothing is behind a paywall, no feature is withheld, and none of what follows changes that.

But if it has been useful — if it saved you an evening of mapping zones by hand, or got a library onto a machine that had no way of reading it, or best of all, if it has your vintage instrument switched on and in use more often than it was, and you are having more fun with it — then please consider supporting the work.

Because here is what it has actually cost:

  • Real machines on a real bench. Much of what these tools know about these formats was measured on hardware rather than read anywhere, because for most of it there is nowhere to read it. That needs the machines — the instruments themselves — and it puts hours of wear on hardware that has been locked up and recovered more than once in the course of it. Some of these instruments were bought specifically to add and verify a format; the others were already here, because the person doing this is a vintage instrument enthusiast first and the projects exist because the instruments were in the room.
  • Dozens — realistically hundreds — of hours of human time. Format reverse-engineering is slow: measure, be wrong, measure again. A single parameter law in one of the projects can represent an evening at the bench.
  • AI assistance, which is a paid service, used heavily throughout and not cheap at this volume.

This is support, not a donation — and the distinction is a legal one, not a turn of phrase. The maintainer is based in Germany, where payments like these are not Spenden in the tax sense: they count as taxable income for the recipient and are not tax-deductible for the giver. So this section is titled Support, deliberately, and no receipt for tax purposes can be issued. (That is a statement of how it is handled here, not tax advice.)

If the project saved you the work, you can support it through GitHub Sponsors — the Sponsor button at the top of the repository. Payment is handled entirely by GitHub and Stripe, so bank and tax details are never handed to the person paying.

Support is not expected, and it is not the only currency.

  • Bug reports — ideally with the bank, preset or disk image that produced them. A tool's failures are usually specific to one file rather than general, and without that file they are very hard to reproduce.
  • Confirmations from hardware that is not on this bench, which matters more here than for a single-machine tool. These tools write for whole families of instruments, and the bench holds only a subset. Whether a variant accepts what we write is genuinely unknown, and several notes say "on this unit" for that reason. A "loads fine here too", or a "no, mine refuses it", is worth a great deal.
  • Corrections to the reverse-engineering notes. The wrong turns are recorded next to the findings — in this repository's TODO.md, and in each project's own docs/RESOLUTION_NOTES.md — retractions included, because a finding that was withdrawn is as useful as one that stood. If any of it is wrong in a way that is still costing someone time, saying so improves the record.

AI assistance & human authorship

vinsynlib was built by its human author together with AI assistance. The ideas, the project vision, and every feature came from the human author; AI assisted with writing the code and analyzing the binary formats. Crucially, the reverse engineering rests on hands-on human work — all testing and verification on real hardware, creating the reference images/banks on those instruments (disk saves, SysEx probes), and aural A/B comparison of presets — which is what makes the results correct. Full account in DISCLAIMER.md.


Legal: DISCLAIMER.md · LICENSE

Metadata

Release files for vinsynlib 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vinsynlib 0.4.2
File Size Uploaded Provenance
vinsynlib-0.4.2.tar.gz 154.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vinsynlib 0.4.2
File Interpreter ABI Platform
vinsynlib-0.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 261.0 kB

Release files / vinsynlib-0.4.2.tar.gz

Download URL vinsynlib-0.4.2.tar.gz
Size 154.9 kB
Tags Source
SHA-256 checksum
How to use checksums
168f5a8c6d69d3a336d966942d5510ac0f26adaed9ae515c546aaa058cb9ae4e
BLAKE2b-256 checksum
How to use checksums
8cb84b61ee67c1de2da717797dd77924f101faca7d710343b0696e238d0c930f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / vinsynlib-0.4.2-py3-none-any.whl

Download URL vinsynlib-0.4.2-py3-none-any.whl
Size 106.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78ac02d826dad86bcff4c4bac807cad178159053ce7e15ecbede1a7d99870bea
BLAKE2b-256 checksum
How to use checksums
87ec7c9d10ff4c50c11f04ced8be8f100d4b7323d895f02ee50f472da2ced742
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Security for vinsynlib 0.4.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vinsynlib-0.4.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page