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