muted regions should be totally ignored during playlist read()
[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                 /* muted regions don't figure into it at all */
211                 if ( ar->muted() )
212                         continue;
213
214                 /* Work out which bits of this region need to be read;
215                    first, trim to the range we are reading...
216                 */
217                 Evoral::Range<framepos_t> region_range = ar->range ();
218                 region_range.from = max (region_range.from, start);
219                 region_range.to = min (region_range.to, start + cnt - 1);
220
221                 /* ... and then remove the bits that are already done */
222
223                 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
224
225                 /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
226                    and start-of-fade-out) to the `done' list.
227                 */
228
229                 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
230
231                 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
232                         Evoral::Range<framepos_t> d = *j;
233                         to_do.push_back (Segment (ar, d));
234
235                         if (ar->opaque ()) {
236                                 /* Cut this range down to just the body and mark it done */
237                                 Evoral::Range<framepos_t> body = ar->body_range ();
238                                 if (body.from < d.to && body.to > d.from) {
239                                         d.from = max (d.from, body.from);
240                                         d.to = min (d.to, body.to);
241                                         done.add (d);
242                                 }
243                         }
244                 }
245         }
246
247         /* Now go backwards through the to_do list doing the actual reads */
248         for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
249                 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
250                                                                    name(), i->region->name(), i->range.from,
251                                                                    i->range.to - i->range.from + 1, (int) chan_n,
252                                                                    buf, i->range.from - start));
253                 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);
254         }
255
256         return cnt;
257 }
258
259 void
260 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
261 {
262         if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
263                 return;
264         }
265
266         boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
267         boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
268
269         RegionList all = *starts;
270         std::copy (ends->begin(), ends->end(), back_inserter (all));
271
272         all.sort (RegionSortByLayer ());
273
274         set<boost::shared_ptr<Region> > done_start;
275         set<boost::shared_ptr<Region> > done_end;
276
277         for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
278                 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
279
280                         if (i == j) {
281                                 continue;
282                         }
283
284                         if ((*i)->muted() || (*j)->muted()) {
285                                 continue;
286                         }
287
288                         if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
289                                 /* precise overlay: no xfade */
290                                 continue;
291                         }
292
293                         if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
294                                 /* starts or ends match: no xfade */
295                                 continue;
296                         }
297                         
298                         boost::shared_ptr<AudioRegion> top;
299                         boost::shared_ptr<AudioRegion> bottom;
300                 
301                         if ((*i)->layer() < (*j)->layer()) {
302                                 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
303                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
304                         } else {
305                                 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
306                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
307                         }
308                         
309                         if (!top->opaque ()) {
310                                 continue;
311                         }
312
313                         Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
314
315                         if (c == Evoral::OverlapStart) {
316                                 
317                                 /* top starts within bottom but covers bottom's end */
318                                 
319                                 /*                   { ==== top ============ } 
320                                  *   [---- bottom -------------------] 
321                                  */
322
323                                 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
324
325                                         /* Top's fade-in will cause an implicit fade-out of bottom */
326                                         
327                                         if (top->fade_in_is_xfade() && top->fade_in_is_short()) {
328
329                                                 /* its already an xfade. if its
330                                                  * really short, leave it
331                                                  * alone.
332                                                  */
333
334                                         } else {
335                                                 framecnt_t len = 0;
336                                                 
337                                                 if (_capture_insertion_underway) {
338                                                         len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
339                                                 } else {
340                                                         switch (_session.config.get_xfade_model()) {
341                                                         case FullCrossfade:
342                                                                 len = bottom->last_frame () - top->first_frame () + 1;
343                                                                 top->set_fade_in_is_short (false);
344                                                                 break;
345                                                         case ShortCrossfade:
346                                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
347                                                                 top->set_fade_in_is_short (true);
348                                                                 break;
349                                                         }
350                                                 }
351
352                                                 top->set_fade_in_active (true);
353                                                 top->set_fade_in_is_xfade (true);
354                                                 
355                                                 /* XXX may 2012: -3dB and -6dB curves
356                                                  * are the same right now 
357                                                  */
358                                                 
359                                                 switch (_session.config.get_xfade_choice ()) {
360                                                 case ConstantPowerMinus3dB:
361                                                         top->set_fade_in (FadeConstantPower, len);
362                                                         break;
363                                                 case ConstantPowerMinus6dB:
364                                                         top->set_fade_in (FadeConstantPower, len);
365                                                         break;
366                                                 case RegionFades:
367                                                         top->set_fade_in_length (len);
368                                                         break;
369                                                 }
370                                         }
371
372                                         done_start.insert (top);
373                                 }
374
375                         } else if (c == Evoral::OverlapEnd) {
376                                 
377                                 /* top covers start of bottom but ends within it */
378                                 
379                                 /* [---- top ------------------------] 
380                                  *                { ==== bottom ============ } 
381                                  */
382
383                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
384                                         /* Top's fade-out will cause an implicit fade-in of bottom */
385                                         
386                                         
387                                         if (top->fade_out_is_xfade() && top->fade_out_is_short()) {
388
389                                                 /* its already an xfade. if its
390                                                  * really short, leave it
391                                                  * alone.
392                                                  */
393
394                                         } else {
395                                                 framecnt_t len = 0;
396                                                 
397                                                 if (_capture_insertion_underway) {
398                                                         len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
399                                                 } else {
400                                                         switch (_session.config.get_xfade_model()) {
401                                                         case FullCrossfade:
402                                                                 len = top->last_frame () - bottom->first_frame () + 1;
403                                                                 break;
404                                                         case ShortCrossfade:
405                                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
406                                                                 break;
407                                                         }
408                                                 }
409                                                 
410                                                 top->set_fade_out_active (true);
411                                                 top->set_fade_out_is_xfade (true);
412                                                 
413                                                 switch (_session.config.get_xfade_choice ()) {
414                                                 case ConstantPowerMinus3dB:
415                                                         top->set_fade_out (FadeConstantPower, len);
416                                                         break;
417                                                 case ConstantPowerMinus6dB:
418                                                         top->set_fade_out (FadeConstantPower, len);
419                                                         break;
420                                                 case RegionFades:
421                                                         top->set_fade_out_length (len);
422                                                         break;
423                                                 }
424                                         }
425
426                                         done_end.insert (top);
427                                 }
428                         }
429                 }
430         }
431
432         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
433                 if (done_start.find (*i) == done_start.end()) {
434                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
435                         if (r->fade_in_is_xfade()) {
436                                 r->set_default_fade_in ();
437                         }
438                 }
439         }
440
441         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
442                 if (done_end.find (*i) == done_end.end()) {
443                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
444                         if (r->fade_out_is_xfade()) {
445                                 r->set_default_fade_out ();
446                         }
447                 }
448         }
449 }
450
451 void
452 AudioPlaylist::dump () const
453 {
454         boost::shared_ptr<Region>r;
455
456         cerr << "Playlist \"" << _name << "\" " << endl
457              << regions.size() << " regions "
458              << endl;
459
460         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
461                 r = *i;
462                 cerr << "  " << r->name() << " @ " << r << " ["
463                      << r->start() << "+" << r->length()
464                      << "] at "
465                      << r->position()
466                      << " on layer "
467                      << r->layer ()
468                      << endl;
469         }
470 }
471
472 bool
473 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
474 {
475         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
476
477         if (!r) {
478                 return false;
479         }
480
481         bool changed = false;
482
483         {
484                 RegionWriteLock rlock (this);
485
486                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
487
488                         RegionList::iterator tmp = i;
489                         ++tmp;
490
491                         if ((*i) == region) {
492                                 regions.erase (i);
493                                 changed = true;
494                         }
495
496                         i = tmp;
497                 }
498
499                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
500
501                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
502                         ++xtmp;
503
504                         if ((*x) == region) {
505                                 all_regions.erase (x);
506                                 changed = true;
507                         }
508
509                         x = xtmp;
510                 }
511
512                 region->set_playlist (boost::shared_ptr<Playlist>());
513         }
514
515         if (changed) {
516                 /* overload this, it normally means "removed", not destroyed */
517                 notify_region_removed (region);
518         }
519
520         return changed;
521 }
522
523 bool
524 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
525 {
526         if (in_flush || in_set_state) {
527                 return false;
528         }
529
530         PropertyChange our_interests;
531
532         our_interests.add (Properties::fade_in_active);
533         our_interests.add (Properties::fade_out_active);
534         our_interests.add (Properties::scale_amplitude);
535         our_interests.add (Properties::envelope_active);
536         our_interests.add (Properties::envelope);
537         our_interests.add (Properties::fade_in);
538         our_interests.add (Properties::fade_out);
539
540         bool parent_wants_notify;
541
542         parent_wants_notify = Playlist::region_changed (what_changed, region);
543
544         if (parent_wants_notify || (what_changed.contains (our_interests))) {
545                 notify_contents_changed ();
546         }
547
548         return true;
549 }
550
551 void
552 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
553 {
554         RegionSortByPosition cmp;
555         boost::shared_ptr<AudioRegion> ar;
556
557         sort (copies.begin(), copies.end(), cmp);
558
559         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
560
561         /* disable fade in of the first region */
562
563         if (ar) {
564                 ar->set_fade_in_active (false);
565         }
566
567         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
568
569         /* disable fade out of the last region */
570
571         if (ar) {
572                 ar->set_fade_out_active (false);
573         }
574 }
575
576 void
577 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
578 {
579         RegionSortByPosition cmp;
580         boost::shared_ptr<AudioRegion> ar;
581         boost::shared_ptr<AudioRegion> cr;
582
583         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
584                 return;
585         }
586
587         sort (originals.begin(), originals.end(), cmp);
588
589         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
590
591         /* copy the fade in of the first into the compound region */
592
593         if (ar) {
594                 cr->set_fade_in (ar->fade_in());
595         }
596
597         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
598
599         if (ar) {
600                 /* copy the fade out of the last into the compound region */
601                 cr->set_fade_out (ar->fade_out());
602         }
603 }
604
605 void
606 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
607 {
608         RegionSortByPosition cmp;
609         boost::shared_ptr<AudioRegion> ar;
610         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
611
612         if (!cr) {
613                 return;
614         }
615
616         sort (originals.begin(), originals.end(), cmp);
617
618         /* no need to call clear_changes() on the originals because that is
619          * done within Playlist::uncombine ()
620          */
621
622         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
623
624                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
625                         continue;
626                 }
627
628                 /* scale the uncombined regions by any gain setting for the
629                  * compound one.
630                  */
631
632                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
633
634                 if (i == originals.begin()) {
635
636                         /* copy the compound region's fade in back into the first
637                            original region.
638                         */
639
640                         if (cr->fade_in()->back()->when <= ar->length()) {
641                                 /* don't do this if the fade is longer than the
642                                  * region
643                                  */
644                                 ar->set_fade_in (cr->fade_in());
645                         }
646
647
648                 } else if (*i == originals.back()) {
649
650                         /* copy the compound region's fade out back into the last
651                            original region.
652                         */
653
654                         if (cr->fade_out()->back()->when <= ar->length()) {
655                                 /* don't do this if the fade is longer than the
656                                  * region
657                                  */
658                                 ar->set_fade_out (cr->fade_out());
659                         }
660
661                 }
662
663                 _session.add_command (new StatefulDiffCommand (*i));
664         }
665 }
666
667 int
668 AudioPlaylist::set_state (const XMLNode& node, int version)
669 {
670         return Playlist::set_state (node, version);
671 }
672
673 void
674 AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
675 {
676         /* Read legacy Crossfade nodes and set up region fades accordingly */
677
678         XMLNodeList children = node.children ();
679         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
680                 if ((*i)->name() == X_("Crossfade")) {
681
682                         XMLProperty* p = (*i)->property (X_("active"));
683                         assert (p);
684
685                         if (!string_is_affirmative (p->value())) {
686                                 continue;
687                         }
688                         
689                         if ((p = (*i)->property (X_("in"))) == 0) {
690                                 continue;
691                         }
692
693                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
694
695                         if (!in) {
696                                 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
697                                                            name()) 
698                                         << endmsg;
699                                 continue;
700                         }
701
702                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
703                         assert (in_a);
704
705                         if ((p = (*i)->property (X_("out"))) == 0) {
706                                 continue;
707                         }
708
709                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
710
711                         if (!out) {
712                                 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
713                                                            name()) 
714                                         << endmsg;
715                                 continue;
716                         }
717
718                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
719                         assert (out_a);
720
721                         /* now decide whether to add a fade in or fade out
722                          * xfade and to which region
723                          */
724
725                         if (in->layer() <= out->layer()) {
726
727                                 /* incoming region is below the outgoing one,
728                                  * so apply a fade out to the outgoing one 
729                                  */
730
731                                 const XMLNodeList c = (*i)->children ();
732                                 
733                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
734                                         if ((*j)->name() == X_("FadeOut")) {
735                                                 out_a->fade_out()->set_state (**j, version);
736                                         } else if ((*j)->name() == X_("FadeIn")) {
737                                                 out_a->inverse_fade_out()->set_state (**j, version);
738                                         }
739                                 }
740                                 
741                                 if ((p = (*i)->property ("follow-overlap")) != 0) {
742                                         out_a->set_fade_out_is_short (!string_is_affirmative (p->value()));
743                                 } else {
744                                         out_a->set_fade_out_is_short (false);
745                                 }
746                                 
747                                 out_a->set_fade_out_is_xfade (true);
748                                 out_a->set_fade_out_active (true);
749
750                         } else {
751
752                                 /* apply a fade in to the incoming region,
753                                  * since its above the outgoing one
754                                  */
755
756                                 const XMLNodeList c = (*i)->children ();
757                                 
758                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
759                                         if ((*j)->name() == X_("FadeIn")) {
760                                                 in_a->fade_in()->set_state (**j, version);
761                                         } else if ((*j)->name() == X_("FadeOut")) {
762                                                 in_a->inverse_fade_in()->set_state (**j, version);
763                                         }
764                                 }
765                                 
766                                 if ((p = (*i)->property ("follow-overlap")) != 0) {
767                                         in_a->set_fade_in_is_short (!string_is_affirmative (p->value()));
768                                 } else {
769                                         in_a->set_fade_in_is_short (false);
770                                 }
771                                 
772                                 in_a->set_fade_in_is_xfade (true);
773                                 in_a->set_fade_in_active (true);
774                         }
775                 }
776         }
777 }