#include <cstdlib>
-#include <sigc++/bind.h>
-
#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;
+namespace ARDOUR {
+ namespace Properties {
+ PBD::PropertyDescriptor<bool> crossfades;
+ }
+}
+
+void
+AudioPlaylist::make_property_quarks ()
+{
+ Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
+ DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
+}
+
+CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
+ : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
+ , _playlist (pl)
+{
+
+}
+
+CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
+ : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
+ , _playlist (p._playlist)
+{
+
+}
+
+
+CrossfadeListProperty *
+CrossfadeListProperty::create () const
+{
+ return new CrossfadeListProperty (_playlist);
+}
+
+CrossfadeListProperty *
+CrossfadeListProperty::clone () const
+{
+ return new CrossfadeListProperty (*this);
+}
+
+void
+CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
+{
+ /* Crossfades are not written to any state when they are no
+ longer in use, so we must write their state here.
+ */
+
+ XMLNode& c = xfade->get_state ();
+ node.add_child_nocopy (c);
+}
+
+boost::shared_ptr<Crossfade>
+CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
+{
+ XMLNodeList const c = node.children ();
+ assert (c.size() == 1);
+ return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
+}
+
+
AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
: Playlist (session, node, DataType::AUDIO, hidden)
+ , _crossfades (*this)
{
+#ifndef NDEBUG
const XMLProperty* prop = node.property("type");
assert(!prop || DataType(prop->value()) == DataType::AUDIO);
+#endif
+
+ add_property (_crossfades);
in_set_state++;
set_state (node, Stateful::loading_state_version);
AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
: Playlist (session, name, DataType::AUDIO, hidden)
+ , _crossfades (*this)
{
+ add_property (_crossfades);
}
AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
: Playlist (other, name, hidden)
+ , _crossfades (*this)
{
+ add_property (_crossfades);
+
RegionList::const_iterator in_o = other->regions.begin();
RegionList::iterator in_n = regions.begin();
}
}
-AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
+AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
: Playlist (other, start, cnt, name, hidden)
+ , _crossfades (*this)
{
+ RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
+ in_set_state++;
+
+ add_property (_crossfades);
+
+ framepos_t const end = start + cnt - 1;
+
+ /* Audio regions that have been created by the Playlist constructor
+ will currently have the same fade in/out as the regions that they
+ were created from. This is wrong, so reset the fades here.
+ */
+
+ RegionList::iterator ours = regions.begin ();
+
+ for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
+ assert (region);
+
+ framecnt_t fade_in = 64;
+ framecnt_t fade_out = 64;
+ framepos_t position;
+ framecnt_t len;
+ frameoffset_t offset;
+
+ switch (region->coverage (start, end)) {
+ case OverlapNone:
+ continue;
+
+ case OverlapInternal:
+ {
+ framecnt_t const offset = start - region->position ();
+ framecnt_t const trim = region->last_frame() - end;
+ if (region->fade_in()->back()->when > offset) {
+ fade_in = region->fade_in()->back()->when - offset;
+ }
+ if (region->fade_out()->back()->when > trim) {
+ fade_out = region->fade_out()->back()->when - trim;
+ }
+ break;
+ }
+
+ case OverlapStart: {
+ position = region->position() - start;
+ len = end - region->position();
+
+ if (end > region->position() + region->fade_in()->back()->when)
+ fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
+ if (end > region->last_frame() - region->fade_out()->back()->when)
+ fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
+ break;
+ }
+
+ case OverlapEnd: {
+ position = 0;
+ offset = start - region->position();
+ len = region->length() - offset;
+
+ if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
+ fade_out = region->fade_out()->back()->when;
+
+ if (start < region->position() + region->fade_in()->back()->when)
+ fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
+ break;
+ }
+
+ case OverlapExternal:
+ fade_in = region->fade_in()->back()->when;
+ fade_out = region->fade_out()->back()->when;
+ break;
+ }
+
+ boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
+ assert (our_region);
+
+ our_region->set_fade_in_length (fade_in);
+ our_region->set_fade_out_length (fade_out);
+ ++ours;
+ }
+
+ in_set_state--;
+
/* this constructor does NOT notify others (session) */
}
AudioPlaylist::~AudioPlaylist ()
{
- GoingAway (); /* EMIT SIGNAL */
-
- /* drop connections to signals */
-
- notify_callbacks ();
-
_crossfades.clear ();
}
}
};
-ARDOUR::nframes_t
-AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
- nframes_t cnt, unsigned chan_n)
+ARDOUR::framecnt_t
+AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
+ framecnt_t cnt, unsigned chan_n)
{
- nframes_t ret = cnt;
- nframes_t end;
- nframes_t read_frames;
- nframes_t skip_frames;
+ framecnt_t ret = cnt;
/* optimizing this memset() away involves a lot of conditionals
that may well cause more of a hit due to cache misses
Glib::RecMutex::Lock rm (region_lock);
- end = start + cnt - 1;
- read_frames = 0;
- skip_frames = 0;
+ framepos_t const end = start + cnt - 1;
+ framecnt_t read_frames = 0;
+ framecnt_t skip_frames = 0;
_read_data_count = 0;
_read_data_count = 0;
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) {
vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
+
for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*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();
void
-AudioPlaylist::flush_notifications ()
+AudioPlaylist::flush_notifications (bool from_undo)
{
- Playlist::flush_notifications();
+ Playlist::flush_notifications (from_undo);
if (in_flush) {
return;
}
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
- nframes_t xfade_length;
-
other = boost::dynamic_pointer_cast<AudioRegion> (*i);
if (other == region) {
continue;
}
+ if (other->position() == r->position() && other->length() == r->length()) {
+ /* precise overlay of two regions - no xfade */
+ continue;
+ }
if (other->layer() < region->layer()) {
top = region;
touched_regions = 0;
try {
+ framecnt_t xfade_length;
switch (c) {
case OverlapNone:
break;
audio engineering.
*/
- xfade_length = min ((nframes_t) 720, top->length());
+ xfade_length = min ((framecnt_t) 720, top->length());
if (top_region_at (top->first_frame()) == top) {
} else {
touched_regions = regions_touched (top->first_frame(),
- top->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
+ 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<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
} else {
touched_regions = regions_touched (bottom->first_frame(),
- bottom->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
+ 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<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
} else {
_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);
}
try {
boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
_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));
NewCrossfade(xfade);
}
AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
{
boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+
+ if (!r) {
+ return false;
+ }
+
bool changed = false;
Crossfades::iterator c, ctmp;
set<boost::shared_ptr<Crossfade> > 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);
}
void
-AudioPlaylist::crossfade_changed (Change)
+AudioPlaylist::crossfade_changed (const PropertyChange&)
{
if (in_flush || in_set_state) {
return;
that occured.
*/
- notify_modified ();
+ notify_contents_changed ();
}
bool
-AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> 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;
}
void
-AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
+AudioPlaylist::crossfades_at (framepos_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
+ framepos_t const start = (*i)->position ();
+ framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
if (frame >= start && frame <= end) {
clist.push_back (*i);
}
void
-AudioPlaylist::foreach_crossfade (sigc::slot<void, boost::shared_ptr<Crossfade> > s)
+AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
{
RegionLock rl (this, false);
for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
s (*i);
}
}
+
+void
+AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
+{
+ for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
+ add_crossfade (*i);
+ }
+
+ /* don't remove crossfades here; they will be dealt with by the dependency code */
+}
+
+boost::shared_ptr<Crossfade>
+AudioPlaylist::find_crossfade (const PBD::ID& id) const
+{
+ Crossfades::const_iterator i = _crossfades.begin ();
+ while (i != _crossfades.end() && (*i)->id() != id) {
+ ++i;
+ }
+
+ if (i == _crossfades.end()) {
+ return boost::shared_ptr<Crossfade> ();
+ }
+
+ return *i;
+}
+
+struct crossfade_triple {
+ boost::shared_ptr<Region> old_in;
+ boost::shared_ptr<Region> new_in;
+ boost::shared_ptr<Region> new_out;
+};
+
+void
+AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, boost::shared_ptr<Playlist> other)
+{
+ boost::shared_ptr<AudioPlaylist> other_audio = boost::dynamic_pointer_cast<AudioPlaylist>(other);
+
+ if (!other_audio) {
+ return;
+ }
+
+ /* our argument is a vector of old and new regions. Each old region
+ might be participant in a crossfade that is already present. Each new
+ region is a copy of the old region, present in the other playlist.
+
+ our task is to find all the relevant xfades in our playlist (involving
+ the "old" regions) and place copies of them in the other playlist.
+ */
+
+ typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
+ CrossfadeInfo crossfade_info;
+
+ /* build up a record that links crossfades, old regions and new regions
+ */
+
+ for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
+
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+
+ if ((*i)->in() == on->first) {
+
+ CrossfadeInfo::iterator cf;
+
+ if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
+
+ /* already have a record for the old fade-in region,
+ so note the new fade-in region
+ */
+
+ cf->second.new_in = on->second;
+
+ } else {
+
+ /* add a record of this crossfade, keeping an association
+ with the new fade-in region
+ */
+
+ crossfade_triple ct;
+
+ ct.old_in = on->first;
+ ct.new_in = on->second;
+
+ crossfade_info[*i] = ct;
+ }
+
+ } else if ((*i)->out() == on->first) {
+
+ /* this old region is the fade-out region of this crossfade */
+
+ CrossfadeInfo::iterator cf;
+
+ if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
+
+ /* already have a record for this crossfade, so just keep
+ an association for the new fade out region
+ */
+
+ cf->second.new_out = on->second;
+
+ } else {
+
+ /* add a record of this crossfade, keeping an association
+ with the new fade-in region
+ */
+
+ crossfade_triple ct;
+
+ ct.old_in = on->first;
+ ct.new_out = on->second;
+
+ crossfade_info[*i] = ct;
+ }
+ }
+ }
+ }
+
+ for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
+
+ /* for each crossfade that involves at least two of the old regions,
+ create a new identical crossfade with the new regions
+ */
+
+ if (!ci->second.new_in || !ci->second.new_out) {
+ continue;
+ }
+
+ boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
+ boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
+ boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
+
+ /* add it at the right position */
+
+ new_xfade->set_position (new_xfade->position() + (ci->second.new_in->position() - ci->second.old_in->position()), this);
+ cerr << "Add new xfade to new playlist @ " << new_xfade->position() << " len " << new_xfade->length()
+ << " in = " << new_xfade->in()->name() << " out = " << new_xfade->out()->name() << endl;
+ other_audio->add_crossfade (new_xfade);
+ }
+}