integrate Session::remove_routes() from Tracks and replace Session::remove_route...
[ardour.git] / libs / ardour / session.cc
index b7b5667d0663bfd960f84329a12d3d49a7009262..9df9f1940730ea15a9e300a5b9b9097b3b66dfc4 100644 (file)
@@ -66,6 +66,7 @@
 #include "ardour/control_protocol_manager.h"
 #include "ardour/data_type.h"
 #include "ardour/debug.h"
+#include "ardour/directory_names.h"
 #include "ardour/filename_extensions.h"
 #include "ardour/graph.h"
 #include "ardour/midiport_manager.h"
@@ -77,6 +78,7 @@
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/process_thread.h"
+#include "ardour/profile.h"
 #include "ardour/rc_configuration.h"
 #include "ardour/recent_sessions.h"
 #include "ardour/region.h"
@@ -91,7 +93,9 @@
 #include "ardour/smf_source.h"
 #include "ardour/source_factory.h"
 #include "ardour/speakers.h"
+#include "ardour/tempo.h"
 #include "ardour/track.h"
+#include "ardour/user_bundle.h"
 #include "ardour/utils.h"
 
 #include "midi++/port.h"
@@ -164,14 +168,13 @@ Session::Session (AudioEngine &eng,
        , _worst_input_latency (0)
        , _worst_track_latency (0)
        , _have_captured (false)
-       , _meter_hold (0)
-       , _meter_falloff (0)
        , _non_soloed_outs_muted (false)
        , _listen_cnt (0)
        , _solo_isolated_cnt (0)
        , _writable (false)
        , _was_seamless (Config->get_seamless_loop ())
        , _under_nsm_control (false)
+       , _xrun_count (0)
        , delta_accumulator_cnt (0)
        , average_slave_delta (1800) // !!! why 1800 ???
        , average_dir (0)
@@ -211,6 +214,9 @@ Session::Session (AudioEngine &eng,
        ,  cumulative_rf_motion (0)
        , rf_scale (1.0)
        , _locations (new Locations (*this))
+       , _ignore_skips_updates (false)
+       , _rt_thread_active (false)
+       , _rt_emit_pending (false)
        , step_speed (0)
        , outbound_mtc_timecode_frame (0)
        , next_quarter_frame_to_send (-1)
@@ -238,6 +244,7 @@ Session::Session (AudioEngine &eng,
        , _all_route_group (new RouteGroup (*this, "all"))
        , routes (new RouteList)
        , _adding_routes_in_progress (false)
+       , _route_deletion_in_progress (false)
        , destructive_index (0)
        , _track_number_decimals(1)
        , solo_update_disabled (false)
@@ -263,10 +270,11 @@ Session::Session (AudioEngine &eng,
        , first_file_header_format_reset (true)
        , have_looped (false)
        , _have_rec_enabled_track (false)
+    , _have_rec_disabled_track (true)
        , _step_editors (0)
        , _suspend_timecode_transmission (0)
        ,  _speakers (new Speakers)
-       , _order_hint (0)
+       , _order_hint (-1)
        , ignore_route_processor_changes (false)
        , _scene_changer (0)
        , _midi_ports (0)
@@ -274,6 +282,9 @@ Session::Session (AudioEngine &eng,
 {
        uint32_t sr = 0;
 
+       pthread_mutex_init (&_rt_emit_mutex, 0);
+       pthread_cond_init (&_rt_emit_cond, 0);
+
        pre_engine_init (fullpath);
        
        if (_is_new) {
@@ -298,8 +309,11 @@ Session::Session (AudioEngine &eng,
                 * of a template.
                 */
 
-               if (!mix_template.empty() && load_state (_current_snapshot_name)) {
-                       throw failed_constructor ();
+               if (!mix_template.empty()) { 
+                       if (load_state (_current_snapshot_name)) {
+                               throw failed_constructor ();
+                       }
+                       store_recent_templates (mix_template);
                }
 
                /* load default session properties - if any */
@@ -352,6 +366,8 @@ Session::Session (AudioEngine &eng,
 
        _is_new = false;
 
+       emit_thread_start ();
+
        /* hook us up to the engine since we are now completely constructed */
 
        BootMessage (_("Connect to engine"));
@@ -443,6 +459,10 @@ Session::immediately_post_engine ()
                return -1;
        }
 
+       /* TODO, connect in different thread. (PortRegisteredOrUnregistered may be in RT context)
+        * can we do that? */
+        _engine.PortRegisteredOrUnregistered.connect_same_thread (*this, boost::bind (&Session::setup_bundles, this));
+
        return 0;
 }
 
@@ -480,6 +500,7 @@ Session::destroy ()
        /* clear state tree so that no references to objects are held any more */
 
        delete state_tree;
+       state_tree = 0;
 
        /* reset dynamic state version back to default */
 
@@ -489,9 +510,13 @@ Session::destroy ()
        delete _butler;
        _butler = 0;
        
-       delete midi_control_ui;
        delete _all_route_group;
 
+       DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n");
+       for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
+               delete *i;
+       }
+
        if (click_data != default_click) {
                delete [] click_data;
        }
@@ -557,21 +582,23 @@ Session::destroy ()
                sources.clear ();
        }
 
-       DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n");
-       for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
-
-               delete *i;
-       }
-
        /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
        playlists.reset ();
 
+       emit_thread_terminate ();
+
+       pthread_cond_destroy (&_rt_emit_cond);
+       pthread_mutex_destroy (&_rt_emit_mutex);
+
        delete _scene_changer; _scene_changer = 0;
+       delete midi_control_ui; midi_control_ui = 0;
 
        delete _mmc; _mmc = 0;
        delete _midi_ports; _midi_ports = 0;
        delete _locations; _locations = 0;
 
+       delete _tempo_map;
+       
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
@@ -686,6 +713,19 @@ Session::setup_click_state (const XMLNode* node)
 void
 Session::setup_bundles ()
 {
+
+       {
+               RCUWriter<BundleList> writer (_bundles);
+               boost::shared_ptr<BundleList> b = writer.get_copy ();
+               for (BundleList::iterator i = b->begin(); i != b->end();) {
+                       if (boost::dynamic_pointer_cast<UserBundle>(*i)) {
+                               ++i;
+                               continue;
+                       }
+                       i = b->erase(i);
+               }
+       }
+
        vector<string> inputs[DataType::num_types];
        vector<string> outputs[DataType::num_types];
        for (uint32_t i = 0; i < DataType::num_types; ++i) {
@@ -705,13 +745,18 @@ Session::setup_bundles ()
 
        for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) {
                char buf[32];
-               snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
+               std::string pn = _engine.get_pretty_name_by_name (outputs[DataType::AUDIO][np]);
+               if (!pn.empty()) {
+                       snprintf (buf, sizeof (buf), _("out %s"), pn.substr(0,12).c_str());
+               } else {
+                       snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
+               }
 
                boost::shared_ptr<Bundle> c (new Bundle (buf, true));
                c->add_channel (_("mono"), DataType::AUDIO);
                c->set_port (0, outputs[DataType::AUDIO][np]);
 
-               add_bundle (c);
+               add_bundle (c, false);
        }
 
        /* stereo output bundles */
@@ -726,7 +771,7 @@ Session::setup_bundles ()
                        c->add_channel (_("R"), DataType::AUDIO);
                        c->set_port (1, outputs[DataType::AUDIO][np + 1]);
 
-                       add_bundle (c);
+                       add_bundle (c, false);
                }
        }
 
@@ -734,13 +779,18 @@ Session::setup_bundles ()
 
        for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) {
                char buf[32];
-               snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
+               std::string pn = _engine.get_pretty_name_by_name (inputs[DataType::AUDIO][np]);
+               if (!pn.empty()) {
+                       snprintf (buf, sizeof (buf), _("in %s"), pn.substr(0,12).c_str());
+               } else {
+                       snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
+               }
 
                boost::shared_ptr<Bundle> c (new Bundle (buf, false));
                c->add_channel (_("mono"), DataType::AUDIO);
                c->set_port (0, inputs[DataType::AUDIO][np]);
 
-               add_bundle (c);
+               add_bundle (c, false);
        }
 
        /* stereo input bundles */
@@ -756,7 +806,7 @@ Session::setup_bundles ()
                        c->add_channel (_("R"), DataType::AUDIO);
                        c->set_port (1, inputs[DataType::AUDIO][np + 1]);
 
-                       add_bundle (c);
+                       add_bundle (c, false);
                }
        }
 
@@ -764,26 +814,36 @@ Session::setup_bundles ()
 
        for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) {
                string n = inputs[DataType::MIDI][np];
-               boost::erase_first (n, X_("alsa_pcm:"));
-
+               std::string pn = _engine.get_pretty_name_by_name (n);
+               if (!pn.empty()) {
+                       n = pn;
+               } else {
+                       boost::erase_first (n, X_("alsa_pcm:"));
+               }
                boost::shared_ptr<Bundle> c (new Bundle (n, false));
                c->add_channel ("", DataType::MIDI);
                c->set_port (0, inputs[DataType::MIDI][np]);
-               add_bundle (c);
+               add_bundle (c, false);
        }
 
        /* MIDI output bundles */
 
        for (uint32_t np = 0; np < outputs[DataType::MIDI].size(); ++np) {
                string n = outputs[DataType::MIDI][np];
-               boost::erase_first (n, X_("alsa_pcm:"));
-
+               std::string pn = _engine.get_pretty_name_by_name (n);
+               if (!pn.empty()) {
+                       n = pn;
+               } else {
+                       boost::erase_first (n, X_("alsa_pcm:"));
+               }
                boost::shared_ptr<Bundle> c (new Bundle (n, true));
                c->add_channel ("", DataType::MIDI);
                c->set_port (0, outputs[DataType::MIDI][np]);
-               add_bundle (c);
+               add_bundle (c, false);
        }
 
