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 */
316 if (top->fade_in_is_xfade() && top->fade_in_is_short()) {
318 /* its already an xfade. if its
319 * really short, leave it
326 if (_capture_insertion_underway) {
327 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
329 switch (_session.config.get_xfade_model()) {
331 len = bottom->last_frame () - top->first_frame ();
332 top->set_fade_in_is_short (false);
335 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
336 top->set_fade_in_is_short (true);
341 top->set_fade_in_active (true);
342 top->set_fade_in_is_xfade (true);
344 /* XXX may 2012: -3dB and -6dB curves
345 * are the same right now
348 switch (_session.config.get_xfade_choice ()) {
349 case ConstantPowerMinus3dB:
350 top->set_fade_in (FadeConstantPower, len);
352 case ConstantPowerMinus6dB:
353 top->set_fade_in (FadeConstantPower, len);
356 top->set_fade_in_length (len);
361 done_start.insert (top);
364 } else if (c == Evoral::OverlapEnd) {
366 /* top covers start of bottom but ends within it */
368 /* [---- top ------------------------]
369 * { ==== bottom ============ }
372 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
373 /* Top's fade-out will cause an implicit fade-in of bottom */
376 if (top->fade_out_is_xfade() && top->fade_out_is_short()) {
378 /* its already an xfade. if its
379 * really short, leave it
386 if (_capture_insertion_underway) {
387 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
389 switch (_session.config.get_xfade_model()) {
391 len = top->last_frame () - bottom->first_frame ();
394 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
399 top->set_fade_out_active (true);
400 top->set_fade_out_is_xfade (true);
402 switch (_session.config.get_xfade_choice ()) {
403 case ConstantPowerMinus3dB:
404 top->set_fade_out (FadeConstantPower, len);
406 case ConstantPowerMinus6dB:
407 top->set_fade_out (FadeConstantPower, len);
410 top->set_fade_out_length (len);
415 done_end.insert (top);
421 for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
422 if (done_start.find (*i) == done_start.end()) {
423 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
424 if (r->fade_in_is_xfade()) {
425 r->set_default_fade_in ();
430 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
431 if (done_end.find (*i) == done_end.end()) {
432 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
433 if (r->fade_out_is_xfade()) {
434 r->set_default_fade_out ();
441 AudioPlaylist::dump () const
443 boost::shared_ptr<Region>r;
445 cerr << "Playlist \"" << _name << "\" " << endl
446 << regions.size() << " regions "
449 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
451 cerr << " " << r->name() << " @ " << r << " ["
452 << r->start() << "+" << r->length()
462 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
464 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
470 bool changed = false;
473 RegionWriteLock rlock (this);
475 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
477 RegionList::iterator tmp = i;
480 if ((*i) == region) {
488 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
490 set<boost::shared_ptr<Region> >::iterator xtmp = x;
493 if ((*x) == region) {
494 all_regions.erase (x);
501 region->set_playlist (boost::shared_ptr<Playlist>());
505 /* overload this, it normally means "removed", not destroyed */
506 notify_region_removed (region);
513 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
515 if (in_flush || in_set_state) {
519 PropertyChange our_interests;
521 our_interests.add (Properties::fade_in_active);
522 our_interests.add (Properties::fade_out_active);
523 our_interests.add (Properties::scale_amplitude);
524 our_interests.add (Properties::envelope_active);
525 our_interests.add (Properties::envelope);
526 our_interests.add (Properties::fade_in);
527 our_interests.add (Properties::fade_out);
529 bool parent_wants_notify;
531 parent_wants_notify = Playlist::region_changed (what_changed, region);
533 if (parent_wants_notify || (what_changed.contains (our_interests))) {
534 notify_contents_changed ();
541 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
543 RegionSortByPosition cmp;
544 boost::shared_ptr<AudioRegion> ar;
546 sort (copies.begin(), copies.end(), cmp);
548 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
550 /* disable fade in of the first region */
553 ar->set_fade_in_active (false);
556 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
558 /* disable fade out of the last region */
561 ar->set_fade_out_active (false);
566 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
568 RegionSortByPosition cmp;
569 boost::shared_ptr<AudioRegion> ar;
570 boost::shared_ptr<AudioRegion> cr;
572 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
576 sort (originals.begin(), originals.end(), cmp);
578 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
580 /* copy the fade in of the first into the compound region */
583 cr->set_fade_in (ar->fade_in());
586 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
589 /* copy the fade out of the last into the compound region */
590 cr->set_fade_out (ar->fade_out());
595 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
597 RegionSortByPosition cmp;
598 boost::shared_ptr<AudioRegion> ar;
599 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
605 sort (originals.begin(), originals.end(), cmp);
607 /* no need to call clear_changes() on the originals because that is
608 * done within Playlist::uncombine ()
611 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
613 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
617 /* scale the uncombined regions by any gain setting for the
621 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
623 if (i == originals.begin()) {
625 /* copy the compound region's fade in back into the first
629 if (cr->fade_in()->back()->when <= ar->length()) {
630 /* don't do this if the fade is longer than the
633 ar->set_fade_in (cr->fade_in());
637 } else if (*i == originals.back()) {
639 /* copy the compound region's fade out back into the last
643 if (cr->fade_out()->back()->when <= ar->length()) {
644 /* don't do this if the fade is longer than the
647 ar->set_fade_out (cr->fade_out());
652 _session.add_command (new StatefulDiffCommand (*i));
657 AudioPlaylist::set_state (const XMLNode& node, int version)
659 int const r = Playlist::set_state (node, version);
664 /* Read legacy Crossfade nodes and set up region fades accordingly */
666 XMLNodeList children = node.children ();
667 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
668 if ((*i)->name() == X_("Crossfade")) {
670 XMLProperty* p = (*i)->property (X_("active"));
672 if (!string_is_affirmative (p->value())) {
676 p = (*i)->property (X_("in"));
678 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
680 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
685 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
688 p = (*i)->property (X_("out"));
690 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
692 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
697 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
700 XMLNodeList c = (*i)->children ();
701 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
702 if ((*j)->name() == X_("FadeIn")) {
703 in_a->fade_in()->set_state (**j, version);
704 in_a->set_fade_in_active (true);
705 in_a->set_fade_in_is_xfade (true);
706 } else if ((*j)->name() == X_("FadeOut")) {
707 out_a->fade_out()->set_state (**j, version);
708 out_a->set_fade_out_active (true);
709 out_a->set_fade_out_is_xfade (true);