feat(state): scale a mapped MIDI CC onto a normalized parameter window#6476
Open
shipyard-local[bot] wants to merge 2 commits into
Open
feat(state): scale a mapped MIDI CC onto a normalized parameter window#6476shipyard-local[bot] wants to merge 2 commits into
shipyard-local[bot] wants to merge 2 commits into
Conversation
MidiParameterMap already routed a MIDI Control Change to any parameter by id, with learn mode, an omni/channel filter, and an RT-safe lock-free apply. It always drove the parameter across its full range, though — a controller could not be scaled to a sub-range or inverted. Give each mapping a MidiMapScale: the normalized [0, 1] window a 0..127 CC sweep is mapped onto. Endpoints clamp into [0, 1]; min > max inverts. The default window is the full range, so existing mappings are unchanged and the prior tests pass untouched. Shaping stays a property of the target ParamRange, not the map. The apply path stays allocation-free (asserted with RtAllocationProbe). Deferred, not required by the mapping foundation: MIDI-learn UI, 14-bit (MSB/LSB) CC pairing, per-map response curves, MPE per-note expression routing, and OSC. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TVQHcnbg3t6TvXsdv6gXhS
…river code The Creative Timeline Engine's only automatable parameter surface is a hosted device parameter, addressed by timeline::DeviceParameterTarget whose param_id is the device's stable 32-bit host-facing ID — the same state::ParamID a StateStore uses and MidiParameterMap drives. There is no separate engine parameter identity to bridge, so the map already reaches every engine-automatable parameter with no per-controller or per-parameter driver code. Prove it against the engine type directly: source the id the engine would automate from a DeviceParameterTarget, register it in the device store, then learn a CC onto it and drive it through the generic learn/apply loop (full, scaled, and inverted windows). Links pulp::timeline so the proof uses the engine's own parameter identity rather than asserting the equivalence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TVQHcnbg3t6TvXsdv6gXhS
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Jul 23, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Jul 23, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Jul 23, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Jul 23, 2026
shipyard-local
Bot
removed this pull request from the merge queue due to a manual request
Jul 23, 2026
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.
What
Phase-3 item #6 of the Creative Timeline Engine — "a hardware MIDI controller maps to any parameter with zero driver code."
Verified against code first: the routing foundation already existed.
pulp::state::MidiParameterMapalready routes an incoming MIDI Control Change to any parameter by id, with learn mode, an omni/channel filter, a lock-free UI→audio command queue, and an RT-safe apply viaStateStore::set_normalized_rt. The engine's only automatable parameter surface —pulp::timeline::AutomationTarget = variant<DeviceParameterTarget>— addresses a hosted device parameter by a 32-bitparam_id, the samepulp::state::ParamIDspace the map already drives. So the DoD's mapping capability is satisfied by absorption; there is no separate engine parameter identity to bridge.The one genuine gap was value scaling: the map always drove the parameter across its full range. This PR adds a per-mapping
MidiMapScale— the normalized[0, 1]window a0..127CC sweep maps onto. Endpoints clamp into[0, 1](on the UI thread), andmin > maxinverts. The default window is the full range, so existing mappings are unchanged and the 5 priorMidiParameterMaptests pass bit-identically. Shaping stays a property of the targetParamRange, not the map.DoD proof
A headless Catch2 case sources the parameter id from the engine's own
DeviceParameterTarget, registers it in a deviceStateStore, then learns a CC onto it and drives it through one generic learn→map→apply loop (full / middle-half / inverted windows) — proving reach into an engine-addressed parameter with zero per-controller/per-parameter code, using the engine type directly rather than asserting the equivalence.Tests
test/test_midi_parameter_map_scaling.cpp(suitepulp-test-midi-parameter-map-scaling, registered intest/cmake/timeline_tests.cmake): 9 cases / 32 assertions, green Release. Covers scaled-window math, non-unit param range, inversion, out-of-range endpoint clamping, learn-carries-scale, unmapped/channel/remove edges, the engine-identity DoD proof, and an allocation-free apply-path assertion viaRtAllocationProbe(negative-control-verified: an escaping allocation makes the probe fail, so the assertion has teeth). The suite linkspulp::timelinefor the DoD proof — test-only.Scope
Out of scope (not required by the DoD): recording CC moves as timeline automation / transport arbitration, MIDI-learn UI, 14-bit CC, per-map response curves, MPE per-note expression, OSC.
Version-at-land assigns the SDK minor post-merge (no
chore: bump versionscommit;version_bump_check --mode=reportexits 0).🤖 Generated with Claude Code
https://claude.ai/code/session_01TVQHcnbg3t6TvXsdv6gXhS
🔎 Provenance
claudem3/private/tmp/claude-501/cte-midi-mapc66f4a64-25ea-42e5-b184-d48611a066a2Resume
Jump to this tab
Relaunch (any agent)
Restore URL — https://claude.ai/code/session_01TVQHcnbg3t6TvXsdv6gXhS
stamped 2026-07-22 22:41 UTC