+       // we trust the backend to only calls us if there's a change
+       BundleAddedOrRemoved (); /* EMIT SIGNAL */
 }
 
 void
@@ -881,7 +941,7 @@ Session::add_monitor_section ()
                return;
        }
 
-       boost::shared_ptr<Route> r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO));
+       boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO));
 
        if (r->init ()) {
                return;
@@ -1020,6 +1080,121 @@ Session::add_monitor_section ()
        }
 }
 
+void
+Session::reset_monitor_section ()
+{
+       /* Process lock should be held by the caller.*/
+
+       if (!_monitor_out) {
+               return;
+       }
+
+       uint32_t limit = _master_out->n_outputs().n_audio();
+
+       /* connect the inputs to the master bus outputs. this
+        * represents a separate data feed from the internal sends from
+        * each route. as of jan 2011, it allows the monitor section to
+        * conditionally ignore either the internal sends or the normal
+        * input feed, but we should really find a better way to do
+        * this, i think.
+        */
+
+       _master_out->output()->disconnect (this);
+       _monitor_out->output()->disconnect (this);
+
+       _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this);
+       _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+
+       for (uint32_t n = 0; n < limit; ++n) {
+               boost::shared_ptr<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
+               boost::shared_ptr<AudioPort> o = _master_out->output()->ports().nth_audio_port (n);
+
+               if (o) {
+                       string connect_to = o->name();
+                       if (_monitor_out->input()->connect (p, connect_to, this)) {
+                               error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to)
+                                     << endmsg;
+                               break;
+                       }
+               }
+       }
+
+       /* connect monitor section to physical outs
+        */
+
+       if (Config->get_auto_connect_standard_busses()) {
+
+               if (!Config->get_monitor_bus_preferred_bundle().empty()) {
+
+                       boost::shared_ptr<Bundle> b = bundle_by_name (Config->get_monitor_bus_preferred_bundle());
+
+                       if (b) {
+                               _monitor_out->output()->connect_ports_to_bundle (b, true, this);
+                       } else {
+                               warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"),
+                                                          Config->get_monitor_bus_preferred_bundle())
+                                       << endmsg;
+                       }
+
+               } else {
+
+                       /* Monitor bus is audio only */
+
+                       vector<string> outputs[DataType::num_types];
+
+                       for (uint32_t i = 0; i < DataType::num_types; ++i) {
+                               _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
+                       }
+
+                       uint32_t mod = outputs[DataType::AUDIO].size();
+                       uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO);
+
+                       if (mod != 0) {
+
+                               for (uint32_t n = 0; n < limit; ++n) {
+
+                                       boost::shared_ptr<Port> p = _monitor_out->output()->ports().port(DataType::AUDIO, n);
+                                       string connect_to;
+                                       if (outputs[DataType::AUDIO].size() > (n % mod)) {
+                                               connect_to = outputs[DataType::AUDIO][n % mod];
+                                       }
+
+                                       if (!connect_to.empty()) {
+                                               if (_monitor_out->output()->connect (p, connect_to, this)) {
+                                                       error << string_compose (
+                                                               _("cannot connect control output %1 to %2"),
+                                                               n, connect_to)
+                                                             << endmsg;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Connect tracks to monitor section. Note that in an
+          existing session, the internal sends will already exist, but we want the
+          routes to notice that they connect to the control out specifically.
+       */
+
+
+       boost::shared_ptr<RouteList> rls = routes.reader ();
+
+       PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+
+       for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) {
+
+               if ((*x)->is_monitor()) {
+                       /* relax */
+               } else if ((*x)->is_master()) {
+                       /* relax */
+               } else {
+                       (*x)->enable_monitor_send ();
+               }
+       }
+}
+
 void
 Session::hookup_io ()
 {
@@ -1177,10 +1352,10 @@ Session::auto_loop_changed (Location* location)
        framepos_t dcp;
        framecnt_t dcl;
        auto_loop_declick_range (location, dcp, dcl);
-       replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
 
        if (transport_rolling() && play_loop) {
 
+               replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
 
                // if (_transport_frame > location->end()) {
 
@@ -1204,9 +1379,13 @@ Session::auto_loop_changed (Location* location)
                        }
 
                }
+       } else {
+               clear_events (SessionEvent::AutoLoopDeclick);
+               clear_events (SessionEvent::AutoLoop);
        }
 
        last_loopend = location->end();
+       set_dirty ();
 }
 
 void
