Crossfades: default xfades are now constant-power, -3dB rule, other options are const...
[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 /** A segment of region that needs to be read */
153 struct Segment {
154         Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
155         
156         boost::shared_ptr<AudioRegion> region; ///< the region
157         Evoral::Range<framepos_t> range;       ///< range of the region to read, in session frames
158 };
159
160 /** @param start Start position in session frames.
161  *  @param cnt Number of frames to read.
162  */
163 ARDOUR::framecnt_t
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
165                      framecnt_t cnt, unsigned chan_n)
166 {
167         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
168                                                            name(), start, cnt, chan_n, regions.size()));
169
170         /* optimizing this memset() away involves a lot of conditionals
171            that may well cause more of a hit due to cache misses
172            and related stuff than just doing this here.
173
174            it would be great if someone could measure this
175            at some point.
176
177            one way or another, parts of the requested area
178            that are not written to by Region::region_at()
179            for all Regions that cover the area need to be
180            zeroed.
181         */
182
183         memset (buf, 0, sizeof (Sample) * cnt);
184
185         /* this function is never called from a realtime thread, so
186            its OK to block (for short intervals).
187         */
188
189 #ifdef HAVE_GLIB_THREADS_RECMUTEX
190         Glib::Threads::RecMutex::Lock lm (region_lock);
191 #else   
192         Glib::RecMutex::Lock rm (region_lock);
193 #endif  
194
195         /* Find all the regions that are involved in the bit we are reading,
196            and sort them by descending layer and ascending position.
197         */
198         boost::shared_ptr<RegionList> all = regions_touched (start, start + cnt - 1);
199         all->sort (ReadSorter ());
200
201         /* This will be a list of the bits of our read range that we have
202            handled completely (ie for which no more regions need to be read).
203            It is a list of ranges in session frames.
204         */
205         Evoral::RangeList<framepos_t> done;
206
207         /* This will be a list of the bits of regions that we need to read */
208         list<Segment> to_do;
209         
210         /* Now go through the `all' list filling in `to_do' and `done' */
211         for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
212                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
213
214                 /* Work out which bits of this region need to be read;
215                    first, trim to the range we are reading...
216                 */
217                 Evoral::Range<framepos_t> region_range = ar->range ();
218                 region_range.from = max (region_range.from, start);
219                 region_range.to = min (region_range.to, start + cnt - 1);
220
221                 /* ... and then remove the bits that are already done */
222                 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
223
224                 /* Read those bits, adding their bodies (the parts between end-of-fade-in
225                    and start-of-fade-out) to the `done' list.
226                 */
227
228                 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
229
230                 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
231                         Evoral::Range<framepos_t> d = *j;
232                         to_do.push_back (Segment (ar, d));
233
234                         if (ar->opaque ()) {
235                                 /* Cut this range down to just the body and mark it done */
236                                 Evoral::Range<framepos_t> body = ar->body_range ();
237                                 if (body.from < d.to && body.to > d.from) {
238                                         d.from = max (d.from, body.from);
239                                         d.to = min (d.to, body.to);
240                                         done.add (d);
241                                 }
242                         }
243                 }
244         }
245
246         /* Now go backwards through the to_do list doing the actual reads */
247         for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
248                 i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
249         }
250
251         return cnt;
252 }
253
254 void
255 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
256 {
257         if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
258                 return;
259         }
260         
261         boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
262         boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
263
264         RegionList all = *starts;
265         std::copy (ends->begin(), ends->end(), back_inserter (all));
266
267         all.sort (RegionSortByLayer ());
268
269         set<boost::shared_ptr<Region> > done_start;
270         set<boost::shared_ptr<Region> > done_end;
271
272         for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
273                 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
274
275                         if (i == j) {
276                                 continue;
277                         }
278
279                         if ((*i)->muted() || (*j)->muted()) {
280                                 continue;
281                         }
282
283                         if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
284                                 /* precise overlay: no xfade */
285                                 continue;
286                         }
287
288                         if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
289                                 /* starts or ends match: no xfade */
290                                 continue;
291                         }
292                         
293                         boost::shared_ptr<AudioRegion> top;
294                         boost::shared_ptr<AudioRegion> bottom;
295                 
296                         if ((*i)->layer() < (*j)->layer()) {
297                                 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
298                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
299                         } else {
300                                 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
301                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
302                         }
303                         
304                         if (!top->opaque ()) {
305                                 continue;
306                         }
307
308                         Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
309                         
310                         if (c == Evoral::OverlapStart) {
311                                 
312                                 /* top starts within bottom but covers bottom's end */
313                                 
314                                 /*                   { ==== top ============ } 
315                                  *   [---- bottom -------------------] 
316                                  */
317
318                                 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
319
320                                         /* Top's fade-in will cause an implicit fade-out of bottom */
321                                         
322                                         framecnt_t len = 0;
323                                         switch (_session.config.get_xfade_model()) {
324                                         case FullCrossfade:
325                                                 len = bottom->last_frame () - top->first_frame ();
326                                                 break;
327                                         case ShortCrossfade:
328                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
329                                                 break;
330                                         }
331                                         
332                                         switch (_session.config.get_xfade_choice ()) {
333                                         case ConstantPowerMinus3dB:
334                                                 top->set_fade_in (FadeConstantPowerMinus3dB, len);
335                                                 break;
336                                         case ConstantPowerMinus6dB:
337                                                 top->set_fade_in (FadeConstantPowerMinus6dB, len);
338                                                 break;
339                                         case RegionFades:
340                                                 top->set_fade_in_length (len);
341                                                 break;
342                                         }
343                                         top->set_fade_in_active (true);
344                                         top->set_fade_in_is_xfade (true);
345                                         
346                                         done_start.insert (top);
347                                 }
348
349                         } else if (c == Evoral::OverlapEnd) {
350                                 
351                                 /* top covers start of bottom but ends within it */
352                                 
353                                 /* [---- top ------------------------] 
354                                  *                { ==== bottom ============ } 
355                                  */
356
357                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
358                                         /* Top's fade-out will cause an implicit fade-in of bottom */
359                                         
360                                         framecnt_t len = 0;
361                                         switch (_session.config.get_xfade_model()) {
362                                         case FullCrossfade:
363                                                 len = top->last_frame () - bottom->first_frame ();
364                                                 break;
365                                         case ShortCrossfade:
366                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
367                                                 break;
368                                         }
369                                         
370                                         switch (_session.config.get_xfade_choice ()) {
371                                         case ConstantPowerMinus3dB:
372                                                 top->set_fade_out (FadeConstantPowerMinus3dB, len);
373                                                 break;
374                                         case ConstantPowerMinus6dB:
375                                                 top->set_fade_out (FadeConstantPowerMinus6dB, len);
376                                                 break;
377                                         case RegionFades:
378                                                 top->set_fade_out_length (len);
379                                                 break;
380                                         }
381                                         top->set_fade_out_active (true);
382                                         top->set_fade_out_is_xfade (true);
383
384                                         done_end.insert (top);
385                                 }
386                         }
387                 }
388         }
389
390         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
391                 if (done_start.find (*i) == done_start.end()) {
392                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
393                         r->set_default_fade_in ();
394                 }
395         }
396
397         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
398                 if (done_end.find (*i) == done_end.end()) {
399                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
400                         r->set_default_fade_out ();
401                 }
402         }
403 }
404
405 void
406 AudioPlaylist::dump () const
407 {
408         boost::shared_ptr<Region>r;
409
410         cerr << "Playlist \"" << _name << "\" " << endl
411              << regions.size() << " regions "
412              << endl;
413
414         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
415                 r = *i;
416                 cerr << "  " << r->name() << " @ " << r << " ["
417                      << r->start() << "+" << r->length()
418                      << "] at "
419                      << r->position()
420                      << " on layer "
421                      << r->layer ()
422                      << endl;
423         }
424 }
425
426 bool
427 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
428 {
429         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
430
431         if (!r) {
432                 return false;
433         }
434
435         bool changed = false;
436
437         {
438                 RegionLock rlock (this);
439
440                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
441
442                         RegionList::iterator tmp = i;
443                         ++tmp;
444
445                         if ((*i) == region) {
446                                 regions.erase (i);
447                                 changed = true;
448                         }
449
450                         i = tmp;
451                 }
452
453                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
454
455                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
456                         ++xtmp;
457
458                         if ((*x) == region) {
459                                 all_regions.erase (x);
460                                 changed = true;
461                         }
462
463                         x = xtmp;
464                 }
465
466                 region->set_playlist (boost::shared_ptr<Playlist>());
467         }
468
469         if (changed) {
470                 /* overload this, it normally means "removed", not destroyed */
471                 notify_region_removed (region);
472         }
473
474         return changed;
475 }
476
477 bool
478 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
479 {
480         if (in_flush || in_set_state) {
481                 return false;
482         }
483
484         PropertyChange our_interests;
485
486         our_interests.add (Properties::fade_in_active);
487         our_interests.add (Properties::fade_out_active);
488         our_interests.add (Properties::scale_amplitude);
489         our_interests.add (Properties::envelope_active);
490         our_interests.add (Properties::envelope);
491         our_interests.add (Properties::fade_in);
492         our_interests.add (Properties::fade_out);
493
494         bool parent_wants_notify;
495
496         parent_wants_notify = Playlist::region_changed (what_changed, region);
497
498         if (parent_wants_notify || (what_changed.contains (our_interests))) {
499                 notify_contents_changed ();
500         }
501
502         return true;
503 }
504
505 void
506 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
507 {
508         RegionSortByPosition cmp;
509         boost::shared_ptr<AudioRegion> ar;
510
511         sort (copies.begin(), copies.end(), cmp);
512
513         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
514
515         /* disable fade in of the first region */
516
517         if (ar) {
518                 ar->set_fade_in_active (false);
519         }
520
521         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
522
523         /* disable fade out of the last region */
524
525         if (ar) {
526                 ar->set_fade_out_active (false);
527         }
528 }
529
530 void
531 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
532 {
533         RegionSortByPosition cmp;
534         boost::shared_ptr<AudioRegion> ar;
535         boost::shared_ptr<AudioRegion> cr;
536
537         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
538                 return;
539         }
540
541         sort (originals.begin(), originals.end(), cmp);
542
543         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
544
545         /* copy the fade in of the first into the compound region */
546
547         if (ar) {
548                 cr->set_fade_in (ar->fade_in());
549         }
550
551         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
552
553         if (ar) {
554                 /* copy the fade out of the last into the compound region */
555                 cr->set_fade_out (ar->fade_out());
556         }
557 }
558
559 void
560 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
561 {
562         RegionSortByPosition cmp;
563         boost::shared_ptr<AudioRegion> ar;
564         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
565
566         if (!cr) {
567                 return;
568         }
569
570         sort (originals.begin(), originals.end(), cmp);
571
572         /* no need to call clear_changes() on the originals because that is
573          * done within Playlist::uncombine ()
574          */
575
576         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
577
578                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
579                         continue;
580                 }
581
582                 /* scale the uncombined regions by any gain setting for the
583                  * compound one.
584                  */
585
586                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
587
588                 if (i == originals.begin()) {
589
590                         /* copy the compound region's fade in back into the first
591                            original region.
592                         */
593
594                         if (cr->fade_in()->back()->when <= ar->length()) {
595                                 /* don't do this if the fade is longer than the
596                                  * region
597                                  */
598                                 ar->set_fade_in (cr->fade_in());
599                         }
600
601
602                 } else if (*i == originals.back()) {
603
604                         /* copy the compound region's fade out back into the last
605                            original region.
606                         */
607
608                         if (cr->fade_out()->back()->when <= ar->length()) {
609                                 /* don't do this if the fade is longer than the
610                                  * region
611                                  */
612                                 ar->set_fade_out (cr->fade_out());
613                         }
614
615                 }
616
617                 _session.add_command (new StatefulDiffCommand (*i));
618         }
619 }
620
621 int
622 AudioPlaylist::set_state (const XMLNode& node, int version)
623 {
624         int const r = Playlist::set_state (node, version);
625         if (r) {
626                 return r;
627         }
628
629         /* Read legacy Crossfade nodes and set up region fades accordingly */
630
631         XMLNodeList children = node.children ();
632         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
633                 if ((*i)->name() == X_("Crossfade")) {
634
635                         XMLProperty* p = (*i)->property (X_("active"));
636                         assert (p);
637                         if (!string_is_affirmative (p->value())) {
638                                 continue;
639                         }
640
641                         p = (*i)->property (X_("in"));
642                         assert (p);
643                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
644                         assert (in);
645                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
646                         assert (in_a);
647
648                         p = (*i)->property (X_("out"));
649                         assert (p);
650                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
651                         assert (out);
652                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
653                         assert (out_a);
654
655                         XMLNodeList c = (*i)->children ();
656                         for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
657                                 if ((*j)->name() == X_("FadeIn")) {
658                                         in_a->fade_in()->set_state (**j, version);
659                                         in_a->set_fade_in_active (true);
660                                 } else if ((*j)->name() == X_("FadeOut")) {
661                                         out_a->fade_out()->set_state (**j, version);
662                                         out_a->set_fade_out_active (true);
663                                 }
664                         }
665                 }
666         }
667
668         return 0;
669 }