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