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.
This commit is contained in:
Colin Fletcher 2013-10-16 22:25:35 +01:00 committed by Ben Loftis
parent 17546f47b6
commit 84f0dceefb
6 changed files with 231 additions and 1 deletions

View File

@ -364,6 +364,7 @@
<menuitem action='track-mute-toggle'/>
<separator/>
<menuitem action='insert-time'/>
<menuitem action='cut-time'/>
<menuitem action="move-selected-tracks-up"/>
<menuitem action="move-selected-tracks-down"/>
<menu action='TrackHeightMenu'>

View File

@ -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 ();

View File

@ -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);

View File

@ -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<Playlist> pl = (*x)->playlist();
if (pl) {
XMLNode &before = pl->get_state();
std::list<AudioRange> 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<Playlist> (*pl, &before, &after));
commit = true;
}
/* automation */
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
if (rtav) {
rtav->route ()->shift (pos, -frames);
commit = true;
}
}
std::list<Location*> 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<Location*>::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<Locations>(*_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<TempoMap>(_session->tempo_map(), &before, &after));
commit = true;
}
}
if (commit) {
commit_reversible_command ();
}
}
void
Editor::fit_selection ()

View File

@ -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;

View File

@ -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<MetricSection*> 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<TempoSection*> (*i);
if (lt)
last_tempo = lt;
MeterSection *lm = dynamic_cast<MeterSection*> (*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<MetricSection*>::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.