@@ -1235,9 +1414,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);
 
@@ -1246,6 +1425,25 @@ Session::set_auto_punch_location (Location* location)
        auto_punch_location_changed (location);
 }
 
+void
+Session::set_session_extents (framepos_t start, framepos_t end)
+{
+       Location* existing;
+       if ((existing = _locations->session_range_location()) == 0) {
+               //if there is no existing session, we need to make a new session location  (should never happen)
+               existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange);
+       }
+       
+       if (end <= start) {
+               error << _("Session: you can't use that location for session start/end)") << endmsg;
+               return;
+       }
+
+       existing->set( start, end );
+       
+       set_dirty();
+}
+
 void
 Session::set_auto_loop_location (Location* location)
 {
@@ -1277,9 +1475,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,51 +1491,188 @@ Session::set_auto_loop_location (Location* location)
 }
 
 void
-Session::locations_added (Location *)
+Session::update_loop (Location*)
 {
        set_dirty ();
 }
 
 void
-Session::locations_changed ()
+Session::update_marks (Location*)
 {
-       _locations->apply (*this, &Session::handle_locations_changed);
+       set_dirty ();
 }
 
 void
-Session::handle_locations_changed (Locations::LocationList& locations)
+Session::update_skips (Location* loc, bool consolidate)
 {
-       Locations::LocationList::iterator i;
-       Location* location;
-       bool set_loop = false;
-       bool set_punch = false;
+    if (_ignore_skips_updates) {
+        return;
+    }
+    
+       Locations::LocationList skips;
 
-       for (i = locations.begin(); i != locations.end(); ++i) {
+        if (consolidate) {
+               PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
+               consolidate_skips (loc);
+        }
 
-               location =* i;
+       sync_locations_to_skips ();
+        
+       set_dirty ();
+}
 
-               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;
-               }
+void
+Session::consolidate_skips (Location* loc)
+{
+        Locations::LocationList all_locations = _locations->list ();
+
+        for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
+
+                if (!(*l)->is_skip ()) {
+                        ++l;
+                        continue;
+                }
+
+                /* don't test against self */
 
-               if (location->is_session_range()) {
-                       _session_range_location = location;
+                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:
+                        ++l;
+                        break;
+                }
+        }
+}
+
+void
+Session::sync_locations_to_skips ()
+{
+       /* This happens asynchronously (in the audioengine thread). After the clear is done, we will call
+        * Session::_sync_locations_to_skips() from the audioengine thread.
+        */
+       clear_events (SessionEvent::Skip, boost::bind (&Session::_sync_locations_to_skips, this));
+}
+
+void
+Session::_sync_locations_to_skips ()
+{
+       /* called as a callback after existing Skip events have been cleared from a realtime audioengine thread */
+
+       Locations::LocationList const & locs (_locations->list());
+
+       for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
+               
+               Location* location = *i;
+               
+               if (location->is_skip() && location->is_skipping()) {
+                       SessionEvent* ev = new SessionEvent (SessionEvent::Skip, SessionEvent::Add, location->start(), location->end(), 1.0);
+                       queue_event (ev);
                }
        }
