Skip to content

182 create midi tracks#192

Merged
Sportinger merged 5 commits into
stagingfrom
182-create-midi-tracks
May 31, 2026
Merged

182 create midi tracks#192
Sportinger merged 5 commits into
stagingfrom
182-create-midi-tracks

Conversation

@kfxs

@kfxs kfxs commented May 31, 2026

Copy link
Copy Markdown
Collaborator

kfxs and others added 3 commits May 29, 2026 17:56
Phases 1-3 of the MIDI tracks feature:

Phase 1 — Data model + Add MIDI Track:
- New src/types/midiClip.ts (MidiNote, MidiClipData, MidiInstrument)
- Widen TimelineSourceType + TimelineTrack.type to include 'midi';
  midiInstrument on tracks, midiData on clips (+ SerializableClip)
- addTrack('midi') in trackSlice, TrackContextMenu entry
- MIDI tracks render in the audio section via isAudioSectionTrackType helper
- Ripple fixes across dock/project/clipboard types and reconcile maps

Phase 2 — Pencil tool:
- New 'midi-draw' tool registered across toolDefaults, registry, icons,
  cursors, and shortcut presets (navigation group)
- addMidiClip action (midiClipSlice) + useMidiClipDraw hook: click-drag on
  a MIDI lane paints a clip (free placement, no snap), viewport-fixed ghost

Phase 3 — Piano-roll detached window:
- PianoRollBoot (window.open popup, shared-heap, per-clip) + PianoRoll editor
- Note CRUD (add/update/remove) with single-snapshot drag history
- Draw/move/resize/delete notes, live playhead cursor, double-click to open
- Fix: drag listeners bound to the popup's ownerDocument (component runs in
  the opener realm, so global `document` was the wrong window)

Tests: midiClipSlice note CRUD; updated navigation tool-cycle expectations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 4 (synth + playback):
- MidiSynth: context-agnostic triangle/osc + ADSR, polyphonic scheduling
- midiPlaybackScheduler: look-ahead scheduler anchoring timeline seconds to
  the AudioContext clock, 1x-forward, seek re-anchor, flush on stop

Phase 4b / Step 2 (mixer parity):
- Generalized audioRoutingManager to route arbitrary AudioNode sources
  (ensureSharedContext/applyNodeEffects/getNodeMeterSnapshot/removeNodeRoute)
  so MIDI reuses the same gain/FX/EQ/pan/meter chain + master bus as audio
- createTrackLiveAudioRouteSettings for clip-less (track) sources
- Properties panel: MIDI tracks get Controls + Instrument + Effects + Sends;
  AudioMixerPanel includes midi tracks
- setTrackMidiInstrument action + MidiInstrumentTab

Header UI:
- MIDI tracks share the full audio mixer strip (vol/pan/solo/mute/rec/FX/
  sends/meter) via a generalized "mixer track" concept (audio || midi);
  .track-header.audio CSS selectors renamed to .track-header.mixer
- Track-type identifier glyphs above the name (waveform / piano-roll bars)
- Instrument picker dropdown on its own full-width row, shared
  MIDI_INSTRUMENT_OPTIONS reused by header and properties tab

Note: MIDI note/instrument persistence and offline export are Phase 5 (todo).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Persistence — MIDI note data now survives save/reload:
- midiInstrument (track) was already persisted via track spread; the gap was
  midiData (notes), which clips serialize via explicit field lists
- serializationUtils: add midiData to getSerializableState clip map + a new
  sourceType==='midi' restore branch in loadState (data-only clip, mirrors
  addMidiClip)
- projectSave/projectLoad: add midiData to the on-disk clip mapping
- test: midiPersistence round-trips serialize -> clear -> load

Offline export — MIDI clips now render into exported audio:
- MidiClipRenderer: pure planMidiClipNotes (clip-local note timing, dropped/
  clamped at clip end) + renderMidiClipToBuffer (OfflineAudioContext + MidiSynth
  -> AudioBuffer)
- AudioExportPipeline: getClipsWithAudio includes MIDI clips with notes;
  extractAllAudio renders the synth for MIDI clips instead of decoding a file.
  The rendered buffer flows through the same clip-fx -> track-fx -> mix -> master
  path as audio clips (full mixer parity, no separate MIDI export path)
- test: midiClipRenderer covers the planner

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kfxs

kfxs commented May 31, 2026

Copy link
Copy Markdown
Collaborator Author

@Sportinger the basic infrastructure for midi tracks is here. There are some things that should be tweaked, but I think this is a good point for merging since there already many changes.

kfxs and others added 2 commits May 31, 2026 08:23
Resolved 5 conflicts (MIDI work vs. staging):
- types/dock.ts: keep staging's trackTypeCounts/Layouts + new interface;
  extend trackTypeVisibility/Counts/Layouts to include 'midi'
- timeline/trackSlice.ts: use staging's createTrackId(type) (widened to 'midi')
  with the MIDI-aware typeLabel for the track name
- services/audioRoutingManager.ts: keep both nodeRoutes and routeCreateFailures
- engine/audio/AudioExportPipeline.ts: run MIDI synth-render branch first,
  then staging's reusable cached/proxy-audio lookup
- timeline/TimelineTracks.css: audio tracks adopt staging's single-column
  compact fader + audio-layer-advanced layout; MIDI keeps the original
  two-column mixer-parity layout (rules scoped to .midi)

Build + lint clean. Pre-existing staging test failures in
IssueCreditCampaignBanner.test.tsx (missing matchMedia polyfill) left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jsdom does not implement matchMedia, so components that query media
features in passive effects (IssueCreditCampaignBanner, useTheme,
DockContainer) threw a "matchMedia is not a function" error and failed
the test suite. Add a standard no-op shim to the shared test setup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Sportinger Sportinger merged commit 6a562f0 into staging May 31, 2026
3 checks passed
@Sportinger Sportinger deleted the 182-create-midi-tracks branch May 31, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants