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