remove absurd internal Crossfade constructor that confused region start and region...
[ardour.git] / libs / ardour / audio_playlist.cc
1 /*
2     Copyright (C) 2003 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <algorithm>
21
22 #include <cstdlib>
23
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"
33
34 #include "i18n.h"
35
36 using namespace ARDOUR;
37 using namespace std;
38 using namespace PBD;
39
40 namespace ARDOUR {
41         namespace Properties {
42                 PBD::PropertyDescriptor<bool> crossfades;
43         }
44 }
45
46 void
47 AudioPlaylist::make_property_quarks ()
48 {
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));
51 }
52
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)
56 {
57
58 }
59
60 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
61         : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
62         , _playlist (p._playlist)
63 {
64
65 }
66
67
68 CrossfadeListProperty *
69 CrossfadeListProperty::create () const
70 {
71         return new CrossfadeListProperty (_playlist);
72 }
73
74 CrossfadeListProperty *
75 CrossfadeListProperty::clone () const
76 {
77         return new CrossfadeListProperty (*this);
78 }
79
80 void
81 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
82 {
83         /* Crossfades are not written to any state when they are no
84            longer in use, so we must write their state here.
85         */
86
87         XMLNode& c = xfade->get_state ();
88         node.add_child_nocopy (c);
89 }
90
91 boost::shared_ptr<Crossfade>
92 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
93 {
94         XMLNodeList const c = node.children ();
95         assert (c.size() == 1);
96         return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
97 }
98
99
100 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
101         : Playlist (session, node, DataType::AUDIO, hidden)
102         , _crossfades (*this)
103 {
104 #ifndef NDEBUG
105         const XMLProperty* prop = node.property("type");
106         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
107 #endif
108
109         add_property (_crossfades);
110
111         in_set_state++;
112         if (set_state (node, Stateful::loading_state_version)) {
113                 throw failed_constructor();
114         }
115         in_set_state--;
116 }
117
118 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
119         : Playlist (session, name, DataType::AUDIO, hidden)
120         , _crossfades (*this)
121 {
122         add_property (_crossfades);
123 }
124
125 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
126         : Playlist (other, name, hidden)
127         , _crossfades (*this)
128 {
129         add_property (_crossfades);
130
131         RegionList::const_iterator in_o  = other->regions.begin();
132         RegionList::iterator in_n = regions.begin();
133
134         while (in_o != other->regions.end()) {
135                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
136
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!
141
142                                 RegionList::const_iterator out_o = other->regions.begin();
143                                 RegionList::const_iterator out_n = regions.begin();
144
145                                 while (out_o != other->regions.end()) {
146
147                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
148
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;
155                                         }
156
157                                         out_o++;
158                                         out_n++;
159                                 }
160 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
161                         }
162                 }
163
164                 in_o++;
165                 in_n++;
166         }
167 }
168
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)
172 {
173         RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
174         in_set_state++;
175
176         add_property (_crossfades);
177
178         framepos_t const end = start + cnt - 1;
179
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.
183         */
184
185         RegionList::iterator ours = regions.begin ();
186
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);
190
191                 framecnt_t fade_in = 64;
192                 framecnt_t fade_out = 64;
193
194                 switch (region->coverage (start, end)) {
195                 case OverlapNone:
196                         continue;
197
198                 case OverlapInternal:
199                 {
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;
204                         }
205                         if (region->fade_out()->back()->when > trim) {
206                                 fade_out = region->fade_out()->back()->when - trim;
207                         }
208                         break;
209                 }
210
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;
217                 }
218
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;
222
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;
226                 }
227
228                 case OverlapExternal:
229                         fade_in = region->fade_in()->back()->when;
230                         fade_out = region->fade_out()->back()->when;
231                         break;
232                 }
233
234                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
235                 assert (our_region);
236
237                 our_region->set_fade_in_length (fade_in);
238                 our_region->set_fade_out_length (fade_out);
239                 ++ours;
240         }
241
242         in_set_state--;
243
244         /* this constructor does NOT notify others (session) */
245 }
246
247 AudioPlaylist::~AudioPlaylist ()
248 {
249         _crossfades.clear ();
250 }
251
252 struct RegionSortByLayer {
253     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
254             return a->layer() < b->layer();
255     }
256 };
257
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)
261 {
262         framecnt_t ret = cnt;
263
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()));
266
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.
270
271            it would be great if someone could measure this
272            at some point.
273
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.
278         */
279
280         memset (buf, 0, sizeof (Sample) * cnt);
281
282         /* this function is never called from a realtime thread, so
283            its OK to block (for short intervals).
284         */
285
286         Glib::RecMutex::Lock rm (region_lock);
287
288         framepos_t const end = start + cnt - 1;
289         _read_data_count = 0;
290
291         _read_data_count = 0;
292
293         RegionList* rlist = regions_to_read (start, start+cnt);
294
295         if (rlist->empty()) {
296                 delete rlist;
297                 return cnt;
298         }
299
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;
303
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());
308                 }
309         }
310
311         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
312
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(),
317                                                                    start, end));
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))));
322                 }
323         }
324
325 //      RegionSortByLayer layer_cmp;
326 //      relevant_regions.sort (layer_cmp);
327
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.
332         */
333
334         sort (relevant_layers.begin(), relevant_layers.end());
335
336         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
337
338                 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
339
340                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
341                 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
342
343
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);
349                         _read_data_count += ar->read_data_count();
350                 }
351
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);
355
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.
358                         */
359                 }
360         }
361
362         delete rlist;
363         return ret;
364 }
365
366
367 void
368 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
369 {
370         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
371
372         if (in_set_state) {
373                 return;
374         }
375
376         if (r == 0) {
377                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
378                       << endmsg;
379                 return;
380         }
381
382         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
383
384                 if ((*i)->involves (r)) {
385                         i = _crossfades.erase (i);
386                 } else {
387                         ++i;
388                 }
389         }
390 }
391
392
393 void
394 AudioPlaylist::flush_notifications (bool from_undo)
395 {
396         Playlist::flush_notifications (from_undo);
397
398         if (in_flush) {
399                 return;
400         }
401
402         in_flush = true;
403
404         Crossfades::iterator a;
405         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
406                 NewCrossfade (*a); /* EMIT SIGNAL */
407         }
408
409         _pending_xfade_adds.clear ();
410
411         in_flush = false;
412 }
413
414 void
415 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
416 {
417         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
418         set<boost::shared_ptr<Crossfade> > updated;
419
420         if (ar == 0) {
421                 return;
422         }
423
424         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
425
426                 Crossfades::iterator tmp;
427
428                 tmp = x;
429                 ++tmp;
430
431                 /* only update them once */
432
433                 if ((*x)->involves (ar)) {
434
435                         pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
436
437                         if (u.second) {
438                                 /* x was successfully inserted into the set, so it has not already been updated */
439                                 try {
440                                         (*x)->refresh ();
441                                 }
442
443                                 catch (Crossfade::NoCrossfadeHere& err) {
444                                         // relax, Invalidated during refresh
445                                 }
446                         }
447                 }
448
449                 x = tmp;
450         }
451 }
452
453 void
454 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
455 {
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);
459
460         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
461                 Crossfades::iterator tmp;
462                 tmp = x;
463                 ++tmp;
464
465                 boost::shared_ptr<Crossfade> fade;
466
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));
473                         }
474                 }
475
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));
482                         }
483                 }
484
485                 if (fade) {
486                         _crossfades.remove (*x);
487                         add_crossfade (fade);
488                 }
489                 x = tmp;
490         }
491 }
492
493 void
494 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
495 {
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;
502
503         if (in_set_state || in_partition) {
504                 return;
505         }
506
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;
511         }
512
513         if (!norefresh) {
514                 refresh_dependents (r);
515         }
516
517
518         if (!_session.config.get_auto_xfade()) {
519                 return;
520         }
521
522         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
523                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
524
525                 if (other == region) {
526                         continue;
527                 }
528
529                 if (other->muted() || region->muted()) {
530                         continue;
531                 }
532
533                 if (other->position() == r->position() && other->length() == r->length()) {
534                         /* precise overlay of two regions - no xfade */
535                         continue;
536                 }
537
538                 if (other->layer() < region->layer()) {
539                         top = region;
540                         bottom = other;
541                 } else {
542                         top = other;
543                         bottom = region;
544                 }
545
546                 if (!top->opaque()) {
547                         continue;
548                 }
549
550                 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
551
552                 delete touched_regions;
553                 touched_regions = 0;
554
555                 try {
556                         framecnt_t xfade_length;
557                         switch (c) {
558                         case OverlapNone:
559                                 break;
560
561                         case OverlapInternal:
562                                  /* {=============== top  =============}
563                                   *     [ ----- bottom  ------- ]
564                                   */
565                                 break;
566
567                         case OverlapExternal:
568
569                                 /*     [ -------- top ------- ]
570                                  * {=========== bottom =============}
571                                  */
572
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.
578                                 */
579
580                                 xfade_length = min ((framecnt_t) 720, top->length());
581
582                                 if (top_region_at (top->first_frame()) == top) {
583
584                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
585                                         xfade->set_position (top->first_frame());
586                                         add_crossfade (xfade);
587                                 }
588
589                                 if (top_region_at (top->last_frame() - 1) == top) {
590
591                                         /*
592                                            only add a fade out if there is no region on top of the end of 'top' (which
593                                            would cover it).
594                                         */
595
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);
599                                 }
600                                 break;
601                         case OverlapStart:
602
603                                 /*                   { ==== top ============ }
604                                  *   [---- bottom -------------------]
605                                  */
606
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);
612                                         }
613                                 } else {
614
615                                         touched_regions = regions_touched (top->first_frame(),
616                                                                            top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
617                                                                                                      top->length()));
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);
621                                         }
622                                 }
623                                 break;
624                         case OverlapEnd:
625
626
627                                 /* [---- top ------------------------]
628                                  *                { ==== bottom ============ }
629                                  */
630
631                                 if (_session.config.get_xfade_model() == FullCrossfade) {
632
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);
638                                         }
639
640                                 } else {
641                                         touched_regions = regions_touched (bottom->first_frame(),
642                                                                            bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
643                                                                                                         bottom->length()));
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);
647                                         }
648                                 }
649                                 break;
650                         default:
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);
654                         }
655                 }
656
657                 catch (failed_constructor& err) {
658                         continue;
659                 }
660
661                 catch (Crossfade::NoCrossfadeHere& err) {
662                         continue;
663                 }
664
665         }
666
667         delete touched_regions;
668 }
669
670 void
671 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
672 {
673         Crossfades::iterator ci;
674
675         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
676                 if (*(*ci) == *xfade) { // Crossfade::operator==()
677                         break;
678                 }
679         }
680
681         if (ci != _crossfades.end()) {
682                 // it will just go away
683         } else {
684                 _crossfades.push_back (xfade);
685
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));
688
689                 notify_crossfade_added (xfade);
690         }
691 }
692
693 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
694 {
695         if (g_atomic_int_get(&block_notifications)) {
696                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
697         } else {
698                 NewCrossfade (x); /* EMIT SIGNAL */
699         }
700 }
701
702 void
703 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
704 {
705         Crossfades::iterator i;
706         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
707
708         xfade->in()->resume_fade_in ();
709         xfade->out()->resume_fade_out ();
710
711         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
712                 _crossfades.erase (i);
713         }
714 }
715
716 int
717 AudioPlaylist::set_state (const XMLNode& node, int version)
718 {
719         XMLNode *child;
720         XMLNodeList nlist;
721         XMLNodeConstIterator niter;
722
723         in_set_state++;
724
725         if (Playlist::set_state (node, version)) {
726                 return -1;
727         }
728
729         freeze ();
730
731         nlist = node.children();
732
733         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
734
735                 child = *niter;
736
737                 if (child->name() != "Crossfade") {
738                         continue;
739                 }
740
741                 try {
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));
746                         NewCrossfade(xfade);
747                 }
748
749                 catch (failed_constructor& err) {
750                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
751                         //        _name)
752                         //    << endl;
753                         continue;
754                 }
755         }
756
757         thaw ();
758         in_set_state--;
759
760         return 0;
761 }
762
763 void
764 AudioPlaylist::clear (bool with_signals)
765 {
766         _crossfades.clear ();
767         Playlist::clear (with_signals);
768 }
769
770 XMLNode&
771 AudioPlaylist::state (bool full_state)
772 {
773         XMLNode& node = Playlist::state (full_state);
774
775         if (full_state) {
776                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
777                         node.add_child_nocopy ((*i)->get_state());
778                 }
779         }
780
781         return node;
782 }
783
784 void
785 AudioPlaylist::dump () const
786 {
787         boost::shared_ptr<Region>r;
788         boost::shared_ptr<Crossfade> x;
789
790         cerr << "Playlist \"" << _name << "\" " << endl
791              << regions.size() << " regions "
792              << _crossfades.size() << " crossfades"
793              << endl;
794
795         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
796                 r = *i;
797                 cerr << "  " << r->name() << " @ " << r << " ["
798                      << r->start() << "+" << r->length()
799                      << "] at "
800                      << r->position()
801                      << " on layer "
802                      << r->layer ()
803                      << endl;
804         }
805
806         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
807                 x = *i;
808                 cerr << "  xfade ["
809                      << x->out()->name()
810                      << ','
811                      << x->in()->name()
812                      << " @ "
813                      << x->position()
814                      << " length = "
815                      << x->length ()
816                      << " active ? "
817                      << (x->active() ? "yes" : "no")
818                      << endl;
819         }
820 }
821
822 bool
823 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
824 {
825         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
826
827         if (!r) {
828                 return false;
829         }
830
831         bool changed = false;
832         Crossfades::iterator c, ctmp;
833         set<boost::shared_ptr<Crossfade> > unique_xfades;
834
835         {
836                 RegionLock rlock (this);
837
838                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
839
840                         RegionList::iterator tmp = i;
841                         ++tmp;
842
843                         if ((*i) == region) {
844                                 regions.erase (i);
845                                 changed = true;
846                         }
847
848                         i = tmp;
849                 }
850
851                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
852
853                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
854                         ++xtmp;
855
856                         if ((*x) == region) {
857                                 all_regions.erase (x);
858                                 changed = true;
859                         }
860
861                         x = xtmp;
862                 }
863
864                 region->set_playlist (boost::shared_ptr<Playlist>());
865         }
866
867         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
868                 ctmp = c;
869                 ++ctmp;
870
871                 if ((*c)->involves (r)) {
872                         unique_xfades.insert (*c);
873                         _crossfades.erase (c);
874                 }
875
876                 c = ctmp;
877         }
878
879         if (changed) {
880                 /* overload this, it normally means "removed", not destroyed */
881                 notify_region_removed (region);
882         }
883
884         return changed;
885 }
886
887 void
888 AudioPlaylist::crossfade_changed (const PropertyChange&)
889 {
890         if (in_flush || in_set_state) {
891                 return;
892         }
893
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
897            that occured.
898         */
899
900         notify_contents_changed ();
901 }
902
903 bool
904 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
905 {
906         if (in_flush || in_set_state) {
907                 return false;
908         }
909
910         PropertyChange our_interests;
911
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);
919
920         bool parent_wants_notify;
921
922         parent_wants_notify = Playlist::region_changed (what_changed, region);
923
924         if (parent_wants_notify || (what_changed.contains (our_interests))) {
925                 notify_contents_changed ();
926         }
927
928         return true;
929 }
930
931 void
932 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
933 {
934         RegionLock rlock (this);
935
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
939
940                 if (frame >= start && frame <= end) {
941                         clist.push_back (*i);
942                 }
943         }
944 }
945
946 void
947 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
948 {
949         RegionLock rl (this, false);
950         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
951                 s (*i);
952         }
953 }
954
955 void
956 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
957 {
958         for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
959                 add_crossfade (*i);
960         }
961
962         /* don't remove crossfades here; they will be dealt with by the dependency code */
963 }
964
965 boost::shared_ptr<Crossfade>
966 AudioPlaylist::find_crossfade (const PBD::ID& id) const
967 {
968         Crossfades::const_iterator i = _crossfades.begin ();
969         while (i != _crossfades.end() && (*i)->id() != id) {
970                 ++i;
971         }
972
973         if (i == _crossfades.end()) {
974                 return boost::shared_ptr<Crossfade> ();
975         }
976
977         return *i;
978 }
979
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;
984 };
985
986 void
987 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
988 {
989         AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
990
991         if (!other_audio) {
992                 return;
993         }
994
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.
998
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.
1001         */
1002
1003         typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1004         CrossfadeInfo crossfade_info;
1005
1006         /* build up a record that links crossfades, old regions and new regions
1007          */
1008
1009         for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1010
1011                 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1012
1013                         if ((*i)->in() == on->first) {
1014
1015                                 CrossfadeInfo::iterator cf;
1016
1017                                 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1018
1019                                         /* already have a record for the old fade-in region,
1020                                            so note the new fade-in region
1021                                         */
1022
1023                                         cf->second.new_in = on->second;
1024
1025                                 } else {
1026
1027                                         /* add a record of this crossfade, keeping an association
1028                                            with the new fade-in region
1029                                         */
1030
1031                                         crossfade_triple ct;
1032
1033                                         ct.old_in = on->first;
1034                                         ct.new_in = on->second;
1035
1036                                         crossfade_info[*i] = ct;
1037                                 }
1038
1039                         } else if ((*i)->out() == on->first) {
1040
1041                                 /* this old region is the fade-out region of this crossfade */
1042
1043                                 CrossfadeInfo::iterator cf;
1044
1045                                 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1046
1047                                         /* already have a record for this crossfade, so just keep
1048                                            an association for the new fade out region
1049                                         */
1050
1051                                         cf->second.new_out = on->second;
1052
1053                                 } else {
1054
1055                                         /* add a record of this crossfade, keeping an association
1056                                            with the new fade-in region
1057                                         */
1058
1059                                         crossfade_triple ct;
1060
1061                                         ct.old_in = on->first;
1062                                         ct.new_out = on->second;
1063
1064                                         crossfade_info[*i] = ct;
1065                                 }
1066                         }
1067                 }
1068         }
1069
1070         for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1071
1072                 /* for each crossfade that involves at least two of the old regions,
1073                    create a new identical crossfade with the new regions
1074                 */
1075
1076                 if (!ci->second.new_in || !ci->second.new_out) {
1077                         continue;
1078                 }
1079
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)));
1083
1084                 /* add it at the right position - which must be at the start
1085                  * of the fade-in region
1086                  */
1087
1088                 new_xfade->set_position (ci->second.new_in->position());
1089                 other_audio->add_crossfade (new_xfade);
1090         }
1091 }
1092
1093 void
1094 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1095 {
1096         RegionSortByPosition cmp;
1097         boost::shared_ptr<AudioRegion> ar;
1098
1099         sort (copies.begin(), copies.end(), cmp);
1100
1101         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1102
1103         /* disable fade in of the first region */
1104
1105         if (ar) {
1106                 ar->set_fade_in_active (false);
1107         }
1108
1109         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1110
1111         /* disable fade out of the last region */
1112
1113         if (ar) {
1114                 ar->set_fade_out_active (false);
1115         }
1116 }
1117
1118 void
1119 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1120 {
1121         RegionSortByPosition cmp;
1122         boost::shared_ptr<AudioRegion> ar;
1123         boost::shared_ptr<AudioRegion> cr;
1124
1125         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1126                 return;
1127         }
1128
1129         sort (originals.begin(), originals.end(), cmp);
1130
1131         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1132
1133         /* copy the fade in of the first into the compound region */
1134
1135         if (ar) {
1136                 cr->set_fade_in (ar->fade_in());
1137         }
1138
1139         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1140
1141         if (ar) {
1142                 /* copy the fade out of the last into the compound region */
1143                 cr->set_fade_out (ar->fade_out());
1144         }
1145 }
1146
1147 void
1148 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1149 {
1150         RegionSortByPosition cmp;
1151         boost::shared_ptr<AudioRegion> ar;
1152         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1153
1154         if (!cr) {
1155                 return;
1156         }
1157
1158         sort (originals.begin(), originals.end(), cmp);
1159
1160         /* no need to call clear_changes() on the originals because that is
1161          * done within Playlist::uncombine ()
1162          */
1163
1164         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1165
1166                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1167                         continue;
1168                 }
1169
1170                 /* scale the uncombined regions by any gain setting for the
1171                  * compound one.
1172                  */
1173
1174                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1175
1176                 if (i == originals.begin()) {
1177
1178                         /* copy the compound region's fade in back into the first
1179                            original region.
1180                         */
1181
1182                         if (cr->fade_in()->back()->when <= ar->length()) {
1183                                 /* don't do this if the fade is longer than the
1184                                  * region
1185                                  */
1186                                 ar->set_fade_in (cr->fade_in());
1187                         }
1188
1189
1190                 } else if (*i == originals.back()) {
1191
1192                         /* copy the compound region's fade out back into the last
1193                            original region.
1194                         */
1195
1196                         if (cr->fade_out()->back()->when <= ar->length()) {
1197                                 /* don't do this if the fade is longer than the
1198                                  * region
1199                                  */
1200                                 ar->set_fade_out (cr->fade_out());
1201                         }
1202
1203                 }
1204
1205                 _session.add_command (new StatefulDiffCommand (*i));
1206         }
1207 }