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 Playlist::RegionLock rl (this, false);
191 /* Find all the regions that are involved in the bit we are reading,
192 and sort them by descending layer and ascending position.
194 boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
195 all->sort (ReadSorter ());
197 /* This will be a list of the bits of our read range that we have
198 handled completely (ie for which no more regions need to be read).
199 It is a list of ranges in session frames.
201 Evoral::RangeList<framepos_t> done;
203 /* This will be a list of the bits of regions that we need to read */
206 /* Now go through the `all' list filling in `to_do' and `done' */
207 for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
208 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
210 /* Work out which bits of this region need to be read;
211 first, trim to the range we are reading...
213 Evoral::Range<framepos_t> region_range = ar->range ();
214 region_range.from = max (region_range.from, start);
215 region_range.to = min (region_range.to, start + cnt - 1);
217 /* ... and then remove the bits that are already done */
218 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
220 /* Read those bits, adding their bodies (the parts between end-of-fade-in
221 and start-of-fade-out) to the `done' list.
224 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
226 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
227 Evoral::Range<framepos_t> d = *j;
228 to_do.push_back (Segment (ar, d));
231 /* Cut this range down to just the body and mark it done */
232 Evoral::Range<framepos_t> body = ar->body_range ();
233 if (body.from < d.to && body.to > d.from) {
234 d.from = max (d.from, body.from);
235 d.to = min (d.to, body.to);
242 /* Now go backwards through the to_do list doing the actual reads */
243 for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
244 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);
251 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
253 if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
257 boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
258 boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
260 RegionList all = *starts;
261 std::copy (ends->begin(), ends->end(), back_inserter (all));
263 all.sort (RegionSortByLayer ());
265 set<boost::shared_ptr<Region> > done_start;
266 set<boost::shared_ptr<Region> > done_end;
268 for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
269 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
275 if ((*i)->muted() || (*j)->muted()) {
279 if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
280 /* precise overlay: no xfade */
284 if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
285 /* starts or ends match: no xfade */
289 boost::shared_ptr<AudioRegion> top;
290 boost::shared_ptr<AudioRegion> bottom;
292 if ((*i)->layer() < (*j)->layer()) {
293 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
294 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
296 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
297 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
300 if (!top->opaque ()) {
304 Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
306 if (c == Evoral::OverlapStart) {
308 /* top starts within bottom but covers bottom's end */
310 /* { ==== top ============ }
311 * [---- bottom -------------------]
314 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
316 /* Top's fade-in will cause an implicit fade-out of bottom */
319 switch (_session.config.get_xfade_model()) {
321 len = bottom->last_frame () - top->first_frame ();
324 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
328 top->set_fade_in_active (true);
329 top->set_fade_in_is_xfade (true);
331 switch (_session.config.get_xfade_choice ()) {
332 case ConstantPowerMinus3dB:
333 top->set_fade_in (FadeConstantPowerMinus3dB, len);
335 case ConstantPowerMinus6dB:
336 top->set_fade_in (FadeConstantPowerMinus6dB, len);
339 top->set_fade_in_length (len);
343 done_start.insert (top);
346 } else if (c == Evoral::OverlapEnd) {
348 /* top covers start of bottom but ends within it */
350 /* [---- top ------------------------]
351 * { ==== bottom ============ }
354 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
355 /* Top's fade-out will cause an implicit fade-in of bottom */
358 switch (_session.config.get_xfade_model()) {
360 len = top->last_frame () - bottom->first_frame ();
363 len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
367 top->set_fade_out_active (true);
368 top->set_fade_out_is_xfade (true);
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);
382 done_end.insert (top);
388 for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
389 if (done_start.find (*i) == done_start.end()) {
390 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
391 r->set_default_fade_in ();
395 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
396 if (done_end.find (*i) == done_end.end()) {
397 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
398 r->set_default_fade_out ();
404 AudioPlaylist::dump () const
406 boost::shared_ptr<Region>r;
408 cerr << "Playlist \"" << _name << "\" " << endl
409 << regions.size() << " regions "
412 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
414 cerr << " " << r->name() << " @ " << r << " ["
415 << r->start() << "+" << r->length()
425 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
427 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
433 bool changed = false;
436 RegionLock rlock (this);
438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
440 RegionList::iterator tmp = i;
443 if ((*i) == region) {
451 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
453 set<boost::shared_ptr<Region> >::iterator xtmp = x;
456 if ((*x) == region) {
457 all_regions.erase (x);
464 region->set_playlist (boost::shared_ptr<Playlist>());
468 /* overload this, it normally means "removed", not destroyed */
469 notify_region_removed (region);
476 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
478 if (in_flush || in_set_state) {
482 PropertyChange our_interests;
484 our_interests.add (Properties::fade_in_active);
485 our_interests.add (Properties::fade_out_active);
486 our_interests.add (Properties::scale_amplitude);
487 our_interests.add (Properties::envelope_active);
488 our_interests.add (Properties::envelope);
489 our_interests.add (Properties::fade_in);
490 our_interests.add (Properties::fade_out);
492 bool parent_wants_notify;
494 parent_wants_notify = Playlist::region_changed (what_changed, region);
496 if (parent_wants_notify || (what_changed.contains (our_interests))) {
497 notify_contents_changed ();
504 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
506 RegionSortByPosition cmp;
507 boost::shared_ptr<AudioRegion> ar;
509 sort (copies.begin(), copies.end(), cmp);
511 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
513 /* disable fade in of the first region */
516 ar->set_fade_in_active (false);
519 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
521 /* disable fade out of the last region */
524 ar->set_fade_out_active (false);
529 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
531 RegionSortByPosition cmp;
532 boost::shared_ptr<AudioRegion> ar;
533 boost::shared_ptr<AudioRegion> cr;
535 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
539 sort (originals.begin(), originals.end(), cmp);
541 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
543 /* copy the fade in of the first into the compound region */
546 cr->set_fade_in (ar->fade_in());
549 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
552 /* copy the fade out of the last into the compound region */
553 cr->set_fade_out (ar->fade_out());
558 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
560 RegionSortByPosition cmp;
561 boost::shared_ptr<AudioRegion> ar;
562 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
568 sort (originals.begin(), originals.end(), cmp);
570 /* no need to call clear_changes() on the originals because that is
571 * done within Playlist::uncombine ()
574 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
576 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
580 /* scale the uncombined regions by any gain setting for the
584 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
586 if (i == originals.begin()) {
588 /* copy the compound region's fade in back into the first
592 if (cr->fade_in()->back()->when <= ar->length()) {
593 /* don't do this if the fade is longer than the
596 ar->set_fade_in (cr->fade_in());
600 } else if (*i == originals.back()) {
602 /* copy the compound region's fade out back into the last
606 if (cr->fade_out()->back()->when <= ar->length()) {
607 /* don't do this if the fade is longer than the
610 ar->set_fade_out (cr->fade_out());
615 _session.add_command (new StatefulDiffCommand (*i));
620 AudioPlaylist::set_state (const XMLNode& node, int version)
622 int const r = Playlist::set_state (node, version);
627 /* Read legacy Crossfade nodes and set up region fades accordingly */
629 XMLNodeList children = node.children ();
630 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
631 if ((*i)->name() == X_("Crossfade")) {
633 XMLProperty* p = (*i)->property (X_("active"));
635 if (!string_is_affirmative (p->value())) {
639 p = (*i)->property (X_("in"));
641 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
643 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
646 p = (*i)->property (X_("out"));
648 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
650 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
653 XMLNodeList c = (*i)->children ();
654 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
655 if ((*j)->name() == X_("FadeIn")) {
656 in_a->fade_in()->set_state (**j, version);
657 in_a->set_fade_in_active (true);
658 } else if ((*j)->name() == X_("FadeOut")) {
659 out_a->fade_out()->set_state (**j, version);
660 out_a->set_fade_out_active (true);