#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"
#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"
, _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)
, 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)
, _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)
{
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) {
* 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 */
_is_new = false;
+ emit_thread_start ();
+
/* hook us up to the engine since we are now completely constructed */
BootMessage (_("Connect to engine"));
return -1;
}
- /* TODO somehow keep bundles in sync with engine.
- *
- * eg. midi ports may come and go dynamically (currently both with jack as well
- * as with CoreMidi. But also changing the engine or device will do the trick..
- *
- * We need to keep track of bundles added during setup_bundles(),
- * and add/remove the changes.
- *
- * also doing so in a background thread would be nice (same_thread may be RT thread).
- * but in principle it's going to be sth like:
- * _engine.PortRegisteredOrUnregistered.connect_same_thread (*this, boost::bind (&Session::do_the_bundles, this));
- */
+ /* 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;
}
/* 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 _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
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) {
c->add_channel (_("mono"), DataType::AUDIO);
c->set_port (0, outputs[DataType::AUDIO][np]);
- add_bundle (c);
+ add_bundle (c, false);
}
/* stereo output bundles */
c->add_channel (_("R"), DataType::AUDIO);
c->set_port (1, outputs[DataType::AUDIO][np + 1]);
- add_bundle (c);
+ add_bundle (c, 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 */
c->add_channel (_("R"), DataType::AUDIO);
c->set_port (1, inputs[DataType::AUDIO][np + 1]);
- add_bundle (c);
+ add_bundle (c, false);
}
}
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 */
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
}
}
+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 ()
{
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()) {
}
}
+ } else {
+ clear_events (SessionEvent::AutoLoopDeclick);
+ clear_events (SessionEvent::AutoLoop);
}
last_loopend = location->end();
_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 */
update_skips (location, true);
}
-
+
set_dirty ();
}
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);
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);
}
}
}
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);
}
}
}
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);
}
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);
}
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
{