Fix off-by-one in computation of crossfade lengths.
[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/audioplaylist.h"
27 #include "ardour/audioregion.h"
28 #include "ardour/region_sorters.h"
29 #include "ardour/session.h"
30
31 #include "i18n.h"
32
33 using namespace ARDOUR;
34 using namespace std;
35 using namespace PBD;
36
37 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
38         : Playlist (session, node, DataType::AUDIO, hidden)
39 {
40 #ifndef NDEBUG
41         const XMLProperty* prop = node.property("type");
42         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
43 #endif
44
45         in_set_state++;
46         if (set_state (node, Stateful::loading_state_version)) {
47                 throw failed_constructor();
48         }
49         in_set_state--;
50
51         relayer ();
52
53         load_legacy_crossfades (node, Stateful::loading_state_version);
54 }
55
56 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
57         : Playlist (session, name, DataType::AUDIO, hidden)
58 {
59 }
60
61 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
62         : Playlist (other, name, hidden)
63 {
64 }
65
66 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
67         : Playlist (other, start, cnt, name, hidden)
68 {
69         RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70         in_set_state++;
71
72         framepos_t const end = start + cnt - 1;
73
74         /* Audio regions that have been created by the Playlist constructor
75            will currently have the same fade in/out as the regions that they
76            were created from.  This is wrong, so reset the fades here.
77         */
78
79         RegionList::iterator ours = regions.begin ();
80
81         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
82                 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
83                 assert (region);
84
85                 framecnt_t fade_in = 64;
86                 framecnt_t fade_out = 64;
87
88                 switch (region->coverage (start, end)) {
89                 case Evoral::OverlapNone:
90                         continue;
91
92                 case Evoral::OverlapInternal:
93                 {
94                         framecnt_t const offset = start - region->position ();
95                         framecnt_t const trim = region->last_frame() - end;
96                         if (region->fade_in()->back()->when > offset) {
97                                 fade_in = region->fade_in()->back()->when - offset;
98                         }
99                         if (region->fade_out()->back()->when > trim) {
100                                 fade_out = region->fade_out()->back()->when - trim;
101                         }
102                         break;
103                 }
104
105                 case Evoral::OverlapStart: {
106                         if (end > region->position() + region->fade_in()->back()->when)
107                                 fade_in = region->fade_in()->back()->when;  //end is after fade-in, preserve the fade-in
108                         if (end > region->last_frame() - region->fade_out()->back()->when)
109                                 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end );  //end is inside the fadeout, preserve the fades endpoint
110                         break;
111                 }
112
113                 case Evoral::OverlapEnd: {
114                         if (start < region->last_frame() - region->fade_out()->back()->when)  //start is before fade-out, preserve the fadeout
115                                 fade_out = region->fade_out()->back()->when;
116
117                         if (start < region->position() + region->fade_in()->back()->when)
118                                 fade_in = region->fade_in()->back()->when - (start - region->position());  //end is inside the fade-in, preserve the fade-in endpoint
119                         break;
120                 }
121
122                 case Evoral::OverlapExternal:
123                         fade_in = region->fade_in()->back()->when;
124                         fade_out = region->fade_out()->back()->when;
125                         break;
126                 }
127
128                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
129                 assert (our_region);
130
131                 our_region->set_fade_in_length (fade_in);
132                 our_region->set_fade_out_length (fade_out);
133                 ++ours;
134         }
135
136         in_set_state--;
137
138         /* this constructor does NOT notify others (session) */
139 }
140
141 /** Sort by descending layer and then by ascending position */
142 struct ReadSorter {
143     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
144             if (a->layer() != b->layer()) {
145                     return a->layer() > b->layer();
146             }
147
148             return a->position() < b->position();
149     }
150 };
151
152 /** A segment of region that needs to be read */
153 struct Segment {
154         Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
155         
156         boost::shared_ptr<AudioRegion> region; ///< the region
157         Evoral::Range<framepos_t> range;       ///< range of the region to read, in session frames
158 };
159
160 /** @param start Start position in session frames.
161  *  @param cnt Number of frames to read.
162  */
163 ARDOUR::framecnt_t
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
165                      framecnt_t cnt, unsigned chan_n)
166 {
167         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
168                                                            name(), start, cnt, chan_n, regions.size()));
169
170         /* optimizing this memset() away involves a lot of conditionals
171            that may well cause more of a hit due to cache misses
172            and related stuff than just doing this here.
173
174            it would be great if someone could measure this
175            at some point.
176
177            one way or another, parts of the requested area
178            that are not written to by Region::region_at()
179            for all Regions that cover the area need to be
180            zeroed.
181         */
182
183         memset (buf, 0, sizeof (Sample) * cnt);
184
185         /* this function is never called from a realtime thread, so
186            its OK to block (for short intervals).
187         */
188
189         Playlist::RegionReadLock rl (this);
190
191         /* Find all the regions that are involved in the bit we are reading,
192            and sort them by descending layer and ascending position.
193         */
194         boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
195         all->sort (ReadSorter ());
196
197         /* This will be a list of the bits of our read range that we have
198            handled completely (ie for which no more regions need to be read).
199            It is a list of ranges in session frames.
200         */
201         Evoral::RangeList<framepos_t> done;
202
203         /* This will be a list of the bits of regions that we need to read */
204         list<Segment> to_do;
205
206         /* Now go through the `all' list filling in `to_do' and `done' */
207         for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
208                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
209
210                 /* Work out which bits of this region need to be read;
211                    first, trim to the range we are reading...
212                 */
213                 Evoral::Range<framepos_t> region_range = ar->range ();
214                 region_range.from = max (region_range.from, start);
215                 region_range.to = min (region_range.to, start + cnt - 1);
216
217                 /* ... and then remove the bits that are already done */
218
219                 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
220
221                 /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
222                    and start-of-fade-out) to the `done' list.
223                 */
224
225                 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
226
227                 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
228                         Evoral::Range<framepos_t> d = *j;
229                         to_do.push_back (Segment (ar, d));
230
231                         if (ar->opaque ()) {
232                                 /* Cut this range down to just the body and mark it done */
233                                 Evoral::Range<framepos_t> body = ar->body_range ();
234                                 if (body.from < d.to && body.to > d.from) {
235                                         d.from = max (d.from, body.from);
236                                         d.to = min (d.to, body.to);
237                                         done.add (d);
238                                 }
239                         }
240                 }
241         }
242
243         /* Now go backwards through the to_do list doing the actual reads */
244         for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
245                 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
246                                                                    name(), i->region->name(), i->range.from,
247                                                                    i->range.to - i->range.from + 1, (int) chan_n,
248                                                                    buf, i->range.from - start));
249                 i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
250         }
251
252         return cnt;
253 }
254
255 void
256 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
257 {
258         if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
259                 return;
260         }
261
262         boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
263         boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
264
265         RegionList all = *starts;
266         std::copy (ends->begin(), ends->end(), back_inserter (all));
267
268         all.sort (RegionSortByLayer ());
269
270         set<boost::shared_ptr<Region> > done_start;
271         set<boost::shared_ptr<Region> > done_end;
272
273         for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
274                 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
275
276                         if (i == j) {
277                                 continue;
278                         }
279
280                         if ((*i)->muted() || (*j)->muted()) {
281                                 continue;
282                         }
283
284                         if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
285                                 /* precise overlay: no xfade */
286                                 continue;
287                         }
288
289                         if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
290                                 /* starts or ends match: no xfade */
291                                 continue;
292                         }
293                         
294                         boost::shared_ptr<AudioRegion> top;
295                         boost::shared_ptr<AudioRegion> bottom;
296                 
297                         if ((*i)->layer() < (*j)->layer()) {
298                                 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
299                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
300                         } else {
301                                 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
302                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
303                         }
304                         
305                         if (!top->opaque ()) {
306                                 continue;
307                         }
308
309                         Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
310
311                         if (c == Evoral::OverlapStart) {
312                                 
313                                 /* top starts within bottom but covers bottom's end */
314                                 
315                                 /*                   { ==== top ============ } 
316                                  *   [---- bottom -------------------] 
317                                  */
318
319                                 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
320
321                                         /* Top's fade-in will cause an implicit fade-out of bottom */
322                                         
323                                         if (top->fade_in_is_xfade() && top->fade_in_is_short()) {
324
325                                                 /* its already an xfade. if its
326                                                  * really short, leave it
327                                                  * alone.
328                                                  */
329
330                                         } else {
331                                                 framecnt_t len = 0;
332                                                 
333                                                 if (_capture_insertion_underway) {
334                                                         len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
335                                                 } else {
336                                                         switch (_session.config.get_xfade_model()) {
337                                                         case FullCrossfade:
338                                                                 len = bottom->last_frame () - top->first_frame () + 1;
339                                                                 top->set_fade_in_is_short (false);
340                                                                 break;
341                                                         case ShortCrossfade:
342                                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
343                                                                 top->set_fade_in_is_short (true);
344                                                                 break;
345                                                         }
346                                                 }
347
348                                                 top->set_fade_in_active (true);
349                                                 top->set_fade_in_is_xfade (true);
350                                                 
351                                                 /* XXX may 2012: -3dB and -6dB curves
352                                                  * are the same right now 
353                                                  */
354                                                 
355                                                 switch (_session.config.get_xfade_choice ()) {
356                                                 case ConstantPowerMinus3dB:
357                                                         top->set_fade_in (FadeConstantPower, len);
358                                                         break;
359                                                 case ConstantPowerMinus6dB:
360                                                         top->set_fade_in (FadeConstantPower, len);
361                                                         break;
362                                                 case RegionFades:
363                                                         top->set_fade_in_length (len);
364                                                         break;
365                                                 }
366                                         }
367
368                                         done_start.insert (top);
369                                 }
370
371                         } else if (c == Evoral::OverlapEnd) {
372                                 
373                                 /* top covers start of bottom but ends within it */
374                                 
375                                 /* [---- top ------------------------] 
376                                  *                { ==== bottom ============ } 
377                                  */
378
379                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
380                                         /* Top's fade-out will cause an implicit fade-in of bottom */
381                                         
382                                         
383                                         if (top->fade_out_is_xfade() && top->fade_out_is_short()) {
384
385                                                 /* its already an xfade. if its
386                                                  * really short, leave it
387                                                  * alone.
388                                                  */
389
390                                         } else {
391                                                 framecnt_t len = 0;
392                                                 
393                                                 if (_capture_insertion_underway) {
394                                                         len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
395                                                 } else {
396                                                         switch (_session.config.get_xfade_model()) {
397                                                         case FullCrossfade:
398                                                                 len = top->last_frame () - bottom->first_frame () + 1;
399                                                                 break;
400                                                         case ShortCrossfade:
401                                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
402                                                                 break;
403                                                         }
404                                                 }
405                                                 
406                                                 top->set_fade_out_active (true);
407                                                 top->set_fade_out_is_xfade (true);
408                                                 
409                                                 switch (_session.config.get_xfade_choice ()) {
410                                                 case ConstantPowerMinus3dB:
411                                                         top->set_fade_out (FadeConstantPower, len);
412                                                         break;
413                                                 case ConstantPowerMinus6dB:
414                                                         top->set_fade_out (FadeConstantPower, len);
415                                                         break;
416                                                 case RegionFades:
417                                                         top->set_fade_out_length (len);
418                                                         break;
419                                                 }
420                                         }
421
422                                         done_end.insert (top);
423                                 }
424                         }
425                 }
426         }
427
428         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
429                 if (done_start.find (*i) == done_start.end()) {
430                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
431                         if (r->fade_in_is_xfade()) {
432                                 r->set_default_fade_in ();
433                         }
434                 }
435         }
436
437         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
438                 if (done_end.find (*i) == done_end.end()) {
439                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
440                         if (r->fade_out_is_xfade()) {
441                                 r->set_default_fade_out ();
442                         }
443                 }
444         }
445 }
446
447 void
448 AudioPlaylist::dump () const
449 {
450         boost::shared_ptr<Region>r;
451
452         cerr << "Playlist \"" << _name << "\" " << endl
453              << regions.size() << " regions "
454              << endl;
455
456         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
457                 r = *i;
458                 cerr << "  " << r->name() << " @ " << r << " ["
459                      << r->start() << "+" << r->length()
460                      << "] at "
461                      << r->position()
462                      << " on layer "
463                      << r->layer ()
464                      << endl;
465         }
466 }
467
468 bool
469 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
470 {
471         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
472
473         if (!r) {
474                 return false;
475         }
476
477         bool changed = false;
478
479         {
480                 RegionWriteLock rlock (this);
481
482                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
483
484                         RegionList::iterator tmp = i;
485                         ++tmp;
486
487                         if ((*i) == region) {
488                                 regions.erase (i);
489                                 changed = true;
490                         }
491
492                         i = tmp;
493                 }
494
495                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
496
497                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
498                         ++xtmp;
499
500                         if ((*x) == region) {
501                                 all_regions.erase (x);
502                                 changed = true;
503                         }
504
505                         x = xtmp;
506                 }
507
508                 region->set_playlist (boost::shared_ptr<Playlist>());
509         }
510
511         if (changed) {
512                 /* overload this, it normally means "removed", not destroyed */
513                 notify_region_removed (region);
514         }
515
516         return changed;
517 }
518
519 bool
520 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
521 {
522         if (in_flush || in_set_state) {
523                 return false;
524         }
525
526         PropertyChange our_interests;
527
528         our_interests.add (Properties::fade_in_active);
529         our_interests.add (Properties::fade_out_active);
530         our_interests.add (Properties::scale_amplitude);
531         our_interests.add (Properties::envelope_active);
532         our_interests.add (Properties::envelope);
533         our_interests.add (Properties::fade_in);
534         our_interests.add (Properties::fade_out);
535
536         bool parent_wants_notify;
537
538         parent_wants_notify = Playlist::region_changed (what_changed, region);
539
540         if (parent_wants_notify || (what_changed.contains (our_interests))) {
541                 notify_contents_changed ();
542         }
543
544         return true;
545 }
546
547 void
548 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
549 {
550         RegionSortByPosition cmp;
551         boost::shared_ptr<AudioRegion> ar;
552
553         sort (copies.begin(), copies.end(), cmp);
554
555         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
556
557         /* disable fade in of the first region */
558
559         if (ar) {
560                 ar->set_fade_in_active (false);
561         }
562
563         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
564
565         /* disable fade out of the last region */
566
567         if (ar) {
568                 ar->set_fade_out_active (false);
569         }
570 }
571
572 void
573 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
574 {
575         RegionSortByPosition cmp;
576         boost::shared_ptr<AudioRegion> ar;
577         boost::shared_ptr<AudioRegion> cr;
578
579         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
580                 return;
581         }
582
583         sort (originals.begin(), originals.end(), cmp);
584
585         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
586
587         /* copy the fade in of the first into the compound region */
588
589         if (ar) {
590                 cr->set_fade_in (ar->fade_in());
591         }
592
593         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
594
595         if (ar) {
596                 /* copy the fade out of the last into the compound region */
597                 cr->set_fade_out (ar->fade_out());
598         }
599 }
600
601 void
602 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
603 {
604         RegionSortByPosition cmp;
605         boost::shared_ptr<AudioRegion> ar;
606         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
607
608         if (!cr) {
609                 return;
610         }
611
612         sort (originals.begin(), originals.end(), cmp);
613
614         /* no need to call clear_changes() on the originals because that is
615          * done within Playlist::uncombine ()
616          */
617
618         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
619
620                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
621                         continue;
622                 }
623
624                 /* scale the uncombined regions by any gain setting for the
625                  * compound one.
626                  */
627
628                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
629
630                 if (i == originals.begin()) {
631
632                         /* copy the compound region's fade in back into the first
633                            original region.
634                         */
635
636                         if (cr->fade_in()->back()->when <= ar->length()) {
637                                 /* don't do this if the fade is longer than the
638                                  * region
639                                  */
640                                 ar->set_fade_in (cr->fade_in());
641                         }
642
643
644                 } else if (*i == originals.back()) {
645
646                         /* copy the compound region's fade out back into the last
647                            original region.
648                         */
649
650                         if (cr->fade_out()->back()->when <= ar->length()) {
651                                 /* don't do this if the fade is longer than the
652                                  * region
653                                  */
654                                 ar->set_fade_out (cr->fade_out());
655                         }
656
657                 }
658
659                 _session.add_command (new StatefulDiffCommand (*i));
660         }
661 }
662
663 int
664 AudioPlaylist::set_state (const XMLNode& node, int version)
665 {
666         return Playlist::set_state (node, version);
667 }
668
669 void
670 AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
671 {
672         /* Read legacy Crossfade nodes and set up region fades accordingly */
673
674         XMLNodeList children = node.children ();
675         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
676                 if ((*i)->name() == X_("Crossfade")) {
677
678                         XMLProperty* p = (*i)->property (X_("active"));
679                         assert (p);
680
681                         if (!string_is_affirmative (p->value())) {
682                                 continue;
683                         }
684                         
685                         if ((p = (*i)->property (X_("in"))) == 0) {
686                                 continue;
687                         }
688
689                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
690
691                         if (!in) {
692                                 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
693                                                            name()) 
694                                         << endmsg;
695                                 continue;
696                         }
697
698                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
699                         assert (in_a);
700
701                         if ((p = (*i)->property (X_("out"))) == 0) {
702                                 continue;
703                         }
704
705                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
706
707                         if (!out) {
708                                 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
709                                                            name()) 
710                                         << endmsg;
711                                 continue;
712                         }
713
714                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
715                         assert (out_a);
716
717                         /* now decide whether to add a fade in or fade out
718                          * xfade and to which region
719                          */
720
721                         if (in->layer() <= out->layer()) {
722
723                                 /* incoming region is below the outgoing one,
724                                  * so apply a fade out to the outgoing one 
725                                  */
726
727                                 const XMLNodeList c = (*i)->children ();
728                                 
729                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
730                                         if ((*j)->name() == X_("FadeOut")) {
731                                                 out_a->fade_out()->set_state (**j, version);
732                                         } else if ((*j)->name() == X_("FadeIn")) {
733                                                 out_a->inverse_fade_out()->set_state (**j, version);
734                                         }
735                                 }
736                                 
737                                 if ((p = (*i)->property ("follow-overlap")) != 0) {
738                                         out_a->set_fade_out_is_short (!string_is_affirmative (p->value()));
739                                 } else {
740                                         out_a->set_fade_out_is_short (false);
741                                 }
742                                 
743                                 out_a->set_fade_out_is_xfade (true);
744                                 out_a->set_fade_out_active (true);
745
746                         } else {
747
748                                 /* apply a fade in to the incoming region,
749                                  * since its above the outgoing one
750                                  */
751
752                                 const XMLNodeList c = (*i)->children ();
753                                 
754                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
755                                         if ((*j)->name() == X_("FadeIn")) {
756                                                 in_a->fade_in()->set_state (**j, version);
757                                         } else if ((*j)->name() == X_("FadeOut")) {
758                                                 in_a->inverse_fade_in()->set_state (**j, version);
759                                         }
760                                 }
761                                 
762                                 if ((p = (*i)->property ("follow-overlap")) != 0) {
763                                         in_a->set_fade_in_is_short (!string_is_affirmative (p->value()));
764                                 } else {
765                                         in_a->set_fade_in_is_short (false);
766                                 }
767                                 
768                                 in_a->set_fade_in_is_xfade (true);
769                                 in_a->set_fade_in_active (true);
770                         }
771                 }
772         }
773 }