Fairly major change to the way in which crossfades are handled;
[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/region_sorters.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
32
33 #include "i18n.h"
34
35 using namespace ARDOUR;
36 using namespace std;
37 using namespace PBD;
38
39 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
40         : Playlist (session, node, DataType::AUDIO, hidden)
41 {
42 #ifndef NDEBUG
43         const XMLProperty* prop = node.property("type");
44         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
45 #endif
46
47         in_set_state++;
48         if (set_state (node, Stateful::loading_state_version)) {
49                 throw failed_constructor();
50         }
51         in_set_state--;
52
53         relayer ();
54 }
55
56 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
57         : Playlist (session, name, DataType::AUDIO, hidden)
58 {
59 }
60
61 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
62         : Playlist (other, name, hidden)
63 {
64 }
65
66 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
67         : Playlist (other, start, cnt, name, hidden)
68 {
69         RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70         in_set_state++;
71
72         framepos_t const end = start + cnt - 1;
73
74         /* Audio regions that have been created by the Playlist constructor
75            will currently have the same fade in/out as the regions that they
76            were created from.  This is wrong, so reset the fades here.
77         */
78
79         RegionList::iterator ours = regions.begin ();
80
81         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
82                 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
83                 assert (region);
84
85                 framecnt_t fade_in = 64;
86                 framecnt_t fade_out = 64;
87
88                 switch (region->coverage (start, end)) {
89                 case Evoral::OverlapNone:
90                         continue;
91
92                 case Evoral::OverlapInternal:
93                 {
94                         framecnt_t const offset = start - region->position ();
95                         framecnt_t const trim = region->last_frame() - end;
96                         if (region->fade_in()->back()->when > offset) {
97                                 fade_in = region->fade_in()->back()->when - offset;
98                         }
99                         if (region->fade_out()->back()->when > trim) {
100                                 fade_out = region->fade_out()->back()->when - trim;
101                         }
102                         break;
103                 }
104
105                 case Evoral::OverlapStart: {
106                         if (end > region->position() + region->fade_in()->back()->when)
107                                 fade_in = region->fade_in()->back()->when;  //end is after fade-in, preserve the fade-in
108                         if (end > region->last_frame() - region->fade_out()->back()->when)
109                                 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end );  //end is inside the fadeout, preserve the fades endpoint
110                         break;
111                 }
112
113                 case Evoral::OverlapEnd: {
114                         if (start < region->last_frame() - region->fade_out()->back()->when)  //start is before fade-out, preserve the fadeout
115                                 fade_out = region->fade_out()->back()->when;
116
117                         if (start < region->position() + region->fade_in()->back()->when)
118                                 fade_in = region->fade_in()->back()->when - (start - region->position());  //end is inside the fade-in, preserve the fade-in endpoint
119                         break;
120                 }
121
122                 case Evoral::OverlapExternal:
123                         fade_in = region->fade_in()->back()->when;
124                         fade_out = region->fade_out()->back()->when;
125                         break;
126                 }
127
128                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
129                 assert (our_region);
130
131                 our_region->set_fade_in_length (fade_in);
132                 our_region->set_fade_out_length (fade_out);
133                 ++ours;
134         }
135
136         in_set_state--;
137
138         /* this constructor does NOT notify others (session) */
139 }
140
141 /** Sort by descending layer and then by ascending position */
142 struct ReadSorter {
143     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
144             if (a->layer() != b->layer()) {
145                     return a->layer() > b->layer();
146             }
147
148             return a->position() < b->position();
149     }
150 };
151
152 ARDOUR::framecnt_t
153 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
154                      framecnt_t cnt, unsigned chan_n)
155 {
156         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
157                                                            name(), start, cnt, chan_n, regions.size()));
158
159         /* optimizing this memset() away involves a lot of conditionals
160            that may well cause more of a hit due to cache misses
161            and related stuff than just doing this here.
162
163            it would be great if someone could measure this
164            at some point.
165
166            one way or another, parts of the requested area
167            that are not written to by Region::region_at()
168            for all Regions that cover the area need to be
169            zeroed.
170         */
171
172         memset (buf, 0, sizeof (Sample) * cnt);
173
174         /* this function is never called from a realtime thread, so
175            its OK to block (for short intervals).
176         */
177
178         Glib::RecMutex::Lock rm (region_lock);
179
180         /* Find all the regions that are involved in the bit we are reading,
181            and sort them by descending layer and ascending position.
182         */
183         boost::shared_ptr<RegionList> all = regions_touched (start, start + cnt - 1);
184         all->sort (ReadSorter ());
185
186         /* This will be a list of the bits of our read range that we have
187            read completely (ie for which no more regions need to be read).
188            It is a list of ranges in session frames.
189         */
190         Evoral::RangeList<framepos_t> done;
191         
192         for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
193                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
194
195                 /* Trim region range to the bit we are reading */
196
197                 /* Work out which bits of this region need to be read;
198                    first, trim to the range we are reading...
199                 */
200                 Evoral::Range<framepos_t> region_range = ar->range ();
201                 region_range.from = max (region_range.from, start);
202                 region_range.to = min (region_range.to, start + cnt - 1);
203
204                 /* ... and then remove the bits that are already done */
205                 Evoral::RangeList<framepos_t> to_do = Evoral::subtract (region_range, done);
206
207                 /* Read those bits, adding their bodies (the parts between end-of-fade-in
208                    and start-of-fade-out) to the `done' list.
209                 */
210
211                 Evoral::RangeList<framepos_t>::List t = to_do.get ();
212
213                 for (Evoral::RangeList<framepos_t>::List::iterator i = t.begin(); i != t.end(); ++i) {
214                         Evoral::Range<framepos_t> d = *i;
215
216                         /* Read the whole range, possibly including fades */
217                         ar->read_at (buf + d.from - start, mixdown_buffer, gain_buffer, d.from, d.to - d.from + 1, chan_n);
218
219                         if (ar->opaque ()) {
220                                 /* Cut this range down to just the body and mark it done */
221                                 Evoral::Range<framepos_t> body = ar->body_range ();
222                                 if (body.from < d.to && body.to > d.from) {
223                                         d.from = max (d.from, body.from);
224                                         d.to = min (d.to, body.to);
225                                         done.add (d);
226                                 }
227                         }
228                 }
229         }
230
231         return cnt;
232 }
233
234 void
235 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
236 {
237         if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
238                 return;
239         }
240         
241         boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
242         boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
243
244         RegionList all = *starts;
245         std::copy (ends->begin(), ends->end(), back_inserter (all));
246
247         all.sort (RegionSortByLayer ());
248
249         set<boost::shared_ptr<Region> > done_start;
250         set<boost::shared_ptr<Region> > done_end;
251
252         for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
253                 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
254
255                         if (i == j) {
256                                 continue;
257                         }
258
259                         if ((*i)->muted() || (*j)->muted()) {
260                                 continue;
261                         }
262
263                         if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
264                                 /* precise overlay: no xfade */
265                                 continue;
266                         }
267
268                         if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
269                                 /* starts or ends match: no xfade */
270                                 continue;
271                         }
272                         
273
274                         boost::shared_ptr<AudioRegion> top;
275                         boost::shared_ptr<AudioRegion> bottom;
276                 
277                         if ((*i)->layer() < (*j)->layer()) {
278                                 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
279                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
280                         } else {
281                                 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
282                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
283                         }
284                         
285                         if (!top->opaque ()) {
286                                 continue;
287                         }
288
289                         Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
290                         
291                         if (c == Evoral::OverlapStart) {
292                                 
293                                 /* top starts within bottom but covers bottom's end */
294                                 
295                                 /*                   { ==== top ============ } 
296                                  *   [---- bottom -------------------] 
297                                  */
298
299                                 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
300                                         framecnt_t const len = bottom->last_frame () - top->first_frame ();
301                                         top->set_fade_in_length (len);
302                                         top->set_fade_in_active (true);
303                                         done_start.insert (top);
304                                         bottom->set_fade_out_length (len);
305                                         bottom->set_fade_out_active (true);
306                                         done_end.insert (bottom);
307                                 }
308
309                         } else if (c == Evoral::OverlapEnd) {
310                                 
311                                 /* top covers start of bottom but ends within it */
312                                 
313                                 /* [---- top ------------------------] 
314                                  *                { ==== bottom ============ } 
315                                  */
316
317                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
318                                         framecnt_t const len = top->last_frame () - bottom->first_frame ();
319                                         top->set_fade_out_length (len);
320                                         top->set_fade_out_active (true);
321                                         done_end.insert (top);
322                                         bottom->set_fade_in_length (len);
323                                         bottom->set_fade_in_active (true);
324                                         done_start.insert (bottom);
325                                 }
326                         }
327                 }
328         }
329
330         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
331                 if (done_start.find (*i) == done_start.end()) {
332                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
333                         r->set_default_fade_in ();
334                 }
335         }
336
337         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
338                 if (done_end.find (*i) == done_end.end()) {
339                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
340                         r->set_default_fade_out ();
341                 }
342         }
343 }
344
345 void
346 AudioPlaylist::dump () const
347 {
348         boost::shared_ptr<Region>r;
349
350         cerr << "Playlist \"" << _name << "\" " << endl
351              << regions.size() << " regions "
352              << endl;
353
354         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
355                 r = *i;
356                 cerr << "  " << r->name() << " @ " << r << " ["
357                      << r->start() << "+" << r->length()
358                      << "] at "
359                      << r->position()
360                      << " on layer "
361                      << r->layer ()
362                      << endl;
363         }
364 }
365
366 bool
367 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
368 {
369         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
370
371         if (!r) {
372                 return false;
373         }
374
375         bool changed = false;
376
377         {
378                 RegionLock rlock (this);
379
380                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
381
382                         RegionList::iterator tmp = i;
383                         ++tmp;
384
385                         if ((*i) == region) {
386                                 regions.erase (i);
387                                 changed = true;
388                         }
389
390                         i = tmp;
391                 }
392
393                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
394
395                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
396                         ++xtmp;
397
398                         if ((*x) == region) {
399                                 all_regions.erase (x);
400                                 changed = true;
401                         }
402
403                         x = xtmp;
404                 }
405
406                 region->set_playlist (boost::shared_ptr<Playlist>());
407         }
408
409         if (changed) {
410                 /* overload this, it normally means "removed", not destroyed */
411                 notify_region_removed (region);
412         }
413
414         return changed;
415 }
416
417 bool
418 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
419 {
420         if (in_flush || in_set_state) {
421                 return false;
422         }
423
424         PropertyChange our_interests;
425
426         our_interests.add (Properties::fade_in_active);
427         our_interests.add (Properties::fade_out_active);
428         our_interests.add (Properties::scale_amplitude);
429         our_interests.add (Properties::envelope_active);
430         our_interests.add (Properties::envelope);
431         our_interests.add (Properties::fade_in);
432         our_interests.add (Properties::fade_out);
433
434         bool parent_wants_notify;
435
436         parent_wants_notify = Playlist::region_changed (what_changed, region);
437
438         if (parent_wants_notify || (what_changed.contains (our_interests))) {
439                 notify_contents_changed ();
440         }
441
442         return true;
443 }
444
445 void
446 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
447 {
448         RegionSortByPosition cmp;
449         boost::shared_ptr<AudioRegion> ar;
450
451         sort (copies.begin(), copies.end(), cmp);
452
453         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
454
455         /* disable fade in of the first region */
456
457         if (ar) {
458                 ar->set_fade_in_active (false);
459         }
460
461         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
462
463         /* disable fade out of the last region */
464
465         if (ar) {
466                 ar->set_fade_out_active (false);
467         }
468 }
469
470 void
471 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
472 {
473         RegionSortByPosition cmp;
474         boost::shared_ptr<AudioRegion> ar;
475         boost::shared_ptr<AudioRegion> cr;
476
477         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
478                 return;
479         }
480
481         sort (originals.begin(), originals.end(), cmp);
482
483         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
484
485         /* copy the fade in of the first into the compound region */
486
487         if (ar) {
488                 cr->set_fade_in (ar->fade_in());
489         }
490
491         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
492
493         if (ar) {
494                 /* copy the fade out of the last into the compound region */
495                 cr->set_fade_out (ar->fade_out());
496         }
497 }
498
499 void
500 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
501 {
502         RegionSortByPosition cmp;
503         boost::shared_ptr<AudioRegion> ar;
504         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
505
506         if (!cr) {
507                 return;
508         }
509
510         sort (originals.begin(), originals.end(), cmp);
511
512         /* no need to call clear_changes() on the originals because that is
513          * done within Playlist::uncombine ()
514          */
515
516         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
517
518                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
519                         continue;
520                 }
521
522                 /* scale the uncombined regions by any gain setting for the
523                  * compound one.
524                  */
525
526                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
527
528                 if (i == originals.begin()) {
529
530                         /* copy the compound region's fade in back into the first
531                            original region.
532                         */
533
534                         if (cr->fade_in()->back()->when <= ar->length()) {
535                                 /* don't do this if the fade is longer than the
536                                  * region
537                                  */
538                                 ar->set_fade_in (cr->fade_in());
539                         }
540
541
542                 } else if (*i == originals.back()) {
543
544                         /* copy the compound region's fade out back into the last
545                            original region.
546                         */
547
548                         if (cr->fade_out()->back()->when <= ar->length()) {
549                                 /* don't do this if the fade is longer than the
550                                  * region
551                                  */
552                                 ar->set_fade_out (cr->fade_out());
553                         }
554
555                 }
556
557                 _session.add_command (new StatefulDiffCommand (*i));
558         }
559 }
560
561 int
562 AudioPlaylist::set_state (const XMLNode& node, int version)
563 {
564         int const r = Playlist::set_state (node, version);
565         if (r) {
566                 return r;
567         }
568
569         /* Read legacy Crossfade nodes and set up region fades accordingly */
570
571         XMLNodeList children = node.children ();
572         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
573                 if ((*i)->name() == X_("Crossfade")) {
574
575                         XMLProperty* p = (*i)->property (X_("active"));
576                         assert (p);
577                         if (!string_is_affirmative (p->value())) {
578                                 continue;
579                         }
580
581                         p = (*i)->property (X_("in"));
582                         assert (p);
583                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
584                         assert (in);
585                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
586                         assert (in_a);
587
588                         p = (*i)->property (X_("out"));
589                         assert (p);
590                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
591                         assert (out);
592                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
593                         assert (out_a);
594
595                         XMLNodeList c = (*i)->children ();
596                         for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
597                                 if ((*j)->name() == X_("FadeIn")) {
598                                         in_a->fade_in()->set_state (**j, version);
599                                         in_a->set_fade_in_active (true);
600                                 } else if ((*j)->name() == X_("FadeOut")) {
601                                         out_a->fade_out()->set_state (**j, version);
602                                         out_a->set_fade_out_active (true);
603                                 }
604                         }
605                 }
606         }
607
608         return 0;
609 }