Skip to main content

preframr-aug

CI

Write-domain corpus augmentation for the preframr SID event model — reduction + transplant arms that attack copy-dominance, torch-free.

Why

The event model learns (teacher-forced) but cannot free-run: generation collapses because the corpus rewards copying the memorised span over applying the generative rule (copyable next-token acc 0.535 vs novel 0.194). preframr-aug breaks that reward by making structure non-literally-copyable — the same real music, recombined so the copy no longer wins. The full rationale and build plan live in IMPLEMENTATION.md.

Everything operates on ordered register writes(frame, reg, val) triples — never event atoms. The encoder re-derives lanes from the writes, so augmentation is agnostic to the atom encoding (it survives codec changes) and structural validity is free: encode(ow, verify=True) self-checks the lossless round-trip. Plausibility (that the output still sounds like the corpus) is enforced separately by the wiring exclusions and the sonic fingerprint band.

Status

Built against the current event substrate (preframr-tokens ≥ 0.51.0 / preframr-audio ≥ 0.5.9). Two trainable arms have landed; the training runner consumes the output exactly like a real dump dir — zero pipeline change.

Arm Module What it does
CORE writes.py dump ↔ OrderedWrites, per-voice partition, frame-ordered rebuild, verified re-encode
M1 · reduce reduce.py melody-only prefix → intact continuation (pure write-domain deletion; first trainable arm)
M2 · instrument transplant.py donor onset-program replayed at host onsets — host pitch/phrasing kept, timbre swapped

Supporting modules: voices.py (role + wiring exclusions), provenance.py (records + the train-split leakage guard), sonic.py (mel-fingerprint plausibility band), cli.py (batch driver).

Deferred by the plan's own gating: M3 melody transplant (build after the M1+M2 dosage A/B), M4 instrument bank + sonic clustering (gate on the onset-program-recurrence measurement first). See IMPLEMENTATION.md for the sequencing and the honest risks.

Install

pip install preframr-aug

From source:

git clone https://github.com/anarkiwi/preframr-aug
cd preframr-aug
pip install -e ".[dev]"

Requires Python ≥ 3.10. Runtime deps: numpy, pandas, pyarrow, preframr-tokens, preframr-audio (no torch).

CLI

preframr-aug <train-dump-dir> --out <aug-dir> --arm reduce --dose 0.25 --seed 0
preframr-aug <train-dump-dir> --out <aug-dir> --arm instrument --dose 0.25 --filter

Selects dose of train-split hosts, applies the arm, and writes augmented *.dump.parquet files plus provenance.jsonl (one record per tune: host, donor(s), voice, transform, anchor) and augmented.list.

Flag Default Meaning
--arm reduce reduce (M1) or instrument (M2)
--dose 0.25 fraction of corpus hosts to augment
--seed 0 RNG seed (host/donor selection)
--prefix-frames 400 reduce: frames of melody-only seed
--strip-ornament off reduce: also strip the target's own PW ornament from the prefix
--filter off gate each output on the sonic plausibility band
--band-sample 40 tunes sampled to build that band

API

The canonical pipeline — load, transform on the triples, verify, emit:

from preframr_aug import writes, reduce, transplant

ow = writes.load_ow("tune.dump.parquet")          # -> OrderedWrites (rejects multispeed)

# M1 — reduction
reduced, info = reduce.reduce_prefix(ow, prefix_frames=400)
writes.reencode(reduced)                           # verified encode; raises on round-trip failure
writes.emit_dump(reduced, "tune__reduce400.dump.parquet")

# M2 — instrument transplant (leakage-guarded end to end)
new_ow, record = transplant.transplant_tune("host.dump.parquet", "donor.dump.parquet")

writes — CORE

Everything manipulates the triples these expose and re-assembles through rebuild, so augmentation stays agnostic to the atom encoding.

  • load_ow(path) -> OrderedWrites — read a *.dump.parquet; raises on non-single-speed material (out of codec scope).
  • split_voices(ow) -> dict — partition triples into buckets keyed 0, 1, 2, "filter". Voice register map: v0 = regs 0–6, v1 = 7–13, v2 = 14–20, filter/global = 21–24.
  • rebuild(parts) -> OrderedWrites — reassemble buckets into one frame-ordered stream (the single re-assembly point).
  • reencode(ow) -> list[int] (alias atoms) — verified encode: decode(encode(ow)) == canonical_writes(ow).
  • emit_dump(ow, path) -> None — write the augmented dump.
  • voice_key(reg) -> 0 | 1 | 2 | "filter", plus VOICE_REGS, FILTER_REGS, VOICE_KEYS.

