restore compound region fade, where possible, to constituent regions, after uncombine
[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         set_state (node, Stateful::loading_state_version);
113         in_set_state--;
114 }
115
116 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
117         : Playlist (session, name, DataType::AUDIO, hidden)
118         , _crossfades (*this)
119 {
120         add_property (_crossfades);
121 }
122
123 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
124         : Playlist (other, name, hidden)
125         , _crossfades (*this)
126 {
127         add_property (_crossfades);
128         
129         RegionList::const_iterator in_o  = other->regions.begin();
130         RegionList::iterator in_n = regions.begin();
131
132         while (in_o != other->regions.end()) {
133                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
134
135                 // We look only for crossfades which begin with the current region, so we don't get doubles
136                 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
137                         if ((*xfades)->in() == ar) {
138                                 // We found one! Now copy it!
139
140                                 RegionList::const_iterator out_o = other->regions.begin();
141                                 RegionList::const_iterator out_n = regions.begin();
142
143                                 while (out_o != other->regions.end()) {
144
145                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
146
147                                         if ((*xfades)->out() == ar2) {
148                                                 boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
149                                                 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
150                                                 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
151                                                 add_crossfade(new_fade);
152                                                 break;
153                                         }
154
155                                         out_o++;
156                                         out_n++;
157                                 }
158 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
159                         }
160                 }
161
162                 in_o++;
163                 in_n++;
164         }
165 }
166
167 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
168         : Playlist (other, start, cnt, name, hidden)
169         , _crossfades (*this)
170 {
171         RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
172         in_set_state++;
173         
174         add_property (_crossfades);
175
176         framepos_t const end = start + cnt - 1;
177
178         /* Audio regions that have been created by the Playlist constructor
179            will currently have the same fade in/out as the regions that they
180            were created from.  This is wrong, so reset the fades here.
181         */
182
183         RegionList::iterator ours = regions.begin ();
184
185         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
186                 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
187                 assert (region);
188
189                 framecnt_t fade_in = 64;
190                 framecnt_t fade_out = 64;
191                 framepos_t position;
192                 framecnt_t len;
193                 frameoffset_t offset;
194
195                 switch (region->coverage (start, end)) {
196                 case OverlapNone:
197                         continue;
198
199                 case OverlapInternal:
200                 {
201                         framecnt_t const offset = start - region->position ();
202                         framecnt_t const trim = region->last_frame() - end;
203                         if (region->fade_in()->back()->when > offset) {
204                                 fade_in = region->fade_in()->back()->when - offset;
205                         }
206                         if (region->fade_out()->back()->when > trim) {
207                                 fade_out = region->fade_out()->back()->when - trim;
208                         }
209                         break;
210                 }
211
212                 case OverlapStart: {
213                         position = region->position() - start;
214                         len = end - region->position();
215
216                         if (end > region->position() + region->fade_in()->back()->when)
217                                 fade_in = region->fade_in()->back()->when;  //end is after fade-in, preserve the fade-in
218                         if (end > region->last_frame() - region->fade_out()->back()->when)
219                                 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end );  //end is inside the fadeout, preserve the fades endpoint
220                         break;
221                 }
222                 
223                 case OverlapEnd: {
224                         position = 0;
225                         offset = start - region->position();
226                         len = region->length() - offset;
227
228                         if (start < region->last_frame() - region->fade_out()->back()->when)  //start is before fade-out, preserve the fadeout
229                                 fade_out = region->fade_out()->back()->when;
230                         
231                         if (start < region->position() + region->fade_in()->back()->when)
232                                 fade_in = region->fade_in()->back()->when - (start - region->position());  //end is inside the fade-in, preserve the fade-in endpoint
233                         break;
234                 }
235                 
236                 case OverlapExternal:
237                         fade_in = region->fade_in()->back()->when;
238                         fade_out = region->fade_out()->back()->when;
239                         break;
240                 }
241
242                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
243                 assert (our_region);
244
245                 our_region->set_fade_in_length (fade_in);
246                 our_region->set_fade_out_length (fade_out);
247                 ++ours;
248         }
249
250         in_set_state--;
251
252         /* this constructor does NOT notify others (session) */
253 }
254
255 AudioPlaylist::~AudioPlaylist ()
256 {
257         _crossfades.clear ();
258 }
259
260 struct RegionSortByLayer {
261     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
262             return a->layer() < b->layer();
263     }
264 };
265
266 ARDOUR::framecnt_t
267 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
268                      framecnt_t cnt, unsigned chan_n)
269 {
270         framecnt_t ret = cnt;
271
272         /* optimizing this memset() away involves a lot of conditionals
273            that may well cause more of a hit due to cache misses
274            and related stuff than just doing this here.
275
276            it would be great if someone could measure this
277            at some point.
278
279            one way or another, parts of the requested area
280            that are not written to by Region::region_at()
281            for all Regions that cover the area need to be
282            zeroed.
283         */
284
285         memset (buf, 0, sizeof (Sample) * cnt);
286
287         /* this function is never called from a realtime thread, so
288            its OK to block (for short intervals).
289         */
290
291         Glib::RecMutex::Lock rm (region_lock);
292
293         framepos_t const end = start + cnt - 1;
294         framecnt_t read_frames = 0;
295         framecnt_t skip_frames = 0;
296         _read_data_count = 0;
297
298         _read_data_count = 0;
299
300         RegionList* rlist = regions_to_read (start, start+cnt);
301
302         if (rlist->empty()) {
303                 delete rlist;
304                 return cnt;
305         }
306
307         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
308         map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
309         vector<uint32_t> relevant_layers;
310
311         for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
312                 if ((*i)->coverage (start, end) != OverlapNone) {
313                         relevant_regions[(*i)->layer()].push_back (*i);
314                         relevant_layers.push_back ((*i)->layer());
315                 }
316         }
317
318         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
319                 if ((*i)->coverage (start, end) != OverlapNone) {
320                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
321                 }
322         }
323
324 //      RegionSortByLayer layer_cmp;
325 //      relevant_regions.sort (layer_cmp);
326
327         /* XXX this whole per-layer approach is a hack that
328            should be removed once Crossfades become
329            CrossfadeRegions and we just grab a list of relevant
330            regions and call read_at() on all of them.
331         */
332
333         sort (relevant_layers.begin(), relevant_layers.end());
334
335         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
336
337                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
338                 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
339
340
341                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
342                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
343                         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
344                         assert(ar);
345                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
346                         _read_data_count += ar->read_data_count();
347                 }
348
349                 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
350                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
351
352                         /* don't JACK up _read_data_count, since its the same data as we just
353                            read from the regions, and the OS should handle that for us.
354                         */
355                 }
356         }
357
358         delete rlist;
359         return ret;
360 }
361
362
363 void
364 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
365 {
366         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
367
368         if (in_set_state) {
369                 return;
370         }
371
372         if (r == 0) {
373                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
374                       << endmsg;
375                 return;
376         }
377
378         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
379
380                 if ((*i)->involves (r)) {
381                         i = _crossfades.erase (i);
382                 } else {
383                         ++i;
384                 }
385         }
386 }
387
388
389 void
390 AudioPlaylist::flush_notifications (bool from_undo)
391 {
392         Playlist::flush_notifications (from_undo);
393
394         if (in_flush) {
395                 return;
396         }
397
398         in_flush = true;
399
400         Crossfades::iterator a;
401         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
402                 NewCrossfade (*a); /* EMIT SIGNAL */
403         }
404
405         _pending_xfade_adds.clear ();
406
407         in_flush = false;
408 }
409
410 void
411 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
412 {
413         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
414         set<boost::shared_ptr<Crossfade> > updated;
415
416         if (ar == 0) {
417                 return;
418         }
419
420         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
421
422                 Crossfades::iterator tmp;
423
424                 tmp = x;
425                 ++tmp;
426
427                 /* only update them once */
428
429                 if ((*x)->involves (ar)) {
430
431                         pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
432
433                         if (u.second) {
434                                 /* x was successfully inserted into the set, so it has not already been updated */
435                                 try {
436                                         (*x)->refresh ();
437                                 }
438
439                                 catch (Crossfade::NoCrossfadeHere& err) {
440                                         // relax, Invalidated during refresh
441                                 }
442                         }
443                 }
444
445                 x = tmp;
446         }
447 }
448
449 void
450 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
451 {
452         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
453         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
454         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
455
456         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
457                 Crossfades::iterator tmp;
458                 tmp = x;
459                 ++tmp;
460
461                 boost::shared_ptr<Crossfade> fade;
462
463                 if ((*x)->_in == orig) {
464                         if (! (*x)->covers(right->position())) {
465                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
466                         } else {
467                                 // Overlap, the crossfade is copied on the left side of the right region instead
468                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
469                         }
470                 }
471
472                 if ((*x)->_out == orig) {
473                         if (! (*x)->covers(right->position())) {
474                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
475                         } else {
476                                 // Overlap, the crossfade is copied on the right side of the left region instead
477                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
478                         }
479                 }
480
481                 if (fade) {
482                         _crossfades.remove (*x);
483                         add_crossfade (fade);
484                 }
485                 x = tmp;
486         }
487 }
488
489 void
490 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
491 {
492         boost::shared_ptr<AudioRegion> other;
493         boost::shared_ptr<AudioRegion> region;
494         boost::shared_ptr<AudioRegion> top;
495         boost::shared_ptr<AudioRegion> bottom;
496         boost::shared_ptr<Crossfade>   xfade;
497         RegionList*  touched_regions = 0;
498
499         if (in_set_state || in_partition) {
500                 return;
501         }
502
503         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
504                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
505                       << endmsg;
506                 return;
507         }
508
509         if (!norefresh) {
510                 refresh_dependents (r);
511         }
512
513
514         if (!_session.config.get_auto_xfade()) {
515                 return;
516         }
517
518         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
519                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
520
521                 if (other == region) {
522                         continue;
523                 }
524
525                 if (other->muted() || region->muted()) {
526                         continue;
527                 }
528
529                 if (other->position() == r->position() && other->length() == r->length()) {
530                         /* precise overlay of two regions - no xfade */
531                         continue;
532                 }
533
534                 if (other->layer() < region->layer()) {
535                         top = region;
536                         bottom = other;
537                 } else {
538                         top = other;
539                         bottom = region;
540                 }
541
542                 if (!top->opaque()) {
543                         continue;
544                 }
545
546                 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
547
548                 delete touched_regions;
549                 touched_regions = 0;
550
551                 try {
552                         framecnt_t xfade_length;
553                         switch (c) {
554                         case OverlapNone:
555                                 break;
556
557                         case OverlapInternal:
558                                  /* {=============== top  =============}
559                                   *     [ ----- bottom  ------- ]
560                                   */
561                                 break;
562
563                         case OverlapExternal:
564
565                                 /*     [ -------- top ------- ]
566                                  * {=========== bottom =============}
567                                  */
568
569                                 /* to avoid discontinuities at the region boundaries of an internal
570                                    overlap (this region is completely within another), we create
571                                    two hidden crossfades at each boundary. this is not dependent
572                                    on the auto-xfade option, because we require it as basic
573                                    audio engineering.
574                                 */
575
576                                 xfade_length = min ((framecnt_t) 720, top->length());
577
578                                 if (top_region_at (top->first_frame()) == top) {
579
580                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
581                                         add_crossfade (xfade);
582                                 }
583
584                                 if (top_region_at (top->last_frame() - 1) == top) {
585
586                                         /*
587                                            only add a fade out if there is no region on top of the end of 'top' (which
588                                            would cover it).
589                                         */
590
591                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
592                                         add_crossfade (xfade);
593                                 }
594                                 break;
595                         case OverlapStart:
596
597                                 /*                   { ==== top ============ }
598                                  *   [---- bottom -------------------]
599                                  */
600
601                                 if (_session.config.get_xfade_model() == FullCrossfade) {
602                                         touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
603                                         if (touched_regions->size() <= 2) {
604                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
605                                                 add_crossfade (xfade);
606                                         }
607                                 } else {
608
609                                         touched_regions = regions_touched (top->first_frame(),
610                                                                            top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
611                                                                                                      top->length()));
612                                         if (touched_regions->size() <= 2) {
613                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
614                                                 add_crossfade (xfade);
615                                         }
616                                 }
617                                 break;
618                         case OverlapEnd:
619
620
621                                 /* [---- top ------------------------]
622                                  *                { ==== bottom ============ }
623                                  */
624
625                                 if (_session.config.get_xfade_model() == FullCrossfade) {
626
627                                         touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
628                                         if (touched_regions->size() <= 2) {
629                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
630                                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
631                                                 add_crossfade (xfade);
632                                         }
633
634                                 } else {
635                                         touched_regions = regions_touched (bottom->first_frame(),
636                                                                            bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
637                                                                                                         bottom->length()));
638                                         if (touched_regions->size() <= 2) {
639                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
640                                                 add_crossfade (xfade);
641                                         }
642                                 }
643                                 break;
644                         default:
645                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
646                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
647                                 add_crossfade (xfade);
648                         }
649                 }
650
651                 catch (failed_constructor& err) {
652                         continue;
653                 }
654
655                 catch (Crossfade::NoCrossfadeHere& err) {
656                         continue;
657                 }
658
659         }
660
661         delete touched_regions;
662 }
663
664 void
665 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
666 {
667         Crossfades::iterator ci;
668
669         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
670                 if (*(*ci) == *xfade) { // Crossfade::operator==()
671                         break;
672                 }
673         }
674
675         if (ci != _crossfades.end()) {
676                 // it will just go away
677         } else {
678                 _crossfades.push_back (xfade);
679
680                 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
681                 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
682
683                 notify_crossfade_added (xfade);
684         }
685 }
686
687 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
688 {
689         if (g_atomic_int_get(&block_notifications)) {
690                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
691         } else {
692                 NewCrossfade (x); /* EMIT SIGNAL */
693         }
694 }
695
696 void
697 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
698 {
699         Crossfades::iterator i;
700         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
701
702         xfade->in()->resume_fade_in ();
703         xfade->out()->resume_fade_out ();
704
705         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
706                 _crossfades.erase (i);
707         }
708 }
709
710 int
711 AudioPlaylist::set_state (const XMLNode& node, int version)
712 {
713         XMLNode *child;
714         XMLNodeList nlist;
715         XMLNodeConstIterator niter;
716
717         in_set_state++;
718
719         Playlist::set_state (node, version);
720
721         freeze ();
722
723         nlist = node.children();
724
725         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
726
727                 child = *niter;
728
729                 if (child->name() != "Crossfade") {
730                         continue;
731                 }
732
733                 try {
734                         boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
735                         _crossfades.push_back (xfade);
736                         xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
737                         xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
738                         NewCrossfade(xfade);
739                 }
740
741                 catch (failed_constructor& err) {
742                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
743                         //        _name)
744                         //    << endl;
745                         continue;
746                 }
747         }
748
749         thaw ();
750         in_set_state--;
751
752         return 0;
753 }
754
755 void
756 AudioPlaylist::clear (bool with_signals)
757 {
758         _crossfades.clear ();
759         Playlist::clear (with_signals);
760 }
761
762 XMLNode&
763 AudioPlaylist::state (bool full_state)
764 {
765         XMLNode& node = Playlist::state (full_state);
766
767         if (full_state) {
768                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
769                         node.add_child_nocopy ((*i)->get_state());
770                 }
771         }
772
773         return node;
774 }
775
776 void
777 AudioPlaylist::dump () const
778 {
779         boost::shared_ptr<Region>r;
780         boost::shared_ptr<Crossfade> x;
781
782         cerr << "Playlist \"" << _name << "\" " << endl
783              << regions.size() << " regions "
784              << _crossfades.size() << " crossfades"
785              << endl;
786
787         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
788                 r = *i;
789                 cerr << "  " << r->name() << " @ " << r << " ["
790                      << r->start() << "+" << r->length()
791                      << "] at "
792                      << r->position()
793                      << " on layer "
794                      << r->layer ()
795                      << endl;
796         }
797
798         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
799                 x = *i;
800                 cerr << "  xfade ["
801                      << x->out()->name()
802                      << ','
803                      << x->in()->name()
804                      << " @ "
805                      << x->position()
806                      << " length = "
807                      << x->length ()
808                      << " active ? "
809                      << (x->active() ? "yes" : "no")
810                      << endl;
811         }
812 }
813
814 bool
815 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
816 {
817         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
818
819         if (!r) {
820                 return false;
821         }
822
823         bool changed = false;
824         Crossfades::iterator c, ctmp;
825         set<boost::shared_ptr<Crossfade> > unique_xfades;
826
827         {
828                 RegionLock rlock (this);
829
830                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
831
832                         RegionList::iterator tmp = i;
833                         ++tmp;
834
835                         if ((*i) == region) {
836                                 regions.erase (i);
837                                 changed = true;
838                         }
839
840                         i = tmp;
841                 }
842
843                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
844
845                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
846                         ++xtmp;
847
848                         if ((*x) == region) {
849                                 all_regions.erase (x);
850                                 changed = true;
851                         }
852
853                         x = xtmp;
854                 }
855
856                 region->set_playlist (boost::shared_ptr<Playlist>());
857         }
858
859         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
860                 ctmp = c;
861                 ++ctmp;
862
863                 if ((*c)->involves (r)) {
864                         unique_xfades.insert (*c);
865                         _crossfades.erase (c);
866                 }
867
868                 c = ctmp;
869         }
870
871         if (changed) {
872                 /* overload this, it normally means "removed", not destroyed */
873                 notify_region_removed (region);
874         }
875
876         return changed;
877 }
878
879 void
880 AudioPlaylist::crossfade_changed (const PropertyChange&)
881 {
882         if (in_flush || in_set_state) {
883                 return;
884         }
885
886         /* XXX is there a loop here? can an xfade change not happen
887            due to a playlist change? well, sure activation would
888            be an example. maybe we should check the type of change
889            that occured.
890         */
891
892         notify_contents_changed ();
893 }
894
895 bool
896 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
897 {
898         if (in_flush || in_set_state) {
899                 return false;
900         }
901
902         PropertyChange our_interests;
903
904         our_interests.add (Properties::fade_in_active);
905         our_interests.add (Properties::fade_out_active);
906         our_interests.add (Properties::scale_amplitude);
907         our_interests.add (Properties::envelope_active);
908         our_interests.add (Properties::envelope);
909         our_interests.add (Properties::fade_in);
910         our_interests.add (Properties::fade_out);
911
912         bool parent_wants_notify;
913
914         parent_wants_notify = Playlist::region_changed (what_changed, region);
915
916         if (parent_wants_notify || (what_changed.contains (our_interests))) {
917                 notify_contents_changed ();
918         }
919
920         return true;
921 }
922
923 void
924 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
925 {
926         RegionLock rlock (this);
927
928         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
929                 framepos_t const start = (*i)->position ();
930                 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
931
932                 if (frame >= start && frame <= end) {
933                         clist.push_back (*i);
934                 }
935         }
936 }
937
938 void
939 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
940 {
941         RegionLock rl (this, false);
942         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
943                 s (*i);
944         }
945 }
946
947 void
948 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
949 {
950         for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
951                 add_crossfade (*i);
952         }
953
954         /* don't remove crossfades here; they will be dealt with by the dependency code */
955 }
956
957 boost::shared_ptr<Crossfade>
958 AudioPlaylist::find_crossfade (const PBD::ID& id) const
959 {
960         Crossfades::const_iterator i = _crossfades.begin ();
961         while (i != _crossfades.end() && (*i)->id() != id) {
962                 ++i;
963         }
964
965         if (i == _crossfades.end()) {
966                 return boost::shared_ptr<Crossfade> ();
967         }
968
969         return *i;
970 }
971
972 struct crossfade_triple {
973     boost::shared_ptr<Region> old_in;
974     boost::shared_ptr<Region> new_in;
975     boost::shared_ptr<Region> new_out;
976 };
977
978 void
979 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, boost::shared_ptr<Playlist> other)
980 {
981         boost::shared_ptr<AudioPlaylist> other_audio = boost::dynamic_pointer_cast<AudioPlaylist>(other);
982
983         if (!other_audio) {
984                 return;
985         }
986
987         /* our argument is a vector of old and new regions. Each old region
988            might be participant in a crossfade that is already present. Each new
989            region is a copy of the old region, present in the other playlist.
990
991            our task is to find all the relevant xfades in our playlist (involving
992            the "old" regions) and place copies of them in the other playlist.
993         */
994
995         typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
996         CrossfadeInfo crossfade_info;
997
998         /* build up a record that links crossfades, old regions and new regions
999          */
1000
1001         for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1002
1003                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1004
1005                         if ((*i)->in() == on->first) {
1006                                 
1007                                 CrossfadeInfo::iterator cf;
1008
1009                                 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1010
1011                                         /* already have a record for the old fade-in region, 
1012                                            so note the new fade-in region
1013                                         */
1014                                         
1015                                         cf->second.new_in = on->second;
1016                                         
1017                                 } else {
1018                                         
1019                                         /* add a record of this crossfade, keeping an association
1020                                            with the new fade-in region
1021                                         */
1022                                         
1023                                         crossfade_triple ct;
1024                                         
1025                                         ct.old_in = on->first;
1026                                         ct.new_in = on->second;
1027
1028                                         crossfade_info[*i] = ct;
1029                                 }
1030
1031                         } else if ((*i)->out() == on->first) {
1032                                 
1033                                 /* this old region is the fade-out region of this crossfade */
1034                                 
1035                                 CrossfadeInfo::iterator cf;
1036                                 
1037                                 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1038                                         
1039                                         /* already have a record for this crossfade, so just keep
1040                                            an association for the new fade out region
1041                                         */
1042                                         
1043                                         cf->second.new_out = on->second;
1044
1045                                 } else {
1046                                         
1047                                         /* add a record of this crossfade, keeping an association
1048                                            with the new fade-in region
1049                                         */
1050
1051                                         crossfade_triple ct;
1052
1053                                         ct.old_in = on->first;
1054                                         ct.new_out = on->second;
1055
1056                                         crossfade_info[*i] = ct;
1057                                 }
1058                         }
1059                 }
1060         }
1061
1062         for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1063                 
1064                 /* for each crossfade that involves at least two of the old regions,
1065                    create a new identical crossfade with the new regions
1066                 */
1067                 
1068                 if (!ci->second.new_in || !ci->second.new_out) {
1069                         continue;
1070                 }
1071
1072                 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first, 
1073                                                                        boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1074                                                                        boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1075                 
1076                 /* add it at the right position - which must be at the start
1077                  * of the fade-in region
1078                  */
1079
1080                 new_xfade->set_position (ci->second.new_in->position(), this);
1081                 other_audio->add_crossfade (new_xfade);
1082         }
1083 }
1084
1085 void
1086 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1087 {
1088         /* sort the originals into time order */
1089
1090         RegionSortByPosition cmp;
1091         boost::shared_ptr<AudioRegion> ar;
1092         boost::shared_ptr<AudioRegion> cr;
1093
1094         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1095                 return;
1096         }
1097
1098         sort (originals.begin(), originals.end(), cmp);
1099
1100         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1101
1102         /* copy the fade in of the first into the compound region */
1103
1104         if (ar) {
1105                 cr->set_fade_in (ar->fade_in());
1106                 
1107                 /* disable the fade in of the first */
1108                 
1109                 ar->set_fade_in_active (false);
1110         }
1111
1112         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1113
1114         if (ar) {
1115                 /* copy the fade out of the last into the compound region */
1116                 cr->set_fade_out (ar->fade_out());
1117                 /* disable the fade out of the first */
1118                 ar->set_fade_out_active (false);
1119         }
1120 }
1121
1122 void
1123 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1124 {
1125         boost::shared_ptr<AudioRegion> ar;
1126         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1127
1128         if (!cr) {
1129                 return;
1130         }
1131
1132         /* no need to call clear_changes() on the originals because that is
1133          * done within Playlist::uncombine ()
1134          */
1135
1136         if ((ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front())) != 0) {
1137
1138                 /* copy the compound region's fade in back into the first
1139                    original region.
1140                 */
1141
1142                 if (cr->fade_in()->back()->when <= ar->length()) {
1143                         /* don't do this if the fade is longer than the
1144                          * region
1145                          */
1146                         ar->set_fade_in (cr->fade_in());
1147                 }
1148
1149                 ar->set_fade_in_active (true);
1150                 _session.add_command (new StatefulDiffCommand (originals.front()));
1151         }
1152
1153         if ((ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back())) != 0) {
1154
1155                 /* copy the compound region's fade out back into the last
1156                    original region.
1157                 */
1158
1159                 if (cr->fade_out()->back()->when <= ar->length()) {
1160                         /* don't do this if the fade is longer than the
1161                          * region
1162                          */
1163                         ar->set_fade_out (cr->fade_out());
1164                 }
1165
1166                 ar->set_fade_out_active (true);
1167                 _session.add_command (new StatefulDiffCommand (originals.back()));
1168         }
1169 }