separate solo & listen. some minor fixes and additional related fixes still to come

git-svn-id: svn://localhost/ardour2/branches/3.0@5298 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-07-01 13:36:50 +00:00
parent 4df4574be4
commit 0d6515a243
19 changed files with 449 additions and 259 deletions

View File

@ -1459,32 +1459,34 @@ MixerStrip::set_button_names ()
case Wide:
rec_enable_button_label.set_text (_("Rec"));
mute_button_label.set_text (_("Mute"));
switch (Config->get_solo_model()) {
case SoloInPlace:
if (!Config->get_solo_control_is_listen_control()) {
solo_button_label.set_text (_("Solo"));
break;
case SoloAFL:
solo_button_label.set_text (_("AFL"));
break;
case SoloPFL:
solo_button_label.set_text (_("PFL"));
break;
} else {
switch (Config->get_listen_position()) {
case AfterFaderListen:
solo_button_label.set_text (_("AFL"));
break;
case PreFaderListen:
solo_button_label.set_text (_("PFL"));
break;
}
}
break;
default:
rec_enable_button_label.set_text (_("R"));
mute_button_label.set_text (_("M"));
switch (Config->get_solo_model()) {
case SoloInPlace:
if (!Config->get_solo_control_is_listen_control()) {
solo_button_label.set_text (_("S"));
break;
case SoloAFL:
solo_button_label.set_text (_("A"));
break;
case SoloPFL:
solo_button_label.set_text (_("P"));
break;
} else {
switch (Config->get_listen_position()) {
case AfterFaderListen:
solo_button_label.set_text (_("A"));
break;
case PreFaderListen:
solo_button_label.set_text (_("P"));
break;
}
}
break;

View File

@ -1063,6 +1063,10 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist)
/* do not copy-n-paste amp */
continue;
} else if (type->value() == "intsend" || type->value() == "intreturn") {
/* do not copy-n-paste internal sends&returns */
continue;
} else if (type->value() == "listen") {
p.reset (new Delivery (_session, _route->mute_master(), **niter));

View File

@ -1158,18 +1158,26 @@ RCOptionEditor::RCOptionEditor ()
add_option (_("Audio"), new OptionEditorHeading (_("Solo")));
ComboOption<SoloModel>* sm = new ComboOption<SoloModel> (
"solo-model",
_("Solo button controls"),
mem_fun (*_rc_config, &RCConfiguration::get_solo_model),
mem_fun (*_rc_config, &RCConfiguration::set_solo_model)
add_option (_("Audio"),
new BoolOption (
"solo-control-is-listen-control",
_("Solo controls are Listen controls"),
mem_fun (*_rc_config, &RCConfiguration::get_solo_control_is_listen_control),
mem_fun (*_rc_config, &RCConfiguration::set_solo_control_is_listen_control)
));
ComboOption<ListenPosition>* lp = new ComboOption<ListenPosition> (
"listen-position",
_("Listen Position"),
mem_fun (*_rc_config, &RCConfiguration::get_listen_position),
mem_fun (*_rc_config, &RCConfiguration::set_listen_position)
);
sm->add (SoloInPlace, _("solo in place"));
sm->add (SoloAFL, _("post-fader listen via monitor bus"));
sm->add (SoloPFL, _("pre-fader listen via monitor bus"));
lp->add (AfterFaderListen, _("after-fader listen"));
lp->add (PreFaderListen, _("pre-fader listen"));
add_option (_("Audio"), sm);
add_option (_("Audio"), lp);
add_option (_("Audio"), new SoloMuteOptions (_rc_config));
add_option (_("Audio"),

View File

@ -2396,7 +2396,13 @@ void
RouteTimeAxisView::set_button_names ()
{
rec_enable_button_label.set_text (_("r"));
solo_button_label.set_text (_("s"));
if (Config->get_solo_control_is_listen_control()) {
solo_button_label.set_text (_("l"));
} else {
solo_button_label.set_text (_("s"));
}
mute_button_label.set_text (_("m"));
}

View File

@ -188,6 +188,7 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
connections.push_back (_route->listen_changed.connect (mem_fun(*this, &RouteUI::listen_changed)));
connections.push_back (_route->solo_isolated_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
if (is_track()) {
@ -322,102 +323,110 @@ RouteUI::solo_press(GdkEventButton* ev)
if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
return true;
}
multiple_solo_change = false;
if (!ignore_toggle) {
if (Keyboard::is_context_menu_event (ev)) {
if (Config->get_solo_control_is_listen_control()) {
if (solo_menu == 0) {
build_solo_menu ();
}
_route->set_listen (!_route->listening(), this);
solo_menu->popup (1, ev->time);
} else {
} else {
if (Keyboard::is_button2_event (ev)) {
// Primary-button2 click is the midi binding click
// button2-click is "momentary"
multiple_solo_change = false;
if (!ignore_toggle) {
if (Keyboard::is_context_menu_event (ev)) {
if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
wait_for_release = true;
} else {
return false;
if (solo_menu == 0) {
build_solo_menu ();
}
}
if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
/* Primary-Tertiary-click applies change to all routes */
bool was_not_latched = false;
if (!Config->get_solo_latched ()) {
was_not_latched = true;
/*
XXX it makes no sense to solo all tracks if we're
not in latched mode, but doing nothing feels like a bug,
so do it anyway
*/
Config->set_solo_latched (true);
}
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
_session.set_all_solo (!_route->soloed());
cmd->mark();
_session.add_command (cmd);
_session.commit_reversible_command ();
multiple_solo_change = true;
if (was_not_latched) {
Config->set_solo_latched (false);
}
solo_menu->popup (1, ev->time);
} else {
if (Keyboard::is_button2_event (ev)) {
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
// Primary-Secondary-click: exclusively solo this track, not a toggle */
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
_session.set_all_solo (false);
_route->set_solo (true, this);
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
// shift-click: set this route to solo safe
if (Profile->get_sae() && ev->button == 1) {
// button 1 and shift-click: disables solo_latched for this click
// Primary-button2 click is the midi binding click
// button2-click is "momentary"
if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
wait_for_release = true;
} else {
return false;
}
}
if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
/* Primary-Tertiary-click applies change to all routes */
bool was_not_latched = false;
if (!Config->get_solo_latched ()) {
was_not_latched = true;
/*
XXX it makes no sense to solo all tracks if we're
not in latched mode, but doing nothing feels like a bug,
so do it anyway
*/
Config->set_solo_latched (true);
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
}
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
_session.set_all_solo (!_route->soloed());
cmd->mark();
_session.add_command (cmd);
_session.commit_reversible_command ();
multiple_solo_change = true;
if (was_not_latched) {
Config->set_solo_latched (false);
}
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
// Primary-Secondary-click: exclusively solo this track, not a toggle */
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
_session.set_all_solo (false);
_route->set_solo (true, this);
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
// shift-click: set this route to solo safe
if (Profile->get_sae() && ev->button == 1) {
// button 1 and shift-click: disables solo_latched for this click
if (!Config->get_solo_latched ()) {
Config->set_solo_latched (true);
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
Config->set_solo_latched (false);
}
} else {
_route->set_solo_isolated (!_route->solo_isolated(), this);
wait_for_release = false;
}
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
/* Primary-button1: solo mix group.
NOTE: Primary-button2 is MIDI learn.
*/
if (ev->button == 1) {
set_route_group_solo (_route, !_route->soloed());
}
} else {
_route->set_solo_isolated (!_route->solo_isolated(), this);
wait_for_release = false;
}
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
/* Primary-button1: solo mix group.
NOTE: Primary-button2 is MIDI learn.
*/
if (ev->button == 1) {
set_route_group_solo (_route, !_route->soloed());
}
} else {
/* click: solo this route */
if (wait_for_release) {
_route->set_solo (!_route->soloed(), this);
} else {
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
/* click: solo this route */
if (wait_for_release) {
_route->set_solo (!_route->soloed(), this);
} else {
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
}
}
}
}
@ -614,7 +623,13 @@ RouteUI::send_blink (bool onoff)
void
RouteUI::solo_changed(void* src)
{
Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
}
void
RouteUI::listen_changed(void* src)
{
Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
}
@ -622,21 +637,30 @@ void
RouteUI::update_solo_display ()
{
bool x;
vector<Gdk::Color> fg_colors;
Gdk::Color c;
if (solo_button->get_active() != (x = _route->soloed())){
ignore_toggle = true;
solo_button->set_active(x);
ignore_toggle = false;
}
if (_route->solo_isolated()) {
solo_button->set_visual_state (2);
} else if (_route->soloed()) {
solo_button->set_visual_state (1);
if (Config->get_solo_control_is_listen_control()) {
if (solo_button->get_active() != (x = _route->listening())) {
ignore_toggle = true;
solo_button->set_active(x);
ignore_toggle = false;
}
} else {
solo_button->set_visual_state (0);
if (solo_button->get_active() != (x = _route->soloed())){
ignore_toggle = true;
solo_button->set_active(x);
ignore_toggle = false;
}
if (_route->solo_isolated()) {
solo_button->set_visual_state (2);
} else if (_route->soloed()) {
solo_button->set_visual_state (1);
} else {
solo_button->set_visual_state (0);
}
}
}
@ -1383,7 +1407,9 @@ RouteUI::parameter_changed (string const & p)
if (p == "disable-disarm-during-roll") {
check_rec_enable_sensitivity ();
} else if (p == "solo-model") {
} else if (p == "solo-control-is-listen-control") {
set_button_names ();
} else if (p == "listen-position") {
set_button_names ();
}
}

View File

@ -123,6 +123,7 @@ class RouteUI : public virtual AxisView
void solo_changed(void*);
void solo_changed_so_update_mute ();
void mute_changed(void*);
void listen_changed(void*);
virtual void processors_changed () {}
void route_rec_enable_changed();
void session_rec_enable_changed();

View File

@ -59,6 +59,7 @@ public:
void attach_buffers(PortSet& ports, nframes_t nframes, nframes_t offset = 0);
void ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capacity);
void ensure_buffers(const ChanCount& chns, size_t buffer_capacity);
const ChanCount& available() const { return _available; }
ChanCount& available() { return _available; }

View File

@ -34,7 +34,7 @@ class InternalReturn : public Return
InternalReturn (Session&);
InternalReturn (Session&, const XMLNode&);
bool visible() const { return false; }
bool visible() const { return true; }
XMLNode& state(bool full);
XMLNode& get_state(void);

View File

@ -77,7 +77,9 @@ CONFIG_VARIABLE (bool, mute_affects_post_fader, "mute-affects-post-fader", true)
CONFIG_VARIABLE (bool, mute_affects_control_outs, "mute-affects-control-outs", true)
CONFIG_VARIABLE (bool, mute_affects_main_outs, "mute-affects-main-outs", true)
CONFIG_VARIABLE (MonitorModel, monitoring_model, "monitoring-model", ExternalMonitoring)
CONFIG_VARIABLE (SoloModel, solo_model, "solo-model", SoloInPlace)
CONFIG_VARIABLE (ListenPosition, listen_position, "listen-position", AfterFaderListen)
CONFIG_VARIABLE (bool, solo_control_is_listen_control, "solo-control-is-listen-control", false)
CONFIG_VARIABLE (bool, solo_latched, "solo-latched", true)
CONFIG_VARIABLE (bool, latched_record_enable, "latched-record-enable", false)
CONFIG_VARIABLE (bool, all_safe, "all-safe", false)

View File

@ -124,6 +124,7 @@ class Route : public SessionObject, public AutomatableControls
void set_mute (bool yn, void* src);
bool muted () const;
/* controls use set_solo() to modify this route's solo state
*/
@ -132,6 +133,9 @@ class Route : public SessionObject, public AutomatableControls
void set_solo_isolated (bool yn, void *src);
bool solo_isolated() const;
void set_listen (bool yn, void* src);
bool listening () const;
void set_phase_invert (bool yn);
bool phase_invert() const;
@ -229,6 +233,7 @@ class Route : public SessionObject, public AutomatableControls
sigc::signal<void> active_changed;
sigc::signal<void> phase_invert_changed;
sigc::signal<void> denormal_protection_changed;
sigc::signal<void,void*> listen_changed;
sigc::signal<void,void*> solo_changed;
sigc::signal<void,void*> solo_safe_changed;
sigc::signal<void,void*> solo_isolated_changed;
@ -262,7 +267,7 @@ class Route : public SessionObject, public AutomatableControls
sigc::signal<void,void*> SelectedChanged;
int listen_via (boost::shared_ptr<Route>, const std::string& name);
int listen_via (boost::shared_ptr<Route>, bool);
void drop_listen (boost::shared_ptr<Route>);
bool feeds (boost::shared_ptr<Route>);

View File

@ -723,9 +723,11 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
/* session-wide solo/mute/rec-enable */
bool soloing() const { return _non_soloed_outs_muted; }
bool listening() const { return _listen_cnt > 0; }
void set_all_solo (bool);
void set_all_mute (bool);
void set_all_listen (bool);
sigc::signal<void,bool> SoloActive;
sigc::signal<void> SoloChanged;
@ -1031,6 +1033,7 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
float _meter_hold;
float _meter_falloff;
bool _non_soloed_outs_muted;
uint32_t _listen_cnt;
void set_worst_io_latencies ();
void set_worst_io_latencies_x (IOChange asifwecare, void *ignored) {
@ -1451,16 +1454,15 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
/* mixer stuff */
bool solo_update_disabled;
bool solo_update_disabled;
void route_listen_changed (void *src, boost::weak_ptr<Route>);
void route_mute_changed (void *src);
void route_solo_changed (void *src, boost::weak_ptr<Route>);
void catch_up_on_solo ();
void catch_up_on_solo_mute_override ();
void solo_model_changed ();
void update_route_solo_state (boost::shared_ptr<RouteList> r = boost::shared_ptr<RouteList>());
void modify_solo_mute (bool, bool);
void strip_portname_for_solo (std::string& portname);
void listen_position_changed ();
void solo_control_mode_changed ();
/* REGION MANAGEMENT */

View File

@ -330,10 +330,9 @@ namespace ARDOUR {
AddHigher
};
enum SoloModel {
SoloInPlace,
SoloAFL,
SoloPFL
enum ListenPosition {
AfterFaderListen,
PreFaderListen
};
enum AutoConnectOption {
@ -455,7 +454,7 @@ std::istream& operator>>(std::istream& o, ARDOUR::AutoConnectOption& sf);
std::istream& operator>>(std::istream& o, ARDOUR::EditMode& sf);
std::istream& operator>>(std::istream& o, ARDOUR::MonitorModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::SoloModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::ListenPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::LayerModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::SlaveSource& sf);

View File

@ -130,6 +130,7 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac
}
_available.set(type, num_buffers);
_count.set (type, num_buffers);
}
#ifdef HAVE_SLV2
@ -149,6 +150,76 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac
assert(bufs[0]->capacity() >= buffer_capacity);
}
/** Ensure that the number of buffers of each type @a type matches @a chns
* and each buffer is of size at least @a buffer_capacity
*/
void
BufferSet::ensure_buffers(const ChanCount& chns, size_t buffer_capacity)
{
if (chns == ChanCount::ZERO) {
return;
}
// If we're a mirror just make sure we're ok
if (_is_mirror) {
assert(_count >= chns);
return;
}
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
// The vector of buffers of this type
BufferVec& bufs = _buffers[*t];
uint32_t nbufs = chns.get (*t);
if (nbufs == 0) {
// Nuke it
for (BufferVec::iterator i = bufs.begin(); i != bufs.end(); ++i) {
delete (*i);
}
bufs.clear();
continue;
}
// If there's not enough or they're too small, just nuke the whole thing and
// rebuild it (so I'm lazy..)
if (bufs.size() < nbufs
|| (bufs.size() > 0 && bufs[0]->capacity() < buffer_capacity)) {
// Nuke it
for (BufferVec::iterator i = bufs.begin(); i != bufs.end(); ++i) {
delete (*i);
}
bufs.clear();
// Rebuild it
for (size_t i = 0; i < nbufs; ++i) {
bufs.push_back(Buffer::create(*t, buffer_capacity));
}
_available.set (*t, nbufs);
}
#ifdef HAVE_SLV2
// Ensure enough low level MIDI format buffers are available for conversion
// in both directions (input & output, out-of-place)
if (*t == DataType::MIDI && _lv2_buffers.size() < _buffers[DataType::MIDI].size() * 2 + 1) {
while (_lv2_buffers.size() < _buffers[DataType::MIDI].size() * 2) {
_lv2_buffers.push_back(std::make_pair(false, new LV2EventBuffer(buffer_capacity)));
}
}
#endif
// Post-conditions
assert(bufs[0]->type() == *t);
assert(bufs.size() == _available.get(*t));
assert(bufs[0]->capacity() >= buffer_capacity);
}
assert (available() == chns);
}
/** Get the capacity (size) of the available buffers of the given type.
*
* All buffers of a certain type always have the same capacity.

View File

@ -64,7 +64,7 @@ setup_enum_writer ()
DenormalModel _DenormalModel;
CrossfadeModel _CrossfadeModel;
LayerModel _LayerModel;
SoloModel _SoloModel;
ListenPosition _ListenPosition;
SampleFormat _SampleFormat;
CDMarkerFormat _CDMarkerFormat;
HeaderFormat _HeaderFormat;
@ -229,10 +229,9 @@ setup_enum_writer ()
REGISTER_ENUM (AddHigher);
REGISTER (_LayerModel);
REGISTER_ENUM (SoloInPlace);
REGISTER_ENUM (SoloAFL);
REGISTER_ENUM (SoloPFL);
REGISTER (_SoloModel);
REGISTER_ENUM (AfterFaderListen);
REGISTER_ENUM (PreFaderListen);
REGISTER (_ListenPosition);
REGISTER_ENUM (AutoConnectPhysical);
REGISTER_ENUM (AutoConnectMaster);

View File

@ -604,7 +604,7 @@ std::istream& operator>>(std::istream& o, AutoConnectOption& var) { return int_t
std::istream& operator>>(std::istream& o, MonitorModel& var) { return int_to_type<MonitorModel> (o, var); }
std::istream& operator>>(std::istream& o, RemoteModel& var) { return int_to_type<RemoteModel> (o, var); }
std::istream& operator>>(std::istream& o, EditMode& var) { return int_to_type<EditMode> (o, var); }
std::istream& operator>>(std::istream& o, SoloModel& var) { return int_to_type<SoloModel> (o, var); }
std::istream& operator>>(std::istream& o, ListenPosition& var) { return int_to_type<ListenPosition> (o, var); }
std::istream& operator>>(std::istream& o, LayerModel& var) { return int_to_type<LayerModel> (o, var); }
std::istream& operator>>(std::istream& o, CrossfadeModel& var) { return int_to_type<CrossfadeModel> (o, var); }
std::istream& operator>>(std::istream& o, SlaveSource& var) { return int_to_type<SlaveSource> (o, var); }

View File

@ -60,7 +60,7 @@ bool
InternalReturn::configure_io (ChanCount in, ChanCount out)
{
IOProcessor::configure_io (in, out);
allocate_buffers (_session.get_block_size());
allocate_buffers (_session.engine().frames_per_cycle());
return true;
}
@ -73,8 +73,8 @@ InternalReturn::set_block_size (nframes_t nframes)
void
InternalReturn::allocate_buffers (nframes_t nframes)
{
buffers.ensure_buffers (DataType::AUDIO, _configured_input.n_audio(), nframes);
buffers.ensure_buffers (DataType::MIDI, _configured_input.n_midi(), nframes);
buffers.ensure_buffers (_configured_input, nframes);
buffers.set_count (_configured_input);
}
BufferSet*

View File

@ -454,14 +454,26 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
}
bufs.set_count (_input->n_ports());
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
BufferSet::iterator o = bufs.begin(*t);
PortSet& ports (_input->ports());
for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
o->read_from (i->get_buffer(nframes), nframes);
if (is_control() && _session.listening()) {
/* control/monitor bus ignores input ports when something is
feeding the listen "stream". data will "arrive" into the
route from the intreturn processor element.
*/
bufs.silence (nframes, 0);
} else {
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
BufferSet::iterator o = bufs.begin(*t);
PortSet& ports (_input->ports());
for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
o->read_from (i->get_buffer(nframes), nframes);
}
}
}
@ -474,6 +486,32 @@ Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t n
process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
}
void
Route::set_listen (bool yn, void* src)
{
if (_control_outs) {
if (yn != _control_outs->active()) {
if (yn) {
_control_outs->activate ();
} else {
_control_outs->deactivate ();
}
listen_changed (src); /* EMIT SIGNAL */
}
}
}
bool
Route::listening () const
{
if (_control_outs) {
return _control_outs->active ();
} else {
return false;
}
}
void
Route::set_solo (bool yn, void *src)
{
@ -506,33 +544,11 @@ Route::mod_solo_level (int32_t delta)
_solo_level += delta;
}
/* tell "special" delivery units what the solo situation is
/* tell main outs what the solo situation is
*/
switch (Config->get_solo_model()) {
case SoloInPlace:
/* main outs are used for soloing */
_main_outs->set_solo_level (_solo_level);
_main_outs->set_solo_isolated (_solo_isolated);
if (_control_outs) {
/* control outs just keep on playing */
_control_outs->set_solo_level (0);
_control_outs->set_solo_isolated (true);
}
break;
case SoloAFL:
case SoloPFL:
/* control outs are used for soloing */
if (_control_outs) {
_control_outs->set_solo_level (_solo_level);
_control_outs->set_solo_isolated (_solo_isolated);
}
/* main outs just keep on playing */
_main_outs->set_solo_level (0);
_main_outs->set_solo_isolated (true);
break;
}
_main_outs->set_solo_level (_solo_level);
_main_outs->set_solo_isolated (_solo_isolated);
}
void
@ -546,28 +562,11 @@ Route::set_solo_isolated (bool yn, void *src)
if (yn != _solo_isolated) {
_solo_isolated = yn;
/* tell "special" delivery units what the solo situation is
/* tell main outs what the solo situation is
*/
switch (Config->get_solo_model()) {
case SoloInPlace:
_main_outs->set_solo_level (_solo_level);
_main_outs->set_solo_isolated (_solo_isolated);
if (_control_outs) {
_main_outs->set_solo_level (1);
_main_outs->set_solo_isolated (false);
}
break;
case SoloAFL:
case SoloPFL:
if (_control_outs) {
_control_outs->set_solo_level (_solo_level);
_control_outs->set_solo_isolated (_solo_isolated);
}
_main_outs->set_solo_level (1);
_main_outs->set_solo_isolated (false);
break;
}
_main_outs->set_solo_level (_solo_level);
_main_outs->set_solo_isolated (_solo_isolated);
solo_isolated_changed (src);
}
@ -711,7 +710,9 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
}
// XXX: do we want to emit the signal here ? change call order.
processor->activate ();
if (!boost::dynamic_pointer_cast<InternalSend>(processor)) {
processor->activate ();
}
processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
_output->set_user_latency (0);
@ -1862,7 +1863,8 @@ Route::get_return_buffer () const
boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
if (d) {
return d->get_buffers ();
BufferSet* bs = d->get_buffers ();
return bs;
}
}
@ -1884,7 +1886,7 @@ Route::release_return_buffer () const
}
int
Route::listen_via (boost::shared_ptr<Route> route, const string& listen_name)
Route::listen_via (boost::shared_ptr<Route> route, bool active)
{
vector<string> ports;
vector<string>::const_iterator i;
@ -1893,6 +1895,7 @@ Route::listen_via (boost::shared_ptr<Route> route, const string& listen_name)
Glib::RWLock::ReaderLock rm (_processor_lock);
for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
if (d && d->target_route() == route) {
@ -1921,6 +1924,10 @@ Route::listen_via (boost::shared_ptr<Route> route, const string& listen_name)
return -1;
}
if (route == _session.control_out()) {
_control_outs = listener;
}
add_processor (listener, PreFader);
return 0;

View File

@ -46,6 +46,7 @@
#include "ardour/analyser.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_diskstream.h"
#include "ardour/audio_port.h"
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
@ -269,16 +270,6 @@ Session::Session (AudioEngine &eng,
RouteList rl;
int control_id = 1;
if (control_out_channels) {
ChanCount count(DataType::AUDIO, control_out_channels);
shared_ptr<Route> r (new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO));
r->input()->ensure_io (count, false, this);
r->output()->ensure_io (count, false, this);
r->set_remote_control_id (control_id++);
rl.push_back (r);
}
if (master_out_channels) {
ChanCount count(DataType::AUDIO, master_out_channels);
shared_ptr<Route> r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO));
@ -292,6 +283,16 @@ Session::Session (AudioEngine &eng,
output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster);
}
if (control_out_channels) {
ChanCount count(DataType::AUDIO, control_out_channels);
shared_ptr<Route> r (new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO));
r->input()->ensure_io (count, false, this);
r->output()->ensure_io (count, false, this);
r->set_remote_control_id (control_id++);
rl.push_back (r);
}
if (!rl.empty()) {
add_routes (rl, false);
}
@ -680,11 +681,46 @@ Session::when_engine_running ()
if (_control_out) {
uint32_t limit = _control_out->n_outputs().n_total();
/* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else
are undefined, at best.
*/
/* control out listens to master bus (but ignores it
under some conditions)
*/
uint32_t limit = _control_out->n_inputs().n_audio();
if (_master_out) {
for (uint32_t n = 0; n < limit; ++n) {
AudioPort* p = _control_out->input()->ports().nth_audio_port (n);
AudioPort* o = _master_out->output()->ports().nth_audio_port (n);
if (o) {
string connect_to = o->name();
if (_control_out->input()->connect (p, connect_to, this)) {
error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to)
<< endmsg;
break;
}
}
}
}
/* connect control out to physical outs, but use ones after the master
if possible
*/
/* XXX this logic is wrong for mixed port types */
uint32_t shift = _master_out->n_outputs().n_audio();
uint32_t mod = _master_out->n_outputs().n_audio();
limit = _control_out->n_outputs().n_audio();
for (uint32_t n = 0; n < limit; ++n) {
Port* p = _control_out->output()->nth (n);
string connect_to = _engine.get_nth_physical_output (DataType (p->type()), n);
string connect_to = _engine.get_nth_physical_output (DataType (p->type()), (n+shift) % mod);
if (!connect_to.empty()) {
if (_control_out->output()->connect (p, connect_to, this)) {
@ -764,7 +800,7 @@ Session::hookup_io ()
continue;
}
(*x)->listen_via (_control_out, X_("listen"));
(*x)->listen_via (_control_out, false);
}
}
@ -780,9 +816,13 @@ Session::hookup_io ()
graph_reordered ();
/* update mixer solo state */
/* update the full solo state, which can't be
correctly determined on a per-route basis, but
needs the global overview that only the session
has.
*/
catch_up_on_solo();
update_route_solo_state ();
}
void
@ -2069,6 +2109,7 @@ Session::add_routes (RouteList& new_routes, bool save)
boost::weak_ptr<Route> wpr (*x);
(*x)->listen_changed.connect (sigc::bind (mem_fun (*this, &Session::route_listen_changed), wpr));
(*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr));
(*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed));
(*x)->output()->changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x));
@ -2090,7 +2131,8 @@ Session::add_routes (RouteList& new_routes, bool save)
if ((*x)->is_control() || (*x)->is_master()) {
continue;
}
(*x)->listen_via (_control_out, "control");
cerr << "Add listen via control outs\n";
(*x)->listen_via (_control_out, false);
}
resort_routes ();
@ -2139,7 +2181,7 @@ Session::add_internal_sends (boost::shared_ptr<Route> dest, boost::shared_ptr<Ro
continue;
}
(*i)->listen_via (dest, "aux");
(*i)->listen_via (dest, true);
}
}
@ -2253,6 +2295,22 @@ Session::route_mute_changed (void* src)
set_dirty ();
}
void
Session::route_listen_changed (void* src, boost::weak_ptr<Route> wpr)
{
boost::shared_ptr<Route> route = wpr.lock();
if (!route) {
error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
return;
}
if (route->listening()) {
_listen_cnt++;
} else if (_listen_cnt > 0) {
_listen_cnt--;
}
}
void
Session::route_solo_changed (void* src, boost::weak_ptr<Route> wpr)
{
@ -2329,34 +2387,6 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
}
}
void
Session::catch_up_on_solo ()
{
/* this is called after set_state() to catch the full solo
state, which can't be correctly determined on a per-route
basis, but needs the global overview that only the session
has.
*/
update_route_solo_state();
}
void
Session::catch_up_on_solo_mute_override ()
{
if (Config->get_solo_model() != SoloInPlace) {
return;
}
/* this is called whenever the param solo-mute-override is
changed.
*/
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
// (*i)->catch_up_on_solo_mute_override ();
}
}
shared_ptr<Route>
Session::route_by_name (string name)
{
@ -3491,6 +3521,20 @@ Session::set_all_solo (bool yn)
set_dirty();
}
void
Session::set_all_listen (bool yn)
{
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) {
(*i)->set_listen (yn, this);
}
}
set_dirty();
}
void
Session::set_all_mute (bool yn)
{
@ -4257,19 +4301,16 @@ Session::update_have_rec_enabled_diskstream ()
}
void
Session::solo_model_changed ()
Session::listen_position_changed ()
{
Placement p;
switch (Config->get_solo_model()) {
case SoloInPlace:
return;
case SoloAFL:
switch (Config->get_listen_position()) {
case AfterFaderListen:
p = PostFader;
break;
case SoloPFL:
case PreFaderListen:
p = PreFader;
break;
}
@ -4281,6 +4322,18 @@ Session::solo_model_changed ()
}
}
void
Session::solo_control_mode_changed ()
{
/* cancel all solo or all listen when solo control mode changes */
if (Config->get_solo_control_is_listen_control()) {
set_all_solo (false);
} else {
set_all_listen (false);
}
}
void
Session::route_group_changed ()
{

View File

@ -152,6 +152,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
_non_soloed_outs_muted = false;
_listen_cnt = 0;
g_atomic_int_set (&processing_prohibited, 0);
_transport_speed = 0;
_last_transport_speed = 0;
@ -3152,10 +3153,13 @@ Session::config_changed (std::string p, bool ours)
}
} else if (p == "solo-mute-override") {
// catch_up_on_solo_mute_override ();
} else if (p == "solo-model") {
solo_model_changed ();
} else if (p == "listen-position") {
listen_position_changed ();
} else if (p == "solo-control-is-listen-control") {
solo_control_mode_changed ();
}
set_dirty ();
}