voices — role + wiring (the exclusion logic)

  • roles(ow) -> {voice: "bass" | "lead" | "perc" | "unknown"} — heuristic (pitch median, noise %, gate rhythm); a mislabel weakens an augmentation, never invalidates it.
  • wiring(ow) -> {voice: {sync, ring, filter_routed}} — read from ctrl bits 1/2 and the reg-23 routing bits.
  • transplantable(ow) -> set[int] — voices safe as donor/host: excludes any sync/ring voice and its modulation source (SID voice v is driven by voice (v-1) mod 3) — never separate a modulator from its carrier.

reduce — M1

  • reduce_prefix(ow, prefix_frames, target=None, strip_ornament=False) -> (OrderedWrites | None, info) — strip non-target voice writes from frames < K, keep the continuation intact. Returns (None, info) when no melodic target is found (the caller skips).

transplant — M2

  • instrument_transplant(host_ow, donor_ow, host_voice, donor_voice) -> (OrderedWrites | None, info) — replay the donor's onset program at each host onset; host freq + gate edges kept.
  • pick_pair(host_ow, donor_ow) -> (host_voice, donor_voice) | None — same-role pair, both transplant-safe; percussion is never transplanted.
  • transplant_tune(host_path, donor_path) -> (OrderedWrites | None, record | None) — leakage-guarded; returns (None, None) when no safe role-matched pair exists.

provenance — records + leakage guard

  • guard_train_split(*paths, allow=("train", "unknown")) -> None — raises LeakageError if any path is outside the allowed split (pass allow=("train",) to enforce strictly).
  • split_of(path) -> str, record(out_path, host, transform, voice, donors=None, anchor=None, **extra) -> dict, write_jsonl(records, path) -> None.

sonic — plausibility filter

  • fingerprint_ow(ow, frames=600, feature="mel") -> np.ndarray — mel fingerprint via the canonical render scaffold.
  • band(fingerprints, k=3.0) -> (lo, hi) — per-dimension acceptance band (mean ± k·std) over reference fingerprints.
  • in_band(fingerprint, bounds, tol=0.02) -> bool — reject if more than tol of dimensions fall outside the band.

Development

pip install -e ".[dev]"
./run_tests.sh        # black --check, pylint, pyright, pytest --cov (floor 80%)

CI runs the suite on Python 3.10 / 3.11 / 3.12. Tests cover the load-bearing partition+rebuild identity gate, per-voice splice byte-exactness, train-split leakage enforcement, the reduce and instrument arms, and a fixture-based audio render of each arm.

Releases

Tagged releases publish to PyPI via a trusted publisher (OIDC — no API token) — see .github/workflows/release.yml. To cut a release, push a vX.Y.Z tag and publish a GitHub Release; setuptools-scm derives the version from the tag.

One-time PyPI setup: register the trusted publisher for project preframr-aug — owner anarkiwi, repository preframr-aug, workflow release.yml, environment pypi.

License

Apache-2.0 — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

preframr_aug-0.1.0.tar.gz (46.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

preframr_aug-0.1.0-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

Details for the file preframr_aug-0.1.0.tar.gz.

File metadata

  • Download URL: preframr_aug-0.1.0.tar.gz
  • Upload date:
  • Size: 46.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for preframr_aug-0.1.0.tar.gz
Algorithm Hash digest
SHA256 04d976ad1d936b7373f7298e8eb1336ce91e6804bf3dcbc659cf69b232670bc6
MD5 42cdca6df6d5b0ab71cc9b3b9e695742
BLAKE2b-256 583be4337aa9e0c204fea166da386dc2cddd9fb0d65fd3039c6ccec0d3444bab

See more details on using hashes here.

Provenance

The following attestation bundles were made for preframr_aug-0.1.0.tar.gz:

Publisher: release.yml on anarkiwi/preframr-aug

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file preframr_aug-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: preframr_aug-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 24.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for preframr_aug-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d8243f758e07548ba2fc7802f19358ce4b6a05eb04403aa3579ab6aec009dd3a
MD5 5f66a5a6b7808fa3ec0882777b488bc0
BLAKE2b-256 75fe6bcdd463d0cd62fec10c49efcf1bc83bb6a562ba6070899773e2006300aa

See more details on using hashes here.

Provenance

The following attestation bundles were made for preframr_aug-0.1.0-py3-none-any.whl:

Publisher: release.yml on anarkiwi/preframr-aug

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page