Replace thinning static state with parameter.
[ardour.git] / libs / ardour / session.cc
index fca74ee4a5db15847be87f237dedeb687cd3d32e..9c5b6fdccf242b0861f6e3f0ba54fd9770d4d689 100644 (file)
@@ -1235,9 +1235,9 @@ Session::set_auto_punch_location (Location* location)
 
        punch_connections.drop_connections ();
 
-       location->start_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, _1));
-       location->end_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, _1));
-       location->changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, _1));
+       location->StartChanged.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, location));
+       location->EndChanged.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, location));
+       location->Changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, location));
 
        location->set_auto_punch (true, this);
 
@@ -1277,9 +1277,9 @@ Session::set_auto_loop_location (Location* location)
 
        loop_connections.drop_connections ();
 
-       location->start_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
-       location->end_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
-       location->changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
+       location->StartChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
+       location->EndChanged.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
+       location->Changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, location));
 
        location->set_auto_loop (true, this);
 
@@ -1293,73 +1293,165 @@ Session::set_auto_loop_location (Location* location)
 }
 
 void
-Session::locations_added (Location *)
+Session::update_skips (Location* loc, bool consolidate)
 {
-       _locations->apply (*this, &Session::sync_locations_to_skips);
-       set_dirty ();
+        Locations::LocationList skips;
+
+        if (consolidate) {
+
+                skips = consolidate_skips (loc);
+
+        } else {
+                Locations::LocationList all_locations = _locations->list ();
+                
+                for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ++l) {
+                        if ((*l)->is_skip ()) {
+                                skips.push_back (*l);
+                        }
+                }
+        }
+
+        sync_locations_to_skips (skips);
 }
 
-void
-Session::locations_changed ()
+Locations::LocationList
+Session::consolidate_skips (Location* loc)
 {
-       _locations->apply (*this, &Session::handle_locations_changed);
+        Locations::LocationList all_locations = _locations->list ();
+        Locations::LocationList skips;
+
+        for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
+
+                if (!(*l)->is_skip ()) {
+                        ++l;
+                        continue;
+                }
+
+                /* don't test against self */
+
+                if (*l == loc) {
+                        ++l;
+                        continue;
+                }
+                        
+                switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
+                case Evoral::OverlapInternal:
+                case Evoral::OverlapExternal:
+                case Evoral::OverlapStart:
+                case Evoral::OverlapEnd:
+                        /* adjust new location to cover existing one */
+                        loc->set_start (min (loc->start(), (*l)->start()));
+                        loc->set_end (max (loc->end(), (*l)->end()));
+                        /* we don't need this one any more */
+                        _locations->remove (*l);
+                        /* the location has been deleted, so remove reference to it in our local list */
+                        l = all_locations.erase (l);
+                        break;
+
+                case Evoral::OverlapNone:
+                        skips.push_back (*l);
+                        ++l;
+                        break;
+                }
+        }
+
+        /* add the new one, which now covers the maximal appropriate range based on overlaps with existing skips */
+
+        skips.push_back (loc);
+
+        return skips;
 }
 
 void
-Session::handle_locations_changed (Locations::LocationList& locations)
+Session::sync_locations_to_skips (const Locations::LocationList& locations)
 {
-       Locations::LocationList::iterator i;
-       Location* location;
-       bool set_loop = false;
-       bool set_punch = false;
+       clear_events (SessionEvent::Skip);
 
-       for (i = locations.begin(); i != locations.end(); ++i) {
+       for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+                
+               Location* location = *i;
+                
+               if (location->is_skipping()) {
+                       SessionEvent* ev = new SessionEvent (SessionEvent::Skip, SessionEvent::Add, location->start(), location->end(), 1.0);
+                       queue_event (ev);
+               }
+       }
+}
 
-               location =* i;
+void
+Session::location_added (Location *location)
+{
+        if (location->is_auto_punch()) {
+                set_auto_punch_location (location);
+        }
 
-               if (location->is_auto_punch()) {
-                       set_auto_punch_location (location);
-                       set_punch = true;
-               }
-               if (location->is_auto_loop()) {
-                       set_auto_loop_location (location);
-                       set_loop = true;
-               }
+        if (location->is_auto_loop()) {
+                set_auto_loop_location (location);
+        }
+        
+        if (location->is_session_range()) {
+                /* no need for any signal handling or event setting with the session range,
+                   because we keep a direct reference to it and use its start/end directly.
+                */
+                _session_range_location = location;
+        }
 
-               if (location->is_session_range()) {
-                       _session_range_location = location;
-               }
-       }
+        if (location->is_skip()) {
+                /* listen for per-location signals that require us to update skip-locate events */
 
-       sync_locations_to_skips (locations);
+                location->StartChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->EndChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->Changed.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->FlagsChanged.connect_same_thread (skip_connections, boost::bind (&Session::update_skips, this, location, false));
 
-       if (!set_loop) {
-               set_auto_loop_location (0);
-       }
-       if (!set_punch) {
-               set_auto_punch_location (0);
-       }
+                update_skips (location, true);
+        }
 
-       set_dirty();
+       set_dirty ();
 }
 
 void
-Session::sync_locations_to_skips (Locations::LocationList& locations)
+Session::location_removed (Location *location)
 {
-       Locations::LocationList::iterator i;
-       Location* location;
+        if (location->is_auto_loop()) {
+                set_auto_loop_location (0);
+        }
+        
+        if (location->is_auto_punch()) {
+                set_auto_punch_location (0);
+        }
 
-       clear_events (SessionEvent::Skip);
+        if (location->is_session_range()) {
+                /* this is never supposed to happen */
+                error << _("programming error: session range removed!") << endl;
+        }
 
-       for (i = locations.begin(); i != locations.end(); ++i) {
+        if (location->is_skip()) {
+                
+                update_skips (location, false);
+        }
 
-               location = *i;
+       set_dirty ();
+}
 
-               if (location->is_skip()) {
-                       SessionEvent* ev = new SessionEvent (SessionEvent::Skip, SessionEvent::Add, location->start(), location->end(), 1.0);
-                       queue_event (ev);
-               }
-       }
+void
+Session::locations_changed ()
+{
+        _locations->apply (*this, &Session::_locations_changed);
+}
+
+void
+Session::_locations_changed (const Locations::LocationList& locations)
+{
+        /* There was some mass-change in the Locations object. 
+
+           We might be re-adding a location here but it doesn't actually matter
+           for all the locations that the Session takes an interest in.
+        */
+
+       for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+                location_added (*i);
+        }
 }
 
 void
@@ -1380,7 +1472,7 @@ Session::enable_record ()
                if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
 
                        _last_record_location = _transport_frame;
-                       _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+                       send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
 
                        if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
                                set_track_monitor_input_status (true);
@@ -1401,7 +1493,7 @@ Session::disable_record (bool rt_context, bool force)
 
                if ((!Config->get_latched_record_enable () && !play_loop) || force) {
                        g_atomic_int_set (&_record_status, Disabled);
-                       _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
+                       send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
                } else {
                        if (rs == Recording) {
                                g_atomic_int_set (&_record_status, Enabled);
@@ -1455,7 +1547,7 @@ Session::maybe_enable_record ()
                        enable_record ();
                }
        } else {
-               _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
+               send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
                RecordStateChanged (); /* EMIT SIGNAL */
        }
 
@@ -1469,30 +1561,10 @@ Session::audible_frame () const
        framepos_t tf;
        framecnt_t offset;
 
-       /* the first of these two possible settings for "offset"
-          mean that the audible frame is stationary until
-          audio emerges from the latency compensation
-          "pseudo-pipeline".
-
-          the second means that the audible frame is stationary
-          until audio would emerge from a physical port
-          in the absence of any plugin latency compensation
-       */
-
        offset = worst_playback_latency ();
 
-       if (offset > current_block_size) {
-               offset -= current_block_size;
-       } else {
-               /* XXX is this correct? if we have no external
-                  physical connections and everything is internal
-                  then surely this is zero? still, how
-                  likely is that anyway?
-               */
-               offset = current_block_size;
-       }
-
        if (synced_to_engine()) {
+               /* Note: this is basically just sync-to-JACK */
                tf = _engine.transport_frame();
        } else {
                tf = _transport_frame;
@@ -2072,6 +2144,14 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing
        }
 }
 
+void
+Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool reconnect_inputs, bool reconnect_outputs)
+{
+        /* TRX does stuff here, ardour does not (but probably should). This is called after an engine reset (in particular).
+         */
+}
+
+
 /** Caller must not hold process lock
  *  @param name_template string to use for the start of the name, or "" to use "Audio".
  */
@@ -2300,7 +2380,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                                /* generate a new name by adding a number to the end of the template name */
                                if (!find_route_name (route_name.c_str(), ++number, name, sizeof(name), true)) {
                                        fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
-                                       /*NOTREACHED*/
+                                       abort(); /*NOTREACHED*/
                                }
                        }
 
@@ -3654,7 +3734,7 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha
        return s;
 }
 
-/** Return a unique name based on \a owner_name for a new internal MIDI source */
+/** Return a unique name based on `base` for a new internal MIDI source */
 string
 Session::new_midi_source_path (const string& base)
 {
@@ -3961,7 +4041,7 @@ Session::available_capture_duration ()
                fatal << string_compose (_("programming error: %1"),
                                         X_("illegal native file data format"))
                      << endmsg;
-               /*NOTREACHED*/
+               abort(); /*NOTREACHED*/
        }
 
        double scale = 4096.0 / sample_bytes_on_disk;
@@ -4037,9 +4117,9 @@ Session::tempo_map_changed (const PropertyChange&)
 }
 
 void
-Session::update_locations_after_tempo_map_change (Locations::LocationList& loc)
+Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc)
 {
-       for (Locations::LocationList::iterator i = loc.begin(); i != loc.end(); ++i) {
+       for (Locations::LocationList::const_iterator i = loc.begin(); i != loc.end(); ++i) {
                (*i)->recompute_frames_from_bbt ();
        }
 }