#include "ardour/region_sorters.h"
#include "ardour/session.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace std;
: Playlist (session, node, DataType::AUDIO, hidden)
{
#ifndef NDEBUG
- const XMLProperty* prop = node.property("type");
+ XMLProperty const * prop = node.property("type");
assert(!prop || DataType(prop->value()) == DataType::AUDIO);
#endif
in_set_state--;
relayer ();
+
+ load_legacy_crossfades (node, Stateful::loading_state_version);
}
AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
/** A segment of region that needs to be read */
struct Segment {
Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
-
+
boost::shared_ptr<AudioRegion> region; ///< the region
Evoral::Range<framepos_t> range; ///< range of the region to read, in session frames
};
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
framecnt_t cnt, unsigned chan_n)
{
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
- name(), start, cnt, chan_n, regions.size()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
+ name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
/* optimizing this memset() away involves a lot of conditionals
that may well cause more of a hit due to cache misses
/* This will be a list of the bits of regions that we need to read */
list<Segment> to_do;
-
+
/* Now go through the `all' list filling in `to_do' and `done' */
for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
+ /* muted regions don't figure into it at all */
+ if ( ar->muted() )
+ continue;
+
/* Work out which bits of this region need to be read;
first, trim to the range we are reading...
*/
region_range.to = min (region_range.to, start + cnt - 1);
/* ... and then remove the bits that are already done */
+
Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
- /* Read those bits, adding their bodies (the parts between end-of-fade-in
+ /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
and start-of-fade-out) to the `done' list.
*/
/* Now go backwards through the to_do list doing the actual reads */
for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
+ name(), i->region->name(), i->range.from,
+ i->range.to - i->range.from + 1, (int) chan_n,
+ buf, i->range.from - start));
i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
}
return cnt;
}
-void
-AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
-{
- if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
- return;
- }
-
- boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
- boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
-
- RegionList all = *starts;
- std::copy (ends->begin(), ends->end(), back_inserter (all));
-
- all.sort (RegionSortByLayer ());
-
- set<boost::shared_ptr<Region> > done_start;
- set<boost::shared_ptr<Region> > done_end;
-
- for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
- for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
-
- if (i == j) {
- continue;
- }
-
- if ((*i)->muted() || (*j)->muted()) {
- continue;
- }
-
- if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
- /* precise overlay: no xfade */
- continue;
- }
-
- if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
- /* starts or ends match: no xfade */
- continue;
- }
-
- boost::shared_ptr<AudioRegion> top;
- boost::shared_ptr<AudioRegion> bottom;
-
- if ((*i)->layer() < (*j)->layer()) {
- top = boost::dynamic_pointer_cast<AudioRegion> (*j);
- bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
- } else {
- top = boost::dynamic_pointer_cast<AudioRegion> (*i);
- bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
- }
-
- if (!top->opaque ()) {
- continue;
- }
-
- Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
-
- if (c == Evoral::OverlapStart) {
-
- /* top starts within bottom but covers bottom's end */
-
- /* { ==== top ============ }
- * [---- bottom -------------------]
- */
-
- if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
-
- /* Top's fade-in will cause an implicit fade-out of bottom */
-
- if (top->fade_in_is_xfade() && top->fade_in_is_short()) {
-
- /* its already an xfade. if its
- * really short, leave it
- * alone.
- */
-
- } else {
- framecnt_t len = 0;
-
- if (_capture_insertion_underway) {
- len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
- } else {
- switch (_session.config.get_xfade_model()) {
- case FullCrossfade:
- len = bottom->last_frame () - top->first_frame ();
- top->set_fade_in_is_short (false);
- break;
- case ShortCrossfade:
- len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
- top->set_fade_in_is_short (true);
- break;
- }
- }
-
- top->set_fade_in_active (true);
- top->set_fade_in_is_xfade (true);
-
- /* XXX may 2012: -3dB and -6dB curves
- * are the same right now
- */
-
- switch (_session.config.get_xfade_choice ()) {
- case ConstantPowerMinus3dB:
- top->set_fade_in (FadeConstantPower, len);
- break;
- case ConstantPowerMinus6dB:
- top->set_fade_in (FadeConstantPower, len);
- break;
- case RegionFades:
- top->set_fade_in_length (len);
- break;
- }
- }
-
- done_start.insert (top);
- }
-
- } else if (c == Evoral::OverlapEnd) {
-
- /* top covers start of bottom but ends within it */
-
- /* [---- top ------------------------]
- * { ==== bottom ============ }
- */
-
- if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
- /* Top's fade-out will cause an implicit fade-in of bottom */
-
-
- if (top->fade_out_is_xfade() && top->fade_out_is_short()) {
-
- /* its already an xfade. if its
- * really short, leave it
- * alone.
- */
-
- } else {
- framecnt_t len = 0;
-
- if (_capture_insertion_underway) {
- len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
- } else {
- switch (_session.config.get_xfade_model()) {
- case FullCrossfade:
- len = top->last_frame () - bottom->first_frame ();
- break;
- case ShortCrossfade:
- len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
- break;
- }
- }
-
- top->set_fade_out_active (true);
- top->set_fade_out_is_xfade (true);
-
- switch (_session.config.get_xfade_choice ()) {
- case ConstantPowerMinus3dB:
- top->set_fade_out (FadeConstantPower, len);
- break;
- case ConstantPowerMinus6dB:
- top->set_fade_out (FadeConstantPower, len);
- break;
- case RegionFades:
- top->set_fade_out_length (len);
- break;
- }
- }
-
- done_end.insert (top);
- }
- }
- }
- }
-
- for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
- if (done_start.find (*i) == done_start.end()) {
- boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
- if (r->fade_in_is_xfade()) {
- r->set_default_fade_in ();
- }
- }
- }
-
- for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
- if (done_end.find (*i) == done_end.end()) {
- boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
- if (r->fade_out_is_xfade()) {
- r->set_default_fade_out ();
- }
- }
- }
-}
-
void
AudioPlaylist::dump () const
{
return false;
}
+ PropertyChange bounds;
+ bounds.add (Properties::start);
+ bounds.add (Properties::position);
+ bounds.add (Properties::length);
+
PropertyChange our_interests;
our_interests.add (Properties::fade_in_active);
bool parent_wants_notify;
parent_wants_notify = Playlist::region_changed (what_changed, region);
-
- if (parent_wants_notify || (what_changed.contains (our_interests))) {
+ /* if bounds changed, we have already done notify_contents_changed ()*/
+ if ((parent_wants_notify || what_changed.contains (our_interests)) && !what_changed.contains (bounds)) {
notify_contents_changed ();
}
int
AudioPlaylist::set_state (const XMLNode& node, int version)
{
- int const r = Playlist::set_state (node, version);
- if (r) {
- return r;
- }
+ return Playlist::set_state (node, version);
+}
+void
+AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
+{
/* Read legacy Crossfade nodes and set up region fades accordingly */
XMLNodeList children = node.children ();
for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
if ((*i)->name() == X_("Crossfade")) {
- XMLProperty* p = (*i)->property (X_("active"));
+ XMLProperty const * p = (*i)->property (X_("active"));
assert (p);
+
if (!string_is_affirmative (p->value())) {
continue;
}
- p = (*i)->property (X_("in"));
- assert (p);
- cerr << "Looking for in xfade region " << p->value() << endl;
+ if ((p = (*i)->property (X_("in"))) == 0) {
+ continue;
+ }
+
boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
- assert (in);
+
+ if (!in) {
+ warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
+ name())
+ << endmsg;
+ continue;
+ }
+
boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
assert (in_a);
- p = (*i)->property (X_("out"));
- assert (p);
- cerr << "Looking for out xfade region " << p->value() << endl;
+ if ((p = (*i)->property (X_("out"))) == 0) {
+ continue;
+ }
+
boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
- assert (out);
+
+ if (!out) {
+ warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
+ name())
+ << endmsg;
+ continue;
+ }
+
boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
assert (out_a);
- XMLNodeList c = (*i)->children ();
- for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
- if ((*j)->name() == X_("FadeIn")) {
- in_a->fade_in()->set_state (**j, version);
- in_a->set_fade_in_active (true);
- } else if ((*j)->name() == X_("FadeOut")) {
- out_a->fade_out()->set_state (**j, version);
- out_a->set_fade_out_active (true);
+ /* now decide whether to add a fade in or fade out
+ * xfade and to which region
+ */
+
+ if (in->layer() <= out->layer()) {
+
+ /* incoming region is below the outgoing one,
+ * so apply a fade out to the outgoing one
+ */
+
+ const XMLNodeList c = (*i)->children ();
+
+ for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
+ if ((*j)->name() == X_("FadeOut")) {
+ out_a->fade_out()->set_state (**j, version);
+ } else if ((*j)->name() == X_("FadeIn")) {
+ out_a->inverse_fade_out()->set_state (**j, version);
+ }
}
+
+ out_a->set_fade_out_active (true);
+
+ } else {
+
+ /* apply a fade in to the incoming region,
+ * since its above the outgoing one
+ */
+
+ const XMLNodeList c = (*i)->children ();
+
+ for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
+ if ((*j)->name() == X_("FadeIn")) {
+ in_a->fade_in()->set_state (**j, version);
+ } else if ((*j)->name() == X_("FadeOut")) {
+ in_a->inverse_fade_in()->set_state (**j, version);
+ }
+ }
+
+ in_a->set_fade_in_active (true);
}
}
}
-
- return 0;
}