+}
 
-       if (!set_loop) {
-               set_auto_loop_location (0);
-       }
-       if (!set_punch) {
-               set_auto_punch_location (0);
-       }
 
-       set_dirty();
+void
+Session::location_added (Location *location)
+{
+        if (location->is_auto_punch()) {
+                set_auto_punch_location (location);
+        }
+
+        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_mark()) {
+                /* listen for per-location signals that require us to do any * global updates for marks */
+
+                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+        }
+
+        if (location->is_skip()) {
+                /* listen for per-location signals that require us to update skip-locate events */
+
+                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
+
+                update_skips (location, true);
+        }
+        
+       set_dirty ();
+}
+
+void
+Session::location_removed (Location *location)
+{
+        if (location->is_auto_loop()) {
+               set_auto_loop_location (0);
+               set_track_loop (false);
+        }
+        
+        if (location->is_auto_punch()) {
+                set_auto_punch_location (0);
+        }
+
+        if (location->is_session_range()) {
+                /* this is never supposed to happen */
+                error << _("programming error: session range removed!") << endl;
+        }
+
+        if (location->is_skip()) {
+                
+                update_skips (location, false);
+        }
+
+       set_dirty ();
+}
+
+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
@@ -1358,7 +1693,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);
@@ -1379,7 +1714,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);
@@ -1433,7 +1768,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 */
        }
 
@@ -1447,30 +1782,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;
@@ -1895,7 +2210,11 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
   failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
-               add_routes (new_routes, true, true, true);
+               if (Profile->get_trx()) {
+                       add_routes (new_routes, false, false, false);
+               } else {
+                       add_routes (new_routes, true, true, false);
+               }
 
                if (instrument) {
                        for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) {
@@ -2050,6 +2369,51 @@ 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).
+         */
+}
+
+#ifdef USE_TRACKS_CODE_FEATURES
+
+void
+Session::reconnect_midi_scene_ports(bool inputs)
+{
+       if (inputs) {
+               scene_in()->disconnect_all ();
+        
+               std::vector<EngineStateController::MidiPortState> midi_port_states;
+               EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
+        
+               std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+        
+               for (; state_iter != midi_port_states.end(); ++state_iter) {
+                       if (state_iter->active && state_iter->available && state_iter->connected) {
+                               scene_in()->connect (state_iter->name);
+                       }
+               }
+
+       } else {
+               scene_out()->disconnect_all ();
+
+               std::vector<EngineStateController::MidiPortState> midi_port_states;
+               EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
+        
+               std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+        
+               for (; state_iter != midi_port_states.end(); ++state_iter) {
+                       if (state_iter->active && state_iter->available && state_iter->connected) {
+                               scene_out()->connect (state_iter->name);
+                       }
+               }
+
+       }
+}
+
+#endif
+
 /** Caller must not hold process lock
  *  @param name_template string to use for the start of the name, or "" to use "Audio".
  */
@@ -2063,10 +2427,19 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
        RouteList new_routes;
        list<boost::shared_ptr<AudioTrack> > ret;
 
-       bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Audio");
+       string name_pattern;
 
+       if (Profile->get_trx() ) {
+               name_pattern = "Track ";
+       } else {
+               name_pattern = "Audio ";
+       }
+    
+       bool const use_number = (how_many != 1) || name_template.empty () || name_template == _(name_pattern.c_str() );
+       
        while (how_many) {
-               if (!find_route_name (name_template.empty() ? _("Audio") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) {
+
+               if (!find_route_name (name_template.empty() ? _(name_pattern.c_str()) : name_template, ++track_id, track_name, sizeof(track_name), use_number)) {
                        error << "cannot find name for new audio track" << endmsg;
                        goto failed;
                }
@@ -2137,7 +2510,11 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
   failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
-               add_routes (new_routes, true, true, true);
+               if (Profile->get_trx()) {
+                       add_routes (new_routes, false, false, false);
+               } else {
+                       add_routes (new_routes, true, true, false);
+               }
        }
 
        return ret;
@@ -2223,7 +2600,11 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
   failure:
        if (!ret.empty()) {
                StateProtector sp (this);
-               add_routes (ret, false, true, true); // autoconnect outputs only
+               if (Profile->get_trx()) {
+                       add_routes (ret, false, false, false);
+               } else {
+                       add_routes (ret, false, true, true); // autoconnect // outputs only
+               }
        }
 
        return ret;
@@ -2278,7 +2659,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*/
                                }
                        }
 
@@ -2340,7 +2721,11 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
   out:
        if (!ret.empty()) {
                StateProtector sp (this);
-               add_routes (ret, true, true, true);
+               if (Profile->get_trx()) {
+                       add_routes (ret, false, false, false);
+               } else {
+                       add_routes (ret, true, true, false);
+               }
                IO::enable_connecting ();
        }
 
@@ -2371,6 +2756,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
        
        reassign_track_numbers();
 
+       update_route_record_state ();
+    
        RouteAdded (new_routes); /* EMIT SIGNAL */
 }
 
@@ -2381,9 +2768,9 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
         ChanCount existing_outputs;
        uint32_t order = next_control_id();
 
-       if (_order_hint != 0) {
+       if (_order_hint > -1) {
                order = _order_hint;
-               _order_hint = 0;
+               _order_hint = -1;
        }
 
         count_existing_track_channels (existing_inputs, existing_outputs);
@@ -2428,7 +2815,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                if (tr) {
                        tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr)));
                        track_playlist_changed (boost::weak_ptr<Track> (tr));
-                       tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this));
+                       tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
 
                        boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
                        if (mt) {
@@ -2484,7 +2871,7 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr<Route> dest)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((s = (*i)->internal_send_for (dest)) != 0) {
-                       s->amp()->gain_control()->set_value (0.0);
+                       s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO);
                }
        }
 }
@@ -2497,7 +2884,7 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr<Route> dest)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((s = (*i)->internal_send_for (dest)) != 0) {
-                       s->amp()->gain_control()->set_value (1.0);
+                       s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY);
                }
        }
 }
@@ -2562,100 +2949,128 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro
        graph_reordered ();
 }
 
+
 void
