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>
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.
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.
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.
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"
37 using namespace ARDOUR;
41 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
42 : Playlist (session, node, DataType::AUDIO, hidden)
45 XMLProperty const * prop = node.property("type");
46 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
50 if (set_state (node, Stateful::loading_state_version)) {
51 throw failed_constructor();
57 load_legacy_crossfades (node, Stateful::loading_state_version);
60 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
61 : Playlist (session, name, DataType::AUDIO, hidden)
65 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
66 : Playlist (other, name, hidden)
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)
73 RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
76 samplepos_t const end = start + cnt - 1;
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.
83 RegionList::iterator ours = regions.begin ();
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);
89 samplecnt_t fade_in = 64;
90 samplecnt_t fade_out = 64;
92 switch (region->coverage (start, end)) {
93 case Evoral::OverlapNone:
96 case Evoral::OverlapInternal:
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;
103 if (region->fade_out()->back()->when > trim) {
104 fade_out = region->fade_out()->back()->when - trim;
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
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;
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
126 case Evoral::OverlapExternal:
127 fade_in = region->fade_in()->back()->when;
128 fade_out = region->fade_out()->back()->when;
132 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
135 our_region->set_fade_in_length (fade_in);
136 our_region->set_fade_out_length (fade_out);
142 /* this constructor does NOT notify others (session) */
145 /** Sort by descending layer and then by ascending position */
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();
152 return a->position() < b->position();
156 /** A segment of region that needs to be read */
158 Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<samplepos_t> a) : region (r), range (a) {}
160 boost::shared_ptr<AudioRegion> region; ///< the region
161 Evoral::Range<samplepos_t> range; ///< range of the region to read, in session samples
164 /** @param start Start position in session samples.
165 * @param cnt Number of samples to read.
168 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, samplepos_t start,
169 samplecnt_t cnt, unsigned chan_n)
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));
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.
178 it would be great if someone could measure this
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
187 memset (buf, 0, sizeof (Sample) * cnt);
189 /* this function is never called from a realtime thread, so
190 its OK to block (for short intervals).
193 Playlist::RegionReadLock rl (this);
195 /* Find all the regions that are involved in the bit we are reading,
196 and sort them by descending layer and ascending position.
198 boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
199 all->sort (ReadSorter ());
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.
205 Evoral::RangeList<samplepos_t> done;
207 /* This will be a list of the bits of regions that we need to read */
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);
214 /* muted regions don't figure into it at all */
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 )
223 /* Work out which bits of this region need to be read;
224 first, trim to the range we are reading...
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);
230 /* ... and then remove the bits that are already done */
232 Evoral::RangeList<samplepos_t> region_to_do = Evoral::subtract (region_range, done);
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.
238 Evoral::RangeList<samplepos_t>::List t = region_to_do.get ();
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));
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);
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);
269 AudioPlaylist::dump () const
271 boost::shared_ptr<Region>r;
273 cerr << "Playlist \"" << _name << "\" " << endl
274 << regions.size() << " regions "
277 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
279 cerr << " " << r->name() << " @ " << r << " ["
280 << r->start() << "+" << r->length()
290 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
292 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
298 bool changed = false;
301 RegionWriteLock rlock (this);
303 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
305 RegionList::iterator tmp = i;
308 if ((*i) == region) {
316 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
318 set<boost::shared_ptr<Region> >::iterator xtmp = x;
321 if ((*x) == region) {
322 all_regions.erase (x);
329 region->set_playlist (boost::shared_ptr<Playlist>());
333 /* overload this, it normally means "removed", not destroyed */
334 notify_region_removed (region);
341 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
343 if (in_flush || in_set_state) {
347 PropertyChange bounds;
348 bounds.add (Properties::start);
349 bounds.add (Properties::position);
350 bounds.add (Properties::length);
352 PropertyChange our_interests;
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);
362 bool parent_wants_notify;
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 ();
374 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
376 RegionSortByPosition cmp;
377 boost::shared_ptr<AudioRegion> ar;
379 sort (copies.begin(), copies.end(), cmp);
381 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
383 /* disable fade in of the first region */
386 ar->set_fade_in_active (false);
389 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
391 /* disable fade out of the last region */
394 ar->set_fade_out_active (false);
399 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
401 RegionSortByPosition cmp;
402 boost::shared_ptr<AudioRegion> ar;
403 boost::shared_ptr<AudioRegion> cr;
405 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
409 sort (originals.begin(), originals.end(), cmp);
411 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
413 /* copy the fade in of the first into the compound region */
416 cr->set_fade_in (ar->fade_in());
419 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
422 /* copy the fade out of the last into the compound region */
423 cr->set_fade_out (ar->fade_out());
428 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
430 RegionSortByPosition cmp;
431 boost::shared_ptr<AudioRegion> ar;
432 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
438 sort (originals.begin(), originals.end(), cmp);
440 /* no need to call clear_changes() on the originals because that is
441 * done within Playlist::uncombine ()
444 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
446 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
450 /* scale the uncombined regions by any gain setting for the
454 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
456 if (i == originals.begin()) {
458 /* copy the compound region's fade in back into the first
462 if (cr->fade_in()->back()->when <= ar->length()) {
463 /* don't do this if the fade is longer than the
466 ar->set_fade_in (cr->fade_in());
470 } else if (*i == originals.back()) {
472 /* copy the compound region's fade out back into the last
476 if (cr->fade_out()->back()->when <= ar->length()) {
477 /* don't do this if the fade is longer than the
480 ar->set_fade_out (cr->fade_out());
485 _session.add_command (new StatefulDiffCommand (*i));
490 AudioPlaylist::set_state (const XMLNode& node, int version)
492 return Playlist::set_state (node, version);
496 AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
498 /* Read legacy Crossfade nodes and set up region fades accordingly */
500 XMLNodeList children = node.children ();
501 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
502 if ((*i)->name() == X_("Crossfade")) {
504 XMLProperty const * p = (*i)->property (X_("active"));
507 if (!string_to<bool> (p->value())) {
511 if ((p = (*i)->property (X_("in"))) == 0) {
515 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
518 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
524 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
527 if ((p = (*i)->property (X_("out"))) == 0) {
531 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
534 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
540 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
543 /* now decide whether to add a fade in or fade out
544 * xfade and to which region
547 if (in->layer() <= out->layer()) {
549 /* incoming region is below the outgoing one,
550 * so apply a fade out to the outgoing one
553 const XMLNodeList c = (*i)->children ();
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);
563 out_a->set_fade_out_active (true);
567 /* apply a fade in to the incoming region,
568 * since its above the outgoing one
571 const XMLNodeList c = (*i)->children ();
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);
581 in_a->set_fade_in_active (true);