X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=9df9f1940730ea15a9e300a5b9b9097b3b66dfc4;hb=4.1-78-g11e371c;hp=5bc609356a6d46b8a0e8ba749a4a43db6e7de3b1;hpb=f3f80900f14ae8f8d577f3eb098b5064d4aa0859;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 5bc609356a..9df9f19407 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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; } @@ -565,6 +585,11 @@ Session::destroy () /* 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; @@ -572,6 +597,8 @@ Session::destroy () 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 writer (_bundles); + boost::shared_ptr b = writer.get_copy (); + for (BundleList::iterator i = b->begin(); i != b->end();) { + if (boost::dynamic_pointer_cast(*i)) { + ++i; + continue; + } + i = b->erase(i); + } + } + vector inputs[DataType::num_types]; vector 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 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 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 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 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 @@ -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 p = _monitor_out->input()->ports().nth_audio_port (n); + boost::shared_ptr 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 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 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 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 rls = routes.reader (); + + PBD::Unwinder 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 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 (*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)); +} - if (location->is_session_range()) { - _session_range_location = location; +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, 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 midi_port_states; + EngineStateController::instance()->get_physical_midi_input_states (midi_port_states); + + std::vector::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 midi_port_states; + EngineStateController::instance()->get_physical_midi_output_states (midi_port_states); + + std::vector::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 > 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 (tr))); track_playlist_changed (boost::weak_ptr (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 mt = boost::dynamic_pointer_cast (tr); if (mt) { @@ -2484,7 +2871,7 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr 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 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 dest, boost::shared_ptr route) +Session::remove_routes (boost::shared_ptr routes_to_remove) { - if (route == _master_out) { - return; - } - - route->set_solo (false, this); - - { + { // RCU Writer scope RCUWriter writer (routes); boost::shared_ptr 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 (); - } - - 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 r = routes.reader (); - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr 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 (); + } + + 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 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 r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + boost::shared_ptr 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 uw (ignore_route_processor_changes, true); + (*iter)->remove_aux_or_listen (_monitor_out); + } + + boost::shared_ptr mt = boost::dynamic_pointer_cast (*iter); + if (mt && mt->step_editing()) { + if (_step_editors > 0) { + _step_editors--; + } + } - boost::shared_ptr mt = boost::dynamic_pointer_cast (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 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) +{ + boost::shared_ptr 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::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 sdirs = source_search_path (DataType::AUDIO); vector::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) return boost::shared_ptr(); } - 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 ( 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) +Session::add_bundle (boost::shared_ptr bundle, bool emit_signal) { { RCUWriter writer (_bundles); @@ -3956,7 +4422,9 @@ Session::add_bundle (boost::shared_ptr 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) } 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 -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 >& srcs, InterThreadInfo& itt, boost::shared_ptr endpoint, bool include_endpoint, @@ -4269,7 +4737,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end, { boost::shared_ptr result; boost::shared_ptr playlist; - boost::shared_ptr fsource; + boost::shared_ptr 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 ( - 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 >::iterator src = srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); - if (afs) + boost::shared_ptr ms; + if (afs) { afs->prepare_for_peakfile_writes (); + } else if ((ms = boost::dynamic_pointer_cast(*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 >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + boost::shared_ptr 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(*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 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 >::iterator src=srcs.begin(); src != srcs.end(); ++src) { boost::shared_ptr afs = boost::dynamic_pointer_cast(*src); + boost::shared_ptr ms; if (afs) { afs->update_header (position, *xnow, now); afs->flush_header (); + } else if ((ms = boost::dynamic_pointer_cast(*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 >::iterator src = srcs.begin(); src != srcs.end(); ++src) { - boost::shared_ptr afs = boost::dynamic_pointer_cast(*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(&_have_rec_enabled_track)) == 1; } +bool +Session::have_rec_disabled_track () const +{ + return g_atomic_int_get (const_cast(&_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 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 tr = boost::dynamic_pointer_cast (*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 r) void Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr r) { - RouteRemovedFromRouteGroup (rg, r); + update_route_record_state (); + RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */ } boost::shared_ptr