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;
195 frameoffset_t offset;
197 switch (region->coverage (start, end)) {
201 case OverlapInternal:
203 framecnt_t const offset = start - region->position ();
204 framecnt_t const trim = region->last_frame() - end;
205 if (region->fade_in()->back()->when > offset) {
206 fade_in = region->fade_in()->back()->when - offset;
208 if (region->fade_out()->back()->when > trim) {
209 fade_out = region->fade_out()->back()->when - trim;
215 position = region->position() - start;
216 len = end - region->position();
218 if (end > region->position() + region->fade_in()->back()->when)
219 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
220 if (end > region->last_frame() - region->fade_out()->back()->when)
221 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
227 offset = start - region->position();
228 len = region->length() - offset;
230 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
231 fade_out = region->fade_out()->back()->when;
233 if (start < region->position() + region->fade_in()->back()->when)
234 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
238 case OverlapExternal:
239 fade_in = region->fade_in()->back()->when;
240 fade_out = region->fade_out()->back()->when;
244 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
247 our_region->set_fade_in_length (fade_in);
248 our_region->set_fade_out_length (fade_out);
254 /* this constructor does NOT notify others (session) */
257 AudioPlaylist::~AudioPlaylist ()
259 _crossfades.clear ();
262 struct RegionSortByLayer {
263 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
264 return a->layer() < b->layer();
269 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
270 framecnt_t cnt, unsigned chan_n)
272 framecnt_t ret = cnt;
274 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n",
275 name(), start, cnt, chan_n, regions.size(), _crossfades.size()));
277 /* optimizing this memset() away involves a lot of conditionals
278 that may well cause more of a hit due to cache misses
279 and related stuff than just doing this here.
281 it would be great if someone could measure this
284 one way or another, parts of the requested area
285 that are not written to by Region::region_at()
286 for all Regions that cover the area need to be
290 memset (buf, 0, sizeof (Sample) * cnt);
292 /* this function is never called from a realtime thread, so
293 its OK to block (for short intervals).
296 Glib::RecMutex::Lock rm (region_lock);
298 framepos_t const end = start + cnt - 1;
299 framecnt_t read_frames = 0;
300 framecnt_t skip_frames = 0;
301 _read_data_count = 0;
303 _read_data_count = 0;
305 RegionList* rlist = regions_to_read (start, start+cnt);
307 if (rlist->empty()) {
312 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
313 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
314 vector<uint32_t> relevant_layers;
316 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
317 if ((*i)->coverage (start, end) != OverlapNone) {
318 relevant_regions[(*i)->layer()].push_back (*i);
319 relevant_layers.push_back ((*i)->layer());
323 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
325 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
326 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ...\n",
327 name(), (*i)->out()->name(), (*i)->in()->name()));
328 if ((*i)->coverage (start, end) != OverlapNone) {
329 relevant_xfades[(*i)->upper_layer()].push_back (*i);
330 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant, place on layer %1\n",
331 (*i)->upper_layer()));
335 // RegionSortByLayer layer_cmp;
336 // relevant_regions.sort (layer_cmp);
338 /* XXX this whole per-layer approach is a hack that
339 should be removed once Crossfades become
340 CrossfadeRegions and we just grab a list of relevant
341 regions and call read_at() on all of them.
344 sort (relevant_layers.begin(), relevant_layers.end());
346 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
348 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
350 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
351 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
354 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
355 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
356 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
358 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
359 _read_data_count += ar->read_data_count();
362 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
363 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
364 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
366 /* don't JACK up _read_data_count, since its the same data as we just
367 read from the regions, and the OS should handle that for us.
378 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
380 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
387 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
392 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
394 if ((*i)->involves (r)) {
395 i = _crossfades.erase (i);
404 AudioPlaylist::flush_notifications (bool from_undo)
406 Playlist::flush_notifications (from_undo);
414 Crossfades::iterator a;
415 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
416 NewCrossfade (*a); /* EMIT SIGNAL */
419 _pending_xfade_adds.clear ();
425 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
427 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
428 set<boost::shared_ptr<Crossfade> > updated;
434 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
436 Crossfades::iterator tmp;
441 /* only update them once */
443 if ((*x)->involves (ar)) {
445 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
448 /* x was successfully inserted into the set, so it has not already been updated */
453 catch (Crossfade::NoCrossfadeHere& err) {
454 // relax, Invalidated during refresh
464 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
466 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
467 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
468 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
470 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
471 Crossfades::iterator tmp;
475 boost::shared_ptr<Crossfade> fade;
477 if ((*x)->_in == orig) {
478 if (! (*x)->covers(right->position())) {
479 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
481 // Overlap, the crossfade is copied on the left side of the right region instead
482 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
486 if ((*x)->_out == orig) {
487 if (! (*x)->covers(right->position())) {
488 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
490 // Overlap, the crossfade is copied on the right side of the left region instead
491 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
496 _crossfades.remove (*x);
497 add_crossfade (fade);
504 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
506 boost::shared_ptr<AudioRegion> other;
507 boost::shared_ptr<AudioRegion> region;
508 boost::shared_ptr<AudioRegion> top;
509 boost::shared_ptr<AudioRegion> bottom;
510 boost::shared_ptr<Crossfade> xfade;
511 RegionList* touched_regions = 0;
513 if (in_set_state || in_partition) {
517 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
518 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
524 refresh_dependents (r);
528 if (!_session.config.get_auto_xfade()) {
532 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
533 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
535 if (other == region) {
539 if (other->muted() || region->muted()) {
543 if (other->position() == r->position() && other->length() == r->length()) {
544 /* precise overlay of two regions - no xfade */
548 if (other->layer() < region->layer()) {
556 if (!top->opaque()) {
560 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
562 delete touched_regions;
566 framecnt_t xfade_length;
571 case OverlapInternal:
572 /* {=============== top =============}
573 * [ ----- bottom ------- ]
577 case OverlapExternal:
579 /* [ -------- top ------- ]
580 * {=========== bottom =============}
583 /* to avoid discontinuities at the region boundaries of an internal
584 overlap (this region is completely within another), we create
585 two hidden crossfades at each boundary. this is not dependent
586 on the auto-xfade option, because we require it as basic
590 xfade_length = min ((framecnt_t) 720, top->length());
592 if (top_region_at (top->first_frame()) == top) {
594 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
595 add_crossfade (xfade);
598 if (top_region_at (top->last_frame() - 1) == top) {
601 only add a fade out if there is no region on top of the end of 'top' (which
605 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
606 add_crossfade (xfade);
611 /* { ==== top ============ }
612 * [---- bottom -------------------]
615 if (_session.config.get_xfade_model() == FullCrossfade) {
616 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
617 if (touched_regions->size() <= 2) {
618 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
619 add_crossfade (xfade);
623 touched_regions = regions_touched (top->first_frame(),
624 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
626 if (touched_regions->size() <= 2) {
627 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
628 add_crossfade (xfade);
635 /* [---- top ------------------------]
636 * { ==== bottom ============ }
639 if (_session.config.get_xfade_model() == FullCrossfade) {
641 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
642 if (touched_regions->size() <= 2) {
643 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
644 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
645 add_crossfade (xfade);
649 touched_regions = regions_touched (bottom->first_frame(),
650 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
652 if (touched_regions->size() <= 2) {
653 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
654 add_crossfade (xfade);
659 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
660 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
661 add_crossfade (xfade);
665 catch (failed_constructor& err) {
669 catch (Crossfade::NoCrossfadeHere& err) {
675 delete touched_regions;
679 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
681 Crossfades::iterator ci;
683 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
684 if (*(*ci) == *xfade) { // Crossfade::operator==()
689 if (ci != _crossfades.end()) {
690 // it will just go away
692 _crossfades.push_back (xfade);
694 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
695 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
697 notify_crossfade_added (xfade);
701 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
703 if (g_atomic_int_get(&block_notifications)) {
704 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
706 NewCrossfade (x); /* EMIT SIGNAL */
711 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
713 Crossfades::iterator i;
714 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
716 xfade->in()->resume_fade_in ();
717 xfade->out()->resume_fade_out ();
719 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
720 _crossfades.erase (i);
725 AudioPlaylist::set_state (const XMLNode& node, int version)
729 XMLNodeConstIterator niter;
733 if (Playlist::set_state (node, version)) {
739 nlist = node.children();
741 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
745 if (child->name() != "Crossfade") {
750 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
751 _crossfades.push_back (xfade);
752 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
753 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
757 catch (failed_constructor& err) {
758 // cout << string_compose (_("could not create crossfade object in playlist %1"),
772 AudioPlaylist::clear (bool with_signals)
774 _crossfades.clear ();
775 Playlist::clear (with_signals);
779 AudioPlaylist::state (bool full_state)
781 XMLNode& node = Playlist::state (full_state);
784 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
785 node.add_child_nocopy ((*i)->get_state());
793 AudioPlaylist::dump () const
795 boost::shared_ptr<Region>r;
796 boost::shared_ptr<Crossfade> x;
798 cerr << "Playlist \"" << _name << "\" " << endl
799 << regions.size() << " regions "
800 << _crossfades.size() << " crossfades"
803 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
805 cerr << " " << r->name() << " @ " << r << " ["
806 << r->start() << "+" << r->length()
814 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
825 << (x->active() ? "yes" : "no")
831 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
833 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
839 bool changed = false;
840 Crossfades::iterator c, ctmp;
841 set<boost::shared_ptr<Crossfade> > unique_xfades;
844 RegionLock rlock (this);
846 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
848 RegionList::iterator tmp = i;
851 if ((*i) == region) {
859 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
861 set<boost::shared_ptr<Region> >::iterator xtmp = x;
864 if ((*x) == region) {
865 all_regions.erase (x);
872 region->set_playlist (boost::shared_ptr<Playlist>());
875 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
879 if ((*c)->involves (r)) {
880 unique_xfades.insert (*c);
881 _crossfades.erase (c);
888 /* overload this, it normally means "removed", not destroyed */
889 notify_region_removed (region);
896 AudioPlaylist::crossfade_changed (const PropertyChange&)
898 if (in_flush || in_set_state) {
902 /* XXX is there a loop here? can an xfade change not happen
903 due to a playlist change? well, sure activation would
904 be an example. maybe we should check the type of change
908 notify_contents_changed ();
912 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
914 if (in_flush || in_set_state) {
918 PropertyChange our_interests;
920 our_interests.add (Properties::fade_in_active);
921 our_interests.add (Properties::fade_out_active);
922 our_interests.add (Properties::scale_amplitude);
923 our_interests.add (Properties::envelope_active);
924 our_interests.add (Properties::envelope);
925 our_interests.add (Properties::fade_in);
926 our_interests.add (Properties::fade_out);
928 bool parent_wants_notify;
930 parent_wants_notify = Playlist::region_changed (what_changed, region);
932 if (parent_wants_notify || (what_changed.contains (our_interests))) {
933 notify_contents_changed ();
940 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
942 RegionLock rlock (this);
944 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
945 framepos_t const start = (*i)->position ();
946 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
948 if (frame >= start && frame <= end) {
949 clist.push_back (*i);
955 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
957 RegionLock rl (this, false);
958 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
964 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
966 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
970 /* don't remove crossfades here; they will be dealt with by the dependency code */
973 boost::shared_ptr<Crossfade>
974 AudioPlaylist::find_crossfade (const PBD::ID& id) const
976 Crossfades::const_iterator i = _crossfades.begin ();
977 while (i != _crossfades.end() && (*i)->id() != id) {
981 if (i == _crossfades.end()) {
982 return boost::shared_ptr<Crossfade> ();
988 struct crossfade_triple {
989 boost::shared_ptr<Region> old_in;
990 boost::shared_ptr<Region> new_in;
991 boost::shared_ptr<Region> new_out;
995 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
997 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
1003 /* our argument is a vector of old and new regions. Each old region
1004 might be participant in a crossfade that is already present. Each new
1005 region is a copy of the old region, present in the other playlist.
1007 our task is to find all the relevant xfades in our playlist (involving
1008 the "old" regions) and place copies of them in the other playlist.
1011 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1012 CrossfadeInfo crossfade_info;
1014 /* build up a record that links crossfades, old regions and new regions
1017 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1019 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1021 if ((*i)->in() == on->first) {
1023 CrossfadeInfo::iterator cf;
1025 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1027 /* already have a record for the old fade-in region,
1028 so note the new fade-in region
1031 cf->second.new_in = on->second;
1035 /* add a record of this crossfade, keeping an association
1036 with the new fade-in region
1039 crossfade_triple ct;
1041 ct.old_in = on->first;
1042 ct.new_in = on->second;
1044 crossfade_info[*i] = ct;
1047 } else if ((*i)->out() == on->first) {
1049 /* this old region is the fade-out region of this crossfade */
1051 CrossfadeInfo::iterator cf;
1053 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1055 /* already have a record for this crossfade, so just keep
1056 an association for the new fade out region
1059 cf->second.new_out = on->second;
1063 /* add a record of this crossfade, keeping an association
1064 with the new fade-in region
1067 crossfade_triple ct;
1069 ct.old_in = on->first;
1070 ct.new_out = on->second;
1072 crossfade_info[*i] = ct;
1078 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1080 /* for each crossfade that involves at least two of the old regions,
1081 create a new identical crossfade with the new regions
1084 if (!ci->second.new_in || !ci->second.new_out) {
1088 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1089 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1090 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1092 /* add it at the right position - which must be at the start
1093 * of the fade-in region
1096 new_xfade->set_position (ci->second.new_in->position());
1097 other_audio->add_crossfade (new_xfade);
1102 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1104 RegionSortByPosition cmp;
1105 boost::shared_ptr<AudioRegion> ar;
1107 sort (copies.begin(), copies.end(), cmp);
1109 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1111 /* disable fade in of the first region */
1114 ar->set_fade_in_active (false);
1117 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1119 /* disable fade out of the last region */
1122 ar->set_fade_out_active (false);
1127 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1129 RegionSortByPosition cmp;
1130 boost::shared_ptr<AudioRegion> ar;
1131 boost::shared_ptr<AudioRegion> cr;
1133 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1137 sort (originals.begin(), originals.end(), cmp);
1139 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1141 /* copy the fade in of the first into the compound region */
1144 cr->set_fade_in (ar->fade_in());
1147 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1150 /* copy the fade out of the last into the compound region */
1151 cr->set_fade_out (ar->fade_out());
1156 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1158 RegionSortByPosition cmp;
1159 boost::shared_ptr<AudioRegion> ar;
1160 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1166 sort (originals.begin(), originals.end(), cmp);
1168 /* no need to call clear_changes() on the originals because that is
1169 * done within Playlist::uncombine ()
1172 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1174 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1178 /* scale the uncombined regions by any gain setting for the
1182 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1184 if (i == originals.begin()) {
1186 /* copy the compound region's fade in back into the first
1190 if (cr->fade_in()->back()->when <= ar->length()) {
1191 /* don't do this if the fade is longer than the
1194 ar->set_fade_in (cr->fade_in());
1198 } else if (*i == originals.back()) {
1200 /* copy the compound region's fade out back into the last
1204 if (cr->fade_out()->back()->when <= ar->length()) {
1205 /* don't do this if the fade is longer than the
1208 ar->set_fade_out (cr->fade_out());
1213 _session.add_command (new StatefulDiffCommand (*i));