X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudio_playlist.cc;h=cb65164a9b03f2adb4be7032798227d72948e8fe;hb=f188ffffc18687c47d1d5a5c8b35bda4d7b75f5f;hp=3eda5b57cfc4e6d8450e59683003afb6d29d2baf;hpb=539aa71d180d6b3d5c887707c356d3d00c0b37e8;p=ardour.git diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 3eda5b57cf..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 @@ -21,32 +21,33 @@ #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::AudioPlaylist (Session& session, const XMLNode& node, bool hidden) : Playlist (session, node, DataType::AUDIO, hidden) { +#ifndef NDEBUG const XMLProperty* prop = node.property("type"); assert(!prop || DataType(prop->value()) == DataType::AUDIO); +#endif in_set_state++; - set_state (node); + set_state (node, Stateful::loading_state_version); in_set_state--; } @@ -65,7 +66,6 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, stri 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 (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) { if ((*xfades)->in() == ar) { // We found one! Now copy it! @@ -74,9 +74,9 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, stri 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); @@ -84,7 +84,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, stri add_crossfade(new_fade); break; } - + out_o++; out_n++; } @@ -105,30 +105,9 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, nfra AudioPlaylist::~AudioPlaylist () { - GoingAway (); /* EMIT SIGNAL */ - - /* drop connections to signals */ - - notify_callbacks (); - _crossfades.clear (); } -void -AudioPlaylist::copy_regions (RegionList& newlist) const -{ - RegionLock rlock (const_cast (this)); - - Playlist::copy_regions (newlist, false); - - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - if (!(*i)->is_dependent()) { - newlist.push_back (RegionFactory::RegionFactory::create (*i)); - } - } -} - - struct RegionSortByLayer { bool operator() (boost::shared_ptr a, boost::shared_ptr b) { return a->layer() < b->layer(); @@ -141,11 +120,13 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf { 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. @@ -157,25 +138,35 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf 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; _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) { @@ -200,13 +191,15 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf vector > r (relevant_regions[*l]); vector >& x (relevant_xfades[*l]); + 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); + 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) { (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); @@ -216,6 +209,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf } } + delete rlist; return ret; } @@ -228,17 +222,17 @@ AudioPlaylist::remove_dependents (boost::shared_ptr 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 (RegionList::iterator i = regions.begin(); i != regions.end(); ) { - - if ((*i)->depends_on (r)) { - i = regions.erase (i); + + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { + + if ((*i)->involves (r)) { + i = _crossfades.erase (i); } else { ++i; } @@ -263,7 +257,7 @@ AudioPlaylist::flush_notifications () } _pending_xfade_adds.clear (); - + in_flush = false; } @@ -277,22 +271,23 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr r) return; } - for (RegionList::iterator x = regions.begin(); x != regions.end();) { - - RegionList::iterator tmp; - + for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { + + Crossfades::iterator tmp; + tmp = x; ++tmp; /* only update them once */ - if ((*x)->depends_on (ar)) { + if ((*x)->involves (ar)) { - if (find (updated.begin(), updated.end(), *x) == updated.end()) { - try { - if ((*x)->refresh ()) { - updated.insert (*x); - } + pair >::iterator, bool> const u = 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) { @@ -312,43 +307,35 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr o, boost::shared boost::shared_ptr left = boost::dynamic_pointer_cast(l); boost::shared_ptr right = boost::dynamic_pointer_cast(r); - for (RegionList::iterator x = regions.begin(); x !=regions.end();) { + for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { Crossfades::iterator tmp; - boost::shared_ptr xfade = boost::dynamic_pointer_cast(*x); - - if (!xfade) { - ++x; - continue; - } - tmp = x; ++tmp; boost::shared_ptr fade; - - if (xfade->_in == orig) { - if (! xfade->covers(right->position())) { - fade = boost::shared_ptr (new Crossfade (*xfade, left, xfade->_out)); + + if ((*x)->_in == orig) { + if (! (*x)->covers(right->position())) { + 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 = boost::shared_ptr (new Crossfade (*xfade, right, xfade->_out)); + fade = boost::shared_ptr (new Crossfade (*x, right, (*x)->_out)); } } - - if (xfade->_out == orig) { - if (! xfade->covers(right->position())) { - fade = boost::shared_ptr (new Crossfade (*xfade, xfade->_in, right)); + + if ((*x)->_out == orig) { + if (! (*x)->covers(right->position())) { + 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 = boost::shared_ptr (new Crossfade (*xfade, xfade->_in, left)); + fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, left)); } } - + if (fade) { - regions.erase (*x); + _crossfades.remove (*x); add_crossfade (fade); } - x = tmp; } } @@ -361,6 +348,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) boost::shared_ptr top; boost::shared_ptr bottom; boost::shared_ptr xfade; + RegionList* touched_regions = 0; if (in_set_state || in_partition) { return; @@ -377,14 +365,11 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) } - if (!Config->get_auto_xfade()) { + if (!_session.config.get_auto_xfade()) { return; } for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - nframes_t xfade_length; - other = boost::dynamic_pointer_cast (*i); if (other == region) { @@ -394,7 +379,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) if (other->muted() || region->muted()) { continue; } - + if (other->layer() < region->layer()) { top = region; @@ -404,11 +389,17 @@ AudioPlaylist::check_dependents (boost::shared_ptr 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 { + nframes_t xfade_length; switch (c) { case OverlapNone: break; @@ -424,32 +415,85 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) /* [ -------- 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 ((nframes_t) 720, top->length()); - - xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn)); - add_crossfade (xfade); + + 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 + + /* + only add a fade out if there is no region on top of the end of 'top' (which would cover it). */ - + 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 { + + 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, Config->get_xfade_model(), Config->get_xfades_active())); + xfade = boost::shared_ptr (new Crossfade (region, other, + _session.config.get_xfade_model(), _session.config.get_xfades_active())); add_crossfade (xfade); } } @@ -457,41 +501,39 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) catch (failed_constructor& err) { continue; } - + catch (Crossfade::NoCrossfadeHere& err) { continue; } - + } + + delete touched_regions; } void -AudioPlaylist::add_crossfade (boost::shared_ptr new_xfade) +AudioPlaylist::add_crossfade (boost::shared_ptr xfade) { - RegionList::iterator r; - boost::shared_ptr xfade; + Crossfades::iterator ci; - for (r = regions.begin(); r != regions.end(); ++r) { - - if ((xfade = boost::dynamic_pointer_cast(*r))) { - if (*xfade == *new_xfade) { // Crossfade::operator==() - break; - } + for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { + if (*(*ci) == *xfade) { // Crossfade::operator==() + break; } } - - if (r != regions.end()) { + + if (ci != _crossfades.end()) { // it will just go away } else { - add_region_internal (new_xfade); + _crossfades.push_back (xfade); - new_xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - new_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 (new_xfade); + notify_crossfade_added (xfade); } } - + void AudioPlaylist::notify_crossfade_added (boost::shared_ptr x) { if (g_atomic_int_get(&block_notifications)) { @@ -504,25 +546,29 @@ void AudioPlaylist::notify_crossfade_added (boost::shared_ptr x) void 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 (); - remove_region_internal (r); + if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) { + _crossfades.erase (i); + } } int -AudioPlaylist::set_state (const XMLNode& node) +AudioPlaylist::set_state (const XMLNode& node, int version) { XMLNode *child; XMLNodeList nlist; XMLNodeConstIterator niter; in_set_state++; - freeze (); - Playlist::set_state (node); + Playlist::set_state (node, version); + + freeze (); nlist = node.children(); @@ -530,22 +576,21 @@ AudioPlaylist::set_state (const XMLNode& node) child = *niter; - if (child->name() != Crossfade::node_name()) { + if (child->name() != "Crossfade") { continue; } - + try { - boost::shared_ptr xfade = boost::dynamic_pointer_cast (RegionFactory::create (*((const Playlist *) this), *child)); - assert (xfade); - add_region_internal (xfade); - xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed)); + 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); } - + catch (failed_constructor& err) { // cout << string_compose (_("could not create crossfade object in playlist %1"), - // _name) + // _name) // << endl; continue; } @@ -560,9 +605,24 @@ AudioPlaylist::set_state (const XMLNode& node) void AudioPlaylist::clear (bool with_signals) { + _crossfades.clear (); Playlist::clear (with_signals); } +XMLNode& +AudioPlaylist::state (bool full_state) +{ + XMLNode& node = Playlist::state (full_state); + + if (full_state) { + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + node.add_child_nocopy ((*i)->get_state()); + } + } + + return node; +} + void AudioPlaylist::dump () const { @@ -571,32 +631,33 @@ AudioPlaylist::dump () const cerr << "Playlist \"" << _name << "\" " << endl << regions.size() << " regions " + << _crossfades.size() << " crossfades" << endl; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { r = *i; + cerr << " " << r->name() << " @ " << r << " [" + << r->start() << "+" << r->length() + << "] at " + << r->position() + << " on layer " + << r->layer () + << endl; + } - if ((x = boost::dynamic_pointer_cast (r))) { - cerr << " xfade [" - << x->out()->name() - << ',' - << x->in()->name() - << " @ " - << x->position() - << " length = " - << x->length () - << " active ? " - << (x->active() ? "yes" : "no") - << endl; - } else { - cerr << " " << r->name() << " @ " << r << " [" - << r->start() << "+" << r->length() - << "] at " - << r->position() - << " on layer " - << r->layer () - << endl; - } + for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + x = *i; + cerr << " xfade [" + << x->out()->name() + << ',' + << x->in()->name() + << " @ " + << x->position() + << " length = " + << x->length () + << " active ? " + << (x->active() ? "yes" : "no") + << endl; } } @@ -604,31 +665,28 @@ bool AudioPlaylist::destroy_region (boost::shared_ptr region) { boost::shared_ptr r = boost::dynamic_pointer_cast (region); - bool changed = false; - if (r == 0) { - fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") - << endmsg; - /*NOTREACHED*/ - return false; - } + if (!r) { + return false; + } + + bool changed = false; + Crossfades::iterator c, ctmp; + set > unique_xfades; - { + { RegionLock rlock (this); for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { - + RegionList::iterator tmp = i; ++tmp; - + if ((*i) == region) { regions.erase (i); changed = true; - } else if ((*i)->depends_on (region)) { - regions.erase (i); - changed = true; } - + i = tmp; } @@ -636,21 +694,30 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) set >::iterator xtmp = x; ++xtmp; - + if ((*x) == region) { all_regions.erase (x); changed = true; - } else if ((*x)->depends_on (region)) { - all_regions.erase (x); - changed = true; } - + x = xtmp; } region->set_playlist (boost::shared_ptr()); } + for (c = _crossfades.begin(); c != _crossfades.end(); ) { + ctmp = c; + ++ctmp; + + if ((*c)->involves (r)) { + unique_xfades.insert (*c); + _crossfades.erase (c); + } + + c = ctmp; + } + if (changed) { /* overload this, it normally means "removed", not destroyed */ notify_region_removed (region); @@ -660,7 +727,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) } void -AudioPlaylist::crossfade_changed (Change ignored) +AudioPlaylist::crossfade_changed (const PropertyChange&) { if (in_flush || in_set_state) { return; @@ -672,31 +739,59 @@ AudioPlaylist::crossfade_changed (Change ignored) that occured. */ - notify_modified (); + notify_contents_changed (); } bool -AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr 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); - 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 (nframes_t frame, Crossfades& clist) +{ + RegionLock rlock (this); + + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + 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); + } +}