#include <stdint.h>
#include <set>
-#include <fstream>
#include <algorithm>
-#include <unistd.h>
-#include <cerrno>
#include <string>
-#include <climits>
#include <boost/lexical_cast.hpp>
#include "pbd/convert.h"
-#include "pbd/failed_constructor.h"
#include "pbd/stateful_diff_command.h"
#include "pbd/xml++.h"
using namespace PBD;
namespace ARDOUR {
-namespace Properties {
-PBD::PropertyDescriptor<bool> regions;
-}
+ namespace Properties {
+ PBD::PropertyDescriptor<bool> regions;
+ }
}
struct ShowMeTheList {
in_set_state--;
_splicing = other->_splicing;
+ _rippling = other->_rippling;
_nudging = other->_nudging;
_edit_mode = other->_edit_mode;
, _type(other->_type)
, _orig_track_id (other->_orig_track_id)
{
- RegionLock rlock2 (const_cast<Playlist*> (other.get()));
+ RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
framepos_t end = start + cnt - 1;
framepos_t position = 0;
framecnt_t len = 0;
string new_name;
- OverlapType overlap;
+ Evoral::OverlapType overlap;
region = *i;
overlap = region->coverage (start, end);
switch (overlap) {
- case OverlapNone:
+ case Evoral::OverlapNone:
continue;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
offset = start - region->position();
position = 0;
len = cnt;
break;
- case OverlapStart:
+ case Evoral::OverlapStart:
offset = 0;
position = region->position() - start;
len = end - region->position();
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
offset = start - region->position();
position = 0;
len = region->length() - offset;
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
offset = 0;
position = region->position() - start;
len = region->length();
plist.add (Properties::layer, region->layer());
plist.add (Properties::layering_index, region->layering_index());
- new_region = RegionFactory::RegionFactory::create (region, plist);
+ new_region = RegionFactory::create (region, plist);
add_region_internal (new_region, position);
}
+ //keep track of any dead space at end (for pasting into Ripple or Splice mode)
+ //at the end of construction, any length of cnt beyond the extents of the regions is end_space
+ _end_space = cnt - (get_extent().second - get_extent().first);
+
in_set_state--;
first_set_state = false;
}
void
Playlist::copy_regions (RegionList& newlist) const
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionReadLock rlock (const_cast<Playlist *> (this));
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
- newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
+ newlist.push_back (RegionFactory::create (*i, true));
}
}
_refcnt = 0;
_hidden = hide;
_splicing = false;
+ _rippling = false;
_shuffling = false;
_nudging = false;
in_set_state = 0;
in_partition = false;
subcnt = 0;
_frozen = false;
+ _capture_insertion_underway = false;
_combine_ops = 0;
+ _end_space = 0;
_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));
DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
{
- RegionLock rl (this);
+ RegionReadLock rl (this);
for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
(*i)->set_playlist (boost::shared_ptr<Playlist>());
pending_contents_change = false;
RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
ContentsChanged (); /* EMIT SIGNAL */
+
}
}
void
Playlist::flush_notifications (bool from_undo)
{
- set<boost::shared_ptr<Region> > dependent_checks_needed;
set<boost::shared_ptr<Region> >::iterator s;
bool regions_changed = false;
regions_changed = true;
}
+ /* XXX: it'd be nice if we could use pending_bounds for
+ RegionsExtended and RegionsMoved.
+ */
+
/* 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"
// RegionSortByLayer cmp;
// pending_bounds.sort (cmp);
+ list<Evoral::Range<framepos_t> > crossfade_ranges;
+
for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
- dependent_checks_needed.insert (*r);
+ crossfade_ranges.push_back ((*r)->last_range ());
+ crossfade_ranges.push_back ((*r)->range ());
}
for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+ crossfade_ranges.push_back ((*s)->range ());
remove_dependents (*s);
- // cerr << _name << " sends RegionRemoved\n";
RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
}
-
+
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
- // 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);
- }
-
- if (
- ((regions_changed || pending_contents_change) && !in_set_state) ||
- pending_layering
- ) {
-
- relayer ();
+ crossfade_ranges.push_back ((*s)->range ());
+ /* don't emit RegionAdded signal until relayering is done,
+ so that the region is fully setup by the time
+ anyone hears that its been added
+ */
}
- if (regions_changed || pending_contents_change) {
- pending_contents_change = false;
- ContentsChanged (); /* EMIT SIGNAL */
- }
-
- for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
- (*s)->clear_changes ();
- 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 ()) {
- RangesMoved (pending_range_moves, from_undo);
- }
+ /* notify about contents/region changes first so that layering changes
+ * in a UI will take place on the new contents.
+ */
- if (!pending_region_extensions.empty ()) {
- RegionsExtended (pending_region_extensions);
- }
+ if (regions_changed || pending_contents_change) {
+ pending_layering = true;
+ ContentsChanged (); /* EMIT SIGNAL */
+ }
+
+ for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
+ (*s)->clear_changes ();
+ RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+ }
+
+ if ((regions_changed && !in_set_state) || pending_layering) {
+ relayer ();
+ }
+
+ coalesce_and_check_crossfades (crossfade_ranges);
+
+ if (!pending_range_moves.empty ()) {
+ /* We don't need to check crossfades for these as pending_bounds has
+ already covered it.
+ */
+ RangesMoved (pending_range_moves, from_undo);
+ }
+
+ if (!pending_region_extensions.empty ()) {
+ RegionsExtended (pending_region_extensions);
+ }
- clear_pending ();
+ clear_pending ();
- in_flush = false;
- }
+ in_flush = false;
+}
void
Playlist::clear_pending ()
PLAYLIST OPERATIONS
*************************************************************/
+/** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
void
Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
times = fabs (times);
int itimes = (int) floor (times);
}
}
- possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
+ possibly_splice_unlocked (position, (pos + length) - position, region);
}
void
Playlist::set_region_ownership ()
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
RegionList::iterator i;
boost::weak_ptr<Playlist> pl (shared_from_this());
notify_region_added (region);
- if (!holding_state ()) {
- check_dependents (region, false);
- }
-
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, framepos_t pos)
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
bool old_sp = _splicing;
_splicing = true;
void
Playlist::remove_region (boost::shared_ptr<Region> region)
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
remove_region_internal (region);
}
if (Config->get_use_overlap_equivalency()) {
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
if ((*i)->overlap_equivalent (other)) {
- results.push_back ((*i));
+ results.push_back (*i);
}
}
} else {
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
if ((*i)->equivalent (other)) {
- results.push_back ((*i));
+ results.push_back (*i);
}
}
}
}
}
+ void
+ Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
+ {
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if ((*i) && (*i)->any_source_equivalent (other)) {
+ results.push_back (*i);
+ }
+ }
+ }
+
void
Playlist::partition (framepos_t start, framepos_t end, bool cut)
{
RegionList new_regions;
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
boost::shared_ptr<Region> region;
boost::shared_ptr<Region> current;
string new_name;
RegionList::iterator tmp;
- OverlapType overlap;
+ Evoral::OverlapType overlap;
framepos_t pos1, pos2, pos3, pos4;
in_partition = true;
continue;
}
- if ((overlap = current->coverage (start, end)) == OverlapNone) {
+ if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
continue;
}
pos3 = end;
pos4 = current->last_frame();
- if (overlap == OverlapInternal) {
+ if (overlap == Evoral::OverlapInternal) {
/* split: we need 3 new regions, the front, middle and end.
cut: we need 2 regions, the front and end.
*/
thawlist.push_back (current);
current->cut_end (pos2 - 1);
- } else if (overlap == OverlapEnd) {
+ } else if (overlap == Evoral::OverlapEnd) {
/*
start end
thawlist.push_back (current);
current->cut_end (pos2 - 1);
- } else if (overlap == OverlapStart) {
+ } else if (overlap == Evoral::OverlapStart) {
/* split: we need 2 regions: the front and the end.
cut: just trim current to skip the cut area
current->suspend_property_changes ();
thawlist.push_back (current);
current->trim_front (pos3);
- } else if (overlap == OverlapExternal) {
+ } else if (overlap == Evoral::OverlapExternal) {
/* split: no split required.
cut: remove the region.
in_partition = false;
}
- for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
- check_dependents (*i, false);
- }
+ //keep track of any dead space at end (for pasting into Ripple or Splice mode)
+ framepos_t wanted_length = end-start;
+ _end_space = wanted_length - get_extent().second-get_extent().first;
}
boost::shared_ptr<Playlist>
new_name += '.';
new_name += buf;
- cnt = min (_get_extent().second - start, cnt);
+ // cnt = min (_get_extent().second - start, cnt); (We need the full range length when copy/pasting in Ripple. Why was this limit here? It's not in CUT... )
+
return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
}
times = fabs (times);
{
- RegionLock rl1 (this);
- RegionLock rl2 (other.get());
+ RegionReadLock rl2 (other.get());
int itimes = (int) floor (times);
framepos_t pos = position;
framecnt_t const shift = other->_get_extent().second;
layer_t top = top_layer ();
- while (itimes--) {
- for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
- boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
-
- /* put these new regions on top of all existing ones, but preserve
- the ordering they had in the original playlist.
- */
-
- add_region_internal (copy_of_region, (*i)->position() + pos);
- set_layer (copy_of_region, copy_of_region->layer() + top);
+ {
+ RegionWriteLock rl1 (this);
+ while (itimes--) {
+ for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
+ boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
+
+ /* put these new regions on top of all existing ones, but preserve
+ the ordering they had in the original playlist.
+ */
+
+ add_region_internal (copy_of_region, (*i)->position() + pos);
+ set_layer (copy_of_region, copy_of_region->layer() + top);
+ }
+ pos += shift;
}
- pos += shift;
}
}
{
times = fabs (times);
- RegionLock rl (this);
+ RegionWriteLock rl (this);
int itimes = (int) floor (times);
framepos_t pos = position + 1;
void
Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
RegionList copy (regions.rlist());
RegionList fixup;
void
Playlist::split (framepos_t at)
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
RegionList copy (regions.rlist());
/* use a copy since this operation can modify the region list
void
Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
_split_region (region, playlist_position);
}
add_region_internal (left, region->position());
add_region_internal (right, region->position() + before);
-
- finalize_split_region (region, left, right);
-
remove_region_internal (region);
_splicing = old_sp;
if (_edit_mode == Splice) {
splice_locked (at, distance, exclude);
- }
+ }
}
void
Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
{
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
core_splice (at, distance, exclude);
}
}
_splicing = false;
notify_contents_changed ();
- }
+}
- void
- Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
- {
- if (in_set_state || _splicing || _nudging || _shuffling) {
+void
+Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
+{
+ {
+ RegionWriteLock rl (this);
+ core_ripple (at, distance, exclude);
+ }
+}
+
+void
+Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
+{
+ core_ripple (at, distance, exclude);
+}
+
+void
+Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
+{
+ if (distance == 0) {
+ return;
+ }
+
+ _rippling = true;
+ RegionListProperty copy = regions;
+ for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+ assert (i != copy.end());
+
+ if (exclude) {
+ if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
+ continue;
+ }
+ }
+
+ if ((*i)->position() >= at) {
+ framepos_t new_pos = (*i)->position() + distance;
+ framepos_t limit = max_framepos - (*i)->length();
+ if (new_pos < 0) {
+ new_pos = 0;
+ } else if (new_pos >= limit ) {
+ new_pos = limit;
+ }
+
+ (*i)->set_position (new_pos);
+ }
+ }
+
+ _rippling = false;
+ notify_contents_changed ();
+}
+
+
+void
+Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
+{
+ if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
return;
}
} else {
notify_contents_changed ();
relayer ();
- check_dependents (region, false);
+ list<Evoral::Range<framepos_t> > xf;
+ xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
+ xf.push_back (Evoral::Range<framepos_t> (region->range()));
+ coalesce_and_check_crossfades (xf);
}
}
}
save = !(_splicing || _nudging);
}
- if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
- check_dependents (region, false);
- }
-
if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
notify_region_moved (region);
} else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
save = true;
}
+ mark_session_dirty ();
+
return save;
}
void
Playlist::drop_regions ()
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
regions.clear ();
all_regions.clear ();
}
void
Playlist::sync_all_regions_with_regions ()
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
all_regions.clear ();
Playlist::clear (bool with_signals)
{
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
region_state_changed_connections.drop_connections ();
FINDING THINGS
**********************************************************************/
- Playlist::RegionList *
- Playlist::regions_at (framepos_t frame)
-
- {
- RegionLock rlock (this);
- return find_regions_at (frame);
- }
+boost::shared_ptr<RegionList>
+Playlist::regions_at (framepos_t frame)
+{
+ RegionReadLock rlock (this);
+ return find_regions_at (frame);
+}
uint32_t
Playlist::count_regions_at (framepos_t frame) const
{
- RegionLock rlock (const_cast<Playlist*>(this));
+ RegionReadLock rlock (const_cast<Playlist*>(this));
uint32_t cnt = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
Playlist::top_region_at (framepos_t frame)
{
- RegionLock rlock (this);
- RegionList *rlist = find_regions_at (frame);
+ RegionReadLock rlock (this);
+ boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
boost::shared_ptr<Region> region;
if (rlist->size()) {
region = rlist->back();
}
- delete rlist;
return region;
}
Playlist::top_unmuted_region_at (framepos_t frame)
{
- RegionLock rlock (this);
- RegionList *rlist = find_regions_at (frame);
+ RegionReadLock rlock (this);
+ boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
region = rlist->back();
}
- delete rlist;
return region;
}
- Playlist::RegionList*
- Playlist::regions_to_read (framepos_t start, framepos_t end)
- {
- /* Caller must hold lock */
-
- RegionList covering;
- set<framepos_t> to_check;
- set<boost::shared_ptr<Region> > unique;
-
- 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 */
-
- switch ((*i)->coverage (start, end)) {
- case OverlapNone:
- break;
-
- 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;
- }
-
- /* don't go too far */
-
- if ((*i)->position() > end) {
- break;
- }
- }
-
- RegionList* rlist = new RegionList;
-
- /* find all the regions that cover each position .... */
-
- 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 {
-
- 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;
- here.sort (cmp);
-
- /* ... and get the top/transparent regions at "here" */
-
- for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
-
- unique.insert (*c);
-
- 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;
- }
- }
- }
-
- for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
- rlist->push_back (*s);
- }
-
- if (rlist->size() > 1) {
- /* now sort by time order */
-
- RegionSortByPosition cmp;
- rlist->sort (cmp);
- }
- }
-
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
-
- return rlist;
- }
-
- Playlist::RegionList *
- Playlist::find_regions_at (framepos_t frame)
- {
- /* Caller must hold lock */
-
- RegionList *rlist = new RegionList;
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if ((*i)->covers (frame)) {
- rlist->push_back (*i);
- }
- }
-
- return rlist;
- }
-
- Playlist::RegionList *
- Playlist::regions_touched (framepos_t start, framepos_t end)
- {
- RegionLock rlock (this);
- RegionList *rlist = new RegionList;
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if ((*i)->coverage (start, end) != OverlapNone) {
- rlist->push_back (*i);
- }
- }
-
- return rlist;
- }
-
- framepos_t
- Playlist::find_next_transient (framepos_t from, int dir)
- {
- RegionLock rlock (this);
- AnalysisFeatureList points;
- AnalysisFeatureList these_points;
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if (dir > 0) {
- if ((*i)->last_frame() < from) {
- continue;
- }
- } else {
- if ((*i)->first_frame() > from) {
- continue;
- }
- }
-
- (*i)->get_transients (these_points);
-
- /* add first frame, just, err, because */
-
- these_points.push_back ((*i)->first_frame());
-
- points.insert (points.end(), these_points.begin(), these_points.end());
- these_points.clear ();
- }
-
- if (points.empty()) {
- return -1;
- }
-
- TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
- bool reached = false;
-
- if (dir > 0) {
- for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
- if ((*x) >= from) {
- reached = true;
- }
-
- if (reached && (*x) > from) {
- return *x;
- }
- }
- } else {
- for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
- if ((*x) <= from) {
- reached = true;
- }
-
- if (reached && (*x) < from) {
- return *x;
- }
- }
- }
-
- return -1;
- }
-
- boost::shared_ptr<Region>
- Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
- {
- RegionLock rlock (this);
- boost::shared_ptr<Region> ret;
- framepos_t closest = max_framepos;
-
- bool end_iter = false;
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+boost::shared_ptr<RegionList>
+Playlist::find_regions_at (framepos_t frame)
+{
+ /* Caller must hold lock */
+
+ boost::shared_ptr<RegionList> rlist (new RegionList);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->covers (frame)) {
+ rlist->push_back (*i);
+ }
+ }
+
+ return rlist;
+}
- if(end_iter) break;
+boost::shared_ptr<RegionList>
+Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
+{
+ RegionReadLock rlock (this);
+ boost::shared_ptr<RegionList> rlist (new RegionList);
- frameoffset_t distance;
- boost::shared_ptr<Region> r = (*i);
- framepos_t pos = 0;
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
+ rlist->push_back (*i);
+ }
+ }
- switch (point) {
- case Start:
- pos = r->first_frame ();
- break;
- case End:
- pos = r->last_frame ();
- break;
- case SyncPoint:
- pos = r->sync_position ();
- break;
- }
+ return rlist;
+}
- switch (dir) {
- case 1: /* forwards */
+boost::shared_ptr<RegionList>
+Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
+{
+ RegionReadLock rlock (this);
+ boost::shared_ptr<RegionList> rlist (new RegionList);
- if (pos > frame) {
- if ((distance = pos - frame) < closest) {
- closest = distance;
- ret = r;
- end_iter = true;
- }
- }
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
+ rlist->push_back (*i);
+ }
+ }
- break;
+ return rlist;
+}
- default: /* backwards */
+/** @param start Range start.
+ * @param end Range end.
+ * @return regions which have some part within this range.
+ */
+boost::shared_ptr<RegionList>
+Playlist::regions_touched (framepos_t start, framepos_t end)
+{
+ RegionReadLock rlock (this);
+ return regions_touched_locked (start, end);
+}
- if (pos < frame) {
- if ((distance = frame - pos) < closest) {
- closest = distance;
- ret = r;
- }
- }
- else {
- end_iter = true;
- }
+boost::shared_ptr<RegionList>
+Playlist::regions_touched_locked (framepos_t start, framepos_t end)
+{
+ boost::shared_ptr<RegionList> rlist (new RegionList);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
+ rlist->push_back (*i);
+ }
+ }
+
+ return rlist;
+}
- break;
- }
- }
+framepos_t
+Playlist::find_next_transient (framepos_t from, int dir)
+{
+ RegionReadLock rlock (this);
+ AnalysisFeatureList points;
+ AnalysisFeatureList these_points;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if (dir > 0) {
+ if ((*i)->last_frame() < from) {
+ continue;
+ }
+ } else {
+ if ((*i)->first_frame() > from) {
+ continue;
+ }
+ }
+
+ (*i)->get_transients (these_points);
+
+ /* add first frame, just, err, because */
+
+ these_points.push_back ((*i)->first_frame());
+
+ points.insert (points.end(), these_points.begin(), these_points.end());
+ these_points.clear ();
+ }
+
+ if (points.empty()) {
+ return -1;
+ }
+
+ TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+ bool reached = false;
+
+ if (dir > 0) {
+ for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+ if ((*x) >= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) > from) {
+ return *x;
+ }
+ }
+ } else {
+ for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+ if ((*x) <= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) < from) {
+ return *x;
+ }
+ }
+ }
+
+ return -1;
+}
- return ret;
- }
+boost::shared_ptr<Region>
+Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
+{
+ RegionReadLock rlock (this);
+ boost::shared_ptr<Region> ret;
+ framepos_t closest = max_framepos;
+
+ bool end_iter = false;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if(end_iter) break;
+
+ frameoffset_t distance;
+ boost::shared_ptr<Region> r = (*i);
+ framepos_t pos = 0;
+
+ switch (point) {
+ case Start:
+ pos = r->first_frame ();
+ break;
+ case End:
+ pos = r->last_frame ();
+ break;
+ case SyncPoint:
+ pos = r->sync_position ();
+ break;
+ }
+
+ switch (dir) {
+ case 1: /* forwards */
+
+ if (pos > frame) {
+ if ((distance = pos - frame) < closest) {
+ closest = distance;
+ ret = r;
+ end_iter = true;
+ }
+ }
+
+ break;
+
+ default: /* backwards */
+
+ if (pos < frame) {
+ if ((distance = frame - pos) < closest) {
+ closest = distance;
+ ret = r;
+ }
+ } else {
+ end_iter = true;
+ }
+
+ break;
+ }
+ }
+
+ return ret;
+}
framepos_t
Playlist::find_next_region_boundary (framepos_t frame, int dir)
{
- RegionLock rlock (this);
+ RegionReadLock rlock (this);
framepos_t closest = max_framepos;
framepos_t ret = -1;
void
Playlist::rdiff (vector<Command*>& cmds) const
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionReadLock rlock (const_cast<Playlist *> (this));
Stateful::rdiff (cmds);
}
void
Playlist::clear_owned_changes ()
{
- RegionLock rlock (this);
+ RegionReadLock rlock (this);
Stateful::clear_owned_changes ();
}
freeze ();
/* add the added regions */
- for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
- add_region ((*i), (*i)->position());
+ for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
+ add_region_internal ((*i), (*i)->position());
}
/* remove the removed regions */
- for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
+ for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
remove_region (*i);
}
}
{
- RegionLock rlock (this);
+ RegionWriteLock rlock (this);
add_region_internal (region, region->position());
}
if (seen_region_nodes && regions.empty()) {
ret = -1;
- } else {
-
- /* update dependents, which was not done during add_region_internal
- due to in_set_state being true
- */
-
- for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
- check_dependents (*r, false);
- }
}
thaw ();
node->add_property (X_("frozen"), _frozen ? "yes" : "no");
if (full_state) {
- RegionLock rlock (this, false);
+ RegionReadLock rlock (this);
snprintf (buf, sizeof (buf), "%u", _combine_ops);
node->add_property ("combine-ops", buf);
bool
Playlist::empty() const
{
- RegionLock rlock (const_cast<Playlist *>(this), false);
+ RegionReadLock rlock (const_cast<Playlist *>(this));
return regions.empty();
}
uint32_t
Playlist::n_regions() const
{
- RegionLock rlock (const_cast<Playlist *>(this), false);
+ RegionReadLock rlock (const_cast<Playlist *>(this));
return regions.size();
}
+/** @return true if the all_regions list is empty, ie this playlist
+ * has never had a region added to it.
+ */
+bool
+Playlist::all_regions_empty() const
+{
+ RegionReadLock rl (const_cast<Playlist *> (this));
+ return all_regions.empty();
+}
+
pair<framepos_t, framepos_t>
Playlist::get_extent () const
{
- RegionLock rlock (const_cast<Playlist *>(this), false);
+ RegionReadLock rlock (const_cast<Playlist *>(this));
return _get_extent ();
}
+pair<framepos_t, framepos_t>
+Playlist::get_extent_with_endspace () const
+{
+ pair<framepos_t, framepos_t> l = get_extent();
+ l.second += _end_space;
+ return l;
+}
+
pair<framepos_t, framepos_t>
Playlist::_get_extent () const
{
layer_t
Playlist::top_layer() const
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionReadLock rlock (const_cast<Playlist *> (this));
layer_t top = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
void
Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
{
- /* Remove the layer we are setting from our region list, and sort it */
+ /* Remove the layer we are setting from our region list, and sort it
+ * using the layer indeces.
+ */
+
RegionList copy = regions.rlist();
copy.remove (region);
copy.sort (RelayerSort ());
copy.insert (i, region);
- /* Then re-write layering indices */
+ setup_layering_indices (copy);
+}
+
+void
+Playlist::setup_layering_indices (RegionList const & regions)
+{
uint64_t j = 0;
- for (RegionList::iterator k = copy.begin(); k != copy.end(); ++k) {
+
+ for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
(*k)->set_layering_index (j++);
}
}
+struct LaterHigherSort {
+ bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->position() < b->position();
+ }
+};
+
/** Take the layering indices of each of our regions, compute the layers
* that they should be on, and write the layers back to the regions.
*/
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
is to avoid having to search the entire region list to establish whether
vector<vector<RegionList> > layers;
layers.push_back (vector<RegionList> (divisions));
- /* Sort our regions into layering index order */
+ /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
RegionList copy = regions.rlist();
- copy.sort (RelayerSort ());
+ switch (Config->get_layer_model()) {
+ case LaterHigher:
+ copy.sort (LaterHigherSort ());
+ break;
+ case Manual:
+ copy.sort (RelayerSort ());
+ break;
+ }
DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
layers[j][k].push_back (*i);
}
- if ((*i)->layer() != j) {
- changed = true;
- }
-
(*i)->set_layer (j);
}
- if (changed) {
- notify_layering_changed ();
- }
+ /* It's a little tricky to know when we could avoid calling this; e.g. if we are
+ relayering because we just removed the only region on the top layer, nothing will
+ appear to have changed, but the StreamView must still sort itself out. We could
+ probably keep a note of the top layer last time we relayered, and check that,
+ but premature optimisation &c...
+ */
+ notify_layering_changed ();
+
+ /* This relayer() may have been called as a result of a region removal, in which
+ case we need to setup layering indices to account for the one that has just
+ gone away.
+ */
+ setup_layering_indices (copy);
}
void
_nudging = true;
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionWriteLock rlock (const_cast<Playlist *> (this));
for (i = regions.begin(); i != regions.end(); ++i) {
bool
Playlist::uses_source (boost::shared_ptr<const Source> src) const
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionReadLock rlock (const_cast<Playlist*> (this));
- for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
+ for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
if ((*r)->uses_source (src)) {
return true;
}
boost::shared_ptr<Region>
Playlist::find_region (const ID& id) const
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionReadLock rlock (const_cast<Playlist*> (this));
/* searches all regions currently in use by the playlist */
uint32_t
Playlist::region_use_count (boost::shared_ptr<Region> r) const
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionReadLock rlock (const_cast<Playlist*> (this));
uint32_t cnt = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
}
}
+ RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+ for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
+ /* check if region is used in a compound */
+ if (it->second == r) {
+ /* region is referenced as 'original' of a compound */
+ ++cnt;
+ break;
+ }
+ if (r->whole_file() && r->max_source_level() > 0) {
+ /* region itself ia a compound.
+ * the compound regions are not referenced -> check regions inside compound
+ */
+ const SourceList& sl = r->sources();
+ for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+ boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
+ if (!ps) continue;
+ if (ps->playlist()->region_use_count(it->first)) {
+ // break out of both loops
+ return ++cnt;
+ }
+ }
+ }
+ }
return cnt;
}
{
/* searches all regions ever added to this playlist */
- for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
+ for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
if ((*i)->id() == id) {
return *i;
}
_shuffling = true;
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionWriteLock rlock (const_cast<Playlist*> (this));
if (dir > 0) {
if (moved) {
relayer ();
- check_dependents (region, false);
-
notify_contents_changed();
}
bool
Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionReadLock rlock (const_cast<Playlist*> (this));
if (regions.size() > 1) {
return true;
return false;
}
+void
+Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
+{
+ ripple_locked (at, distance, exclude);
+}
+
void
Playlist::update_after_tempo_map_change ()
{
- RegionLock rlock (const_cast<Playlist*> (this));
+ RegionWriteLock rlock (const_cast<Playlist*> (this));
RegionList copy (regions.rlist());
freeze ();
void
Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
{
- RegionLock rl (this, false);
+ RegionWriteLock rl (this, false);
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
s (*i);
}
bool
Playlist::has_region_at (framepos_t const p) const
{
- RegionLock (const_cast<Playlist *> (this));
+ RegionReadLock (const_cast<Playlist *> (this));
RegionList::const_iterator i = regions.begin ();
while (i != regions.end() && !(*i)->covers (p)) {
void
Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
{
- RegionLock rl (this);
+ RegionWriteLock rl (this);
RegionList::iterator i = regions.begin();
while (i != regions.end()) {
framepos_t
Playlist::find_next_top_layer_position (framepos_t t) const
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionReadLock rlock (const_cast<Playlist *> (this));
layer_t const top = top_layer ();
pl->in_partition = true;
- for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+ /* sort by position then layer.
+ * route_time_axis passes 'selected_regions' - which is not sorted.
+ * here we need the top-most first, then every layer's region sorted by position.
+ */
+ RegionList sorted(r);
+ sorted.sort(RegionSortByLayerAndPosition());
+
+ for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
/* copy the region */
/* make position relative to zero */
pl->add_region (copied_region, original_region->position() - earliest_position);
+ copied_region->set_layer (original_region->layer ());
/* use the maximum number of channels for any region */
pre_combine (copies);
- /* add any dependent regions to the new playlist */
-
- copy_dependents (old_and_new_regions, pl.get());
-
/* now create a new PlaylistSource for each channel in the new playlist */
SourceList sources;
}
boost::shared_ptr<Region> original (ca->second);
+ cassocs.erase(ca);
bool modified_region;
if (i == rl.begin()) {
modified_region = false;
switch (original->coverage (adjusted_start, adjusted_end)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
/* original region does not cover any part
of the current state of the compound region
*/
continue;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
/* overlap is just a small piece inside the
* original so trim both ends
*/
modified_region = true;
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
/* overlap fully covers original, so leave it
as is
*/
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
/* overlap starts within but covers end,
so trim the front of the region
*/
modified_region = true;
break;
- case OverlapStart:
+ case Evoral::OverlapStart:
/* overlap covers start but ends within, so
* trim the end of the region.
*/
for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
add_region ((*i), (*i)->position());
+ set_layer((*i), (*i)->layer());
+ if (!RegionFactory::region_by_id((*i)->id())) {
+ RegionFactory::map_add(*i);
+ }
}
- /* now move dependent regions back from the compound to this playlist */
-
- pl->copy_dependents (old_and_new_regions, this);
-
in_partition = false;
thaw ();
}
+void
+Playlist::fade_range (list<AudioRange>& ranges)
+{
+ for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ (*i)->fade_range ((*r).start, (*r).end);
+ }
+ }
+}
+
uint32_t
Playlist::max_source_level () const
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ RegionReadLock rlock (const_cast<Playlist *> (this));
uint32_t lvl = 0;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
return lvl;
}
-
-uint32_t
-Playlist::count_joined_regions () const
-{
- RegionLock rlock (const_cast<Playlist *> (this));
- uint32_t cnt = 0;
-
- for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
- if ((*i)->max_source_level() > 0) {
- cnt++;
- }
- }
-
- return cnt;
-}
-
void
Playlist::set_orig_track_id (const PBD::ID& id)
{
_orig_track_id = id;
}
-uint64_t
-Playlist::highest_layering_index () const
+/** Take a list of ranges, coalesce any that can be coalesced, then call
+ * check_crossfades for each one.
+ */
+void
+Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
{
- RegionLock rlock (const_cast<Playlist *> (this));
+ /* XXX: it's a shame that this coalesce algorithm also exists in
+ TimeSelection::consolidate().
+ */
- uint64_t h = 0;
- for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
- h = max (h, (*i)->layering_index ());
- }
+ /* XXX: xfade: this is implemented in Evoral::RangeList */
+
+restart:
+ for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+ for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
+
+ if (i == j) {
+ continue;
+ }
- return h;
+ // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
+ if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
+ i->from = min (i->from, j->from);
+ i->to = max (i->to, j->to);
+ ranges.erase (j);
+ goto restart;
+ }
+ }
+ }
}
+void
+Playlist::set_capture_insertion_in_progress (bool yn)
+{
+ _capture_insertion_underway = yn;
+}