fixes for destructive track offsets of various kinds; move from jack_nframes_t -...
[ardour.git] / libs / ardour / audio_playlist.cc
index e4a244aa16f03cfbdac293bce115b524bd5dcf73..526003e6b2702d25fbad994e440d6c43ad57a2b5 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <algorithm>
 
-#include <stdlib.h>
+#include <cstdlib>
 
 #include <sigc++/bind.h>
 
@@ -37,6 +37,7 @@
 using namespace ARDOUR;
 using namespace sigc;
 using namespace std;
+using namespace PBD;
 
 AudioPlaylist::State::~State ()
 {
@@ -72,12 +73,49 @@ AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidd
 {
        save_state (_("initial state"));
 
+       RegionList::const_iterator in_o  = other.regions.begin();
+       RegionList::iterator in_n = regions.begin();
+
+       while (in_o != other.regions.end()) {
+               boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
+
+               // We look only for crossfades which begin with the current region, so we don't get doubles
+               for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
+                       if ((*xfades)->in() == ar) {
+                               // We found one! Now copy it!
+
+                               RegionList::const_iterator out_o = other.regions.begin();
+                               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);
+                                               Crossfade *new_fade = new Crossfade (*(*xfades), in, out);
+                                               add_crossfade(*new_fade);
+                                               break;
+                                       }
+                                       
+                                       out_o++;
+                                       out_n++;
+                               }
+//                             cerr << "HUH!? second region in the crossfade not found!" << endl;
+                       }
+               }
+
+               in_o++;
+               in_n++;
+       }
+
        if (!hidden) {
                PlaylistCreated (this); /* EMIT SIGNAL */
        }
 }
 
-AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden)
+AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, nframes_t start, nframes_t cnt, string name, bool hidden)
        : Playlist (other, start, cnt, name, hidden)
 {
        save_state (_("initial state"));
@@ -90,15 +128,11 @@ AudioPlaylist::~AudioPlaylist ()
        set<Crossfade*> all_xfades;
        set<Region*> all_regions;
 
-       GoingAway (this);
+       GoingAway (); /* EMIT SIGNAL */
 
-       /* find every region we've ever used, and add it to the set of 
-          all regions. same for xfades;
-       */
+       /* drop connections to signals */
 
-       for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
-               all_regions.insert (*x);
-       }
+       notify_callbacks ();
 
        for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ++x) {
                all_xfades.insert (*x);
@@ -108,23 +142,13 @@ AudioPlaylist::~AudioPlaylist ()
                
                AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
 
-               for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
-                       all_regions.insert (*r);
-               }
-               for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
+       for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
                        all_xfades.insert (*xf);
                }
 
                delete apstate;
        }
 
-       /* delete every region */
-
-       for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
-               (*ar)->unlock_sources ();
-               delete *ar;
-       }
-
        /* delete every crossfade */
 
        for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
