From 2f7f313f6dc3c63496cd9eeee40ced10a36e9427 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 29 Jun 2022 08:09:12 -0600 Subject: [PATCH] MIDI combine (basically operational) May need some tweaks to address notes that are cut off by the end of the region --- gtk2_ardour/editor_selection.cc | 12 +++---- gtk2_ardour/route_time_axis.cc | 7 ----- libs/ardour/ardour/midi_playlist.h | 3 ++ libs/ardour/ardour/midi_region.h | 2 ++ libs/ardour/ardour/playlist.h | 4 +-- libs/ardour/midi_playlist.cc | 50 ++++++++++++++++++++++++++++++ libs/ardour/midi_region.cc | 42 +++++++++++++++++++++++++ 7 files changed, 103 insertions(+), 17 deletions(-) diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index a5b23d5052..4718515405 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -1518,14 +1518,10 @@ Editor::sensitize_the_right_region_actions (bool because_canvas_crossing) _region_actions->get_action("show-region-list-editor")->set_sensitive (false); _region_actions->get_action("show-region-properties")->set_sensitive (false); _region_actions->get_action("rename-region")->set_sensitive (false); - if (have_audio) { - /* XXX need to check whether there is than 1 per - playlist, because otherwise this makes no sense. - */ - _region_actions->get_action("combine-regions")->set_sensitive (true); - } else { - _region_actions->get_action("combine-regions")->set_sensitive (false); - } + /* XXX need to check whether there is than 1 per + playlist, because otherwise this makes no sense. + */ + _region_actions->get_action("combine-regions")->set_sensitive (true); } else if (rs.size() == 1) { _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false); _region_actions->get_action("close-region-gaps")->set_sensitive (false); diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index eb8d524e12..194a5ffc41 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -2541,13 +2541,6 @@ void add_region_to_list (RegionView* rv, RegionList* l) RegionView* RouteTimeAxisView::combine_regions () { - /* as of may 2011, we do not offer uncombine for MIDI tracks - */ - - if (!is_audio_track()) { - return 0; - } - if (!_view) { return 0; } diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h index ee781fadc2..5b201a4ba2 100644 --- a/libs/ardour/ardour/midi_playlist.h +++ b/libs/ardour/ardour/midi_playlist.h @@ -84,6 +84,9 @@ public: std::set contained_automation(); + boost::shared_ptr combine (const RegionList&); + void uncombine (boost::shared_ptr); + protected: void remove_dependents (boost::shared_ptr region); void region_going_away (boost::weak_ptr region); diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 3861658134..4c64ac147c 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -82,6 +82,8 @@ class LIBARDOUR_API MidiRegion : public Region uint32_t chan_n = 0, NoteMode mode = Sustained) const; + void merge (boost::shared_ptr); + XMLNode& state () const; int set_state (const XMLNode&, int version); diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 69c2283c61..a71d921025 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -186,8 +186,8 @@ public: void duplicate_range (TimelineRange&, float times); void duplicate_ranges (std::list&, float times); void nudge_after (timepos_t const & start, timecnt_t const & distance, bool forwards); - boost::shared_ptr combine (const RegionList&); - void uncombine (boost::shared_ptr); + virtual boost::shared_ptr combine (const RegionList&); + virtual void uncombine (boost::shared_ptr); void fade_range (std::list&); void remove_gaps (timecnt_t const & gap_threshold, timecnt_t const & leave_gap, boost::function gap_callback); diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 28270c9222..918ff20c5b 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -35,6 +35,7 @@ #include "ardour/midi_source.h" #include "ardour/midi_state_tracker.h" #include "ardour/region_factory.h" +#include "ardour/region_sorters.h" #include "ardour/rt_midibuffer.h" #include "ardour/session.h" #include "ardour/tempo.h" @@ -374,3 +375,52 @@ MidiPlaylist::rendered () { return &_rendered; } + +boost::shared_ptr +MidiPlaylist::combine (RegionList const & rl) +{ + RegionWriteLock rwl (this, true); + + std::cerr << "combine " << rl.size() << " regions\n"; + + if (rl.size() < 2) { + return boost::shared_ptr (); + } + + RegionList sorted (rl); + sorted.sort (RegionSortByLayerAndPosition()); + + boost::shared_ptr first = sorted.front(); + RegionList::const_iterator i = sorted.begin(); + ++i; + +#ifndef NDEBUG + for (auto const & r : rl) { + assert (boost::dynamic_pointer_cast (r)); + } +#endif + + boost::shared_ptr new_region = boost::dynamic_pointer_cast (RegionFactory::create (first, true, true, &rwl.thawlist)); + + timepos_t pos (first->position()); + + remove_region_internal (first, rwl.thawlist); + std::cerr << "Remove " << first->name() << std::endl; + + while (i != sorted.end()) { + new_region->merge (boost::dynamic_pointer_cast (*i)); + remove_region_internal (*i, rwl.thawlist); + std::cerr << "Remove " << (*i)->name() << std::endl; + ++i; + } + + add_region_internal (new_region, pos, rwl.thawlist); + std::cerr << "Add " << new_region->name() << std::endl; + + return new_region; +} + +void +MidiPlaylist::uncombine (boost::shared_ptr r) +{ +} diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index 858e4475e7..c99456477d 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -539,3 +539,45 @@ MidiRegion::set_name (const std::string& str) return Region::set_name (str); } + +void +MidiRegion::merge (boost::shared_ptr other_region) +{ + Temporal::Beats last_event_time; + + { + Source::WriterLock lm (midi_source(0)->mutex()); + boost::shared_ptr other = other_region->model(); + boost::shared_ptr self = model(); + + Temporal::Beats other_region_start (other_region->start().beats()); + Temporal::Beats other_region_end ((other_region->start() + other_region->length()).beats()); + + midi_source (0)->mark_streaming_midi_write_started (lm, Sustained); + + for (Evoral::Sequence::const_iterator e = other->begin(); e != other->end(); ++e) { + + if (e->time() < other_region_start) { + continue; + } + + if (e->time() >= other_region_end) { + break; + } + + Evoral::Event ev (*e, true); + + Temporal::Beats abs_time = other_region->source_beats_to_absolute_time (ev.time()).beats (); + Temporal::Beats srt = absolute_time_to_source_beats (timepos_t (abs_time)); + ev.set_time (srt); + self->append (ev, Evoral::next_event_id()); + last_event_time = abs_time; + } + + midi_source (0)->mark_streaming_write_completed (lm); + } + + Temporal::Beats len = last_event_time - position().beats(); + + set_length (timecnt_t (len, position())); +}