Fix midi tracks recording previews

This commit is contained in:
Skyler Lehmkuhl 2026-03-11 12:53:26 -04:00
parent 3bc980d08d
commit be8514e2e6
3 changed files with 46 additions and 1 deletions

View File

@ -416,13 +416,23 @@ impl Engine {
if let Some(recording) = &self.midi_recording_state {
let current_time = self.playhead as f64 / self.sample_rate as f64;
let duration = current_time - recording.start_time;
let notes = recording.get_notes().to_vec();
let notes = recording.get_notes_with_active(current_time);
let _ = self.event_tx.push(AudioEvent::MidiRecordingProgress(
recording.track_id,
recording.clip_id,
duration,
notes,
));
// Keep the snapshot up to date so the UI can display a growing clip bar.
let track_id = recording.track_id;
let clip_id = recording.clip_id;
if let Some(crate::audio::track::TrackNode::Midi(track)) = self.project.get_track_mut(track_id) {
if let Some(instance) = track.clip_instances.iter_mut().find(|i| i.clip_id == clip_id) {
instance.internal_end = duration;
instance.external_duration = duration;
}
}
self.refresh_clip_snapshot();
}
}
} else {

View File

@ -253,6 +253,18 @@ impl MidiRecordingState {
self.completed_notes.len()
}
/// Get all completed notes plus currently-held notes with a provisional duration.
/// Used for live preview during recording so held notes appear immediately.
pub fn get_notes_with_active(&self, current_time: f64) -> Vec<(f64, u8, u8, f64)> {
let mut notes = self.completed_notes.clone();
for active in self.active_notes.values() {
let time_offset = active.start_time - self.start_time;
let provisional_dur = (current_time - active.start_time).max(0.0);
notes.push((time_offset, active.note, active.velocity, provisional_dur));
}
notes
}
/// Get the note numbers of all currently held (active) notes
pub fn active_note_numbers(&self) -> Vec<u8> {
self.active_notes.keys().copied().collect()

View File

@ -5164,6 +5164,29 @@ impl eframe::App for EditorApp {
.filter(|lid| self.recording_layer_ids.contains(lid))
.copied();
if let Some(layer_id) = midi_layer_id {
// Lazily create the doc clip + instance on the first progress event
// (there is no MidiRecordingStarted event from the backend).
let already_exists = self.clip_instance_to_backend_map.values().any(|v| {
matches!(v, lightningbeam_core::action::BackendClipInstanceId::Midi(id) if *id == clip_id)
});
if !already_exists {
use lightningbeam_core::clip::{AudioClip, ClipInstance};
let clip = AudioClip::new_recording("Recording...");
let doc_clip_id = self.action_executor.document_mut().add_audio_clip(clip);
let clip_instance = ClipInstance::new(doc_clip_id)
.with_timeline_start(self.recording_start_time);
let clip_instance_id = clip_instance.id;
if let Some(layer) = self.action_executor.document_mut().get_layer_mut(&layer_id) {
if let lightningbeam_core::layer::AnyLayer::Audio(audio_layer) = layer {
audio_layer.clip_instances.push(clip_instance);
}
}
self.clip_instance_to_backend_map.insert(
clip_instance_id,
lightningbeam_core::action::BackendClipInstanceId::Midi(clip_id),
);
}
let doc_clip_id = {
let document = self.action_executor.document();
document.get_layer(&layer_id)