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 */
317 switch (_session.config.get_xfade_model()) {
319 len = bottom->last_frame () - top->first_frame ();
322 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
326 top->set_fade_in_active (true);
327 top->set_fade_in_is_xfade (true);
329 /* XXX may 2012: -3dB and -6dB curves
330 * are the same right now
333 switch (_session.config.get_xfade_choice ()) {
334 case ConstantPowerMinus3dB:
335 top->set_fade_in (FadeConstantPower, len);
337 case ConstantPowerMinus6dB:
338 top->set_fade_in (FadeConstantPower, len);
341 top->set_fade_in_length (len);
345 done_start.insert (top);
348 } else if (c == Evoral::OverlapEnd) {
350 /* top covers start of bottom but ends within it */
352 /* [---- top ------------------------]
353 * { ==== bottom ============ }
356 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
357 /* Top's fade-out will cause an implicit fade-in of bottom */
360 switch (_session.config.get_xfade_model()) {
362 len = top->last_frame () - bottom->first_frame ();
365 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
369 top->set_fade_out_active (true);
370 top->set_fade_out_is_xfade (true);
372 switch (_session.config.get_xfade_choice ()) {
373 case ConstantPowerMinus3dB:
374 top->set_fade_out (FadeConstantPower, len);
376 case ConstantPowerMinus6dB:
377 top->set_fade_out (FadeConstantPower, len);
380 top->set_fade_out_length (len);
384 done_end.insert (top);
390 for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
391 if (done_start.find (*i) == done_start.end()) {
392 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
393 r->set_default_fade_in ();
397 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
398 if (done_end.find (*i) == done_end.end()) {
399 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
400 r->set_default_fade_out ();
406 AudioPlaylist::dump () const
408 boost::shared_ptr<Region>r;
410 cerr << "Playlist \"" << _name << "\" " << endl
411 << regions.size() << " regions "
414 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
416 cerr << " " << r->name() << " @ " << r << " ["
417 << r->start() << "+" << r->length()
427 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
429 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
435 bool changed = false;
438 RegionWriteLock rlock (this);
440 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
442 RegionList::iterator tmp = i;
445 if ((*i) == region) {
453 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
455 set<boost::shared_ptr<Region> >::iterator xtmp = x;
458 if ((*x) == region) {
459 all_regions.erase (x);
466 region->set_playlist (boost::shared_ptr<Playlist>());
470 /* overload this, it normally means "removed", not destroyed */
471 notify_region_removed (region);
478 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
480 if (in_flush || in_set_state) {
484 PropertyChange our_interests;
486 our_interests.add (Properties::fade_in_active);
487 our_interests.add (Properties::fade_out_active);
488 our_interests.add (Properties::scale_amplitude);
489 our_interests.add (Properties::envelope_active);
490 our_interests.add (Properties::envelope);
491 our_interests.add (Properties::fade_in);
492 our_interests.add (Properties::fade_out);
494 bool parent_wants_notify;
496 parent_wants_notify = Playlist::region_changed (what_changed, region);
498 if (parent_wants_notify || (what_changed.contains (our_interests))) {
499 notify_contents_changed ();
506 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
508 RegionSortByPosition cmp;
509 boost::shared_ptr<AudioRegion> ar;
511 sort (copies.begin(), copies.end(), cmp);
513 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
515 /* disable fade in of the first region */
518 ar->set_fade_in_active (false);
521 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
523 /* disable fade out of the last region */
526 ar->set_fade_out_active (false);
531 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
533 RegionSortByPosition cmp;
534 boost::shared_ptr<AudioRegion> ar;
535 boost::shared_ptr<AudioRegion> cr;
537 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
541 sort (originals.begin(), originals.end(), cmp);
543 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
545 /* copy the fade in of the first into the compound region */
548 cr->set_fade_in (ar->fade_in());
551 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
554 /* copy the fade out of the last into the compound region */
555 cr->set_fade_out (ar->fade_out());
560 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
562 RegionSortByPosition cmp;
563 boost::shared_ptr<AudioRegion> ar;
564 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
570 sort (originals.begin(), originals.end(), cmp);
572 /* no need to call clear_changes() on the originals because that is
573 * done within Playlist::uncombine ()
576 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
578 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
582 /* scale the uncombined regions by any gain setting for the
586 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
588 if (i == originals.begin()) {
590 /* copy the compound region's fade in back into the first
594 if (cr->fade_in()->back()->when <= ar->length()) {
595 /* don't do this if the fade is longer than the
598 ar->set_fade_in (cr->fade_in());
602 } else if (*i == originals.back()) {
604 /* copy the compound region's fade out back into the last
608 if (cr->fade_out()->back()->when <= ar->length()) {
609 /* don't do this if the fade is longer than the
612 ar->set_fade_out (cr->fade_out());
617 _session.add_command (new StatefulDiffCommand (*i));
622 AudioPlaylist::set_state (const XMLNode& node, int version)
624 int const r = Playlist::set_state (node, version);
629 /* Read legacy Crossfade nodes and set up region fades accordingly */
631 XMLNodeList children = node.children ();
632 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
633 if ((*i)->name() == X_("Crossfade")) {
635 XMLProperty* p = (*i)->property (X_("active"));
637 if (!string_is_affirmative (p->value())) {
641 p = (*i)->property (X_("in"));
643 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
645 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
648 p = (*i)->property (X_("out"));
650 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
652 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
655 XMLNodeList c = (*i)->children ();
656 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
657 if ((*j)->name() == X_("FadeIn")) {
658 in_a->fade_in()->set_state (**j, version);
659 in_a->set_fade_in_active (true);
660 } else if ((*j)->name() == X_("FadeOut")) {
661 out_a->fade_out()->set_state (**j, version);
662 out_a->set_fade_out_active (true);