Phase 21: add filtered MIDI composition edits and triplet piano-roll grids

This commit is contained in:
Johannes Findeisen 2026-10-27 15:34:54 +01:00
commit 73fc5eea8d
12 changed files with 796 additions and 39 deletions

View file

@ -14,9 +14,9 @@ are connected automatically. Connect a MIDI source to the backend's `midi_in`
JACK port, and route audio output explicitly to hear it while Play is running.
The piano roll shows all 128 pitches and edits clip-source time. Click blank
space to draw a one-beat note (velocity 100, channel 0); drag its body to move
space to draw a one-beat note (velocity 100, filtered channel or channel 0); drag its body to move
it, drag the right edge to resize, or right-click/Delete to remove it. Edits
snap to 1/16 notes. Escape cancels a drag. Trimmed-out source notes remain editable
snap to the configured grid (default 1/16). Escape cancels a drag. Trimmed-out source notes remain editable
and are preserved. Arrangement/note edits stop playback and are blocked during
audio recording. Save/Open preserves MIDI flags, notes and stable IDs.
@ -37,8 +37,9 @@ builds audio and MIDI candidates before committing the stopped backend state.
JACK events have sample offsets within the current block. Supported messages
are complete note on/off (including velocity-zero note on) and CC120/123 on
channels 0..15. Equal-offset events retain input order. Other messages are
ignored; sustain, bend, program changes, running status and SysEx are unsupported.
channels 0..15, plus CC7 volume, CC10 pan and CC64 sustain (see CONTROL.md).
Equal-offset events retain input order. Bend, program changes, running status
and SysEx are unsupported.
At most 256 events are accepted per block. Malformed/overflowed input discards
that block's events and triggers panic, with a MIDI-drop status counter.
@ -58,8 +59,8 @@ rejects more than 32 overlapping scheduled notes with CAPACITY. Such metadata
can be saved by the C model, but GUI Open/Play rejects it until overlap is
reduced. Immediate note-off/stealing can click; there is no envelope or limiter,
and summed voices can exceed unity. Above-Nyquist pitches are silent.
There is no per-track/channel instrument routing, velocity editor, MIDI recording,
SMF import, undo or piano-roll zoom yet.
There is no per-track/channel instrument routing, MIDI recording, SMF import
or piano-roll zoom yet. Note edits participate in project snapshot undo/redo.
## Verification
@ -74,3 +75,59 @@ Physical MIDI devices, bridge/device loss behind an unchanged port, latency and
long-running performance remain unverified. Panic/Stop provides manual recovery.
Phase 8 adds track mixing, automation and project undo/redo; see MIXER.md.
## Phase 21 composition commands
The piano-roll **Notes** menu exposes these commands. Shortcuts apply while the
piano roll has focus; use the main window Edit menu for undo/redo.
| Command | Shortcut | Behavior |
| --- | --- | --- |
| Grid | G | 1, 120, 160, 240, 320, 480 or 960 ticks |
| Filter | F | Channel -1 (all) or 0..15, inclusive pitch range 0..127 |
| Velocity and channel | V | Set either property; -1 preserves it |
| Quantize | Q | Snap starts to nearest configured grid; ties go later |
| Transpose | T / Up / Down | Semitone count / one semitone up / down |
| Repeat | Ctrl+D | Additional copies at a positive source-tick spacing |
| Select filtered | Ctrl+A | Select every note matching the current clip filter |
| Delete | Delete | Remove the single selected note or all filtered notes |
160 ticks is a sixteenth-note triplet; 320 is an eighth-note triplet.
The grid governs drawing, drag movement and minimum resize duration. At 1-tick
snap the visual grid remains 120 ticks to stay legible at the fixed zoom.
The header shows current grid, channel/pitch filter and selection scope.
Velocity also affects unselected note brightness. Preferences are session-only.
Click selects one visible note; Ctrl+A selects the whole filtered set. A click
or filter change clears that set selection. Hidden notes cannot be mouse-edited.
A channel filter also sets the channel for newly drawn notes. No lasso or
arbitrary disjoint multi-selection is provided. Moving/resizing uses the mouse;
V edits velocity/channel without changing timing, pitch or IDs. Quantization
changes starts only, preserving durations and other properties. Repetition
keeps originals and gives every copy a fresh stable ID; spacing need not equal
note length, so both overlapping patterns and rests are possible. The existing
clip source must have room for every result; extend it in Sequencer first if
needed. Source notes outside the visible arrangement trim remain editable.
All batch edits validate before mutation. Invalid pitch/range, copy counts,
capacity overflow or a note crossing the source boundary rejects the entire
operation; notes are never silently clamped, merged or shortened. Unmatched
notes, notes in other clips and unrelated properties are preserved. Each
successful command records one undo step and stops playback before model edits;
Play prepares the new engine plan. Undo/redo reinstalls the corresponding plan.
These edits use the existing persisted note fields; format remains v7.
Scheduled notes occupy [start,end), with ends counted before starts at an equal
sample for the 32-voice limit. Overlapping same-channel/pitch scheduled notes
are independent sine voices; adjacent repetitions restart phase. Live repeated
notes instead retrigger the matching channel/pitch voice, and a note-off releases
that voice (or defers release under sustain). Equal-sample live on/off/control
messages execute in caller/JACK order, with no implicit priority reordering.
The renderer rejects malformed/unsorted/out-of-range events before touching
output or runtime; raw velocity-zero note-ons decode to note-off.
`make test-composition` covers filters, property preservation, triplet rounding,
repeats/fresh IDs, rejected batch rollback, capacity, undo snapshots, equal-offset
live order, adjacent/overlapping notes, exact block partitions and seek chase.
`make test-gui` covers mouse edits and the new commands with undo/redo and engine
reinstallation. Listening and physical MIDI-device validation remain pending.

