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