Skip to content

RFC: create-first editing — start from scratch, tracks panel (add/rename/reorder any instrument), spec type, stem split #39

Description

@topkoa

Summary

Tester feedback (verbatim excerpts):

"you can start an editor with an empty canvas and I don't know what instrument that is. Is that guitar or bass?"
"if you start with one instrument [...] and you later want to add a bass I don't think you can do that. There are buttons to add drums and there are buttons to add keyboards, but there are no buttons to add any other instrument"
"There are no buttons to re-order the tracks [...] There is no way to rename a track if it says lead and it really should be lead 2. Some tracks are called combo when they're really a lead or rhythm guitar"
"it really feels like the editor was built to get off the ground with an import and not so much add a track, finish it, add another track, finish it [...] go as long as you want"

This RFC proposes reworking the editor from import-first to create-first: start a song from nothing, add tracks of any instrument one at a time (blank or imported), rename/reorder/delete them, and import stems/GP material later if wanted. It also closes the feedpak-spec gaps that make this fragile today, and brings back stem splitting (remote demucs) in-editor.

Note: #37 (merged) already added "+ Guitar/Bass" GP-track import into an open session, which addresses the narrow "add a bass later" case — but only via a Guitar Pro file. Everything else below is still open.

Current state (verified at 270d564, spec 1.13.0)

New-song flow is import-first, and the blank path is dead code. The toolbar "New…" button opens the GP-import modal directly (screen.html:23editorShowCreateModal, screen.js:5542). A genuine blank-song builder editorShowCreateSloppakModal (screen.js:5588 — audio drop + metadata + initial Lead/Rhythm/Bass + optional empty drum tab) and a "what are you making?" picker editorShowNewFormatPicker (screen.js:5477) both exist but are unreachable — the picker is never wired to any button. Dead HTML block #editor-create-blank-opts at screen.html:191-202.

Creating a song requires audio and locks the first track to guitar/bass. POST /create_sloppak (routes.py:3300) 400s without an audio file or audio_url, and rejects initial_arrangement outside {Lead, Rhythm, Bass} (routes.py:3371-3375). No blank-Keys or drums-first creation.

No instrument field anywhere — instrument is inferred from the display name. Bass = /bass/i on arr.name (screen.js:209, 230, 1725; backend _arrangement_string_count, routes.py:2238), keys = KEYS_PATTERN /^(keys|piano|keyboard|synth)/i (screen.js:64). The feedpak spec (§5.2) defines an optional arrangement type hint (guitar, bass, piano, …) for exactly this — the editor never writes or reads it. Manifests also omit feedpak_version (spec SHOULD). Consequence: renaming is semantically dangerous ("Bass Lead" → 4 lanes), which is presumably part of why there's no rename UI.

No rename, no reorder. The switcher is a bare <select> (screen.html:14) that is hidden entirely when the song has one arrangement (screen.js:3637) — so a single-track song shows no track UI at all, which is the tester's "I don't know what instrument that is". Good news, verified: the persistence plumbing for both rename and reorder already exists — sloppak save sends a full arrangements snapshot and the server prefers a client-supplied stable id over re-slugging from name (routes.py:2922-2928), and the sloppak loader preserves manifest order (lib/sloppak.py:402-467; the Lead>Combo>Rhythm sort is scanner-/archive-only). What's missing is purely UI + a couple of guards.

"Combo" is Rocksmith jargon leaking from the GP converter. lib/gp2rs.py:1418-1424: 1st guitar → "Lead", 2nd → "Rhythm", 3rd+ → "Combo" (repeated at gp2rs.py:2040-2043, gp2rs_gpx.py:1446). Bonus bug: the 4th+ guitar gets a duplicate "Combo" name (no counter on that branch).

Stem splitting is gone but the pieces to restore it exist. Local demucs was removed in the "Clean release snapshot" (scripts/split_stems.py is a stub). The remote feedBack-demucs-server is alive with POST /separate (htdemucs_ft / htdemucs_6s / bs_roformer_sw, per-stem caching, WS progress), core already has the demucs_server_url setting, and lib/lyrics_transcribe.py / lib/vocal_pitch.py establish the remote-client idiom — nobody calls /separate. The editor also carries a dead 🎚 Stems button (screen.html:33editorToggleStemMixer(), which no longer exists in screen.js).

Other bugs found on the way:

  • /replace-audio (routes.py:3849) rewrites manifest["stems"] but never updates manifest["duration"].
  • routes.py:2438-2440 builds the arrangement-id list from the manifest by index, but sloppak.load_song can skip malformed entries → misalignment; loaded.arrangement_ids (lib/sloppak.py:369) exists for this and is unused.
  • Spec inconsistency: §5.2 lists type: piano, but the §7.7 example at line 1178 says keys. Proposal: write piano, accept keys as an alias when reading (separate one-line spec errata).

Proposal

Three PRs. PR-A is in the main feedback repo, B and C here. Land A → B → C; each degrades gracefully alone (getattr guards; readers ignore unknown manifest keys per spec §1.2).

