Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

vstation-tool

A command-line tool and Python library for Novation V-Station patch and bank files. It reads and writes .fxb, .fxp, .fst, .aupreset and SysEx .mid, converts freely between them, and builds new banks out of individual presets.

Why it exists

V-Station's own file handling is awkward in a few specific ways, and this tool works around all of them:

  • The plugin has no way to export its presets one-by-one, so sharing a single patch means shipping a whole bank.
  • Its Audio Unit and VST formats don't interchange, so a bank collected on one platform is unusable on the other.
  • A bank dumped over MIDI comes back without any names attached.

The one thing to understand first

Every V-Station container holds the entire 400-program bank, plus a pointer to whichever program is "current". A .fxp or .fst is not a single patch — it is the full bank with the pointer aimed at one slot. The same is true of an .aupreset.

Three consequences that explain most of this tool's behaviour:

  • Extracting a preset means slicing one program out of a bank.
  • Building a bank means starting from an existing bank and overwriting slots.
  • Anything that creates a bank needs a --template to supply the slots you aren't setting. Without one you'd get 400 empty programs.

A good template is a Default.fxb exported from a fresh instance of the plugin. Most examples below assume you have one.

Install

python3 -m venv .venv
source .venv/bin/activate
pip install -e .

That puts a vstation-tool command on your path.

Commands

Run vstation-tool <command> --help for the full set of options.

info — what am I looking at?

Start here with anything unfamiliar. It reports the container type, the program the file points at, and for a .mid which SysEx flavour it is.

vstation-tool info bank.fxb
vstation-tool info "V-Station - Pad2.fxp"
vstation-tool info dump.mid
file:         bank.fxb
container:    fxb
num programs: 400
pointer:      0 ('Bass1')

list — show the names

Names come from the bank's own name table, so they're the real names the plugin displays.

vstation-tool list bank.fxb
vstation-tool list bank.fxb --filter lead
* 0    Bass1
  1    Hard Lead1
  2    Pad2

extract — pull presets out of a bank

With no -i, you get whichever program the file itself points at — the natural thing to want from a .fxp or .fst:

vstation-tool extract "V-Station - Pad2.fxp" -o Pad2.fxp

Or name the program you want, by index or by name:

vstation-tool extract bank.fxb -i 42     -o preset.fxp
vstation-tool extract bank.fxb -i "Pad2" -o Pad2.aupreset

Export a whole bank as one file per preset with --all. Here --out is a directory and --format is required:

vstation-tool extract bank.fxb -o "au/My Bank" --all --format aupreset

Third-party banks usually only customise 100–160 of the 400 slots and leave the rest as stock factory presets. --exclude-duplicates-of skips those, so you export what the bank actually adds:

vstation-tool extract bank.fxb -o "au/My Bank" --all --format aupreset \
    --exclude-duplicates-of Default.fxb

Matching is by parameter content at any index, not by slot position, so it still works when a bank has shuffled the factory presets around.

convert — change format

vstation-tool convert bank.fxb bank.aupreset
vstation-tool convert bank.fxb bank.fxp
vstation-tool convert bank.fxb bank.fst --out-template Default.fst

Writing .fst needs --out-template, an existing .fst to splice the data into — the FL Studio wrapper around it isn't reproduced from scratch.

Older V-Station versions use a different chunk layout. --legacy writes that instead, and is only valid for .fxb output:

vstation-tool convert bank.fxb old-plugin.fxb --legacy

pack — a folder of presets into a bank

Takes any mix of .fxp, .fst, .mid and .aupreset, and splits into Leads_1.fxb, Leads_2.fxb, … past 400 presets:

vstation-tool pack ./leads -o ./leads/Leads.fxb --template Default.fxb

Names come from filenames by default. That's deliberate: a preset saved on its own usually carries a generic internal name (User 341, Import 128) that nobody ever changed, so the filename is the only real name it has. Use --name-source internal for collections you know carry proper embedded names.

build — a bank from a template plus specific patches

Places each patch at a slot you choose:

vstation-tool build MyBank.fxb --template Default.fxb \
    --patch 0=Bass1.fxp --patch 1=Lead3.fxp --patch 12=Pad2.aupreset

Without an explicit INDEX=, a .fxp or .aupreset goes to the slot it already points at. A .mid always needs an explicit index.

--dir merges a whole directory of .fxp files, each at its own embedded index:

vstation-tool build MyBank.fxb --template Default.fxb --dir ./presets

export-bulk-midi — a bank as one SysEx file

Writes all 400 programs, names included, as a single .mid. It round-trips through this tool losslessly:

vstation-tool export-bulk-midi bank.fxb bank-backup.mid

MIDI SysEx dumps

Three flavours are read, and info tells you which one you have:

Flavour Contents
single-patch dump one program with its name
bulk bank dump all 400 programs, parameters only — no names
this tool's bulk format all 400 programs with names

A bulk bank dump carries no name table at all, so merge it into a template and supply the names yourself:

from vstation_tool import io_ops, blob, midi

base = io_ops.load_blob("Default.fxb")
bank = midi.read_native_bulk_dump("dump.mid", base)
for i, name in enumerate(names_from_your_patchlist):
    bank = blob.set_name(bank, i, name)
io_ops.save_blob("recovered.fxb", bank)

Such dumps usually ship alongside a printed patchlist. Two things worth checking before you trust one: the numbering often starts at 100 or 300 while the patches themselves occupy slots 0..N, and names longer than 20 characters get truncated.

Python API

The CLI is a thin layer over the library, and the library is easy to script against:

from vstation_tool import io_ops, blob

bank = io_ops.load_blob("bank.fxb")

print(blob.get_pointer(bank))          # currently selected program
print(blob.get_name(bank, 0))          # 'Bass1'
print(blob.all_names(bank))            # {0: 'Bass1', 1: 'Hard Lead1', ...}

bank = blob.set_name(bank, 0, "My Bass")
bank = blob.set_slot(bank, 5, blob.get_slot(bank, 0))   # copy a program
io_ops.save_blob("edited.fxb", bank)

io_ops.load_blob and io_ops.save_blob dispatch on the file extension, so the same two calls cover every supported format.

Things that will bite you

  • Names are capped at 20 characters. Longer ones are silently truncated.
  • Anything producing a bank needs --template. Skipping it gives you 400 empty programs rather than a bank with a few presets in it.
  • .fst output needs --out-template. The tagless .fst variant can be read but not written.
  • --legacy can't represent program indices above 255, because that format stores the index in a single byte.
  • .aupreset files are exactly 59610 data bytes with a leading 0x03. Anything else won't load, which makes it a quick sanity check on output.
  • The plugin checks state length exactly and silently loads nothing on a mismatch. If a converted file appears to load but does nothing at all, check the size first.

Checking your work

After any bulk operation, these are cheap and catch most mistakes:

from vstation_tool import io_ops, blob

bank = io_ops.load_blob("out.fxb")
print(blob.get_pointer(bank), blob.get_name(bank, 0))
print(max(len(n) for n in blob.all_names(bank).values()))   # must be <= 20

Round-tripping an untouched bank through the same path should return it byte-identical. One caveat: a Default.fxb straight from the plugin has an incorrect byteSize field, so a round-trip of that particular file differs by one byte at offset 7 — that's the tool writing the correct value.

Extracting a bank to loose presets and packing them back should also reproduce the original bank.

Requirements

Python 3.9 or newer. No third-party dependencies.

About

novation v-station patch/bank converter

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages