fix minor type error
[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
191                 switch (region->coverage (start, end)) {
192                 case OverlapNone:
193                         continue;
194
195                 case OverlapInternal:
196                 {
197                         framecnt_t const offset = start - region->position ();
198                         framecnt_t const trim = region->last_frame() - end;
199                         if (region->fade_in()->back()->when > offset) {
200                                 fade_in = region->fade_in()->back()->when - offset;
201                         }
202                         if (region->fade_out()->back()->when > trim) {
203                                 fade_out = region->fade_out()->back()->when - trim;
204                         }
205                         break;
206                 }
207
208                 case OverlapStart:
209                 {
210                         if (region->fade_in()->back()->when > 0) {
211                                 fade_in = region->fade_in()->back()->when;
212                         }
213                         if (start > region->last_frame() - region->fade_out()->back()->when) {
214                                 fade_out = region->last_frame() - start;
215                         }
216                         break;
217                 }
218
219                 case OverlapEnd:
220                 {
221                         framecnt_t const offset = start - region->position();
222                         if (region->fade_in()->back()->when > offset) {
223                                 fade_in = region->fade_in()->back()->when - offset;
224                         }
225                         if (start > region->last_frame() - region->fade_out()->back()->when) {
226                                 fade_out = region->last_frame() - start;
227                         } else {
228                                 fade_out = region->fade_out()->back()->when;
229                         }
230                         break;
231                 }
232
233                 case OverlapExternal:
234                         fade_in = region->fade_in()->back()->when;
235                         fade_out = region->fade_out()->back()->when;
236                         break;
237                 }
238
239                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
240                 assert (our_region);
241
242                 our_region->set_fade_in_length (fade_in);
243                 our_region->set_fade_out_length (fade_out);
244                 ++ours;
245         }
246
247         in_set_state--;
248
249         /* this constructor does NOT notify others (session) */
250 }
251
252 AudioPlaylist::~AudioPlaylist ()
253 {
254         _crossfades.clear ();
255 }
256
257 struct RegionSortByLayer {
258     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
259             return a->layer() < b->layer();
260     }
261 };
262
263 ARDOUR::framecnt_t
264 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
265                      framecnt_t cnt, unsigned chan_n)
266 {
267         framecnt_t ret = cnt;
268
269         /* optimizing this memset() away involves a lot of conditionals
270            that may well cause more of a hit due to cache misses
271            and related stuff than just doing this here.
272
273            it would be great if someone could measure this
274            at some point.
275
276            one way or another, parts of the requested area
277            that are not written to by Region::region_at()
278            for all Regions that cover the area need to be
279            zeroed.
280         */
281
282         memset (buf, 0, sizeof (Sample) * cnt);
283
284         /* this function is never called from a realtime thread, so
285            its OK to block (for short intervals).
286         */
287
288         Glib::RecMutex::Lock rm (region_lock);
289
290         framepos_t const end = start + cnt - 1;
291         framecnt_t read_frames = 0;
292         framecnt_t skip_frames = 0;
293         _read_data_count = 0;
294
295         _read_data_count = 0;
296
297         RegionList* rlist = regions_to_read (start, start+cnt);
298
299         if (rlist->empty()) {
300                 delete rlist;
301                 return cnt;
302         }
303
304         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
305         map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
306         vector<uint32_t> relevant_layers;
307
308         for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
309                 if ((*i)->coverage (start, end) != OverlapNone) {
310                         relevant_regions[(*i)->layer()].push_back (*i);
311                         relevant_layers.push_back ((*i)->layer());
312                 }
313         }
314
315         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
316                 if ((*i)->coverage (start, end) != OverlapNone) {
317                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
318                 }
319         }
320
321 //      RegionSortByLayer layer_cmp;
322 //      relevant_regions.sort (layer_cmp);
323
324         /* XXX this whole per-layer approach is a hack that
325            should be removed once Crossfades become
326            CrossfadeRegions and we just grab a list of relevant
327            regions and call read_at() on all of them.
328         */
329
330         sort (relevant_layers.begin(), relevant_layers.end());
331
332         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
333
334                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
335                 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
336
337
338                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
339                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
340                         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
341                         assert(ar);
342                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
343                         _read_data_count += ar->read_data_count();
344                 }
345
346                 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
347                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
348
349                         /* don't JACK up _read_data_count, since its the same data as we just
350                            read from the regions, and the OS should handle that for us.
351                         */
352                 }
353         }
354
355         delete rlist;
356         return ret;
357 }
358
359
360 void
361 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
362 {
363         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
364
365         if (in_set_state) {
366                 return;
367         }
368
369         if (r == 0) {
370                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
371                       << endmsg;
372                 return;
373         }
374
375         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
376
377                 if ((*i)->involves (r)) {
378                         i = _crossfades.erase (i);
379                 } else {
380                         ++i;
381                 }
382         }
383 }
384
385
386 void
387 AudioPlaylist::flush_notifications (bool from_undo)
388 {
389         Playlist::flush_notifications (from_undo);
390
391         if (in_flush) {
392                 return;
393         }
394
395         in_flush = true;
396
397         Crossfades::iterator a;
398         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
399                 NewCrossfade (*a); /* EMIT SIGNAL */
400         }
401
402         _pending_xfade_adds.clear ();
403
404         in_flush = false;
405 }
406
407 void
408 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
409 {
410         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
411         set<boost::shared_ptr<Crossfade> > updated;
412
413         if (ar == 0) {
414                 return;
415         }
416
417         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
418
419                 Crossfades::iterator tmp;
420
421                 tmp = x;
422                 ++tmp;
423
424                 /* only update them once */
425
426                 if ((*x)->involves (ar)) {
427
428                         pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
429
430                         if (u.second) {
431                                 /* x was successfully inserted into the set, so it has not already been updated */
432                                 try {
433                                         (*x)->refresh ();
434                                 }
435
436                                 catch (Crossfade::NoCrossfadeHere& err) {
437                                         // relax, Invalidated during refresh
438                                 }
439                         }
440                 }
441
442                 x = tmp;
443         }
444 }
445
446 void
447 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
448 {
449         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
450         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
451         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
452
453         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
454                 Crossfades::iterator tmp;
455                 tmp = x;
456                 ++tmp;
457
458                 boost::shared_ptr<Crossfade> fade;
459
460                 if ((*x)->_in == orig) {
461                         if (! (*x)->covers(right->position())) {
462                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
463                         } else {
464                                 // Overlap, the crossfade is copied on the left side of the right region instead
465                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
466                         }
467                 }
468
469                 if ((*x)->_out == orig) {
470                         if (! (*x)->covers(right->position())) {
471                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
472                         } else {
473                                 // Overlap, the crossfade is copied on the right side of the left region instead
474                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
475                         }
476                 }
477
478                 if (fade) {
479                         _crossfades.remove (*x);
480                         add_crossfade (fade);
481                 }
482                 x = tmp;
483         }
484 }
485
486 void
487 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
488 {
489         boost::shared_ptr<AudioRegion> other;
490         boost::shared_ptr<AudioRegion> region;
491         boost::shared_ptr<AudioRegion> top;
492         boost::shared_ptr<AudioRegion> bottom;
493         boost::shared_ptr<Crossfade>   xfade;
494         RegionList*  touched_regions = 0;
495
496         if (in_set_state || in_partition) {
497                 return;
498         }
499
500         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
501                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
502                       << endmsg;
503                 return;
504         }
505
506         if (!norefresh) {
507                 refresh_dependents (r);
508         }
509
510
511         if (!_session.config.get_auto_xfade()) {
512                 return;
513         }
514
515         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
516                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
517
518                 if (other == region) {
519                         continue;
520                 }
521
522                 if (other->muted() || region->muted()) {
523                         continue;
524                 }
525
526                 if (other->position() == r->position() && other->length() == r->length()) {
527                         /* precise overlay of two regions - no xfade */
528                         continue;
529                 }
530
531                 if (other->layer() < region->layer()) {
532                         top = region;
533                         bottom = other;
534                 } else {
535                         top = other;
536                         bottom = region;
537                 }
538
539                 if (!top->opaque()) {
540                         continue;
541                 }
542
543                 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
544
545                 delete touched_regions;
546                 touched_regions = 0;
547
548                 try {
549                         framecnt_t xfade_length;
550                         switch (c) {
551                         case OverlapNone:
552                                 break;
553
554                         case OverlapInternal:
555                                  /* {=============== top  =============}
556                                   *     [ ----- bottom  ------- ]
557                                   */
558                                 break;
559
560                         case OverlapExternal:
561
562                                 /*     [ -------- top ------- ]
563                                  * {=========== bottom =============}
564                                  */
565
566                                 /* to avoid discontinuities at the region boundaries of an internal
567                                    overlap (this region is completely within another), we create
568                                    two hidden crossfades at each boundary. this is not dependent
569                                    on the auto-xfade option, because we require it as basic
570                                    audio engineering.
571                                 */
572
573                                 xfade_length = min ((framecnt_t) 720, top->length());
574
575                                 if (top_region_at (top->first_frame()) == top) {
576
577                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
578                                         add_crossfade (xfade);
579                                 }
580
581                                 if (top_region_at (top->last_frame() - 1) == top) {
582
583                                         /*
584                                            only add a fade out if there is no region on top of the end of 'top' (which
585                                            would cover it).
586                                         */
587
588                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
589                                         add_crossfade (xfade);
590                                 }
591                                 break;
592                         case OverlapStart:
593
594                                 /*                   { ==== top ============ }
595                                  *   [---- bottom -------------------]
596                                  */
597
598                                 if (_session.config.get_xfade_model() == FullCrossfade) {
599                                         touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
600                                         if (touched_regions->size() <= 2) {
601                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
602                                                 add_crossfade (xfade);
603                                         }
604                                 } else {
605
606                                         touched_regions = regions_touched (top->first_frame(),
607                                                                            top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
608                                                                                                      top->length()));
609                                         if (touched_regions->size() <= 2) {
610                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
611                                                 add_crossfade (xfade);
612                                         }
613                                 }
614                                 break;
615                         case OverlapEnd:
616
617
618                                 /* [---- top ------------------------]
619                                  *                { ==== bottom ============ }
620                                  */
621
622                                 if (_session.config.get_xfade_model() == FullCrossfade) {
623
624                                         touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
625                                         if (touched_regions->size() <= 2) {
626                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
627                                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
628                                                 add_crossfade (xfade);
629                                         }
630
631                                 } else {
632                                         touched_regions = regions_touched (bottom->first_frame(),
633                                                                            bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
634                                                                                                         bottom->length()));
635                                         if (touched_regions->size() <= 2) {
636                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
637                                                 add_crossfade (xfade);
638                                         }
639                                 }
640                                 break;
641                         default:
642                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
643                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
644                                 add_crossfade (xfade);
645                         }
646                 }
647
648                 catch (failed_constructor& err) {
649                         continue;
650                 }
651
652                 catch (Crossfade::NoCrossfadeHere& err) {
653                         continue;
654                 }
655
656         }
657
658         delete touched_regions;
659 }
660
661 void
662 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
663 {
664         Crossfades::iterator ci;
665
666         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
667                 if (*(*ci) == *xfade) { // Crossfade::operator==()
668                         break;
669                 }
670         }
671
672         if (ci != _crossfades.end()) {
673                 // it will just go away
674         } else {
675                 _crossfades.push_back (xfade);
676
677                 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
678                 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
679
680                 notify_crossfade_added (xfade);
681         }
682 }
683
684 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
685 {
686         if (g_atomic_int_get(&block_notifications)) {
687                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
688         } else {
689                 NewCrossfade (x); /* EMIT SIGNAL */
690         }
691 }
692
693 void
694 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
695 {
696         Crossfades::iterator i;
697         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
698
699         xfade->in()->resume_fade_in ();
700         xfade->out()->resume_fade_out ();
701
702         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
703                 _crossfades.erase (i);
704         }
705 }
706
707 int
708 AudioPlaylist::set_state (const XMLNode& node, int version)
709 {
710         XMLNode *child;
711         XMLNodeList nlist;
712         XMLNodeConstIterator niter;
713
714         in_set_state++;
715
716         Playlist::set_state (node, version);
717
718         freeze ();
719
720         nlist = node.children();
721
722         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
723
724                 child = *niter;
725
726                 if (child->name() != "Crossfade") {
727                         continue;
728                 }
729
730                 try {
731                         boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
732                         _crossfades.push_back (xfade);
733                         xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
734                         xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
735                         NewCrossfade(xfade);
736                 }
737
738                 catch (failed_constructor& err) {
739                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
740                         //        _name)
741                         //    << endl;
742                         continue;
743                 }
744         }
745
746         thaw ();
747         in_set_state--;
748
749         return 0;
750 }
751
752 void
753 AudioPlaylist::clear (bool with_signals)
754 {
755         _crossfades.clear ();
756         Playlist::clear (with_signals);
757 }
758
759 XMLNode&
760 AudioPlaylist::state (bool full_state)
761 {
762         XMLNode& node = Playlist::state (full_state);
763
764         if (full_state) {
765                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
766                         node.add_child_nocopy ((*i)->get_state());
767                 }
768         }
769
770         return node;
771 }
772
773 void
774 AudioPlaylist::dump () const
775 {
776         boost::shared_ptr<Region>r;
777         boost::shared_ptr<Crossfade> x;
778
779         cerr << "Playlist \"" << _name << "\" " << endl
780              << regions.size() << " regions "
781              << _crossfades.size() << " crossfades"
782              << endl;
783
784         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
785                 r = *i;
786                 cerr << "  " << r->name() << " @ " << r << " ["
787                      << r->start() << "+" << r->length()
788                      << "] at "
789                      << r->position()
790                      << " on layer "
791                      << r->layer ()
792                      << endl;
793         }
794
795         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
796                 x = *i;
797                 cerr << "  xfade ["
798                      << x->out()->name()
799                      << ','
800                      << x->in()->name()
801                      << " @ "
802                      << x->position()
803                      << " length = "
804                      << x->length ()
805                      << " active ? "
806                      << (x->active() ? "yes" : "no")
807                      << endl;
808         }
809 }
810
811 bool
812 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
813 {
814         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
815
816         if (!r) {
817                 return false;
818         }
819
820         bool changed = false;
821         Crossfades::iterator c, ctmp;
822         set<boost::shared_ptr<Crossfade> > unique_xfades;
823
824         {
825                 RegionLock rlock (this);
826
827                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
828
829                         RegionList::iterator tmp = i;
830                         ++tmp;
831
832                         if ((*i) == region) {
833                                 regions.erase (i);
834                                 changed = true;
835                         }
836
837                         i = tmp;
838                 }
839
840                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
841
842                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
843                         ++xtmp;
844
845                         if ((*x) == region) {
846                                 all_regions.erase (x);
847                                 changed = true;
848                         }
849
850                         x = xtmp;
851                 }
852
853                 region->set_playlist (boost::shared_ptr<Playlist>());
854         }
855
856         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
857                 ctmp = c;
858                 ++ctmp;
859
860                 if ((*c)->involves (r)) {
861                         unique_xfades.insert (*c);
862                         _crossfades.erase (c);
863                 }
864
865                 c = ctmp;
866         }
867
868         if (changed) {
869                 /* overload this, it normally means "removed", not destroyed */
870                 notify_region_removed (region);
871         }
872
873         return changed;
874 }
875
876 void
877 AudioPlaylist::crossfade_changed (const PropertyChange&)
878 {
879         if (in_flush || in_set_state) {
880                 return;
881         }
882
883         /* XXX is there a loop here? can an xfade change not happen
884            due to a playlist change? well, sure activation would
885            be an example. maybe we should check the type of change
886            that occured.
887         */
888
889         notify_contents_changed ();
890 }
891
892 bool
893 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
894 {
895         if (in_flush || in_set_state) {
896                 return false;
897         }
898
899         PropertyChange our_interests;
900
901         our_interests.add (Properties::fade_in_active);
902         our_interests.add (Properties::fade_out_active);
903         our_interests.add (Properties::scale_amplitude);
904         our_interests.add (Properties::envelope_active);
905         our_interests.add (Properties::envelope);
906         our_interests.add (Properties::fade_in);
907         our_interests.add (Properties::fade_out);
908
909         bool parent_wants_notify;
910
911         parent_wants_notify = Playlist::region_changed (what_changed, region);
912
913         if (parent_wants_notify || (what_changed.contains (our_interests))) {
914                 notify_contents_changed ();
915         }
916
917         return true;
918 }
919
920 void
921 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
922 {
923         RegionLock rlock (this);
924
925         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
926                 framepos_t const start = (*i)->position ();
927                 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
928
929                 if (frame >= start && frame <= end) {
930                         clist.push_back (*i);
931                 }
932         }
933 }
934
935 void
936 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
937 {
938         RegionLock rl (this, false);
939         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
940                 s (*i);
941         }
942 }
943
944 void
945 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
946 {
947         for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
948                 add_crossfade (*i);
949         }
950
951         /* don't remove crossfades here; they will be dealt with by the dependency code */
952 }
953
954 boost::shared_ptr<Crossfade>
955 AudioPlaylist::find_crossfade (const PBD::ID& id) const
956 {
957         Crossfades::const_iterator i = _crossfades.begin ();
958         while (i != _crossfades.end() && (*i)->id() != id) {
959                 ++i;
960         }
961
962         if (i == _crossfades.end()) {
963                 return boost::shared_ptr<Crossfade> ();
964         }
965
966         return *i;
967 }