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