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