Import a GP guitar/bass track into an open session (add or replace) - #37
Merged
Conversation
… or replace) Adds the missing path to bring a Guitar Pro GUITAR or BASS track into the song you're already editing — either as a new arrangement or to replace an existing one. The core conversion (lib/gp2rs convert_file) already produces guitar/bass arrangements; this is the "into an existing session" wiring plus a modal, mirroring the existing Add-Keys flow. Timing: imported notes are aligned to the session's existing audio by passing the current _effectiveAudioOffset() as the convert audio_offset. The song-level beats / sections / audio are left untouched (finer alignment stays the existing offset tool). Backend: - New POST /api/plugins/editor/import-guitar-track — mirrors import-keys but without piano forcing (single guitar/bass track). Rejects piano/drums/ percussion/vocal tracks and validates a finite audio_offset. - Hoisted _arr_to_data(arr, name) from inside import_keys_track to module scope so keys + guitar import share one builder (no copy-paste). Also gives chord notes harmonic_pinch parity with single notes. Frontend: - New "+ Guitar/Bass" toolbar button + "Import Guitar / Bass from GP" modal: upload a GP, pick a guitar/bass track (filtered), choose Add or Replace. - Add reuses the generic append helper (now labelled + modal-agnostic). - Replace goes through ReplaceArrangementChartCmd — snapshot/rollback swap of notes/chords/templates/tuning that keeps the target's name, tones and the song-level timeline in one undo step. The command deep-copies the incoming chart and flattens inside exec() so redo is idempotent, recomputes lane height (LANE_H) for 4/5/6-bass and 6/7/8-guitar swaps, and clears selection state so a stale ref can't hit an imported note. - Bass tracks are named to match /bass/i so they render on E/A/D/G (4 lanes), not shifted. Replace targets are family-filtered (same guitar/bass family, Keys/Drums excluded) so a swap can't leave a chart in the wrong render mode. - arr.anchors (note-derived auto hand positions) are cleared on replace and regenerate from the new notes on save; arr.phrases are deliberately KEPT — they're time-anchored to the unchanged sections and their levels repopulate from the new chart on save. Tests: node tests for the pure helpers (track filter, naming, swap/rollback, deep-copy/redo idempotency) + a pytest for the shared _arr_to_data dict shape. Independent Codex review run to clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds the missing “import Guitar Pro guitar/bass track into the currently-open editor session” path, including both Add as a new arrangement and Replace an existing arrangement flows, while preserving the session’s song-level timeline and aligning imported notes via the current audio offset.
Changes:
- Backend: hoists shared
_arr_to_databuilder and addsPOST /api/plugins/editor/import-guitar-trackfor importing a single GP guitar/bass track into an existing session. - Frontend: adds “+ Guitar/Bass” button + modal, plus
ReplaceArrangementChartCmd(swap/rollback) and supporting pure helpers. - Tests/docs: adds unit tests for the new pure helpers and
_arr_to_data, and updatesCHANGELOG.md.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
routes.py |
Adds import-guitar-track endpoint and hoists _arr_to_data for shared arrangement wire-shape building. |
screen.js |
Implements modal logic, track filtering/naming, and replace-chart command + chord-flatten refactor. |
screen.html |
Adds “+ Guitar/Bass” toolbar button and the import modal UI. |
tests/import_guitar_track.test.js |
Unit tests for the new pure JS helpers and replace snapshot/rollback behavior. |
tests/test_arr_to_data.py |
Unit tests for the shared _arr_to_data arrangement-dict builder. |
CHANGELOG.md |
Documents the new import guitar/bass feature and its behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+7754
to
+7757
| const data = await resp.json(); | ||
| if (data.error) { statusEl.textContent = 'Error: ' + data.error; goBtn.disabled = false; return; } | ||
| // The modal was closed / a new file picked while this was in flight. | ||
| if (reqSeq !== _importGuitarReqSeq) return; |
Comment on lines
+4835
to
+4846
| # Reject a malformed/non-finite offset rather than silently aligning to | ||
| # 0.0 (mirrors convert_gp) — a wrong offset imports mis-timed notes. | ||
| _raw_offset = data.get("audio_offset") | ||
| if _raw_offset is None: | ||
| audio_offset = 0.0 | ||
| else: | ||
| try: | ||
| audio_offset = float(_raw_offset) | ||
| except (TypeError, ValueError): | ||
| return JSONResponse({"error": "audio_offset must be a number"}, 400) | ||
| if not math.isfinite(audio_offset): | ||
| return JSONResponse({"error": "audio_offset must be finite"}, 400) |
Comment on lines
+26
to
+33
| def _chord_note(**kw): | ||
| # Chord notes carry the same tech fields minus `harmonic_pinch`. | ||
| base = dict( | ||
| time=2.0, string=1, fret=7, sustain=0.0, | ||
| bend=None, slide_to=-1, slide_unpitch_to=-1, hammer_on=False, | ||
| pull_off=False, harmonic=False, harmonic_pinch=False, palm_mute=False, | ||
| mute=False, tremolo=False, accent=False, tap=False, link_next=False, | ||
| ) |
Comment on lines
+33
to
+34
| song-level timeline. New backend endpoint `POST /api/plugins/editor/ | ||
| import-guitar-track` (reuses the shared `_arr_to_data` arrangement builder); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #36
What
Adds the missing path to bring a Guitar Pro guitar or bass track into the song you're already editing — either as a new arrangement or to replace an existing one. The core conversion (
lib/gp2rsconvert_file) already produces guitar/bass arrangements; this is the "into an existing session" wiring plus a modal, mirroring the existing Add Keys flow.Design (as specified)
_effectiveAudioOffset()(passed as the convertaudio_offset). The song-level beats / sections / audio are untouched; there's no re-sync from the GP (finer alignment stays the existing offset tool).Changes
Backend (
routes.py)POST /api/plugins/editor/import-guitar-track— mirrorsimport-keysbut without the piano forcing (single guitar/bass track). Rejects piano/drums/percussion/vocal tracks and validates a finiteaudio_offset._arr_to_data(arr, name)from insideimport_keys_trackto module scope so keys + guitar import share one builder (no copy-paste). Also gives chord notesharmonic_pinchparity with single notes.Frontend (
screen.js,screen.html)ReplaceArrangementChartCmd: a snapshot/rollback swap of notes/chords/templates/tuning that keeps the target's name, tones and the song-level timeline, in one undo step./bass/iso it renders on E/A/D/G (4 lanes), not shifted.Correctness notes (from the review loop)
exec(), so redo is idempotent (no double-flatten / duplicated chord notes)._resizeForLaneChange) for 4/5/6-bass and 6/7/8-guitar swaps, and clears selection state so a stale ref can't hit an imported note.arr.anchors(note-derived auto hand positions) are cleared on replace and regenerate from the new notes on save;arr.phrasesare kept — they're time-anchored to the unchanged sections and their levels repopulate from the new chart on save.Testing (local)
python -m pytest→ 174 passed (incl. newtests/test_arr_to_data.py).node tests/*.test.jsgreen (incl. newtests/import_guitar_track.test.js, 19 assertions: track filter, naming/bass/dedupe/misroute, swap/rollback exactness, deep-copy reference-independence, redo idempotency, phrases-kept).node --check screen.jsclean.Not yet exercised end-to-end in a running app with a real GP file — worth a manual pass (add a guitar track; replace a Bass; confirm bass lands on E/A/D/G and replace is one undo).
🤖 Generated with Claude Code