-Session::remove_route (boost::shared_ptr<Route> route)
+Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 {
-       if (route == _master_out) {
-               return;
-       }
-
-       route->set_solo (false, this);
-
-       {
+       { // RCU Writer scope
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> rs = writer.get_copy ();
-
-               rs->remove (route);
-
-               /* deleting the master out seems like a dumb
-                  idea, but its more of a UI policy issue
-                  than our concern.
-               */
-
-               if (route == _master_out) {
-                       _master_out = boost::shared_ptr<Route> ();
-               }
-
-               if (route == _monitor_out) {
-                       _monitor_out.reset ();
-               }
-
-               /* writer goes out of scope, forces route list update */
-       }
-
-       update_route_solo_state ();
-
-       // We need to disconnect the route's inputs and outputs
-
-       route->input()->disconnect (0);
-       route->output()->disconnect (0);
-
-       /* if the route had internal sends sending to it, remove them */
-       if (route->internal_return()) {
-
-               boost::shared_ptr<RouteList> r = routes.reader ();
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       boost::shared_ptr<Send> s = (*i)->internal_send_for (route);
-                       if (s) {
-                               (*i)->remove_processor (s);
+        
+        
+               for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
+            
+                       if (*iter == _master_out) {
+                               continue;
+                       }
+            
+                       (*iter)->set_solo (false, this);
+            
+                       rs->remove (*iter);
+            
+                       /* deleting the master out seems like a dumb
+                          idea, but its more of a UI policy issue
+                          than our concern.
+                       */
+            
+                       if (*iter == _master_out) {
+                               _master_out = boost::shared_ptr<Route> ();
+                       }
+            
+                       if (*iter == _monitor_out) {
+                               _monitor_out.reset ();
                        }
-               }
-       }
 
-       /* if the monitoring section had a pointer to this route, remove it */
-       if (_monitor_out && !route->is_master() && !route->is_monitor()) {
-               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
-               PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
-               route->remove_aux_or_listen (_monitor_out);
-       }
+                       update_route_solo_state ();
+            
+                       // We need to disconnect the route's inputs and outputs
+            
+                       (*iter)->input()->disconnect (0);
+                       (*iter)->output()->disconnect (0);
+            
+                       /* if the route had internal sends sending to it, remove them */
+                       if ((*iter)->internal_return()) {
+                
+                               boost::shared_ptr<RouteList> r = routes.reader ();
+                               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                                       boost::shared_ptr<Send> s = (*i)->internal_send_for (*iter);
+                                       if (s) {
+                                               (*i)->remove_processor (s);
+                                       }
+                               }
+                       }
+            
+                       /* if the monitoring section had a pointer to this route, remove it */
+                       if (_monitor_out && !(*iter)->is_master() && !(*iter)->is_monitor()) {
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+                               (*iter)->remove_aux_or_listen (_monitor_out);
+                       }
+            
+                       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (*iter);
+                       if (mt && mt->step_editing()) {
+                               if (_step_editors > 0) {
+                                       _step_editors--;
+                               }
+                       }
 
-       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
-       if (mt && mt->step_editing()) {
-               if (_step_editors > 0) {
-                       _step_editors--;
+                       RouteAddedOrRemoved (false); /* EMIT SIGNAL */
                }
-       }
+    
+               /* writer goes out of scope, forces route list update */
 
+       } // end of RCU Writer scope
+    
        update_latency_compensation ();
        set_dirty();
-
+    
        /* Re-sort routes to remove the graph's current references to the one that is
         * going away, then flush old references out of the graph.
+        * Wave Tracks: reconnect routes
         */
 
-       resort_routes ();
+       if (ARDOUR::Profile->get_trx () ) {
+               reconnect_existing_routes(true, false);
+       } else {
+               resort_routes ();
+       }
+    
        if (_process_graph) {
                _process_graph->clear_other_chain ();
        }
-
+    
        /* get rid of it from the dead wood collection in the route list manager */
-
        /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
-
+    
        routes.flush ();
+    
+       /* try to cause everyone to drop their references
+        * and unregister ports from the backend
+        */
+       PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
 
-       /* try to cause everyone to drop their references */
-
-       route->drop_references ();
-
+       for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
+               (*iter)->drop_references ();
+       }
+    
        Route::RemoteControlIDChange(); /* EMIT SIGNAL */
-
+    
        /* save the new state of the world */
-
+    
        if (save_state (_current_snapshot_name)) {
                save_history (_current_snapshot_name);
        }
+
        reassign_track_numbers();
+       update_route_record_state ();
+}
+
+void
+Session::remove_route (boost::shared_ptr<Route> route)
+{
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+       remove_routes (rl);
 }
 
 void
@@ -2849,6 +3264,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p
 
        for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name()));
+               (*i)->act_on_mute ();
                (*i)->mute_changed (this);
        }
 
