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();
153 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
154 framecnt_t cnt, unsigned chan_n)
156 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
157 name(), start, cnt, chan_n, regions.size()));
159 /* optimizing this memset() away involves a lot of conditionals
160 that may well cause more of a hit due to cache misses
161 and related stuff than just doing this here.
163 it would be great if someone could measure this
166 one way or another, parts of the requested area
167 that are not written to by Region::region_at()
168 for all Regions that cover the area need to be
172 memset (buf, 0, sizeof (Sample) * cnt);
174 /* this function is never called from a realtime thread, so
175 its OK to block (for short intervals).
178 Glib::RecMutex::Lock rm (region_lock);
180 /* Find all the regions that are involved in the bit we are reading,
181 and sort them by descending layer and ascending position.
183 boost::shared_ptr<RegionList> all = regions_touched (start, start + cnt - 1);
184 all->sort (ReadSorter ());
186 /* This will be a list of the bits of our read range that we have
187 read completely (ie for which no more regions need to be read).
188 It is a list of ranges in session frames.
190 Evoral::RangeList<framepos_t> done;
192 for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
193 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
195 /* Trim region range to the bit we are reading */
197 /* Work out which bits of this region need to be read;
198 first, trim to the range we are reading...
200 Evoral::Range<framepos_t> region_range = ar->range ();
201 region_range.from = max (region_range.from, start);
202 region_range.to = min (region_range.to, start + cnt - 1);
204 /* ... and then remove the bits that are already done */
205 Evoral::RangeList<framepos_t> to_do = Evoral::subtract (region_range, done);
207 /* Read those bits, adding their bodies (the parts between end-of-fade-in
208 and start-of-fade-out) to the `done' list.
211 Evoral::RangeList<framepos_t>::List t = to_do.get ();
213 for (Evoral::RangeList<framepos_t>::List::iterator i = t.begin(); i != t.end(); ++i) {
214 Evoral::Range<framepos_t> d = *i;
216 /* Read the whole range, possibly including fades */
217 ar->read_at (buf + d.from - start, mixdown_buffer, gain_buffer, d.from, d.to - d.from + 1, chan_n);
220 /* Cut this range down to just the body and mark it done */
221 Evoral::Range<framepos_t> body = ar->body_range ();
222 if (body.from < d.to && body.to > d.from) {
223 d.from = max (d.from, body.from);
224 d.to = min (d.to, body.to);
235 AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
237 if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
241 boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
242 boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
244 RegionList all = *starts;
245 std::copy (ends->begin(), ends->end(), back_inserter (all));
247 all.sort (RegionSortByLayer ());
249 set<boost::shared_ptr<Region> > done_start;
250 set<boost::shared_ptr<Region> > done_end;
252 for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
253 for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
259 if ((*i)->muted() || (*j)->muted()) {
263 if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
264 /* precise overlay: no xfade */
268 if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
269 /* starts or ends match: no xfade */
274 boost::shared_ptr<AudioRegion> top;
275 boost::shared_ptr<AudioRegion> bottom;
277 if ((*i)->layer() < (*j)->layer()) {
278 top = boost::dynamic_pointer_cast<AudioRegion> (*j);
279 bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
281 top = boost::dynamic_pointer_cast<AudioRegion> (*i);
282 bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
285 if (!top->opaque ()) {
289 Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
291 if (c == Evoral::OverlapStart) {
293 /* top starts within bottom but covers bottom's end */
295 /* { ==== top ============ }
296 * [---- bottom -------------------]
299 if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
300 framecnt_t const len = bottom->last_frame () - top->first_frame ();
301 top->set_fade_in_length (len);
302 top->set_fade_in_active (true);
303 done_start.insert (top);
304 bottom->set_fade_out_length (len);
305 bottom->set_fade_out_active (true);
306 done_end.insert (bottom);
309 } else if (c == Evoral::OverlapEnd) {
311 /* top covers start of bottom but ends within it */
313 /* [---- top ------------------------]
314 * { ==== bottom ============ }
317 if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
318 framecnt_t const len = top->last_frame () - bottom->first_frame ();
319 top->set_fade_out_length (len);
320 top->set_fade_out_active (true);
321 done_end.insert (top);
322 bottom->set_fade_in_length (len);
323 bottom->set_fade_in_active (true);
324 done_start.insert (bottom);
330 for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
331 if (done_start.find (*i) == done_start.end()) {
332 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
333 r->set_default_fade_in ();
337 for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
338 if (done_end.find (*i) == done_end.end()) {
339 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
340 r->set_default_fade_out ();
346 AudioPlaylist::dump () const
348 boost::shared_ptr<Region>r;
350 cerr << "Playlist \"" << _name << "\" " << endl
351 << regions.size() << " regions "
354 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
356 cerr << " " << r->name() << " @ " << r << " ["
357 << r->start() << "+" << r->length()
367 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
369 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
375 bool changed = false;
378 RegionLock rlock (this);
380 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
382 RegionList::iterator tmp = i;
385 if ((*i) == region) {
393 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
395 set<boost::shared_ptr<Region> >::iterator xtmp = x;
398 if ((*x) == region) {
399 all_regions.erase (x);
406 region->set_playlist (boost::shared_ptr<Playlist>());
410 /* overload this, it normally means "removed", not destroyed */
411 notify_region_removed (region);
418 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
420 if (in_flush || in_set_state) {
424 PropertyChange our_interests;
426 our_interests.add (Properties::fade_in_active);
427 our_interests.add (Properties::fade_out_active);
428 our_interests.add (Properties::scale_amplitude);
429 our_interests.add (Properties::envelope_active);
430 our_interests.add (Properties::envelope);
431 our_interests.add (Properties::fade_in);
432 our_interests.add (Properties::fade_out);
434 bool parent_wants_notify;
436 parent_wants_notify = Playlist::region_changed (what_changed, region);
438 if (parent_wants_notify || (what_changed.contains (our_interests))) {
439 notify_contents_changed ();
446 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
448 RegionSortByPosition cmp;
449 boost::shared_ptr<AudioRegion> ar;
451 sort (copies.begin(), copies.end(), cmp);
453 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
455 /* disable fade in of the first region */
458 ar->set_fade_in_active (false);
461 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
463 /* disable fade out of the last region */
466 ar->set_fade_out_active (false);
471 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
473 RegionSortByPosition cmp;
474 boost::shared_ptr<AudioRegion> ar;
475 boost::shared_ptr<AudioRegion> cr;
477 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
481 sort (originals.begin(), originals.end(), cmp);
483 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
485 /* copy the fade in of the first into the compound region */
488 cr->set_fade_in (ar->fade_in());
491 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
494 /* copy the fade out of the last into the compound region */
495 cr->set_fade_out (ar->fade_out());
500 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
502 RegionSortByPosition cmp;
503 boost::shared_ptr<AudioRegion> ar;
504 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
510 sort (originals.begin(), originals.end(), cmp);
512 /* no need to call clear_changes() on the originals because that is
513 * done within Playlist::uncombine ()
516 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
518 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
522 /* scale the uncombined regions by any gain setting for the
526 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
528 if (i == originals.begin()) {
530 /* copy the compound region's fade in back into the first
534 if (cr->fade_in()->back()->when <= ar->length()) {
535 /* don't do this if the fade is longer than the
538 ar->set_fade_in (cr->fade_in());
542 } else if (*i == originals.back()) {
544 /* copy the compound region's fade out back into the last
548 if (cr->fade_out()->back()->when <= ar->length()) {
549 /* don't do this if the fade is longer than the
552 ar->set_fade_out (cr->fade_out());
557 _session.add_command (new StatefulDiffCommand (*i));
562 AudioPlaylist::set_state (const XMLNode& node, int version)
564 int const r = Playlist::set_state (node, version);
569 /* Read legacy Crossfade nodes and set up region fades accordingly */
571 XMLNodeList children = node.children ();
572 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
573 if ((*i)->name() == X_("Crossfade")) {
575 XMLProperty* p = (*i)->property (X_("active"));
577 if (!string_is_affirmative (p->value())) {
581 p = (*i)->property (X_("in"));
583 boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
585 boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
588 p = (*i)->property (X_("out"));
590 boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
592 boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
595 XMLNodeList c = (*i)->children ();
596 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
597 if ((*j)->name() == X_("FadeIn")) {
598 in_a->fade_in()->set_state (**j, version);
599 in_a->set_fade_in_active (true);
600 } else if ((*j)->name() == X_("FadeOut")) {
601 out_a->fade_out()->set_state (**j, version);
602 out_a->set_fade_out_active (true);