remove unused SHUTTLE_FRACT constant
[ardour2.git] / libs / ardour / audio_playlist.cc
blob4e15aa70c2d3a89318df3ec9457276f27a155e72
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 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
171 in_set_state++;
173 add_property (_crossfades);
175 framepos_t const end = start + cnt - 1;
177 /* Audio regions that have been created by the Playlist constructor
178 will currently have the same fade in/out as the regions that they
179 were created from. This is wrong, so reset the fades here.
182 RegionList::iterator ours = regions.begin ();
184 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
185 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
186 assert (region);
188 framecnt_t fade_in = 64;
189 framecnt_t fade_out = 64;
191 switch (region->coverage (start, end)) {
192 case OverlapNone:
193 continue;
195 case OverlapInternal:
197 framecnt_t const offset = start - region->position ();
198 framecnt_t const trim = region->last_frame() - end;
199 if (region->fade_in()->back()->when > offset) {
200 fade_in = region->fade_in()->back()->when - offset;
202 if (region->fade_out()->back()->when > trim) {
203 fade_out = region->fade_out()->back()->when - trim;
205 break;
208 case OverlapStart:
210 if (region->fade_in()->back()->when > 0) {
211 fade_in = region->fade_in()->back()->when;
213 if (start > region->last_frame() - region->fade_out()->back()->when) {
214 fade_out = region->last_frame() - start;
216 break;
219 case OverlapEnd:
221 framecnt_t const offset = start - region->position();
222 if (region->fade_in()->back()->when > offset) {
223 fade_in = region->fade_in()->back()->when - offset;
225 if (start > region->last_frame() - region->fade_out()->back()->when) {
226 fade_out = region->last_frame() - start;
227 } else {
228 fade_out = region->fade_out()->back()->when;
230 break;
233 case OverlapExternal:
234 fade_in = region->fade_in()->back()->when;
235 fade_out = region->fade_out()->back()->when;
236 break;
239 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
240 assert (our_region);
242 our_region->set_fade_in_length (fade_in);
243 our_region->set_fade_out_length (fade_out);
244 ++ours;
247 in_set_state--;
249 /* this constructor does NOT notify others (session) */
252 AudioPlaylist::~AudioPlaylist ()
254 _crossfades.clear ();
257 struct RegionSortByLayer {
258 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
259 return a->layer() < b->layer();
263 ARDOUR::framecnt_t
264 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
265 framecnt_t cnt, unsigned chan_n)
267 framecnt_t ret = cnt;
269 /* optimizing this memset() away involves a lot of conditionals
270 that may well cause more of a hit due to cache misses
271 and related stuff than just doing this here.
273 it would be great if someone could measure this
274 at some point.
276 one way or another, parts of the requested area
277 that are not written to by Region::region_at()
278 for all Regions that cover the area need to be
279 zeroed.
282 memset (buf, 0, sizeof (Sample) * cnt);
284 /* this function is never called from a realtime thread, so
285 its OK to block (for short intervals).
288 Glib::RecMutex::Lock rm (region_lock);
290 framepos_t const end = start + cnt - 1;
291 framecnt_t read_frames = 0;
292 framecnt_t skip_frames = 0;
293 _read_data_count = 0;
295 _read_data_count = 0;
297 RegionList* rlist = regions_to_read (start, start+cnt);
299 if (rlist->empty()) {
300 delete rlist;
301 return cnt;
304 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
305 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
306 vector<uint32_t> relevant_layers;
308 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
309 if ((*i)->coverage (start, end) != OverlapNone) {
310 relevant_regions[(*i)->layer()].push_back (*i);
311 relevant_layers.push_back ((*i)->layer());
315 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
316 if ((*i)->coverage (start, end) != OverlapNone) {
317 relevant_xfades[(*i)->upper_layer()].push_back (*i);
321 // RegionSortByLayer layer_cmp;
322 // relevant_regions.sort (layer_cmp);
324 /* XXX this whole per-layer approach is a hack that
325 should be removed once Crossfades become
326 CrossfadeRegions and we just grab a list of relevant
327 regions and call read_at() on all of them.
330 sort (relevant_layers.begin(), relevant_layers.end());
332 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
334 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
335 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
338 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
339 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
340 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
341 assert(ar);
342 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
343 _read_data_count += ar->read_data_count();
346 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
347 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
349 /* don't JACK up _read_data_count, since its the same data as we just
350 read from the regions, and the OS should handle that for us.
355 delete rlist;
356 return ret;
360 void
361 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
363 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
365 if (in_set_state) {
366 return;
369 if (r == 0) {
370 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
371 << endmsg;
372 return;
375 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
377 if ((*i)->involves (r)) {
378 i = _crossfades.erase (i);
379 } else {
380 ++i;
386 void
387 AudioPlaylist::flush_notifications (bool from_undo)
389 Playlist::flush_notifications (from_undo);
391 if (in_flush) {
392 return;
395 in_flush = true;
397 Crossfades::iterator a;
398 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
399 NewCrossfade (*a); /* EMIT SIGNAL */
402 _pending_xfade_adds.clear ();
404 in_flush = false;
407 void
408 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
410 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
411 set<boost::shared_ptr<Crossfade> > updated;
413 if (ar == 0) {
414 return;
417 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
419 Crossfades::iterator tmp;
421 tmp = x;
422 ++tmp;
424 /* only update them once */
426 if ((*x)->involves (ar)) {
428 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
430 if (u.second) {
431 /* x was successfully inserted into the set, so it has not already been updated */
432 try {
433 (*x)->refresh ();
436 catch (Crossfade::NoCrossfadeHere& err) {
437 // relax, Invalidated during refresh
442 x = tmp;
446 void
447 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
449 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
450 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
451 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
453 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
454 Crossfades::iterator tmp;
455 tmp = x;
456 ++tmp;
458 boost::shared_ptr<Crossfade> fade;
460 if ((*x)->_in == orig) {
461 if (! (*x)->covers(right->position())) {
462 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
463 } else {
464 // Overlap, the crossfade is copied on the left side of the right region instead
465 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
469 if ((*x)->_out == orig) {
470 if (! (*x)->covers(right->position())) {
471 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
472 } else {
473 // Overlap, the crossfade is copied on the right side of the left region instead
474 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
478 if (fade) {
479 _crossfades.remove (*x);
480 add_crossfade (fade);
482 x = tmp;
486 void
487 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
489 boost::shared_ptr<AudioRegion> other;
490 boost::shared_ptr<AudioRegion> region;
491 boost::shared_ptr<AudioRegion> top;
492 boost::shared_ptr<AudioRegion> bottom;
493 boost::shared_ptr<Crossfade> xfade;
494 RegionList* touched_regions = 0;
496 if (in_set_state || in_partition) {
497 return;
500 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
501 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
502 << endmsg;
503 return;
506 if (!norefresh) {
507 refresh_dependents (r);
511 if (!_session.config.get_auto_xfade()) {
512 return;
515 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
516 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
518 if (other == region) {
519 continue;
522 if (other->muted() || region->muted()) {
523 continue;
526 if (other->position() == r->position() && other->length() == r->length()) {
527 /* precise overlay of two regions - no xfade */
528 continue;
531 if (other->layer() < region->layer()) {
532 top = region;
533 bottom = other;
534 } else {
535 top = other;
536 bottom = region;
539 if (!top->opaque()) {
540 continue;
543 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
545 delete touched_regions;
546 touched_regions = 0;
548 try {
549 framecnt_t xfade_length;
550 switch (c) {
551 case OverlapNone:
552 break;
554 case OverlapInternal:
555 /* {=============== top =============}
556 * [ ----- bottom ------- ]
558 break;
560 case OverlapExternal:
562 /* [ -------- top ------- ]
563 * {=========== bottom =============}
566 /* to avoid discontinuities at the region boundaries of an internal
567 overlap (this region is completely within another), we create
568 two hidden crossfades at each boundary. this is not dependent
569 on the auto-xfade option, because we require it as basic
570 audio engineering.
573 xfade_length = min ((framecnt_t) 720, top->length());
575 if (top_region_at (top->first_frame()) == top) {
577 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
578 add_crossfade (xfade);
581 if (top_region_at (top->last_frame() - 1) == top) {
584 only add a fade out if there is no region on top of the end of 'top' (which
585 would cover it).
588 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
589 add_crossfade (xfade);
591 break;
592 case OverlapStart:
594 /* { ==== top ============ }
595 * [---- bottom -------------------]
598 if (_session.config.get_xfade_model() == FullCrossfade) {
599 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
600 if (touched_regions->size() <= 2) {
601 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
602 add_crossfade (xfade);
604 } else {
606 touched_regions = regions_touched (top->first_frame(),
607 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
608 top->length()));
609 if (touched_regions->size() <= 2) {
610 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
611 add_crossfade (xfade);
614 break;
615 case OverlapEnd:
618 /* [---- top ------------------------]
619 * { ==== bottom ============ }
622 if (_session.config.get_xfade_model() == FullCrossfade) {
624 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
625 if (touched_regions->size() <= 2) {
626 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
627 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
628 add_crossfade (xfade);
631 } else {
632 touched_regions = regions_touched (bottom->first_frame(),
633 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
634 bottom->length()));
635 if (touched_regions->size() <= 2) {
636 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
637 add_crossfade (xfade);
640 break;
641 default:
642 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
643 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
644 add_crossfade (xfade);
648 catch (failed_constructor& err) {
649 continue;
652 catch (Crossfade::NoCrossfadeHere& err) {
653 continue;
658 delete touched_regions;
661 void
662 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
664 Crossfades::iterator ci;
666 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
667 if (*(*ci) == *xfade) { // Crossfade::operator==()
668 break;
672 if (ci != _crossfades.end()) {
673 // it will just go away
674 } else {
675 _crossfades.push_back (xfade);
677 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
678 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
680 notify_crossfade_added (xfade);
684 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
686 if (g_atomic_int_get(&block_notifications)) {
687 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
688 } else {
689 NewCrossfade (x); /* EMIT SIGNAL */
693 void
694 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
696 Crossfades::iterator i;
697 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
699 xfade->in()->resume_fade_in ();
700 xfade->out()->resume_fade_out ();
702 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
703 _crossfades.erase (i);
708 AudioPlaylist::set_state (const XMLNode& node, int version)
710 XMLNode *child;
711 XMLNodeList nlist;
712 XMLNodeConstIterator niter;
714 in_set_state++;
716 Playlist::set_state (node, version);
718 freeze ();
720 nlist = node.children();
722 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
724 child = *niter;
726 if (child->name() != "Crossfade") {
727 continue;
730 try {
731 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
732 _crossfades.push_back (xfade);
733 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
734 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
735 NewCrossfade(xfade);
738 catch (failed_constructor& err) {
739 // cout << string_compose (_("could not create crossfade object in playlist %1"),
740 // _name)
741 // << endl;
742 continue;
746 thaw ();
747 in_set_state--;
749 return 0;
752 void
753 AudioPlaylist::clear (bool with_signals)
755 _crossfades.clear ();
756 Playlist::clear (with_signals);
759 XMLNode&
760 AudioPlaylist::state (bool full_state)
762 XMLNode& node = Playlist::state (full_state);
764 if (full_state) {
765 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
766 node.add_child_nocopy ((*i)->get_state());
770 return node;
773 void
774 AudioPlaylist::dump () const
776 boost::shared_ptr<Region>r;
777 boost::shared_ptr<Crossfade> x;
779 cerr << "Playlist \"" << _name << "\" " << endl
780 << regions.size() << " regions "
781 << _crossfades.size() << " crossfades"
782 << endl;
784 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
785 r = *i;
786 cerr << " " << r->name() << " @ " << r << " ["
787 << r->start() << "+" << r->length()
788 << "] at "
789 << r->position()
790 << " on layer "
791 << r->layer ()
792 << endl;
795 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
796 x = *i;
797 cerr << " xfade ["
798 << x->out()->name()
799 << ','
800 << x->in()->name()
801 << " @ "
802 << x->position()
803 << " length = "
804 << x->length ()
805 << " active ? "
806 << (x->active() ? "yes" : "no")
807 << endl;
811 bool
812 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
814 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
816 if (!r) {
817 return false;
820 bool changed = false;
821 Crossfades::iterator c, ctmp;
822 set<boost::shared_ptr<Crossfade> > unique_xfades;
825 RegionLock rlock (this);
827 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
829 RegionList::iterator tmp = i;
830 ++tmp;
832 if ((*i) == region) {
833 regions.erase (i);
834 changed = true;
837 i = tmp;
840 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
842 set<boost::shared_ptr<Region> >::iterator xtmp = x;
843 ++xtmp;
845 if ((*x) == region) {
846 all_regions.erase (x);
847 changed = true;
850 x = xtmp;
853 region->set_playlist (boost::shared_ptr<Playlist>());
856 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
857 ctmp = c;
858 ++ctmp;
860 if ((*c)->involves (r)) {
861 unique_xfades.insert (*c);
862 _crossfades.erase (c);
865 c = ctmp;
868 if (changed) {
869 /* overload this, it normally means "removed", not destroyed */
870 notify_region_removed (region);
873 return changed;
876 void
877 AudioPlaylist::crossfade_changed (const PropertyChange&)
879 if (in_flush || in_set_state) {
880 return;
883 /* XXX is there a loop here? can an xfade change not happen
884 due to a playlist change? well, sure activation would
885 be an example. maybe we should check the type of change
886 that occured.
889 notify_contents_changed ();
892 bool
893 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
895 if (in_flush || in_set_state) {
896 return false;
899 PropertyChange our_interests;
901 our_interests.add (Properties::fade_in_active);
902 our_interests.add (Properties::fade_out_active);
903 our_interests.add (Properties::scale_amplitude);
904 our_interests.add (Properties::envelope_active);
905 our_interests.add (Properties::envelope);
906 our_interests.add (Properties::fade_in);
907 our_interests.add (Properties::fade_out);
909 bool parent_wants_notify;
911 parent_wants_notify = Playlist::region_changed (what_changed, region);
913 if (parent_wants_notify || (what_changed.contains (our_interests))) {
914 notify_contents_changed ();
917 return true;
920 void
921 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
923 RegionLock rlock (this);
925 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
926 framepos_t const start = (*i)->position ();
927 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
929 if (frame >= start && frame <= end) {
930 clist.push_back (*i);
935 void
936 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
938 RegionLock rl (this, false);
939 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
940 s (*i);
944 void
945 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
947 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
948 add_crossfade (*i);
951 /* don't remove crossfades here; they will be dealt with by the dependency code */
954 boost::shared_ptr<Crossfade>
955 AudioPlaylist::find_crossfade (const PBD::ID& id) const
957 Crossfades::const_iterator i = _crossfades.begin ();
958 while (i != _crossfades.end() && (*i)->id() != id) {
959 ++i;
962 if (i == _crossfades.end()) {
963 return boost::shared_ptr<Crossfade> ();
966 return *i;