fix up fwd/reverse stuff in semitone mode
[ardour2.git] / libs / ardour / audio_playlist.cc
blobc3d4bfc8de9627b404c6f416d950a4d8a7eaba51
1 /*
2 Copyright (C) 2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <algorithm>
22 #include <cstdlib>
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/crossfade.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
33 #include "i18n.h"
35 using namespace ARDOUR;
36 using namespace std;
37 using namespace PBD;
39 namespace ARDOUR {
40 namespace Properties {
41 PBD::PropertyDescriptor<bool> crossfades;
45 void
46 AudioPlaylist::make_property_quarks ()
48 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
49 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
52 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
53 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
54 , _playlist (pl)
59 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
60 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
61 , _playlist (p._playlist)
67 CrossfadeListProperty *
68 CrossfadeListProperty::create () const
70 return new CrossfadeListProperty (_playlist);
73 CrossfadeListProperty *
74 CrossfadeListProperty::clone () const
76 return new CrossfadeListProperty (*this);
79 void
80 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
82 /* Crossfades are not written to any state when they are no
83 longer in use, so we must write their state here.
86 XMLNode& c = xfade->get_state ();
87 node.add_child_nocopy (c);
90 boost::shared_ptr<Crossfade>
91 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
93 XMLNodeList const c = node.children ();
94 assert (c.size() == 1);
95 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
99 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
100 : Playlist (session, node, DataType::AUDIO, hidden)
101 , _crossfades (*this)
103 #ifndef NDEBUG
104 const XMLProperty* prop = node.property("type");
105 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
106 #endif
108 add_property (_crossfades);
110 in_set_state++;
111 set_state (node, Stateful::loading_state_version);
112 in_set_state--;
115 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
116 : Playlist (session, name, DataType::AUDIO, hidden)
117 , _crossfades (*this)
119 add_property (_crossfades);
122 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
123 : Playlist (other, name, hidden)
124 , _crossfades (*this)
126 add_property (_crossfades);
128 RegionList::const_iterator in_o = other->regions.begin();
129 RegionList::iterator in_n = regions.begin();
131 while (in_o != other->regions.end()) {
132 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
134 // We look only for crossfades which begin with the current region, so we don't get doubles
135 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
136 if ((*xfades)->in() == ar) {
137 // We found one! Now copy it!
139 RegionList::const_iterator out_o = other->regions.begin();
140 RegionList::const_iterator out_n = regions.begin();
142 while (out_o != other->regions.end()) {
144 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
146 if ((*xfades)->out() == ar2) {
147 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
148 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
149 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
150 add_crossfade(new_fade);
151 break;
154 out_o++;
155 out_n++;
157 // cerr << "HUH!? second region in the crossfade not found!" << endl;
161 in_o++;
162 in_n++;
166 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
167 : Playlist (other, start, cnt, name, hidden)
168 , _crossfades (*this)
170 add_property (_crossfades);
172 /* this constructor does NOT notify others (session) */
175 AudioPlaylist::~AudioPlaylist ()
177 _crossfades.clear ();
180 struct RegionSortByLayer {
181 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
182 return a->layer() < b->layer();
186 ARDOUR::framecnt_t
187 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
188 framecnt_t cnt, unsigned chan_n)
190 framecnt_t ret = cnt;
192 /* optimizing this memset() away involves a lot of conditionals
193 that may well cause more of a hit due to cache misses
194 and related stuff than just doing this here.
196 it would be great if someone could measure this
197 at some point.
199 one way or another, parts of the requested area
200 that are not written to by Region::region_at()
201 for all Regions that cover the area need to be
202 zeroed.
205 memset (buf, 0, sizeof (Sample) * cnt);
207 /* this function is never called from a realtime thread, so
208 its OK to block (for short intervals).
211 Glib::RecMutex::Lock rm (region_lock);
213 framepos_t const end = start + cnt - 1;
214 framecnt_t read_frames = 0;
215 framecnt_t skip_frames = 0;
216 _read_data_count = 0;
218 _read_data_count = 0;
220 RegionList* rlist = regions_to_read (start, start+cnt);
222 if (rlist->empty()) {
223 delete rlist;
224 return cnt;
227 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
228 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
229 vector<uint32_t> relevant_layers;
231 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
232 if ((*i)->coverage (start, end) != OverlapNone) {
233 relevant_regions[(*i)->layer()].push_back (*i);
234 relevant_layers.push_back ((*i)->layer());
238 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
239 if ((*i)->coverage (start, end) != OverlapNone) {
240 relevant_xfades[(*i)->upper_layer()].push_back (*i);
244 // RegionSortByLayer layer_cmp;
245 // relevant_regions.sort (layer_cmp);
247 /* XXX this whole per-layer approach is a hack that
248 should be removed once Crossfades become
249 CrossfadeRegions and we just grab a list of relevant
250 regions and call read_at() on all of them.
253 sort (relevant_layers.begin(), relevant_layers.end());
255 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
257 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
258 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
261 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
262 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
263 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
264 assert(ar);
265 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
266 _read_data_count += ar->read_data_count();
269 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
270 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
272 /* don't JACK up _read_data_count, since its the same data as we just
273 read from the regions, and the OS should handle that for us.
278 delete rlist;
279 return ret;
283 void
284 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
286 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
288 if (in_set_state) {
289 return;
292 if (r == 0) {
293 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
294 << endmsg;
295 return;
298 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
300 if ((*i)->involves (r)) {
301 i = _crossfades.erase (i);
302 } else {
303 ++i;
309 void
310 AudioPlaylist::flush_notifications (bool from_undo)
312 Playlist::flush_notifications (from_undo);
314 if (in_flush) {
315 return;
318 in_flush = true;
320 Crossfades::iterator a;
321 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
322 NewCrossfade (*a); /* EMIT SIGNAL */
325 _pending_xfade_adds.clear ();
327 in_flush = false;
330 void
331 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
333 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
334 set<boost::shared_ptr<Crossfade> > updated;
336 if (ar == 0) {
337 return;
340 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
342 Crossfades::iterator tmp;
344 tmp = x;
345 ++tmp;
347 /* only update them once */
349 if ((*x)->involves (ar)) {
351 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
353 if (u.second) {
354 /* x was successfully inserted into the set, so it has not already been updated */
355 try {
356 (*x)->refresh ();
359 catch (Crossfade::NoCrossfadeHere& err) {
360 // relax, Invalidated during refresh
365 x = tmp;
369 void
370 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
372 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
373 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
374 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
376 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
377 Crossfades::iterator tmp;
378 tmp = x;
379 ++tmp;
381 boost::shared_ptr<Crossfade> fade;
383 if ((*x)->_in == orig) {
384 if (! (*x)->covers(right->position())) {
385 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
386 } else {
387 // Overlap, the crossfade is copied on the left side of the right region instead
388 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
392 if ((*x)->_out == orig) {
393 if (! (*x)->covers(right->position())) {
394 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
395 } else {
396 // Overlap, the crossfade is copied on the right side of the left region instead
397 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
401 if (fade) {
402 _crossfades.remove (*x);
403 add_crossfade (fade);
405 x = tmp;
409 void
410 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
412 boost::shared_ptr<AudioRegion> other;
413 boost::shared_ptr<AudioRegion> region;
414 boost::shared_ptr<AudioRegion> top;
415 boost::shared_ptr<AudioRegion> bottom;
416 boost::shared_ptr<Crossfade> xfade;
417 RegionList* touched_regions = 0;
419 if (in_set_state || in_partition) {
420 return;
423 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
424 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
425 << endmsg;
426 return;
429 if (!norefresh) {
430 refresh_dependents (r);
434 if (!_session.config.get_auto_xfade()) {
435 return;
438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
439 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
441 if (other == region) {
442 continue;
445 if (other->muted() || region->muted()) {
446 continue;
449 if (other->position() == r->position() && other->length() == r->length()) {
450 /* precise overlay of two regions - no xfade */
451 continue;
454 if (other->layer() < region->layer()) {
455 top = region;
456 bottom = other;
457 } else {
458 top = other;
459 bottom = region;
462 if (!top->opaque()) {
463 continue;
466 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
468 delete touched_regions;
469 touched_regions = 0;
471 try {
472 framecnt_t xfade_length;
473 switch (c) {
474 case OverlapNone:
475 break;
477 case OverlapInternal:
478 /* {=============== top =============}
479 * [ ----- bottom ------- ]
481 break;
483 case OverlapExternal:
485 /* [ -------- top ------- ]
486 * {=========== bottom =============}
489 /* to avoid discontinuities at the region boundaries of an internal
490 overlap (this region is completely within another), we create
491 two hidden crossfades at each boundary. this is not dependent
492 on the auto-xfade option, because we require it as basic
493 audio engineering.
496 xfade_length = min ((framecnt_t) 720, top->length());
498 if (top_region_at (top->first_frame()) == top) {
500 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
501 add_crossfade (xfade);
504 if (top_region_at (top->last_frame() - 1) == top) {
507 only add a fade out if there is no region on top of the end of 'top' (which
508 would cover it).
511 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
512 add_crossfade (xfade);
514 break;
515 case OverlapStart:
517 /* { ==== top ============ }
518 * [---- bottom -------------------]
521 if (_session.config.get_xfade_model() == FullCrossfade) {
522 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
523 if (touched_regions->size() <= 2) {
524 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
525 add_crossfade (xfade);
527 } else {
529 touched_regions = regions_touched (top->first_frame(),
530 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
531 top->length()));
532 if (touched_regions->size() <= 2) {
533 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
534 add_crossfade (xfade);
537 break;
538 case OverlapEnd:
541 /* [---- top ------------------------]
542 * { ==== bottom ============ }
545 if (_session.config.get_xfade_model() == FullCrossfade) {
547 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
548 if (touched_regions->size() <= 2) {
549 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
550 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
551 add_crossfade (xfade);
554 } else {
555 touched_regions = regions_touched (bottom->first_frame(),
556 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
557 bottom->length()));
558 if (touched_regions->size() <= 2) {
559 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
560 add_crossfade (xfade);
563 break;
564 default:
565 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
566 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
567 add_crossfade (xfade);
571 catch (failed_constructor& err) {
572 continue;
575 catch (Crossfade::NoCrossfadeHere& err) {
576 continue;
581 delete touched_regions;
584 void
585 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
587 Crossfades::iterator ci;
589 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
590 if (*(*ci) == *xfade) { // Crossfade::operator==()
591 break;
595 if (ci != _crossfades.end()) {
596 // it will just go away
597 } else {
598 _crossfades.push_back (xfade);
600 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
601 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
603 notify_crossfade_added (xfade);
607 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
609 if (g_atomic_int_get(&block_notifications)) {
610 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
611 } else {
612 NewCrossfade (x); /* EMIT SIGNAL */
616 void
617 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
619 Crossfades::iterator i;
620 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
622 xfade->in()->resume_fade_in ();
623 xfade->out()->resume_fade_out ();
625 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
626 _crossfades.erase (i);
631 AudioPlaylist::set_state (const XMLNode& node, int version)
633 XMLNode *child;
634 XMLNodeList nlist;
635 XMLNodeConstIterator niter;
637 in_set_state++;
639 Playlist::set_state (node, version);
641 freeze ();
643 nlist = node.children();
645 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
647 child = *niter;
649 if (child->name() != "Crossfade") {
650 continue;
653 try {
654 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
655 _crossfades.push_back (xfade);
656 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
657 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
658 NewCrossfade(xfade);
661 catch (failed_constructor& err) {
662 // cout << string_compose (_("could not create crossfade object in playlist %1"),
663 // _name)
664 // << endl;
665 continue;
669 thaw ();
670 in_set_state--;
672 return 0;
675 void
676 AudioPlaylist::clear (bool with_signals)
678 _crossfades.clear ();
679 Playlist::clear (with_signals);
682 XMLNode&
683 AudioPlaylist::state (bool full_state)
685 XMLNode& node = Playlist::state (full_state);
687 if (full_state) {
688 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
689 node.add_child_nocopy ((*i)->get_state());
693 return node;
696 void
697 AudioPlaylist::dump () const
699 boost::shared_ptr<Region>r;
700 boost::shared_ptr<Crossfade> x;
702 cerr << "Playlist \"" << _name << "\" " << endl
703 << regions.size() << " regions "
704 << _crossfades.size() << " crossfades"
705 << endl;
707 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
708 r = *i;
709 cerr << " " << r->name() << " @ " << r << " ["
710 << r->start() << "+" << r->length()
711 << "] at "
712 << r->position()
713 << " on layer "
714 << r->layer ()
715 << endl;
718 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
719 x = *i;
720 cerr << " xfade ["
721 << x->out()->name()
722 << ','
723 << x->in()->name()
724 << " @ "
725 << x->position()
726 << " length = "
727 << x->length ()
728 << " active ? "
729 << (x->active() ? "yes" : "no")
730 << endl;
734 bool
735 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
737 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
739 if (!r) {
740 return false;
743 bool changed = false;
744 Crossfades::iterator c, ctmp;
745 set<boost::shared_ptr<Crossfade> > unique_xfades;
748 RegionLock rlock (this);
750 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
752 RegionList::iterator tmp = i;
753 ++tmp;
755 if ((*i) == region) {
756 regions.erase (i);
757 changed = true;
760 i = tmp;
763 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
765 set<boost::shared_ptr<Region> >::iterator xtmp = x;
766 ++xtmp;
768 if ((*x) == region) {
769 all_regions.erase (x);
770 changed = true;
773 x = xtmp;
776 region->set_playlist (boost::shared_ptr<Playlist>());
779 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
780 ctmp = c;
781 ++ctmp;
783 if ((*c)->involves (r)) {
784 unique_xfades.insert (*c);
785 _crossfades.erase (c);
788 c = ctmp;
791 if (changed) {
792 /* overload this, it normally means "removed", not destroyed */
793 notify_region_removed (region);
796 return changed;
799 void
800 AudioPlaylist::crossfade_changed (const PropertyChange&)
802 if (in_flush || in_set_state) {
803 return;
806 /* XXX is there a loop here? can an xfade change not happen
807 due to a playlist change? well, sure activation would
808 be an example. maybe we should check the type of change
809 that occured.
812 notify_contents_changed ();
815 bool
816 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
818 if (in_flush || in_set_state) {
819 return false;
822 PropertyChange our_interests;
824 our_interests.add (Properties::fade_in_active);
825 our_interests.add (Properties::fade_out_active);
826 our_interests.add (Properties::scale_amplitude);
827 our_interests.add (Properties::envelope_active);
828 our_interests.add (Properties::envelope);
829 our_interests.add (Properties::fade_in);
830 our_interests.add (Properties::fade_out);
832 bool parent_wants_notify;
834 parent_wants_notify = Playlist::region_changed (what_changed, region);
836 if (parent_wants_notify || (what_changed.contains (our_interests))) {
837 notify_contents_changed ();
840 return true;
843 void
844 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
846 RegionLock rlock (this);
848 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
849 framepos_t const start = (*i)->position ();
850 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
852 if (frame >= start && frame <= end) {
853 clist.push_back (*i);
858 void
859 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
861 RegionLock rl (this, false);
862 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
863 s (*i);
867 void
868 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
870 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
871 add_crossfade (*i);
874 /* don't remove crossfades here; they will be dealt with by the dependency code */
877 boost::shared_ptr<Crossfade>
878 AudioPlaylist::find_crossfade (const PBD::ID& id) const
880 Crossfades::const_iterator i = _crossfades.begin ();
881 while (i != _crossfades.end() && (*i)->id() != id) {
882 ++i;
885 if (i == _crossfades.end()) {
886 return boost::shared_ptr<Crossfade> ();
889 return *i;