# DIAGNOSTIC REPORT: WHY MIDI SIGNAL IS RECEIVED BUT NOT RECORDED / RENDERED --- ## 1. Executive Summary & Root Cause Analysis Based on the DAW UI screenshot provided, the system is successfully receiving MIDI hardware signals (as indicated by the active VU meter on Track 01 set to `MIDIIN2 (SE49)`), but no MIDI data is being written or displayed on the timeline. This issue occurs due to four architectural and state-management gaps in the current implementation. --- ## 2. Detailed Root Causes ### Root Cause 1: Global Transport Record vs. Track Arm Disconnect * **Observed State:** Track 01 has its individual Arm `[R]` button active (red indicator ON). However, the Global Transport Record button (red circle on the top toolbar) is inactive/stopped at time position `0:01.951`. * **Technical Issue:** Arming a track only enables Live Monitoring (routing MIDI input to the virtual synth engine for real-time audio playback). Recording MIDI into timeline buffers requires both **Track Arm = `true**` AND **Transport Engine State = `RECORDING**`. ```text [ Track Armed ] + [ Transport STOPPED ] --> Live Monitoring ONLY (VU meter lights up, no recording) [ Track Armed ] + [ Transport RECORDING ] --> Live Monitoring + Event Buffer Write + Canvas Redraw ``` ### Root Cause 2: Gate Condition in `handleMIDIMessage` In the client recording engine (`ClientMIDIRecorder`), incoming MIDI events trigger live synth audio, but note recording is gated behind a transport flag: ```javascript handleMIDIMessage(event) { // BUG: If global transport is not in RECORD mode, execution stops here. // Synth gets triggered elsewhere, but recordedNotes array remains empty. if (!this.isRecording) return; const [status, pitch, velocity] = event.data; // ... logic to write to activeNotes and recordedNotes } ``` ### Root Cause 3: Absence of Real-Time Canvas Redraw Loop For notes to render dynamically inside the MIDI Item clip as keys are pressed: * The UI Canvas must run a `requestAnimationFrame` render loop while `isRecording === true`. * The renderer must query the `activeNotes` Map (currently held keys) in addition to finalized `recordedNotes`. * If the UI only renders on static session updates (e.g., when clicking or stopping transport), live notes will not appear on screen during playback. ### Root Cause 4: Track Target ID Unbound to Input Stream If multiple tracks exist, `ClientMIDIRecorder` must know which `track_id` is currently armed and matched to device `MIDIIN2 (SE49)`. If events arrive without a target track context, they cannot be routed into the target `MIDIItem.source_data.notes` array. --- ## 3. Technical Solutions & Code Adjustments ### Step 1: Ensure Dual-Stage Recording State Verification Update the transport control logic so pressing **Record + Play** on the top toolbar initializes active record buffers on all armed tracks: ```javascript // Transport Controller function startTransportRecording() { const armedTracks = session.tracks.filter(t => t.is_armed); if (armedTracks.length === 0) { console.warn("No tracks armed for recording."); startPlaybackOnly(); return; } // Activate global transport record state transport.isRecording = true; transport.isPlaying = true; // Initialize temporary recording items on each armed track armedTracks.forEach(track => { const newRecordingItem = { id: `rec_item_${Date.now()}`, type: "MIDI_ITEM", start_bar: transport.currentBar, duration_bars: 0.1, // Expands dynamically during recording clip_start_offset_bars: 0.0, source_data: { total_buffer_bars: 8.0, notes: [] } }; track.activeRecordingItem = newRecordingItem; midiRecorder.start(track.id, transport.currentBar); }); // Start UI animation loop for live waveform/note preview requestAnimationFrame(renderLiveRecordingUI); } ``` ### Step 2: Live MIDI Note Binding & Duration Expansion Update `ClientMIDIRecorder` to feed both the active buffer and the active recording clip: ```javascript handleMIDIMessage(event) { const [status, pitch, velocity] = event.data; const command = status >> 4; // 1. Always trigger Live Audio Preview (VU Meter + Synth Node) this.triggerSynthPreview(pitch, velocity); // 2. Gate recording buffer write behind global transport record state if (!transport.isRecording || !this.targetTrack) return; const currentBeat = this.calculateLatencyCompensatedBeat(); // Command 0x9: Note On if (command === 0x9 && velocity > 0) { const liveNote = { id: `note_${Date.now()}_${pitch}`, pitch: pitch, start_beat: currentBeat, duration_beats: 0.25, // Default initial length until Note Off velocity: velocity / 127.0 }; this.activeNotes.set(pitch, liveNote); this.targetTrack.activeRecordingItem.source_data.notes.push(liveNote); } // Command 0x8: Note Off else if (command === 0x8 || (command === 0x9 && velocity === 0)) { if (this.activeNotes.has(pitch)) { const note = this.activeNotes.get(pitch); note.duration_beats = Math.max(0.125, currentBeat - note.start_beat); this.activeNotes.delete(pitch); } } } ``` ### Step 3: Real-Time UI Canvas Render Loop Add real-time item length expansion and live note drawing on the main canvas during recording: ```javascript function renderLiveRecordingUI() { if (!transport.isRecording) return; const currentBar = transport.getCurrentBarPosition(); session.tracks.forEach(track => { if (track.is_armed && track.activeRecordingItem) { const item = track.activeRecordingItem; // Expand item duration on timeline as playhead moves forward item.duration_bars = Math.max(0.5, currentBar - item.start_bar); // Draw item bounding box and active/completed MIDI note rectangles drawTimelineItem(trackCanvasCtx, item); } }); requestAnimationFrame(renderLiveRecordingUI); } ``` --- ## 4. Checklist to Fix in Your Application * [ ] Check if clicking top toolbar **Record + Play** sets `transport.isRecording = true`. * [ ] Verify that Track 01 generates a temporary `activeRecordingItem` on record start. * [ ] Confirm `requestAnimationFrame` is re-rendering the canvas continuously while transport is moving. * [ ] Ensure incoming MIDI events on `MIDIIN2 (SE49)` push notes into Track 01's item note array rather than just playing the synth.