From 84f0dceefb2e4079e1f015a17e538f0cd795ba2e Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Wed, 16 Oct 2013 22:25:35 +0100 Subject: [PATCH] Port 'Cut time' code from Mixbus Copy the 'Cut time' code from Mixbus, making a few obvious fixes to work in A3 (e.g. nframes_t => framepos_t / framecnt_t). Seems to work to move & remove markers, tempo & meter markers, and regions on selected tracks. Still TODO: - use existing A3 'Insert time' dialogue - make it respect 'No selection = all tracks' - rename the command to something like 'Remove time' or 'Delete time': 'Cut' sounds to me as if the removed range should end up on the clipboard ready to be pasted somewhere, which of course it doesn't. --- gtk2_ardour/ardour.menus.in | 1 + gtk2_ardour/editor.h | 3 + gtk2_ardour/editor_actions.cc | 4 + gtk2_ardour/editor_ops.cc | 169 ++++++++++++++++++++++++++++++++++ libs/ardour/ardour/tempo.h | 3 +- libs/ardour/tempo.cc | 52 +++++++++++ 6 files changed, 231 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 3364557000..35f83025c6 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -364,6 +364,7 @@ + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 9f37f9d1f2..e0737626ac 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1261,6 +1261,9 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void do_insert_time (); void insert_time (framepos_t, framecnt_t, Editing::InsertTimeOption, bool, bool, bool, bool, bool, bool); + void do_cut_time (); + void cut_time (framepos_t pos, framecnt_t distance, Editing::InsertTimeOption opt, bool ignore_music_glue, bool markers_too, bool tempo_too); + void tab_to_transient (bool forward); void set_tempo_from_region (); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 322a823ff2..544815111e 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -398,6 +398,10 @@ Editor::register_actions () act = reg_sens (editor_actions, "insert-time", _("Insert Time"), (sigc::mem_fun(*this, &Editor::do_insert_time))); ActionManager::track_selection_sensitive_actions.push_back (act); + act = ActionManager::register_action (editor_actions, "cut-time", _("Cut Time"), (mem_fun(*this, &Editor::do_cut_time))); + ActionManager::session_sensitive_actions.push_back (act); + ActionManager::track_selection_sensitive_actions.push_back (act); + act = reg_sens (editor_actions, "toggle-track-active", _("Toggle Active"), (sigc::mem_fun(*this, &Editor::toggle_tracks_active))); ActionManager::track_selection_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 116bc4571d..185ab5f1a8 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -7018,6 +7018,175 @@ Editor::insert_time ( commit_reversible_command (); } } +void +Editor::do_cut_time () +{ + if (selection->tracks.empty()) { + return; + } + + framepos_t pos = get_preferred_edit_position (); + ArdourDialog d (*this, _("Cut Time")); + VButtonBox button_box; + VBox option_box; + + CheckButton glue_button (_("Move Glued Regions")); + CheckButton marker_button (_("Move Markers")); + CheckButton tempo_button (_("Move Tempo & Meters")); + AudioClock clock ("insertTimeClock", true, X_("InsertTimeClock"), true, true, true); + HBox clock_box; + + clock.set (0); + clock.set_session (_session); + clock.set_bbt_reference (pos); + + clock_box.pack_start (clock, false, true); + + option_box.set_spacing (6); + option_box.pack_start (button_box, false, false); + option_box.pack_start (glue_button, false, false); + option_box.pack_start (marker_button, false, false); + option_box.pack_start (tempo_button, false, false); + + d.get_vbox()->set_border_width (12); + d.get_vbox()->pack_start (clock_box, false, false); + d.get_vbox()->pack_start (option_box, false, false); + + option_box.show (); + button_box.show (); + glue_button.show (); + clock.show_all(); + clock_box.show (); + marker_button.show (); + tempo_button.show (); + + d.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK); + d.show (); + + int response = d.run (); + + if (response != RESPONSE_OK) { + return; + } + + framecnt_t distance = clock.current_duration (pos); + + if (distance == 0) { + return; + } + + cut_time (pos, distance, SplitIntersected, glue_button.get_active(), marker_button.get_active(), tempo_button.get_active()); +} + +void +Editor::cut_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt, + bool ignore_music_glue, bool markers_too, bool tempo_too) +{ + bool commit = false; + + if (Config->get_edit_mode() == Lock) { + error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg; + return; + } + + begin_reversible_command (_("cut time")); + + for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { + /* regions */ + boost::shared_ptr pl = (*x)->playlist(); + + if (pl) { + + XMLNode &before = pl->get_state(); + + std::list rl; + AudioRange ar(pos, pos+frames, 0); + rl.push_back(ar); + pl->cut (rl); + pl->shift (pos, -frames, true, ignore_music_glue); + + XMLNode &after = pl->get_state(); + + _session->add_command (new MementoCommand (*pl, &before, &after)); + commit = true; + } + + /* automation */ + RouteTimeAxisView* rtav = dynamic_cast (*x); + if (rtav) { + rtav->route ()->shift (pos, -frames); + commit = true; + } + } + + std::list loc_kill_list; + + /* markers */ + if (markers_too) { + bool moved = false; + XMLNode& before (_session->locations()->get_state()); + Locations::LocationList copy (_session->locations()->list()); + + for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) { + + if (!(*i)->is_mark()) { //range; have to handle both start and end + if ((*i)->end() >= pos + && (*i)->end() < pos+frames + && (*i)->start() >= pos + && (*i)->end() < pos+frames) { //range is completely enclosed; kill it + moved = true; + loc_kill_list.push_back(*i); + } else { //ony start or end is included, try to do the right thing + if ((*i)->end() >= pos && (*i)->end() < pos+frames) { + (*i)->set_end (pos); //bring the end to the cut + moved = true; + } else if ((*i)->end() >= pos) { + (*i)->set_end ((*i)->end()-frames); //slip the end marker back + moved = true; + } + if ((*i)->start() >= pos && (*i)->start() < pos+frames) { + (*i)->set_start (pos); //bring the start marker to the beginning of the cut + moved = true; + } else if ((*i)->start() >= pos) { + (*i)->set_start ((*i)->start() -frames); //slip the end marker back + moved = true; + } + } + } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) { + loc_kill_list.push_back(*i); + moved = true; + } else if ((*i)->start() >= pos) { + (*i)->set_start ((*i)->start() -frames); + moved = true; + } + } + + for (list::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) { + _session->locations()->remove( *i ); + } + + if (moved) { + XMLNode& after (_session->locations()->get_state()); + _session->add_command (new MementoCommand(*_session->locations(), &before, &after)); + commit = true; + } + } + + if (tempo_too) { + XMLNode& before (_session->tempo_map().get_state()); + + if (_session->tempo_map().cut_time (pos, frames) ) { + XMLNode& after (_session->tempo_map().get_state()); + _session->add_command (new MementoCommand(_session->tempo_map(), &before, &after)); + commit = true; + } + } + + if (commit) { + commit_reversible_command (); + } +} void Editor::fit_selection () diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 9e6a0c17bd..24a3f53fe9 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -323,7 +323,8 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible void change_initial_tempo (double bpm, double note_type); void insert_time (framepos_t, framecnt_t); - + bool cut_time (framepos_t where, framecnt_t amount); //returns true if anything was moved + int n_tempos () const; int n_meters () const; diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index c5f7272b7e..b9ad1be682 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -1900,6 +1900,58 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) PropertyChanged (PropertyChange ()); } +bool +TempoMap::cut_time (framepos_t where, framecnt_t amount) +{ + bool moved = false; + + std::list metric_kill_list; + + TempoSection* last_tempo = NULL; + MeterSection* last_meter = NULL; + { + Glib::Threads::RWLock::WriterLock lm (lock); + for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) { + if ((*i)->frame() >= where && (*i)->frame() < where+amount) { + metric_kill_list.push_back(*i); + TempoSection *lt = dynamic_cast (*i); + if (lt) + last_tempo = lt; + MeterSection *lm = dynamic_cast (*i); + if (lm) + last_meter = lm; + } + else if ((*i)->frame() >= where) { + (*i)->set_frame ((*i)->frame() - amount); + moved = true; + } + } + + //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct + if (last_tempo) { + metric_kill_list.remove(last_tempo); + last_tempo->set_frame(where); + moved = true; + } + if (last_meter) { + metric_kill_list.remove(last_meter); + last_meter->set_frame(where); + moved = true; + } + + //remove all the remaining metrics + for (std::list::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) { + metrics.remove(*i); + moved = true; + } + + if (moved) { + recompute_map (true); + } + } + PropertyChanged (PropertyChange ()); + return moved; +} /** Add some (fractional) beats to a session frame position, and return the result in frames. * pos can be -ve, if required.