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/crossfade.h"
30 #include "ardour/crossfade_compare.h"
31 #include "ardour/session.h"
32 #include "pbd/enumwriter.h"
36 using namespace ARDOUR;
41 namespace Properties {
42 PBD::PropertyDescriptor<bool> crossfades;
47 AudioPlaylist::make_property_quarks ()
49 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
50 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
53 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
54 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
60 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
61 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
62 , _playlist (p._playlist)
68 CrossfadeListProperty *
69 CrossfadeListProperty::create () const
71 return new CrossfadeListProperty (_playlist);
74 CrossfadeListProperty *
75 CrossfadeListProperty::clone () const
77 return new CrossfadeListProperty (*this);
81 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
83 /* Crossfades are not written to any state when they are no
84 longer in use, so we must write their state here.
87 XMLNode& c = xfade->get_state ();
88 node.add_child_nocopy (c);
91 boost::shared_ptr<Crossfade>
92 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
94 XMLNodeList const c = node.children ();
95 assert (c.size() == 1);
96 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
100 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
101 : Playlist (session, node, DataType::AUDIO, hidden)
102 , _crossfades (*this)
105 const XMLProperty* prop = node.property("type");
106 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
109 add_property (_crossfades);
112 set_state (node, Stateful::loading_state_version);
116 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
117 : Playlist (session, name, DataType::AUDIO, hidden)
118 , _crossfades (*this)
120 add_property (_crossfades);
123 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
124 : Playlist (other, name, hidden)
125 , _crossfades (*this)
127 add_property (_crossfades);
129 RegionList::const_iterator in_o = other->regions.begin();
130 RegionList::iterator in_n = regions.begin();
132 while (in_o != other->regions.end()) {
133 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
135 // We look only for crossfades which begin with the current region, so we don't get doubles
136 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
137 if ((*xfades)->in() == ar) {
138 // We found one! Now copy it!
140 RegionList::const_iterator out_o = other->regions.begin();
141 RegionList::const_iterator out_n = regions.begin();
143 while (out_o != other->regions.end()) {
145 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
147 if ((*xfades)->out() == ar2) {
148 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
149 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
150 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
151 add_crossfade(new_fade);
158 // cerr << "HUH!? second region in the crossfade not found!" << endl;
167 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
168 : Playlist (other, start, cnt, name, hidden)
169 , _crossfades (*this)
171 add_property (_crossfades);
173 /* this constructor does NOT notify others (session) */
176 AudioPlaylist::~AudioPlaylist ()
178 _crossfades.clear ();
181 struct RegionSortByLayer {
182 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
183 return a->layer() < b->layer();
188 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
189 framecnt_t cnt, unsigned chan_n)
191 framecnt_t ret = cnt;
193 /* optimizing this memset() away involves a lot of conditionals
194 that may well cause more of a hit due to cache misses
195 and related stuff than just doing this here.
197 it would be great if someone could measure this
200 one way or another, parts of the requested area
201 that are not written to by Region::region_at()
202 for all Regions that cover the area need to be
206 memset (buf, 0, sizeof (Sample) * cnt);
208 /* this function is never called from a realtime thread, so
209 its OK to block (for short intervals).
212 Glib::RecMutex::Lock rm (region_lock);
214 framepos_t const end = start + cnt - 1;
215 framecnt_t read_frames = 0;
216 framecnt_t skip_frames = 0;
217 _read_data_count = 0;
219 _read_data_count = 0;
221 RegionList* rlist = regions_to_read (start, start+cnt);
223 if (rlist->empty()) {
228 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
229 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
230 vector<uint32_t> relevant_layers;
232 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
233 if ((*i)->coverage (start, end) != OverlapNone) {
234 relevant_regions[(*i)->layer()].push_back (*i);
235 relevant_layers.push_back ((*i)->layer());
239 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
240 if ((*i)->coverage (start, end) != OverlapNone) {
241 relevant_xfades[(*i)->upper_layer()].push_back (*i);
245 // RegionSortByLayer layer_cmp;
246 // relevant_regions.sort (layer_cmp);
248 /* XXX this whole per-layer approach is a hack that
249 should be removed once Crossfades become
250 CrossfadeRegions and we just grab a list of relevant
251 regions and call read_at() on all of them.
254 sort (relevant_layers.begin(), relevant_layers.end());
256 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
258 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
259 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
262 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
263 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
264 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
266 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
267 _read_data_count += ar->read_data_count();
270 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
271 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
273 /* don't JACK up _read_data_count, since its the same data as we just
274 read from the regions, and the OS should handle that for us.
285 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
287 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
294 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
299 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
301 if ((*i)->involves (r)) {
302 i = _crossfades.erase (i);
311 AudioPlaylist::flush_notifications (bool from_undo)
313 Playlist::flush_notifications (from_undo);
321 Crossfades::iterator a;
322 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
323 NewCrossfade (*a); /* EMIT SIGNAL */
326 _pending_xfade_adds.clear ();
332 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
334 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
335 set<boost::shared_ptr<Crossfade> > updated;
341 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
343 Crossfades::iterator tmp;
348 /* only update them once */
350 if ((*x)->involves (ar)) {
352 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
355 /* x was successfully inserted into the set, so it has not already been updated */
360 catch (Crossfade::NoCrossfadeHere& err) {
361 // relax, Invalidated during refresh
371 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
373 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
374 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
375 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
377 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
378 Crossfades::iterator tmp;
382 boost::shared_ptr<Crossfade> fade;
384 if ((*x)->_in == orig) {
385 if (! (*x)->covers(right->position())) {
386 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
388 // Overlap, the crossfade is copied on the left side of the right region instead
389 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
393 if ((*x)->_out == orig) {
394 if (! (*x)->covers(right->position())) {
395 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
397 // Overlap, the crossfade is copied on the right side of the left region instead
398 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
403 _crossfades.remove (*x);
404 add_crossfade (fade);
411 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
413 boost::shared_ptr<AudioRegion> other;
414 boost::shared_ptr<AudioRegion> region;
415 boost::shared_ptr<AudioRegion> top;
416 boost::shared_ptr<AudioRegion> bottom;
417 boost::shared_ptr<Crossfade> xfade;
418 RegionList* touched_regions = 0;
420 if (in_set_state || in_partition) {
424 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
425 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
431 refresh_dependents (r);
435 if (!_session.config.get_auto_xfade()) {
439 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
440 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
442 if (other == region) {
446 if (other->muted() || region->muted()) {
451 if (other->layer() < region->layer()) {
459 if (!top->opaque()) {
463 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
465 delete touched_regions;
469 framecnt_t xfade_length;
474 case OverlapInternal:
475 /* {=============== top =============}
476 * [ ----- bottom ------- ]
480 case OverlapExternal:
482 /* [ -------- top ------- ]
483 * {=========== bottom =============}
486 /* to avoid discontinuities at the region boundaries of an internal
487 overlap (this region is completely within another), we create
488 two hidden crossfades at each boundary. this is not dependent
489 on the auto-xfade option, because we require it as basic
493 xfade_length = min ((framecnt_t) 720, top->length());
495 if (top_region_at (top->first_frame()) == top) {
497 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
498 add_crossfade (xfade);
501 if (top_region_at (top->last_frame() - 1) == top) {
504 only add a fade out if there is no region on top of the end of 'top' (which
508 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
509 add_crossfade (xfade);
514 /* { ==== top ============ }
515 * [---- bottom -------------------]
518 if (_session.config.get_xfade_model() == FullCrossfade) {
519 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
520 if (touched_regions->size() <= 2) {
521 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
522 add_crossfade (xfade);
526 touched_regions = regions_touched (top->first_frame(),
527 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
529 if (touched_regions->size() <= 2) {
530 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
531 add_crossfade (xfade);
538 /* [---- top ------------------------]
539 * { ==== bottom ============ }
542 if (_session.config.get_xfade_model() == FullCrossfade) {
544 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
545 if (touched_regions->size() <= 2) {
546 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
547 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
548 add_crossfade (xfade);
552 touched_regions = regions_touched (bottom->first_frame(),
553 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
555 if (touched_regions->size() <= 2) {
556 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
557 add_crossfade (xfade);
562 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
563 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
564 add_crossfade (xfade);
568 catch (failed_constructor& err) {
572 catch (Crossfade::NoCrossfadeHere& err) {
578 delete touched_regions;
582 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
584 Crossfades::iterator ci;
586 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
587 if (*(*ci) == *xfade) { // Crossfade::operator==()
592 if (ci != _crossfades.end()) {
593 // it will just go away
595 _crossfades.push_back (xfade);
597 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
598 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
600 notify_crossfade_added (xfade);
604 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
606 if (g_atomic_int_get(&block_notifications)) {
607 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
609 NewCrossfade (x); /* EMIT SIGNAL */
614 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
616 Crossfades::iterator i;
617 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
619 xfade->in()->resume_fade_in ();
620 xfade->out()->resume_fade_out ();
622 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
623 _crossfades.erase (i);
628 AudioPlaylist::set_state (const XMLNode& node, int version)
632 XMLNodeConstIterator niter;
636 Playlist::set_state (node, version);
640 nlist = node.children();
642 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
646 if (child->name() != "Crossfade") {
651 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
652 _crossfades.push_back (xfade);
653 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
654 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
658 catch (failed_constructor& err) {
659 // cout << string_compose (_("could not create crossfade object in playlist %1"),
673 AudioPlaylist::clear (bool with_signals)
675 _crossfades.clear ();
676 Playlist::clear (with_signals);
680 AudioPlaylist::state (bool full_state)
682 XMLNode& node = Playlist::state (full_state);
685 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
686 node.add_child_nocopy ((*i)->get_state());
694 AudioPlaylist::dump () const
696 boost::shared_ptr<Region>r;
697 boost::shared_ptr<Crossfade> x;
699 cerr << "Playlist \"" << _name << "\" " << endl
700 << regions.size() << " regions "
701 << _crossfades.size() << " crossfades"
704 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
706 cerr << " " << r->name() << " @ " << r << " ["
707 << r->start() << "+" << r->length()
715 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
726 << (x->active() ? "yes" : "no")
732 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
734 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
740 bool changed = false;
741 Crossfades::iterator c, ctmp;
742 set<boost::shared_ptr<Crossfade> > unique_xfades;
745 RegionLock rlock (this);
747 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
749 RegionList::iterator tmp = i;
752 if ((*i) == region) {
760 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
762 set<boost::shared_ptr<Region> >::iterator xtmp = x;
765 if ((*x) == region) {
766 all_regions.erase (x);
773 region->set_playlist (boost::shared_ptr<Playlist>());
776 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
780 if ((*c)->involves (r)) {
781 unique_xfades.insert (*c);
782 _crossfades.erase (c);
789 /* overload this, it normally means "removed", not destroyed */
790 notify_region_removed (region);
797 AudioPlaylist::crossfade_changed (const PropertyChange&)
799 if (in_flush || in_set_state) {
803 /* XXX is there a loop here? can an xfade change not happen
804 due to a playlist change? well, sure activation would
805 be an example. maybe we should check the type of change
809 notify_contents_changed ();
813 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
815 if (in_flush || in_set_state) {
819 PropertyChange our_interests;
821 our_interests.add (Properties::fade_in_active);
822 our_interests.add (Properties::fade_out_active);
823 our_interests.add (Properties::scale_amplitude);
824 our_interests.add (Properties::envelope_active);
825 our_interests.add (Properties::envelope);
826 our_interests.add (Properties::fade_in);
827 our_interests.add (Properties::fade_out);
829 bool parent_wants_notify;
831 parent_wants_notify = Playlist::region_changed (what_changed, region);
833 if (parent_wants_notify || (what_changed.contains (our_interests))) {
834 notify_contents_changed ();
841 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
843 RegionLock rlock (this);
845 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
846 framepos_t const start = (*i)->position ();
847 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
849 if (frame >= start && frame <= end) {
850 clist.push_back (*i);
856 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
858 RegionLock rl (this, false);
859 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
865 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
867 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
871 /* don't remove crossfades here; they will be dealt with by the dependency code */
874 boost::shared_ptr<Crossfade>
875 AudioPlaylist::find_crossfade (const PBD::ID& id) const
877 Crossfades::const_iterator i = _crossfades.begin ();
878 while (i != _crossfades.end() && (*i)->id() != id) {
882 if (i == _crossfades.end()) {
883 return boost::shared_ptr<Crossfade> ();