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