@@ -3440,6 +3856,41 @@ Session::count_sources_by_origin (const string& path)
 string
 Session::peak_path (string base) const
 {
+       if (Glib::path_is_absolute (base)) {
+
+               /* rip the session dir from the audiofile source */
+
+               string session_path;
+               string interchange_dir_string = string (interchange_dir_name) + G_DIR_SEPARATOR;
+               bool in_another_session = true;
+               
+               if (base.find (interchange_dir_string) != string::npos) {
+               
+                       session_path = Glib::path_get_dirname (base); /* now ends in audiofiles */
+                       session_path = Glib::path_get_dirname (session_path); /* now ends in session name */
+                       session_path = Glib::path_get_dirname (session_path); /* now ends in interchange */
+                       session_path = Glib::path_get_dirname (session_path); /* now has session path */
+
+                       /* see if it is within our session */
+
+                       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+                               if (i->path == session_path) {
+                                       in_another_session = false;
+                                       break;
+                               }
+                       }
+               } else {
+                       in_another_session = false;
+               }
+               
+
+               if (in_another_session) {
+                       SessionDirectory sd (session_path);
+                       return Glib::build_filename (sd.peak_path(), Glib::path_get_basename (base) + peakfile_suffix);
+               }
+       }
+
+       base = Glib::path_get_basename (base);
        return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix);
 }
 
@@ -3503,8 +3954,19 @@ Session::new_audio_source_path_for_embedded (const std::string& path)
        return newpath;
 }
 
+/** Return true if there are no audio file sources that use @param name as 
+ * the filename component of their path. 
+ *
+ * Return false otherwise.
+ *
+ * This method MUST ONLY be used to check in-session, mono files since it 
+ * hard-codes the channel of the audio file source we are looking for as zero.
+ * 
+ * If/when Ardour supports native files in non-mono formats, the logic here
+ * will need to be revisited.
+ */
 bool
-Session::audio_source_name_is_unique (const string& name, uint32_t chan)
+Session::audio_source_name_is_unique (const string& name)
 {
        std::vector<string> sdirs = source_search_path (DataType::AUDIO);
        vector<space_and_path>::iterator i;
@@ -3537,7 +3999,7 @@ Session::audio_source_name_is_unique (const string& name, uint32_t chan)
                
                string possible_path = Glib::build_filename (spath, name);
 
-               if (audio_source_by_path_and_channel (possible_path, chan)) {
+               if (audio_source_by_path_and_channel (possible_path, 0)) {
                        existing++;
                        break;
                }
@@ -3605,7 +4067,7 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha
 
                possible_name = format_audio_source_name (legalized, nchan, chan, destructive, take_required, cnt, some_related_source_name_exists);
                
-               if (audio_source_name_is_unique (possible_name, chan)) {
+               if (audio_source_name_is_unique (possible_name)) {
                        break;
                }
                
@@ -3632,7 +4094,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)
 {
@@ -3761,7 +4223,11 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
                return boost::shared_ptr<MidiSource>();
        }
 
-       const string path = new_midi_source_path (name);
+       /* MIDI files are small, just put them in the first location of the
+          session source search path.
+       */
+
+       const string path = Glib::build_filename (source_search_path (DataType::MIDI).front(), name);
 
        return boost::dynamic_pointer_cast<SMFSource> (
                SourceFactory::createWritable (
@@ -3935,7 +4401,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;
@@ -3948,7 +4414,7 @@ Session::available_capture_duration ()
 }
 
 void
-Session::add_bundle (boost::shared_ptr<Bundle> bundle)
+Session::add_bundle (boost::shared_ptr<Bundle> bundle, bool emit_signal)
 {
        {
                RCUWriter<BundleList> writer (_bundles);
@@ -3956,7 +4422,9 @@ Session::add_bundle (boost::shared_ptr<Bundle> bundle)
                b->push_back (bundle);
        }
 
-       BundleAdded (bundle); /* EMIT SIGNAL */
+       if (emit_signal) {
+               BundleAddedOrRemoved (); /* EMIT SIGNAL */
+       }
 
        set_dirty();
 }
@@ -3978,7 +4446,7 @@ Session::remove_bundle (boost::shared_ptr<Bundle> bundle)
        }
 
        if (removed) {
-                BundleRemoved (bundle); /* EMIT SIGNAL */
+                BundleAddedOrRemoved (); /* EMIT SIGNAL */
        }
 
        set_dirty();
@@ -4011,9 +4479,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 ();
        }
 }
@@ -4261,7 +4729,7 @@ Session::freeze_all (InterThreadInfo& itt)
 }
 
 boost::shared_ptr<Region>
-Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
+Session::write_one_track (Track& track, framepos_t start, framepos_t end,
                          bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
                          InterThreadInfo& itt, 
                          boost::shared_ptr<Processor> endpoint, bool include_endpoint,
@@ -4269,7 +4737,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
 {
        boost::shared_ptr<Region> result;
        boost::shared_ptr<Playlist> playlist;
-       boost::shared_ptr<AudioFileSource> fsource;
+       boost::shared_ptr<Source> source;
        ChanCount diskstream_channels (track.n_channels());
        framepos_t position;
        framecnt_t this_chunk;
@@ -4291,8 +4759,8 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
        diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
                        include_endpoint, for_export, for_freeze);
 
-       if (diskstream_channels.n_audio() < 1) {
-               error << _("Cannot write a range with no audio.") << endmsg;
+       if (diskstream_channels.n(track.data_type()) < 1) {
+               error << _("Cannot write a range with no data.") << endmsg;
                return result;
        }
 
@@ -4318,26 +4786,27 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
 
        legal_playlist_name = legalize_for_path (playlist->name());
 
-       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
+       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(track.data_type()); ++chan_n) {
 
                string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
-               string path = new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true);
+               string path = ((track.data_type() == DataType::AUDIO)
+                              ? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true)
+                              : new_midi_source_path (legal_playlist_name));
                
                if (path.empty()) {
                        goto out;
                }
 
                try {
-                       fsource = boost::dynamic_pointer_cast<AudioFileSource> (
-                               SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()));
+                       source = SourceFactory::createWritable (track.data_type(), *this, path, false, frame_rate());
                }
 
                catch (failed_constructor& err) {
-                       error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg;
+                       error << string_compose (_("cannot create new file \"%1\" for %2"), path, track.name()) << endmsg;
                        goto out;
                }
 
-               srcs.push_back (fsource);
+               srcs.push_back (source);
        }
 
        /* tell redirects that care that we are about to use a much larger
@@ -4361,8 +4830,13 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
 
        for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
                boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
-               if (afs)
+               boost::shared_ptr<MidiSource> ms;
+               if (afs) {
                        afs->prepare_for_peakfile_writes ();
+               } else if ((ms = boost::dynamic_pointer_cast<MidiSource>(*src))) {
+                       Source::Lock lock(ms->mutex());
+                       ms->mark_streaming_write_started(lock);
+               }
        }
 
        while (to_do && !itt.cancel) {
@@ -4387,11 +4861,21 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
                uint32_t n = 0;
                for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
                        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+                       boost::shared_ptr<MidiSource> ms;
 
                        if (afs) {
                                if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) {
                                        goto out;
                                }
+                       } else if ((ms = boost::dynamic_pointer_cast<MidiSource>(*src))) {
+                               Source::Lock lock(ms->mutex());
+
+                               const MidiBuffer& buf = buffers.get_midi(0);
+                               for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
+                                       Evoral::Event<framepos_t> ev = *i;
+                                       ev.set_time(ev.time() - position);
+                                       ms->append_event_frames(lock, ev, ms->timeline_position());
+                               }
                        }
                }
                latency_skip = 0;
@@ -4428,10 +4912,14 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
 
                for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
                        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+                       boost::shared_ptr<MidiSource> ms;
 
                        if (afs) {
                                afs->update_header (position, *xnow, now);
                                afs->flush_header ();
+                       } else if ((ms = boost::dynamic_pointer_cast<MidiSource>(*src))) {
+                               Source::Lock lock(ms->mutex());
+                               ms->mark_streaming_write_completed(lock);
                        }
                }
 
@@ -4450,12 +4938,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
   out:
        if (!result) {
                for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
-                       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
-
-                       if (afs) {
-                               afs->mark_for_remove ();
-                       }
-
+                       (*src)->mark_for_remove ();
                        (*src)->drop_references ();
                }
 
@@ -4486,6 +4969,12 @@ Session::gain_automation_buffer() const
        return ProcessThread::gain_automation_buffer ();
 }
 
+gain_t*
+Session::trim_automation_buffer() const
+{
+       return ProcessThread::trim_automation_buffer ();
+}
+
 gain_t*
 Session::send_gain_automation_buffer() const
 {
@@ -4566,9 +5055,15 @@ Session::have_rec_enabled_track () const
        return g_atomic_int_get (const_cast<gint*>(&_have_rec_enabled_track)) == 1;
 }
 
+bool
+Session::have_rec_disabled_track () const
+{
+    return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
+}
+
 /** Update the state of our rec-enabled tracks flag */
 void
-Session::update_have_rec_enabled_track ()
+Session::update_route_record_state ()
 {
        boost::shared_ptr<RouteList> rl = routes.reader ();
        RouteList::iterator i = rl->begin();
@@ -4589,6 +5084,20 @@ Session::update_have_rec_enabled_track ()
        if (g_atomic_int_get (&_have_rec_enabled_track) != old) {
                RecordStateChanged (); /* EMIT SIGNAL */
        }
+
+    
+    i = rl->begin();
+       while (i != rl->end ()) {
+        
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (tr && !tr->record_enabled ()) {
+                       break;
+               }
+        
+               ++i;
+       }
+    
+    g_atomic_int_set (&_have_rec_disabled_track, i != rl->end () ? 1 : 0);
 }
 
 void
@@ -4631,7 +5140,8 @@ Session::route_added_to_route_group (RouteGroup* rg, boost::weak_ptr<Route> r)
 void
 Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr<Route> r)
 {
-       RouteRemovedFromRouteGroup (rg, r);
+       update_route_record_state ();
+       RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */
 }
 
 boost::shared_ptr<RouteList>