remove recursive mutex from Playlist, replace with private regular mutex, force every...
[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                                         switch (_session.config.get_xfade_choice ()) {
332                                         case ConstantPowerMinus3dB:
333                                                 top->set_fade_in (FadeConstantPowerMinus3dB, len);
334                                                 break;
335                                         case ConstantPowerMinus6dB:
336                                                 top->set_fade_in (FadeConstantPowerMinus6dB, len);
337                                                 break;
338                                         case RegionFades:
339                                                 top->set_fade_in_length (len);
340                                                 break;
341                                         }
342                                         
343                                         done_start.insert (top);
344                                 }
345
346                         } else if (c == Evoral::OverlapEnd) {
347                                 
348                                 /* top covers start of bottom but ends within it */
349                                 
350                                 /* [---- top ------------------------] 
351                                  *                { ==== bottom ============ } 
352                                  */
353
354                                 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
355                                         /* Top's fade-out will cause an implicit fade-in of bottom */
356                                         
357                                         framecnt_t len = 0;
358                                         switch (_session.config.get_xfade_model()) {
359                                         case FullCrossfade:
360                                                 len = top->last_frame () - bottom->first_frame ();
361                                                 break;
362                                         case ShortCrossfade:
363                                                 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
364                                                 break;
365                                         }
366                                         
367                                         top->set_fade_out_active (true);
368                                         top->set_fade_out_is_xfade (true);
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
382                                         done_end.insert (top);
383                                 }
384                         }
385                 }
386         }
387
388         for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
389                 if (done_start.find (*i) == done_start.end()) {
390                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
391                         r->set_default_fade_in ();
392                 }
393         }
394
395         for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
396                 if (done_end.find (*i) == done_end.end()) {
397                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
398                         r->set_default_fade_out ();
399                 }
400         }
401 }
402
403 void
404 AudioPlaylist::dump () const
405 {
406         boost::shared_ptr<Region>r;
407
408         cerr << "Playlist \"" << _name << "\" " << endl
409              << regions.size() << " regions "
410              << endl;
411
412         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
413                 r = *i;
414                 cerr << "  " << r->name() << " @ " << r << " ["
415                      << r->start() << "+" << r->length()
416                      << "] at "
417                      << r->position()
418                      << " on layer "
419                      << r->layer ()
420                      << endl;
421         }
422 }
423
424 bool
425 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
426 {
427         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
428
429         if (!r) {
430                 return false;
431         }
432
433         bool changed = false;
434
435         {
436                 RegionLock rlock (this);
437
438                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
439
440                         RegionList::iterator tmp = i;
441                         ++tmp;
442
443                         if ((*i) == region) {
444                                 regions.erase (i);
445                                 changed = true;
446                         }
447
448                         i = tmp;
449                 }
450
451                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
452
453                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
454                         ++xtmp;
455
456                         if ((*x) == region) {
457                                 all_regions.erase (x);
458                                 changed = true;
459                         }
460
461                         x = xtmp;
462                 }
463
464                 region->set_playlist (boost::shared_ptr<Playlist>());
465         }
466
467         if (changed) {
468                 /* overload this, it normally means "removed", not destroyed */
469                 notify_region_removed (region);
470         }
471
472         return changed;
473 }
474
475 bool
476 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
477 {
478         if (in_flush || in_set_state) {
479                 return false;
480         }
481
482         PropertyChange our_interests;
483
484         our_interests.add (Properties::fade_in_active);
485         our_interests.add (Properties::fade_out_active);
486         our_interests.add (Properties::scale_amplitude);
487         our_interests.add (Properties::envelope_active);
488         our_interests.add (Properties::envelope);
489         our_interests.add (Properties::fade_in);
490         our_interests.add (Properties::fade_out);
491
492         bool parent_wants_notify;
493
494         parent_wants_notify = Playlist::region_changed (what_changed, region);
495
496         if (parent_wants_notify || (what_changed.contains (our_interests))) {
497                 notify_contents_changed ();
498         }
499
500         return true;
501 }
502
503 void
504 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
505 {
506         RegionSortByPosition cmp;
507         boost::shared_ptr<AudioRegion> ar;
508
509         sort (copies.begin(), copies.end(), cmp);
510
511         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
512
513         /* disable fade in of the first region */
514
515         if (ar) {
516                 ar->set_fade_in_active (false);
517         }
518
519         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
520
521         /* disable fade out of the last region */
522
523         if (ar) {
524                 ar->set_fade_out_active (false);
525         }
526 }
527
528 void
529 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
530 {
531         RegionSortByPosition cmp;
532         boost::shared_ptr<AudioRegion> ar;
533         boost::shared_ptr<AudioRegion> cr;
534
535         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
536                 return;
537         }
538
539         sort (originals.begin(), originals.end(), cmp);
540
541         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
542
543         /* copy the fade in of the first into the compound region */
544
545         if (ar) {
546                 cr->set_fade_in (ar->fade_in());
547         }
548
549         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
550
551         if (ar) {
552                 /* copy the fade out of the last into the compound region */
553                 cr->set_fade_out (ar->fade_out());
554         }
555 }
556
557 void
558 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
559 {
560         RegionSortByPosition cmp;
561         boost::shared_ptr<AudioRegion> ar;
562         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
563
564         if (!cr) {
565                 return;
566         }
567
568         sort (originals.begin(), originals.end(), cmp);
569
570         /* no need to call clear_changes() on the originals because that is
571          * done within Playlist::uncombine ()
572          */
573
574         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
575
576                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
577                         continue;
578                 }
579
580                 /* scale the uncombined regions by any gain setting for the
581                  * compound one.
582                  */
583
584                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
585
586                 if (i == originals.begin()) {
587
588                         /* copy the compound region's fade in back into the first
589                            original region.
590                         */
591
592                         if (cr->fade_in()->back()->when <= ar->length()) {
593                                 /* don't do this if the fade is longer than the
594                                  * region
595                                  */
596                                 ar->set_fade_in (cr->fade_in());
597                         }
598
599
600                 } else if (*i == originals.back()) {
601
602                         /* copy the compound region's fade out back into the last
603                            original region.
604                         */
605
606                         if (cr->fade_out()->back()->when <= ar->length()) {
607                                 /* don't do this if the fade is longer than the
608                                  * region
609                                  */
610                                 ar->set_fade_out (cr->fade_out());
611                         }
612
613                 }
614
615                 _session.add_command (new StatefulDiffCommand (*i));
616         }
617 }
618
619 int
620 AudioPlaylist::set_state (const XMLNode& node, int version)
621 {
622         int const r = Playlist::set_state (node, version);
623         if (r) {
624                 return r;
625         }
626
627         /* Read legacy Crossfade nodes and set up region fades accordingly */
628
629         XMLNodeList children = node.children ();
630         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
631                 if ((*i)->name() == X_("Crossfade")) {
632
633                         XMLProperty* p = (*i)->property (X_("active"));
634                         assert (p);
635                         if (!string_is_affirmative (p->value())) {
636                                 continue;
637                         }
638
639                         p = (*i)->property (X_("in"));
640                         assert (p);
641                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
642                         assert (in);
643                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
644                         assert (in_a);
645
646                         p = (*i)->property (X_("out"));
647                         assert (p);
648                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
649                         assert (out);
650                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
651                         assert (out_a);
652
653                         XMLNodeList c = (*i)->children ();
654                         for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
655                                 if ((*j)->name() == X_("FadeIn")) {
656                                         in_a->fade_in()->set_state (**j, version);
657                                         in_a->set_fade_in_active (true);
658                                 } else if ((*j)->name() == X_("FadeOut")) {
659                                         out_a->fade_out()->set_state (**j, version);
660                                         out_a->set_fade_out_active (true);
661                                 }
662                         }
663                 }
664         }
665
666         return 0;
667 }