Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

89 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

feedpakr

A feedBack plugin that imports Guitar Pro files into .feedpak at maximum fidelity, and batch-upgrades existing .sloppak libraries to .feedpak.

Plugin id: feedpakr. Install into plugins/feedpakr/ (folder name must match the id).

Status: Phase 4 (feature-complete for v1)

  • Upload a .gp3-.gp5 (pyguitarpro) or .gp6/.gpx/.gp (GPIF, GP6-GP8) file, pick which tracks to include and name their arrangements, optionally combine compatible same-name layered parts into one arrangement, and attach cover art.
  • Audio: synthesize with FluidSynth (.gp3-.gp5 only), use a GP8 file's embedded backing track, or sync a user-uploaded/YouTube-fetched recording to the chart via chroma-DTW autosync — or skip audio entirely.
  • Every enrichment step is best-effort: a failure degrades that one feature (recorded as a warning) rather than aborting the import. Output is self-validated against the vendored feedpak-spec JSON Schemas (assets/schemas/) before being written. The build result reports which features actually landed (features: {...}), shown in the UI as a "Captured: …" summary rather than promising more than the source actually had.
  • A pack built without audio is a valid authoring intermediate (feedpak spec §5.3.2) but will not pass strict validation (stems is schema-required) until audio is added.

What gets captured, and from where