View file

@ -458,6 +458,239 @@ or distro registration is performed. Independent human installation/usability,
physical-device/endurance and listening acceptance remain pending and must be
completed before claiming production qualification.
## Phase 21 — Advanced MIDI and Composition
### Goal
Make MIDI composition expressive and efficient without turning the application into an unnecessarily complicated notation editor.
### Tasks
- Improve piano-roll editing for note creation, selection, movement, resizing, and deletion.
- Support configurable snap and grid resolution, including triplets where practical.
- Add velocity editing and editing for supported MIDI note properties.
- Support MIDI channel and note-range filtering where appropriate.
- Improve quantization, transposition, duplication, and pattern repetition.
- Ensure MIDI event ordering is deterministic, especially when note-off and note-on events share a timestamp.
- Define handling for overlapping notes, repeated notes, and malformed MIDI events.
- Add regression tests for MIDI editing and playback scheduling.
### Acceptance criteria
- Common MIDI editing tasks can be completed without manually editing project files.
- Editing preserves unrelated notes and event properties.
- MIDI scheduling remains deterministic at buffer boundaries and under transport changes.
- MIDI operations are covered by automated tests.
Implemented: transactional C filtered note transforms, nearest-grid quantization,
velocity/channel edits, transposition, bounded fresh-ID repetition and deletion;
piano-roll Notes menu, keyboard commands, configurable straight/triplet grids,
channel/pitch filtering and filtered-set selection. Existing mouse move/resize
and project undo/redo remain supported. Core and GUI regression tests demonstrate
property/other-clip preservation, rollback, undo/redo, adjacent/overlapping notes,
equal-offset caller ordering, exact block partitions and seek chase. See MIDI.md.
No format change, MIDI capture, SMF import, lasso selection or piano-roll zoom;
physical-device and listening validation remain pending.
Acceptance evidence:
- [x] Menu/keyboard/mouse commands edit notes without project-file editing.
- [x] Filtered batch tests preserve unmatched notes and unrelated properties.
- [x] Exact scheduling tests cover buffer boundaries and transport seeks.
- [x] Core composition and GUI undo/redo regressions exercise these operations.
## Phase 22 — Audio Clip Editing and Utility Operations
### Goal
Provide focused, non-destructive audio editing tools for arranging and preparing recorded or imported material.
### Tasks
- Support moving, trimming, splitting, duplicating, and deleting audio clips.
- Define clip-boundary behavior, including fades and source offsets.
- Add non-destructive clip gain and basic fade-in/fade-out controls.
- Support time-stretching and pitch-shifting only after selecting a suitable implementation and quality target; otherwise document them as future work.
- Ensure edits do not unexpectedly modify source audio files.
- Add waveform display caching that never blocks the audio thread.
- Test edits at project boundaries, source-file boundaries, and very short clip lengths.
- Document which operations are destructive and non-destructive.
### Acceptance criteria
- Basic clip editing is predictable and undoable.
- Source files remain unchanged unless a documented destructive operation is explicitly invoked.
- Clip boundaries do not produce unintended clicks from invalid offsets or discontinuities.
- Editing and waveform generation never perform blocking work in the real-time audio callback.
## Phase 23 — Modular Workflow and Routing Refinement
### Goal
Preserve the modular spirit of the application while making complex projects understandable and manageable.
### Tasks
- Refine graph and routing views so signal paths are easy to inspect.
- Make connections, disconnections, and invalid routing states clear.
- Provide useful names and visual distinctions for tracks, instruments, effects, buses, and I/O ports.
- Support track grouping or folders if the project model can represent them cleanly.
- Add routing presets or reusable track templates only if they do not complicate the core model.
- Define behavior for cycles and feedback paths; reject unsupported cycles explicitly.
- Make graph changes transactional so failed edits do not leave partially updated routing.
- Add tests for graph changes while playback is running.
### Acceptance criteria
- Users can inspect and understand a project's signal path.
- Invalid graph edits fail safely and leave the previous valid graph intact.
- Graph updates follow the documented real-time synchronization strategy.
- GUI routing concepts map cleanly to the public C engine API.
## Phase 24 — Mixing and Mastering Essentials
### Goal
Provide the essential mixing controls needed to finish a small or medium-sized project.
### Tasks
- Complete track and bus gain, pan, mute, solo, and metering behavior.
- Define how solo states affect buses, sends, and sidechains.
- Add peak meters with clear units and documented ballistics.
- Add master-bus monitoring and clipping indicators.
- Support routing to buses and auxiliary sends if the graph model supports them.
- Make gain staging explicit and avoid hidden or surprising level changes.
- Document meter tap points and whether values are pre-fader, post-fader, or post-effects.
- Test mute, solo, pan, gain, and routing interactions.
### Acceptance criteria
- Mixer controls behave consistently across tracks, buses, and master output.
- Meter readings correspond to documented signal points and units.
- Solo and mute behavior is covered by automated tests.
- No mixer operation introduces allocations, locks, or other prohibited work in the audio callback.
## Phase 25 — Plugin and Instrument Usability
### Goal
Make instruments and effects practical to use, manage, and diagnose in real projects.
### Tasks
- Improve plugin discovery, insertion, removal, replacement, and ordering.
- Show useful plugin identity and status information, including load failures.
- Define parameter metadata, ranges, defaults, units, and formatting.
- Support parameter reset and safe default values.
- Preserve plugin state across save/load where the plugin API supports it.
- Define behavior when a saved plugin is missing or incompatible.
- Manage plugin UI lifetime separately from engine object lifetime.
- Add tests for plugin lifecycle, state restoration, and failure paths.
- Document plugin API versioning and compatibility expectations.
### Acceptance criteria
- Users can identify and manage loaded instruments and effects without editing configuration files.
- Missing or incompatible plugins produce actionable diagnostics and do not silently corrupt projects.
- Plugin state restoration is tested for supported plugin types.
- The engine remains safe if plugin initialization or processing fails, within the documented failure model.
## Phase 26 — Automation and Control Mapping
### Goal
Enable repeatable changes to mix and instrument parameters over time, with clear timing and ownership rules.
### Tasks
- Define a common parameter identity and metadata model.
- Support automation lanes for a carefully selected set of parameters.
- Define interpolation modes and behavior at clip boundaries.
- Specify how automation interacts with live parameter changes and MIDI control.
- Add control mapping for selected external MIDI controllers if feasible.
- Schedule automation deterministically without allocating memory in the audio callback.
- Define behavior when a target parameter or plugin is missing.
- Test automation at buffer boundaries, during seeks, and across loop points.
- Document precedence rules for automation, controller input, and direct user changes.
### Acceptance criteria
- Supported parameters can be automated and saved in a project.
- Playback produces deterministic automation values for a given project and transport position.
- Missing targets fail safely and are reported to the user.
- Automation processing meets the engine's real-time safety requirements.
## Phase 27 — Editing Workflow, Undo/Redo, and Usability
### Goal
Make common editing actions quick, discoverable, and safe to reverse.
### Tasks
- Audit user-visible editing operations for undo/redo support.
- Implement command-based edits with explicit ownership and rollback behavior.
- Group related low-level changes into one meaningful undo step.
- Define undo behavior for operations affecting engine state or external files.
- Improve keyboard shortcuts, selection behavior, focus handling, and context menus.
- Use confirmation only for genuinely destructive or difficult-to-reverse actions.
- Provide clear feedback for long-running operations and failures.
- Add GUI tests or reproducible manual test procedures for critical workflows.
- Keep presentation logic out of the core C engine.
### Acceptance criteria
- Documented editing operations have predictable undo/redo behavior.
- Undo and redo restore the project model and relevant engine state consistently.
- Failed operations do not leave partial changes behind.
- Common tasks can be completed using documented keyboard and mouse interactions.
## Phase 28 — Project Format, Compatibility, and Portability
### Goal
Make project files robust, diagnosable, and maintainable across application versions and supported systems.
### Tasks
- Version the project format and document its compatibility policy.
- Validate project input before applying it to the live engine.
- Handle missing media, plugins, and external dependencies with clear diagnostics.
- Define relative-path, absolute-path, and media-relocation behavior.
- Add safe migration routines for supported older versions.
- Preserve unknown optional fields where practical, or document when they cannot be preserved.
- Test malformed files, truncated files, oversized input, and invalid values.
- Make saves atomic where the platform permits, using a temporary file and replacement strategy.
- Document portability limits, including plugin and audio-backend dependencies.
### Acceptance criteria
- Invalid project files are rejected safely without partially replacing the current project.
- Save failures do not silently destroy the previous valid project file.
- Version incompatibilities and missing dependencies are clearly reported.
- Supported project versions have migration and regression tests.
## Phase 29 — Performance, Scalability, and Stress Testing
### Goal
Establish measurable performance expectations and verify that larger projects remain stable.
### Tasks
- Define representative test projects and target buffer sizes.
- Measure audio callback duration, deadline misses, CPU use, memory use, and graph rebuild costs.
- Test projects with increasing numbers of tracks, graph nodes, connections, MIDI events, and automation points.
- Test long playback sessions and repeated start, stop, seek, and loop operations.
- Stress-test device changes, plugin failures, missing media, and project reloads.
- Use profiling to identify bottlenecks before introducing optimizations.
- Verify that the real-time callback does not allocate, block on mutexes, perform file I/O, or wait on GUI work.
- Document expected limits and known performance bottlenecks.
- Add sanitizers and static analysis to regular development builds where supported.
### Acceptance criteria
- Performance is measured against documented test cases, not subjective impressions alone.
- Stress tests can run repeatedly and produce reproducible results.
- No known real-time safety violation remains in the engine's own callback path.
- Performance regressions can be detected by repeatable tests or benchmark reports.
## Phase 30 — Release Engineering, Packaging, and Maintenance
### Goal
Turn the application into a maintainable release that users can build, install, run, and troubleshoot.
### Tasks
- Define supported operating systems, compiler versions, and audio backends.
- Document build dependencies and provide clear build and test commands.
- Add install and uninstall targets that respect `PREFIX` and standard directory variables.
- Provide appropriate package metadata and desktop integration where relevant.
- Verify installation paths for binaries, libraries, headers, resources, documentation, and example projects.
- Add version reporting and a concise diagnostic mode where useful.
- Prepare release notes, known limitations, and upgrade guidance.
- Test clean builds from a fresh checkout and installation into a staging directory.
- Define a release checklist covering tests, warnings, packaging, and project compatibility.
- Document how users can report reproducible bugs and how maintainers should triage them.
### Acceptance criteria
- A clean checkout can be built and tested using documented commands.
- Installation works with a configurable prefix and does not overwrite unrelated user files.
- The release includes documentation, known limitations, and a reproducible version identifier.
- The project has a repeatable process for preparing and validating releases.
---
## Recommended Execution Order

