Move panner bypass state up to the PannerShell so that it is preserved even when...
[ardour2.git] / libs / ardour / audio_playlist.cc
blob70cd9ec3f5de0f837b4d4210e705a1844d6990be
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/region_sorters.h"
31 #include "ardour/session.h"
32 #include "pbd/enumwriter.h"
34 #include "i18n.h"
36 using namespace ARDOUR;
37 using namespace std;
38 using namespace PBD;
40 namespace ARDOUR {
41 namespace Properties {
42 PBD::PropertyDescriptor<bool> crossfades;
46 void
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))
55 , _playlist (pl)
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);
80 void
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)
104 #ifndef NDEBUG
105 const XMLProperty* prop = node.property("type");
106 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
107 #endif
109 add_property (_crossfades);
111 in_set_state++;
112 if (set_state (node, Stateful::loading_state_version)) {
113 throw failed_constructor();
115 in_set_state--;
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);
154 break;
157 out_o++;
158 out_n++;
160 // cerr << "HUH!? second region in the crossfade not found!" << endl;
164 in_o++;
165 in_n++;
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()));
174 in_set_state++;
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);
189 assert (region);
191 framecnt_t fade_in = 64;
192 framecnt_t fade_out = 64;
194 switch (region->coverage (start, end)) {
195 case OverlapNone:
196 continue;
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;
208 break;
211 case OverlapStart: {
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
216 break;
219 case OverlapEnd: {
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
225 break;
228 case OverlapExternal:
229 fade_in = region->fade_in()->back()->when;
230 fade_out = region->fade_out()->back()->when;
231 break;
234 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
235 assert (our_region);
237 our_region->set_fade_in_length (fade_in);
238 our_region->set_fade_out_length (fade_out);
239 ++ours;
242 in_set_state--;
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();
258 ARDOUR::framecnt_t
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
272 at some point.
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
277 zeroed.
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 framecnt_t read_frames = 0;
290 framecnt_t skip_frames = 0;
291 _read_data_count = 0;
293 _read_data_count = 0;
295 RegionList* rlist = regions_to_read (start, start+cnt);
297 if (rlist->empty()) {
298 delete rlist;
299 return cnt;
302 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
303 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
304 vector<uint32_t> relevant_layers;
306 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
307 if ((*i)->coverage (start, end) != OverlapNone) {
308 relevant_regions[(*i)->layer()].push_back (*i);
309 relevant_layers.push_back ((*i)->layer());
313 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
315 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
316 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ...\n",
317 name(), (*i)->out()->name(), (*i)->in()->name()));
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, place on layer %1\n",
321 (*i)->upper_layer()));
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()));
347 assert(ar);
348 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
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.
362 delete rlist;
363 return ret;
367 void
368 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
370 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
372 if (in_set_state) {
373 return;
376 if (r == 0) {
377 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
378 << endmsg;
379 return;
382 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
384 if ((*i)->involves (r)) {
385 i = _crossfades.erase (i);
386 } else {
387 ++i;
393 void
394 AudioPlaylist::flush_notifications (bool from_undo)
396 Playlist::flush_notifications (from_undo);
398 if (in_flush) {
399 return;
402 in_flush = true;
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 ();
411 in_flush = false;
414 void
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;
420 if (ar == 0) {
421 return;
424 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
426 Crossfades::iterator tmp;
428 tmp = x;
429 ++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);
437 if (u.second) {
438 /* x was successfully inserted into the set, so it has not already been updated */
439 try {
440 (*x)->refresh ();
443 catch (Crossfade::NoCrossfadeHere& err) {
444 // relax, Invalidated during refresh
449 x = tmp;
453 void
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;
462 tmp = x;
463 ++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));
470 } else {
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));
479 } else {
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));
485 if (fade) {
486 _crossfades.remove (*x);
487 add_crossfade (fade);
489 x = tmp;
493 void
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) {
504 return;
507 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
508 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
509 << endmsg;
510 return;
513 if (!norefresh) {
514 refresh_dependents (r);
518 if (!_session.config.get_auto_xfade()) {
519 return;
522 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
523 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
525 if (other == region) {
526 continue;
529 if (other->muted() || region->muted()) {
530 continue;
533 if (other->position() == r->position() && other->length() == r->length()) {
534 /* precise overlay of two regions - no xfade */
535 continue;
538 if (other->layer() < region->layer()) {
539 top = region;
540 bottom = other;
541 } else {
542 top = other;
543 bottom = region;
546 if (!top->opaque()) {
547 continue;
550 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
552 delete touched_regions;
553 touched_regions = 0;
555 try {
556 framecnt_t xfade_length;
557 switch (c) {
558 case OverlapNone:
559 break;
561 case OverlapInternal:
562 /* {=============== top =============}
563 * [ ----- bottom ------- ]
565 break;
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
577 audio engineering.
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, top->first_frame(), StartOfIn));
585 add_crossfade (xfade);
588 if (top_region_at (top->last_frame() - 1) == top) {
591 only add a fade out if there is no region on top of the end of 'top' (which
592 would cover it).
595 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
596 add_crossfade (xfade);
598 break;
599 case OverlapStart:
601 /* { ==== top ============ }
602 * [---- bottom -------------------]
605 if (_session.config.get_xfade_model() == FullCrossfade) {
606 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
607 if (touched_regions->size() <= 2) {
608 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
609 add_crossfade (xfade);
611 } else {
613 touched_regions = regions_touched (top->first_frame(),
614 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
615 top->length()));
616 if (touched_regions->size() <= 2) {
617 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
618 add_crossfade (xfade);
621 break;
622 case OverlapEnd:
625 /* [---- top ------------------------]
626 * { ==== bottom ============ }
629 if (_session.config.get_xfade_model() == FullCrossfade) {
631 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
632 if (touched_regions->size() <= 2) {
633 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
634 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
635 add_crossfade (xfade);
638 } else {
639 touched_regions = regions_touched (bottom->first_frame(),
640 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
641 bottom->length()));
642 if (touched_regions->size() <= 2) {
643 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
644 add_crossfade (xfade);
647 break;
648 default:
649 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
650 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
651 add_crossfade (xfade);
655 catch (failed_constructor& err) {
656 continue;
659 catch (Crossfade::NoCrossfadeHere& err) {
660 continue;
665 delete touched_regions;
668 void
669 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
671 Crossfades::iterator ci;
673 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
674 if (*(*ci) == *xfade) { // Crossfade::operator==()
675 break;
679 if (ci != _crossfades.end()) {
680 // it will just go away
681 } else {
682 _crossfades.push_back (xfade);
684 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
685 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
687 notify_crossfade_added (xfade);
691 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
693 if (g_atomic_int_get(&block_notifications)) {
694 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
695 } else {
696 NewCrossfade (x); /* EMIT SIGNAL */
700 void
701 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
703 Crossfades::iterator i;
704 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
706 xfade->in()->resume_fade_in ();
707 xfade->out()->resume_fade_out ();
709 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
710 _crossfades.erase (i);
715 AudioPlaylist::set_state (const XMLNode& node, int version)
717 XMLNode *child;
718 XMLNodeList nlist;
719 XMLNodeConstIterator niter;
721 in_set_state++;
723 if (Playlist::set_state (node, version)) {
724 return -1;
727 freeze ();
729 nlist = node.children();
731 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
733 child = *niter;
735 if (child->name() != "Crossfade") {
736 continue;
739 try {
740 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
741 _crossfades.push_back (xfade);
742 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
743 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
744 NewCrossfade(xfade);
747 catch (failed_constructor& err) {
748 // cout << string_compose (_("could not create crossfade object in playlist %1"),
749 // _name)
750 // << endl;
751 continue;
755 thaw ();
756 in_set_state--;
758 return 0;
761 void
762 AudioPlaylist::clear (bool with_signals)
764 _crossfades.clear ();
765 Playlist::clear (with_signals);
768 XMLNode&
769 AudioPlaylist::state (bool full_state)
771 XMLNode& node = Playlist::state (full_state);
773 if (full_state) {
774 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
775 node.add_child_nocopy ((*i)->get_state());
779 return node;
782 void
783 AudioPlaylist::dump () const
785 boost::shared_ptr<Region>r;
786 boost::shared_ptr<Crossfade> x;
788 cerr << "Playlist \"" << _name << "\" " << endl
789 << regions.size() << " regions "
790 << _crossfades.size() << " crossfades"
791 << endl;
793 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
794 r = *i;
795 cerr << " " << r->name() << " @ " << r << " ["
796 << r->start() << "+" << r->length()
797 << "] at "
798 << r->position()
799 << " on layer "
800 << r->layer ()
801 << endl;
804 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
805 x = *i;
806 cerr << " xfade ["
807 << x->out()->name()
808 << ','
809 << x->in()->name()
810 << " @ "
811 << x->position()
812 << " length = "
813 << x->length ()
814 << " active ? "
815 << (x->active() ? "yes" : "no")
816 << endl;
820 bool
821 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
823 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
825 if (!r) {
826 return false;
829 bool changed = false;
830 Crossfades::iterator c, ctmp;
831 set<boost::shared_ptr<Crossfade> > unique_xfades;
834 RegionLock rlock (this);
836 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
838 RegionList::iterator tmp = i;
839 ++tmp;
841 if ((*i) == region) {
842 regions.erase (i);
843 changed = true;
846 i = tmp;
849 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
851 set<boost::shared_ptr<Region> >::iterator xtmp = x;
852 ++xtmp;
854 if ((*x) == region) {
855 all_regions.erase (x);
856 changed = true;
859 x = xtmp;
862 region->set_playlist (boost::shared_ptr<Playlist>());
865 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
866 ctmp = c;
867 ++ctmp;
869 if ((*c)->involves (r)) {
870 unique_xfades.insert (*c);
871 _crossfades.erase (c);
874 c = ctmp;
877 if (changed) {
878 /* overload this, it normally means "removed", not destroyed */
879 notify_region_removed (region);
882 return changed;
885 void
886 AudioPlaylist::crossfade_changed (const PropertyChange&)
888 if (in_flush || in_set_state) {
889 return;
892 /* XXX is there a loop here? can an xfade change not happen
893 due to a playlist change? well, sure activation would
894 be an example. maybe we should check the type of change
895 that occured.
898 notify_contents_changed ();
901 bool
902 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
904 if (in_flush || in_set_state) {
905 return false;
908 PropertyChange our_interests;
910 our_interests.add (Properties::fade_in_active);
911 our_interests.add (Properties::fade_out_active);
912 our_interests.add (Properties::scale_amplitude);
913 our_interests.add (Properties::envelope_active);
914 our_interests.add (Properties::envelope);
915 our_interests.add (Properties::fade_in);
916 our_interests.add (Properties::fade_out);
918 bool parent_wants_notify;
920 parent_wants_notify = Playlist::region_changed (what_changed, region);
922 if (parent_wants_notify || (what_changed.contains (our_interests))) {
923 notify_contents_changed ();
926 return true;
929 void
930 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
932 RegionLock rlock (this);
934 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
935 framepos_t const start = (*i)->position ();
936 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
938 if (frame >= start && frame <= end) {
939 clist.push_back (*i);
944 void
945 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
947 RegionLock rl (this, false);
948 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
949 s (*i);
953 void
954 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
956 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
957 add_crossfade (*i);
960 /* don't remove crossfades here; they will be dealt with by the dependency code */
963 boost::shared_ptr<Crossfade>
964 AudioPlaylist::find_crossfade (const PBD::ID& id) const
966 Crossfades::const_iterator i = _crossfades.begin ();
967 while (i != _crossfades.end() && (*i)->id() != id) {
968 ++i;
971 if (i == _crossfades.end()) {
972 return boost::shared_ptr<Crossfade> ();
975 return *i;
978 struct crossfade_triple {
979 boost::shared_ptr<Region> old_in;
980 boost::shared_ptr<Region> new_in;
981 boost::shared_ptr<Region> new_out;
984 void
985 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
987 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
989 if (!other_audio) {
990 return;
993 /* our argument is a vector of old and new regions. Each old region
994 might be participant in a crossfade that is already present. Each new
995 region is a copy of the old region, present in the other playlist.
997 our task is to find all the relevant xfades in our playlist (involving
998 the "old" regions) and place copies of them in the other playlist.
1001 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1002 CrossfadeInfo crossfade_info;
1004 /* build up a record that links crossfades, old regions and new regions
1007 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1009 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1011 if ((*i)->in() == on->first) {
1013 CrossfadeInfo::iterator cf;
1015 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1017 /* already have a record for the old fade-in region,
1018 so note the new fade-in region
1021 cf->second.new_in = on->second;
1023 } else {
1025 /* add a record of this crossfade, keeping an association
1026 with the new fade-in region
1029 crossfade_triple ct;
1031 ct.old_in = on->first;
1032 ct.new_in = on->second;
1034 crossfade_info[*i] = ct;
1037 } else if ((*i)->out() == on->first) {
1039 /* this old region is the fade-out region of this crossfade */
1041 CrossfadeInfo::iterator cf;
1043 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1045 /* already have a record for this crossfade, so just keep
1046 an association for the new fade out region
1049 cf->second.new_out = on->second;
1051 } else {
1053 /* add a record of this crossfade, keeping an association
1054 with the new fade-in region
1057 crossfade_triple ct;
1059 ct.old_in = on->first;
1060 ct.new_out = on->second;
1062 crossfade_info[*i] = ct;
1068 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1070 /* for each crossfade that involves at least two of the old regions,
1071 create a new identical crossfade with the new regions
1074 if (!ci->second.new_in || !ci->second.new_out) {
1075 continue;
1078 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1079 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1080 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1082 /* add it at the right position - which must be at the start
1083 * of the fade-in region
1086 new_xfade->set_position (ci->second.new_in->position());
1087 other_audio->add_crossfade (new_xfade);
1091 void
1092 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1094 RegionSortByPosition cmp;
1095 boost::shared_ptr<AudioRegion> ar;
1097 sort (copies.begin(), copies.end(), cmp);
1099 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1101 /* disable fade in of the first region */
1103 if (ar) {
1104 ar->set_fade_in_active (false);
1107 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1109 /* disable fade out of the last region */
1111 if (ar) {
1112 ar->set_fade_out_active (false);
1116 void
1117 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1119 RegionSortByPosition cmp;
1120 boost::shared_ptr<AudioRegion> ar;
1121 boost::shared_ptr<AudioRegion> cr;
1123 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1124 return;
1127 sort (originals.begin(), originals.end(), cmp);
1129 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1131 /* copy the fade in of the first into the compound region */
1133 if (ar) {
1134 cr->set_fade_in (ar->fade_in());
1137 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1139 if (ar) {
1140 /* copy the fade out of the last into the compound region */
1141 cr->set_fade_out (ar->fade_out());
1145 void
1146 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1148 RegionSortByPosition cmp;
1149 boost::shared_ptr<AudioRegion> ar;
1150 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1152 if (!cr) {
1153 return;
1156 sort (originals.begin(), originals.end(), cmp);
1158 /* no need to call clear_changes() on the originals because that is
1159 * done within Playlist::uncombine ()
1162 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1164 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1165 continue;
1168 /* scale the uncombined regions by any gain setting for the
1169 * compound one.
1172 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1174 if (i == originals.begin()) {
1176 /* copy the compound region's fade in back into the first
1177 original region.
1180 if (cr->fade_in()->back()->when <= ar->length()) {
1181 /* don't do this if the fade is longer than the
1182 * region
1184 ar->set_fade_in (cr->fade_in());
1188 } else if (*i == originals.back()) {
1190 /* copy the compound region's fade out back into the last
1191 original region.
1194 if (cr->fade_out()->back()->when <= ar->length()) {
1195 /* don't do this if the fade is longer than the
1196 * region
1198 ar->set_fade_out (cr->fade_out());
1203 _session.add_command (new StatefulDiffCommand (*i));