@@ -133,19 +157,19 @@ AudioPlaylist::~AudioPlaylist ()
 }
 
 struct RegionSortByLayer {
-    bool operator() (Region *a, Region *b) {
+    bool operator() (boost::shared_ptr<Region>a, boost::shared_ptr<Region>b) {
            return a->layer() < b->layer();
     }
 };
 
-jack_nframes_t
-AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, jack_nframes_t start,
-                    jack_nframes_t cnt, unsigned chan_n)
+nframes_t
+AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
+                    nframes_t cnt, unsigned chan_n)
 {
-       jack_nframes_t ret = cnt;
-       jack_nframes_t end;
-       jack_nframes_t read_frames;
-       jack_nframes_t skip_frames;
+       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 
@@ -166,7 +190,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja
           its OK to block (for short intervals).
        */
 
-       LockMonitor rm (region_lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock rm (region_lock);
 
        end =  start + cnt - 1;
 
@@ -174,13 +198,12 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja
        skip_frames = 0;
        _read_data_count = 0;
 
-       map<uint32_t,vector<Region*> > relevant_regions;
+       map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
        map<uint32_t,vector<Crossfade*> > relevant_xfades;
        vector<uint32_t> relevant_layers;
 
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                if ((*i)->coverage (start, end) != OverlapNone) {
-                       
                        relevant_regions[(*i)->layer()].push_back (*i);
                        relevant_layers.push_back ((*i)->layer());
                }
@@ -205,16 +228,17 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja
 
        for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
 
-               vector<Region*>& r (relevant_regions[*l]);
+               vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
                vector<Crossfade*>& x (relevant_xfades[*l]);
 
-               for (vector<Region*>::iterator i = r.begin(); i != r.end(); ++i) {
-                       (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
-                       _read_data_count += (*i)->read_data_count();
+               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);
+                       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();
                }
                
                for (vector<Crossfade*>::iterator i = x.begin(); i != x.end(); ++i) {
-                       
                        (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
 
                        /* don't JACK up _read_data_count, since its the same data as we just
@@ -228,10 +252,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, ja
 
 
 void
-AudioPlaylist::remove_dependents (Region& region)
+AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
 {
        Crossfades::iterator i, tmp;
-       AudioRegion* r = dynamic_cast<AudioRegion*> (&region);
+       boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
        
        if (r == 0) {
                fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
@@ -243,7 +267,7 @@ AudioPlaylist::remove_dependents (Region& region)
                tmp = i;
                tmp++;
 
-               if ((*i)->involves (*r)) {
+               if ((*i)->involves (r)) {
                        /* do not delete crossfades */
                        _crossfades.erase (i);
                }
@@ -275,9 +299,9 @@ AudioPlaylist::flush_notifications ()
 }
 
 void
-AudioPlaylist::refresh_dependents (Region& r)
+AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
 {
-       AudioRegion* ar = dynamic_cast<AudioRegion*>(&r);
+       boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
        set<Crossfade*> updated;
 
        if (ar == 0) {
@@ -293,7 +317,7 @@ AudioPlaylist::refresh_dependents (Region& r)
 
                /* only update them once */
 
-               if ((*x)->involves (*ar)) {
+               if ((*x)->involves (ar)) {
 
                        if (find (updated.begin(), updated.end(), *x) == updated.end()) {
                                if ((*x)->refresh ()) {
@@ -308,19 +332,59 @@ AudioPlaylist::refresh_dependents (Region& r)
 }
 
 void
-AudioPlaylist::check_dependents (Region& r, bool norefresh)
+AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
 {
-       AudioRegion* other;
-       AudioRegion* region;
-       AudioRegion* top;
-       AudioRegion* bottom;
+       boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
+       boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
+       boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
+
+       for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
+               Crossfades::iterator tmp;
+               tmp = x;
+               ++tmp;
+
+               Crossfade *fade = 0;
+               
+               if ((*x)->_in == orig) {
+                       if (! (*x)->covers(right->position())) {
+                               fade = new Crossfade (**x, left, (*x)->_out);
+                       } else {
+                               // Overlap, the crossfade is copied on the left side of the right region instead
+                               fade = new Crossfade (**x, right, (*x)->_out);
+                       }
+               }
+               
+               if ((*x)->_out == orig) {
+                       if (! (*x)->covers(right->position())) {
+                               fade = new Crossfade (**x, (*x)->_in, right);
+                       } else {
+                               // Overlap, the crossfade is copied on the right side of the left region instead
+                               fade = new Crossfade (**x, (*x)->_in, left);
+                       }
+               }
+               
+               if (fade) {
+                       _crossfades.remove (*x);
+                       add_crossfade (*fade);
+               }
+               x = tmp;
+       }
+}
+
+void
+AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
+{
+       boost::shared_ptr<AudioRegion> other;
+       boost::shared_ptr<AudioRegion> region;
+       boost::shared_ptr<AudioRegion> top;
+       boost::shared_ptr<AudioRegion> bottom;
        Crossfade*   xfade;
 
        if (in_set_state || in_partition) {
                return;
        }
 
-       if ((region = dynamic_cast<AudioRegion*> (&r)) == 0) {
+       if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
                fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
                      << endmsg;
                return;
@@ -336,7 +400,7 @@ AudioPlaylist::check_dependents (Region& r, bool norefresh)
 
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
-               other = dynamic_cast<AudioRegion*> (*i);
+               other = boost::dynamic_pointer_cast<AudioRegion> (*i);
 
                if (other == region) {
                        continue;
@@ -375,17 +439,17 @@ AudioPlaylist::check_dependents (Region& r, bool norefresh)
                                           audio engineering.
                                        */
                                        
-                                       jack_nframes_t xfade_length = min ((jack_nframes_t) 720, top->length());
+                                       nframes_t xfade_length = min ((nframes_t) 720, top->length());
                                        
                                                            /*  in,      out */
-                                       xfade = new Crossfade (*top, *bottom, xfade_length, top->first_frame(), StartOfIn);
+                                       xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn);
                                        add_crossfade (*xfade);
-                                       xfade = new Crossfade (*bottom, *top, xfade_length, top->last_frame() - xfade_length, EndOfOut);
+                                       xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut);
                                        add_crossfade (*xfade);
                                        
                                } else {
                
-                                       xfade = new Crossfade (*other, *region, _session.get_xfade_model(), _session.get_crossfades_active());
+                                       xfade = new Crossfade (other, region, Config->get_xfade_model(), Config->get_crossfades_active());
                                        add_crossfade (*xfade);
                                }
                        } 
@@ -427,7 +491,7 @@ AudioPlaylist::add_crossfade (Crossfade& xfade)
        
 void AudioPlaylist::notify_crossfade_added (Crossfade *x)
 {
-       if (atomic_read(&block_notifications)) {
+       if (g_atomic_int_get(&block_notifications)) {
                _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
        } else {
                NewCrossfade (x); /* EMIT SIGNAL */
@@ -439,8 +503,8 @@ AudioPlaylist::crossfade_invalidated (Crossfade* xfade)
 {
        Crossfades::iterator i;
 
-       xfade->in().resume_fade_in ();
-       xfade->out().resume_fade_out ();
+       xfade->in()->resume_fade_in ();
+       xfade->out()->resume_fade_out ();
 
        if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
                _crossfades.erase (i);
@@ -473,7 +537,7 @@ AudioPlaylist::set_state (const XMLNode& node)
                        }
 
                        catch (failed_constructor& err) {
-                         //    cout << compose (_("could not create crossfade object in playlist %1"),
+                         //    cout << string_compose (_("could not create crossfade object in playlist %1"),
                          //      _name) 
                          //    << endl;
                                continue;
@@ -491,7 +555,7 @@ AudioPlaylist::set_state (const XMLNode& node)
                                _crossfades.push_back (xfade);
                                xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
                                xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
-                               /* no need to notify here */
+                               NewCrossfade(xfade);
                        } else {
                                delete xfade;
                        }
@@ -506,7 +570,7 @@ void
 AudioPlaylist::drop_all_states ()
 {
        set<Crossfade*> all_xfades;
-       set<Region*> all_regions;
+       set<boost::shared_ptr<Region> > all_regions;
 
        /* find every region we've ever used, and add it to the set of 
           all regions. same for xfades;
@@ -519,6 +583,7 @@ AudioPlaylist::drop_all_states ()
                for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
                        all_regions.insert (*r);
                }
+
                for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
                        all_xfades.insert (*xf);
                }
@@ -526,8 +591,8 @@ AudioPlaylist::drop_all_states ()
 
        /* now remove from the "all" lists every region that is in the current list. */
 
-       for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
-               set<Region*>::iterator x = all_regions.find (*i);
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               set<boost::shared_ptr<Region> >::iterator x = all_regions.find (*i);
                if (x != all_regions.end()) {
                        all_regions.erase (x);
                }
@@ -542,13 +607,6 @@ AudioPlaylist::drop_all_states ()
                }
        }
 
-       /* delete every region that is left - these are all things that are part of our "history" */
-
-       for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
-               (*ar)->unlock_sources ();
-               delete *ar;
-       }
-
        /* delete every crossfade that is left (ditto as per regions) */
 
        for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
@@ -576,7 +634,6 @@ AudioPlaylist::state_factory (std::string why) const
        for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
                state->crossfade_states.push_back ((*i)->get_memento());
        }
-
        return state;
 }
 
@@ -592,13 +649,13 @@ AudioPlaylist::restore_state (StateManager::State& state)
                regions = apstate->regions;
 
                for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
-                 *s;
+                       (*s) ();
                }
 
                _crossfades = apstate->crossfades;
                
                for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
-                 *s;
+                       (*s) ();
                }
 
                in_set_state = false;
@@ -611,21 +668,15 @@ AudioPlaylist::restore_state (StateManager::State& state)
 UndoAction
 AudioPlaylist::get_memento () const
 {
-  return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
+       return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
 }
 
 void
-AudioPlaylist::clear (bool with_delete, bool with_save)
+AudioPlaylist::clear (bool with_save)
 {
-       if (with_delete) {
-               for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
-                       delete *i;
-               }
-       }
-
        _crossfades.clear ();
        
-       Playlist::clear (with_delete, with_save);
+       Playlist::clear (with_save);
 }
 
 XMLNode&
@@ -645,7 +696,7 @@ AudioPlaylist::state (bool full_state)
 void
 AudioPlaylist::dump () const
 {
-       Region *r;
+       boost::shared_ptr<Region>r;
        Crossfade *x;
 
        cerr << "Playlist \"" << _name << "\" " << endl
@@ -667,9 +718,9 @@ AudioPlaylist::dump () const
        for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
                x = *i;
                cerr << "  xfade [" 
-                    << x->out().name()
+                    << x->out()->name()
                     << ','
-                    << x->in().name()
+                    << x->in()->name()
                     << " @ "
                     << x->position()
                     << " length = " 
@@ -681,9 +732,9 @@ AudioPlaylist::dump () const
 }
 
 bool
-AudioPlaylist::destroy_region (Region* region)
+AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 {
-       AudioRegion* r = dynamic_cast<AudioRegion*> (region);
+       boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
        bool changed = false;
        Crossfades::iterator c, ctmp;
        set<Crossfade*> unique_xfades;
@@ -706,7 +757,6 @@ AudioPlaylist::destroy_region (Region* region)
                        ++tmp;
                        
                        if ((*i) == region) {
-                               (*i)->unlock_sources ();
                                regions.erase (i);
                                changed = true;
                        }
@@ -719,7 +769,7 @@ AudioPlaylist::destroy_region (Region* region)
                ctmp = c;
                ++ctmp;
 
-               if ((*c)->involves (*r)) {
+               if ((*c)->involves (r)) {
                        unique_xfades.insert (*c);
                        _crossfades.erase (c);
                }
@@ -740,7 +790,7 @@ AudioPlaylist::destroy_region (Region* region)
                        ctmp = c;
                        ++ctmp;
 
-                       if ((*c)->involves (*r)) {
+                       if ((*c)->involves (r)) {
                                unique_xfades.insert (*c);
                                _crossfades.erase (c);
                        }
@@ -788,7 +838,7 @@ AudioPlaylist::destroy_region (Region* region)
 void
 AudioPlaylist::crossfade_changed (Change ignored)
 {
-       if (in_flush) {
+       if (in_flush || in_set_state) {
                return;
        }
 
@@ -799,37 +849,12 @@ AudioPlaylist::crossfade_changed (Change ignored)
        */
 
        maybe_save_state (_("xfade change"));
-       notify_modified ();
-}
-
-void
-AudioPlaylist::get_equivalent_regions (const AudioRegion& other, vector<AudioRegion*>& results)
-{
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-               AudioRegion* ar = dynamic_cast<AudioRegion*> (*i);
-
-               if (ar && ar->equivalent (other)) {
-                       results.push_back (ar);
-               }
-       }
-}
-
-void
-AudioPlaylist::get_region_list_equivalent_regions (const AudioRegion& other, vector<AudioRegion*>& results)
-{
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
-               AudioRegion* ar = dynamic_cast<AudioRegion*> (*i);
-               
-               if (ar && ar->region_list_equivalent (other)) {
-                       results.push_back (ar);
-               }
-       }
+       notify_modified ();
 }
 
 bool
-AudioPlaylist::region_changed (Change what_changed, Region* region)
+AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
 {
        if (in_flush || in_set_state) {
                return false;
@@ -848,7 +873,7 @@ AudioPlaylist::region_changed (Change what_changed, Region* region)
 
        maybe_save_state (_("region modified"));
 
-       if (parent_wants_notify || (what_changed & our_interests)) {
+       if ((parent_wants_notify || (what_changed & our_interests))) {
                notify_modified ();
        }
 
@@ -856,12 +881,12 @@ AudioPlaylist::region_changed (Change what_changed, Region* region)
 }
 
 void
-AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist)
+AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
 {
        RegionLock rlock (this);
 
        for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
-               jack_nframes_t start, end;
+               nframes_t start, end;
 
                start = (*i)->position();
                end = start + (*i)->overlap_length(); // not length(), important difference
@@ -871,3 +896,4 @@ AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist)
                } 
        }
 }
+