2 Copyright (C) 2003 Paul Davis
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.
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.
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.
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"
33 using namespace ARDOUR;
37 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
38 : Playlist (session, node, DataType::AUDIO, hidden)
41 const XMLProperty* prop = node.property("type");
42 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
46 if (set_state (node, Stateful::loading_state_version)) {
47 throw failed_constructor();
54 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
55 : Playlist (session, name, DataType::AUDIO, hidden)
59 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
60 : Playlist (other, name, hidden)
64 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
65 : Playlist (other, start, cnt, name, hidden)
67 RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70 framepos_t const end = start + cnt - 1;
72 /* Audio regions that have been created by the Playlist constructor
73 will currently have the same fade in/out as the regions that they
74 were created from. This is wrong, so reset the fades here.
77 RegionList::iterator ours = regions.begin ();
79 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
80 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
83 framecnt_t fade_in = 64;
84 framecnt_t fade_out = 64;
86 switch (region->coverage (start, end)) {
87 case Evoral::OverlapNone:
90 case Evoral::OverlapInternal:
92 framecnt_t const offset = start - region->position ();
93 framecnt_t const trim = region->last_frame() - end;
94 if (region->fade_in()->back()->when > offset) {
95 fade_in = region->fade_in()->back()->when - offset;
97 if (region->fade_out()->back()->when > trim) {
98 fade_out = region->fade_out()->back()->when - trim;
103 case Evoral::OverlapStart: {
104 if (end > region->position() + region->fade_in()->back()->when)
105 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
106 if (end > region->last_frame() - region->fade_out()->back()->when)
107 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
111 case Evoral::OverlapEnd: {
112 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
113 fade_out = region->fade_out()->back()->when;
115 if (start < region->position() + region->fade_in()->back()->when)
116 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
120 case Evoral::OverlapExternal:
121 fade_in = region->fade_in()->back()->when;
122 fade_out = region->fade_out()->back()->when;
126 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
129 our_region->set_fade_in_length (fade_in);
130 our_region->set_fade_out_length (fade_out);
136 /* this constructor does NOT notify others (session) */
139 /** Sort by descending layer and then by ascending position */
141 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
142 if (a->layer() != b->layer()) {
143 return a->layer() > b->layer();
146 return a->position() < b->position();
150 /** A segment of region that needs to be read */
152 Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
154 boost::shared_ptr<AudioRegion> region; ///< the region
155 Evoral::Range<framepos_t> range; ///< range of the region to read, in session frames
158 /** @param start Start position in session frames.
159 * @param cnt Number of frames to read.
162 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
163 framecnt_t cnt, unsigned chan_n)
165 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
166 name(), start, cnt, chan_n, regions.size()));
168 /* optimizing this memset() away involves a lot of conditionals
169 that may well cause more of a hit due to cache misses
170 and related stuff than just doing this here.
172 it would be great if someone could measure this
175 one way or another, parts of the requested area
176 that are not written to by Region::region_at()
177 for all Regions that cover the area need to be
181 memset (buf, 0, sizeof (Sample) * cnt);
183 /* this function is never called from a realtime thread, so
184 its OK to block (for short intervals).
187 Playlist::RegionReadLock rl (this);
189 /* Find all the regions that are involved in the bit we are reading,
190 and sort them by descending layer and ascending position.
192 boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
193 all->sort (ReadSorter ());
195 /* This will be a list of the bits of our read range that we have
196 handled completely (ie for which no more regions need to be read).
197 It is a list of ranges in session frames.
199 Evoral::RangeList<framepos_t> done;
201 /* This will be a list of the bits of regions that we need to read */
204 /* Now go through the `all' list filling in `to_do' and `done' */
205 for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
206 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
208 /* Work out which bits of this region need to be read;
209 first, trim to the range we are reading...
211 Evoral::Range<framepos_t> region_range = ar->range ();
212 region_range.from = max (region_range.from, start);
213 region_range.to = min (region_range.to, start + cnt - 1);
215 /* ... and then remove the bits that are already done */
216 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
218 /* Read those bits, adding their bodies (the parts between end-of-fade-in
219 and start-of-fade-out) to the `done' list.
222 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
224 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
225 Evoral::Range<framepos_t> d = *j;
226 to_do.push_back (Segment (ar, d));
229 /* Cut this range down to just the body and mark it done */
230 Evoral::Range<framepos_t> body = ar->body_range ();
231 if (body.from < d.to && body.to > d.from) {
232 d.from = max (d.from, body.from);
233 d.to = min (d.to, body.to);
240 /* Now go backwards through the to_do list doing the actual reads */
241 for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
242 i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
249 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
251 if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
255 boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
256 boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
258 RegionList all = *starts;
259 std::copy (ends->begin(), ends->end(), back_inserter (all));
261 all.sort (RegionSortByLayer ());
263 set<boost::shared_ptr<Region> > done_start;
264 set<boost::shared_ptr<Region> > done_end;
266 for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
267 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
273 if ((*i)->muted() || (*j)->muted()) {
277 if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
278 /* precise overlay: no xfade */
282 if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
283 /* starts or ends match: no xfade */
287 boost::shared_ptr<AudioRegion> top;
288 boost::shared_ptr<AudioRegion> bottom;
290 if ((*i)->layer() < (*j)->layer()) {
291 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
292 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
294 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
295 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
298 if (!top->opaque ()) {
302 Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
304 if (c == Evoral::OverlapStart) {
306 /* top starts within bottom but covers bottom's end */
308 /* { ==== top ============ }
309 * [---- bottom -------------------]
312 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
314 /* Top's fade-in will cause an implicit fade-out of bottom */
318 if (_capture_insertion_underway) {
319 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
321 switch (_session.config.get_xfade_model()) {
323 len = bottom->last_frame () - top->first_frame ();
326 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
331 top->set_fade_in_active (true);
332 top->set_fade_in_is_xfade (true);
334 /* XXX may 2012: -3dB and -6dB curves
335 * are the same right now
338 switch (_session.config.get_xfade_choice ()) {
339 case ConstantPowerMinus3dB:
340 top->set_fade_in (FadeConstantPower, len);
342 case ConstantPowerMinus6dB:
343 top->set_fade_in (FadeConstantPower, len);
346 top->set_fade_in_length (len);
350 done_start.insert (top);
353 } else if (c == Evoral::OverlapEnd) {
355 /* top covers start of bottom but ends within it */
357 /* [---- top ------------------------]
358 * { ==== bottom ============ }
361 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
362 /* Top's fade-out will cause an implicit fade-in of bottom */
366 if (_capture_insertion_underway) {
367 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
369 switch (_session.config.get_xfade_model()) {
371 len = top->last_frame () - bottom->first_frame ();
374 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
379 top->set_fade_out_active (true);
380 top->set_fade_out_is_xfade (true);
382 switch (_session.config.get_xfade_choice ()) {
383 case ConstantPowerMinus3dB:
384 top->set_fade_out (FadeConstantPower, len);
386 case ConstantPowerMinus6dB:
387 top->set_fade_out (FadeConstantPower, len);
390 top->set_fade_out_length (len);
394 done_end.insert (top);
400 for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
401 if (done_start.find (*i) == done_start.end()) {
402 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
403 r->set_default_fade_in ();
407 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
408 if (done_end.find (*i) == done_end.end()) {
409 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
410 r->set_default_fade_out ();
416 AudioPlaylist::dump () const
418 boost::shared_ptr<Region>r;
420 cerr << "Playlist \"" << _name << "\" " << endl
421 << regions.size() << " regions "
424 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
426 cerr << " " << r->name() << " @ " << r << " ["
427 << r->start() << "+" << r->length()
437 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
439 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
445 bool changed = false;
448 RegionWriteLock rlock (this);
450 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
452 RegionList::iterator tmp = i;
455 if ((*i) == region) {
463 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
465 set<boost::shared_ptr<Region> >::iterator xtmp = x;
468 if ((*x) == region) {
469 all_regions.erase (x);
476 region->set_playlist (boost::shared_ptr<Playlist>());
480 /* overload this, it normally means "removed", not destroyed */
481 notify_region_removed (region);
488 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
490 if (in_flush || in_set_state) {
494 PropertyChange our_interests;
496 our_interests.add (Properties::fade_in_active);
497 our_interests.add (Properties::fade_out_active);
498 our_interests.add (Properties::scale_amplitude);
499 our_interests.add (Properties::envelope_active);
500 our_interests.add (Properties::envelope);
501 our_interests.add (Properties::fade_in);
502 our_interests.add (Properties::fade_out);
504 bool parent_wants_notify;
506 parent_wants_notify = Playlist::region_changed (what_changed, region);
508 if (parent_wants_notify || (what_changed.contains (our_interests))) {
509 notify_contents_changed ();
516 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
518 RegionSortByPosition cmp;
519 boost::shared_ptr<AudioRegion> ar;
521 sort (copies.begin(), copies.end(), cmp);
523 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
525 /* disable fade in of the first region */
528 ar->set_fade_in_active (false);
531 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
533 /* disable fade out of the last region */
536 ar->set_fade_out_active (false);
541 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
543 RegionSortByPosition cmp;
544 boost::shared_ptr<AudioRegion> ar;
545 boost::shared_ptr<AudioRegion> cr;
547 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
551 sort (originals.begin(), originals.end(), cmp);
553 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
555 /* copy the fade in of the first into the compound region */
558 cr->set_fade_in (ar->fade_in());
561 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
564 /* copy the fade out of the last into the compound region */
565 cr->set_fade_out (ar->fade_out());
570 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
572 RegionSortByPosition cmp;
573 boost::shared_ptr<AudioRegion> ar;
574 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
580 sort (originals.begin(), originals.end(), cmp);
582 /* no need to call clear_changes() on the originals because that is
583 * done within Playlist::uncombine ()
586 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
588 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
592 /* scale the uncombined regions by any gain setting for the
596 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
598 if (i == originals.begin()) {
600 /* copy the compound region's fade in back into the first
604 if (cr->fade_in()->back()->when <= ar->length()) {
605 /* don't do this if the fade is longer than the
608 ar->set_fade_in (cr->fade_in());
612 } else if (*i == originals.back()) {
614 /* copy the compound region's fade out back into the last
618 if (cr->fade_out()->back()->when <= ar->length()) {
619 /* don't do this if the fade is longer than the
622 ar->set_fade_out (cr->fade_out());
627 _session.add_command (new StatefulDiffCommand (*i));
632 AudioPlaylist::set_state (const XMLNode& node, int version)
634 int const r = Playlist::set_state (node, version);
639 /* Read legacy Crossfade nodes and set up region fades accordingly */
641 XMLNodeList children = node.children ();
642 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
643 if ((*i)->name() == X_("Crossfade")) {
645 XMLProperty* p = (*i)->property (X_("active"));
647 if (!string_is_affirmative (p->value())) {
651 p = (*i)->property (X_("in"));
653 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
655 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
658 p = (*i)->property (X_("out"));
660 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
662 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
665 XMLNodeList c = (*i)->children ();
666 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
667 if ((*j)->name() == X_("FadeIn")) {
668 in_a->fade_in()->set_state (**j, version);
669 in_a->set_fade_in_active (true);
670 } else if ((*j)->name() == X_("FadeOut")) {
671 out_a->fade_out()->set_state (**j, version);
672 out_a->set_fade_out_active (true);