*/
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
#include <set>
#include <fstream>
#include <algorithm>
#include <string>
#include <climits>
+#include <boost/lexical_cast.hpp>
#include "pbd/failed_constructor.h"
-#include "pbd/stl_delete.h"
+#include "pbd/stateful_diff_command.h"
#include "pbd/xml++.h"
-#include "pbd/stacktrace.h"
#include "ardour/debug.h"
#include "ardour/playlist.h"
using namespace ARDOUR;
using namespace PBD;
+namespace ARDOUR {
+namespace Properties {
+PBD::PropertyDescriptor<bool> regions;
+}
+}
+
struct ShowMeTheList {
ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
~ShowMeTheList () {
}
};
+void
+Playlist::make_property_quarks ()
+{
+ Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
+ DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
+}
+
+RegionListProperty::RegionListProperty (Playlist& pl)
+ : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
+ , _playlist (pl)
+{
+}
+
+boost::shared_ptr<Region>
+RegionListProperty::lookup_id (const ID& id)
+{
+ boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
+
+ if (!ret) {
+ ret = RegionFactory::region_by_id (id);
+ }
+
+ return ret;
+}
+
+RegionListProperty*
+RegionListProperty::copy_for_history () const
+{
+ RegionListProperty* copy = new RegionListProperty (_playlist);
+ /* this is all we need */
+ copy->_change = _change;
+ return copy;
+}
+
+void
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo, Command* cmd) const
+{
+ if (changed()) {
+ /* list of the removed/added regions since clear_history() was last called */
+ RegionListProperty* a = copy_for_history ();
+
+ /* the same list, but with removed/added lists swapped (for undo purposes) */
+ RegionListProperty* b = copy_for_history ();
+ b->invert_changes ();
+
+ if (cmd) {
+ /* whenever one of the regions emits DropReferences, make sure
+ that the Destructible we've been told to notify hears about
+ it. the Destructible is likely to be the Command being built
+ with this diff().
+ */
+
+ for (set<boost::shared_ptr<Region> >::iterator i = a->change().added.begin(); i != a->change().added.end(); ++i) {
+ (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
+ }
+ }
+
+ undo.add (b);
+ redo.add (a);
+ }
+}
Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
: SessionObject(sess, nom)
+ , regions (*this)
, _type(type)
{
init (hide);
first_set_state = false;
_name = nom;
+ _set_sort_id ();
}
Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
: SessionObject(sess, "unnamed playlist")
- , _type(type)
+ , regions (*this)
+ , _type(type)
+
{
+#ifndef NDEBUG
const XMLProperty* prop = node.property("type");
assert(!prop || DataType(prop->value()) == _type);
+#endif
init (hide);
_name = "unnamed"; /* reset by set_state */
+ _set_sort_id ();
/* set state called by derived class */
}
Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
: SessionObject(other->_session, namestr)
+ , regions (*this)
, _type(other->_type)
- , _orig_diskstream_id(other->_orig_diskstream_id)
+ , _orig_diskstream_id (other->_orig_diskstream_id)
{
init (hide);
freeze_length = other->freeze_length;
}
-Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
+Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
: SessionObject(other->_session, str)
+ , regions (*this)
, _type(other->_type)
- , _orig_diskstream_id(other->_orig_diskstream_id)
+ , _orig_diskstream_id (other->_orig_diskstream_id)
{
RegionLock rlock2 (const_cast<Playlist*> (other.get()));
- nframes_t end = start + cnt - 1;
+ framepos_t end = start + cnt - 1;
init (hide);
in_set_state++;
- for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
+ for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
boost::shared_ptr<Region> region;
boost::shared_ptr<Region> new_region;
- nframes_t offset = 0;
- nframes_t position = 0;
- nframes_t len = 0;
+ frameoffset_t offset = 0;
+ framepos_t position = 0;
+ framecnt_t len = 0;
string new_name;
OverlapType overlap;
break;
}
- _session.region_name (new_name, region->name(), false);
+ RegionFactory::region_name (new_name, region->name(), false);
+
+ PropertyList plist;
- new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
+ plist.add (Properties::start, region->start() + offset);
+ plist.add (Properties::length, len);
+ plist.add (Properties::name, new_name);
+ plist.add (Properties::layer, region->layer());
+
+ new_region = RegionFactory::RegionFactory::create (region, plist);
add_region_internal (new_region, position);
}
void
Playlist::init (bool hide)
{
+ add_property (regions);
+ _xml_node_name = X_("Playlist");
+
g_atomic_int_set (&block_notifications, 0);
g_atomic_int_set (&ignore_state_changes, 0);
- pending_modified = false;
+ pending_contents_change = false;
pending_length = false;
+ pending_layering = false;
first_set_state = true;
_refcnt = 0;
_hidden = hide;
_shuffling = false;
_nudging = false;
in_set_state = 0;
+ in_update = false;
_edit_mode = Config->get_edit_mode();
in_flush = false;
in_partition = false;
freeze_length = 0;
_explicit_relayering = false;
- Modified.connect (*this, boost::bind (&Playlist::mark_session_dirty, this));
+ _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
+ _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
+
+ ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
}
Playlist::~Playlist ()
/* GoingAway must be emitted by derived classes */
}
+void
+Playlist::_set_sort_id ()
+{
+ /*
+ Playlists are given names like <track name>.<id>
+ or <track name>.<edit group name>.<id> where id
+ is an integer. We extract the id and sort by that.
+ */
+
+ size_t dot_position = _name.val().find_last_of(".");
+
+ if (dot_position == string::npos) {
+ _sort_id = 0;
+ } else {
+ string t = _name.val().substr(dot_position + 1);
+
+ try {
+ _sort_id = boost::lexical_cast<int>(t);
+ }
+
+ catch (boost::bad_lexical_cast e) {
+ _sort_id = 0;
+ }
+ }
+}
+
bool
Playlist::set_name (const string& str)
{
if (_refcnt > 2) {
return false;
- } else {
- return SessionObject::set_name(str);
- }
+ }
+
+ bool ret = SessionObject::set_name(str);
+ if (ret) {
+ _set_sort_id ();
+ }
+ return ret;
}
/***********************************************************************
the lock (e.g. to read from the playlist).
***********************************************************************/
+void
+Playlist::begin_undo ()
+{
+ in_update = true;
+ freeze ();
+}
+
+void
+Playlist::end_undo ()
+{
+ thaw ();
+ in_update = false;
+}
+
void
Playlist::freeze ()
{
Playlist::delay_notifications ()
{
g_atomic_int_inc (&block_notifications);
- freeze_length = _get_maximum_extent();
+ freeze_length = _get_extent().second;
}
void
{
if (g_atomic_int_dec_and_test (&block_notifications)) {
flush_notifications ();
+ }
+
+}
+
+void
+Playlist::notify_contents_changed ()
+{
+ if (holding_state ()) {
+ pending_contents_change = true;
+ } else {
+ pending_contents_change = false;
+ ContentsChanged(); /* EMIT SIGNAL */
}
}
void
-Playlist::notify_modified ()
+Playlist::notify_layering_changed ()
{
if (holding_state ()) {
- pending_modified = true;
+ pending_layering = true;
} else {
- pending_modified = false;
- Modified(); /* EMIT SIGNAL */
+ pending_layering = false;
+ LayeringChanged(); /* EMIT SIGNAL */
}
}
{
if (holding_state ()) {
pending_removes.insert (r);
- pending_modified = true;
+ pending_contents_change = true;
pending_length = true;
} else {
/* this might not be true, but we have to act
*/
pending_length = false;
LengthChanged (); /* EMIT SIGNAL */
- pending_modified = false;
+ pending_contents_change = false;
RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
- Modified (); /* EMIT SIGNAL */
+ ContentsChanged (); /* EMIT SIGNAL */
}
}
void
Playlist::notify_region_moved (boost::shared_ptr<Region> r)
{
- Evoral::RangeMove<nframes_t> const move (r->last_position (), r->length (), r->position ());
+ Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
if (holding_state ()) {
} else {
- list< Evoral::RangeMove<nframes_t> > m;
+ list< Evoral::RangeMove<framepos_t> > m;
m.push_back (move);
RangesMoved (m);
}
/* the length change might not be true, but we have to act
as though it could be.
*/
-
+
if (holding_state()) {
pending_adds.insert (r);
- pending_modified = true;
+ pending_contents_change = true;
pending_length = true;
} else {
+ r->clear_history ();
pending_length = false;
LengthChanged (); /* EMIT SIGNAL */
- pending_modified = false;
+ pending_contents_change = false;
RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
- Modified (); /* EMIT SIGNAL */
+ ContentsChanged (); /* EMIT SIGNAL */
}
}
} else {
pending_length = false;
LengthChanged(); /* EMIT SIGNAL */
- pending_modified = false;
- Modified (); /* EMIT SIGNAL */
+ pending_contents_change = false;
+ ContentsChanged (); /* EMIT SIGNAL */
}
}
{
set<boost::shared_ptr<Region> > dependent_checks_needed;
set<boost::shared_ptr<Region> >::iterator s;
- uint32_t n = 0;
+ uint32_t regions_changed = false;
+ bool check_length = false;
+ framecnt_t old_length = 0;
if (in_flush) {
return;
in_flush = true;
+ if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
+ regions_changed = true;
+ if (!pending_length) {
+ old_length = _get_extent ().second;
+ check_length = true;
+ }
+ }
+
/* we have no idea what order the regions ended up in pending
bounds (it could be based on selection order, for example).
so, to preserve layering in the "most recently moved is higher"
if (_session.config.get_layer_model() == MoveAddHigher) {
timestamp_layer_op (*r);
}
-
- pending_length = true;
dependent_checks_needed.insert (*r);
-
- n++;
}
for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
remove_dependents (*s);
+ // cerr << _name << " sends RegionRemoved\n";
RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
- n++;
}
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
- RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+ // cerr << _name << " sends RegionAdded\n";
+ /* don't emit RegionAdded signal until relayering is done,
+ so that the region is fully setup by the time
+ anyone hear's that its been added
+ */
dependent_checks_needed.insert (*s);
- n++;
}
- if ((freeze_length != _get_maximum_extent()) || pending_length) {
- pending_length = 0;
+ if (check_length) {
+ if (old_length != _get_extent().second) {
+ pending_length = true;
+ // cerr << _name << " length has changed\n";
+ }
+ }
+
+ if (pending_length || (freeze_length != _get_extent().second)) {
+ pending_length = false;
+ // cerr << _name << " sends LengthChanged\n";
LengthChanged(); /* EMIT SIGNAL */
- n++;
}
- if (n || pending_modified) {
+ if (regions_changed || pending_contents_change) {
if (!in_set_state) {
relayer ();
}
- pending_modified = false;
- Modified (); /* EMIT SIGNAL */
+ pending_contents_change = false;
+ // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
+ ContentsChanged (); /* EMIT SIGNAL */
+ // cerr << _name << "done contents change @ " << get_microseconds() << endl;
}
+ for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
+ (*s)->clear_history ();
+ RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+ }
+
for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
check_dependents (*s, false);
}
if (!pending_range_moves.empty ()) {
+ // cerr << _name << " sends RangesMoved\n";
RangesMoved (pending_range_moves);
}
+
+ clear_pending ();
+
+ in_flush = false;
+}
+void
+Playlist::clear_pending ()
+{
pending_adds.clear ();
pending_removes.clear ();
pending_bounds.clear ();
pending_range_moves.clear ();
-
- in_flush = false;
+ pending_contents_change = false;
+ pending_length = false;
}
/*************************************************************
*************************************************************/
void
-Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool auto_partition)
+Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
{
RegionLock rlock (this);
times = fabs (times);
int itimes = (int) floor (times);
- nframes_t pos = position;
+ framepos_t pos = position;
if (times == 1 && auto_partition){
- partition(pos, (nframes_t) (pos + region->length()), true);
+ partition(pos, (pos + region->length()), true);
}
if (itimes >= 1) {
pos += region->length();
}
- nframes_t length = 0;
+ framecnt_t length = 0;
if (floor (times) != times) {
- length = (nframes_t) floor (region->length() * (times - floor (times)));
+ length = (framecnt_t) floor (region->length() * (times - floor (times)));
string name;
- _session.region_name (name, region->name(), false);
- boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
- add_region_internal (sub, pos);
+ RegionFactory::region_name (name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::start, region->start());
+ plist.add (Properties::length, length);
+ plist.add (Properties::name, name);
+ plist.add (Properties::layer, region->layer());
+
+ boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+ add_region_internal (sub, pos);
+ }
}
possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
}
bool
-Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
+Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
{
if (region->data_type() != _type){
return false;
RegionSortByPosition cmp;
- nframes_t old_length = 0;
+ framecnt_t old_length = 0;
if (!holding_state()) {
- old_length = _get_maximum_extent();
+ old_length = _get_extent().second;
}
if (!first_set_state) {
possibly_splice_unlocked (position, region->length(), region);
- if (!holding_state () && !in_set_state) {
- /* layers get assigned from XML state */
+ if (!holding_state ()) {
+ /* layers get assigned from XML state, and are not reset during undo/redo */
relayer ();
}
check_dependents (region, false);
- if (old_length != _get_maximum_extent()) {
+ if (old_length != _get_extent().second) {
notify_length_changed ();
}
}
- region->StateChanged.connect (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
+ region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
return true;
}
void
-Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
+Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
{
RegionLock rlock (this);
_splicing = old_sp;
- possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
+ possibly_splice_unlocked (pos, old->length() - newr->length());
}
void
Playlist::remove_region_internal (boost::shared_ptr<Region> region)
{
RegionList::iterator i;
- nframes_t old_length = 0;
+ framecnt_t old_length = 0;
+ int ret = -1;
if (!holding_state()) {
- old_length = _get_maximum_extent();
+ old_length = _get_extent().second;
}
if (!in_set_state) {
region->set_playlist (boost::weak_ptr<Playlist>());
}
+ /* XXX should probably freeze here .... */
+
for (i = regions.begin(); i != regions.end(); ++i) {
if (*i == region) {
- nframes_t pos = (*i)->position();
- nframes64_t distance = (*i)->length();
+ framepos_t pos = (*i)->position();
+ framecnt_t distance = (*i)->length();
regions.erase (i);
possibly_splice_unlocked (pos, -distance);
if (!holding_state ()) {
- relayer ();
+ relayer ();
remove_dependents (region);
- if (old_length != _get_maximum_extent()) {
+ if (old_length != _get_extent().second) {
notify_length_changed ();
}
}
notify_region_removed (region);
- return 0;
+ ret = 0;
+ break;
}
}
+ /* XXX and thaw ... */
-
- return -1;
+ return ret;
}
void
}
void
-Playlist::partition (nframes_t start, nframes_t end, bool cut)
+Playlist::partition (framepos_t start, framepos_t end, bool cut)
{
RegionList thawlist;
partition_internal (start, end, cut, thawlist);
for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
- (*i)->thaw ("separation");
+ (*i)->resume_property_changes ();
}
}
void
-Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
+Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
{
RegionList new_regions;
string new_name;
RegionList::iterator tmp;
OverlapType overlap;
- nframes_t pos1, pos2, pos3, pos4;
+ framepos_t pos1, pos2, pos3, pos4;
in_partition = true;
get operated on as well.
*/
- RegionList copy = regions;
+ RegionList copy = regions.rlist();
for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
if (!cutting) {
/* "middle" ++++++ */
- _session.region_name (new_name, current->name(), false);
- region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
- regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
+ RegionFactory::region_name (new_name, current->name(), false);
+
+ PropertyList plist;
+
+ plist.add (Properties::start, current->start() + (pos2 - pos1));
+ plist.add (Properties::length, pos3 - pos2);
+ plist.add (Properties::name, new_name);
+ plist.add (Properties::layer, regions.size());
+ plist.add (Properties::automatic, true);
+ plist.add (Properties::left_of_split, true);
+ plist.add (Properties::right_of_split, true);
+
+ region = RegionFactory::create (current, plist);
add_region_internal (region, start);
new_regions.push_back (region);
}
/* "end" ====== */
- _session.region_name (new_name, current->name(), false);
- region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
- regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+ RegionFactory::region_name (new_name, current->name(), false);
+
+ PropertyList plist;
+
+ plist.add (Properties::start, current->start() + (pos3 - pos1));
+ plist.add (Properties::length, pos4 - pos3);
+ plist.add (Properties::name, new_name);
+ plist.add (Properties::layer, regions.size());
+ plist.add (Properties::automatic, true);
+ plist.add (Properties::right_of_split, true);
+
+ region = RegionFactory::create (current, plist);
add_region_internal (region, end);
new_regions.push_back (region);
/* "front" ***** */
- current->freeze ();
+ current->suspend_property_changes ();
thawlist.push_back (current);
- current->trim_end (pos2, this);
+ current->cut_end (pos2 - 1, this);
} else if (overlap == OverlapEnd) {
/* end +++++ */
- _session.region_name (new_name, current->name(), false);
- region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
- Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
+ RegionFactory::region_name (new_name, current->name(), false);
+
+ PropertyList plist;
+
+ plist.add (Properties::start, current->start() + (pos2 - pos1));
+ plist.add (Properties::length, pos4 - pos2);
+ plist.add (Properties::name, new_name);
+ plist.add (Properties::layer, regions.size());
+ plist.add (Properties::automatic, true);
+ plist.add (Properties::left_of_split, true);
+
+ region = RegionFactory::create (current, plist);
add_region_internal (region, start);
new_regions.push_back (region);
/* front ****** */
- current->freeze ();
+ current->suspend_property_changes ();
thawlist.push_back (current);
- current->trim_end (pos2, this);
+ current->cut_end (pos2 - 1, this);
} else if (overlap == OverlapStart) {
if (!cutting) {
/* front **** */
- _session.region_name (new_name, current->name(), false);
- region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
- regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+ RegionFactory::region_name (new_name, current->name(), false);
+
+ PropertyList plist;
+
+ plist.add (Properties::start, current->start());
+ plist.add (Properties::length, pos3 - pos1);
+ plist.add (Properties::name, new_name);
+ plist.add (Properties::layer, regions.size());
+ plist.add (Properties::automatic, true);
+ plist.add (Properties::right_of_split, true);
+
+ region = RegionFactory::create (current, plist);
add_region_internal (region, pos1);
new_regions.push_back (region);
/* end */
- current->freeze ();
+ current->suspend_property_changes ();
thawlist.push_back (current);
current->trim_front (pos3, this);
} else if (overlap == OverlapExternal) {
}
boost::shared_ptr<Playlist>
-Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
+Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
{
boost::shared_ptr<Playlist> ret;
boost::shared_ptr<Playlist> pl;
- nframes_t start;
+ framepos_t start;
if (ranges.empty()) {
return boost::shared_ptr<Playlist>();
boost::shared_ptr<Playlist>
Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
{
- boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
return cut_copy (pmf, ranges, result_is_hidden);
}
boost::shared_ptr<Playlist>
Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
{
- boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
return cut_copy (pmf, ranges, result_is_hidden);
}
boost::shared_ptr<Playlist>
-Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
+Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
{
boost::shared_ptr<Playlist> the_copy;
RegionList thawlist;
partition_internal (start, start+cnt-1, true, thawlist);
for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
- (*i)->thaw ("playlist cut");
+ (*i)->resume_property_changes();
}
return the_copy;
}
boost::shared_ptr<Playlist>
-Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
+Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
{
char buf[32];
new_name += '.';
new_name += buf;
- cnt = min (_get_maximum_extent() - start, cnt);
+ cnt = min (_get_extent().second - start, cnt);
return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
}
int
-Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
+Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
{
times = fabs (times);
- nframes_t old_length;
{
RegionLock rl1 (this);
RegionLock rl2 (other.get());
- old_length = _get_maximum_extent();
+ framecnt_t const old_length = _get_extent().second;
int itimes = (int) floor (times);
- nframes_t pos = position;
- nframes_t shift = other->_get_maximum_extent();
+ framepos_t pos = position;
+ framecnt_t const shift = other->_get_extent().second;
layer_t top_layer = regions.size();
while (itimes--) {
/* XXX shall we handle fractional cases at some point? */
- if (old_length != _get_maximum_extent()) {
+ if (old_length != _get_extent().second) {
notify_length_changed ();
}
void
-Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
+Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
{
times = fabs (times);
RegionLock rl (this);
int itimes = (int) floor (times);
- nframes_t pos = position;
+ framepos_t pos = position;
while (itimes--) {
boost::shared_ptr<Region> copy = RegionFactory::create (region);
}
if (floor (times) != times) {
- nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
+ framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
string name;
- _session.region_name (name, region->name(), false);
- boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
- add_region_internal (sub, pos);
+ RegionFactory::region_name (name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::start, region->start());
+ plist.add (Properties::length, length);
+ plist.add (Properties::name, name);
+
+ boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+ add_region_internal (sub, pos);
+ }
}
}
void
-Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue)
+Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
{
RegionLock rlock (this);
- RegionList copy (regions);
+ RegionList copy (regions.rlist());
RegionList fixup;
for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
has to be done separately.
*/
- if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
+ if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
fixup.push_back (*r);
continue;
}
}
void
-Playlist::split (nframes64_t at)
+Playlist::split (framepos_t at)
{
RegionLock rlock (this);
- RegionList copy (regions);
+ RegionList copy (regions.rlist());
/* use a copy since this operation can modify the region list
*/
}
void
-Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
{
RegionLock rl (this);
_split_region (region, playlist_position);
}
void
-Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
{
if (!region->covers (playlist_position)) {
return;
boost::shared_ptr<Region> left;
boost::shared_ptr<Region> right;
- nframes_t before;
- nframes_t after;
+ frameoffset_t before;
+ frameoffset_t after;
string before_name;
string after_name;
before = playlist_position - region->position();
after = region->length() - before;
- _session.region_name (before_name, region->name(), false);
- left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
+ RegionFactory::region_name (before_name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::start, region->start());
+ plist.add (Properties::length, before);
+ plist.add (Properties::name, before_name);
+ plist.add (Properties::left_of_split, true);
+
+ left = RegionFactory::create (region, plist);
+ }
+
+ RegionFactory::region_name (after_name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::start, region->start() + before);
+ plist.add (Properties::length, after);
+ plist.add (Properties::name, after_name);
+ plist.add (Properties::right_of_split, true);
- _session.region_name (after_name, region->name(), false);
- right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
+ right = RegionFactory::create (region, plist);
+ }
add_region_internal (left, region->position());
add_region_internal (right, region->position() + before);
}
void
-Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
if (_splicing || in_set_state) {
/* don't respond to splicing moves or state setting */
}
void
-Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
if (_splicing || in_set_state) {
/* don't respond to splicing moves or state setting */
}
void
-Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
{
RegionLock rl (this);
}
void
-Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
core_splice (at, distance, exclude);
}
void
-Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
_splicing = true;
}
if ((*i)->position() >= at) {
- nframes64_t new_pos = (*i)->position() + distance;
+ framepos_t new_pos = (*i)->position() + distance;
if (new_pos < 0) {
new_pos = 0;
} else if (new_pos >= max_frames - (*i)->length()) {
}
void
-Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
+Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
{
if (in_set_state || _splicing || _nudging || _shuffling) {
return;
}
- if (what_changed & ARDOUR::PositionChanged) {
+ if (what_changed.contains (Properties::position)) {
/* remove it from the list then add it back in
the right place again.
RegionList::iterator i = find (regions.begin(), regions.end(), region);
if (i == regions.end()) {
- warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
- _name, region->name())
- << endmsg;
+ /* the region bounds are being modified but its not currently
+ in the region list. we will use its bounds correctly when/if
+ it is added
+ */
return;
}
regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
}
- if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
+ if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
- nframes64_t delta = 0;
+ frameoffset_t delta = 0;
- if (what_changed & ARDOUR::PositionChanged) {
- delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
+ if (what_changed.contains (Properties::position)) {
+ delta = region->position() - region->last_position();
}
- if (what_changed & ARDOUR::LengthChanged) {
- delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
+ if (what_changed.contains (Properties::length)) {
+ delta += region->length() - region->last_length();
}
if (delta) {
}
void
-Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
+Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
{
boost::shared_ptr<Region> region (weak_region.lock());
}
bool
-Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
{
- Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
+ PropertyChange our_interests;
+ PropertyChange bounds;
+ PropertyChange pos_and_length;
bool save = false;
if (in_set_state || in_flush) {
return false;
}
- if (what_changed & BoundsChanged) {
+ our_interests.add (Properties::muted);
+ our_interests.add (Properties::layer);
+ our_interests.add (Properties::opaque);
+
+ bounds.add (Properties::start);
+ bounds.add (Properties::position);
+ bounds.add (Properties::length);
+
+ pos_and_length.add (Properties::position);
+ pos_and_length.add (Properties::length);
+
+ if (what_changed.contains (bounds)) {
region_bounds_changed (what_changed, region);
save = !(_splicing || _nudging);
}
- if ((what_changed & our_interests) &&
- !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
+ if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
check_dependents (region, false);
}
- if (what_changed & Change (ARDOUR::PositionChanged)) {
+ if (what_changed.contains (Properties::position)) {
notify_region_moved (region);
}
- if (what_changed & our_interests) {
+
+ /* don't notify about layer changes, since we are the only object that can initiate
+ them, and we notify in ::relayer()
+ */
+
+ if (what_changed.contains (our_interests)) {
save = true;
}
}
regions.clear ();
+
+ for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+ remove_dependents (*s);
+ }
}
if (with_signals) {
+
+ for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+ RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+ }
+
+ pending_removes.clear ();
pending_length = false;
LengthChanged ();
- pending_modified = false;
- Modified ();
+ pending_contents_change = false;
+ ContentsChanged ();
}
}
**********************************************************************/
Playlist::RegionList *
-Playlist::regions_at (nframes_t frame)
+Playlist::regions_at (framepos_t frame)
{
RegionLock rlock (this);
}
boost::shared_ptr<Region>
-Playlist::top_region_at (nframes_t frame)
+Playlist::top_region_at (framepos_t frame)
{
RegionLock rlock (this);
}
boost::shared_ptr<Region>
-Playlist::top_unmuted_region_at (nframes_t frame)
+Playlist::top_unmuted_region_at (framepos_t frame)
{
RegionLock rlock (this);
}
Playlist::RegionList*
-Playlist::regions_to_read (nframes_t start, nframes_t end)
+Playlist::regions_to_read (framepos_t start, framepos_t end)
{
/* Caller must hold lock */
RegionList covering;
- set<nframes_t> to_check;
+ set<framepos_t> to_check;
set<boost::shared_ptr<Region> > unique;
- RegionList here;
to_check.insert (start);
to_check.insert (end);
+ DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
+
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
/* find all/any regions that span start+end */
case OverlapInternal:
covering.push_back (*i);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
break;
case OverlapStart:
to_check.insert ((*i)->position());
+ if ((*i)->position() != 0) {
+ to_check.insert ((*i)->position()-1);
+ }
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
covering.push_back (*i);
break;
case OverlapEnd:
to_check.insert ((*i)->last_frame());
+ to_check.insert ((*i)->last_frame()+1);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
covering.push_back (*i);
break;
case OverlapExternal:
covering.push_back (*i);
to_check.insert ((*i)->position());
+ if ((*i)->position() != 0) {
+ to_check.insert ((*i)->position()-1);
+ }
to_check.insert ((*i)->last_frame());
+ to_check.insert ((*i)->last_frame()+1);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
break;
}
if (covering.size() == 1) {
rlist->push_back (covering.front());
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
} else {
- for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
+ RegionList here;
+ for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
here.clear ();
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
+
for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
if ((*x)->covers (*t)) {
here.push_back (*x);
- }
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
+ (*x)->name(),
+ (*t)));
+ } else {
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
+ (*x)->name(),
+ (*t)));
+ }
+
}
RegionSortByLayer cmp;
if ((*c)->opaque()) {
/* the other regions at this position are hidden by this one */
-
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
+ (*c)->name()));
break;
}
}
}
}
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+
return rlist;
}
Playlist::RegionList *
-Playlist::find_regions_at (nframes_t frame)
+Playlist::find_regions_at (framepos_t frame)
{
/* Caller must hold lock */
}
Playlist::RegionList *
-Playlist::regions_touched (nframes_t start, nframes_t end)
+Playlist::regions_touched (framepos_t start, framepos_t end)
{
RegionLock rlock (this);
RegionList *rlist = new RegionList;
return rlist;
}
-nframes64_t
-Playlist::find_next_transient (nframes64_t from, int dir)
+framepos_t
+Playlist::find_next_transient (framepos_t from, int dir)
{
RegionLock rlock (this);
AnalysisFeatureList points;
}
boost::shared_ptr<Region>
-Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
+Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
{
RegionLock rlock (this);
boost::shared_ptr<Region> ret;
- nframes_t closest = max_frames;
+ framepos_t closest = max_frames;
bool end_iter = false;
if(end_iter) break;
- nframes_t distance;
+ frameoffset_t distance;
boost::shared_ptr<Region> r = (*i);
- nframes_t pos = 0;
+ framepos_t pos = 0;
switch (point) {
case Start:
return ret;
}
-nframes64_t
-Playlist::find_next_region_boundary (nframes64_t frame, int dir)
+framepos_t
+Playlist::find_next_region_boundary (framepos_t frame, int dir)
{
RegionLock rlock (this);
- nframes64_t closest = max_frames;
- nframes64_t ret = -1;
+ framepos_t closest = max_frames;
+ framepos_t ret = -1;
if (dir > 0) {
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
boost::shared_ptr<Region> r = (*i);
- nframes64_t distance;
+ frameoffset_t distance;
if (r->first_frame() > frame) {
for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
boost::shared_ptr<Region> r = (*i);
- nframes64_t distance;
+ frameoffset_t distance;
if (r->last_frame() < frame) {
}
}
+bool
+Playlist::set_property (const PropertyBase& prop)
+{
+ if (prop == Properties::regions.property_id) {
+ const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
+ regions.update (change);
+ return (!change.added.empty() && !change.removed.empty());
+ }
+ return false;
+}
+
+void
+Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->changed ()) {
+ StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
+ cmds.push_back (sdc);
+ }
+ }
+}
+
+void
+Playlist::clear_owned_history ()
+{
+ RegionLock rlock (this);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ (*i)->clear_history ();
+ }
+}
+
+void
+Playlist::update (const RegionListProperty::ChangeRecord& change)
+{
+ DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
+ name(), change.added.size(), change.removed.size()));
+
+ freeze ();
+ /* add the added regions */
+ for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
+ add_region ((*i), (*i)->position());
+ }
+ /* remove the removed regions */
+ for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
+ remove_region (*i);
+ }
+
+ thaw ();
+}
+
+PropertyList*
+Playlist::property_factory (const XMLNode& history_node) const
+{
+ const XMLNodeList& children (history_node.children());
+ PropertyList* prop_list = 0;
+
+ for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+
+ if ((*i)->name() == capitalize (regions.property_name())) {
+
+ RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
+
+ if (rlp->load_history_state (**i)) {
+ if (!prop_list) {
+ prop_list = new PropertyList();
+ }
+ prop_list->add (rlp);
+ } else {
+ delete rlp;
+ }
+ }
+ }
+
+ return prop_list;
+}
+
int
Playlist::set_state (const XMLNode& node, int version)
{
if (prop->name() == X_("name")) {
_name = prop->value();
+ _set_sort_id ();
+ } else if (prop->name() == X_("id")) {
+ _id = prop->value();
} else if (prop->name() == X_("orig_diskstream_id")) {
_orig_diskstream_id = prop->value ();
} else if (prop->name() == X_("frozen")) {
}
}
- clear (false);
+ clear (true);
nlist = node.children();
error << _("region state node has no ID, ignored") << endmsg;
continue;
}
-
+
ID id = prop->value ();
if ((region = region_by_id (id))) {
- Change what_changed = Change (0);
+ region->suspend_property_changes ();
- if (region->set_live_state (*child, version, what_changed, true)) {
- error << _("Playlist: cannot reset region state from XML") << endmsg;
+ if (region->set_state (*child, version)) {
+ region->resume_property_changes ();
continue;
}
-
- } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
+
+ } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
+ region->suspend_property_changes ();
+ } else {
error << _("Playlist: cannot create region from XML") << endmsg;
continue;
}
-
+
add_region (region, region->position(), 1.0);
// So that layer_op ordering doesn't get screwed up
region->set_last_layer_op( region->layer());
-
+ region->resume_property_changes ();
}
}
- notify_modified ();
-
- thaw ();
-
/* update dependents, which was not done during add_region_internal
due to in_set_state being true
*/
check_dependents (*r, false);
}
+ thaw ();
+ notify_contents_changed ();
+
in_set_state--;
first_set_state = false;
return 0;
XMLNode *node = new XMLNode (X_("Playlist"));
char buf[64];
+ node->add_property (X_("id"), id().to_s());
node->add_property (X_("name"), _name);
node->add_property (X_("type"), _type.to_string());
return regions.size();
}
-nframes_t
-Playlist::get_maximum_extent () const
+pair<framecnt_t, framecnt_t>
+Playlist::get_extent () const
{
RegionLock rlock (const_cast<Playlist *>(this), false);
- return _get_maximum_extent ();
+ return _get_extent ();
}
-ARDOUR::nframes_t
-Playlist::_get_maximum_extent () const
+pair<framecnt_t, framecnt_t>
+Playlist::_get_extent () const
{
- RegionList::const_iterator i;
- nframes_t max_extent = 0;
- nframes_t end = 0;
+ pair<framecnt_t, framecnt_t> ext (max_frames, 0);
- for (i = regions.begin(); i != regions.end(); ++i) {
- if ((end = (*i)->position() + (*i)->length()) > max_extent) {
- max_extent = end;
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ pair<framecnt_t, framecnt_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
+ if (e.first < ext.first) {
+ ext.first = e.first;
+ }
+ if (e.second > ext.second) {
+ ext.second = e.second;
}
}
- return max_extent;
+ return ext;
}
string
string newname = name;
do {
- newname = bump_name_once (newname);
+ newname = bump_name_once (newname, '.');
} while (session.playlists->by_name (newname)!=NULL);
return newname;
void
Playlist::relayer ()
{
- /* don't send multiple Modified notifications
- when multiple regions are relayered.
- */
+ /* never compute layers when changing state for undo/redo or setting from XML */
- freeze ();
+ if (in_update || in_set_state) {
+ return;
+ }
+
+ bool changed = false;
/* Build up a new list of regions on each layer, stored in a set of lists
each of which represent some period of time on some layer. The idea
int const divisions = 512;
/* find the start and end positions of the regions on this playlist */
- nframes_t start = UINT_MAX;
- nframes_t end = 0;
+ framepos_t start = INT64_MAX;
+ framepos_t end = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
start = min (start, (*i)->position());
end = max (end, (*i)->position() + (*i)->length());
which depends on the layer model
*/
- RegionList copy = regions;
+ RegionList copy = regions.rlist();
/* sort according to the model and the layering mode that we're in */
}
+
for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
/* reset the pending explicit relayer flag for every region, now that we're relayering */
(*i)->set_pending_explicit_relayer (false);
- /* find the time divisions that this region covers */
- int const start_division = floor ( ((*i)->position() - start) / division_size);
- int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
- if (end_division == divisions) {
- end_division--;
+ /* find the time divisions that this region covers; if there are no regions on the list,
+ division_size will equal 0 and in this case we'll just say that
+ start_division = end_division = 0.
+ */
+ int start_division = 0;
+ int end_division = 0;
+
+ if (division_size > 0) {
+ start_division = floor ( ((*i)->position() - start) / division_size);
+ end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
+ if (end_division == divisions) {
+ end_division--;
+ }
}
- assert (end_division < divisions);
+ assert (divisions == 0 || end_division < divisions);
/* find the lowest layer that this region can go on */
size_t j = layers.size();
for (int k = start_division; k <= end_division; ++k) {
layers[j][k].push_back (*i);
}
+
+ if ((*i)->layer() != j) {
+ changed = true;
+ }
(*i)->set_layer (j);
}
- /* sending Modified means that various kinds of layering
- models operate correctly at the GUI
- level. slightly inefficient, but only slightly.
-
- We force a Modified signal here in case no layers actually
- changed.
- */
-
- notify_modified ();
-
- thaw ();
+ if (changed) {
+ notify_layering_changed ();
+ }
}
/* XXX these layer functions are all deprecated */
Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
{
/* does nothing useful if layering mode is later=higher */
- if ((_session.config.get_layer_model() == MoveAddHigher) ||
- (_session.config.get_layer_model() == AddHigher)) {
- timestamp_layer_op (region);
- relayer ();
+ switch (_session.config.get_layer_model()) {
+ case LaterHigher:
+ return;
+ default:
+ break;
}
+
+ layer_t top = regions.size() - 1;
+
+ if (region->layer() >= top) {
+ /* already on the top */
+ return;
+ }
+
+ move_region_to_layer (top, region, 1);
+ /* mark the region's last_layer_op as now, so that it remains on top when
+ doing future relayers (until something else takes over)
+ */
+ timestamp_layer_op (region);
}
void
Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
{
/* does nothing useful if layering mode is later=higher */
- if ((_session.config.get_layer_model() == MoveAddHigher) ||
- (_session.config.get_layer_model() == AddHigher)) {
- region->set_last_layer_op (0);
- relayer ();
+ switch (_session.config.get_layer_model()) {
+ case LaterHigher:
+ return;
+ default:
+ break;
}
+
+ if (region->layer() == 0) {
+ /* already on the bottom */
+ return;
+ }
+
+ move_region_to_layer (0, region, -1);
+ /* force region's last layer op to zero so that it stays at the bottom
+ when doing future relayers
+ */
+ region->set_last_layer_op (0);
}
int
RegionList::iterator i;
typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
list<LayerInfo> layerinfo;
- layer_t dest;
{
RegionLock rlock (const_cast<Playlist *> (this));
continue;
}
+ layer_t dest;
+
if (dir > 0) {
/* region is moving up, move all regions on intermediate layers
}
void
-Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
+Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
{
RegionList::iterator i;
- nframes_t new_pos;
bool moved = false;
_nudging = true;
if ((*i)->position() >= start) {
+ framepos_t new_pos;
+
if (forwards) {
if ((*i)->last_frame() > max_frames - distance) {
}
boost::shared_ptr<Region>
-Playlist::region_by_id (ID id)
+Playlist::region_by_id (const ID& id)
{
/* searches all regions ever added to this playlist */
void
Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
{
-// struct timeval tv;
-// gettimeofday (&tv, 0);
region->set_last_layer_op (++layer_op_counter);
}
Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
{
bool moved = false;
- nframes_t new_pos;
if (region->locked()) {
return;
break;
}
+ framepos_t new_pos;
+
if ((*next)->position() != region->last_frame() + 1) {
/* they didn't used to touch, so after shuffle,
just have them swap positions.
break;
}
+ framepos_t new_pos;
if (region->position() != (*prev)->last_frame() + 1) {
/* they didn't used to touch, so after shuffle,
just have them swap positions.
relayer ();
check_dependents (region, false);
- notify_modified();
+ notify_contents_changed();
}
}
Playlist::update_after_tempo_map_change ()
{
RegionLock rlock (const_cast<Playlist*> (this));
- RegionList copy (regions);
+ RegionList copy (regions.rlist());
freeze ();
bool
-Playlist::has_region_at (nframes64_t const p) const
+Playlist::has_region_at (framepos_t const p) const
{
RegionLock (const_cast<Playlist *> (this));
return (i != regions.end());
}
+
+/** Remove any region that uses a given source */
+void
+Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
+{
+ RegionLock rl (this);
+
+ RegionList::iterator i = regions.begin();
+ while (i != regions.end()) {
+ RegionList::iterator j = i;
+ ++j;
+
+ if ((*i)->uses_source (s)) {
+ remove_region_internal (*i);
+ }
+
+ i = j;
+ }
+}