/*
- 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
#include <cstdlib>
-#include <sigc++/bind.h>
-#include <ardour/types.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 "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--;
}
RegionList::const_iterator out_n = regions.begin();
while (out_o != other->regions.end()) {
-
+
boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
-
+
if ((*xfades)->out() == ar2) {
boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
add_crossfade(new_fade);
break;
}
-
+
out_o++;
out_n++;
}
AudioPlaylist::~AudioPlaylist ()
{
- GoingAway (); /* EMIT SIGNAL */
-
- /* drop connections to signals */
-
- notify_callbacks ();
-
_crossfades.clear ();
}
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
nframes_t cnt, unsigned chan_n)
{
+ 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.
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<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
vector<uint32_t> 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) {
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);
+ 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<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
(*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
}
}
- return cnt;
+ delete rlist;
+ return ret;
}
if (in_set_state) {
return;
}
-
+
if (r == 0) {
fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
<< endmsg;
}
for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
-
+
if ((*i)->involves (r)) {
i = _crossfades.erase (i);
} else {
}
_pending_xfade_adds.clear ();
-
+
in_flush = false;
}
for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
Crossfades::iterator tmp;
-
+
tmp = x;
++tmp;
if ((*x)->involves (ar)) {
pair<set<boost::shared_ptr<Crossfade> >::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 {
++tmp;
boost::shared_ptr<Crossfade> fade;
-
+
if ((*x)->_in == orig) {
if (! (*x)->covers(right->position())) {
fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
}
}
-
+
if ((*x)->_out == orig) {
if (! (*x)->covers(right->position())) {
fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
}
}
-
+
if (fade) {
_crossfades.remove (*x);
add_crossfade (fade);
boost::shared_ptr<AudioRegion> top;
boost::shared_ptr<AudioRegion> bottom;
boost::shared_ptr<Crossfade> xfade;
- RegionList* touched_regions;
+ RegionList* touched_regions = 0;
if (in_set_state || in_partition) {
return;
}
- 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<AudioRegion> (*i);
if (other == region) {
if (other->muted() || region->muted()) {
continue;
}
-
+
if (other->layer() < region->layer()) {
top = region;
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;
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 ((nframes_t) 720, top->length());
+
+ xfade_length = min ((framecnt_t) 720, top->length());
if (top_region_at (top->first_frame()) == top) {
-
+
xfade = boost::shared_ptr<Crossfade> (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<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
add_crossfade (xfade);
}
break;
case OverlapStart:
- /* { ==== top ============ }
- * [---- bottom -------------------]
+ /* { ==== top ============ }
+ * [---- bottom -------------------]
*/
- if (Config->get_xfade_model() == FullCrossfade) {
+ 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<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ xfade = boost::shared_ptr<Crossfade> (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 ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(),
+ 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<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
add_crossfade (xfade);
}
}
case OverlapEnd:
- /* [---- top ------------------------]
- * { ==== bottom ============ }
- */
+ /* [---- top ------------------------]
+ * { ==== bottom ============ }
+ */
- if (Config->get_xfade_model() == FullCrossfade) {
+ 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<Crossfade> (new Crossfade (region, other,
- Config->get_xfade_model(), Config->get_xfades_active()));
+ xfade = boost::shared_ptr<Crossfade> (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 ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(),
+ 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<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
add_crossfade (xfade);
}
}
break;
default:
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
- Config->get_xfade_model(), Config->get_xfades_active()));
+ xfade = boost::shared_ptr<Crossfade> (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
break;
}
}
-
+
if (ci != _crossfades.end()) {
// it will just go away
} 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);
}
}
-
+
void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
{
if (g_atomic_int_get(&block_notifications)) {
_pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
} else {
-
NewCrossfade (x); /* EMIT SIGNAL */
}
}
}
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();
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);
}
-
+
catch (failed_constructor& err) {
// cout << string_compose (_("could not create crossfade object in playlist %1"),
- // _name)
+ // _name)
// << endl;
continue;
}
node.add_child_nocopy ((*i)->get_state());
}
}
-
+
return node;
}
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 ()
for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
x = *i;
- cerr << " xfade ["
+ cerr << " xfade ["
<< x->out()->name()
<< ','
<< x->in()->name()
<< " @ "
<< x->position()
- << " length = "
+ << " length = "
<< x->length ()
<< " active ? "
<< (x->active() ? "yes" : "no")
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);
for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
-
+
RegionList::iterator tmp = i;
++tmp;
-
+
if ((*i) == region) {
regions.erase (i);
changed = true;
}
-
+
i = tmp;
}
set<boost::shared_ptr<Region> >::iterator xtmp = x;
++xtmp;
-
+
if ((*x) == region) {
all_regions.erase (x);
changed = true;
}
-
+
x = xtmp;
}
unique_xfades.insert (*c);
_crossfades.erase (c);
}
-
+
c = ctmp;
}
}
void
-AudioPlaylist::crossfade_changed (Change ignored)
+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;
+ return true;
}
void
if (frame >= start && frame <= end) {
clist.push_back (*i);
- }
+ }
}
}
+void
+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);
+ }
+}