initial-development #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "initial-development"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Replaces the original Phase 2's flat Note/Score model with a domain- modeled data layer that respects how musicians actually think: per-measure key/time/tempo with forward inheritance, engraved duration symbols distinct from played MIDI events, key-aware accidental resolution. Data Model: - New namespaces GuitarPrometheus::MusicModels (theory primitives) and GuitarPrometheus::SongStructure (composed pieces) - enum class Accidental + enum Mode/PitchClass/NoteValue with NUM_* sentinels for sequential validation - KeySignature with two constructors: (fifths, mode) for parser and (tonic, accidental, mode) for human-friendly construction; static lookup tables resolve accidentals (15 entries, fifths-keyed) and tonic (15 entries, fifths+mode-keyed) - Engraving class replaces Note: pitch + accidental + octave + Duration (NoteValue + dots + tuplet) + position metadata - Measure carries denormalized effective TimeSig/Key/Tempo so downstream consumers never walk back for inheritance - Part identity = juce::Uuid + scorePartId string; Score immutable - Old Note.h, Score.h, Part.h, Measure.h deleted Parser: - MusicXmlParser rewritten in GuitarPrometheus::Parser namespace - Propagates effective time/key/tempo forward across measure changes - Parses NoteValue from <type>, dots from <dot/>, tuplets from <time-modification>, accidentals from <alter>, drums from <unpitched> - Tick tracking with chord/grace handling and <backup>/<forward> cursor adjustments - Result type now exposes warnings list separately from errors Tests: - 20 tests across 5 files, all green - KeySignatureTests: fifths-based + tonic-based construction, out-of-range rejection, round-trip tonic preservation - EngravingTests: duration computation (whole through 64th, dots, tuplets), MIDI note number with key-aware accidental fallback - MeasureInheritanceTests: hand-crafted XML fixture exercises mid-song time/key/tempo changes - ParserStructuralTests: rewritten against new types, validates parts (5), score attributes, per-part note counts (2342/368/350/ 1142/1729), engraving detail, tick tracking, drum populations, effective-value denormalization - tests/fixtures/ now holds lesson-riff-v14.{xml,mid} regression corpus + inheritance-test.xml Build: - Source layout split into src/public/ (headers) and src/private/ (implementations) following Unreal-style API/impl separation - CMake target_include_directories points at src/public; old include paths in source files unchanged - guitar_prometheus_core static library now links juce::juce_core for juce::Uuid (only JUCE dep in the core) - .clang-format added with 4-space indent, 150-col limit, IndentAccessModifiers, sorted/regrouped includes, BSD braces - format-check + format CMake targets; format-check wired as a build dependency of GuitarPrometheus and GuitarPrometheusTests so misformatted code blocks the build - find_program prefers Homebrew clang-format (22+) over Apple CLT (17) so the modern config keys (AlignFunctionDeclarations etc.) are recognized Documentation: - PROJECT_CONTEXT updated with Music-theory-aware data model decision, full Coding Conventions section (style principles, mixed enum style, class member access, parameter passing rules, source layout, formatting enforcement), refreshed Domain Model Reference - Implementation plan: Phase 2 marked Complete (rebuilt) with Decisions Made entries; Phase 5 scope expanded to include drum mapping profile + per-track octave offset; new Phase 9 (Future Implementations) with score merging + visual score renderer entries (custom JUCE renderer preferred, Verovio LGPL-3.0 fallback) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>Drum-Part Model: - Engraving gains optional instrumentRef + tieStart / tieStop flags. Part gains scoreInstruments (id->name) + instrumentMidiPitch (id->GM percussion pitch from <midi-instrument><midi-unpitched>) + isDrumPart() helper. Parser collects every <score-instrument> and every <midi-instrument> per part. Per-note <instrument id> ref + <tied> elements threaded into PerNoteMarkings and onto Engraving. Score Tempo Map: - Replace Score's initialTempoBpm scalar with a sparse std::map<int, int> tempoByMeasure plus cached scalar fields initialTempoBpm_ and currentScoreTempoBpm_ (first / last entries). Parser writes a map entry per <sound tempo> site, keeping the cache in sync. Reads are O(1) without map iteration. Parser PPQN + Cursor + Transpose: - Output PPQN normalized to 480 regardless of source <divisions> (per-measure scale factor applied at each duration computation). MIDI files now play correctly in any DAW. - cursorTick is reset to a running measureStartTick at the start of every measure and advanced by the measure's musical length at the end. Empty / attributes-only / leading- rest measures still consume their full bar of time on the global timeline; parts with leading silence (e.g. P2 silent for 18 measures) start at the correct absolute tick. - <transpose><octave-change> applied to each Engraving's octave so getMidiNoteNumber yields the *sounding* pitch. Guitar octave-change=-1, bass typically -2. MIDI Event Pipeline: - MidiEvent + ExportConfig + HumanizerConfig headers in src/public/midi/. MidiEvent carries sourceNoteId so downstream stages distinguish music from control data. - KeyswitchInjector (5-pass walk): bank/program setup, per- note NoteOn/NoteOff with velocity + length mod, sustained run-merge, per-note fire-and-forget, span keyswitch, articulation keyswitch. - flattenPartNotes filters: drop rests, grace notes, staff>1 (GP's tab-staff duplicate), HammerOn span endTick (left- hand-articulated target), and merges tied-note pairs into one NoteOn covering the combined duration. - Drum pitch resolution: Profile.drumMap override → Part.instrumentMidiPitch (from <midi-unpitched>, 1-128 → MIDI 0-127) → Engraving display pitch. - OctaveTransposer: in-place per-part octave offset, skips drum parts and keyswitch events, clamps to [0, 127]. - MidiWriter: SMF Format 0 per-track + Format 1 merged via juce::MidiFile. Walks Score::getTempoByMeasure to emit one tempoMetaEvent per entry at the matching measure-start tick (computed via measureStartTicks helper). Regression Test: - PPQN match (exact), track count match (exact), per-track ProgramChange multiset (exact), per-track structural Note-pair compare (10% size + symmetric-difference tolerance, ±1 tick duration window), drum-part channel = 10 assertion, distinct channels per pitched part assertion, always-on per-track stderr diagnostic. - MusicXML <midi-program> 1-based → MIDI 0-based fix at the injector's ProgramChange emission. Build + Tests: - juce::juce_audio_basics linked into guitar_prometheus_core. - 30+ new tests across injector (palm-mute, let-ring run, slide span, tap, accent, staccato, drum unmapped, drum warn-once, missing-keyswitch, staff-skip, grace-skip, tied-pair, tied-chain, hammer-on suppress), transposer (5), writer (3), regression (5 sections), parser drum (95 score-instruments, instrumentRef per note), tempo map (3), PPQN (3). 108/108 tests pass. New Files: - src/public/midi/{MidiEvent,ExportConfig,HumanizerConfig, KeyswitchInjector,OctaveTransposer,MidiWriter}.h - src/private/midi/{KeyswitchInjector,OctaveTransposer, MidiWriter}.cpp - tests/{KeyswitchInjectorTests,OctaveTransposerTests, MidiWriterTests,RegressionTest,ScoreTempoMapTests, PpqnNormalizationTests}.cpp - documentation/agentic-context/implementation-plans/ 2026-04-24-phases-5-6-midi-pipeline.md - documentation/agentic-context/implementation-plans/ 2026-04-24-phases-5-6-followup.md - documentation/agentic-context/implementation-plans/ 2026-04-24-regression-hardening.mdCloses the obvious "next-step" gaps Phase 7 deferred: dirty-state safety, recent projects, drag-and-drop opens, replace-XML, profile management UI, the long-promised PianoKeyPicker integration into the profile editor, and the per-technique keyswitch velocity Odin III's articulation matrix needs. All additive on top of Phase 7's JUCE shell, all individually shippable. App preferences (F8.1): - AppPreferences wraps juce::PropertiesFile + RecentlyOpenedFilesList; Open Recent submenu re-built from controller_.recentProjects() each time it shows. Greyed out for missing files; Clear Recents at the bottom Dirty-state + autosave (F8.2): - closeButtonPressed shows Save / Don't Save / Cancel when dirty - AppController inherits juce::Timer with kAutosaveSeconds = 30 — only fires when currentPath_ + dirty_; never silently Save-As Drag-and-drop opens (F8.3): - New DropClassifier helper: classifyDroppedFile -> OpenProject / NewProject / Reject. .gpproj -> open; .musicxml / .xml -> new project flow with the drag's basename as the default project name - MainWindow inherits juce::FileDragAndDropTarget; classification is pure + tested Replace MusicXML (F8.4): - AppController::replaceMusicXml(newSourcePath) re-parses, builds a fresh gzip + Project, and rewires bindings via new RebuildBindings helper (matching scorePartIds carry profile + name; new parts get default; missing parts produce a warning) - Tempo overrides drop entries past the new score's measure count; per-part octave / humanizer drop entries for vanished parts. Every drop surfaces in the post-replace AlertWindow Keyswitch picker integration (F8.5): - New KeyswitchRow private class in ProfileEditorPanel: button labelled via formatKeyswitchLabel ("C2 (36)" or "(none)"); click opens an async DialogWindow hosting PianoKeyPicker; right-click clears the keyswitch - formatKeyswitchLabel is a free function in src/public/profile/ with its own KeyswitchLabelTests - Applied on the Techniques and Articulations tabs only; Velocities + Articulation Mods + Drum Map keep their existing widgets Profile management UI (F8.6): - ProfileStore::remove deletes a profile's on-disk file (same fileByUuid_ map already used during save) - isBundledDefault free function in DefaultProfiles greys out Delete on bundled rows - Editor gains New / Import... / Export... / Delete buttons with AlertWindow confirmations and Replace/Copy collision resolver (placeholder — F9.2 lifts to interactive) Per-technique keyswitch velocity (F8.7): - Profile gains optional keyswitchVelocityForTechnique map (additive in schema v1; old profiles parse cleanly with empty map) - KeyswitchInjector adds keyswitchVelocityFor(profile, technique) helper; 4 of 5 emit sites use it (sustained, per-note, span, and the new override). Articulation site stays at 100 — F9.4 covers it - Editor "KS Velocity" tab next to Techniques. 0 means "use default"; positive value lands in the optional Profile field MIDI artifact fixes (F8.0 follow-ups, squashed in): - Re-articulation priority reorder: Music NoteOff sorts before Music NoteOn at the same tick. Fixes JUCE's MidiMessageSequence emitting stuck-note OFFs at re-articulation (530 -> 0 unmatched offs on lesson-riff) - Identical-pitch chord member dedup in emitNoteEvents collapses two NoteOns at the same (tick, pitch) into one ON/OFF pair - ReArticulationTests covers both fixes Misc: - File menu gained "New Profile..." that mints "Untitled Profile" via AppController::createNewProfile and opens the editor pre-selected - TrackListPanel Instrument column falls back to "Drum Kit" or GM patch name when MusicXmlParser doesn't promote a single score-instrument to the part-wide instrumentName - Menu / button ellipsis mojibake -> ASCII three-dots throughout Tests: 146 (Phase 7) -> 172 after Phase 8. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Wraps every F9.x sub-phase plus the Phase 9/10/11 plan documents into a single review commit. Tests: 146 (Phase 7 tip) -> 192 after Phase 9. The schema migration framework lands first so subsequent sub-phases can rely on it; mid-measure tempo (F9.6) is the first real exercise. Schema migrations (F9.1, F9.6): - ProjectMigrationRegistry framework with kCurrentSchemaVersion - v1 -> v2 transformer in BundledProjectMigrations rewrites the legacy measure -> bpm tempo map into an array of {measure, tick, bpm} entries. ProjectStore::load registers the bundled migrations idempotently before validation - project-v1.schema.json renamed to project-v2.schema.json; literal in ProjectSchema.cpp matches byte-for-byte (test enforces) - New Project::TempoOverrides type alias propagates through Project, RebuildBindings, MidiWriter, PipelineOrchestration, ExportDialog, and AppController Profile editor + store polish (F9.2, F9.3, F9.4, F9.7-SH1, F9.7-SH2): - F9.2: Replace / Copy / Cancel collision dialog replaces the hardcoded resolver. ProfileStore exposes previewImportFromFile / previewImportFromClipboard so the editor can detect a UUID collision before committing - F9.3: Always-on bundled defaults dropped. DefaultProfiles returns empty vectors; isBundledDefault is always false; the three resource JSONs (generic-guitar / -bass / GM-drumkit) are gone - F9.4: Articulation keyswitch velocity mirrors F8.7 for the articulation map. Editor gains an "Articulation KS Velocity" tab; the injector calls keyswitchVelocityFor(profile, articulation) at the articulation site instead of hardcoding 100 - F9.7 (SH1): exportAllToZip / importAllFromZip via juce::ZipFile. Editor gains an Archive... button with Export All / Import All popup. Batch collision policy (Replace All / Copy All / Skip) is picked once before iteration to keep the resolver synchronous - F9.7 (SH2): six bundled VST starter profiles in resources/profiles/vst-presets/ scraped from public manuals (Shreddage 3.5 Hydra / Argent / Darkwall / Serpent + SubMission Audio UmanskyBass / GroveBass). Solemn Tones Odin III + Sanguine Bass deferred — their keyswitch chart is only inside the running plugin Technique curation (F9.5): - Add ClosedPalmMute, DeadMute (Sustained), and ChokedNote (PerNote) to the Technique enum. kNumTechniques bumps 11 -> 14 - Parser detects each via <other-technical type="closed-palm-mute"/> etc. — opens the standard MusicXML escape hatch through techniqueFromXML - profile-v1 schema literal + on-disk JSON gain the three new names - KeyswitchInjector kSustainedTechniques (4 -> 6) and kPerNoteTechniques (3 -> 4) extended in enum order Mid-measure tempo (F9.6): - TempoMapPanel gains a Beat picker per row that walks 1.0 ... N.875 in 0.125 (32nd-note) steps. Changing the picker moves the row's override from oldTick to newTick, preserving BPM - AppController gains setTempoOverride(measure, tick, bpm) and clearTempoOverride(measure, tick); setTempoForMeasure becomes a thin wrapper for the downbeat-only path - MidiWriter::buildAbsoluteTempoMap resolves overrides via measureStartTicks(score) + tickWithinMeasure -> absolute MIDI tick Documentation pipeline (F9.8): - documentation/ reorganized into architecture / development / customer / agentic-context / api-reference per the documentation-and-plans rule - Doxyfile emits XML only; build-api-docs.sh runs Doxygen -> Doxybook2 to produce Markdown under api-reference/ (gitignored) - .forgejo/workflows/publish-docs.yaml builds the project, runs the API doc pipeline, and rsyncs every section into the project wiki on push to main - New customer docs (getting-started, profile-binding) and development docs (building, testing, coding-rules, techniques, wishlist). Wishlist seeds from Phase 10 triage with explicit Future / Drop entries Tests: - 13 new test files: ProfileImportCollisionTests (5), ArticulationKeyswitchVelocityTests (3), ProfileArchiveTests (3), BundledVstPresetTests (3), plus extensions to existing files (ProjectMigrationTests gains chained migration + bundled v1->v2 cases, MidiWriterTempoOverrideTests gains a mid-measure case, TechniqueCodingTests + TechniqueParserTests gain the three new Technique entries) - 172 -> 192 tests, all pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Replace Verovio with an in-tree SMuFL-based SVG score renderer. Renderer: - New `LayoutEngine` computes staff/note positions from the MusicXML model with no external engraving dependency. - New `SmuflGlyphs` resolves Bravura glyph codepoints + metrics. - New `SvgScoreRenderer` walks the layout and emits a JUCE `Drawable` SVG, including accidentals and ledger lines. - `ScoreRenderPanel` now drives the custom pipeline. Verovio Removal: - Drop the Verovio FetchContent block, target_compile_definitions for VEROVIO_RESOURCE_DIR, and the verovio link dependency. - Delete `MusicXmlEngraver.{h,cpp}` and its tests (`MusicXmlEngraverTests.cpp`, `VerovioSmokeTests.cpp`). Resources: - Bundle Bravura.otf + LICENSE-Bravura.txt under `resources/fonts/` as binary data. Tests: - Add `LayoutEngineTests.cpp` and `SmuflGlyphsTests.cpp` covering baseline/staff geometry and glyph resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>App Preferences: - Persist TableHeader column state and showBundledPresets via juce::PropertiesFile. - Add per-UUID `showUnmapped` accessors so bundled (read-only) profiles remember the toggle individually. Track List Panel: - Reorder + width-configurable columns (saved to prefs). - Octave offset bound to a text-input override surfaced from the AppController. - Wire profile-list double-select + row-height fixes. Main Window: - Open Profiles Folder menu item. - Title bar reflects project name + dirty marker; em-dashes routed through CharPointer_UTF8 so juce::String stops asserting. - Replace/Add MusicXML diagnostics dialog titles use UTF-8. Theming: - AppLookAndFeel button contrast + scrollbar fixes. - `ThemeColourIds`: switch unscoped enum to `enum : int` with `static_cast<int>(0x...u)` values so `findColour` no longer triggers `-Wsign-conversion` at every site. - Drop deprecated `juce::Font(...)` constructors in favour of `juce::Font(juce::FontOptions {...})`. UnknownTechniquesBanner: - Tighten layout + delete dead `kBannerHeight` constant. TempoMapPanel: - Drop names of unused `width`/`height` params on `paintRowBackground`. ProfileEditorPanel (cleanup only — feature work in prior commit): - Cast `profiles_[int]` indexers to `std::size_t` (4 sites). - Rename shadowed lambda capture in `launchKeyswitchPicker`. - Remove the verbose `DBG()` block from `selectedRowsChanged`. Documentation: - LICENSE (PolyForm-NC) + README updates. - PROJECT_CONTEXT.md notes for the Phase 15 work. - Stage seven `implementation-plans/` plan files documenting the editor rebuild, technique consolidation, MIDI completeness, and build-cleanup efforts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>Parser walker classes: - MusicXmlParser::parseLoadedDocument split into PartWalker (measure-spanning state) + MeasureBodyWalker (within- measure cursor + spans). Orchestrator down to ~50 lines. - GpifReader::read split into GpifIndex + readMasterBars + GpifTempoReader + GpifTrackReader + GpifBeatWalker. Read orchestrator down to ~25 lines. - Shared ParserUtilities consolidates readIntChild and kSafeXmlParseFlags (drop legacy ParserSafety.h). - Out-parameter purge: midiToPitch -> DecodedPitch, applySlideFlags -> decodeSlideFlags -> DecodedSlideFlags, parseGpPiTechniques / collectGpPiTechniques -> GpPiResult. TechniqueSet gains unionWith for the recursive PI walker. - XML recursion-depth guard (256 levels) wraps every loaded doc; depth-bomb payloads surface a Diagnostic. Humanizer: - Profile gains humanizerEnabled + HumanizerConfig settings with full JSON round-trip (schema + ProfileStore + ProfileValidator + ProfileEdits). - ProfileEditorPanel exposes the toggle + sigma / floor / ceiling / jitter / seed widgets in both profile mode and the per-part override editor. AppController:: setHumanizerForPart mirrors the octave-offset path. - PipelineOrchestration overlays profile humanizer settings onto explicit per-part overrides; Humanizer::apply stays a pure no-op when neither is configured. Technique enrichment: - AutoSlide added to the Technique enum + coding table (kNumTechniques 23 -> 24); restored to the Odin profile. - BendPoint / BendCurve model + per-Engraving optional bendCurve_; GpifReader consolidates GP's seven Bend* / Offset properties into the curve. - OctaveCorrectionMode { Manual, Inferred }; inferOctaveOffset derives the offset from tuning + per-note string/fret data. - AutoPowerChord documented as a permanent non-feature (GP exports never carry enough info to disambiguate). GP project round-trip: - IScoreImporter gains importMemory(bytes, size) with a temp-file default; MusicXmlParser overrides to route through parse(string_view) directly. - ScoreImporter::importMemory(bytes, size, SourceFormat) dispatches by format. - ProjectStore::materializeScore routes every SourceFormat through the new memory path. Replaces the previous "MusicXml only" guard. Test infrastructure: - tests/support/{Builders,XmlFixtures} consolidates 7+ duplicated builder copies + fixture loader. - Multi-line inline XML literals migrated to tests/fixtures. - New error-path tests across all three parsers (15 cases), inferred-octave tests, GP project round-trip tests, Humanizer mean-velocity property test, malicious XML depth-bomb test. - Scoped -Werror=switch-enum re-enabled on our test sources (JUCE bridges unaffected). 295 -> 318 tests; all pass.