From e11caf2dea61b2105bbbee6bcdcc69d086b1b325 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 22 Oct 2024 14:41:40 -0600 Subject: [PATCH] various changes to get MIDI clip recording to display the post-capture region --- gtk2_ardour/midi_cue_editor.cc | 164 +++++++++++++++++++-------------- gtk2_ardour/midi_cue_editor.h | 10 +- gtk2_ardour/midi_cue_view.cc | 2 +- gtk2_ardour/midi_view.cc | 30 +++--- gtk2_ardour/trigger_page.cc | 32 +------ gtk2_ardour/view_background.h | 1 + libs/ardour/triggerbox.cc | 23 ++++- 7 files changed, 143 insertions(+), 119 deletions(-) diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index a4f9054607..158472fc1f 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -494,20 +494,6 @@ MidiCueEditor::idle_data_captured () return false; } -void -MidiCueEditor::set_box (std::shared_ptr b) -{ - capture_connections.drop_connections (); - idle_update_queued.store (0); - - if (b) { - b->Captured.connect (capture_connections, invalidator (*this), std::bind (&MidiCueEditor::data_captured, this, _1), gui_context()); - /* Don't bind a shared_ptr within the lambda */ - TriggerBox* tb (b.get()); - b->RecEnableChanged.connect (capture_connections, invalidator (*this), [&, tb]() { box_rec_enable_change (*tb); }, gui_context()); - } -} - void MidiCueEditor::box_rec_enable_change (ARDOUR::TriggerBox const & b) { @@ -528,61 +514,6 @@ MidiCueEditor::trigger_rec_enable_change (ARDOUR::Trigger const & t) } } -void -MidiCueEditor::set_track (std::shared_ptr t) -{ - _track = t; - - view->set_track (t); - - _update_connection.disconnect (); - capture_connections.drop_connections (); - - if (t) { - set_box (t->triggerbox()); - _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &MidiCueEditor::maybe_update)); - _track->DropReferences.connect (track_connection, invalidator (*this), std::bind (&MidiCueEditor::set_track, this, nullptr), gui_context()); - } else { - set_box (nullptr); - } -} - -void -MidiCueEditor::set_region (std::shared_ptr r) -{ - if (!r) { - view->set_region (nullptr); - return; - } - - view->set_region (r); - - /* Compute zoom level to show entire source plus some margin if possible */ - - Temporal::timecnt_t duration = Temporal::timecnt_t (r->midi_source()->length().beats()); - - bool provided = false; - std::shared_ptr map; - std::shared_ptr smf (std::dynamic_pointer_cast (r->midi_source())); - - if (smf) { - map = smf->tempo_map (provided); - } - - if (!provided) { - map.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4))); - } - - { - EditingContext::TempoMapScope tms (*this, map); - double width = bg->width(); - samplecnt_t samples = duration.samples(); - - samplecnt_t spp = floor (samples / width); - reset_zoom (spp); - } -} - bool MidiCueEditor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { @@ -1761,3 +1692,98 @@ MidiCueEditor::selectable_owners() return std::list (); } + +void +MidiCueEditor::trigger_prop_change (PBD::PropertyChange const & what_changed) +{ + if (what_changed.contains (Properties::region)) { + std::shared_ptr mr = std::dynamic_pointer_cast (ref.trigger()->the_region()); + if (mr) { + set_region (mr); + } + } +} + +void +MidiCueEditor::set (TriggerReference & tref) +{ + _update_connection.disconnect (); + object_connections.drop_connections (); + + ref = tref; + + idle_update_queued.store (0); + + ref.box()->Captured.connect (object_connections, invalidator (*this), std::bind (&MidiCueEditor::data_captured, this, _1), gui_context()); + /* Don't bind a shared_ptr within the lambda */ + TriggerBox* tb (ref.box().get()); + tb->RecEnableChanged.connect (object_connections, invalidator (*this), [&, tb]() { box_rec_enable_change (*tb); }, gui_context()); + + Stripable* st = dynamic_cast (ref.box()->owner()); + assert (st); + _track = std::dynamic_pointer_cast (st->shared_from_this()); + assert (_track); + + view->set_track (_track); + + _update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &MidiCueEditor::maybe_update)); + _track->DropReferences.connect (object_connections, invalidator (*this), std::bind (&MidiCueEditor::unset, this), gui_context()); + ref.trigger()->PropertyChanged.connect (object_connections, invalidator (*this), std::bind (&MidiCueEditor::trigger_prop_change, this, _1), gui_context()); + + if (ref.trigger()->the_region()) { + + std::shared_ptr mr = std::dynamic_pointer_cast (ref.trigger()->the_region()); + + if (mr) { + set_region (mr); + } + } +} + +void +MidiCueEditor::unset () +{ + _update_connection.disconnect(); + object_connections.drop_connections (); + _track.reset (); + view->set_region (nullptr); + ref = TriggerReference (); +} + +void +MidiCueEditor::set_region (std::shared_ptr r) +{ + if (!r) { + view->set_region (nullptr); + return; + } + + view->set_region (r); + + /* Compute zoom level to show entire source plus some margin if possible */ + + Temporal::timecnt_t duration = Temporal::timecnt_t (r->midi_source()->length().beats()); + + std::cerr << "new region: " << duration << std::endl; + + bool provided = false; + std::shared_ptr map; + std::shared_ptr smf (std::dynamic_pointer_cast (r->midi_source())); + + if (smf) { + map = smf->tempo_map (provided); + } + + if (!provided) { + map.reset (new Temporal::TempoMap (Temporal::Tempo (120, 4), Temporal::Meter (4, 4))); + } + + { + EditingContext::TempoMapScope tms (*this, map); + double width = bg->width(); + samplecnt_t samples = duration.samples(); + std::cerr << "new spp from " << samples << " / " << width << std::endl; + samplecnt_t spp = floor (samples / width); + reset_zoom (spp); + } +} diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index c541e7175e..b77e4da53e 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -72,9 +72,8 @@ class MidiCueEditor : public CueEditor int32_t get_grid_beat_divisions (Editing::GridType gt) const { return 1; } int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) const { return 1; } + void set (ARDOUR::TriggerReference&); void set_region (std::shared_ptr); - void set_box (std::shared_ptr); - void set_track (std::shared_ptr); ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; } ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; } @@ -140,6 +139,7 @@ class MidiCueEditor : public CueEditor void on_samples_per_pixel_changed (); private: + ARDOUR::TriggerReference ref; std::shared_ptr _track; ArdourCanvas::GtkCanvasViewport* _canvas_viewport; ArdourCanvas::GtkCanvas* _canvas; @@ -208,8 +208,11 @@ class MidiCueEditor : public CueEditor void stop_canvas_autoscroll (); sigc::connection _update_connection; - PBD::ScopedConnection track_connection; + PBD::ScopedConnectionList object_connections; void maybe_update (); + void trigger_prop_change (PBD::PropertyChange const &); + + void unset (); void visual_changer (const VisualChange&); void bindings_changed (); @@ -217,7 +220,6 @@ class MidiCueEditor : public CueEditor void data_captured (Temporal::timecnt_t); bool idle_data_captured (); std::atomic idle_update_queued; - PBD::ScopedConnectionList capture_connections; Temporal::timecnt_t data_capture_duration; }; diff --git a/gtk2_ardour/midi_cue_view.cc b/gtk2_ardour/midi_cue_view.cc index 374b39a15a..51300ad131 100644 --- a/gtk2_ardour/midi_cue_view.cc +++ b/gtk2_ardour/midi_cue_view.cc @@ -95,7 +95,7 @@ MidiCueView::set_height (double h) double automation_height = h - note_area_height - velocity_height; event_rect->set (ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, note_area_height)); - midi_context().set_size (ArdourCanvas::COORD_MAX, note_area_height); + midi_context().set_size (midi_context().width(), note_area_height); velocity_base->set_position (ArdourCanvas::Duple (0., note_area_height)); velocity_base->set (ArdourCanvas::Rect (0., 0., ArdourCanvas::COORD_MAX, velocity_height)); diff --git a/gtk2_ardour/midi_view.cc b/gtk2_ardour/midi_view.cc index deb9aa159d..5e0329a57d 100644 --- a/gtk2_ardour/midi_view.cc +++ b/gtk2_ardour/midi_view.cc @@ -236,8 +236,18 @@ MidiView::set_model (std::shared_ptr m) assert (_midi_track); assert (_midi_region); + clear_events (); + connections_requiring_model.drop_connections (); + _model = m; + if (!_model) { + std::cerr << "no model!\n"; + return; + } + + std::cerr << "model set to " << _model << std::endl; + //set_height (trackview.current_height()); #warning paul pianorule needs these fixed @@ -251,8 +261,6 @@ MidiView::set_model (std::shared_ptr m) reset_width_dependent_items (_pixel_width); */ - connections_requiring_model.drop_connections (); - _model->ContentsChanged.connect (connections_requiring_model, invalidator (*this), std::bind (&MidiView::model_changed, this), gui_context()); _midi_track->playback_filter().ChannelModeChanged.connect (connections_requiring_model, invalidator (*this), @@ -849,9 +857,8 @@ MidiView::clear_events () { // clear selection without signaling or trying to change state of event objects _selection.clear (); - clear_ghost_events (); - + /* This will delete all the NoteBase* in the _events map */ _note_group->clear (true); _events.clear(); _patch_changes.clear(); @@ -863,8 +870,6 @@ void MidiView::display_model (std::shared_ptr model) { set_model (_model); - /* Don't signal as nobody else needs to know until selection has been altered. */ - clear_events(); model_changed (); } @@ -1099,12 +1104,14 @@ MidiView::model_changed() return; } - if (!_model) { - return; + for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) { + _optimization_iterator->second->invalidate (); } - for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) { - _optimization_iterator->second->invalidate(); + /* note that _optimization_iterator now points to _events.end() */ + + if (!_model) { + return; } bool empty_when_starting = _events.empty(); @@ -1588,6 +1595,7 @@ void MidiView::end_write() { if (_active_notes) { + std::cerr << "active notes deleted in end_write\n"; for (unsigned i = 0; i < 128; ++i) { delete _active_notes[i]; } @@ -2271,7 +2279,7 @@ MidiView::clear_selection_internal () DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n"); for (auto & sel : _selection) { - sel->set_selected(false); + sel->set_selected (false); sel->hide_velocity(); ghost_sync_selection (sel); } diff --git a/gtk2_ardour/trigger_page.cc b/gtk2_ardour/trigger_page.cc index defd01699d..f1d4c0243b 100644 --- a/gtk2_ardour/trigger_page.cc +++ b/gtk2_ardour/trigger_page.cc @@ -419,23 +419,7 @@ TriggerPage::rec_enable_changed (Trigger const * trigger) _midi_trig_box.set_trigger (ref); _midi_trig_box.show (); - _midi_editor->set_box (trigger->boxptr()); - - Stripable* st = dynamic_cast (box.owner()); - assert (st); - std::shared_ptr mt = std::dynamic_pointer_cast (st->shared_from_this()); - assert (mt); - _midi_editor->set_track (mt); - - if (trigger->the_region()) { - - std::shared_ptr mr = std::dynamic_pointer_cast (trigger->the_region()); - - if (mr) { - _midi_editor->set_region (mr); - } - } - + _midi_editor->set (ref); _midi_editor->viewport().show (); } @@ -475,19 +459,7 @@ TriggerPage::selection_changed () _midi_trig_box.set_trigger (ref); _midi_trig_box.show (); - std::shared_ptr mt = std::dynamic_pointer_cast (entry->strip().stripable()); - assert (mt); - _midi_editor->set_track (mt); - - if (trigger->the_region()) { - - std::shared_ptr mr = std::dynamic_pointer_cast (trigger->the_region()); - - if (mr) { - _midi_editor->set_region (mr); - } - } - + _midi_editor->set (ref); _midi_editor->viewport().show (); } diff --git a/gtk2_ardour/view_background.h b/gtk2_ardour/view_background.h index 80d59d84e5..1a11555419 100644 --- a/gtk2_ardour/view_background.h +++ b/gtk2_ardour/view_background.h @@ -41,6 +41,7 @@ class ViewBackground virtual ~ViewBackground (); virtual double height() const { return 0.; } + virtual double width() const { return 0.; } virtual double contents_height() const { return 0.; } /** @return y position, or -1 if hidden */ diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 25eadacb0a..c30966c1fe 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -127,6 +127,7 @@ TriggerBox::all_trigger_props() all.add(Properties::patch_change); all.add(Properties::channel_map); all.add(Properties::used_channels); + all.add(Properties::region); return all; } @@ -313,7 +314,6 @@ Trigger::arm () void Trigger::disarm () { - _box.disarm (); _armed = false; ArmChanged(); /* EMIT SIGNAL */ TriggerArmChanged (this); @@ -1965,10 +1965,12 @@ AudioTrigger::captured (SlotArmInfo& ai, BufferSet&) delete &ai; // XXX delete is not RT-safe _box.queue_explict (index()); - TriggerBox::worker->request_build_source (this); _armed = false; ArmChanged(); /* EMIT SIGNAL */ + TriggerArmChanged (this); + + TriggerBox::worker->request_build_source (this, timecnt_t (data.length)); } int @@ -2438,6 +2440,7 @@ void MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs) { if (ai.midi_buf->size() == 0) { + std::cerr << "nothing recorded\n"; _armed = false; ArmChanged(); /* EMIT SIGNAL */ delete &ai; @@ -2468,10 +2471,13 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs) _box.queue_explict (index()); /* Meanwhile, build a new source and region from the data now in rt_midibuffer */ - TriggerBox::worker->request_build_source (this); + std::cerr << "request source ...\n"; _armed = false; ArmChanged(); /* EMIT SIGNAL */ + TriggerArmChanged (this); + + TriggerBox::worker->request_build_source (this, timecnt_t (data_length)); } void @@ -2946,6 +2952,8 @@ MIDITrigger::set_region_in_worker_thread_from_capture (std::shared_ptr r { assert (r); + std::cerr << "SRIWTFC " << r->name() << std::endl; + std::shared_ptr mr = std::dynamic_pointer_cast (r); if (!mr) { @@ -2966,7 +2974,8 @@ MIDITrigger::set_region_in_worker_thread_from_capture (std::shared_ptr r /* This is being used as a kind of shorthand for "everything" which is pretty stupid */ - send_property_change (ARDOUR::Properties::name); + std::cerr << "send region change\n"; + send_property_change (ARDOUR::Properties::region); return 0; } @@ -3597,10 +3606,16 @@ TriggerBox::disarm_all () void TriggerBox::finish_recording (BufferSet& bufs) { + std::cerr << "FR!\n"; SlotArmInfo* ai = _arm_info.load(); assert (ai); + + /* This transfers responsibility for the SlotArmInfo object to the + trigger + */ ai->slot.captured (*ai, bufs); _arm_info = nullptr; + _record_state = Disabled; } void