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/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/region_sorters.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
35 using namespace ARDOUR;
39 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
40 : Playlist (session, node, DataType::AUDIO, hidden)
43 const XMLProperty* prop = node.property("type");
44 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
48 if (set_state (node, Stateful::loading_state_version)) {
49 throw failed_constructor();
56 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
57 : Playlist (session, name, DataType::AUDIO, hidden)
61 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
62 : Playlist (other, name, hidden)
66 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
67 : Playlist (other, start, cnt, name, hidden)
69 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
72 framepos_t const end = start + cnt - 1;
74 /* Audio regions that have been created by the Playlist constructor
75 will currently have the same fade in/out as the regions that they
76 were created from. This is wrong, so reset the fades here.
79 RegionList::iterator ours = regions.begin ();
81 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
82 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
85 framecnt_t fade_in = 64;
86 framecnt_t fade_out = 64;
88 switch (region->coverage (start, end)) {
89 case Evoral::OverlapNone:
92 case Evoral::OverlapInternal:
94 framecnt_t const offset = start - region->position ();
95 framecnt_t const trim = region->last_frame() - end;
96 if (region->fade_in()->back()->when > offset) {
97 fade_in = region->fade_in()->back()->when - offset;
99 if (region->fade_out()->back()->when > trim) {
100 fade_out = region->fade_out()->back()->when - trim;
105 case Evoral::OverlapStart: {
106 if (end > region->position() + region->fade_in()->back()->when)
107 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
108 if (end > region->last_frame() - region->fade_out()->back()->when)
109 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
113 case Evoral::OverlapEnd: {
114 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
115 fade_out = region->fade_out()->back()->when;
117 if (start < region->position() + region->fade_in()->back()->when)
118 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
122 case Evoral::OverlapExternal:
123 fade_in = region->fade_in()->back()->when;
124 fade_out = region->fade_out()->back()->when;
128 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
131 our_region->set_fade_in_length (fade_in);
132 our_region->set_fade_out_length (fade_out);
138 /* this constructor does NOT notify others (session) */
141 /** Sort by descending layer and then by ascending position */
143 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
144 if (a->layer() != b->layer()) {
145 return a->layer() > b->layer();
148 return a->position() < b->position();
152 /** A segment of region that needs to be read */
154 Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
156 boost::shared_ptr<AudioRegion> region; ///< the region
157 Evoral::Range<framepos_t> range; ///< range of the region to read, in session frames
160 /** @param start Start position in session frames.
161 * @param cnt Number of frames to read.
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
165 framecnt_t cnt, unsigned chan_n)
167 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
168 name(), start, cnt, chan_n, regions.size()));
170 /* optimizing this memset() away involves a lot of conditionals
171 that may well cause more of a hit due to cache misses
172 and related stuff than just doing this here.
174 it would be great if someone could measure this
177 one way or another, parts of the requested area
178 that are not written to by Region::region_at()
179 for all Regions that cover the area need to be
183 memset (buf, 0, sizeof (Sample) * cnt);
185 /* this function is never called from a realtime thread, so
186 its OK to block (for short intervals).
189 #ifdef HAVE_GLIB_THREADS_RECMUTEX
190 Glib::Threads::RecMutex::Lock lm (region_lock);
192 Glib::RecMutex::Lock rm (region_lock);
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 (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 frames.
205 Evoral::RangeList<framepos_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 /* Work out which bits of this region need to be read;
215 first, trim to the range we are reading...
217 Evoral::Range<framepos_t> region_range = ar->range ();
218 region_range.from = max (region_range.from, start);
219 region_range.to = min (region_range.to, start + cnt - 1);
221 /* ... and then remove the bits that are already done */
222 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
224 /* Read those bits, adding their bodies (the parts between end-of-fade-in
225 and start-of-fade-out) to the `done' list.
228 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
230 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
231 Evoral::Range<framepos_t> d = *j;
232 to_do.push_back (Segment (ar, d));
235 /* Cut this range down to just the body and mark it done */
236 Evoral::Range<framepos_t> body = ar->body_range ();
237 if (body.from < d.to && body.to > d.from) {
238 d.from = max (d.from, body.from);
239 d.to = min (d.to, body.to);
246 /* Now go backwards through the to_do list doing the actual reads */
247 for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
248 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);
255 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
257 if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
261 boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
262 boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
264 RegionList all = *starts;
265 std::copy (ends->begin(), ends->end(), back_inserter (all));
267 all.sort (RegionSortByLayer ());
269 set<boost::shared_ptr<Region> > done_start;
270 set<boost::shared_ptr<Region> > done_end;
272 for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
273 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
279 if ((*i)->muted() || (*j)->muted()) {
283 if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
284 /* precise overlay: no xfade */
288 if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
289 /* starts or ends match: no xfade */
293 boost::shared_ptr<AudioRegion> top;
294 boost::shared_ptr<AudioRegion> bottom;
296 if ((*i)->layer() < (*j)->layer()) {
297 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
298 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
300 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
301 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
304 if (!top->opaque ()) {
308 Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
310 if (c == Evoral::OverlapStart) {
312 /* top starts within bottom but covers bottom's end */
314 /* { ==== top ============ }
315 * [---- bottom -------------------]
318 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
320 /* Top's fade-in will cause an implicit fade-out of bottom */
323 switch (_session.config.get_xfade_model()) {
325 len = bottom->last_frame () - top->first_frame ();
328 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
332 switch (_session.config.get_xfade_choice ()) {
333 case ConstantPowerMinus3dB:
334 top->set_fade_in (FadeConstantPowerMinus3dB, len);
336 case ConstantPowerMinus6dB:
337 top->set_fade_in (FadeConstantPowerMinus6dB, len);
340 top->set_fade_in_length (len);
343 top->set_fade_in_active (true);
344 top->set_fade_in_is_xfade (true);
346 done_start.insert (top);
349 } else if (c == Evoral::OverlapEnd) {
351 /* top covers start of bottom but ends within it */
353 /* [---- top ------------------------]
354 * { ==== bottom ============ }
357 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
358 /* Top's fade-out will cause an implicit fade-in of bottom */
361 switch (_session.config.get_xfade_model()) {
363 len = top->last_frame () - bottom->first_frame ();
366 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
370 switch (_session.config.get_xfade_choice ()) {
371 case ConstantPowerMinus3dB:
372 top->set_fade_out (FadeConstantPowerMinus3dB, len);
374 case ConstantPowerMinus6dB:
375 top->set_fade_out (FadeConstantPowerMinus6dB, len);
378 top->set_fade_out_length (len);
381 top->set_fade_out_active (true);
382 top->set_fade_out_is_xfade (true);
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 RegionLock 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);