Re-work fade operation to be closer to Mixbus; things
[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         Glib::RecMutex::Lock rm (region_lock);
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 (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
290                         boost::shared_ptr<AudioRegion> top;
291                         boost::shared_ptr<AudioRegion> bottom;
292                 
293                         if ((*i)->layer() < (*j)->layer()) {
294                                 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
295                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
296                         } else {
297                                 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
298                                 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
299                         }
300                         
301                         if (!top->opaque ()) {
302                                 continue;
303                         }
304
305                         Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
306                         
307                         if (c == Evoral::OverlapStart) {
308                                 
309                                 /* top starts within bottom but covers bottom's end */
310                                 
311                                 /*                   { ==== top ============ } 
312                                  *   [---- bottom -------------------] 
313                                  */
314
315                                 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
316                                         framecnt_t const len = bottom->last_frame () - top->first_frame ();
317                                         top->set_fade_in_length (len);
318                                         top->set_fade_in_active (true);
319                                         done_start.insert (top);
320                                         bottom->set_fade_out_length (len);
321                                         bottom->set_fade_out_active (true);
322                                         done_end.insert (bottom);
323                                 }
324
325                         } else if (c == Evoral::OverlapEnd) {
326                                 
327                                 /* top covers start of bottom but ends within it */
328                                 
329                                 /* [---- top ------------------------] 
330                                  *                { ==== bottom ============ } 
331                                  */
332
333                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
334                                         framecnt_t const len = top->last_frame () - bottom->first_frame ();
335                                         top->set_fade_out_length (len);
336                                         top->set_fade_out_active (true);
337                                         done_end.insert (top);
338                                         bottom->set_fade_in_length (len);
339                                         bottom->set_fade_in_active (true);
340                                         done_start.insert (bottom);
341                                 }
342                         }
343                 }
344         }
345
346         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
347                 if (done_start.find (*i) == done_start.end()) {
348                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
349                         r->set_default_fade_in ();
350                 }
351         }
352
353         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
354                 if (done_end.find (*i) == done_end.end()) {
355                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
356                         r->set_default_fade_out ();
357                 }
358         }
359 }
360
361 void
362 AudioPlaylist::dump () const
363 {
364         boost::shared_ptr<Region>r;
365
366         cerr << "Playlist \"" << _name << "\" " << endl
367              << regions.size() << " regions "
368              << endl;
369
370         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
371                 r = *i;
372                 cerr << "  " << r->name() << " @ " << r << " ["
373                      << r->start() << "+" << r->length()
374                      << "] at "
375                      << r->position()
376                      << " on layer "
377                      << r->layer ()
378                      << endl;
379         }
380 }
381
382 bool
383 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
384 {
385         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
386
387         if (!r) {
388                 return false;
389         }
390
391         bool changed = false;
392
393         {
394                 RegionLock rlock (this);
395
396                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
397
398                         RegionList::iterator tmp = i;
399                         ++tmp;
400
401                         if ((*i) == region) {
402                                 regions.erase (i);
403                                 changed = true;
404                         }
405
406                         i = tmp;
407                 }
408
409                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
410
411                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
412                         ++xtmp;
413
414                         if ((*x) == region) {
415                                 all_regions.erase (x);
416                                 changed = true;
417                         }
418
419                         x = xtmp;
420                 }
421
422                 region->set_playlist (boost::shared_ptr<Playlist>());
423         }
424
425         if (changed) {
426                 /* overload this, it normally means "removed", not destroyed */
427                 notify_region_removed (region);
428         }
429
430         return changed;
431 }
432
433 bool
434 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
435 {
436         if (in_flush || in_set_state) {
437                 return false;
438         }
439
440         PropertyChange our_interests;
441
442         our_interests.add (Properties::fade_in_active);
443         our_interests.add (Properties::fade_out_active);
444         our_interests.add (Properties::scale_amplitude);
445         our_interests.add (Properties::envelope_active);
446         our_interests.add (Properties::envelope);
447         our_interests.add (Properties::fade_in);
448         our_interests.add (Properties::fade_out);
449
450         bool parent_wants_notify;
451
452         parent_wants_notify = Playlist::region_changed (what_changed, region);
453
454         if (parent_wants_notify || (what_changed.contains (our_interests))) {
455                 notify_contents_changed ();
456         }
457
458         return true;
459 }
460
461 void
462 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
463 {
464         RegionSortByPosition cmp;
465         boost::shared_ptr<AudioRegion> ar;
466
467         sort (copies.begin(), copies.end(), cmp);
468
469         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
470
471         /* disable fade in of the first region */
472
473         if (ar) {
474                 ar->set_fade_in_active (false);
475         }
476
477         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
478
479         /* disable fade out of the last region */
480
481         if (ar) {
482                 ar->set_fade_out_active (false);
483         }
484 }
485
486 void
487 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
488 {
489         RegionSortByPosition cmp;
490         boost::shared_ptr<AudioRegion> ar;
491         boost::shared_ptr<AudioRegion> cr;
492
493         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
494                 return;
495         }
496
497         sort (originals.begin(), originals.end(), cmp);
498
499         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
500
501         /* copy the fade in of the first into the compound region */
502
503         if (ar) {
504                 cr->set_fade_in (ar->fade_in());
505         }
506
507         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
508
509         if (ar) {
510                 /* copy the fade out of the last into the compound region */
511                 cr->set_fade_out (ar->fade_out());
512         }
513 }
514
515 void
516 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
517 {
518         RegionSortByPosition cmp;
519         boost::shared_ptr<AudioRegion> ar;
520         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
521
522         if (!cr) {
523                 return;
524         }
525
526         sort (originals.begin(), originals.end(), cmp);
527
528         /* no need to call clear_changes() on the originals because that is
529          * done within Playlist::uncombine ()
530          */
531
532         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
533
534                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
535                         continue;
536                 }
537
538                 /* scale the uncombined regions by any gain setting for the
539                  * compound one.
540                  */
541
542                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
543
544                 if (i == originals.begin()) {
545
546                         /* copy the compound region's fade in back into the first
547                            original region.
548                         */
549
550                         if (cr->fade_in()->back()->when <= ar->length()) {
551                                 /* don't do this if the fade is longer than the
552                                  * region
553                                  */
554                                 ar->set_fade_in (cr->fade_in());
555                         }
556
557
558                 } else if (*i == originals.back()) {
559
560                         /* copy the compound region's fade out back into the last
561                            original region.
562                         */
563
564                         if (cr->fade_out()->back()->when <= ar->length()) {
565                                 /* don't do this if the fade is longer than the
566                                  * region
567                                  */
568                                 ar->set_fade_out (cr->fade_out());
569                         }
570
571                 }
572
573                 _session.add_command (new StatefulDiffCommand (*i));
574         }
575 }
576
577 int
578 AudioPlaylist::set_state (const XMLNode& node, int version)
579 {
580         int const r = Playlist::set_state (node, version);
581         if (r) {
582                 return r;
583         }
584
585         /* Read legacy Crossfade nodes and set up region fades accordingly */
586
587         XMLNodeList children = node.children ();
588         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
589                 if ((*i)->name() == X_("Crossfade")) {
590
591                         XMLProperty* p = (*i)->property (X_("active"));
592                         assert (p);
593                         if (!string_is_affirmative (p->value())) {
594                                 continue;
595                         }
596
597                         p = (*i)->property (X_("in"));
598                         assert (p);
599                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
600                         assert (in);
601                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
602                         assert (in_a);
603
604                         p = (*i)->property (X_("out"));
605                         assert (p);
606                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
607                         assert (out);
608                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
609                         assert (out_a);
610
611                         XMLNodeList c = (*i)->children ();
612                         for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
613                                 if ((*j)->name() == X_("FadeIn")) {
614                                         in_a->fade_in()->set_state (**j, version);
615                                         in_a->set_fade_in_active (true);
616                                 } else if ((*j)->name() == X_("FadeOut")) {
617                                         out_a->fade_out()->set_state (**j, version);
618                                         out_a->set_fade_out_active (true);
619                                 }
620                         }
621                 }
622         }
623
624         return 0;
625 }