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