Fidelity gap (from the original data-loss audit) Status Source
Capo Fixed, both source families GP3-5 Track.offset; GPIF CapoFret property — gp2rs/gp2rs_gpx hardcode/ignore this
Sections, beats Fixed song_timeline.json, via song.load_song() on the converted (playback-schedule-accurate) XML
Hand shapes Derived, both families gp2rs never populates these ("empty for now") — feedpakr groups consecutive same-chord hits into contiguous spans
Chord names Optional Chordr enhancement When selected during import, Chordr's shared chart-analysis bridge names unnamed templates from played voicings on fretted arrangements and carries names across partial strums; piano/Keys arrangements are skipped because their wire encoding uses MIDI pitches; existing names are preserved
Tone changes Fixed where the source has them GPIF: read back from XML gp2rs_gpx already injects but song.parse_arrangement silently drops; GP3-5: scanned from every selected track's mixTableChange, not just the first (unlike the legacy importer this project replaces)
Key signature New capability keys.json, GP3-5 MeasureHeader.keySignature / GPIF MasterBar/Key, spelled via the standard circle-of-fifths table
Lyrics Fixed (GPIF), approximated (GP3-5, labeled as such) GPIF vocal track's own <vocals> XML (exact timing); GP3-5's single per-measure song.lyrics blob has no per-syllable timing to work with
Vocal pitch New capability (GPIF only) Read directly from the same <vocals> XML's note= attribute
Drums Promoted to real drum arrangements (GP3-5 only) gp2rs.convert_drum_track_to_drumtab → type: drums + drum_tab.json, instead of the legacy string*24+fret fretted encoding. GPIF drums have no host-side equivalent yet, so they stay fret-encoded
Standard notation New capability (GPIF melodic tracks) Per-track opt-in notation_<id>.json via gp2notation.convert_track_to_notation; enabled by default for keys, single-hand only
Repeats/D.S./D.C./Coda Fixed (GP3-5), known limitation (GPIF) gp2rs already expands these; gp2rs_gpx doesn't yet (host TODO) — feedpakr detects repeat/volta markup up front and warns rather than silently shipping drifted timing
Rigs/gear chains, harmony Out of scope GP carries no rig/signal-chain data; harmony (chord intent vs. what's played) needs separate work

Upgrade Library tab

Batch-converts existing .sloppak files to .feedpak without touching the originals. Verified against every sample sloppak this project has (21 real packs spanning a GP-import pipeline, CDLC-derived packs, and minimal tutorial packs): 21/21 upgrade to a fully spec-valid .feedpak. Per file, the upgrade:

  • Promotes arrangement-embedded beats/sections (the shape most existing packs actually use) into a proper song_timeline.json side file — accepting both the time and start_time section-key variants seen in the wild, and never overwriting a song_timeline.json that's already genuinely there.
  • Ensures stems[] has an id: full entry: promotes the deprecated original_audio key when present, or renames a pack's only stem to full when it isn't separated (a single unseparated mix is the complete mix, whatever id an older tool gave it). A pack with multiple separated stems and no full mix is left alone and reported honestly — nothing is fabricated.
  • Normalizes non-spec lyrics_source values this ecosystem's own earlier tools wrote: xml and sng both meant "authored" (chart/CDLC-sourced, not machine-transcribed); an engine name written directly (e.g. whisperx) is corrected to transcribed when a lyric_transcription block already names that engine properly. An unrecognized value with nothing to infer from is left alone and reported, never guessed at.
  • Preserves every other file and every other manifest key byte-for-byte (spec §9) — an upgrade pass only adds data, never removes or reinterprets what's already there.

Handoffs to song-preview / stem-splitter / difficulty-ladder

After a successful import or upgrade, the result panel offers Generate Preview, Split Stems, and Generate Difficulty Ladder buttons when those plugins are installed (probed via their own REST endpoints — feedpakr never generates a preview, splits stems, or authors difficulty ladders itself, it only calls the dedicated plugins that own those features). Buttons for a plugin that isn't installed simply don't appear. Split Stems is only offered for embedded or synced recordings, not synthesized or audio-free builds. Generate Difficulty Ladder is only offered when the new pack does not already report phrase-level difficulty data.

Why

feedBack's existing GP import pipeline (documented in a data-loss audit predating this plugin) silently drops sections, beats, tones, capo, key/time signatures, and more. The .feedpak format (spec ≥1.19.0) can hold all of it; feedpakr's job is to actually put it there, without needing any changes to the feedBack core.

Architecture

  • routes.py — thin FastAPI routes (upload / upload-cover / upload-audio / youtube-audio / autosync-preview / WS build / validate / sloppaks / WS upgrade), following the upload-token / streaming-build pattern used by feedBack-plugin-musicxml-import.
  • feedpakr_pipeline.py — orchestrates parsing, conversion (via the host's gp2rs / gp2rs_gpx / song), and all fidelity enrichment (capo, timeline, lyrics, keys, tones, handshapes, drums-as-arrangements, notation, vocal pitch, and optional Chordr-based chord naming).
  • feedpakr_audio.py — MIDI synthesis, GP8 embedded audio extraction, autosync, YouTube fetch, and OGG normalization. Every function degrades to (None, ..., error) rather than raising.
  • feedpakr_lyrics.py — lyrics + vocal pitch, both read from the same GPIF <vocals> XML where available; GP3-5 approximation otherwise.
  • feedpakr_tones.py — tone-change extraction for both source families.
  • feedpakr_keys.py — key/scale extraction (keys.json) for both source families.
  • feedpakr_handshapes.py — hand-shape derivation from chord data.
  • feedpakr_notation.py — opt-in GPIF melodic-track notation sidecars.
  • feedpakr_upgrade.py — the .sloppak → .feedpak batch upgrade path (see above). Reads via the host's sloppak.load_manifest/read_member_bytes when available, with a small standalone YAML/zip fallback so its tests don't require the host lib either.
  • feedpakr_pack.py — manifest assembly and .feedpak zip writing. Pure dicts, no pyguitarpro dependency — easy to unit test.
  • feedpakr_validate.py — validates every payload shape (manifest, arrangement, song_timeline, keys, vocal_pitch, drum_tab, notation) against the vendored feedpak-spec schemas, always returning a report rather than raising. The validation route accepts a DLC-relative .feedpak path for checking existing packs without rebuilding them.

Handoffs to song-preview/stem-splitter/difficulty_ladder are invoked by screen.js (fprProbeHandoffs / fprRunHandoff). Optional Chordr naming runs during the build through Chordr's versioned chart-analysis bridge; it fills only missing names and reports a warning if the capability is unavailable or fails. The backend exposes GET /api/plugins/feedpakr/handoffs as a read-only availability probe for server sibling routes; feedpakr still leaves the actual preview generation, stem splitting, and difficulty-ladder generation work to the dedicated plugins.

routes.py loads its siblings via context['load_sibling'] (not bare imports) to avoid the plugin-module-name collisions that mechanism exists to prevent.

Host compatibility

feedpakr calls the host's own core helpers (gp2rs/song/sloppak) rather than vendoring them, so which host can run which feature is declared and checked rather than assumed:

  • feedpakr_compat.py records the tested host baseline by exact commit (d876ded00f9254b13bf4a0587c5645a1edaccf67). The host's VERSION string is not a unique identifier — the same 0.3.0-alpha.1 appears on checkouts both with and without sloppak.read_member_bytes() — so the pin stays at commit granularity until a published release uniquely identifies the baseline.
  • The compatibility matrix separates concerns: each of the five features (GP import, existing-pack reuse, upgrade, duplicate scan, output playback) lists the core modules/helpers it needs, independent of the external binaries (FFmpeg/ffprobe, FluidSynth, yt-dlp) and of the optional sibling plugins (song-preview, stem-splitter, lyrics-sync, difficulty-ladder, Chordr). A missing binary or sibling is not a core-version problem and isn't reported as one.
  • GET /api/plugins/feedpakr/compat (backend) + a banner (frontend) report each feature as available, degraded (a compatible fallback covers the missing helper — e.g. upgrade/duplicate-scan/lyrics-text on a host without read_member_bytes()), or unavailable, with an actionable message.
  • feedpakr_upgrade checks for read_member_bytes()/load_manifest() explicitly and falls back to its own bounded reader rather than assuming an importable sloppak is complete.

A note on the GPIF (.gp6/.gpx/.gp) path

gp2rs.list_tracks / gp2rs.convert_file already dispatch to gp2rs_gpx internally based on file extension, so most of the pipeline is written against gp2rs's uniform surface. A few things needed format-specific handling because pyguitarpro has no model for them on the GPIF side at all:

  • Capo lives at Track/Staves/Staff/Properties/Property[@name='CapoFret']/Fret in the raw GPIF XML — nothing in the host reads it, so feedpakr does, via gp2rs_gpx._load_gpif + _gpif_tracks (private but stable — the module's only way to reach a raw <Track> element).
  • Vocal tracks produce a <vocals> XML (via gp2rs_gpx.convert_vocal_track internally), not the <song> shape song.parse_arrangement expects. feedpakr peeks at each output XML's root tag and routes <vocals> files to feedpakr_lyrics.py instead — routing them through song.parse_arrangement by mistake was a real bug caught while building this (an empty-named ghost arrangement, no lyrics produced at all; now a regression test in tests/test_pipeline.py). The same <vocals> XML also feeds vocal_pitch.json directly (its note= attribute is already the sung MIDI pitch) — no need for gp2rs_gpx's separate, much more involved private pitch-sidecar function.
  • Piano LH/RH pairing (gp2rs_gpx._find_piano_pairs) can silently drop a track index from convert_file's output when it's merged into its RH partner — feedpakr independently computes the same filtered order up front (_output_track_order) so the capo/lyrics/notation routing can still zip() output XMLs to the right source track.
  • <tones> is written but never read: gp2rs_gpx.convert_file already injects a <tonebase>/<tones> block into GPIF-converted XML (via its own internal _collect_tone_events/_inject_tones) — but song.parse_arrangement has no code path that reads those elements back out, so the data was being silently discarded on the way to the wire format. feedpakr_tones.parse_tones_xml closes that gap.

Tests

GP-to-feedpak fidelity report

Build a real Guitar Pro file through the production pipeline and report whether metadata and playable features survive in the resulting feedpak:

py compare_gp_to_feedpak.py "path/to/song.gp" --json

Use --audio-mode embedded for GP8 embedded audio or --audio-mode midi for a synthesized reference stem. The command exits nonzero when an authored field is missing or only partially preserved; warnings remain visible separately.

python -m pip install -r requirements.txt -r requirements-test.txt \
  -r ../pakr/feedBack/requirements.txt
python -m pytest -q

The third -r is the feedBack host's own requirements, and it is not optional: PyYAML is imported at module scope by feedpakr_pack, which tests/conftest.py imports unconditionally, so a missing PyYAML fails collection whether or not the host checkout is present. That -r needs a sibling pakr/feedBack checkout to exist — pip exits nonzero on a missing requirements file — so a plugin-only checkout installs PyYAML directly instead and still runs everything that doesn't need the host.

python -m pytest -ra is the default (pytest.ini sets addopts), so the short test summary always lists every skip with its reason. The full skip inventory and the policy for what may skip in CI is in tests/SKIPS.md.

Portable conversion regressions

The release-gating import tests run against fixtures generated in-repo by tests/portable_fixtures.py — a hand-authored GPIF (GP7/8) .gp and a real GP4 .gp4 written with pyguitarpro. They need no song data, so they run on every CI run and cover sections, chord/template consistency, drums, and the GPIF-vs-GP3-5 format/tuning differences (tests/test_portable_conversion.py).

Optional local fixtures

Tests that lock in behavior against real, copyrighted songs/sloppaks read them from an optional personal sample library, never from the repo. Point FEEDPAKR_LOCAL_FIXTURES at it; with no env var the historical c:\Users\PC\Downloads\Alll path is used if it exists. When it is absent the dependent tests self-skip with a reason naming the env var, while the portable suite (and everything else) still runs. A set-but-missing path warns and skips rather than silently falling back to the historical folder.

tests/test_pipeline.py needs the host's lib/ (for guitarpro, gp2rs, gp2rs_gpx, song) on sys.path — tests/conftest.py looks for a sibling pakr/feedBack checkout and self-skips those tests if it isn't found.

Required capabilities in CI

.github/workflows/tests.yml checks out the host and installs ffmpeg, then sets FEEDPAKR_REQUIRE=host,ffmpeg. tests/conftest.py::pytest_configure aborts the session if any required capability is missing, and pytest_sessionfinish fails the run if a required regression still skipped for an unexpected reason — so a broken provisioning step fails loudly instead of producing a green run full of skips. Locally (FEEDPAKR_REQUIRE unset) the same tests skip cleanly when the host lib or ffmpeg isn't installed.

Two mixdown tests run a real ffmpeg amix (test_extract_pack_assets_separated_stems_builds_mixdown_with_valid_audio, ..._corrupt_audio_errors); the same-name-shape bookkeeping test (test_extract_pack_assets_separated_stems_no_full_mix) mocks the mixdown boundary and so runs everywhere.

Notable regression tests:

  • test_build_feedpak_extracts_all_16_sections — "Money (J).gp5" was documented as losing all 16 of its section markers through the legacy sloppak pipeline; must extract all 16.
  • test_build_feedpak_gpif_vocal_track_becomes_lyrics_not_arrangement — locks in the <vocals>-routing fix described above, against the GP8 companion file.
  • test_gpif_capo_lookup_reads_capo_fret — capo extraction against a real capo'd fixture, not just an empty-dict check.
  • test_build_feedpak_gp345_drums_become_type_drums_arrangement, test_build_feedpak_handshapes_derived_from_chords, test_build_feedpak_gpif_keys_track_gets_notation, test_build_feedpak_gpif_vocal_track_produces_vocal_pitch — Phase 3 fidelity features, each checked against real converted output, not just unit-level logic.
  • tests/test_portable_conversion.py — the portable, always-on counterparts of the real-tab section/chord checks plus GP4 drums-as-drum-tab.
  • tests/test_upgrade.py::test_real_fixture_batch_all_fully_valid — every sample .sloppak this project has upgrades to a fully spec-valid .feedpak (21/21).

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages