Merged with trunk R920.
[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     $Id$
19 */
20
21 #include <algorithm>
22
23 #include <cstdlib>
24
25 #include <sigc++/bind.h>
26
27 #include <ardour/types.h>
28 #include <ardour/configuration.h>
29 #include <ardour/audioplaylist.h>
30 #include <ardour/audioregion.h>
31 #include <ardour/crossfade.h>
32 #include <ardour/crossfade_compare.h>
33 #include <ardour/session.h>
34
35 #include "i18n.h"
36
37 using namespace ARDOUR;
38 using namespace sigc;
39 using namespace std;
40 using namespace PBD;
41
42 AudioPlaylist::State::~State ()
43 {
44 }
45
46 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
47         : Playlist (session, node, DataType::AUDIO, hidden)
48 {
49         const XMLProperty* prop = node.property("type");
50         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
51
52         in_set_state = true;
53         set_state (node);
54         in_set_state = false;
55
56         save_state (_("initial state"));
57
58         if (!hidden) {
59                 PlaylistCreated (this); /* EMIT SIGNAL */
60         }
61 }
62
63 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
64         : Playlist (session, name, DataType::AUDIO, hidden)
65 {
66         save_state (_("initial state"));
67
68         if (!hidden) {
69                 PlaylistCreated (this); /* EMIT SIGNAL */
70         }
71
72 }
73
74 AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden)
75         : Playlist (other, name, hidden)
76 {
77         save_state (_("initial state"));
78
79         RegionList::const_iterator in_o  = other.regions.begin();
80         RegionList::iterator in_n = regions.begin();
81
82         while (in_o != other.regions.end()) {
83                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
84
85                 // We look only for crossfades which begin with the current region, so we don't get doubles
86                 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
87                         if ((*xfades)->in() == ar) {
88                                 // We found one! Now copy it!
89
90                                 RegionList::const_iterator out_o = other.regions.begin();
91                                 RegionList::const_iterator out_n = regions.begin();
92
93                                 while (out_o != other.regions.end()) {
94                                         
95                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
96                                         
97                                         if ((*xfades)->out() == ar2) {
98                                                 boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
99                                                 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
100                                                 Crossfade *new_fade = new Crossfade (*(*xfades), in, out);
101                                                 add_crossfade(*new_fade);
102                                                 break;
103                                         }
104                                         
105                                         out_o++;
106                                         out_n++;
107                                 }
108 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
109                         }
110                 }
111
112                 in_o++;
113                 in_n++;
114         }
115
116         if (!hidden) {
117                 PlaylistCreated (this); /* EMIT SIGNAL */
118         }
119 }
120
121 AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden)
122         : Playlist (other, start, cnt, name, hidden)
123 {
124         save_state (_("initial state"));
125
126         /* this constructor does NOT notify others (session) */
127 }
128
129 AudioPlaylist::~AudioPlaylist ()
130 {
131         set<Crossfade*> all_xfades;
132
133         GoingAway (); /* EMIT SIGNAL */
134
135         /* drop connections to signals */
136
137         notify_callbacks ();
138
139         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ++x) {
140                 all_xfades.insert (*x);
141         }
142
143         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
144
145                 AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
146
147                 for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
148                         all_xfades.insert (*xf);
149                 }
150
151                 delete apstate;
152         }
153
154         /* delete every crossfade */
155
156         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
157                 delete *axf;
158         }
159 }
160
161 struct RegionSortByLayer {
162     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
163             return a->layer() < b->layer();
164     }
165 };
166
167 jack_nframes_t
168 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, jack_nframes_t start,
169                      jack_nframes_t cnt, unsigned chan_n)
170 {
171         jack_nframes_t ret = cnt;
172         jack_nframes_t end;
173         jack_nframes_t read_frames;
174         jack_nframes_t skip_frames;
175
176         /* optimizing this memset() away involves a lot of conditionals
177            that may well cause more of a hit due to cache misses 
178            and related stuff than just doing this here.
179            
180            it would be great if someone could measure this
181            at some point.
182
183            one way or another, parts of the requested area
184            that are not written to by Region::region_at()
185            for all Regions that cover the area need to be
186            zeroed.
187         */
188
189         memset (buf, 0, sizeof (Sample) * cnt);
190
191         /* this function is never called from a realtime thread, so 
192            its OK to block (for short intervals).
193         */
194
195         Glib::Mutex::Lock rm (region_lock);
196
197         end =  start + cnt - 1;
198
199         read_frames = 0;
200         skip_frames = 0;
201         _read_data_count = 0;
202
203         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
204         map<uint32_t,vector<Crossfade*> > relevant_xfades;
205         vector<uint32_t> relevant_layers;
206
207         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
208                 if ((*i)->coverage (start, end) != OverlapNone) {
209                         
210                         relevant_regions[(*i)->layer()].push_back (*i);
211                         relevant_layers.push_back ((*i)->layer());
212                 }
213         }
214
215         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
216                 if ((*i)->coverage (start, end) != OverlapNone) {
217                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
218                 }
219         }
220
221 //      RegionSortByLayer layer_cmp;
222 //      relevant_regions.sort (layer_cmp);
223
224         /* XXX this whole per-layer approach is a hack that
225            should be removed once Crossfades become
226            CrossfadeRegions and we just grab a list of relevant
227            regions and call read_at() on all of them.
228         */
229
230         sort (relevant_layers.begin(), relevant_layers.end());
231
232         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
233
234                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
235                 vector<Crossfade*>& x (relevant_xfades[*l]);
236
237                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
238                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
239                         assert(ar);
240                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
241                         _read_data_count += ar->read_data_count();
242                 }
243                 
244                 for (vector<Crossfade*>::iterator i = x.begin(); i != x.end(); ++i) {
245                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
246
247                         /* don't JACK up _read_data_count, since its the same data as we just
248                            read from the regions, and the OS should handle that for us.
249                         */
250                 }
251         }
252
253         return ret;
254 }
255
256
257 void
258 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
259 {
260         Crossfades::iterator i, tmp;
261         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
262         
263         if (r == 0) {
264                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
265                       << endmsg;
266                 return;
267         }
268
269         for (i = _crossfades.begin(); i != _crossfades.end(); ) {
270                 tmp = i;
271                 tmp++;
272
273                 if ((*i)->involves (r)) {
274                         /* do not delete crossfades */
275                         _crossfades.erase (i);
276                 }
277                 
278                 i = tmp;
279         }
280 }
281
282
283 void
284 AudioPlaylist::flush_notifications ()
285 {
286         Playlist::flush_notifications();
287
288         if (in_flush) {
289                 return;
290         }
291
292         in_flush = true;
293
294         Crossfades::iterator a;
295         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
296                 NewCrossfade (*a); /* EMIT SIGNAL */
297         }
298
299         _pending_xfade_adds.clear ();
300         
301         in_flush = false;
302 }
303
304 void
305 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
306 {
307         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
308         set<Crossfade*> updated;
309
310         if (ar == 0) {
311                 return;
312         }
313
314         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
315
316                 Crossfades::iterator tmp;
317                 
318                 tmp = x;
319                 ++tmp;
320
321                 /* only update them once */
322
323                 if ((*x)->involves (ar)) {
324
325                         if (find (updated.begin(), updated.end(), *x) == updated.end()) {
326                                 if ((*x)->refresh ()) {
327                                         /* not invalidated by the refresh */
328                                         updated.insert (*x);
329                                 }
330                         }
331                 }
332
333                 x = tmp;
334         }
335 }
336
337 void
338 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
339 {
340         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
341         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
342         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
343
344         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
345                 Crossfades::iterator tmp;
346                 tmp = x;
347                 ++tmp;
348
349                 Crossfade *fade = 0;
350                 
351                 if ((*x)->_in == orig) {
352                         if (! (*x)->covers(right->position())) {
353                                 fade = new Crossfade (**x, left, (*x)->_out);
354                         } else {
355                                 // Overlap, the crossfade is copied on the left side of the right region instead
356                                 fade = new Crossfade (**x, right, (*x)->_out);
357                         }
358                 }
359                 
360                 if ((*x)->_out == orig) {
361                         if (! (*x)->covers(right->position())) {
362                                 fade = new Crossfade (**x, (*x)->_in, right);
363                         } else {
364                                 // Overlap, the crossfade is copied on the right side of the left region instead
365                                 fade = new Crossfade (**x, (*x)->_in, left);
366                         }
367                 }
368                 
369                 if (fade) {
370                         _crossfades.remove (*x);
371                         add_crossfade (*fade);
372                 }
373                 x = tmp;
374         }
375 }
376
377 void
378 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
379 {
380         boost::shared_ptr<AudioRegion> other;
381         boost::shared_ptr<AudioRegion> region;
382         boost::shared_ptr<AudioRegion> top;
383         boost::shared_ptr<AudioRegion> bottom;
384         Crossfade*   xfade;
385
386         if (in_set_state || in_partition) {
387                 return;
388         }
389
390         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
391                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
392                       << endmsg;
393                 return;
394         }
395
396         if (!norefresh) {
397                 refresh_dependents (r);
398         }
399
400         if (!Config->get_auto_xfade()) {
401                 return;
402         }
403
404         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
405
406                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
407
408                 if (other == region) {
409                         continue;
410                 }
411
412                 if (other->muted() || region->muted()) {
413                         continue;
414                 }
415
416                 if (other->layer() < region->layer()) {
417                         top = region;
418                         bottom = other;
419                 } else {
420                         top = other;
421                         bottom = region;
422                 }
423
424                 try {
425                                 
426                         if (top->coverage (bottom->position(), bottom->last_frame()) != OverlapNone) {
427                                 
428                                 /* check if the upper region is within the lower region */
429                                 
430                                 if (top->first_frame() > bottom->first_frame() &&
431                                     top->last_frame() < bottom->last_frame()) {
432                                         
433                                         
434                                         /*     [ -------- top ------- ]
435                                          * {=========== bottom =============}
436                                          */
437                                         
438                                         /* to avoid discontinuities at the region boundaries of an internal
439                                            overlap (this region is completely within another), we create
440                                            two hidden crossfades at each boundary. this is not dependent
441                                            on the auto-xfade option, because we require it as basic
442                                            audio engineering.
443                                         */
444                                         
445                                         jack_nframes_t xfade_length = min ((jack_nframes_t) 720, top->length());
446                                         
447                                                             /*  in,      out */
448                                         xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn);
449                                         add_crossfade (*xfade);
450                                         xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut);
451                                         add_crossfade (*xfade);
452                                         
453                                 } else {
454                 
455                                         xfade = new Crossfade (other, region, _session.get_xfade_model(), _session.get_crossfades_active());
456                                         add_crossfade (*xfade);
457                                 }
458                         } 
459                 }
460                 
461                 catch (failed_constructor& err) {
462                         continue;
463                 }
464                 
465                 catch (Crossfade::NoCrossfadeHere& err) {
466                         continue;
467                 }
468                 
469         }
470 }
471
472 void
473 AudioPlaylist::add_crossfade (Crossfade& xfade)
474 {
475         Crossfades::iterator ci;
476
477         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
478                 if (*(*ci) == xfade) { // Crossfade::operator==()
479                         break;
480                 }
481         }
482         
483         if (ci != _crossfades.end()) {
484                 delete &xfade;
485         } else {
486                 _crossfades.push_back (&xfade);
487
488                 xfade.Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
489                 xfade.StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
490
491                 notify_crossfade_added (&xfade);
492         }
493 }
494         
495 void AudioPlaylist::notify_crossfade_added (Crossfade *x)
496 {
497         if (g_atomic_int_get(&block_notifications)) {
498                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
499         } else {
500                 NewCrossfade (x); /* EMIT SIGNAL */
501         }
502 }
503
504 void
505 AudioPlaylist::crossfade_invalidated (Crossfade* xfade)
506 {
507         Crossfades::iterator i;
508
509         xfade->in()->resume_fade_in ();
510         xfade->out()->resume_fade_out ();
511
512         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
513                 _crossfades.erase (i);
514         }
515 }
516
517 int
518 AudioPlaylist::set_state (const XMLNode& node)
519 {
520         XMLNode *child;
521         XMLNodeList nlist;
522         XMLNodeConstIterator niter;
523
524         if (!in_set_state) {
525                 Playlist::set_state (node);
526         }
527
528         nlist = node.children();
529
530         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
531
532                 child = *niter;
533
534                 if (child->name() == "Crossfade") {
535
536                         Crossfade *xfade;
537                         
538                         try {
539                                 xfade = new Crossfade (*((const Playlist *)this), *child);
540                         }
541
542                         catch (failed_constructor& err) {
543                           //    cout << string_compose (_("could not create crossfade object in playlist %1"),
544                           //      _name) 
545                           //    << endl;
546                                 continue;
547                         }
548
549                         Crossfades::iterator ci;
550
551                         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
552                                 if (*(*ci) == *xfade) {
553                                         break;
554                                 }
555                         }
556
557                         if (ci == _crossfades.end()) {
558                                 _crossfades.push_back (xfade);
559                                 xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
560                                 xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
561                                 /* no need to notify here */
562                         } else {
563                                 delete xfade;
564                         }
565                 }
566
567         }
568
569         return 0;
570 }
571
572 void
573 AudioPlaylist::drop_all_states ()
574 {
575         set<Crossfade*> all_xfades;
576         set<boost::shared_ptr<Region> > all_regions;
577
578         /* find every region we've ever used, and add it to the set of 
579            all regions. same for xfades;
580         */
581
582         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
583                 
584                 AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
585
586                 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
587                         all_regions.insert (*r);
588                 }
589
590                 for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
591                         all_xfades.insert (*xf);
592                 }
593         }
594
595         /* now remove from the "all" lists every region that is in the current list. */
596
597         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
598                 set<boost::shared_ptr<Region> >::iterator x = all_regions.find (*i);
599                 if (x != all_regions.end()) {
600                         all_regions.erase (x);
601                 }
602         }
603
604         /* ditto for every crossfade */
605
606         for (list<Crossfade*>::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
607                 set<Crossfade*>::iterator x = all_xfades.find (*i);
608                 if (x != all_xfades.end()) {
609                         all_xfades.erase (x);
610                 }
611         }
612
613         /* delete every crossfade that is left (ditto as per regions) */
614
615         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
616                 delete *axf;
617         }
618
619         /* Now do the generic thing ... */
620
621         StateManager::drop_all_states ();
622 }
623
624 StateManager::State*
625 AudioPlaylist::state_factory (std::string why) const
626 {
627         State* state = new State (why);
628
629         state->regions = regions;
630         state->region_states.clear ();
631         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
632                 state->region_states.push_back ((*i)->get_memento());
633         }
634
635         state->crossfades = _crossfades;
636         state->crossfade_states.clear ();
637         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
638                 state->crossfade_states.push_back ((*i)->get_memento());
639         }
640         return state;
641 }
642
643 Change
644 AudioPlaylist::restore_state (StateManager::State& state)
645
646         { 
647                 RegionLock rlock (this);
648                 State* apstate = dynamic_cast<State*> (&state);
649
650                 in_set_state = true;
651
652                 regions = apstate->regions;
653
654                 for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
655                         (*s) ();
656                 }
657
658                 _crossfades = apstate->crossfades;
659                 
660                 for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
661                         (*s) ();
662                 }
663
664                 in_set_state = false;
665         }
666
667         notify_length_changed ();
668         return Change (~0);
669 }
670
671 UndoAction
672 AudioPlaylist::get_memento () const
673 {
674         return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
675 }
676
677 void
678 AudioPlaylist::clear (bool with_save)
679 {
680         _crossfades.clear ();
681         
682         Playlist::clear (with_save);
683 }
684
685 XMLNode&
686 AudioPlaylist::state (bool full_state)
687 {
688         XMLNode& node = Playlist::state (full_state);
689
690         if (full_state) {
691                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
692                         node.add_child_nocopy ((*i)->get_state());
693                 }
694         }
695         
696         return node;
697 }
698
699 void
700 AudioPlaylist::dump () const
701 {
702         boost::shared_ptr<Region>r;
703         Crossfade *x;
704
705         cerr << "Playlist \"" << _name << "\" " << endl
706              << regions.size() << " regions "
707              << _crossfades.size() << " crossfades"
708              << endl;
709
710         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
711                 r = *i;
712                 cerr << "  " << r->name() << " @ " << r << " [" 
713                      << r->start() << "+" << r->length() 
714                      << "] at " 
715                      << r->position()
716                      << " on layer "
717                      << r->layer ()
718                      << endl;
719         }
720
721         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
722                 x = *i;
723                 cerr << "  xfade [" 
724                      << x->out()->name()
725                      << ','
726                      << x->in()->name()
727                      << " @ "
728                      << x->position()
729                      << " length = " 
730                      << x->length ()
731                      << " active ? "
732                      << (x->active() ? "yes" : "no")
733                      << endl;
734         }
735 }
736
737 bool
738 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
739 {
740         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
741         bool changed = false;
742         Crossfades::iterator c, ctmp;
743         set<Crossfade*> unique_xfades;
744
745         if (r == 0) {
746                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
747                       << endmsg;
748                 /*NOTREACHED*/
749                 return false;
750         }
751
752         { 
753                 RegionLock rlock (this);
754                 RegionList::iterator i;
755                 RegionList::iterator tmp;
756
757                 for (i = regions.begin(); i != regions.end(); ) {
758                         
759                         tmp = i;
760                         ++tmp;
761                         
762                         if ((*i) == region) {
763                                 regions.erase (i);
764                                 changed = true;
765                         }
766                         
767                         i = tmp;
768                 }
769         }
770
771         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
772                 ctmp = c;
773                 ++ctmp;
774
775                 if ((*c)->involves (r)) {
776                         unique_xfades.insert (*c);
777                         _crossfades.erase (c);
778                 }
779                 
780                 c = ctmp;
781         }
782
783         for (StateMap::iterator s = states.begin(); s != states.end(); ) {
784                 StateMap::iterator tmp;
785
786                 tmp = s;
787                 ++tmp;
788
789                 State* astate = dynamic_cast<State*> (*s);
790                 
791                 for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) {
792
793                         ctmp = c;
794                         ++ctmp;
795
796                         if ((*c)->involves (r)) {
797                                 unique_xfades.insert (*c);
798                                 _crossfades.erase (c);
799                         }
800
801                         c = ctmp;
802                 }
803
804                 list<UndoAction>::iterator rsi, rsitmp;
805                 RegionList::iterator ri, ritmp;
806
807                 for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); 
808                      ri != astate->regions.end() && rsi != astate->region_states.end();) {
809
810
811                         ritmp = ri;
812                         ++ritmp;
813
814                         rsitmp = rsi; 
815                         ++rsitmp;
816
817                         if (region == (*ri)) {
818                                 astate->regions.erase (ri);
819                                 astate->region_states.erase (rsi);
820                         }
821
822                         ri = ritmp;
823                         rsi = rsitmp;
824                 }
825                 
826                 s = tmp;
827         }
828
829         for (set<Crossfade*>::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) {
830                 delete *c;
831         }
832
833         if (changed) {
834                 /* overload this, it normally means "removed", not destroyed */
835                 notify_region_removed (region);
836         }
837
838         return changed;
839 }
840
841 void
842 AudioPlaylist::crossfade_changed (Change ignored)
843 {
844         if (in_flush || in_set_state) {
845                 return;
846         }
847
848         /* XXX is there a loop here? can an xfade change not happen
849            due to a playlist change? well, sure activation would
850            be an example. maybe we should check the type of change
851            that occured.
852         */
853
854         maybe_save_state (_("xfade change"));
855
856         notify_modified ();
857 }
858
859 bool
860 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
861 {
862         if (in_flush || in_set_state) {
863                 return false;
864         }
865
866         Change our_interests = Change (AudioRegion::FadeInChanged|
867                                        AudioRegion::FadeOutChanged|
868                                        AudioRegion::FadeInActiveChanged|
869                                        AudioRegion::FadeOutActiveChanged|
870                                        AudioRegion::EnvelopeActiveChanged|
871                                        AudioRegion::ScaleAmplitudeChanged|
872                                        AudioRegion::EnvelopeChanged);
873         bool parent_wants_notify;
874
875         parent_wants_notify = Playlist::region_changed (what_changed, region);
876
877         maybe_save_state (_("region modified"));
878
879         if ((parent_wants_notify || (what_changed & our_interests))) {
880                 notify_modified ();
881         }
882
883         return true; 
884 }
885
886 void
887 AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist)
888 {
889         RegionLock rlock (this);
890
891         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
892                 jack_nframes_t start, end;
893
894                 start = (*i)->position();
895                 end = start + (*i)->overlap_length(); // not length(), important difference
896
897                 if (frame >= start && frame <= end) {
898                         clist.push_back (*i);
899                 } 
900         }
901 }
902