X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudio_playlist.cc;h=cb65164a9b03f2adb4be7032798227d72948e8fe;hb=f188ffffc18687c47d1d5a5c8b35bda4d7b75f5f;hp=328c9b25f5e2b75c32ce79ff36add52f4e956d0e;hpb=7d2b7e72fd72379aa379d568032bf0fa251a7a85;p=ardour.git diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 328c9b25f5..cb65164a9b 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2003 Paul Davis + Copyright (C) 2003 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,90 +15,76 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "ardour/types.h" +#include "ardour/debug.h" +#include "ardour/configuration.h" +#include "ardour/audioplaylist.h" +#include "ardour/audioregion.h" +#include "ardour/crossfade.h" +#include "ardour/crossfade_compare.h" +#include "ardour/session.h" +#include "pbd/enumwriter.h" #include "i18n.h" using namespace ARDOUR; -using namespace sigc; using namespace std; using namespace PBD; -AudioPlaylist::State::~State () -{ -} - AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden) - : Playlist (session, node, hidden) + : Playlist (session, node, DataType::AUDIO, hidden) { - in_set_state = true; - set_state (node); - in_set_state = false; - - save_state (_("initial state")); - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } +#ifndef NDEBUG + const XMLProperty* prop = node.property("type"); + assert(!prop || DataType(prop->value()) == DataType::AUDIO); +#endif + + in_set_state++; + set_state (node, Stateful::loading_state_version); + in_set_state--; } AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) - : Playlist (session, name, hidden) + : Playlist (session, name, DataType::AUDIO, hidden) { - save_state (_("initial state")); - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } - } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr other, string name, bool hidden) : Playlist (other, name, hidden) { - save_state (_("initial state")); + RegionList::const_iterator in_o = other->regions.begin(); + RegionList::iterator in_n = regions.begin(); - list::const_iterator in_o = other.regions.begin(); - list::iterator in_n = regions.begin(); - - while (in_o != other.regions.end()) { - AudioRegion *ar = dynamic_cast( (*in_o) ); + while (in_o != other->regions.end()) { + boost::shared_ptr ar = boost::dynamic_pointer_cast(*in_o); // We look only for crossfades which begin with the current region, so we don't get doubles - for (list::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) { - if ( &(*xfades)->in() == ar) { + for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) { + if ((*xfades)->in() == ar) { // We found one! Now copy it! - list::const_iterator out_o = other.regions.begin(); - list::const_iterator out_n = regions.begin(); - - while (out_o != other.regions.end()) { - - AudioRegion *ar2 = dynamic_cast( (*out_o) ); - - if ( &(*xfades)->out() == ar2) { - AudioRegion *in = dynamic_cast( (*in_n) ); - AudioRegion *out = dynamic_cast( (*out_n) ); - Crossfade *new_fade = new Crossfade( *(*xfades), in, out); - add_crossfade(*new_fade); + RegionList::const_iterator out_o = other->regions.begin(); + RegionList::const_iterator out_n = regions.begin(); + + while (out_o != other->regions.end()) { + + boost::shared_ptrar2 = boost::dynamic_pointer_cast(*out_o); + + if ((*xfades)->out() == ar2) { + boost::shared_ptrin = boost::dynamic_pointer_cast(*in_n); + boost::shared_ptrout = boost::dynamic_pointer_cast(*out_n); + boost::shared_ptr new_fade = boost::shared_ptr (new Crossfade (*xfades, in, out)); + add_crossfade(new_fade); break; } - + out_o++; out_n++; } @@ -109,86 +95,38 @@ AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidd in_o++; in_n++; } - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr other, nframes_t start, nframes_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) { - save_state (_("initial state")); - /* this constructor does NOT notify others (session) */ } AudioPlaylist::~AudioPlaylist () { - set all_xfades; - set all_regions; - - GoingAway (this); - - /* find every region we've ever used, and add it to the set of - all regions. same for xfades; - */ - - for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) { - all_regions.insert (*x); - } - - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ++x) { - all_xfades.insert (*x); - } - - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - - AudioPlaylist::State* apstate = dynamic_cast (*i); - - for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) { - all_regions.insert (*r); - } - for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) { - all_xfades.insert (*xf); - } - - delete apstate; - } - - /* delete every region */ - - for (set::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { - (*ar)->unlock_sources (); - delete *ar; - } - - /* delete every crossfade */ - - for (set::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) { - delete *axf; - } + _crossfades.clear (); } struct RegionSortByLayer { - bool operator() (Region *a, Region *b) { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { return a->layer() < b->layer(); } }; -jack_nframes_t -AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, jack_nframes_t start, - jack_nframes_t cnt, unsigned chan_n) +ARDOUR::nframes_t +AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start, + nframes_t cnt, unsigned chan_n) { - jack_nframes_t ret = cnt; - jack_nframes_t end; - jack_nframes_t read_frames; - jack_nframes_t skip_frames; + nframes_t ret = cnt; + nframes_t end; + nframes_t read_frames; + nframes_t skip_frames; /* optimizing this memset() away involves a lot of conditionals - that may well cause more of a hit due to cache misses + that may well cause more of a hit due to cache misses and related stuff than just doing this here. - + it would be great if someone could measure this at some point. @@ -200,28 +138,35 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja memset (buf, 0, sizeof (Sample) * cnt); - /* this function is never called from a realtime thread, so + /* this function is never called from a realtime thread, so its OK to block (for short intervals). */ - Glib::Mutex::Lock rm (region_lock); + Glib::RecMutex::Lock rm (region_lock); end = start + cnt - 1; - read_frames = 0; skip_frames = 0; _read_data_count = 0; - map > relevant_regions; - map > relevant_xfades; + _read_data_count = 0; + + RegionList* rlist = regions_to_read (start, start+cnt); + + if (rlist->empty()) { + delete rlist; + return cnt; + } + + map > > relevant_regions; + map > > relevant_xfades; vector relevant_layers; - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) { if ((*i)->coverage (start, end) != OverlapNone) { - relevant_regions[(*i)->layer()].push_back (*i); relevant_layers.push_back ((*i)->layer()); - } + } } for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { @@ -243,18 +188,19 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { - // FIXME: Should be vector - vector& r (relevant_regions[*l]); - vector& x (relevant_xfades[*l]); + vector > r (relevant_regions[*l]); + vector >& x (relevant_xfades[*l]); - for (vector::iterator i = r.begin(); i != r.end(); ++i) { - AudioRegion* const ar = dynamic_cast(*i); + + for (vector >::iterator i = r.begin(); i != r.end(); ++i) { + boost::shared_ptr ar = boost::dynamic_pointer_cast(*i); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name())); assert(ar); ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames); _read_data_count += ar->read_data_count(); } - - for (vector::iterator i = x.begin(); i != x.end(); ++i) { + + for (vector >::iterator i = x.begin(); i != x.end(); ++i) { (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); /* don't JACK up _read_data_count, since its the same data as we just @@ -263,32 +209,33 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja } } + delete rlist; return ret; } void -AudioPlaylist::remove_dependents (Region& region) +AudioPlaylist::remove_dependents (boost::shared_ptr region) { - Crossfades::iterator i, tmp; - AudioRegion* r = dynamic_cast (®ion); - + boost::shared_ptr r = boost::dynamic_pointer_cast (region); + + if (in_set_state) { + return; + } + if (r == 0) { fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") << endmsg; return; } - for (i = _crossfades.begin(); i != _crossfades.end(); ) { - tmp = i; - tmp++; + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { - if ((*i)->involves (*r)) { - /* do not delete crossfades */ - _crossfades.erase (i); + if ((*i)->involves (r)) { + i = _crossfades.erase (i); + } else { + ++i; } - - i = tmp; } } @@ -310,15 +257,15 @@ AudioPlaylist::flush_notifications () } _pending_xfade_adds.clear (); - + in_flush = false; } void -AudioPlaylist::refresh_dependents (Region& r) +AudioPlaylist::refresh_dependents (boost::shared_ptr r) { - AudioRegion* ar = dynamic_cast(&r); - set updated; + boost::shared_ptr ar = boost::dynamic_pointer_cast(r); + set > updated; if (ar == 0) { return; @@ -327,18 +274,24 @@ AudioPlaylist::refresh_dependents (Region& r) for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { Crossfades::iterator tmp; - + tmp = x; ++tmp; /* only update them once */ - if ((*x)->involves (*ar)) { + if ((*x)->involves (ar)) { + + pair >::iterator, bool> const u = updated.insert (*x); - if (find (updated.begin(), updated.end(), *x) == updated.end()) { - if ((*x)->refresh ()) { - /* not invalidated by the refresh */ - updated.insert (*x); + if (u.second) { + /* x was successfully inserted into the set, so it has not already been updated */ + try { + (*x)->refresh (); + } + + catch (Crossfade::NoCrossfadeHere& err) { + // relax, Invalidated during refresh } } } @@ -348,59 +301,60 @@ AudioPlaylist::refresh_dependents (Region& r) } void -AudioPlaylist::finalize_split_region (Region *o, Region *l, Region *r) +AudioPlaylist::finalize_split_region (boost::shared_ptr o, boost::shared_ptr l, boost::shared_ptr r) { - AudioRegion *orig = dynamic_cast(o); - AudioRegion *left = dynamic_cast(l); - AudioRegion *right = dynamic_cast(r); + boost::shared_ptr orig = boost::dynamic_pointer_cast(o); + boost::shared_ptr left = boost::dynamic_pointer_cast(l); + boost::shared_ptr right = boost::dynamic_pointer_cast(r); for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { Crossfades::iterator tmp; tmp = x; ++tmp; - Crossfade *fade = 0; - + boost::shared_ptr fade; + if ((*x)->_in == orig) { if (! (*x)->covers(right->position())) { - fade = new Crossfade( *(*x), left, (*x)->_out); + fade = boost::shared_ptr (new Crossfade (*x, left, (*x)->_out)); } else { // Overlap, the crossfade is copied on the left side of the right region instead - fade = new Crossfade( *(*x), right, (*x)->_out); + fade = boost::shared_ptr (new Crossfade (*x, right, (*x)->_out)); } } - + if ((*x)->_out == orig) { if (! (*x)->covers(right->position())) { - fade = new Crossfade( *(*x), (*x)->_in, right); + fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, right)); } else { // Overlap, the crossfade is copied on the right side of the left region instead - fade = new Crossfade( *(*x), (*x)->_in, left); + fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, left)); } } - + if (fade) { - _crossfades.remove( (*x) ); - add_crossfade (*fade); + _crossfades.remove (*x); + add_crossfade (fade); } x = tmp; } } void -AudioPlaylist::check_dependents (Region& r, bool norefresh) +AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) { - AudioRegion* other; - AudioRegion* region; - AudioRegion* top; - AudioRegion* bottom; - Crossfade* xfade; + boost::shared_ptr other; + boost::shared_ptr region; + boost::shared_ptr top; + boost::shared_ptr bottom; + boost::shared_ptr xfade; + RegionList* touched_regions = 0; if (in_set_state || in_partition) { return; } - if ((region = dynamic_cast (&r)) == 0) { + if ((region = boost::dynamic_pointer_cast (r)) == 0) { fatal << _("programming error: non-audio Region tested for overlap in audio playlist") << endmsg; return; @@ -410,13 +364,13 @@ AudioPlaylist::check_dependents (Region& r, bool norefresh) refresh_dependents (r); } - if (!Config->get_auto_xfade()) { + + if (!_session.config.get_auto_xfade()) { return; } for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - other = dynamic_cast (*i); + other = boost::dynamic_pointer_cast (*i); if (other == region) { continue; @@ -426,6 +380,7 @@ AudioPlaylist::check_dependents (Region& r, bool norefresh) continue; } + if (other->layer() < region->layer()) { top = region; bottom = other; @@ -434,78 +389,152 @@ AudioPlaylist::check_dependents (Region& r, bool norefresh) bottom = region; } + if (!top->opaque()) { + continue; + } + + OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); + + delete touched_regions; + touched_regions = 0; + try { - - if (top->coverage (bottom->position(), bottom->last_frame()) != OverlapNone) { - - /* check if the upper region is within the lower region */ - - if (top->first_frame() > bottom->first_frame() && - top->last_frame() < bottom->last_frame()) { - - - /* [ -------- top ------- ] - * {=========== bottom =============} - */ - - /* to avoid discontinuities at the region boundaries of an internal - overlap (this region is completely within another), we create - two hidden crossfades at each boundary. this is not dependent - on the auto-xfade option, because we require it as basic - audio engineering. + nframes_t xfade_length; + switch (c) { + case OverlapNone: + break; + + case OverlapInternal: + /* {=============== top =============} + * [ ----- bottom ------- ] + */ + break; + + case OverlapExternal: + + /* [ -------- top ------- ] + * {=========== bottom =============} + */ + + /* to avoid discontinuities at the region boundaries of an internal + overlap (this region is completely within another), we create + two hidden crossfades at each boundary. this is not dependent + on the auto-xfade option, because we require it as basic + audio engineering. + */ + + xfade_length = min ((framecnt_t) 720, top->length()); + + if (top_region_at (top->first_frame()) == top) { + + xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn)); + add_crossfade (xfade); + } + + if (top_region_at (top->last_frame() - 1) == top) { + + /* + only add a fade out if there is no region on top of the end of 'top' (which + would cover it). */ - - jack_nframes_t xfade_length = min ((jack_nframes_t) 720, top->length()); - - /* in, out */ - xfade = new Crossfade (*top, *bottom, xfade_length, top->first_frame(), StartOfIn); - add_crossfade (*xfade); - xfade = new Crossfade (*bottom, *top, xfade_length, top->last_frame() - xfade_length, EndOfOut); - add_crossfade (*xfade); - + + xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut)); + add_crossfade (xfade); + } + break; + case OverlapStart: + + /* { ==== top ============ } + * [---- bottom -------------------] + */ + + if (_session.config.get_xfade_model() == FullCrossfade) { + touched_regions = regions_touched (top->first_frame(), bottom->last_frame()); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } else { - - xfade = new Crossfade (*other, *region, _session.get_xfade_model(), _session.get_crossfades_active()); - add_crossfade (*xfade); + + touched_regions = regions_touched (top->first_frame(), + top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(), + top->length())); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } - } + break; + case OverlapEnd: + + + /* [---- top ------------------------] + * { ==== bottom ============ } + */ + + if (_session.config.get_xfade_model() == FullCrossfade) { + + touched_regions = regions_touched (bottom->first_frame(), top->last_frame()); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, + _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } + + } else { + touched_regions = regions_touched (bottom->first_frame(), + bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(), + bottom->length())); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } + } + break; + default: + xfade = boost::shared_ptr (new Crossfade (region, other, + _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } - + catch (failed_constructor& err) { continue; } - + catch (Crossfade::NoCrossfadeHere& err) { continue; } - + } + + delete touched_regions; } void -AudioPlaylist::add_crossfade (Crossfade& xfade) +AudioPlaylist::add_crossfade (boost::shared_ptr xfade) { Crossfades::iterator ci; for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { - if (*(*ci) == xfade) { // Crossfade::operator==() + if (*(*ci) == *xfade) { // Crossfade::operator==() break; } } - + if (ci != _crossfades.end()) { - delete &xfade; + // it will just go away } else { - _crossfades.push_back (&xfade); + _crossfades.push_back (xfade); - xfade.Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - xfade.StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed)); + xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); + xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); - notify_crossfade_added (&xfade); + notify_crossfade_added (xfade); } } - -void AudioPlaylist::notify_crossfade_added (Crossfade *x) + +void AudioPlaylist::notify_crossfade_added (boost::shared_ptr x) { if (g_atomic_int_get(&block_notifications)) { _pending_xfade_adds.insert (_pending_xfade_adds.end(), x); @@ -515,12 +544,13 @@ void AudioPlaylist::notify_crossfade_added (Crossfade *x) } void -AudioPlaylist::crossfade_invalidated (Crossfade* xfade) +AudioPlaylist::crossfade_invalidated (boost::shared_ptr r) { Crossfades::iterator i; + boost::shared_ptr xfade = boost::dynamic_pointer_cast (r); - xfade->in().resume_fade_in (); - xfade->out().resume_fade_out (); + xfade->in()->resume_fade_in (); + xfade->out()->resume_fade_out (); if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) { _crossfades.erase (i); @@ -528,15 +558,17 @@ AudioPlaylist::crossfade_invalidated (Crossfade* xfade) } int -AudioPlaylist::set_state (const XMLNode& node) +AudioPlaylist::set_state (const XMLNode& node, int version) { XMLNode *child; XMLNodeList nlist; XMLNodeConstIterator niter; - if (!in_set_state) { - Playlist::set_state (node); - } + in_set_state++; + + Playlist::set_state (node, version); + + freeze (); nlist = node.children(); @@ -544,167 +576,37 @@ AudioPlaylist::set_state (const XMLNode& node) child = *niter; - if (child->name() == "Crossfade") { - - Crossfade *xfade; - - try { - xfade = new Crossfade (*((const Playlist *)this), *child); - } - - catch (failed_constructor& err) { - // cout << string_compose (_("could not create crossfade object in playlist %1"), - // _name) - // << endl; - continue; - } - - Crossfades::iterator ci; - - for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { - if (*(*ci) == *xfade) { - break; - } - } - - if (ci == _crossfades.end()) { - _crossfades.push_back (xfade); - xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed)); - /* no need to notify here */ - } else { - delete xfade; - } - } - - } - - return 0; -} - -void -AudioPlaylist::drop_all_states () -{ - set all_xfades; - set all_regions; - - /* find every region we've ever used, and add it to the set of - all regions. same for xfades; - */ - - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - - AudioPlaylist::State* apstate = dynamic_cast (*i); - - for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) { - all_regions.insert (*r); - } - for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) { - all_xfades.insert (*xf); - } - } - - /* now remove from the "all" lists every region that is in the current list. */ - - for (list::iterator i = regions.begin(); i != regions.end(); ++i) { - set::iterator x = all_regions.find (*i); - if (x != all_regions.end()) { - all_regions.erase (x); - } - } - - /* ditto for every crossfade */ - - for (list::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - set::iterator x = all_xfades.find (*i); - if (x != all_xfades.end()) { - all_xfades.erase (x); + if (child->name() != "Crossfade") { + continue; } - } - - /* delete every region that is left - these are all things that are part of our "history" */ - for (set::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { - (*ar)->unlock_sources (); - delete *ar; - } - - /* delete every crossfade that is left (ditto as per regions) */ - - for (set::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) { - delete *axf; - } - - /* Now do the generic thing ... */ - - StateManager::drop_all_states (); -} - -StateManager::State* -AudioPlaylist::state_factory (std::string why) const -{ - State* state = new State (why); - - state->regions = regions; - state->region_states.clear (); - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - state->region_states.push_back ((*i)->get_memento()); - } - - state->crossfades = _crossfades; - state->crossfade_states.clear (); - for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - state->crossfade_states.push_back ((*i)->get_memento()); - } - return state; -} - -Change -AudioPlaylist::restore_state (StateManager::State& state) -{ - { - RegionLock rlock (this); - State* apstate = dynamic_cast (&state); - - in_set_state = true; - - regions = apstate->regions; - - for (list::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) { - (*s) (); + try { + boost::shared_ptr xfade = boost::shared_ptr (new Crossfade (*((const Playlist *)this), *child)); + _crossfades.push_back (xfade); + xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); + xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); + NewCrossfade(xfade); } - _crossfades = apstate->crossfades; - - for (list::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) { - (*s) (); + catch (failed_constructor& err) { + // cout << string_compose (_("could not create crossfade object in playlist %1"), + // _name) + // << endl; + continue; } - - in_set_state = false; } - notify_length_changed (); - return Change (~0); -} + thaw (); + in_set_state--; -UndoAction -AudioPlaylist::get_memento () const -{ - return sigc::bind (mem_fun (*(const_cast (this)), &StateManager::use_state), _current_state_id); + return 0; } void -AudioPlaylist::clear (bool with_delete, bool with_save) +AudioPlaylist::clear (bool with_signals) { - if (with_delete) { - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - delete *i; - } - } - _crossfades.clear (); - - Playlist::clear (with_delete, with_save); + Playlist::clear (with_signals); } XMLNode& @@ -717,15 +619,15 @@ AudioPlaylist::state (bool full_state) node.add_child_nocopy ((*i)->get_state()); } } - + return node; } void AudioPlaylist::dump () const { - Region *r; - Crossfade *x; + boost::shared_ptrr; + boost::shared_ptr x; cerr << "Playlist \"" << _name << "\" " << endl << regions.size() << " regions " @@ -734,9 +636,9 @@ AudioPlaylist::dump () const for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { r = *i; - cerr << " " << r->name() << " @ " << r << " [" - << r->start() << "+" << r->length() - << "] at " + cerr << " " << r->name() << " @ " << r << " [" + << r->start() << "+" << r->length() + << "] at " << r->position() << " on layer " << r->layer () @@ -745,13 +647,13 @@ AudioPlaylist::dump () const for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { x = *i; - cerr << " xfade [" - << x->out().name() + cerr << " xfade [" + << x->out()->name() << ',' - << x->in().name() + << x->in()->name() << " @ " << x->position() - << " length = " + << " length = " << x->length () << " active ? " << (x->active() ? "yes" : "no") @@ -760,100 +662,60 @@ AudioPlaylist::dump () const } bool -AudioPlaylist::destroy_region (Region* region) +AudioPlaylist::destroy_region (boost::shared_ptr region) { - AudioRegion* r = dynamic_cast (region); + boost::shared_ptr r = boost::dynamic_pointer_cast (region); + + if (!r) { + return false; + } + bool changed = false; Crossfades::iterator c, ctmp; - set unique_xfades; + set > unique_xfades; - if (r == 0) { - fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") - << endmsg; - /*NOTREACHED*/ - return false; - } - - { + { RegionLock rlock (this); - RegionList::iterator i; - RegionList::iterator tmp; - for (i = regions.begin(); i != regions.end(); ) { - - tmp = i; + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { + + RegionList::iterator tmp = i; ++tmp; - + if ((*i) == region) { - (*i)->unlock_sources (); regions.erase (i); changed = true; } - - i = tmp; - } - } - - for (c = _crossfades.begin(); c != _crossfades.end(); ) { - ctmp = c; - ++ctmp; - if ((*c)->involves (*r)) { - unique_xfades.insert (*c); - _crossfades.erase (c); + i = tmp; } - - c = ctmp; - } - for (StateMap::iterator s = states.begin(); s != states.end(); ) { - StateMap::iterator tmp; + for (set >::iterator x = all_regions.begin(); x != all_regions.end(); ) { - tmp = s; - ++tmp; + set >::iterator xtmp = x; + ++xtmp; - State* astate = dynamic_cast (*s); - - for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) { - - ctmp = c; - ++ctmp; - - if ((*c)->involves (*r)) { - unique_xfades.insert (*c); - _crossfades.erase (c); + if ((*x) == region) { + all_regions.erase (x); + changed = true; } - c = ctmp; + x = xtmp; } - list::iterator rsi, rsitmp; - RegionList::iterator ri, ritmp; - - for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); - ri != astate->regions.end() && rsi != astate->region_states.end();) { - - - ritmp = ri; - ++ritmp; - - rsitmp = rsi; - ++rsitmp; + region->set_playlist (boost::shared_ptr()); + } - if (region == (*ri)) { - astate->regions.erase (ri); - astate->region_states.erase (rsi); - } + for (c = _crossfades.begin(); c != _crossfades.end(); ) { + ctmp = c; + ++ctmp; - ri = ritmp; - rsi = rsitmp; + if ((*c)->involves (r)) { + unique_xfades.insert (*c); + _crossfades.erase (c); } - - s = tmp; - } - for (set::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) { - delete *c; + c = ctmp; } if (changed) { @@ -865,7 +727,7 @@ AudioPlaylist::destroy_region (Region* region) } void -AudioPlaylist::crossfade_changed (Change ignored) +AudioPlaylist::crossfade_changed (const PropertyChange&) { if (in_flush || in_set_state) { return; @@ -877,51 +739,59 @@ AudioPlaylist::crossfade_changed (Change ignored) that occured. */ - maybe_save_state (_("xfade change")); - - notify_modified (); + notify_contents_changed (); } bool -AudioPlaylist::region_changed (Change what_changed, Region* region) +AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) { if (in_flush || in_set_state) { return false; } - Change our_interests = Change (AudioRegion::FadeInChanged| - AudioRegion::FadeOutChanged| - AudioRegion::FadeInActiveChanged| - AudioRegion::FadeOutActiveChanged| - AudioRegion::EnvelopeActiveChanged| - AudioRegion::ScaleAmplitudeChanged| - AudioRegion::EnvelopeChanged); + PropertyChange our_interests; + + our_interests.add (Properties::fade_in_active); + our_interests.add (Properties::fade_out_active); + our_interests.add (Properties::scale_amplitude); + our_interests.add (Properties::envelope_active); + our_interests.add (Properties::envelope); + our_interests.add (Properties::fade_in); + our_interests.add (Properties::fade_out); + bool parent_wants_notify; parent_wants_notify = Playlist::region_changed (what_changed, region); - maybe_save_state (_("region modified")); - - if ((parent_wants_notify || (what_changed & our_interests))) { - notify_modified (); + if (parent_wants_notify || (what_changed.contains (our_interests))) { + notify_contents_changed (); } - return true; + return true; } void -AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist) +AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist) { RegionLock rlock (this); for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - jack_nframes_t start, end; + nframes_t start, end; start = (*i)->position(); end = start + (*i)->overlap_length(); // not length(), important difference if (frame >= start && frame <= end) { clist.push_back (*i); - } + } + } +} + +void +AudioPlaylist::foreach_crossfade (boost::function)> s) +{ + RegionLock rl (this, false); + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + s (*i); } }