Inrease the export "chunk size" to speed it up over 10% at least in some situations
[ardour.git] / libs / ardour / audio_playlist.cc
index 9c304967dcfe64670800377a40ba6cdaf4170902..c1b6bb6e6d36652eac54e6302e90299c84f95937 100644 (file)
 
 #include "ardour/types.h"
 #include "ardour/debug.h"
-#include "ardour/configuration.h"
 #include "ardour/audioplaylist.h"
 #include "ardour/audioregion.h"
 #include "ardour/region_sorters.h"
 #include "ardour/session.h"
-#include "pbd/enumwriter.h"
 
 #include "i18n.h"
 
@@ -51,6 +49,8 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
        in_set_state--;
 
        relayer ();
+
+       load_legacy_crossfades (node, Stateful::loading_state_version);
 }
 
 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
@@ -66,7 +66,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
        : Playlist (other, start, cnt, name, hidden)
 {
-       RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
+       RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
        in_set_state++;
 
        framepos_t const end = start + cnt - 1;
@@ -186,16 +186,12 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
           its OK to block (for short intervals).
        */
 
-#ifdef HAVE_GLIB_THREADS_RECMUTEX
-       Glib::Threads::RecMutex::Lock lm (region_lock);
-#else  
-       Glib::RecMutex::Lock rm (region_lock);
-#endif 
+       Playlist::RegionReadLock rl (this);
 
        /* Find all the regions that are involved in the bit we are reading,
           and sort them by descending layer and ascending position.
        */
-       boost::shared_ptr<RegionList> all = regions_touched (start, start + cnt - 1);
+       boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
        all->sort (ReadSorter ());
 
        /* This will be a list of the bits of our read range that we have
@@ -206,7 +202,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
 
        /* 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);
@@ -219,9 +215,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
                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.
                */
 
@@ -245,6 +242,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
 
        /* 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);
        }
 
@@ -257,7 +258,7 @@ 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);
 
@@ -306,7 +307,7 @@ AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
                        }
 
                        Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
-                       
+
                        if (c == Evoral::OverlapStart) {
                                
                                /* top starts within bottom but covers bottom's end */
@@ -319,31 +320,51 @@ AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
 
                                        /* Top's fade-in will cause an implicit fade-out of bottom */
                                        
-                                       framecnt_t len = 0;
-                                       switch (_session.config.get_xfade_model()) {
-                                       case FullCrossfade:
-                                               len = bottom->last_frame () - top->first_frame ();
-                                               break;
-                                       case ShortCrossfade:
-                                               len = _session.config.get_short_xfade_seconds() * _session.frame_rate();
-                                               break;
+                                       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 () + 1;
+                                                               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;
+                                               }
                                        }
-                                       
-                                       top->set_fade_in_active (true);
-                                       top->set_fade_in_is_xfade (true);
-
-                                       switch (_session.config.get_xfade_choice ()) {
-                                       case ConstantPowerMinus3dB:
-                                               top->set_fade_in (FadeConstantPowerMinus3dB, len);
-                                               break;
-                                       case ConstantPowerMinus6dB:
-                                               top->set_fade_in (FadeConstantPowerMinus6dB, len);
-                                               break;
-                                       case RegionFades:
-                                               top->set_fade_in_length (len);
-                                               break;
-                                       }
-                                       
+
                                        done_start.insert (top);
                                }
 
@@ -358,29 +379,44 @@ AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
                                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 */
                                        
-                                       framecnt_t len = 0;
-                                       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 (FadeConstantPowerMinus3dB, len);
-                                               break;
-                                       case ConstantPowerMinus6dB:
-                                               top->set_fade_out (FadeConstantPowerMinus6dB, len);
-                                               break;
-                                       case RegionFades:
-                                               top->set_fade_out_length (len);
-                                               break;
+                                       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 () + 1;
+                                                               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);
@@ -392,14 +428,18 @@ AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
        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);
-                       r->set_default_fade_in ();
+                       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);
-                       r->set_default_fade_out ();
+                       if (r->fade_out_is_xfade()) {
+                               r->set_default_fade_out ();
+                       }
                }
        }
 }
@@ -437,7 +477,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
        bool changed = false;
 
        {
-               RegionLock rlock (this);
+               RegionWriteLock rlock (this);
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
 
@@ -623,11 +663,12 @@ AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boo
 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 ();
@@ -636,36 +677,97 @@ AudioPlaylist::set_state (const XMLNode& node, int version)
 
                        XMLProperty* p = (*i)->property (X_("active"));
                        assert (p);
+
                        if (!string_is_affirmative (p->value())) {
                                continue;
                        }
+                       
+                       if ((p = (*i)->property (X_("in"))) == 0) {
+                               continue;
+                       }
 
-                       p = (*i)->property (X_("in"));
-                       assert (p);
                        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);
+                       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);
+                                       }
+                               }
+                               
+                               if ((p = (*i)->property ("follow-overlap")) != 0) {
+                                       out_a->set_fade_out_is_short (!string_is_affirmative (p->value()));
+                               } else {
+                                       out_a->set_fade_out_is_short (false);
+                               }
+                               
+                               out_a->set_fade_out_is_xfade (true);
+                               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);
+                                       }
                                }
+                               
+                               if ((p = (*i)->property ("follow-overlap")) != 0) {
+                                       in_a->set_fade_in_is_short (!string_is_affirmative (p->value()));
+                               } else {
+                                       in_a->set_fade_in_is_short (false);
+                               }
+                               
+                               in_a->set_fade_in_is_xfade (true);
+                               in_a->set_fade_in_active (true);
                        }
                }
        }
-
-       return 0;
 }