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 r->set_default_fade_in ();
428 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
429 if (done_end.find (*i) == done_end.end()) {
430 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
431 r->set_default_fade_out ();
437 AudioPlaylist::dump () const
439 boost::shared_ptr<Region>r;
441 cerr << "Playlist \"" << _name << "\" " << endl
442 << regions.size() << " regions "
445 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
447 cerr << " " << r->name() << " @ " << r << " ["
448 << r->start() << "+" << r->length()
458 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
460 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
466 bool changed = false;
469 RegionWriteLock rlock (this);
471 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
473 RegionList::iterator tmp = i;
476 if ((*i) == region) {
484 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
486 set<boost::shared_ptr<Region> >::iterator xtmp = x;
489 if ((*x) == region) {
490 all_regions.erase (x);
497 region->set_playlist (boost::shared_ptr<Playlist>());
501 /* overload this, it normally means "removed", not destroyed */
502 notify_region_removed (region);
509 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
511 if (in_flush || in_set_state) {
515 PropertyChange our_interests;
517 our_interests.add (Properties::fade_in_active);
518 our_interests.add (Properties::fade_out_active);
519 our_interests.add (Properties::scale_amplitude);
520 our_interests.add (Properties::envelope_active);
521 our_interests.add (Properties::envelope);
522 our_interests.add (Properties::fade_in);
523 our_interests.add (Properties::fade_out);
525 bool parent_wants_notify;
527 parent_wants_notify = Playlist::region_changed (what_changed, region);
529 if (parent_wants_notify || (what_changed.contains (our_interests))) {
530 notify_contents_changed ();
537 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
539 RegionSortByPosition cmp;
540 boost::shared_ptr<AudioRegion> ar;
542 sort (copies.begin(), copies.end(), cmp);
544 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
546 /* disable fade in of the first region */
549 ar->set_fade_in_active (false);
552 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
554 /* disable fade out of the last region */
557 ar->set_fade_out_active (false);
562 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
564 RegionSortByPosition cmp;
565 boost::shared_ptr<AudioRegion> ar;
566 boost::shared_ptr<AudioRegion> cr;
568 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
572 sort (originals.begin(), originals.end(), cmp);
574 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
576 /* copy the fade in of the first into the compound region */
579 cr->set_fade_in (ar->fade_in());
582 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
585 /* copy the fade out of the last into the compound region */
586 cr->set_fade_out (ar->fade_out());
591 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
593 RegionSortByPosition cmp;
594 boost::shared_ptr<AudioRegion> ar;
595 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
601 sort (originals.begin(), originals.end(), cmp);
603 /* no need to call clear_changes() on the originals because that is
604 * done within Playlist::uncombine ()
607 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
609 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
613 /* scale the uncombined regions by any gain setting for the
617 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
619 if (i == originals.begin()) {
621 /* copy the compound region's fade in back into the first
625 if (cr->fade_in()->back()->when <= ar->length()) {
626 /* don't do this if the fade is longer than the
629 ar->set_fade_in (cr->fade_in());
633 } else if (*i == originals.back()) {
635 /* copy the compound region's fade out back into the last
639 if (cr->fade_out()->back()->when <= ar->length()) {
640 /* don't do this if the fade is longer than the
643 ar->set_fade_out (cr->fade_out());
648 _session.add_command (new StatefulDiffCommand (*i));
653 AudioPlaylist::set_state (const XMLNode& node, int version)
655 int const r = Playlist::set_state (node, version);
660 /* Read legacy Crossfade nodes and set up region fades accordingly */
662 XMLNodeList children = node.children ();
663 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
664 if ((*i)->name() == X_("Crossfade")) {
666 XMLProperty* p = (*i)->property (X_("active"));
668 if (!string_is_affirmative (p->value())) {
672 p = (*i)->property (X_("in"));
674 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
676 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
679 p = (*i)->property (X_("out"));
681 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
683 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
686 XMLNodeList c = (*i)->children ();
687 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
688 if ((*j)->name() == X_("FadeIn")) {
689 in_a->fade_in()->set_state (**j, version);
690 in_a->set_fade_in_active (true);
691 } else if ((*j)->name() == X_("FadeOut")) {
692 out_a->fade_out()->set_state (**j, version);
693 out_a->set_fade_out_active (true);