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.
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/region_sorters.h"
31 #include "ardour/session.h"
32 #include "pbd/enumwriter.h"
36 using namespace ARDOUR;
41 namespace Properties {
42 PBD::PropertyDescriptor<bool> crossfades;
47 AudioPlaylist::make_property_quarks ()
49 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
50 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
53 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
54 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
60 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
61 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
62 , _playlist (p._playlist)
68 CrossfadeListProperty *
69 CrossfadeListProperty::create () const
71 return new CrossfadeListProperty (_playlist);
74 CrossfadeListProperty *
75 CrossfadeListProperty::clone () const
77 return new CrossfadeListProperty (*this);
81 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
83 /* Crossfades are not written to any state when they are no
84 longer in use, so we must write their state here.
87 XMLNode& c = xfade->get_state ();
88 node.add_child_nocopy (c);
91 boost::shared_ptr<Crossfade>
92 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
94 XMLNodeList const c = node.children ();
95 assert (c.size() == 1);
96 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
100 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
101 : Playlist (session, node, DataType::AUDIO, hidden)
102 , _crossfades (*this)
105 const XMLProperty* prop = node.property("type");
106 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
109 add_property (_crossfades);
112 if (set_state (node, Stateful::loading_state_version)) {
113 throw failed_constructor();
118 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
119 : Playlist (session, name, DataType::AUDIO, hidden)
120 , _crossfades (*this)
122 add_property (_crossfades);
125 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
126 : Playlist (other, name, hidden)
127 , _crossfades (*this)
129 add_property (_crossfades);
131 RegionList::const_iterator in_o = other->regions.begin();
132 RegionList::iterator in_n = regions.begin();
134 while (in_o != other->regions.end()) {
135 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
137 // We look only for crossfades which begin with the current region, so we don't get doubles
138 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
139 if ((*xfades)->in() == ar) {
140 // We found one! Now copy it!
142 RegionList::const_iterator out_o = other->regions.begin();
143 RegionList::const_iterator out_n = regions.begin();
145 while (out_o != other->regions.end()) {
147 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
149 if ((*xfades)->out() == ar2) {
150 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
151 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
152 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
153 add_crossfade(new_fade);
160 // cerr << "HUH!? second region in the crossfade not found!" << endl;
169 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
170 : Playlist (other, start, cnt, name, hidden)
171 , _crossfades (*this)
173 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
176 add_property (_crossfades);
178 framepos_t const end = start + cnt - 1;
180 /* Audio regions that have been created by the Playlist constructor
181 will currently have the same fade in/out as the regions that they
182 were created from. This is wrong, so reset the fades here.
185 RegionList::iterator ours = regions.begin ();
187 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
188 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
191 framecnt_t fade_in = 64;
192 framecnt_t fade_out = 64;
194 switch (region->coverage (start, end)) {
198 case OverlapInternal:
200 framecnt_t const offset = start - region->position ();
201 framecnt_t const trim = region->last_frame() - end;
202 if (region->fade_in()->back()->when > offset) {
203 fade_in = region->fade_in()->back()->when - offset;
205 if (region->fade_out()->back()->when > trim) {
206 fade_out = region->fade_out()->back()->when - trim;
212 if (end > region->position() + region->fade_in()->back()->when)
213 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
214 if (end > region->last_frame() - region->fade_out()->back()->when)
215 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
220 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
221 fade_out = region->fade_out()->back()->when;
223 if (start < region->position() + region->fade_in()->back()->when)
224 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
228 case OverlapExternal:
229 fade_in = region->fade_in()->back()->when;
230 fade_out = region->fade_out()->back()->when;
234 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
237 our_region->set_fade_in_length (fade_in);
238 our_region->set_fade_out_length (fade_out);
244 /* this constructor does NOT notify others (session) */
247 AudioPlaylist::~AudioPlaylist ()
249 _crossfades.clear ();
252 struct RegionSortByLayer {
253 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
254 return a->layer() < b->layer();
259 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
260 framecnt_t cnt, unsigned chan_n)
262 framecnt_t ret = cnt;
264 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n",
265 name(), start, cnt, chan_n, regions.size(), _crossfades.size()));
267 /* optimizing this memset() away involves a lot of conditionals
268 that may well cause more of a hit due to cache misses
269 and related stuff than just doing this here.
271 it would be great if someone could measure this
274 one way or another, parts of the requested area
275 that are not written to by Region::region_at()
276 for all Regions that cover the area need to be
280 memset (buf, 0, sizeof (Sample) * cnt);
282 /* this function is never called from a realtime thread, so
283 its OK to block (for short intervals).
286 Glib::RecMutex::Lock rm (region_lock);
288 framepos_t const end = start + cnt - 1;
289 _read_data_count = 0;
291 _read_data_count = 0;
293 RegionList* rlist = regions_to_read (start, start+cnt);
295 if (rlist->empty()) {
300 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
301 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
302 vector<uint32_t> relevant_layers;
304 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
305 if ((*i)->coverage (start, end) != OverlapNone) {
306 relevant_regions[(*i)->layer()].push_back (*i);
307 relevant_layers.push_back ((*i)->layer());
311 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
313 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
314 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n",
315 name(), (*i)->out()->name(), (*i)->in()->name(),
316 (*i)->first_frame(), (*i)->last_frame(),
318 if ((*i)->coverage (start, end) != OverlapNone) {
319 relevant_xfades[(*i)->upper_layer()].push_back (*i);
320 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n",
321 (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end))));
325 // RegionSortByLayer layer_cmp;
326 // relevant_regions.sort (layer_cmp);
328 /* XXX this whole per-layer approach is a hack that
329 should be removed once Crossfades become
330 CrossfadeRegions and we just grab a list of relevant
331 regions and call read_at() on all of them.
334 sort (relevant_layers.begin(), relevant_layers.end());
336 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
338 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
340 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
341 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
344 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
345 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
346 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
348 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
349 _read_data_count += ar->read_data_count();
352 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
353 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
354 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
356 /* don't JACK up _read_data_count, since its the same data as we just
357 read from the regions, and the OS should handle that for us.
368 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
370 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
377 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
382 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
384 if ((*i)->involves (r)) {
385 i = _crossfades.erase (i);
394 AudioPlaylist::flush_notifications (bool from_undo)
396 Playlist::flush_notifications (from_undo);
404 Crossfades::iterator a;
405 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
406 NewCrossfade (*a); /* EMIT SIGNAL */
409 _pending_xfade_adds.clear ();
415 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
417 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
418 set<boost::shared_ptr<Crossfade> > updated;
424 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
426 Crossfades::iterator tmp;
431 /* only update them once */
433 if ((*x)->involves (ar)) {
435 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
438 /* x was successfully inserted into the set, so it has not already been updated */
443 catch (Crossfade::NoCrossfadeHere& err) {
444 // relax, Invalidated during refresh
454 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
456 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
457 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
458 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
460 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
461 Crossfades::iterator tmp;
465 boost::shared_ptr<Crossfade> fade;
467 if ((*x)->_in == orig) {
468 if (! (*x)->covers(right->position())) {
469 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
471 // Overlap, the crossfade is copied on the left side of the right region instead
472 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
476 if ((*x)->_out == orig) {
477 if (! (*x)->covers(right->position())) {
478 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
480 // Overlap, the crossfade is copied on the right side of the left region instead
481 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
486 _crossfades.remove (*x);
487 add_crossfade (fade);
494 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
496 boost::shared_ptr<AudioRegion> other;
497 boost::shared_ptr<AudioRegion> region;
498 boost::shared_ptr<AudioRegion> top;
499 boost::shared_ptr<AudioRegion> bottom;
500 boost::shared_ptr<Crossfade> xfade;
501 RegionList* touched_regions = 0;
503 if (in_set_state || in_partition) {
507 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
508 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
514 refresh_dependents (r);
518 if (!_session.config.get_auto_xfade()) {
522 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
523 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
525 if (other == region) {
529 if (other->muted() || region->muted()) {
533 if (other->position() == r->position() && other->length() == r->length()) {
534 /* precise overlay of two regions - no xfade */
538 if (other->layer() < region->layer()) {
546 if (!top->opaque()) {
550 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
552 delete touched_regions;
556 framecnt_t xfade_length;
561 case OverlapInternal:
562 /* {=============== top =============}
563 * [ ----- bottom ------- ]
567 case OverlapExternal:
569 /* [ -------- top ------- ]
570 * {=========== bottom =============}
573 /* to avoid discontinuities at the region boundaries of an internal
574 overlap (this region is completely within another), we create
575 two hidden crossfades at each boundary. this is not dependent
576 on the auto-xfade option, because we require it as basic
580 xfade_length = min ((framecnt_t) 720, top->length());
582 if (top_region_at (top->first_frame()) == top) {
584 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
585 xfade->set_position (top->first_frame());
586 add_crossfade (xfade);
589 if (top_region_at (top->last_frame() - 1) == top) {
592 only add a fade out if there is no region on top of the end of 'top' (which
596 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, EndOfOut));
597 xfade->set_position (top->last_frame() - xfade_length);
598 add_crossfade (xfade);
603 /* { ==== top ============ }
604 * [---- bottom -------------------]
607 if (_session.config.get_xfade_model() == FullCrossfade) {
608 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
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);
615 touched_regions = regions_touched (top->first_frame(),
616 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
618 if (touched_regions->size() <= 2) {
619 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
620 add_crossfade (xfade);
627 /* [---- top ------------------------]
628 * { ==== bottom ============ }
631 if (_session.config.get_xfade_model() == FullCrossfade) {
633 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
634 if (touched_regions->size() <= 2) {
635 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
636 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
637 add_crossfade (xfade);
641 touched_regions = regions_touched (bottom->first_frame(),
642 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
644 if (touched_regions->size() <= 2) {
645 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
646 add_crossfade (xfade);
651 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
652 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
653 add_crossfade (xfade);
657 catch (failed_constructor& err) {
661 catch (Crossfade::NoCrossfadeHere& err) {
667 delete touched_regions;
671 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
673 Crossfades::iterator ci;
675 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
676 if (*(*ci) == *xfade) { // Crossfade::operator==()
681 if (ci != _crossfades.end()) {
682 // it will just go away
684 _crossfades.push_back (xfade);
686 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
687 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
689 notify_crossfade_added (xfade);
693 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
695 if (g_atomic_int_get(&block_notifications)) {
696 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
698 NewCrossfade (x); /* EMIT SIGNAL */
703 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
705 Crossfades::iterator i;
706 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
708 xfade->in()->resume_fade_in ();
709 xfade->out()->resume_fade_out ();
711 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
712 _crossfades.erase (i);
717 AudioPlaylist::set_state (const XMLNode& node, int version)
721 XMLNodeConstIterator niter;
725 if (Playlist::set_state (node, version)) {
731 nlist = node.children();
733 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
737 if (child->name() != "Crossfade") {
742 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
743 _crossfades.push_back (xfade);
744 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
745 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
749 catch (failed_constructor& err) {
750 // cout << string_compose (_("could not create crossfade object in playlist %1"),
764 AudioPlaylist::clear (bool with_signals)
766 _crossfades.clear ();
767 Playlist::clear (with_signals);
771 AudioPlaylist::state (bool full_state)
773 XMLNode& node = Playlist::state (full_state);
776 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
777 node.add_child_nocopy ((*i)->get_state());
785 AudioPlaylist::dump () const
787 boost::shared_ptr<Region>r;
788 boost::shared_ptr<Crossfade> x;
790 cerr << "Playlist \"" << _name << "\" " << endl
791 << regions.size() << " regions "
792 << _crossfades.size() << " crossfades"
795 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
797 cerr << " " << r->name() << " @ " << r << " ["
798 << r->start() << "+" << r->length()
806 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
817 << (x->active() ? "yes" : "no")
823 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
825 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
831 bool changed = false;
832 Crossfades::iterator c, ctmp;
833 set<boost::shared_ptr<Crossfade> > unique_xfades;
836 RegionLock rlock (this);
838 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
840 RegionList::iterator tmp = i;
843 if ((*i) == region) {
851 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
853 set<boost::shared_ptr<Region> >::iterator xtmp = x;
856 if ((*x) == region) {
857 all_regions.erase (x);
864 region->set_playlist (boost::shared_ptr<Playlist>());
867 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
871 if ((*c)->involves (r)) {
872 unique_xfades.insert (*c);
873 _crossfades.erase (c);
880 /* overload this, it normally means "removed", not destroyed */
881 notify_region_removed (region);
888 AudioPlaylist::crossfade_changed (const PropertyChange&)
890 if (in_flush || in_set_state) {
894 /* XXX is there a loop here? can an xfade change not happen
895 due to a playlist change? well, sure activation would
896 be an example. maybe we should check the type of change
900 notify_contents_changed ();
904 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
906 if (in_flush || in_set_state) {
910 PropertyChange our_interests;
912 our_interests.add (Properties::fade_in_active);
913 our_interests.add (Properties::fade_out_active);
914 our_interests.add (Properties::scale_amplitude);
915 our_interests.add (Properties::envelope_active);
916 our_interests.add (Properties::envelope);
917 our_interests.add (Properties::fade_in);
918 our_interests.add (Properties::fade_out);
920 bool parent_wants_notify;
922 parent_wants_notify = Playlist::region_changed (what_changed, region);
924 if (parent_wants_notify || (what_changed.contains (our_interests))) {
925 notify_contents_changed ();
932 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
934 RegionLock rlock (this);
936 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
937 framepos_t const start = (*i)->position ();
938 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
940 if (frame >= start && frame <= end) {
941 clist.push_back (*i);
947 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
949 RegionLock rl (this, false);
950 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
956 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
958 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
962 /* don't remove crossfades here; they will be dealt with by the dependency code */
965 boost::shared_ptr<Crossfade>
966 AudioPlaylist::find_crossfade (const PBD::ID& id) const
968 Crossfades::const_iterator i = _crossfades.begin ();
969 while (i != _crossfades.end() && (*i)->id() != id) {
973 if (i == _crossfades.end()) {
974 return boost::shared_ptr<Crossfade> ();
980 struct crossfade_triple {
981 boost::shared_ptr<Region> old_in;
982 boost::shared_ptr<Region> new_in;
983 boost::shared_ptr<Region> new_out;
987 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
989 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
995 /* our argument is a vector of old and new regions. Each old region
996 might be participant in a crossfade that is already present. Each new
997 region is a copy of the old region, present in the other playlist.
999 our task is to find all the relevant xfades in our playlist (involving
1000 the "old" regions) and place copies of them in the other playlist.
1003 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1004 CrossfadeInfo crossfade_info;
1006 /* build up a record that links crossfades, old regions and new regions
1009 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1011 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1013 if ((*i)->in() == on->first) {
1015 CrossfadeInfo::iterator cf;
1017 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1019 /* already have a record for the old fade-in region,
1020 so note the new fade-in region
1023 cf->second.new_in = on->second;
1027 /* add a record of this crossfade, keeping an association
1028 with the new fade-in region
1031 crossfade_triple ct;
1033 ct.old_in = on->first;
1034 ct.new_in = on->second;
1036 crossfade_info[*i] = ct;
1039 } else if ((*i)->out() == on->first) {
1041 /* this old region is the fade-out region of this crossfade */
1043 CrossfadeInfo::iterator cf;
1045 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1047 /* already have a record for this crossfade, so just keep
1048 an association for the new fade out region
1051 cf->second.new_out = on->second;
1055 /* add a record of this crossfade, keeping an association
1056 with the new fade-in region
1059 crossfade_triple ct;
1061 ct.old_in = on->first;
1062 ct.new_out = on->second;
1064 crossfade_info[*i] = ct;
1070 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1072 /* for each crossfade that involves at least two of the old regions,
1073 create a new identical crossfade with the new regions
1076 if (!ci->second.new_in || !ci->second.new_out) {
1080 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1081 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1082 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1084 /* add it at the right position - which must be at the start
1085 * of the fade-in region
1088 new_xfade->set_position (ci->second.new_in->position());
1089 other_audio->add_crossfade (new_xfade);
1094 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1096 RegionSortByPosition cmp;
1097 boost::shared_ptr<AudioRegion> ar;
1099 sort (copies.begin(), copies.end(), cmp);
1101 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1103 /* disable fade in of the first region */
1106 ar->set_fade_in_active (false);
1109 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1111 /* disable fade out of the last region */
1114 ar->set_fade_out_active (false);
1119 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1121 RegionSortByPosition cmp;
1122 boost::shared_ptr<AudioRegion> ar;
1123 boost::shared_ptr<AudioRegion> cr;
1125 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1129 sort (originals.begin(), originals.end(), cmp);
1131 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1133 /* copy the fade in of the first into the compound region */
1136 cr->set_fade_in (ar->fade_in());
1139 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1142 /* copy the fade out of the last into the compound region */
1143 cr->set_fade_out (ar->fade_out());
1148 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1150 RegionSortByPosition cmp;
1151 boost::shared_ptr<AudioRegion> ar;
1152 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1158 sort (originals.begin(), originals.end(), cmp);
1160 /* no need to call clear_changes() on the originals because that is
1161 * done within Playlist::uncombine ()
1164 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1166 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1170 /* scale the uncombined regions by any gain setting for the
1174 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1176 if (i == originals.begin()) {
1178 /* copy the compound region's fade in back into the first
1182 if (cr->fade_in()->back()->when <= ar->length()) {
1183 /* don't do this if the fade is longer than the
1186 ar->set_fade_in (cr->fade_in());
1190 } else if (*i == originals.back()) {
1192 /* copy the compound region's fade out back into the last
1196 if (cr->fade_out()->back()->when <= ar->length()) {
1197 /* don't do this if the fade is longer than the
1200 ar->set_fade_out (cr->fade_out());
1205 _session.add_command (new StatefulDiffCommand (*i));