PR-A — feedback repo (lib/)

  1. Arrangement.arr_type: str = "" (lib/song.py:155); arrangement_to_wire/from_wire carry type; sloppak loader passes entry["type"] through; FEEDPAK_VERSION constant in lib/sloppak.py; _tuning_for_meta prefers type == "guitar".
  2. gp2rs: replace "Combo" with a shared _extra_guitar_name(track_name, used) — sanitized GP track name (rejecting names that would misroute convert_file's instrument dispatch at gp2rs.py:2024), falling back to "Guitar N", always de-duped (also fixes the duplicate-Combo bug). Set arr_type on converted arrangements. Untouched: the archive XML sort (song.py:1621-1629, load-bearing for test_arrangement_xml_alignment.py) and _NAME_FALLBACK's combo entry — instead add a guitar-ish-name → path_lead fallback there so GP-named tracks still classify for RS-XML export.

PR-B — this repo: create-first + tracks panel + spec type

B1. type foundation (makes rename safe). Backend: _infer_arr_type(name) helper; stamp type on load and in every manifest write (_write_sloppak_pak + snapshot-save entry build); write feedpak_version; _arrangement_string_count prefers type; switch id stamping to loaded.arrangement_ids. Frontend: pure _arrTypeOf(arr) (prefers arr.type, keys→piano alias, name-regex fallback for legacy paks) replacing the ~15 scattered regex sites; import paths set type explicitly.

B2. Rename / reorder / delete. Rename pins type first (so renaming on a legacy pak can't flip lane count), mutates name only, never id; undoable (RenameArrangementCmd, modeled on ReplaceArrangementChartCmd). Reorder = splice + currentArr fixup by object identity; undoable; persists automatically via snapshot + manifest order. Delete refactors editorRemoveArrangement to take an index.

B3. Tracks panel. One "Tracks" button → modal listing every track: instrument icon, name, tuning summary, active highlight, click-to-switch, ▲▼ / ✎ / ✕ per row. Drum tab appears as a pinned row (song-level side-file; renameable, deletable — note _buildSaveBody must send explicit drum_tab: null, since JSON.stringify drops undefined). "+ Add Track" → Guitar / Bass / Keys / Drums, each Blank (new editorAddEmptyArrangement(type) mirroring editorAddEmptyKeys, unique name, correct tuning length, explicit type) or Import… (routes to the existing three modals). The three per-instrument toolbar buttons are removed; their modals stay, invoked from the panel. The arrangement <select> stays for quick switching and becomes always-visible.

B4. New Song flow. "New…" → picker (reviving the dead editorShowNewFormatPicker skeleton): Start from scratch (zero files), Start from audio (promoted blank builder), Import GP / EOF (existing modal, demoted). Start-from-scratch: the spec requires non-empty stems + duration, so the server generates a silent OGG (ffmpeg -f lavfi -i anullsrc…) from bars × BPM × time-sig inputs, seeds the full beat grid over that length, and the existing Replace Audio swaps in real audio later (fixed to update manifest["duration"]). create_sloppak drops the Lead/Rhythm/Bass enum: any name ≤64 chars + arrangement_type ∈ {guitar, bass, piano}, drums-first supported (empty drum tab + one empty guitar arrangement, since spec §5.1 requires non-empty arrangements).

B5. Instrument clarity. Always-visible arrangement indicator (icon + name + tuning) in the top bar even with one track; empty-arrangement canvas hint ("{icon} {name} — {tuning} — double-click to place a note").

PR-C — this repo: stem splitting (remote demucs)

POST /api/plugins/editor/split-stems {session_id, model?} → uploads the session's mixed stem to the demucs server's /separate (URL from the existing demucs_server_url core setting; clear 400 when unset), transcodes results to OGG, rewrites manifest["stems"] to the split stems, adds stem_separation provenance (spec §5.3.1). full.ogg stays on disk unreferenced as backup so a split can be undone or re-run with a different model; the editor keeps using it for its own playback/waveform when present. UI: "Split stems…" in the Tracks panel footer with model dropdown + progress. The dead 🎚 Stems button/panel HTML is removed — playback mixing belongs to feedBack-plugin-stems.

Compatibility

  • New editor + old lib: type inferred from names (getattr guards); editor still writes type itself.
  • Old editor + new lib: arr_type unread; server re-stamps on save.
  • Old paks: name-regex fallback; first save upgrades the manifest; rename pins type first.
  • Old readers of new paks: unknown manifest keys ignored per spec.

Risks

  • Reorder currentArr fixup must use object identity, not index math.
  • GP-track-name sanitization must not collide with convert_file's name dispatch.
  • anullsrc/lavfi availability in the bundled ffmpeg (verify early).
  • Untitled scratch songs → create_sloppak filename 409; client retry-with-suffix.
  • Split must be atomic: write stems to temp, swap manifest only when all stems landed.

Verification sketch

  1. Scratch → Drums, 32 bars @ 140: drum edit mode, canvas hint, duration ≈ 54.9 s, full grid; save/reopen.
  2. Scratch Guitar (silence) → place notes → Replace Audio with real mp3 → duration updates; save/reopen.
  3. Tracks panel: add blank Bass (4 lanes immediately), rename Lead → "Lead 2" (still 6-lane, id/file stable), reorder, delete a track and the drum tab; inspect manifest.yaml (order, type, feedpak_version).
  4. GP with 3+ guitar tracks: GP names / "Guitar 3", no "Combo", type set.
  5. Legacy pak: load, rename, save, open on the 3D highway.
  6. Split stems: manifest lists split stems only, full.ogg still on disk, provenance present, song opens in feedBack-plugin-stems mixer.
  7. node tests/*.test.js; pytest tests/; feedback repo: pytest tests/test_gp2rs.py tests/test_gp2rs_gpx.py tests/test_arrangement_xml_alignment.py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions