Initial backend support for external export encoder
[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 "pbd/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         XMLProperty const * 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         load_legacy_crossfades (node, Stateful::loading_state_version);
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, samplepos_t start, samplecnt_t cnt, string name, bool hidden)
67         : Playlist (other, start, cnt, name, hidden)
68 {
69         RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70         in_set_state++;
71
72         samplepos_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                 samplecnt_t fade_in = 64;
86                 samplecnt_t fade_out = 64;
87
88                 switch (region->coverage (start, end)) {
89                 case Evoral::OverlapNone:
90                         continue;
91
92                 case Evoral::OverlapInternal:
93                 {
94                         samplecnt_t const offset = start - region->position ();
95                         samplecnt_t const trim = region->last_sample() - 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_sample() - region->fade_out()->back()->when)
109                                 fade_out = region->fade_out()->back()->when - ( region->last_sample() - end );  //end is inside the fadeout, preserve the fades endpoint
110                         break;
111                 }
112
113                 case Evoral::OverlapEnd: {
114                         if (start < region->last_sample() - 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<samplepos_t> a) : region (r), range (a) {}
155
156         boost::shared_ptr<AudioRegion> region; ///< the region
157         Evoral::Range<samplepos_t> range;       ///< range of the region to read, in session samples
158 };
159
160 /** @param start Start position in session samples.
161  *  @param cnt Number of samples to read.
162  */
163 ARDOUR::samplecnt_t
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, samplepos_t start,
165                      samplecnt_t cnt, unsigned chan_n)
166 {
167         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
168                                                            name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
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::RegionReadLock rl (this);
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 samples.
200         */
201         Evoral::RangeList<samplepos_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                 /* muted regions don't figure into it at all */
211                 if ( ar->muted() )
212                         continue;
213
214                 /* check for the case of solo_selection */
215                 bool force_transparent = ( _session.solo_selection_active() && SoloSelectedActive() && !SoloSelectedListIncludes( (const Region*) &(**i) ) );
216                 if ( force_transparent )
217                         continue;
218
219                 /* Work out which bits of this region need to be read;
220                    first, trim to the range we are reading...
221                 */
222                 Evoral::Range<samplepos_t> region_range = ar->range ();
223                 region_range.from = max (region_range.from, start);
224                 region_range.to = min (region_range.to, start + cnt - 1);
225
226                 /* ... and then remove the bits that are already done */
227
228                 Evoral::RangeList<samplepos_t> region_to_do = Evoral::subtract (region_range, done);
229
230                 /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
231                    and start-of-fade-out) to the `done' list.
232                 */
233
234                 Evoral::RangeList<samplepos_t>::List t = region_to_do.get ();
235
236                 for (Evoral::RangeList<samplepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
237                         Evoral::Range<samplepos_t> d = *j;
238                         to_do.push_back (Segment (ar, d));
239
240                         if (ar->opaque ()) {
241                                 /* Cut this range down to just the body and mark it done */
242                                 Evoral::Range<samplepos_t> body = ar->body_range ();
243                                 if (body.from < d.to && body.to > d.from) {
244                                         d.from = max (d.from, body.from);
245                                         d.to = min (d.to, body.to);
246                                         done.add (d);
247                                 }
248                         }
249                 }
250         }
251
252         /* Now go backwards through the to_do list doing the actual reads */
253         for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
254                 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
255                                                                    name(), i->region->name(), i->range.from,
256                                                                    i->range.to - i->range.from + 1, (int) chan_n,
257                                                                    buf, i->range.from - start));
258                 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);
259         }
260
261         return cnt;
262 }
263
264 void
265 AudioPlaylist::dump () const
266 {
267         boost::shared_ptr<Region>r;
268
269         cerr << "Playlist \"" << _name << "\" " << endl
270              << regions.size() << " regions "
271              << endl;
272
273         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
274                 r = *i;
275                 cerr << "  " << r->name() << " @ " << r << " ["
276                      << r->start() << "+" << r->length()
277                      << "] at "
278                      << r->position()
279                      << " on layer "
280                      << r->layer ()
281                      << endl;
282         }
283 }
284
285 bool
286 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
287 {
288         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
289
290         if (!r) {
291                 return false;
292         }
293
294         bool changed = false;
295
296         {
297                 RegionWriteLock rlock (this);
298
299                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
300
301                         RegionList::iterator tmp = i;
302                         ++tmp;
303
304                         if ((*i) == region) {
305                                 regions.erase (i);
306                                 changed = true;
307                         }
308
309                         i = tmp;
310                 }
311
312                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
313
314                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
315                         ++xtmp;
316
317                         if ((*x) == region) {
318                                 all_regions.erase (x);
319                                 changed = true;
320                         }
321
322                         x = xtmp;
323                 }
324
325                 region->set_playlist (boost::shared_ptr<Playlist>());
326         }
327
328         if (changed) {
329                 /* overload this, it normally means "removed", not destroyed */
330                 notify_region_removed (region);
331         }
332
333         return changed;
334 }
335
336 bool
337 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
338 {
339         if (in_flush || in_set_state) {
340                 return false;
341         }
342
343         PropertyChange bounds;
344         bounds.add (Properties::start);
345         bounds.add (Properties::position);
346         bounds.add (Properties::length);
347
348         PropertyChange our_interests;
349
350         our_interests.add (Properties::fade_in_active);
351         our_interests.add (Properties::fade_out_active);
352         our_interests.add (Properties::scale_amplitude);
353         our_interests.add (Properties::envelope_active);
354         our_interests.add (Properties::envelope);
355         our_interests.add (Properties::fade_in);
356         our_interests.add (Properties::fade_out);
357
358         bool parent_wants_notify;
359
360         parent_wants_notify = Playlist::region_changed (what_changed, region);
361         /* if bounds changed, we have already done notify_contents_changed ()*/
362         if ((parent_wants_notify || what_changed.contains (our_interests)) && !what_changed.contains (bounds)) {
363                 notify_contents_changed ();
364         }
365
366         return true;
367 }
368
369 void
370 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
371 {
372         RegionSortByPosition cmp;
373         boost::shared_ptr<AudioRegion> ar;
374
375         sort (copies.begin(), copies.end(), cmp);
376
377         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
378
379         /* disable fade in of the first region */
380
381         if (ar) {
382                 ar->set_fade_in_active (false);
383         }
384
385         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
386
387         /* disable fade out of the last region */
388
389         if (ar) {
390                 ar->set_fade_out_active (false);
391         }
392 }
393
394 void
395 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
396 {
397         RegionSortByPosition cmp;
398         boost::shared_ptr<AudioRegion> ar;
399         boost::shared_ptr<AudioRegion> cr;
400
401         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
402                 return;
403         }
404
405         sort (originals.begin(), originals.end(), cmp);
406
407         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
408
409         /* copy the fade in of the first into the compound region */
410
411         if (ar) {
412                 cr->set_fade_in (ar->fade_in());
413         }
414
415         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
416
417         if (ar) {
418                 /* copy the fade out of the last into the compound region */
419                 cr->set_fade_out (ar->fade_out());
420         }
421 }
422
423 void
424 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
425 {
426         RegionSortByPosition cmp;
427         boost::shared_ptr<AudioRegion> ar;
428         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
429
430         if (!cr) {
431                 return;
432         }
433
434         sort (originals.begin(), originals.end(), cmp);
435
436         /* no need to call clear_changes() on the originals because that is
437          * done within Playlist::uncombine ()
438          */
439
440         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
441
442                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
443                         continue;
444                 }
445
446                 /* scale the uncombined regions by any gain setting for the
447                  * compound one.
448                  */
449
450                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
451
452                 if (i == originals.begin()) {
453
454                         /* copy the compound region's fade in back into the first
455                            original region.
456                         */
457
458                         if (cr->fade_in()->back()->when <= ar->length()) {
459                                 /* don't do this if the fade is longer than the
460                                  * region
461                                  */
462                                 ar->set_fade_in (cr->fade_in());
463                         }
464
465
466                 } else if (*i == originals.back()) {
467
468                         /* copy the compound region's fade out back into the last
469                            original region.
470                         */
471
472                         if (cr->fade_out()->back()->when <= ar->length()) {
473                                 /* don't do this if the fade is longer than the
474                                  * region
475                                  */
476                                 ar->set_fade_out (cr->fade_out());
477                         }
478
479                 }
480
481                 _session.add_command (new StatefulDiffCommand (*i));
482         }
483 }
484
485 int
486 AudioPlaylist::set_state (const XMLNode& node, int version)
487 {
488         return Playlist::set_state (node, version);
489 }
490
491 void
492 AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
493 {
494         /* Read legacy Crossfade nodes and set up region fades accordingly */
495
496         XMLNodeList children = node.children ();
497         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
498                 if ((*i)->name() == X_("Crossfade")) {
499
500                         XMLProperty const * p = (*i)->property (X_("active"));
501                         assert (p);
502
503                         if (!string_to<bool> (p->value())) {
504                                 continue;
505                         }
506
507                         if ((p = (*i)->property (X_("in"))) == 0) {
508                                 continue;
509                         }
510
511                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
512
513                         if (!in) {
514                                 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
515                                                            name())
516                                         << endmsg;
517                                 continue;
518                         }
519
520                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
521                         assert (in_a);
522
523                         if ((p = (*i)->property (X_("out"))) == 0) {
524                                 continue;
525                         }
526
527                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
528
529                         if (!out) {
530                                 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
531                                                            name())
532                                         << endmsg;
533                                 continue;
534                         }
535
536                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
537                         assert (out_a);
538
539                         /* now decide whether to add a fade in or fade out
540                          * xfade and to which region
541                          */
542
543                         if (in->layer() <= out->layer()) {
544
545                                 /* incoming region is below the outgoing one,
546                                  * so apply a fade out to the outgoing one
547                                  */
548
549                                 const XMLNodeList c = (*i)->children ();
550
551                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
552                                         if ((*j)->name() == X_("FadeOut")) {
553                                                 out_a->fade_out()->set_state (**j, version);
554                                         } else if ((*j)->name() == X_("FadeIn")) {
555                                                 out_a->inverse_fade_out()->set_state (**j, version);
556                                         }
557                                 }
558
559                                 out_a->set_fade_out_active (true);
560
561                         } else {
562
563                                 /* apply a fade in to the incoming region,
564                                  * since its above the outgoing one
565                                  */
566
567                                 const XMLNodeList c = (*i)->children ();
568
569                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
570                                         if ((*j)->name() == X_("FadeIn")) {
571                                                 in_a->fade_in()->set_state (**j, version);
572                                         } else if ((*j)->name() == X_("FadeOut")) {
573                                                 in_a->inverse_fade_in()->set_state (**j, version);
574                                         }
575                                 }
576
577                                 in_a->set_fade_in_active (true);
578                         }
579                 }
580         }
581 }