View file

@ -96,3 +96,12 @@ core files may contain media paths and audio memory; share only what you intend.
Report bugs following MAINTENANCE.md, including whether the issue reproduces
without plugins. Physical listening and independent human install/usability
acceptance remain pending.
## MIDI composition
In Piano roll, use the Notes menu for grid (including triplets), channel/pitch
filters, velocity/channel, quantization, transposition and pattern repetition.
Click selects a single note; Ctrl+A selects all filtered notes. Ctrl+D repeats
that selection at a chosen spacing within the existing clip source. Up/Down
transposes by a semitone. Commands are undoable through the main Edit menu.
See [MIDI.md](MIDI.md#phase-21-composition-commands) for shortcuts, timing and limits.

View file

@ -113,7 +113,7 @@ Duplicate, Rename and Delete. Invalid/capacity edits retain the existing model.
View/Snap selects 1, 120, 240 (default), 480 or 960 ticks. It affects ruler seeks,
clip moves and trims; 1 tick is the finest grid. Existing +/- zoom and scrollbars
retain their behavior. Piano roll and mixer lanes retain their documented 240-tick
retain their behavior. Piano roll has its own configurable grid (see MIDI.md); mixer lanes retain their 240-tick
grid. Grid/zoom/selection are session UI preferences, not undoable music edits.
Escape cancels a drag; Delete affects the focused editor; text fields keep their
own editing keys. Commands are named in menus/tooltips and timeline focus accepts