universal change in the design of the way Route/Track controls are designed and used. The controls now own their own state, rather than proxy for state in their owners.

Massive changes all over the code to accomodate this. Many things are not finished. Consider this a backup safety commit
This commit is contained in:
Paul Davis 2016-04-08 16:49:47 -04:00
parent c107f1ab56
commit 653ae4acd6
61 changed files with 889 additions and 2480 deletions

View File

@ -54,7 +54,7 @@ class AddRouteDialog : public ArdourDialog
MidiTrack,
MixedTrack,
AudioBus,
MidiBus
MidiBus,
VCAMaster,
};
TypeWanted type_wanted() const;

View File

@ -92,7 +92,9 @@
#include "ardour/source_factory.h"
#include "ardour/slave.h"
#include "ardour/system_exec.h"
#include "ardour/track.h"
#include "ardour/vca_manager.h"
#include "ardour/utils.h"
#include "LuaBridge/LuaBridge.h"
@ -1570,7 +1572,7 @@ void
ARDOUR_UI::count_recenabled_streams (Route& route)
{
Track* track = dynamic_cast<Track*>(&route);
if (track && track->record_enabled()) {
if (track && track->rec_enable_control()->get_value()) {
rec_enabled_streams += track->n_inputs().n_total();
}
}
@ -2083,14 +2085,14 @@ ARDOUR_UI::trx_record_enable_all_tracks ()
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r);
assert (t);
if (t->record_enabled()) {
if (t->rec_enable_control()->get_value()) {
none_record_enabled = false;
break;
}
}
if (none_record_enabled) {
_session->set_record_enabled (rl, true, Session::rt_cleanup);
_session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), 1.0, Controllable::NoGroup);
}
return none_record_enabled;
@ -2388,7 +2390,7 @@ ARDOUR_UI::toggle_record_enable (uint32_t rid)
boost::shared_ptr<Track> t;
if ((t = boost::dynamic_pointer_cast<Track>(r)) != 0) {
t->set_record_enabled (!t->record_enabled(), Controllable::UseGroup);
t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup);
}
}
}
@ -5587,12 +5589,6 @@ void
ARDOUR_UI::cancel_solo ()
{
if (_session) {
if (_session->soloing()) {
_session->set_solo (_session->get_routes(), false);
} else if (_session->listening()) {
_session->set_listen (_session->get_routes(), false);
}
_session->clear_all_solo_state (_session->get_routes()); // safeguard, ideally this won't do anything, check the log-window
}
}

View File

@ -186,7 +186,7 @@ AudioStreamView::setup_rec_box ()
if (!rec_active &&
_trackview.session()->record_status() == Session::Recording &&
_trackview.track()->record_enabled()) {
_trackview.track()->rec_enable_control()->get_value()) {
if (_trackview.audio_track()->mode() == Normal && UIConfiguration::instance().get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) {
/* add a new region, but don't bother if they set show-waveforms-while-recording mid-record */
@ -240,7 +240,7 @@ AudioStreamView::setup_rec_box ()
} else if (rec_active &&
(_trackview.session()->record_status() != Session::Recording ||
!_trackview.track()->record_enabled())) {
!_trackview.track()->rec_enable_control()->get_value())) {
screen_update_connection.disconnect();
rec_active = false;
rec_updating = false;

View File

@ -5699,11 +5699,11 @@ Editor::toggle_record_enable ()
continue;
if (first) {
new_state = !rtav->track()->record_enabled();
new_state = !rtav->track()->rec_enable_control()->get_value();
first = false;
}
rtav->track()->set_record_enabled (new_state, Controllable::UseGroup);
rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
}
}
@ -5712,7 +5712,7 @@ Editor::toggle_solo ()
{
bool new_state = false;
bool first = true;
boost::shared_ptr<RouteList> rl (new RouteList);
boost::shared_ptr<ControlList> cl (new ControlList);
for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
@ -5726,10 +5726,10 @@ Editor::toggle_solo ()
first = false;
}
rl->push_back (rtav->route());
cl->push_back (rtav->route()->solo_control());
}
_session->set_solo (rl, new_state, Session::rt_cleanup, Controllable::UseGroup);
_session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
}
void
@ -5754,7 +5754,7 @@ Editor::toggle_mute ()
rl->push_back (rtav->route());
}
_session->set_mute (rl, new_state, Session::rt_cleanup, Controllable::UseGroup);
_session->set_controls (route_list_to_control_list (rl, &Route::mute_control), new_state, Controllable::UseGroup);
}
void

View File

@ -31,6 +31,8 @@
#include "ardour/midi_track.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/utils.h"
#include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
#include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
@ -422,11 +424,8 @@ EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
if (rtv && rtv->track()) {
DisplaySuspender ds;
boost::shared_ptr<RouteList> rl (new RouteList);
// TODO check rec-safe and ...
rl->push_back (rtv->route());
_session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
_session->set_control (rtv->track()->rec_enable_control(), !rtv->track()->rec_enable_control()->get_value(), Controllable::UseGroup);
}
}
@ -455,9 +454,7 @@ EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
if (rtv != 0) {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (rtv->route());
_session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
_session->set_control (rtv->route()->mute_control(), rtv->route()->mute_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
}
}
@ -471,13 +468,15 @@ EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
if (rtv != 0) {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (rtv->route());
bool newval;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
newval = !rtv->route()->listening_via_monitor();
} else {
_session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
newval = !rtv->route()->self_soloed();
}
rtv->route()->solo_control()->set_value (newval ? 1.0 : 0.0, Controllable::UseGroup);
}
}
@ -491,7 +490,7 @@ EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
if (rtv) {
rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), Controllable::UseGroup);
rtv->route()->solo_isolate_control()->set_value (rtv->route()->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
}
}
@ -505,7 +504,7 @@ EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
if (rtv) {
rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), Controllable::UseGroup);
rtv->route()->solo_safe_control()->set_value (rtv->route()->solo_safe_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
}
}
@ -734,8 +733,8 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
row[_columns.solo_visible] = !(*x)->route()->is_master ();
row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
row[_columns.solo_isolate_state] = (*x)->route()->solo_isolate_control()->solo_isolated();
row[_columns.solo_safe_state] = (*x)->route()->solo_safe_control()->solo_safe();
row[_columns.name_editable] = true;
boost::weak_ptr<Route> wr ((*x)->route());
@ -745,8 +744,8 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
if ((*x)->is_track()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
t->RecordSafeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
t->rec_enable_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
t->rec_safe_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
}
if ((*x)->is_midi_track()) {
@ -1280,25 +1279,27 @@ EditorRoutes::key_press (GdkEventKey* ev)
case 'm':
if (get_relevant_routes (rl)) {
_session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
_session->set_controls (route_list_to_control_list (rl, &Route::mute_control), rl->front()->muted() ? 0.0 : 1.0, Controllable::NoGroup);
}
return true;
break;
case 's':
if (get_relevant_routes (rl)) {
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
} else {
_session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
}
_session->set_controls (route_list_to_control_list (rl, &Route::solo_control), rl->front()->self_soloed() ? 0.0 : 1.0, Controllable::NoGroup);
}
return true;
break;
case 'r':
if (get_relevant_routes (rl)) {
_session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r);
if (t) {
_session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), !t->rec_enable_control()->get_value(), Controllable::NoGroup);
break;
}
}
}
break;
@ -1645,10 +1646,13 @@ EditorRoutes::idle_update_mute_rec_solo_etc()
(*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
(*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
(*i)[_columns.active] = route->active ();
if (boost::dynamic_pointer_cast<Track> (route)) {
boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track>(route));
if (trk) {
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
if (route->record_enabled()) {
if (trk->rec_enable_control()->get_value()) {
if (_session->record_status() == Session::Recording) {
(*i)[_columns.rec_state] = 1;
} else {
@ -1660,8 +1664,8 @@ EditorRoutes::idle_update_mute_rec_solo_etc()
(*i)[_columns.rec_state] = 0;
}
(*i)[_columns.rec_safe] = route->record_safe () ? 1 : 0;
(*i)[_columns.name_editable] = !route->record_enabled ();
(*i)[_columns.rec_safe] = !trk->rec_safe_control()->get_value();
(*i)[_columns.name_editable] = !trk->rec_enable_control()->get_value();
}
}

View File

@ -479,10 +479,10 @@ GainMeterBase::gain_activated ()
/* clamp to displayable values */
if (_data_type == DataType::AUDIO) {
f = min (f, 6.0f);
_control->set_value (dB_to_coefficient(f), Controllable::NoGroup);
_control->set_value (dB_to_coefficient(f), Controllable::UseGroup);
} else {
f = min (fabs (f), 2.0f);
_control->set_value (f, Controllable::NoGroup);
_control->set_value (f, Controllable::UseGroup);
}
if (gain_display.has_focus()) {
@ -541,11 +541,7 @@ GainMeterBase::fader_moved ()
value = gain_adjustment.get_value();
}
if (_route && _control == _route->gain_control()) {
_route->set_gain (value, Controllable::UseGroup);
} else {
_control->set_value (value, Controllable::NoGroup);
}
_control->set_value (value, Controllable::UseGroup);
}
show_gain ();

View File

@ -366,7 +366,8 @@ GroupTabs::new_from_rec_enabled ()
RouteList rec_enabled;
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if ((*i)->record_enabled()) {
boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (*i));
if (trk && trk->rec_enable_control()->get_value()) {
rec_enabled.push_back (*i);
}
}

View File

@ -366,7 +366,7 @@ MeterStrip::set_button_names()
{
mute_button->set_text (S_("Mute|M"));
if (_route && _route->solo_safe()) {
if (_route && _route->solo_safe_control()->solo_safe()) {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
} else {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));

View File

@ -440,7 +440,7 @@ MidiStreamView::setup_rec_box ()
if (!rec_active &&
_trackview.session()->record_status() == Session::Recording &&
_trackview.track()->record_enabled()) {
_trackview.track()->rec_enable_control()->get_value()) {
if (UIConfiguration::instance().get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) {
@ -512,7 +512,7 @@ MidiStreamView::setup_rec_box ()
} else if (rec_active &&
(_trackview.session()->record_status() != Session::Recording ||
!_trackview.track()->record_enabled())) {
!_trackview.track()->rec_enable_control()->get_value())) {
screen_update_connection.disconnect();
rec_active = false;
rec_updating = false;

View File

@ -172,7 +172,7 @@ printf("setting gain to unity (?)");
BOOST_FOREACH(RouteUI* r, _route_targets) {
boost::shared_ptr<Route> rp = r->route();
if (rp) {
rp->set_gain (1.0, Controllable::NoGroup);
rp->gain_control()->set_value (1.0, Controllable::NoGroup);
}
}
}

View File

@ -950,7 +950,7 @@ MixerStrip::input_press (GdkEventButton *ev)
return true;
}
if (_session->actively_recording() && _route->record_enabled())
if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
return true;
switch (ev->button) {
@ -1665,7 +1665,7 @@ MixerStrip::name_button_button_press (GdkEventButton* ev)
list_route_operations ();
/* do not allow rename if the track is record-enabled */
rename_menu_item->set_sensitive (!_route->record_enabled());
rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
route_ops_menu->popup (1, ev->time);
return true;
@ -1681,7 +1681,7 @@ MixerStrip::name_button_button_release (GdkEventButton* ev)
list_route_operations ();
/* do not allow rename if the track is record-enabled */
rename_menu_item->set_sensitive (!_route->record_enabled());
rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
route_ops_menu->popup (1, ev->time);
}
@ -1695,7 +1695,7 @@ MixerStrip::number_button_button_press (GdkEventButton* ev)
list_route_operations ();
/* do not allow rename if the track is record-enabled */
rename_menu_item->set_sensitive (!_route->record_enabled());
rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
route_ops_menu->popup (1, ev->time);
return true;
@ -2141,7 +2141,7 @@ MixerStrip::set_button_names ()
monitor_section_button->set_text (_("Mon"));
}
if (_route && _route->solo_safe()) {
if (_route && _route->solo_safe_control()->solo_safe()) {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
} else {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
@ -2170,7 +2170,7 @@ MixerStrip::set_button_names ()
monitor_section_button->set_text (S_("Mon|O"));
}
if (_route && _route->solo_safe()) {
if (_route && _route->solo_safe_control()->solo_safe()) {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
} else {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));

View File

@ -36,6 +36,7 @@
#include "ardour/monitor_processor.h"
#include "ardour/port.h"
#include "ardour/route.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/user_bundle.h"
#include "ardour/plugin_manager.h"
@ -1246,7 +1247,7 @@ MonitorSection::cancel_isolate (GdkEventButton*)
{
if (_session) {
boost::shared_ptr<RouteList> rl (_session->get_routes ());
_session->set_solo_isolated (rl, false, Session::rt_cleanup, Controllable::NoGroup);
_session->set_controls (route_list_to_control_list (rl, &Route::solo_isolate_control), 0.0, Controllable::NoGroup);
}
return true;

View File

@ -2493,7 +2493,11 @@ RouteTimeAxisView::can_edit_name () const
{
/* we do not allow track name changes if it is record enabled
*/
return !_route->record_enabled();
boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
if (!trk) {
return true;
}
return !trk->rec_enable_control()->get_value();
}
void
@ -2724,7 +2728,7 @@ RouteTimeAxisView::remove_underlay (StreamView* v)
void
RouteTimeAxisView::set_button_names ()
{
if (_route && _route->solo_safe()) {
if (_route && _route->solo_safe_control()->solo_safe()) {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
} else {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));

View File

@ -34,8 +34,21 @@
#include "ardour/dB.h"
#include "ardour/route_group.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/vca.h"
#include "ardour/vca_manager.h"
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/filename_extensions.h"
#include "ardour/midi_track.h"
#include "ardour/monitor_control.h"
#include "ardour/internal_send.h"
#include "ardour/profile.h"
#include "ardour/phase_control.h"
#include "ardour/send.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/template_utils.h"
#include "ardour_ui.h"
#include "editor.h"
@ -55,16 +68,6 @@
#include "timers.h"
#include "ui_config.h"
#include "ardour/audio_track.h"
#include "ardour/audioengine.h"
#include "ardour/filename_extensions.h"
#include "ardour/midi_track.h"
#include "ardour/internal_send.h"
#include "ardour/profile.h"
#include "ardour/send.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/template_utils.h"
#include "i18n.h"
using namespace Gtk;
@ -270,10 +273,7 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
_route->solo_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
_route->solo_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
_route->solo_isolate_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
if (_route->phase_control()) {
_route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context());
}
_route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context());
if (is_track()) {
track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteUI::map_frozen, this), gui_context());
@ -290,8 +290,8 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
if (_session->writable() && is_track()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
t->RecordEnableChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
t->RecordSafeChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
t->rec_enable_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
t->rec_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
rec_enable_button->show();
rec_enable_button->set_controllable (t->rec_enable_control());
@ -309,7 +309,7 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
if (is_track()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
t->MonitoringChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::monitoring_changed, this), gui_context());
t->monitoring_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::monitoring_changed, this, _1, _2), gui_context());
update_monitoring_display ();
}
@ -383,7 +383,7 @@ RouteUI::mute_press (GdkEventButton* ev)
if (Keyboard::is_button2_event (ev)) {
// button2-click is "momentary"
_mute_release = new SoloMuteRelease (_route->muted ());
_mute_release = new SoloMuteRelease (_route->mute_control()->muted ());
}
if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
@ -415,7 +415,7 @@ RouteUI::mute_press (GdkEventButton* ev)
}
DisplaySuspender ds;
_session->set_mute (copy, !_route->muted());
_session->set_controls (route_list_to_control_list (copy, &Route::mute_control), _route->muted() ? 0.0 : 1.0, Controllable::UseGroup);
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
@ -444,7 +444,7 @@ RouteUI::mute_press (GdkEventButton* ev)
}
DisplaySuspender ds;
_session->set_mute (rl, !_route->muted(), Session::rt_cleanup, Controllable::InverseGroup);
_session->set_controls (route_list_to_control_list (rl, &Route::mute_control), _route->muted() ? 0.0 : 1.0, Controllable::InverseGroup);
}
} else {
@ -458,7 +458,7 @@ RouteUI::mute_press (GdkEventButton* ev)
_mute_release->routes = rl;
}
_session->set_mute (rl, !_route->muted());
_session->set_control (_route->mute_control(), _route->muted() ? 0.0 : 1.0, Controllable::UseGroup);
}
}
@ -472,7 +472,7 @@ RouteUI::mute_release (GdkEventButton* /*ev*/)
{
if (_mute_release){
DisplaySuspender ds;
_session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, Controllable::UseGroup);
_session->set_controls (route_list_to_control_list (_mute_release->routes, &Route::mute_control), _mute_release->active, Controllable::UseGroup);
delete _mute_release;
_mute_release = 0;
}
@ -572,11 +572,7 @@ RouteUI::solo_press(GdkEventButton* ev)
}
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (_session->get_routes(), !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::UseGroup);
} else {
_session->set_solo (_session->get_routes(), !_route->self_soloed(), Session::rt_cleanup, Controllable::UseGroup);
}
_session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_control), !_route->solo_control()->get_value(), Controllable::UseGroup);
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
@ -600,14 +596,16 @@ RouteUI::solo_press(GdkEventButton* ev)
/* ??? we need a just_one_listen() method */
} else {
DisplaySuspender ds;
_session->set_just_one_solo (_route, true);
boost::shared_ptr<ControlList> cl (new ControlList);
cl->push_back (_route->solo_control());
_session->set_controls (cl, 1.0, Controllable::NoGroup);
}
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
// shift-click: toggle solo isolated status
_route->set_solo_isolated (!_route->solo_isolated(), Controllable::UseGroup);
_route->solo_isolate_control()->set_value (_route->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
delete _solo_release;
_solo_release = 0;
@ -647,11 +645,7 @@ RouteUI::solo_press(GdkEventButton* ev)
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::InverseGroup);
} else {
_session->set_solo (rl, !_route->self_soloed(), Session::rt_cleanup, Controllable::InverseGroup);
}
_session->set_controls (route_list_to_control_list (rl, &Route::solo_control), !_route->self_soloed(), Controllable::InverseGroup);
}
delete _solo_release;
@ -669,11 +663,7 @@ RouteUI::solo_press(GdkEventButton* ev)
}
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !_route->listening_via_monitor());
} else {
_session->set_solo (rl, !_route->self_soloed());
}
_session->set_controls (route_list_to_control_list (rl, &Route::solo_control), !_route->self_soloed(), Controllable::UseGroup);
}
}
}
@ -690,11 +680,7 @@ RouteUI::solo_release (GdkEventButton* /*ev*/)
} else {
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup);
} else {
_session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup);
}
_session->set_controls (route_list_to_control_list (_solo_release->routes, &Route::solo_control), _solo_release->active ? 1.0 : 0.0, Controllable::UseGroup);
}
delete _solo_release;
@ -741,7 +727,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
DisplaySuspender ds;
_session->set_record_enabled (_session->get_routes(), !_route->record_enabled());
_session->set_controls (route_list_to_control_list (_session->get_routes(), &Track::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::NoGroup);
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
@ -757,7 +743,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
rl->push_back (_route);
DisplaySuspender ds;
_session->set_record_enabled (rl, !_route->record_enabled(), Session::rt_cleanup, Controllable::InverseGroup);
_session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::InverseGroup);
}
} else if (Keyboard::is_context_menu_event (ev)) {
@ -766,10 +752,8 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
} else {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (route());
DisplaySuspender ds;
_session->set_record_enabled (rl, !_route->record_enabled());
boost::shared_ptr<Track> trk = track();
_session->set_control (trk->rec_enable_control(), !trk->rec_enable_control()->get_value(), Controllable::UseGroup);
}
}
@ -777,7 +761,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
}
void
RouteUI::monitoring_changed ()
RouteUI::monitoring_changed (bool, Controllable::GroupControlDisposition)
{
update_monitoring_display ();
}
@ -797,7 +781,7 @@ RouteUI::update_monitoring_display ()
MonitorState ms = t->monitoring_state();
if (t->monitoring_choice() & MonitorInput) {
if (t->monitoring_control()->monitoring_choice() & MonitorInput) {
monitor_input_button->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
if (ms & MonitoringInput) {
@ -807,7 +791,7 @@ RouteUI::update_monitoring_display ()
}
}
if (t->monitoring_choice() & MonitorDisk) {
if (t->monitoring_control()->monitoring_choice() & MonitorDisk) {
monitor_disk_button->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
if (ms & MonitoringDisk) {
@ -863,8 +847,8 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
signal together, which requires yet more buffers.
*/
if (t->monitoring_choice() & monitor_choice) {
mc = MonitorChoice (t->monitoring_choice() & ~monitor_choice);
if (t->monitoring_control()->monitoring_choice() & monitor_choice) {
mc = MonitorChoice (t->monitoring_control()->monitoring_choice() & ~monitor_choice);
} else {
/* this line will change when the options are non-orthogonal */
// mc = MonitorChoice (t->monitoring_choice() | monitor_choice);
@ -887,7 +871,7 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
}
DisplaySuspender ds;
_session->set_monitoring (rl, mc, Session::rt_cleanup, Controllable::UseGroup);
_session->set_controls (route_list_to_control_list (rl, &Route::monitoring_control), (double) mc, Controllable::UseGroup);
return false;
}
@ -912,7 +896,9 @@ RouteUI::build_record_menu ()
}
if (step_edit_item) {
step_edit_item->set_sensitive (!_route->record_enabled());
if (track()->rec_enable_control()->get_value()) {
step_edit_item->set_sensitive (false);
}
step_edit_item->set_active (midi_track()->step_editing());
}
if (rec_safe_item) {
@ -924,7 +910,7 @@ RouteUI::build_record_menu ()
void
RouteUI::toggle_step_edit ()
{
if (!is_midi_track() || _route->record_enabled()) {
if (!is_midi_track() || track()->rec_enable_control()->get_value()) {
return;
}
@ -1168,7 +1154,7 @@ RouteUI::solo_isolate_active_state (boost::shared_ptr<Route> r)
return Gtkmm2ext::Off;
}
if (r->solo_isolated()) {
if (r->solo_isolate_control()->solo_isolated()) {
return Gtkmm2ext::ExplicitActive;
} else {
return Gtkmm2ext::Off;
@ -1182,7 +1168,7 @@ RouteUI::solo_safe_active_state (boost::shared_ptr<Route> r)
return Gtkmm2ext::Off;
}
if (r->solo_safe()) {
if (r->solo_safe_control()->solo_safe()) {
return Gtkmm2ext::ExplicitActive;
} else {
return Gtkmm2ext::Off;
@ -1192,13 +1178,13 @@ RouteUI::solo_safe_active_state (boost::shared_ptr<Route> r)
void
RouteUI::update_solo_display ()
{
bool yn = _route->solo_safe ();
bool yn = _route->solo_safe_control()->solo_safe ();
if (solo_safe_check && solo_safe_check->get_active() != yn) {
solo_safe_check->set_active (yn);
}
yn = _route->solo_isolated ();
yn = _route->solo_isolate_control()->solo_isolated ();
if (solo_isolated_check && solo_isolated_check->get_active() != yn) {
solo_isolated_check->set_active (yn);
@ -1207,7 +1193,7 @@ RouteUI::update_solo_display ()
set_button_names ();
if (solo_isolated_led) {
if (_route->solo_isolated()) {
if (_route->solo_isolate_control()->solo_isolated()) {
solo_isolated_led->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
solo_isolated_led->unset_active_state ();
@ -1215,7 +1201,7 @@ RouteUI::update_solo_display ()
}
if (solo_safe_led) {
if (_route->solo_safe()) {
if (_route->solo_safe_control()->solo_safe()) {
solo_safe_led->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
solo_safe_led->unset_active_state ();
@ -1248,7 +1234,7 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr<Route> r)
if (r->muted ()) {
/* full mute */
return Gtkmm2ext::ExplicitActive;
} else if (r->muted_by_others ()) {
} else if (r->muted_by_others_soloing ()) {
/* this will reflect both solo mutes AND master mutes */
return Gtkmm2ext::ImplicitActive;
} else {
@ -1261,10 +1247,11 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr<Route> r)
if (r->muted()) {
/* full mute */
return Gtkmm2ext::ExplicitActive;
} else if (r->mute_master()->muted_by_others()) {
} else if (r->muted_by_others()) {
/* note the direct use of MuteMaster API here. We are
not interested in showing
others-soloed-so-this-muted status in this branch.
others-soloed-so-this-muted status in this
conditional branch.
*/
return Gtkmm2ext::ImplicitActive;
} else {
@ -1335,11 +1322,16 @@ RouteUI::blink_rec_display (bool blinkOn)
if (!rec_enable_button || !_route) {
return;
}
if (boost::dynamic_pointer_cast<Send>(_current_delivery)) {
return;
}
if (_route->record_enabled()) {
if (!is_track()) {
return;
}
if (track()->rec_enable_control()->get_value()) {
switch (_session->record_status ()) {
case Session::Recording:
rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
@ -1347,12 +1339,12 @@ RouteUI::blink_rec_display (bool blinkOn)
case Session::Disabled:
case Session::Enabled:
if ( UIConfiguration::instance().get_blink_rec_arm() )
rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off );
else
rec_enable_button->set_active_state ( ImplicitActive );
if (UIConfiguration::instance().get_blink_rec_arm()) {
rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off );
} else {
rec_enable_button->set_active_state ( ImplicitActive );
}
break;
}
if (step_edit_item) {
@ -1381,14 +1373,14 @@ RouteUI::build_solo_menu (void)
Gtk::CheckMenuItem* check;
check = new Gtk::CheckMenuItem(_("Solo Isolate"));
check->set_active (_route->solo_isolated());
check->set_active (_route->solo_isolate_control()->solo_isolated());
check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
items.push_back (CheckMenuElem(*check));
solo_isolated_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
check->show_all();
check = new Gtk::CheckMenuItem(_("Solo Safe"));
check->set_active (_route->solo_safe());
check->set_active (_route->solo_safe_control()->solo_safe());
check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_safe), check));
items.push_back (CheckMenuElem(*check));
solo_safe_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
@ -1442,16 +1434,16 @@ RouteUI::build_mute_menu(void)
void
RouteUI::init_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
{
check->set_active (_route->mute_points() & mp);
check->set_active (_route->mute_control()->mute_points() & mp);
}
void
RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
{
if (check->get_active()) {
_route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() | mp));
_route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() | mp));
} else {
_route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() & ~mp));
_route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() & ~mp));
}
}
@ -1461,7 +1453,7 @@ RouteUI::muting_change ()
ENSURE_GUI_THREAD (*this, &RouteUI::muting_change)
bool yn;
MuteMaster::MutePoint current = _route->mute_points ();
MuteMaster::MutePoint current = _route->mute_control()->mute_points ();
yn = (current & MuteMaster::PreFader);
@ -1496,7 +1488,7 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev)
}
bool view = solo_isolated_led->active_state();
bool model = _route->solo_isolated();
bool model = _route->solo_isolate_control()->solo_isolated();
/* called BEFORE the view has changed */
@ -1506,11 +1498,11 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev)
if (model) {
/* disable isolate for all routes */
DisplaySuspender ds;
_session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, Controllable::NoGroup);
_session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_isolate_control), 0.0, Controllable::NoGroup);
} else {
/* enable isolate for all routes */
DisplaySuspender ds;
_session->set_solo_isolated (_session->get_routes(), true, Session::rt_cleanup, Controllable::NoGroup);
_session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_isolate_control), 1.0, Controllable::NoGroup);
}
} else {
@ -1522,7 +1514,7 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev)
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (_route);
DisplaySuspender ds;
_session->set_solo_isolated (rl, !view, Session::rt_cleanup, Controllable::NoGroup);
_session->set_controls (route_list_to_control_list (rl, &Route::solo_isolate_control), view ? 0.0 : 1.0, Controllable::NoGroup);
}
}
}
@ -1538,7 +1530,7 @@ RouteUI::solo_safe_button_release (GdkEventButton* ev)
}
bool view = solo_safe_led->active_state();
bool model = _route->solo_safe();
bool model = _route->solo_safe_control()->solo_safe();
if (ev->button == 1) {
if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
@ -1547,20 +1539,20 @@ RouteUI::solo_safe_button_release (GdkEventButton* ev)
/* disable solo safe for all routes */
DisplaySuspender ds;
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
(*i)->set_solo_safe (false, Controllable::NoGroup);
(*i)->solo_safe_control()->set_value (0.0, Controllable::NoGroup);
}
} else {
/* enable solo safe for all routes */
DisplaySuspender ds;
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
(*i)->set_solo_safe (true, Controllable::NoGroup);
(*i)->solo_safe_control()->set_value (1.0, Controllable::NoGroup);
}
}
}
else {
if (model == view) {
/* flip just this route */
_route->set_solo_safe (!view, Controllable::NoGroup);
_route->solo_safe_control()->set_value (view ? 0.0 : 1.0, Controllable::NoGroup);
}
}
}
@ -1572,19 +1564,19 @@ void
RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
{
bool view = check->get_active();
bool model = _route->solo_isolated();
bool model = _route->solo_isolate_control()->solo_isolated();
/* called AFTER the view has changed */
if (model != view) {
_route->set_solo_isolated (view, Controllable::UseGroup);
_route->solo_isolate_control()->set_value (view ? 1.0 : 0.0, Controllable::UseGroup);
}
}
void
RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
{
_route->set_solo_safe (check->get_active(), Controllable::UseGroup);
_route->solo_safe_control()->set_value (check->get_active() ? 1.0 : 0.0, Controllable::UseGroup);
}
/** Ask the user to choose a colour, and then apply that color to my route
@ -2022,25 +2014,25 @@ RouteUI::parameter_changed (string const & p)
void
RouteUI::step_gain_up ()
{
_route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup);
_route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup);
}
void
RouteUI::page_gain_up ()
{
_route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup);
_route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup);
}
void
RouteUI::step_gain_down ()
{
_route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup);
_route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup);
}
void
RouteUI::page_gain_down ()
{
_route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup);
_route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup);
}
void
@ -2158,9 +2150,9 @@ RouteUI::set_invert_button_state ()
ArdourButton* b = _invert_buttons.front ();
if (_route->phase_invert().count() == _route->phase_invert().size()) {
if (_route->phase_control()->count() == _route->phase_control()->size()) {
b->set_active_state (Gtkmm2ext::ExplicitActive);
} else if (_route->phase_invert().any()) {
} else if (_route->phase_control()->any()) {
b->set_active_state (Gtkmm2ext::ImplicitActive);
} else {
b->set_active_state (Gtkmm2ext::Off);
@ -2172,7 +2164,7 @@ RouteUI::set_invert_button_state ()
int j = 0;
for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i, ++j) {
(*i)->set_active (_route->phase_invert (j));
(*i)->set_active (_route->phase_control()->inverted (j));
}
}
@ -2185,7 +2177,7 @@ RouteUI::invert_release (GdkEventButton* ev, uint32_t i)
uint32_t const N = _route->input()->n_ports().n_audio ();
if (N <= _max_invert_buttons) {
/* left-click inverts phase so long as we have a button per channel */
_route->set_phase_invert (i, !_invert_buttons[i]->get_active());
_route->phase_control()->set_phase_invert (i, !_invert_buttons[i]->get_active());
return false;
}
}
@ -2216,7 +2208,7 @@ RouteUI::invert_press (GdkEventButton* ev)
items.push_back (CheckMenuElem (string_compose (X_("Ø%1"), i + 1), sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_menu_toggled), i)));
Gtk::CheckMenuItem* e = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
++_i_am_the_modifier;
e->set_active (_route->phase_invert (i));
e->set_active (_route->phase_control()->inverted (i));
--_i_am_the_modifier;
}
@ -2232,7 +2224,8 @@ RouteUI::invert_menu_toggled (uint32_t c)
return;
}
_route->set_phase_invert (c, !_route->phase_invert (c));
_route->phase_control()->set_phase_invert (c, !_route->phase_control()->inverted (c));
}
void

View File

@ -156,7 +156,7 @@ class RouteUI : public virtual AxisView
bool monitor_input_release(GdkEventButton*);
bool monitor_disk_press(GdkEventButton*);
bool monitor_disk_release(GdkEventButton*);
void monitoring_changed ();
void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition);
void update_monitoring_display ();
void edit_input_configuration ();

View File

@ -78,7 +78,7 @@ StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Container* canvas_g
if (_trackview.is_track()) {
_trackview.track()->DiskstreamChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::diskstream_changed, this), gui_context());
_trackview.track()->RecordEnableChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::rec_enable_changed, this), gui_context());
_trackview.track()->rec_enable_control()->Changed.connect (*this, invalidator (*this), boost::bind (&StreamView::rec_enable_changed, this), gui_context());
_trackview.session()->TransportStateChange.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_changed, this), gui_context());
_trackview.session()->TransportLooped.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_looped, this), gui_context());

View File

@ -763,7 +763,7 @@ TimeAxisView::end_name_edit (int response)
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
if (rtav && rtav->route()->record_enabled()) {
if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
continue;
}
@ -794,7 +794,7 @@ TimeAxisView::end_name_edit (int response)
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*>(*i);
if (rtav && rtav->route()->record_enabled()) {
if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) {
continue;
}

View File

@ -41,7 +41,7 @@ namespace ARDOUR {
class Session;
class Automatable;
class ControlGroup;
/** A PBD::Controllable with associated automation data (AutomationList)
*/
@ -50,7 +50,7 @@ class LIBARDOUR_API AutomationControl
, public Evoral::Control
, public boost::enable_shared_from_this<AutomationControl>
{
public:
public:
AutomationControl(ARDOUR::Session&,
const Evoral::Parameter& parameter,
const ParameterDescriptor& desc,
@ -87,8 +87,6 @@ public:
void stop_touch(bool mark, double when);
/* inherited from PBD::Controllable.
* Derived classes MUST call ::writable() to verify
* that writing to the parameter is legal at that time.
*/
double get_value () const;
/* inherited from PBD::Controllable.
@ -99,10 +97,11 @@ public:
/* automation related value setting */
virtual bool writable () const;
/* Call to ::set_value() with no test for writable() because
* this is only used by automation playback. We would like
* to make it pure virtual
* this is only used by automation playback.
*/
virtual void set_value_unchecked (double val) {}
void set_value_unchecked (double val) {
actually_set_value (val, PBD::Controllable::NoGroup);
}
double lower() const { return _desc.lower; }
double upper() const { return _desc.upper; }
@ -117,6 +116,37 @@ public:
const ARDOUR::Session& session() const { return _session; }
void commit_transaction (bool did_write);
void set_group (boost::shared_ptr<ControlGroup>);
protected:
ARDOUR::Session& _session;
boost::shared_ptr<ControlGroup> _group;
const ParameterDescriptor _desc;
bool check_rt (double val, Controllable::GroupControlDisposition gcd);
/* derived classes may reimplement this, but should either
call this explicitly inside their version OR make sure that the
Controllable::Changed signal is emitted when necessary.
*/
virtual void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
};
class SlavableAutomationControl : public AutomationControl
{
public:
SlavableAutomationControl(ARDOUR::Session&,
const Evoral::Parameter& parameter,
const ParameterDescriptor& desc,
boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(),
const std::string& name="");
~SlavableAutomationControl ();
double get_value () const;
void add_master (boost::shared_ptr<AutomationControl>);
void remove_master (boost::shared_ptr<AutomationControl>);
void clear_masters ();
@ -126,11 +156,7 @@ public:
PBD::Signal0<void> MasterStatusChange;
protected:
ARDOUR::Session& _session;
const ParameterDescriptor _desc;
protected:
class MasterRecord {
public:
@ -155,12 +181,12 @@ public:
typedef std::map<PBD::ID,MasterRecord> Masters;
Masters _masters;
PBD::ScopedConnectionList masters_connections;
virtual void master_changed (bool from_self, GroupControlDisposition gcd);
void master_going_away (boost::weak_ptr<AutomationControl>);
virtual void recompute_masters_ratios (double val) { /* do nothing by default */}
virtual double get_masters_value_locked () const;
double get_value_locked() const;
};

View File

@ -34,14 +34,11 @@ namespace ARDOUR {
class Session;
class LIBARDOUR_API GainControl : public AutomationControl {
class LIBARDOUR_API GainControl : public SlavableAutomationControl {
public:
GainControl (Session& session, const Evoral::Parameter &param,
boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>());
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double internal_to_interface (double) const;
double interface_to_internal (double) const;
double internal_to_user (double) const;
@ -54,6 +51,8 @@ class LIBARDOUR_API GainControl : public AutomationControl {
int set_state (XMLNode const&, int);
XMLNode& get_state();
void inc_gain (gain_t);
private:
std::string masters_string;
PBD::ScopedConnection vca_loaded_connection;
@ -61,7 +60,7 @@ class LIBARDOUR_API GainControl : public AutomationControl {
void vcas_loaded();
void recompute_masters_ratios (double val);
void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
};
} /* namespace */

View File

@ -50,8 +50,9 @@ public:
boost::shared_ptr<Diskstream> create_diskstream ();
void set_diskstream (boost::shared_ptr<Diskstream>);
void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition);
void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition);
bool can_be_record_enabled ();
bool can_be_record_safe ();
DataType data_type () const {
return DataType::MIDI;
@ -89,14 +90,12 @@ public:
, _route (route)
{}
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
bool writable() const { return true; }
MidiTrack* _route;
private:
void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
};
virtual void set_parameter_automation_state (Evoral::Parameter param, AutoState);
@ -133,7 +132,6 @@ public:
PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded;
boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition);
MonitorState monitoring_state () const;
void set_input_active (bool);
@ -144,6 +142,7 @@ protected:
XMLNode& state (bool full);
void act_on_mute ();
void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
MidiRingBuffer<framepos_t> _immediate_events;

View File

@ -20,12 +20,15 @@
#ifndef __ardour_mute_master_h__
#define __ardour_mute_master_h__
#include "evoral/Parameter.hpp"
#include "pbd/signals.h"
#include "pbd/stateful.h"
#include <string>
#include "pbd/signals.h"
#include "pbd/stateful.h"
#include "evoral/Parameter.hpp"
#include "ardour/session_handle.h"
#include "ardour/types.h"
namespace ARDOUR {
@ -73,6 +76,7 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful
XMLNode& get_state();
int set_state(const XMLNode&, int version);
static const std::string xml_node_name;
private:
MutePoint _mute_point;

View File

@ -47,12 +47,10 @@ class LIBARDOUR_API PanControllable : public AutomationControl
{}
double lower () const;
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
private:
Pannable* owner;
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
} // namespace

View File

@ -185,15 +185,13 @@ class LIBARDOUR_API PluginInsert : public Processor
const ParameterDescriptor& desc,
boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>());
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value (void) const;
void catch_up_with_external_value (double val);
XMLNode& get_state();
private:
PluginInsert* _plugin;
void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
};
/** A control that manipulates a plugin property (message). */
@ -204,10 +202,10 @@ class LIBARDOUR_API PluginInsert : public Processor
const ParameterDescriptor& desc,
boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>());
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value (void) const;
XMLNode& get_state();
protected:
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
private:
PluginInsert* _plugin;

View File

@ -28,7 +28,6 @@
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <glibmm/threads.h>
@ -46,12 +45,18 @@
#include "ardour/io_vector.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"
#include "ardour/monitorable.h"
#include "ardour/muteable.h"
#include "ardour/mute_master.h"
#include "ardour/mute_control.h"
#include "ardour/route_group_member.h"
#include "ardour/stripable.h"
#include "ardour/graphnode.h"
#include "ardour/automatable.h"
#include "ardour/unknown_processor.h"
#include "ardour/soloable.h"
#include "ardour/solo_control.h"
#include "ardour/solo_safe_control.h"
class RoutePinWindowProxy;
@ -74,8 +79,18 @@ class Pannable;
class CapturingProcessor;
class InternalSend;
class VCA;
class SoloIsolateControl;
class PhaseControl;
class MonitorControl;
class LIBARDOUR_API Route : public Stripable, public Automatable, public RouteGroupMember, public GraphNode, public boost::enable_shared_from_this<Route>
class LIBARDOUR_API Route : public Stripable,
public Soloable,
public Muteable,
public Monitorable,
public Automatable,
public RouteGroupMember,
public GraphNode,
public boost::enable_shared_from_this<Route>
{
public:
@ -119,7 +134,7 @@ public:
bool is_master() const { return _flags & MasterOut; }
bool is_monitor() const { return _flags & MonitorOut; }
virtual MonitorState monitoring_state () const;
MonitorState monitoring_state () const;
virtual MeterState metering_state () const;
/* these are the core of the API of a Route. see the protected sections as well */
@ -135,10 +150,6 @@ public:
virtual bool can_record() { return false; }
virtual void set_record_enabled (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {}
virtual bool record_enabled() const { return false; }
virtual void set_record_safe (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {}
virtual bool record_safe () const {return false; }
virtual void nonrealtime_handle_transport_stopped (bool abort, bool did_locate, bool flush_processors);
virtual void realtime_handle_transport_stopped () {}
virtual void realtime_locate () {}
@ -149,47 +160,31 @@ public:
void shift (framepos_t, framecnt_t);
void set_gain (gain_t val, PBD::Controllable::GroupControlDisposition);
void inc_gain (gain_t delta);
void set_trim (gain_t val, PBD::Controllable::GroupControlDisposition);
void set_mute_points (MuteMaster::MutePoint);
MuteMaster::MutePoint mute_points () const;
bool muted () const;
void set_mute (bool yn, PBD::Controllable::GroupControlDisposition);
bool muted_by_others_soloing () const;
bool muted_by_others () const;
/* controls use set_solo() to modify this route's solo state
*/
void set_solo (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
bool soloed () const { return self_soloed () || soloed_by_others (); }
void clear_all_solo_state ();
bool soloed_by_others () const { return _soloed_by_others_upstream||_soloed_by_others_downstream; }
bool soloed_by_others_upstream () const { return _soloed_by_others_upstream; }
bool soloed_by_others_downstream () const { return _soloed_by_others_downstream; }
bool self_soloed () const { return _self_solo; }
bool soloed_by_others () const { return _solo_control->soloed_by_others(); }
bool soloed_by_others_upstream () const { return _solo_control->soloed_by_others_upstream(); }
bool soloed_by_others_downstream () const { return _solo_control->soloed_by_others_downstream(); }
bool self_soloed () const { return _solo_control->self_soloed(); }
bool soloed () const { return self_soloed () || soloed_by_others (); }
void set_solo_isolated (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
bool solo_isolated() const;
void push_solo_upstream (int32_t delta);
void push_solo_isolate_upstream (int32_t delta);
bool can_solo () const {
return !(is_master() || is_monitor() || is_auditioner());
}
bool is_safe () const {
return _solo_safe_control->get_value();
}
void set_solo_safe (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
bool solo_safe() const;
void set_listen (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
bool listening_via_monitor () const;
void enable_monitor_send ();
void set_phase_invert (uint32_t, bool yn);
void set_phase_invert (boost::dynamic_bitset<>);
bool phase_invert (uint32_t) const;
boost::dynamic_bitset<> phase_invert () const;
void set_denormal_protection (bool yn);
bool denormal_protection() const;
@ -353,7 +348,6 @@ public:
PBD::Signal0<void> active_changed;
PBD::Signal0<void> denormal_protection_changed;
PBD::Signal0<void> comment_changed;
PBD::Signal0<void> mute_points_changed;
/** track numbers - assigned by session
* nubers > 0 indicate tracks (audio+midi)
@ -456,185 +450,32 @@ public:
boost::shared_ptr<AutomationControl> get_control (const Evoral::Parameter& param);
class RouteAutomationControl : public AutomationControl {
public:
RouteAutomationControl (const std::string& name,
AutomationType atype,
boost::shared_ptr<AutomationList> alist,
boost::shared_ptr<Route> route);
protected:
friend class Route;
void route_set_value (double val) {
AutomationControl::set_value (val, Controllable::NoGroup);
}
boost::weak_ptr<Route> _route;
};
class BooleanRouteAutomationControl : public RouteAutomationControl {
public:
BooleanRouteAutomationControl (const std::string& name,
AutomationType atype,
boost::shared_ptr<AutomationList> alist,
boost::shared_ptr<Route> route)
: RouteAutomationControl (name, atype, alist, route) {}
protected:
double get_masters_value_locked() const;
};
class GainControllable : public GainControl {
public:
GainControllable (Session& session,
AutomationType type,
boost::shared_ptr<Route> route);
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override) {
boost::shared_ptr<Route> r = _route.lock();
if (r) {
/* Route must mediate group control */
r->set_control ((AutomationType) parameter().type(), val, group_override);
}
}
protected:
friend class Route;
void route_set_value (double val) {
GainControl::set_value (val, Controllable::NoGroup);
}
boost::weak_ptr<Route> _route;
};
class SoloControllable : public BooleanRouteAutomationControl {
public:
SoloControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value () const;
/* Export additional API so that objects that only get access
* to a Controllable/AutomationControl can do more fine-grained
* operations with respect to solo. Obviously, they would need
* to dynamic_cast<Route::SoloControllable> first.
*
* Solo state is not representable by a single scalar value,
* so this AutomationControl maps set_value() and get_value()
* to r->set_self_solo() and r->soloed() respectively. This
* means that the Controllable is technically asymmetric. It is
* possible to call ::set_value (0.0) to disable (self)solo,
* and then call ::get_value() and get a return of 1.0 because
* the Route owner is soloed by upstream/downstream.
*/
void set_self_solo (bool yn) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->set_self_solo (yn);
}
void mod_solo_by_others_upstream (int32_t delta) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta);
}
void mod_solo_by_others_downstream (int32_t delta) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta);
}
bool soloed_by_others () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others(); else return false;
}
bool soloed_by_others_upstream () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false;
}
bool soloed_by_others_downstream () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false;
}
bool self_soloed () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->self_soloed(); else return false;
}
protected:
void master_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
struct MuteControllable : public BooleanRouteAutomationControl {
public:
MuteControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value () const;
/* Pretend to change value, but do not affect actual route mute. */
void set_superficial_value(bool muted);
protected:
void master_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
boost::weak_ptr<Route> _route;
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
class LIBARDOUR_API PhaseControllable : public BooleanRouteAutomationControl {
public:
PhaseControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
/* currently no automation, so no need for set_value_unchecked() */
void set_channel (uint32_t);
double get_value () const;
uint32_t channel() const;
private:
uint32_t _current_phase;
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
class LIBARDOUR_API SoloIsolateControllable : public BooleanRouteAutomationControl {
public:
SoloIsolateControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
/* currently no automation, so no need for set_value_unchecked() */
double get_value () const;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
class LIBARDOUR_API SoloSafeControllable : public BooleanRouteAutomationControl {
public:
SoloSafeControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
/* currently no automation, so no need for set_value_unchecked() */
double get_value () const;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
void set_control (AutomationType, double val, PBD::Controllable::GroupControlDisposition group_override);
boost::shared_ptr<AutomationControl> solo_control() const {
boost::shared_ptr<SoloControl> solo_control() const {
return _solo_control;
}
boost::shared_ptr<AutomationControl> mute_control() const {
boost::shared_ptr<MuteControl> mute_control() const {
return _mute_control;
}
boost::shared_ptr<MuteMaster> mute_master() const {
return _mute_master;
}
bool can_be_muted_by_others () const { return !is_master(); }
bool muted () const { return _mute_control->muted(); }
bool muted_by_others_soloing () const;
bool muted_by_others () const;
boost::shared_ptr<AutomationControl> solo_isolate_control() const {
boost::shared_ptr<SoloIsolateControl> solo_isolate_control() const {
return _solo_isolate_control;
}
boost::shared_ptr<AutomationControl> solo_safe_control() const {
boost::shared_ptr<SoloSafeControl> solo_safe_control() const {
return _solo_safe_control;
}
boost::shared_ptr<AutomationControl> monitoring_control() const {
boost::shared_ptr<MonitorControl> monitoring_control() const {
/* tracks override this to provide actual monitoring control;
busses have no possible choices except input monitoring.
*/
return boost::shared_ptr<AutomationControl> ();
return boost::shared_ptr<MonitorControl> ();
}
/* Route doesn't own these items, but sub-objects that it does own have them
@ -647,8 +488,8 @@ public:
boost::shared_ptr<Pannable> pannable() const;
boost::shared_ptr<GainControl> gain_control() const;
boost::shared_ptr<AutomationControl> trim_control() const;
boost::shared_ptr<AutomationControl> phase_control() const;
boost::shared_ptr<GainControl> trim_control() const;
boost::shared_ptr<PhaseControl> phase_control() const;
/**
Return the first processor that accepts has at least one MIDI input
@ -766,8 +607,8 @@ public:
friend class Session;
void catch_up_on_solo_mute_override ();
void mod_solo_by_others_upstream (int32_t);
void mod_solo_by_others_downstream (int32_t);
void set_listen (bool);
void curve_reallocate ();
virtual void set_block_size (pframes_t nframes);
@ -829,14 +670,6 @@ protected:
MeterPoint _meter_point;
MeterPoint _pending_meter_point;
MeterType _meter_type;
boost::dynamic_bitset<> _phase_invert;
bool _self_solo;
uint32_t _soloed_by_others_upstream;
uint32_t _soloed_by_others_downstream;
bool _solo_isolated;
uint32_t _solo_isolated_by_upstream;
void mod_solo_isolated_by_upstream (bool);
bool _denormal_protection;
@ -844,18 +677,14 @@ protected:
bool _silent : 1;
bool _declickable : 1;
boost::shared_ptr<SoloControllable> _solo_control;
boost::shared_ptr<MuteControllable> _mute_control;
boost::shared_ptr<MuteMaster> _mute_master;
boost::shared_ptr<PhaseControllable> _phase_control;
boost::shared_ptr<SoloIsolateControllable> _solo_isolate_control;
boost::shared_ptr<SoloSafeControllable> _solo_safe_control;
virtual void act_on_mute () {}
boost::shared_ptr<SoloControl> _solo_control;
boost::shared_ptr<MuteControl> _mute_control;
boost::shared_ptr<PhaseControl> _phase_control;
boost::shared_ptr<SoloIsolateControl> _solo_isolate_control;
boost::shared_ptr<SoloSafeControl> _solo_safe_control;
std::string _comment;
bool _have_internal_generator;
bool _solo_safe;
DataType _default_type;
FedBy _fed_by;
@ -882,9 +711,9 @@ protected:
virtual void maybe_declick (BufferSet&, framecnt_t, int);
boost::shared_ptr<GainControllable> _gain_control;
boost::shared_ptr<GainControl> _gain_control;
boost::shared_ptr<Amp> _amp;
boost::shared_ptr<GainControllable> _trim_control;
boost::shared_ptr<GainControl> _trim_control;
boost::shared_ptr<Amp> _trim;
boost::shared_ptr<PeakMeter> _meter;
boost::shared_ptr<DelayLine> _delayline;
@ -928,7 +757,6 @@ private:
void placement_range (Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end);
void set_self_solo (bool yn);
void set_mute_master_solo ();
void set_processor_positions ();
framecnt_t update_port_latencies (PortSet& ports, PortSet& feeders, bool playback, framecnt_t) const;
@ -985,6 +813,7 @@ private:
void reset_instrument_info ();
void set_remote_control_id_internal (uint32_t id, bool notify_class_listeners = true);
void solo_control_changed (bool self, PBD::Controllable::GroupControlDisposition);
};
} // namespace ARDOUR

View File

@ -29,10 +29,12 @@
#include "pbd/signals.h"
#include "pbd/stateful.h"
#include "ardour/libardour_visibility.h"
#include "ardour/control_group.h"
#include "ardour/types.h"
#include "ardour/session_object.h"
#include "ardour/libardour_visibility.h"
namespace ARDOUR {
namespace Properties {
@ -157,8 +159,17 @@ class LIBARDOUR_API RouteGroup : public SessionObject
PBD::Property<bool> _color;
PBD::Property<bool> _monitoring;
boost::shared_ptr<ControlGroup> _solo_group;
boost::shared_ptr<ControlGroup> _mute_group;
boost::shared_ptr<ControlGroup> _rec_enable_group;
boost::shared_ptr<ControlGroup> _gain_group;
boost::shared_ptr<ControlGroup> _monitoring_group;
void remove_when_going_away (boost::weak_ptr<Route>);
int set_state_2X (const XMLNode&, int);
void post_set (PBD::PropertyChange const &);
void push_to_groups ();
};
} /* namespace */

View File

@ -791,16 +791,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
static const SessionEvent::RTeventCallback rt_cleanup;
void set_solo (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_implicit_solo (boost::shared_ptr<RouteList>, int delta, bool up_or_downstream, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void clear_all_solo_state (boost::shared_ptr<RouteList>);
void set_just_one_solo (boost::shared_ptr<Route>, bool, SessionEvent::RTeventCallback after = rt_cleanup);
void set_mute (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_listen (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_record_enabled (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_record_safe (boost::shared_ptr<RouteList>, bool yn, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_solo_isolated (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
void set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup);
/* Control-based methods */
void set_controls (boost::shared_ptr<ControlList>, double val, PBD::Controllable::GroupControlDisposition);
void set_control (boost::shared_ptr<AutomationControl>, double val, PBD::Controllable::GroupControlDisposition);
void set_exclusive_input_active (boost::shared_ptr<RouteList> rt, bool onoff, bool flip_others = false);
PBD::Signal1<void,bool> SoloActive;
@ -1936,16 +1933,18 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
return ev;
}
void rt_set_solo (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_implicit_solo (boost::shared_ptr<RouteList>, int delta, bool up_or_downstream, PBD::Controllable::GroupControlDisposition);
/* specialized version realtime "apply to set of controls" operations */
SessionEvent* get_rt_event (boost::shared_ptr<ControlList> cl, double arg, PBD::Controllable::GroupControlDisposition group_override) {
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = boost::bind (&Session::rt_set_controls, this, cl, arg, group_override);
ev->rt_return = Session::rt_cleanup;
ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
return ev;
}
void rt_set_controls (boost::shared_ptr<ControlList>, double val, PBD::Controllable::GroupControlDisposition group_override);
void rt_clear_all_solo_state (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_just_one_solo (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition /* ignored*/ );
void rt_set_mute (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_listen (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_solo_isolated (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_record_enabled (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_record_safe (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, PBD::Controllable::GroupControlDisposition group_override);
/** temporary list of Diskstreams used only during load of 2.X sessions */
std::list<boost::shared_ptr<Diskstream> > _diskstreams_2X;

View File

@ -84,26 +84,28 @@ public:
union {
void* ptr;
bool yes_or_no;
framepos_t target2_frame;
framepos_t target2_frame;
Slave* slave;
Route* route;
};
union {
bool second_yes_or_no;
double control_value;
};
union {
bool third_yes_or_no;
};
/* 4 members to handle a multi-group event handled in RT context */
/* 5 members to handle a multi-group event handled in RT context */
typedef boost::function<void (SessionEvent*)> RTeventCallback;
boost::shared_ptr<RouteList> routes; /* apply to */
boost::function<void (void)> rt_slot; /* what to call in RT context */
RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */
boost::shared_ptr<ControlList> controls; /* apply to */
boost::shared_ptr<RouteList> routes; /* apply to */
boost::function<void (void)> rt_slot; /* what to call in RT context */
RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */
PBD::EventLoop* event_loop;
std::list<AudioRange> audio_range;

View File

@ -26,13 +26,19 @@
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include "ardour/gain_control.h"
#include "ardour/session_object.h"
namespace ARDOUR {
class AutomationControl;
class GainControl;
class PeakMeter;
class SoloControl;
class MuteControl;
class PhaseControl;
class SoloIsolateControl;
class SoloSafeControl;
class MonitorControl;
/* This is a virtual base class for any object that needs to be potentially
* represented by a control-centric user interface using the general model of a
@ -58,12 +64,13 @@ class Stripable : public SessionObject {
virtual boost::shared_ptr<GainControl> gain_control() const = 0;
virtual boost::shared_ptr<AutomationControl> solo_control() const = 0;
virtual boost::shared_ptr<AutomationControl> mute_control() const = 0;
virtual boost::shared_ptr<AutomationControl> phase_control() const = 0;
virtual boost::shared_ptr<AutomationControl> trim_control() const = 0;
virtual boost::shared_ptr<SoloControl> solo_control() const = 0;
virtual boost::shared_ptr<MuteControl> mute_control() const = 0;
virtual boost::shared_ptr<AutomationControl> monitoring_control() const = 0;
virtual boost::shared_ptr<PhaseControl> phase_control() const = 0;
virtual boost::shared_ptr<GainControl> trim_control() const = 0;
virtual boost::shared_ptr<MonitorControl> monitoring_control() const = 0;
virtual boost::shared_ptr<AutomationControl> recenable_control() const { return boost::shared_ptr<AutomationControl>(); }
/* "well-known" controls for panning. Any or all of these may return
@ -131,6 +138,9 @@ class Stripable : public SessionObject {
* the route.
*/
virtual boost::shared_ptr<AutomationControl> master_send_enable_controllable () const = 0;
virtual bool muted_by_others_soloing () const = 0;
virtual bool muted_by_others () const = 0;
};

View File

@ -22,6 +22,7 @@
#include <boost/shared_ptr.hpp>
#include "ardour/interthread_info.h"
#include "ardour/recordable.h"
#include "ardour/route.h"
#include "ardour/public_diskstream.h"
@ -34,13 +35,14 @@ class Source;
class Region;
class Diskstream;
class IO;
class MonitorControl;
/** A track is an route (bus) with a recordable diskstream and
* related objects relevant to tracking, playback and editing.
*
* Specifically a track has regions and playlist objects.
*/
class LIBARDOUR_API Track : public Route, public PublicDiskstream
class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskstream
{
public:
Track (Session&, std::string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal, DataType default_type = DataType::AUDIO);
@ -56,23 +58,9 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
virtual bool can_use_mode (TrackMode /*m*/, bool& /*bounce_required*/) { return false; }
PBD::Signal0<void> TrackModeChanged;
class LIBARDOUR_API MonitoringControllable : public RouteAutomationControl {
public:
MonitoringControllable (std::string name, boost::shared_ptr<Track>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
/* currently no automation, so no need for set_value_unchecked() */
double get_value () const;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition group_override);
MonitorChoice monitoring_choice() const { return _monitoring; }
MonitorState monitoring_state () const;
PBD::Signal0<void> MonitoringChanged;
boost::shared_ptr<AutomationControl> monitoring_control() const { return _monitoring_control; }
boost::shared_ptr<MonitorControl> monitoring_control() const { return _monitoring_control; }
MonitorState monitoring_state () const;
MeterState metering_state () const;
virtual int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
@ -142,13 +130,12 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
virtual int set_state (const XMLNode&, int version);
static void zero_diskstream_id_in_xml (XMLNode&);
boost::shared_ptr<AutomationControl> rec_enable_control() { return _rec_enable_control; }
boost::shared_ptr<AutomationControl> rec_enable_control() const { return _record_enable_control; }
boost::shared_ptr<AutomationControl> rec_safe_control() const { return _record_safe_control; }
bool record_enabled() const;
bool record_safe () const;
void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition);
void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition);
void prep_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition);
int prep_record_enabled (bool);
bool can_be_record_enabled ();
bool can_be_record_safe ();
bool using_diskstream_id (PBD::ID) const;
@ -204,8 +191,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
PBD::Signal0<void> FreezeChange;
/* Emitted when our diskstream is set to use a different playlist */
PBD::Signal0<void> PlaylistChanged;
PBD::Signal0<void> RecordEnableChanged;
PBD::Signal0<void> RecordSafeChanged;
PBD::Signal0<void> SpeedChanged;
PBD::Signal0<void> AlignmentStyleChanged;
@ -216,8 +201,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
MeterPoint _saved_meter_point;
TrackMode _mode;
bool _needs_butler;
MonitorChoice _monitoring;
boost::shared_ptr<MonitoringControllable> _monitoring_control;
boost::shared_ptr<MonitorControl> _monitoring_control;
//private: (FIXME)
struct FreezeRecordProcessorInfo {
@ -242,20 +226,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
FreezeState state;
};
class RecEnableControl : public AutomationControl {
public:
RecEnableControl (boost::shared_ptr<Track> t);
void set_value (double, PBD::Controllable::GroupControlDisposition);
void set_value_unchecked (double);
double get_value (void) const;
boost::weak_ptr<Track> track;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition);
};
virtual void set_state_part_two () = 0;
FreezeRecord _freeze_record;
@ -264,17 +234,20 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
void maybe_declick (BufferSet&, framecnt_t, int);
boost::shared_ptr<RecEnableControl> _rec_enable_control;
boost::shared_ptr<AutomationControl> _record_enable_control;
boost::shared_ptr<AutomationControl> _record_safe_control;
virtual void record_enable_changed (bool, PBD::Controllable::GroupControlDisposition);
virtual void record_safe_changed (bool, PBD::Controllable::GroupControlDisposition);
framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&);
virtual void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
virtual boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &) = 0;
void diskstream_playlist_changed ();
void diskstream_record_enable_changed ();
void diskstream_record_safe_changed ();
void diskstream_speed_changed ();
void diskstream_alignment_style_changed ();
void parameter_changed (std::string const & p);

View File

@ -53,6 +53,7 @@ namespace ARDOUR {
class Route;
class Region;
class VCA;
class AutomationControl;
typedef float Sample;
typedef float pan_t;
@ -149,6 +150,7 @@ namespace ARDOUR {
FadeOutAutomation,
EnvelopeAutomation,
RecEnableAutomation,
RecSafeAutomation,
TrimAutomation,
PhaseAutomation,
MonitoringAutomation,
@ -565,6 +567,7 @@ namespace ARDOUR {
typedef std::list<boost::shared_ptr<Route> > RouteList;
typedef std::list<boost::weak_ptr <Route> > WeakRouteList;
typedef std::list<boost::shared_ptr<AutomationControl> > ControlList;
typedef std::list<boost::shared_ptr<VCA> > VCAList;

View File

@ -28,19 +28,26 @@
#include <string>
#include <cmath>
#include "boost/shared_ptr.hpp"
#if __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#endif /* __APPLE__ */
#include "ardour/libardour_visibility.h"
#include "ardour/ardour.h"
#include "ardour/data_type.h"
#include "ardour/dB.h"
#include "ardour/types.h"
#include "ardour/libardour_visibility.h"
class XMLNode;
namespace ARDOUR {
class Route;
class Track;
LIBARDOUR_API std::string legalize_for_path (const std::string& str);
LIBARDOUR_API std::string legalize_for_universal_path (const std::string& str);
LIBARDOUR_API std::string legalize_for_uri (const std::string& str);
@ -169,6 +176,29 @@ LIBARDOUR_API bool matching_unsuffixed_filename_exists_in (const std::string& di
LIBARDOUR_API uint32_t how_many_dsp_threads ();
template<typename T> boost::shared_ptr<ControlList> route_list_to_control_list (boost::shared_ptr<RouteList> rl, boost::shared_ptr<T> (Route::*get_control)() const) {
boost::shared_ptr<ControlList> cl (new ControlList);
for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
boost::shared_ptr<AutomationControl> ac = ((*r).get()->*get_control)();
if (ac) {
cl->push_back (ac);
}
}
return cl;
}
template<typename T> boost::shared_ptr<ControlList> route_list_to_control_list (boost::shared_ptr<RouteList> rl, boost::shared_ptr<T> (Track::*get_control)() const) {
boost::shared_ptr<ControlList> cl (new ControlList);
for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r);
boost::shared_ptr<AutomationControl> ac = (t.get()->*get_control)();
if (ac) {
cl->push_back (ac);
}
}
return cl;
}
#if __APPLE__
LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef);
#endif // __APPLE__
@ -176,4 +206,3 @@ LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef);
} //namespave
#endif /* __ardour_utils_h__ */

View File

@ -27,14 +27,19 @@
#include "pbd/statefuldestructible.h"
#include "ardour/automatable.h"
#include "ardour/muteable.h"
#include "ardour/soloable.h"
#include "ardour/stripable.h"
namespace ARDOUR {
class GainControl;
class Route;
class GainControl;
class SoloControl;
class MuteControl;
class MonitorControl;
class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::enable_shared_from_this<VCA> {
class LIBARDOUR_API VCA : public Stripable, public Soloable, public Muteable, public Automatable, public boost::enable_shared_from_this<VCA> {
public:
VCA (Session& session, uint32_t num, const std::string& name);
~VCA();
@ -47,7 +52,15 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en
int set_state (XMLNode const&, int version);
bool soloed () const;
void push_solo_upstream (int32_t) {}
void push_solo_isolate_upstream (int32_t) {}
bool can_solo() const { return true; }
bool is_safe () const { return false; }
bool muted () const;
bool can_be_muted_by_others () const { return true; }
bool muted_by_others_soloing() const { return false; }
bool muted_by_others() const { return false; }
static std::string default_name_template ();
static int next_vca_number ();
@ -58,16 +71,16 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en
static void set_next_vca_number (uint32_t);
virtual boost::shared_ptr<GainControl> gain_control() const { return _gain_control; }
virtual boost::shared_ptr<AutomationControl> solo_control() const { return _solo_control; }
virtual boost::shared_ptr<AutomationControl> mute_control() const { return _mute_control; }
virtual boost::shared_ptr<SoloControl> solo_control() const { return _solo_control; }
virtual boost::shared_ptr<MuteControl> mute_control() const { return _mute_control; }
/* null Stripable API, because VCAs don't have any of this */
virtual boost::shared_ptr<PeakMeter> peak_meter() { return boost::shared_ptr<PeakMeter>(); }
virtual boost::shared_ptr<const PeakMeter> peak_meter() const { return boost::shared_ptr<PeakMeter>(); }
virtual boost::shared_ptr<AutomationControl> phase_control() const { return boost::shared_ptr<AutomationControl>(); }
virtual boost::shared_ptr<AutomationControl> trim_control() const { return boost::shared_ptr<AutomationControl>(); }
virtual boost::shared_ptr<AutomationControl> monitoring_control() const { return boost::shared_ptr<AutomationControl>(); }
virtual boost::shared_ptr<PeakMeter> peak_meter() { return boost::shared_ptr<PeakMeter>(); }
virtual boost::shared_ptr<const PeakMeter> peak_meter() const { return boost::shared_ptr<PeakMeter>(); }
virtual boost::shared_ptr<PhaseControl> phase_control() const { return boost::shared_ptr<PhaseControl>(); }
virtual boost::shared_ptr<GainControl> trim_control() const { return boost::shared_ptr<GainControl>(); }
virtual boost::shared_ptr<MonitorControl> monitoring_control() const { return boost::shared_ptr<MonitorControl>(); }
virtual boost::shared_ptr<AutomationControl> recenable_control() const { return boost::shared_ptr<AutomationControl>(); }
virtual boost::shared_ptr<AutomationControl> pan_azimuth_control() const { return boost::shared_ptr<AutomationControl>(); }
virtual boost::shared_ptr<AutomationControl> pan_elevation_control() const { return boost::shared_ptr<AutomationControl>(); }
@ -96,36 +109,12 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en
virtual boost::shared_ptr<AutomationControl> master_send_enable_controllable () const { return boost::shared_ptr<AutomationControl>(); }
private:
class VCASoloControllable : public AutomationControl {
public:
VCASoloControllable (std::string const & name, boost::shared_ptr<VCA> vca);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value () const;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
boost::weak_ptr<VCA> _vca;
};
class VCAMuteControllable : public AutomationControl {
public:
VCAMuteControllable (std::string const & name, boost::shared_ptr<VCA> vca);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value () const;
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
boost::weak_ptr<VCA> _vca;
};
friend class VCASoloControllable;
friend class VCAMuteControllable;
uint32_t _number;
boost::shared_ptr<GainControl> _gain_control;
boost::shared_ptr<VCASoloControllable> _solo_control;
boost::shared_ptr<VCAMuteControllable> _mute_control;
boost::shared_ptr<SoloControl> _solo_control;
boost::shared_ptr<MuteControl> _mute_control;
bool _solo_requested;
bool _mute_requested;

View File

@ -33,6 +33,7 @@
#include "ardour/buffer_set.h"
#include "ardour/delivery.h"
#include "ardour/meter.h"
#include "ardour/monitor_control.h"
#include "ardour/playlist_factory.h"
#include "ardour/processor.h"
#include "ardour/profile.h"
@ -349,7 +350,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
if (!_active) {
silence (nframes);
if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) {
_meter->reset();
}
return 0;
@ -391,7 +392,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
fill_buffers_with_input (bufs, _input, nframes);
if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) {
_meter->run (bufs, start_frame, end_frame, nframes, true);
}
@ -629,8 +630,9 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
/* reset stuff that has already been accounted for in the freeze process */
set_gain (GAIN_COEFF_UNITY, Controllable::NoGroup);
_amp->gain_control()->set_automation_state (Off);
gain_control()->set_value (GAIN_COEFF_UNITY, Controllable::NoGroup);
gain_control()->set_automation_state (Off);
/* XXX need to use _main_outs _panner->set_automation_state (Off); */
_freeze_record.state = Frozen;

View File

@ -20,14 +20,17 @@
#include <math.h>
#include <iostream>
#include "ardour/automation_control.h"
#include "ardour/automation_watch.h"
#include "ardour/event_type_map.h"
#include "ardour/session.h"
#include "pbd/memento_command.h"
#include "pbd/stacktrace.h"
#include "ardour/audioengine.h"
#include "ardour/automation_control.h"
#include "ardour/automation_watch.h"
#include "ardour/control_group.h"
#include "ardour/event_type_map.h"
#include "ardour/session.h"
#include "i18n.h"
#ifdef COMPILER_MSVC
@ -69,42 +72,38 @@ AutomationControl::writable() const
return true;
}
double
AutomationControl::get_masters_value_locked () const
{
gain_t v = 1.0;
for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
/* get current master value, scale by our current ratio with that master */
v *= mr->second.master()->get_value () * mr->second.ratio();
}
return min (_desc.upper, v);
}
double
AutomationControl::get_value_locked() const
{
/* read or write masters lock must be held */
if (_masters.empty()) {
return Control::get_double (false, _session.transport_frame());
}
return get_masters_value_locked ();
}
/** Get the current effective `user' value based on automation state */
double
AutomationControl::get_value() const
{
bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
return Control::get_double (from_list, _session.transport_frame());
}
if (!from_list) {
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return get_value_locked ();
void
AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
if (!writable()) {
return;
}
/* enforce strict double/boolean value mapping */
if (_desc.toggled) {
if (val != 0.0) {
val = 1.0;
}
}
if (check_rt (val, gcd)) {
/* change has been queued to take place in an RT context */
return;
}
if (_group && _group->use_me (gcd)) {
_group->set_group_value (shared_from_this(), val);
} else {
return Control::get_double (from_list, _session.transport_frame());
actually_set_value (val, gcd);
}
}
@ -113,12 +112,15 @@ AutomationControl::get_value() const
* @param value `user' value
*/
void
AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
{
bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
bool to_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_write();
Control::set_double (value, _session.transport_frame(), to_list);
AutomationType at = (AutomationType) _parameter.type();
std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value << " @ " << this << std::endl;
Changed (true, gcd);
}
@ -263,147 +265,25 @@ AutomationControl::interface_to_internal (double val) const
return val;
}
void
AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg)
{
double current_value;
double new_value;
std::pair<Masters::iterator,bool> res;
{
Glib::Threads::RWLock::WriterLock lm (master_lock);
current_value = get_value_locked ();
/* ratio will be recomputed below */
res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
if (res.second) {
recompute_masters_ratios (current_value);
/* note that we bind @param m as a weak_ptr<AutomationControl>, thus
avoiding holding a reference to the control in the binding
itself.
*/
m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
/* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
and we no longer hear about changes to the AutomationControl.
Note that we fix the "from_self" argument that will
be given to our own Changed signal to "false",
because the change came from the master.
*/
m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2));
}
new_value = get_value_locked ();
}
if (res.second) {
/* this will notify everyone that we're now slaved to the master */
MasterStatusChange (); /* EMIT SIGNAL */
}
if (new_value != current_value) {
/* force a call to to ::master_changed() to carry the
* consequences that would occur if the master assumed
* its current value WHILE we were slaved.
*/
master_changed (false, Controllable::NoGroup);
/* effective value changed by master */
Changed (false, Controllable::NoGroup);
}
}
void
AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
{
/* our value has (likely) changed, but not because we were
* modified. Just the master.
*/
Changed (false, gcd); /* EMIT SIGNAL */
}
void
AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
{
boost::shared_ptr<AutomationControl> m = wm.lock();
if (m) {
remove_master (m);
}
}
void
AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
{
double current_value;
double new_value;
Masters::size_type erased = 0;
{
Glib::Threads::RWLock::WriterLock lm (master_lock);
current_value = get_value_locked ();
erased = _masters.erase (m->id());
if (erased) {
recompute_masters_ratios (current_value);
}
new_value = get_value_locked ();
}
if (erased) {
MasterStatusChange (); /* EMIT SIGNAL */
}
if (new_value != current_value) {
Changed (false, Controllable::NoGroup);
}
}
void
AutomationControl::clear_masters ()
{
double current_value;
double new_value;
bool had_masters = false;
{
Glib::Threads::RWLock::WriterLock lm (master_lock);
current_value = get_value_locked ();
if (!_masters.empty()) {
had_masters = true;
}
_masters.clear ();
new_value = get_value_locked ();
}
if (had_masters) {
MasterStatusChange (); /* EMIT SIGNAL */
}
if (new_value != current_value) {
Changed (false, Controllable::NoGroup);
if (_group) {
_group->remove_control (shared_from_this());
}
_group = cg;
}
bool
AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd)
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return _masters.find (m->id()) != _masters.end();
}
if ((flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
/* queue change in RT context */
std::cerr << "::set_value (" << val << ", " << enum_2_string (gcd) << ") called for " << enum_2_string ((AutomationType) _parameter.type()) << ", queueing in RT context\n";
_session.set_control (shared_from_this(), val, gcd);
return true;
}
bool
AutomationControl::slaved () const
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return !_masters.empty();
return false;
}

View File

@ -140,21 +140,30 @@ setup_enum_writer ()
#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e)
#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
REGISTER_ENUM (NullAutomation);
REGISTER_ENUM (GainAutomation);
REGISTER_ENUM (TrimAutomation);
REGISTER_ENUM (PanAzimuthAutomation);
REGISTER_ENUM (PanElevationAutomation);
REGISTER_ENUM (PanWidthAutomation);
REGISTER_ENUM (PanFrontBackAutomation);
REGISTER_ENUM (PanLFEAutomation);
REGISTER_ENUM (PluginAutomation);
REGISTER_ENUM (PluginPropertyAutomation);
REGISTER_ENUM (SoloAutomation);
REGISTER_ENUM (SoloIsolateAutomation);
REGISTER_ENUM (SoloSafeAutomation);
REGISTER_ENUM (MuteAutomation);
REGISTER_ENUM (MidiCCAutomation);
REGISTER_ENUM (MidiPgmChangeAutomation);
REGISTER_ENUM (MidiPitchBenderAutomation);
REGISTER_ENUM (MidiChannelPressureAutomation);
REGISTER_ENUM (MidiSystemExclusiveAutomation);
REGISTER_ENUM (FadeInAutomation);
REGISTER_ENUM (FadeOutAutomation);
REGISTER_ENUM (EnvelopeAutomation);
REGISTER_ENUM (SoloIsolateAutomation);
REGISTER_ENUM (SoloSafeAutomation);
REGISTER_ENUM (RecEnableAutomation);
REGISTER_ENUM (RecSafeAutomation);
REGISTER_ENUM (TrimAutomation);
REGISTER_ENUM (PhaseAutomation);
REGISTER_ENUM (MonitoringAutomation);
REGISTER_ENUM (EQGain);

View File

@ -33,9 +33,9 @@ using namespace ARDOUR;
using namespace std;
GainControl::GainControl (Session& session, const Evoral::Parameter &param, boost::shared_ptr<AutomationList> al)
: AutomationControl (session, param, ParameterDescriptor(param),
al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)),
param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) {
: SlavableAutomationControl (session, param, ParameterDescriptor(param),
al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)),
param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) {
alist()->reset_default (1.0);
@ -44,22 +44,7 @@ GainControl::GainControl (Session& session, const Evoral::Parameter &param, boos
}
void
GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (val, group_override);
}
}
void
GainControl::set_value_unchecked (double val)
{
/* used only automation playback */
_set_value (val, Controllable::NoGroup);
}
void
GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
GainControl::actually_set_value (double val, Controllable::GroupControlDisposition group_override)
{
val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
@ -75,7 +60,7 @@ GainControl::_set_value (double val, Controllable::GroupControlDisposition group
be retrieved by AutomationControl::get_value ()
*/
AutomationControl::set_value (val, group_override);
AutomationControl::actually_set_value (val, group_override);
_session.set_dirty ();
}
@ -119,6 +104,23 @@ GainControl::get_user_string () const
return std::string(theBuf);
}
void
GainControl::inc_gain (gain_t factor)
{
/* To be used ONLY when doing group-relative gain adjustment, from
* ControlGroup::set_group_values().
*/
const float desired_gain = user_double();
if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
// really?! what's the idea here?
actually_set_value (0.000001f + (0.000001f * factor), Controllable::ForGroup);
} else {
actually_set_value (desired_gain + (desired_gain * factor), Controllable::ForGroup);
}
}
void
GainControl::recompute_masters_ratios (double val)
{

View File

@ -583,10 +583,10 @@ LuaBindings::common (lua_State* L)
.addCast<MidiTrack> ("to_midi_track")
.addFunction ("set_name", &Track::set_name)
.addFunction ("can_record", &Track::can_record)
.addFunction ("record_enabled", &Track::record_enabled)
.addFunction ("record_safe", &Track::record_safe)
.addFunction ("set_record_enabled", &Track::set_record_enabled)
.addFunction ("set_record_safe", &Track::set_record_safe)
//.addFunction ("record_enabled", &Track::record_enabled)
//.addFunction ("record_safe", &Track::record_safe)
//.addFunction ("set_record_enabled", &Track::set_record_enabled)
//.addFunction ("set_record_safe", &Track::set_record_safe)
.addFunction ("bounceable", &Track::bounceable)
.addFunction ("bounce", &Track::bounce)
.addFunction ("bounce_range", &Track::bounce_range)

View File

@ -44,6 +44,7 @@
#include "ardour/midi_port.h"
#include "ardour/midi_region.h"
#include "ardour/midi_track.h"
#include "ardour/monitor_control.h"
#include "ardour/parameter_types.h"
#include "ardour/port.h"
#include "ardour/processor.h"
@ -103,24 +104,24 @@ MidiTrack::create_diskstream ()
}
void
MidiTrack::set_record_enabled (bool yn, Controllable::GroupControlDisposition group_override)
bool
MidiTrack::can_be_record_safe ()
{
if (_step_editing) {
return;
return false;
}
Track::set_record_enabled (yn, group_override);
return Track::can_be_record_safe ();
}
void
MidiTrack::set_record_safe (bool yn, Controllable::GroupControlDisposition group_override)
bool
MidiTrack::can_be_record_enabled ()
{
if (_step_editing) { /* REQUIRES REVIEW */
return;
if (_step_editing) {
return false;
}
Track::set_record_safe (yn, group_override);
return Track::can_be_record_enabled ();
}
void
@ -372,7 +373,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
if (!_active) {
silence (nframes);
if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) {
_meter->reset();
}
return 0;
@ -412,7 +413,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
/* filter captured data before meter sees it */
_capture_filter.filter (bufs);
if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) {
_meter->run (bufs, start_frame, end_frame, nframes, true);
}
@ -726,22 +727,7 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st
}
void
MidiTrack::MidiControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (val, group_override);
}
}
void
MidiTrack::MidiControl::set_value_unchecked (double val)
{
/* used only by automation playback */
_set_value (val, Controllable::NoGroup);
}
void
MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
const Evoral::Parameter &parameter = _list ? _list->parameter() : Control::parameter();
const Evoral::ParameterDescriptor &desc = EventTypeMap::instance().descriptor(parameter);
@ -798,7 +784,7 @@ MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlD
_route->write_immediate_event(size, ev);
}
AutomationControl::set_value(val, group_override);
AutomationControl::actually_set_value(val, group_override);
}
void
@ -958,35 +944,27 @@ MidiTrack::act_on_mute ()
}
void
MidiTrack::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd)
MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition gcd)
{
if (use_group (gcd, &RouteGroup::is_monitoring)) {
_route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup);
return;
Track::monitoring_changed (self, gcd);
/* monitoring state changed, so flush out any on notes at the
* port level.
*/
PortSet& ports (_output->ports());
for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) {
boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p);
if (mp) {
mp->require_resolve ();
}
}
if (mc != _monitoring) {
boost::shared_ptr<MidiDiskstream> md (midi_diskstream());
Track::set_monitoring (mc, gcd);
/* monitoring state changed, so flush out any on notes at the
* port level.
*/
PortSet& ports (_output->ports());
for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) {
boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p);
if (mp) {
mp->require_resolve ();
}
}
boost::shared_ptr<MidiDiskstream> md (midi_diskstream());
if (md) {
md->reset_tracker ();
}
if (md) {
md->reset_tracker ();
}
}

View File

@ -31,6 +31,8 @@
using namespace ARDOUR;
using namespace std;
const string MuteMaster::xml_node_name (X_("MuteMaster"));
const MuteMaster::MutePoint MuteMaster::AllPoints = MuteMaster::MutePoint(
PreFader|PostFader|Listen|Main);
@ -155,7 +157,7 @@ MuteMaster::set_state (const XMLNode& node, int /*version*/)
XMLNode&
MuteMaster::get_state()
{
XMLNode* node = new XMLNode (X_("MuteMaster"));
XMLNode* node = new XMLNode (xml_node_name);
node->add_property ("mute-point", enum_2_string (_mute_point));
node->add_property ("muted", (_muted_by_self ? X_("yes") : X_("no")));
return *node;

View File

@ -35,27 +35,13 @@ PanControllable::lower () const
}
void
PanControllable::set_value (double v, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (v, group_override);
}
}
void
PanControllable::set_value_unchecked (double v)
{
/* used only automation playback */
_set_value (v, Controllable::NoGroup);
}
void
PanControllable::_set_value (double v, Controllable::GroupControlDisposition group_override)
PanControllable::actually_set_value (double v, Controllable::GroupControlDisposition group_override)
{
boost::shared_ptr<Panner> p = owner->panner();
if (!p) {
/* no panner: just do it */
AutomationControl::set_value (v, group_override);
AutomationControl::actually_set_value (v, group_override);
return;
}
@ -76,7 +62,7 @@ PanControllable::_set_value (double v, Controllable::GroupControlDisposition gro
}
if (can_set) {
AutomationControl::set_value (v, group_override);
AutomationControl::actually_set_value (v, group_override);
}
}

View File

@ -75,6 +75,7 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter)
normal = 0.0f;
break;
case RecEnableAutomation:
case RecSafeAutomation:
lower = 0.0;
upper = 1.0;
toggled = true;

View File

@ -2614,22 +2614,9 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p,
}
/** @param val `user' value */
void
PluginInsert::PluginControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (user_val, group_override);
}
}
void
PluginInsert::PluginControl::set_value_unchecked (double user_val)
{
/* used only by automation playback */
_set_value (user_val, Controllable::NoGroup);
}
void
PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
PluginInsert::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
{
/* FIXME: probably should be taking out some lock here.. */
@ -2642,13 +2629,13 @@ PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::Gro
iasp->set_parameter (_list->parameter().id(), user_val);
}
AutomationControl::set_value (user_val, group_override);
AutomationControl::actually_set_value (user_val, group_override);
}
void
PluginInsert::PluginControl::catch_up_with_external_value (double user_val)
{
AutomationControl::set_value (user_val, Controllable::NoGroup);
AutomationControl::actually_set_value (user_val, Controllable::NoGroup);
}
XMLNode&
@ -2700,15 +2687,7 @@ PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert*
}
void
PluginInsert::PluginPropertyControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition /* group_override*/)
{
if (writable()) {
set_value_unchecked (user_val);
}
}
void
PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val)
PluginInsert::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd)
{
/* Old numeric set_value(), coerce to appropriate datatype if possible.
This is lossy, but better than nothing until Ardour's automation system
@ -2724,7 +2703,8 @@ PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val)
}
_value = value;
AutomationControl::set_value (user_val, Controllable::NoGroup);
AutomationControl::actually_set_value (user_val, gcd);
}
XMLNode&

View File

@ -58,6 +58,7 @@
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/parameter_descriptor.h"
#include "ardour/phase_control.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
@ -67,6 +68,8 @@
#include "ardour/route_group.h"
#include "ardour/send.h"
#include "ardour/session.h"
#include "ardour/solo_control.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/unknown_processor.h"
#include "ardour/utils.h"
#include "ardour/vca.h"
@ -84,6 +87,7 @@ PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Rout
/** Base class for all routable/mixable objects (tracks and busses) */
Route::Route (Session& sess, string name, Flag flg, DataType default_type)
: Stripable (sess, name)
, Muteable (sess, name)
, Automatable (sess)
, GraphNode (sess._process_graph)
, _active (true)
@ -99,18 +103,11 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
, _meter_point (MeterPostFader)
, _pending_meter_point (MeterPostFader)
, _meter_type (MeterPeak)
, _self_solo (false)
, _soloed_by_others_upstream (0)
, _soloed_by_others_downstream (0)
, _solo_isolated (false)
, _solo_isolated_by_upstream (0)
, _denormal_protection (false)
, _recordable (true)
, _silent (false)
, _declickable (false)
, _mute_master (new MuteMaster (sess, name))
, _have_internal_generator (false)
, _solo_safe (false)
, _default_type (default_type)
, _order_key (0)
, _has_order_key (false)
@ -141,21 +138,30 @@ Route::init ()
/* add standard controls */
_solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
_mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
_phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ()));
_gain_control.reset (new GainControl (_session, GainAutomation));
add_control (_gain_control);
_solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ()));
_solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ()));
_trim_control.reset (new GainControl (_session, TrimAutomation));
add_control (_trim_control);
_solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
_mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
_phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle));
add_control (_solo_control);
_solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2));
_mute_control.reset (new MuteControl (_session, X_("mute"), *this));
_mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
add_control (_mute_control);
_phase_control.reset (new PhaseControl (_session, X_("phase")));
add_control (_phase_control);
_solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this));
add_control (_solo_isolate_control);
_solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe")));
add_control (_solo_safe_control);
/* panning */
if (!(_flags & Route::MonitorOut)) {
@ -177,9 +183,6 @@ Route::init ()
* it should be the first processor to be added on every route.
*/
_gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
add_control (_gain_control);
_amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
add_processor (_amp, PostFader);
@ -196,9 +199,6 @@ Route::init ()
/* and input trim */
_trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
add_control (_trim_control);
_trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
_trim->set_display_to_user (false);
@ -398,83 +398,11 @@ Route::ensure_track_or_route_name(string name, Session &session)
return newname;
}
void
Route::inc_gain (gain_t factor)
{
/* To be used ONLY when doing group-relative gain adjustment, from
* ::set_gain()
*/
float desired_gain = _gain_control->user_double();
if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
// really?! what's the idea here?
_gain_control->route_set_value (0.000001f + (0.000001f * factor));
} else {
_gain_control->route_set_value (desired_gain + (desired_gain * factor));
}
}
void
Route::set_gain (gain_t val, Controllable::GroupControlDisposition gcd)
{
if (use_group (gcd, &RouteGroup::is_gain)) {
if (_route_group->is_relative()) {
gain_t usable_gain = _gain_control->get_value();
if (usable_gain < 0.000001f) {
usable_gain = 0.000001f;
}
gain_t delta = val;
if (delta < 0.000001f) {
delta = 0.000001f;
}
delta -= usable_gain;
if (delta == 0.0f)
return;
gain_t factor = delta / usable_gain;
if (factor > 0.0f) {
factor = _route_group->get_max_factor(factor);
if (factor == 0.0f) {
_amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */
return;
}
} else {
factor = _route_group->get_min_factor(factor);
if (factor == 0.0f) {
_amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */
return;
}
}
_route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
} else {
_route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
}
return;
}
if (val == _gain_control->get_value()) {
return;
}
_gain_control->route_set_value (val);
}
void
Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
{
// TODO route group, see set_gain()
_trim_control->route_set_value (val);
// _trim_control->route_set_value (val);
}
void
@ -551,7 +479,7 @@ Route::process_output_buffers (BufferSet& bufs,
DENORMAL CONTROL/PHASE INVERT
----------------------------------------------------------------------------------------- */
if (_phase_invert.any ()) {
if (!_phase_control->none()) {
int chn = 0;
@ -560,7 +488,7 @@ Route::process_output_buffers (BufferSet& bufs,
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
if (_phase_invert[chn]) {
if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
sp[nx] += 1.0e-27f;
@ -577,7 +505,7 @@ Route::process_output_buffers (BufferSet& bufs,
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
if (_phase_invert[chn]) {
if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
}
@ -808,31 +736,24 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
}
void
Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
Route::set_listen (bool yn)
{
if (_solo_safe) {
return;
if (yn) {
_monitor_send->activate ();
} else {
_monitor_send->deactivate ();
}
}
if (use_group (group_override, &RouteGroup::is_solo)) {
_route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
return;
}
void
Route::solo_control_changed (bool, Controllable::GroupControlDisposition)
{
/* nothing to do if we're not using AFL/PFL. But if we are, we need
to alter the active state of the monitor send.
*/
if (_monitor_send) {
if (yn != _monitor_send->active()) {
if (yn) {
_monitor_send->activate ();
_mute_master->set_soloed_by_self (true);
} else {
_monitor_send->deactivate ();
_mute_master->set_soloed_by_self (false);
}
_mute_master->set_soloed_by_others (false);
/* first argument won't matter because solo <=> listen right now */
_solo_control->Changed (false, group_override); /* EMIT SIGNAL */
}
if (Config->get_solo_control_is_listen_control ()) {
set_listen (_solo_control->self_soloed());
}
}
@ -847,244 +768,14 @@ Route::listening_via_monitor () const
}
void
Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition gcd)
Route::push_solo_isolate_upstream (int32_t delta)
{
if (_solo_safe != yn) {
_solo_safe = yn;
_solo_safe_control->Changed (true, gcd); /* EMIT SIGNAL */
}
}
bool
Route::solo_safe() const
{
return _solo_safe;
}
void
Route::clear_all_solo_state ()
{
// ideally this function will never do anything, it only exists to forestall Murphy
bool emit_changed = false;
#ifndef NDEBUG
// these are really debug messages, but of possible interest.
if (_self_solo) {
PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
}
if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
}
#endif
if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
// if self-soled, set_solo() will do signal emission
emit_changed = true;
}
_soloed_by_others_upstream = 0;
_soloed_by_others_downstream = 0;
{
PBD::Unwinder<bool> uw (_solo_safe, false);
set_solo (false, Controllable::NoGroup);
}
if (emit_changed) {
set_mute_master_solo ();
_solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
}
}
void
Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
name(), yn, enum_2_string(group_override), self_soloed()));
if (_solo_safe) {
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
return;
}
if (is_master() || is_monitor() || is_auditioner()) {
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name()));
return;
}
if (use_group (group_override, &RouteGroup::is_solo)) {
_route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
return;
}
if (self_soloed() != yn) {
set_self_solo (yn);
_solo_control->Changed (true, group_override); /* EMIT SIGNAL */
}
assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
}
void
Route::set_self_solo (bool yn)
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
_self_solo = yn;
set_mute_master_solo ();
}
void
Route::mod_solo_by_others_upstream (int32_t delta)
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
uint32_t old_sbu = _soloed_by_others_upstream;
if (delta < 0) {
if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
_soloed_by_others_upstream += delta;
} else {
_soloed_by_others_upstream = 0;
}
} else {
_soloed_by_others_upstream += delta;
}
DEBUG_TRACE (DEBUG::Solo, string_compose (
"%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
name(), delta, _soloed_by_others_upstream, old_sbu,
_soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
/* push the inverse solo change to everything that feeds us.
This is important for solo-within-group. When we solo 1 track out of N that
feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
tracks that feed it. This will silence them if they were audible because
of a bus solo, but the newly soloed track will still be audible (because
it is self-soloed).
but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
not in reverse.
*/
if ((_self_solo || _soloed_by_others_downstream) &&
((old_sbu == 0 && _soloed_by_others_upstream > 0) ||
(old_sbu > 0 && _soloed_by_others_upstream == 0))) {
if (delta > 0 || !Config->get_exclusive_solo()) {
DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
if (i->sends_only) {
continue;
}
boost::shared_ptr<Route> sr = i->r.lock();
if (sr) {
sr->mod_solo_by_others_downstream (-delta);
}
}
}
}
set_mute_master_solo ();
cerr << name() << " SC->Changed (false, UseGroup)\n";
_solo_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
void
Route::mod_solo_by_others_downstream (int32_t delta)
{
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
if (delta < 0) {
if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
_soloed_by_others_downstream += delta;
} else {
_soloed_by_others_downstream = 0;
}
} else {
_soloed_by_others_downstream += delta;
}
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
set_mute_master_solo ();
_solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
}
void
Route::set_mute_master_solo ()
{
_mute_master->set_soloed_by_self (self_soloed());
_mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
}
void
Route::mod_solo_isolated_by_upstream (bool yn)
{
bool old = solo_isolated ();
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
if (!yn) {
if (_solo_isolated_by_upstream >= 1) {
_solo_isolated_by_upstream--;
} else {
_solo_isolated_by_upstream = 0;
}
} else {
_solo_isolated_by_upstream++;
}
if (solo_isolated() != old) {
/* solo isolated status changed */
_mute_master->set_solo_ignore (solo_isolated());
_solo_isolate_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
void
Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
{
if (is_master() || is_monitor() || is_auditioner()) {
return;
}
if (use_group (group_override, &RouteGroup::is_solo)) {
_route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
return;
}
bool changed = false;
if (yn) {
if (_solo_isolated == false) {
_mute_master->set_solo_ignore (true);
changed = true;
}
_solo_isolated = true;
} else {
if (_solo_isolated == true) {
_solo_isolated = false;
_mute_master->set_solo_ignore (false);
changed = true;
}
}
if (!changed) {
return;
}
/* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
boost::shared_ptr<RouteList> routes = _session.get_routes ();
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
if ((*i).get() == this || !(*i)->can_solo()) {
continue;
}
@ -1092,87 +783,26 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o
bool does_feed = feeds (*i, &sends_only);
if (does_feed && !sends_only) {
(*i)->mod_solo_isolated_by_upstream (yn);
(*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta);
}
}
/* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
_solo_isolate_control->Changed (true, group_override); /* EMIT SIGNAL */
}
bool
Route::solo_isolated () const
{
return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0);
}
void
Route::set_mute_points (MuteMaster::MutePoint mp)
Route::push_solo_upstream (int delta)
{
_mute_master->set_mute_points (mp);
mute_points_changed (); /* EMIT SIGNAL */
if (_mute_master->muted_by_self()) {
_mute_control->Changed (true, Controllable::UseGroup); /* EMIT SIGNAL */
DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
if (i->sends_only) {
continue;
}
boost::shared_ptr<Route> sr (i->r.lock());
if (sr) {
sr->solo_control()->mod_solo_by_others_downstream (-delta);
}
}
}
void
Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
{
if (use_group (group_override, &RouteGroup::is_mute)) {
_route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
return;
}
if (muted() != yn) {
_mute_master->set_muted_by_self (yn);
/* allow any derived classes to respond to the mute change
before anybody else knows about it.
*/
act_on_mute ();
/* tell everyone else */
_mute_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
bool
Route::muted () const
{
return _mute_master->muted_by_self();
}
bool
Route::muted_by_others_soloing () const
{
// This method is only used by route_ui for display state.
// The real thing is MuteMaster::muted_by_others_at()
//master is never muted by others
if (is_master())
return false;
//now check to see if something is soloed (and I am not)
//see also MuteMaster::mute_gain_at()
return _session.soloing() && !soloed() && !solo_isolated();
}
bool
Route::muted_by_others () const
{
// This method is only used by route_ui for display state.
// The real thing is MuteMaster::muted_by_others_at()
//master is never muted by others
if (is_master())
return false;
//now check to see if something is soloed (and I am not)
//see also MuteMaster::mute_gain_at()
return _mute_master->muted_by_others() || (_session.soloing() && !soloed() && !solo_isolated());
}
#if 0
static void
dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
@ -2743,8 +2373,6 @@ Route::state(bool full_state)
node->add_property("active", _active?"yes":"no");
string p;
boost::to_string (_phase_invert, p);
node->add_property("phase-invert", p);
node->add_property("denormal-protection", _denormal_protection?"yes":"no");
node->add_property("meter-point", enum_2_string (_meter_point));
@ -2756,18 +2384,9 @@ Route::state(bool full_state)
snprintf (buf, sizeof (buf), "%d", _order_key);
node->add_property ("order-key", buf);
node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
node->add_property ("soloed-by-upstream", buf);
snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
node->add_property ("soloed-by-downstream", buf);
node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
node->add_child_nocopy (_input->state (full_state));
node->add_child_nocopy (_output->state (full_state));
node->add_child_nocopy (_solo_control->get_state ());
node->add_child_nocopy (_mute_control->get_state ());
node->add_child_nocopy (_mute_master->get_state ());
if (full_state) {
@ -2866,7 +2485,7 @@ Route::set_state (const XMLNode& node, int version)
_strict_io = string_is_affirmative (prop->value());
}
if (is_master() || is_monitor() || is_auditioner()) {
if (!can_solo()) {
_mute_master->set_solo_ignore (true);
}
@ -2933,31 +2552,10 @@ Route::set_state (const XMLNode& node, int version)
// this looks up the internal instrument in processors
reset_instrument_info();
if ((prop = node.property ("self-solo")) != 0) {
set_self_solo (string_is_affirmative (prop->value()));
}
if ((prop = node.property ("soloed-by-upstream")) != 0) {
_soloed_by_others_upstream = 0; // needed for mod_.... () to work
mod_solo_by_others_upstream (atoi (prop->value()));
}
if ((prop = node.property ("soloed-by-downstream")) != 0) {
_soloed_by_others_downstream = 0; // needed for mod_.... () to work
mod_solo_by_others_downstream (atoi (prop->value()));
}
if ((prop = node.property ("solo-isolated")) != 0) {
set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
}
if ((prop = node.property ("solo-safe")) != 0) {
set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
}
if ((prop = node.property (X_("phase-invert"))) != 0) {
set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
}
_solo_control->set_state (node, version);
_solo_safe_control->set_state (node, version);
_solo_isolate_control->set_state (node, version);
_mute_control->set_state (node, version);
if ((prop = node.property (X_("denormal-protection"))) != 0) {
set_denormal_protection (string_is_affirmative (prop->value()));
@ -3046,7 +2644,7 @@ Route::set_state (const XMLNode& node, int version)
set_remote_control_id_internal (x);
}
} else if (child->name() == X_("MuteMaster")) {
} else if (child->name() == MuteMaster::xml_node_name) {
_mute_master->set_state (*child, version);
} else if (child->name() == Automatable::xml_node_name) {
@ -3089,26 +2687,10 @@ Route::set_state_2X (const XMLNode& node, int version)
_mute_master->set_solo_ignore (true);
}
if ((prop = node.property (X_("phase-invert"))) != 0) {
boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
if (string_is_affirmative (prop->value ())) {
p.set ();
}
set_phase_invert (p);
}
if ((prop = node.property (X_("denormal-protection"))) != 0) {
set_denormal_protection (string_is_affirmative (prop->value()));
}
if ((prop = node.property (X_("soloed"))) != 0) {
bool yn = string_is_affirmative (prop->value());
/* XXX force reset of solo status */
set_solo (yn);
}
if ((prop = node.property (X_("muted"))) != 0) {
bool first = true;
@ -3870,11 +3452,11 @@ Route::input_change_handler (IOChange change, void * /*src*/)
contains ConfigurationChanged
*/
configure_processors (0);
_phase_invert.resize (_input->n_ports().n_audio ());
_phase_control->resize (_input->n_ports().n_audio ());
io_changed (); /* EMIT SIGNAL */
}
if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) {
int sbou = 0;
int ibou = 0;
boost::shared_ptr<RouteList> routes = _session.get_routes ();
@ -3889,38 +3471,36 @@ Route::input_change_handler (IOChange change, void * /*src*/)
if ((*i)->soloed()) {
++sbou;
}
if ((*i)->solo_isolated()) {
if ((*i)->solo_isolate_control()->solo_isolated()) {
++ibou;
}
}
}
}
int delta = sbou - _soloed_by_others_upstream;
int idelta = ibou - _solo_isolated_by_upstream;
int delta = sbou - _solo_control->soloed_by_others_upstream();
int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream();
if (idelta < -1) {
PBD::warning << string_compose (
_("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
_name, ibou, _solo_isolated_by_upstream, idelta)
_name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta)
<< endmsg;
}
if (_soloed_by_others_upstream) {
if (_solo_control->soloed_by_others_upstream()) {
// ignore new connections (they're not propagated)
if (delta <= 0) {
mod_solo_by_others_upstream (delta);
_solo_control->mod_solo_by_others_upstream (delta);
}
}
if (_solo_isolated_by_upstream) {
if (_solo_isolate_control->solo_isolated_by_upstream()) {
// solo-isolate currently only propagates downstream
if (idelta < 0) {
mod_solo_isolated_by_upstream (false);
_solo_isolate_control->mod_solo_isolated_by_upstream (1);
}
// TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
// but idelta can't be smaller than -1, can it?
//_solo_isolated_by_upstream = ibou;
}
@ -3933,11 +3513,11 @@ Route::input_change_handler (IOChange change, void * /*src*/)
bool sends_only;
bool does_feed = feeds (*i, &sends_only);
if (delta <= 0 && does_feed && !sends_only) {
(*i)->mod_solo_by_others_upstream (delta);
(*i)->solo_control()->mod_solo_by_others_upstream (delta);
}
if (idelta < 0 && does_feed && !sends_only) {
(*i)->mod_solo_isolated_by_upstream (false);
(*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1);
}
}
}
@ -3963,7 +3543,7 @@ Route::output_change_handler (IOChange change, void * /*src*/)
io_changed (); /* EMIT SIGNAL */
}
if (_soloed_by_others_downstream) {
if (_solo_control->soloed_by_others_downstream()) {
int sbod = 0;
/* checking all all downstream routes for
* explicit of implict solo is a rather drastic measure,
@ -3986,20 +3566,20 @@ Route::output_change_handler (IOChange change, void * /*src*/)
}
}
}
int delta = sbod - _soloed_by_others_downstream;
int delta = sbod - _solo_control->soloed_by_others_downstream();
if (delta <= 0) {
// do not allow new connections to change implicit solo (no propagation)
mod_solo_by_others_downstream (delta);
_solo_control->mod_solo_by_others_downstream (delta);
// Session::route_solo_changed() does not propagate indirect solo-changes
// propagate upstream to tracks
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
if ((*i).get() == this || !can_solo()) {
continue;
}
bool sends_only;
bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
if (delta != 0 && does_feed && !sends_only) {
(*i)->mod_solo_by_others_downstream (delta);
(*i)->solo_control()->mod_solo_by_others_downstream (delta);
}
}
@ -4639,41 +4219,6 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
return boost::shared_ptr<Send>();
}
/** @param c Audio channel index.
* @param yn true to invert phase, otherwise false.
*/
void
Route::set_phase_invert (uint32_t c, bool yn)
{
if (_phase_invert[c] != yn) {
_phase_invert[c] = yn;
_phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */
_session.set_dirty ();
}
}
void
Route::set_phase_invert (boost::dynamic_bitset<> p)
{
if (_phase_invert != p) {
_phase_invert = p;
_phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */
_session.set_dirty ();
}
}
bool
Route::phase_invert (uint32_t c) const
{
return _phase_invert[c];
}
boost::dynamic_bitset<>
Route::phase_invert () const
{
return _phase_invert;
}
void
Route::set_denormal_protection (bool yn)
{
@ -4735,20 +4280,16 @@ Route::gain_control() const
return _gain_control;
}
boost::shared_ptr<AutomationControl>
boost::shared_ptr<GainControl>
Route::trim_control() const
{
return _trim_control;
}
boost::shared_ptr<AutomationControl>
boost::shared_ptr<PhaseControl>
Route::phase_control() const
{
if (phase_invert().size()) {
return _phase_control;
} else {
return boost::shared_ptr<PhaseControllable>();
}
return _phase_control;
}
boost::shared_ptr<AutomationControl>
@ -4841,12 +4382,6 @@ Route::has_io_processor_named (const string& name)
return false;
}
MuteMaster::MutePoint
Route::mute_points () const
{
return _mute_master->mute_points ();
}
void
Route::set_processor_positions ()
{
@ -5903,7 +5438,7 @@ Route::vca_assign (boost::shared_ptr<VCA> vca)
{
_gain_control->add_master (vca->gain_control());
_solo_control->add_master (vca->solo_control());
_mute_control->add_master (vca->mute_control());
// _mute_control->add_master (vca->mute_control());
}
void
@ -5913,10 +5448,48 @@ Route::vca_unassign (boost::shared_ptr<VCA> vca)
/* unassign from all */
_gain_control->clear_masters ();
_solo_control->clear_masters ();
_mute_control->clear_masters ();
//_mute_control->clear_masters ();
} else {
_gain_control->remove_master (vca->gain_control());
_solo_control->remove_master (vca->solo_control());
_mute_control->remove_master (vca->mute_control());
//_mute_control->remove_master (vca->mute_control());
}
}
bool
Route::muted_by_others_soloing () const
{
// This method is only used by route_ui for display state.
// The DSP version is MuteMaster::muted_by_others_at()
if (!can_be_muted_by_others ()) {
return false;
}
return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
}
bool
Route::muted_by_others () const
{
// This method is only used by route_ui for display state.
// The DSP version is MuteMaster::muted_by_others_at()
if (!can_be_muted_by_others()) {
return false;
}
return _mute_master->muted_by_others();
}
void
Route::clear_all_solo_state ()
{
double v = _solo_safe_control->get_value ();
_solo_control->clear_all_solo_state ();
if (v != 0.0) {
_solo_safe_control->set_value (v, Controllable::NoGroup);
}
}

View File

@ -1,431 +0,0 @@
/*
Copyright (C) 2000 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef WAF_BUILD
#include "libardour-config.h"
#endif
#include "ardour/automation_control.h"
#include "ardour/parameter_descriptor.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
void
Route::set_control (AutomationType type, double val, PBD::Controllable::GroupControlDisposition group_override)
{
boost::shared_ptr<RouteList> rl;
switch (type) {
case GainAutomation:
/* route must mediate group control */
set_gain (val, group_override);
break;
case TrimAutomation:
/* route must mediate group control */
set_trim (val, group_override);
break;
case RecEnableAutomation:
/* session must mediate group control */
rl.reset (new RouteList);
rl->push_back (shared_from_this());
_session.set_record_enabled (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
break;
case SoloAutomation:
/* session must mediate group control */
rl.reset (new RouteList);
rl->push_back (shared_from_this());
if (Config->get_solo_control_is_listen_control()) {
_session.set_listen (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
} else {
_session.set_solo (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
}
break;
case MuteAutomation:
/* session must mediate group control */
rl.reset (new RouteList);
rl->push_back (shared_from_this());
_session.set_mute (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
return;
break;
default:
/* Not a route automation control */
fatal << string_compose (_("programming error: %1%2\n"), X_("illegal type of route automation control passed to Route::set_control(): "), enum_2_string(type)) << endmsg;
/*NOTREACHED*/
return;
}
}
Route::RouteAutomationControl::RouteAutomationControl (const std::string& name,
AutomationType atype,
boost::shared_ptr<AutomationList> alist,
boost::shared_ptr<Route> r)
: AutomationControl (r->session(), Evoral::Parameter (atype),
ParameterDescriptor (Evoral::Parameter (atype)),
alist, name)
, _route (r)
{
}
double
Route::BooleanRouteAutomationControl::get_masters_value_locked () const
{
/* masters (read/write) lock must be held */
/* if any master is enabled (val > 0.0) then we consider the master
value to be 1.0
*/
for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
if (mr->second.master()->get_value()) {
return 1.0;
}
}
return 0.0;
}
Route::GainControllable::GainControllable (Session& s, AutomationType atype, boost::shared_ptr<Route> r)
: GainControl (s, Evoral::Parameter(atype))
, _route (r)
{
}
Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
: BooleanRouteAutomationControl (name, SoloAutomation, boost::shared_ptr<AutomationList>(), r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
void
Route::SoloControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
bool master_soloed;
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
master_soloed = (bool) get_masters_value_locked ();
}
/* Master is considered equivalent to an upstream solo control, not
* direct control over self-soloed.
*/
r->mod_solo_by_others_upstream (master_soloed ? 1 : -1);
AutomationControl::master_changed (false, gcd);
}
void
Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (val, group_override);
}
}
void
Route::SoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
r->set_control (SoloAutomation, val, group_override);
}
void
Route::SoloControllable::set_value_unchecked (double val)
{
/* Used only by automation playback */
_set_value (val, Controllable::NoGroup);
}
double
Route::SoloControllable::get_value () const
{
if (slaved()) {
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
// Playing back automation, get the value from the list
return AutomationControl::get_value();
}
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return 0;
}
if (Config->get_solo_control_is_listen_control()) {
return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
} else {
return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
}
}
Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
: BooleanRouteAutomationControl (name, MuteAutomation, boost::shared_ptr<AutomationList>(), r)
, _route (r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
void
Route::MuteControllable::set_superficial_value(bool muted)
{
/* Note we can not use AutomationControl::set_value here since it will emit
Changed(), but the value will not be correct to the observer. */
const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
const double where = _session.audible_frame ();
if (to_list) {
/* Note that we really need this:
* if (as == Touch && _list->in_new_write_pass ()) {
* alist->start_write_pass (_session.audible_frame ());
* }
* here in the case of the user calling from a GUI or whatever.
* Without the ability to distinguish between user and
* automation-initiated changes, we lose the "touch mute"
* behaviour we have in AutomationController::toggled ().
*/
_list->set_in_write_pass (true, false, where);
}
Control::set_double (muted, where, to_list);
}
void
Route::MuteControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
{
bool master_muted;
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
master_muted = (bool) get_masters_value_locked ();
}
boost::shared_ptr<Route> r (_route.lock());
if (r) {
r->mute_master()->mod_muted_by_others (master_muted ? 1 : -1);
}
AutomationControl::master_changed (false, gcd);
}
void
Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (val, group_override);
}
}
void
Route::MuteControllable::set_value_unchecked (double val)
{
/* used only automation playback */
_set_value (val, Controllable::NoGroup);
}
void
Route::MuteControllable::_set_value (double val, Controllable::GroupControlDisposition group_override)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
// Set superficial/automation value to drive controller (and possibly record)
const bool bval = ((val >= 0.5) ? true : false);
set_superficial_value (bval);
// Playing back automation, set route mute directly
r->set_mute (bval, Controllable::NoGroup);
} else {
r->set_control (MuteAutomation, val, group_override);
}
}
double
Route::MuteControllable::get_value () const
{
if (slaved()) {
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return get_masters_value_locked () ? 1.0 : 0.0;
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
// Playing back automation, get the value from the list
return AutomationControl::get_value();
}
// Not playing back automation, get the actual route mute value
boost::shared_ptr<Route> r = _route.lock ();
return (r && r->muted()) ? 1.0 : 0.0;
}
Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)
: BooleanRouteAutomationControl (name, PhaseAutomation, boost::shared_ptr<AutomationList>(), r)
, _current_phase (0)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
void
Route::PhaseControllable::set_value (double v, PBD::Controllable::GroupControlDisposition /* group_override */)
{
boost::shared_ptr<Route> r = _route.lock ();
if (r->phase_invert().size()) {
if (v == 0 || (v < 1 && v > 0.9) ) {
r->set_phase_invert (_current_phase, false);
} else {
r->set_phase_invert (_current_phase, true);
}
}
}
double
Route::PhaseControllable::get_value () const
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return 0.0;
}
return (double) r->phase_invert (_current_phase);
}
void
Route::PhaseControllable::set_channel (uint32_t c)
{
_current_phase = c;
}
uint32_t
Route::PhaseControllable::channel () const
{
return _current_phase;
}
Route::SoloIsolateControllable::SoloIsolateControllable (std::string name, boost::shared_ptr<Route> r)
: BooleanRouteAutomationControl (name, SoloIsolateAutomation, boost::shared_ptr<AutomationList>(), r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloIsolateAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
double
Route::SoloIsolateControllable::get_value () const
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return 0.0; /* "false" */
}
return r->solo_isolated() ? 1.0 : 0.0;
}
void
Route::SoloIsolateControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
_set_value (val, gcd);
}
void
Route::SoloIsolateControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
/* no group semantics yet */
r->set_solo_isolated (val >= 0.5 ? true : false);
}
Route::SoloSafeControllable::SoloSafeControllable (std::string name, boost::shared_ptr<Route> r)
: BooleanRouteAutomationControl (name, SoloSafeAutomation, boost::shared_ptr<AutomationList>(), r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloSafeAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
void
Route::SoloSafeControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
_set_value (val, gcd);
}
void
Route::SoloSafeControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
/* no group semantics yet */
r->set_solo_safe (val >= 0.5 ? true : false);
}
double
Route::SoloSafeControllable::get_value () const
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return 0.0; /* "false" */
}
return r->solo_safe() ? 1.0 : 0.0;
}

View File

@ -20,6 +20,7 @@
#include "ardour/route.h"
#include "ardour/route_graph.h"
#include "ardour/track.h"
#include "i18n.h"
@ -195,21 +196,39 @@ struct RouteRecEnabledComparator
{
bool operator () (GraphVertex r1, GraphVertex r2) const
{
if (r1->record_enabled()) {
if (r2->record_enabled()) {
/* both rec-enabled, just use signal order */
boost::shared_ptr<Track> t1 (boost::dynamic_pointer_cast<Track>(r1));
boost::shared_ptr<Track> t2 (boost::dynamic_pointer_cast<Track>(r2));
if (!t1) {
if (!t2) {
/* makes no difference which is first, use signal order */
return r1->order_key () < r2->order_key ();
} else {
/* r1 rec-enabled, r2 not rec-enabled, run r2 early */
/* r1 is not a track, r2 is, run it early */
return false;
}
}
if (!t2) {
/* we already tested !t1, so just use signal order */
return r1->order_key () < r2->order_key ();
}
if (t1->rec_enable_control()->get_value()) {
if (t2->rec_enable_control()->get_value()) {
/* both rec-enabled, just use signal order */
return t1->order_key () < t2->order_key ();
} else {
/* t1 rec-enabled, t2 not rec-enabled, run t2 early */
return false;
}
} else {
if (r2->record_enabled()) {
/* r2 rec-enabled, r1 not rec-enabled, run r1 early */
if (t2->rec_enable_control()->get_value()) {
/* t2 rec-enabled, t1 not rec-enabled, run t1 early */
return true;
} else {
/* neither rec-enabled, use signal order */
return r1->order_key () < r2->order_key ();
return t1->order_key () < t2->order_key ();
}
}
}

View File

@ -28,6 +28,7 @@
#include "ardour/amp.h"
#include "ardour/audio_track.h"
#include "ardour/monitor_control.h"
#include "ardour/route.h"
#include "ardour/route_group.h"
#include "ardour/session.h"
@ -57,21 +58,21 @@ void
RouteGroup::make_property_quarks ()
{
Properties::relative.property_id = g_quark_from_static_string (X_("relative"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id));
Properties::active.property_id = g_quark_from_static_string (X_("active"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id));
Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
Properties::gain.property_id = g_quark_from_static_string (X_("gain"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id));
Properties::mute.property_id = g_quark_from_static_string (X_("mute"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id));
Properties::solo.property_id = g_quark_from_static_string (X_("solo"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id));
Properties::recenable.property_id = g_quark_from_static_string (X_("recenable"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id));
Properties::select.property_id = g_quark_from_static_string (X_("select"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id));
Properties::route_active.property_id = g_quark_from_static_string (X_("route-active"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for route-active = %1\n", Properties::route_active.property_id));
Properties::color.property_id = g_quark_from_static_string (X_("color"));
@ -96,6 +97,11 @@ RouteGroup::RouteGroup (Session& s, const string &n)
: SessionObject (s, n)
, routes (new RouteList)
, ROUTE_GROUP_DEFAULT_PROPERTIES
, _solo_group (new ControlGroup (SoloAutomation))
, _mute_group (new ControlGroup (MuteAutomation))
, _rec_enable_group (new ControlGroup (RecEnableAutomation))
, _gain_group (new ControlGroup (GainAutomation))
, _monitoring_group (new ControlGroup (MonitoringAutomation))
{
_xml_node_name = X_("RouteGroup");
@ -114,6 +120,12 @@ RouteGroup::RouteGroup (Session& s, const string &n)
RouteGroup::~RouteGroup ()
{
_solo_group->clear ();
_mute_group->clear ();
_gain_group->clear ();
_rec_enable_group->clear ();
_monitoring_group->clear ();
for (RouteList::iterator i = routes->begin(); i != routes->end();) {
RouteList::iterator tmp = i;
++tmp;
@ -140,6 +152,15 @@ RouteGroup::add (boost::shared_ptr<Route> r)
routes->push_back (r);
_solo_group->add_control (r->solo_control());
_mute_group->add_control (r->mute_control());
_gain_group->add_control (r->gain_control());
boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r);
if (trk) {
_rec_enable_group->add_control (trk->rec_enable_control());
_monitoring_group->add_control (trk->monitoring_control());
}
r->set_route_group (this);
r->DropReferences.connect_same_thread (*this, boost::bind (&RouteGroup::remove_when_going_away, this, boost::weak_ptr<Route> (r)));
@ -165,6 +186,14 @@ RouteGroup::remove (boost::shared_ptr<Route> r)
if ((i = find (routes->begin(), routes->end(), r)) != routes->end()) {
r->set_route_group (0);
_solo_group->remove_control (r->solo_control());
_mute_group->remove_control (r->mute_control());
_gain_group->remove_control (r->gain_control());
boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r);
if (trk) {
_rec_enable_group->remove_control (trk->rec_enable_control());
_monitoring_group->remove_control (trk->monitoring_control());
}
routes->erase (i);
_session.set_dirty ();
RouteRemoved (this, boost::weak_ptr<Route> (r)); /* EMIT SIGNAL */
@ -175,49 +204,6 @@ RouteGroup::remove (boost::shared_ptr<Route> r)
}
gain_t
RouteGroup::get_min_factor (gain_t factor)
{
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
gain_t const g = (*i)->gain_control()->get_value();
if ((g + g * factor) >= 0.0f) {
continue;
}
if (g <= 0.0000003f) {
return 0.0f;
}
factor = 0.0000003f / g - 1.0f;
}
return factor;
}
gain_t
RouteGroup::get_max_factor (gain_t factor)
{
for (RouteList::iterator i = routes->begin(); i != routes->end(); i++) {
gain_t const g = (*i)->gain_control()->get_value();
// if the current factor woulnd't raise this route above maximum
if ((g + g * factor) <= 1.99526231f) {
continue;
}
// if route gain is already at peak, return 0.0f factor
if (g >= 1.99526231f) {
return 0.0f;
}
// factor is calculated so that it would raise current route to max
factor = 1.99526231f / g - 1.0f;
}
return factor;
}
XMLNode&
RouteGroup::get_state ()
{
@ -269,6 +255,8 @@ RouteGroup::set_state (const XMLNode& node, int version)
}
}
push_to_groups ();
return 0;
}
@ -293,6 +281,8 @@ RouteGroup::set_state_2X (const XMLNode& node, int /*version*/)
_color = false;
}
push_to_groups ();
return 0;
}
@ -303,6 +293,8 @@ RouteGroup::set_gain (bool yn)
return;
}
_gain = yn;
_gain_group->set_active (yn);
send_change (PropertyChange (Properties::gain));
}
@ -313,6 +305,7 @@ RouteGroup::set_mute (bool yn)
return;
}
_mute = yn;
_mute_group->set_active (yn);
send_change (PropertyChange (Properties::mute));
}
@ -323,6 +316,7 @@ RouteGroup::set_solo (bool yn)
return;
}
_solo = yn;
_solo_group->set_active (yn);
send_change (PropertyChange (Properties::solo));
}
@ -333,6 +327,7 @@ RouteGroup::set_recenable (bool yn)
return;
}
_recenable = yn;
_rec_enable_group->set_active (yn);
send_change (PropertyChange (Properties::recenable));
}
@ -384,6 +379,8 @@ RouteGroup::set_monitoring (bool yn)
}
_monitoring = yn;
_monitoring_group->set_active (yn);
send_change (PropertyChange (Properties::monitoring));
_session.set_dirty ();
@ -531,3 +528,19 @@ RouteGroup::enabled_property (PBD::PropertyID prop)
return dynamic_cast<const PropertyTemplate<bool>* > (i->second)->val ();
}
void
RouteGroup::post_set (PBD::PropertyChange const &)
{
push_to_groups ();
}
void
RouteGroup::push_to_groups ()
{
_gain_group->set_active (_gain);
_solo_group->set_active (_solo);
_mute_group->set_active (_mute);
_rec_enable_group->set_active (_recenable);
_monitoring_group->set_active (_monitoring);
}

View File

@ -98,6 +98,7 @@
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
#include "ardour/smf_source.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
#include "ardour/tempo.h"
@ -1506,7 +1507,7 @@ Session::set_track_monitor_input_status (bool yn)
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
if (tr && tr->record_enabled ()) {
if (tr && tr->rec_enable_control()->get_value()) {
//cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
tr->request_input_monitoring (yn);
}
@ -1941,15 +1942,9 @@ void
Session::set_all_tracks_record_enabled (bool enable )
{
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->set_record_enabled (enable, Controllable::NoGroup);
}
}
set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), enable, Controllable::NoGroup);
}
void
Session::disable_record (bool rt_context, bool force)
{
@ -2981,7 +2976,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
// 0 for Stereo Out mode
// 0 Multi Out mode
if (Config->get_output_auto_connect() & AutoConnectMaster) {
track->set_gain (dB_to_coefficient (0), Controllable::NoGroup);
track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup);
}
}
@ -3430,7 +3425,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_route_record_state, this));
tr->rec_enable_control()->Changed.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) {
@ -3580,7 +3575,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
continue;
}
(*iter)->set_solo (false, Controllable::NoGroup);
(*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
rs->remove (*iter);
@ -3721,7 +3716,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr
continue;
}
if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
@ -3734,7 +3729,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr
*/
continue;
}
(*i)->set_listen (false, Controllable::NoGroup);
(*i)->solo_control()->set_value (0.0, Controllable::NoGroup);
}
}
@ -3759,7 +3754,7 @@ Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
bool send_changed = false;
if (route->solo_isolated()) {
if (route->solo_isolate_control()->solo_isolated()) {
if (_solo_isolated_cnt == 0) {
send_changed = true;
}
@ -3838,7 +3833,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
continue;
}
if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
@ -3852,7 +3847,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
continue;
}
(*i)->set_solo (false, group_override);
(*i)->solo_control()->set_value (0.0, group_override);
}
}
@ -3871,7 +3866,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
continue;
}
if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
/* route does not get solo propagated to it */
continue;
}
@ -3893,7 +3888,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name()));
if (!via_sends_only) {
if (!route->soloed_by_others_upstream()) {
(*i)->mod_solo_by_others_downstream (delta);
(*i)->solo_control()->mod_solo_by_others_downstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n");
}
@ -3922,7 +3917,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
if (!via_sends_only) {
//NB. Triggers Invert Push, which handles soloed by downstream
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
(*i)->mod_solo_by_others_upstream (delta);
(*i)->solo_control()->mod_solo_by_others_upstream (delta);
} else {
DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name()));
}
@ -3969,7 +3964,7 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) {
if ((*i)->can_solo() && (*i)->self_soloed()) {
something_soloed = true;
}
@ -3978,11 +3973,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
listeners++;
something_listening = true;
} else {
(*i)->set_listen (false, Controllable::NoGroup);
(*i)->set_listen (false);
}
}
if ((*i)->solo_isolated()) {
if ((*i)->solo_isolate_control()->solo_isolated()) {
isolated++;
}
}
@ -6078,7 +6073,7 @@ Session::update_route_record_state ()
while (i != rl->end ()) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr && tr->record_enabled ()) {
if (tr && tr->rec_enable_control()->get_value()) {
break;
}
@ -6095,7 +6090,7 @@ Session::update_route_record_state ()
for (i = rl->begin(); i != rl->end (); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr && !tr->record_enabled ()) {
if (tr && !tr->rec_enable_control()->get_value()) {
break;
}
}
@ -6124,12 +6119,7 @@ void
Session::solo_control_mode_changed ()
{
/* cancel all solo or all listen when solo control mode changes */
if (soloing()) {
set_solo (get_routes(), false);
} else if (listening()) {
set_listen (get_routes(), false);
}
clear_all_solo_state (get_routes());
}
/** Called when a property of one of our route groups changes */

View File

@ -350,7 +350,7 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
if ((at = dynamic_cast<AudioTrack*>((*i).get())) != 0) {
if (trk == at->remote_control_id()) {
at->set_record_enabled (enabled, Controllable::UseGroup);
at->rec_enable_control()->set_value (enabled, Controllable::UseGroup);
break;
}
}

View File

@ -21,8 +21,9 @@
#include "pbd/error.h"
#include "pbd/compose.h"
#include "ardour/session.h"
#include "ardour/monitor_control.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/track.h"
#include "i18n.h"
@ -33,26 +34,26 @@ using namespace ARDOUR;
using namespace Glib;
void
Session::set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc,
SessionEvent::RTeventCallback after,
Controllable::GroupControlDisposition group_override)
Session::set_controls (boost::shared_ptr<ControlList> cl, double val, Controllable::GroupControlDisposition gcd)
{
queue_event (get_rt_event (rl, mc, after, group_override, &Session::rt_set_monitoring));
std::cerr << "Session::set_controls called on " << cl->size() << " controls, group = " << enum_2_string (gcd) << std::endl;
queue_event (get_rt_event (cl, val, gcd));
}
void
Session::rt_set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc, Controllable::GroupControlDisposition group_override)
Session::set_control (boost::shared_ptr<AutomationControl> ac, double val, Controllable::GroupControlDisposition gcd)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
if (t) {
t->set_monitoring (mc, group_override);
}
}
}
boost::shared_ptr<ControlList> cl (new ControlList);
cl->push_back (ac);
set_controls (cl, val, gcd);
}
set_dirty();
void
Session::rt_set_controls (boost::shared_ptr<ControlList> cl, double val, Controllable::GroupControlDisposition gcd)
{
for (ControlList::iterator c = cl->begin(); c != cl->end(); ++c) {
(*c)->set_value (val, gcd);
}
}
void
@ -73,224 +74,6 @@ Session::rt_clear_all_solo_state (boost::shared_ptr<RouteList> rl, bool /* yn */
set_dirty();
}
void
Session::set_solo (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after,
Controllable::GroupControlDisposition group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo));
}
void
Session::rt_set_solo (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner()) {
(*i)->set_solo (yn, group_override);
}
}
set_dirty();
/* XXX boost::shared_ptr<RouteList> goes out of scope here and is likley free()ed in RT context
* because boost's shared_ptr does reference counting and free/delete in the dtor.
* (this also applies to other rt_ methods here)
*/
}
void
Session::set_implicit_solo (boost::shared_ptr<RouteList> rl, int delta, bool upstream, SessionEvent::RTeventCallback after,
Controllable::GroupControlDisposition group_override)
{
queue_event (get_rt_event (rl, delta, upstream, after, group_override, &Session::rt_set_implicit_solo));
}
void
Session::rt_set_implicit_solo (boost::shared_ptr<RouteList> rl, int delta, bool upstream, PBD::Controllable::GroupControlDisposition)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner()) {
if (upstream) {
std::cerr << "Changing " << (*i)->name() << " upstream by " << delta << std::endl;
(*i)->mod_solo_by_others_upstream (delta);
} else {
std::cerr << "Changing " << (*i)->name() << " downstream by " << delta << std::endl;
(*i)->mod_solo_by_others_downstream (delta);
}
}
}
set_dirty();
/* XXX boost::shared_ptr<RouteList> goes out of scope here and is likley free()ed in RT context
* because boost's shared_ptr does reference counting and free/delete in the dtor.
* (this also applies to other rt_ methods here)
*/
}
void
Session::set_just_one_solo (boost::shared_ptr<Route> r, bool yn, SessionEvent::RTeventCallback after)
{
/* its a bit silly to have to do this, but it keeps the API for this public method sane (we're
only going to solo one route) and keeps our ability to use get_rt_event() for the internal
private method.
*/
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (r);
queue_event (get_rt_event (rl, yn, after, Controllable::NoGroup, &Session::rt_set_just_one_solo));
}
void
Session::rt_set_just_one_solo (boost::shared_ptr<RouteList> just_one, bool yn, Controllable::GroupControlDisposition /*ignored*/)
{
boost::shared_ptr<RouteList> rl = routes.reader ();
boost::shared_ptr<Route> r = just_one->front();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner() && r != *i) {
(*i)->set_solo (!yn, Controllable::NoGroup);
}
}
r->set_solo (yn, Controllable::NoGroup);
set_dirty();
}
void
Session::set_listen (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_listen));
}
void
Session::rt_set_listen (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner()) {
(*i)->set_listen (yn, group_override);
}
}
set_dirty();
}
void
Session::set_mute (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override)
{
/* Set superficial value of mute controls for automation. */
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Route::MuteControllable> mc = boost::dynamic_pointer_cast<Route::MuteControllable> ((*i)->mute_control());
mc->set_superficial_value(yn);
}
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_mute));
}
void
Session::rt_set_mute (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_monitor() && !(*i)->is_auditioner()) {
(*i)->set_mute (yn, group_override);
}
}
set_dirty();
}
void
Session::set_solo_isolated (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override)
{
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo_isolated));
}
void
Session::rt_set_solo_isolated (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
(*i)->set_solo_isolated (yn, group_override);
}
}
set_dirty();
}
void
Session::set_record_enabled (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override)
{
if (!writable()) {
return;
}
/* do the non-RT part of rec-enabling first - the RT part will be done
* on the next process cycle. This does mean that theoretically we are
* doing things provisionally on the assumption that the rec-enable
* change will work, but this had better be a solid assumption for
* other reasons.
*/
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if ((*i)->is_auditioner() || (*i)->record_safe ()) {
continue;
}
boost::shared_ptr<Track> t;
if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) {
t->prep_record_enabled (yn, group_override);
}
}
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_enabled));
}
void
Session::rt_set_record_enabled (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if ((*i)->is_auditioner() || (*i)->record_safe ()) {
continue;
}
boost::shared_ptr<Track> t;
if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) {
t->set_record_enabled (yn, group_override);
}
}
set_dirty ();
}
void
Session::set_record_safe (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override)
{
set_record_enabled (rl, false, after, group_override);
queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_safe));
}
void
Session::rt_set_record_safe (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override)
{
for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
if ((*i)->is_auditioner ()) { // REQUIRES REVIEW Can audiotioner be in Record Safe mode?
continue;
}
boost::shared_ptr<Track> t;
if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) {
t->set_record_safe (yn, group_override);
}
}
set_dirty ();
}
void
Session::process_rtop (SessionEvent* ev)
{

View File

@ -1265,7 +1265,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr && tr->record_enabled ()) {
if (tr && tr->rec_enable_control()->get_value()) {
// tell it we've looped, so it can deal with the record state
tr->transport_looped (_transport_frame);
}

View File

@ -23,9 +23,11 @@
#include "ardour/diskstream.h"
#include "ardour/io_processor.h"
#include "ardour/meter.h"
#include "ardour/monitor_control.h"
#include "ardour/playlist.h"
#include "ardour/port.h"
#include "ardour/processor.h"
#include "ardour/record_enable_control.h"
#include "ardour/route_group_specialized.h"
#include "ardour/session.h"
#include "ardour/session_playlists.h"
@ -42,7 +44,6 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data
: Route (sess, name, flag, default_type)
, _saved_meter_point (_meter_point)
, _mode (mode)
, _monitoring (MonitorAuto)
{
_freeze_record.state = NoFreeze;
_declickable = true;
@ -62,17 +63,24 @@ Track::init ()
boost::shared_ptr<Route> rp (shared_from_this());
boost::shared_ptr<Track> rt = boost::dynamic_pointer_cast<Track> (rp);
_rec_enable_control = boost::shared_ptr<RecEnableControl> (new RecEnableControl(rt));
_rec_enable_control->set_flags (Controllable::Toggle);
_monitoring_control.reset (new MonitoringControllable (X_("monitoring"), rt));
/* don't add rec_enable_control to controls because we don't want it to
* appear as an automatable parameter
*/
_record_enable_control.reset (new RecordEnableControl (_session, X_("recenable"), *this));
_record_enable_control->set_flags (Controllable::Toggle);
_monitoring_control.reset (new MonitorControl (_session, X_("monitoring"), *this));
_record_safe_control.reset (new AutomationControl (_session, RecSafeAutomation, ParameterDescriptor (RecSafeAutomation),
boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (RecSafeAutomation))),
X_("recsafe")));
track_number_changed.connect_same_thread (*this, boost::bind (&Track::resync_track_name, this));
_session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1));
return 0;
_monitoring_control->Changed.connect_same_thread (*this, boost::bind (&Track::monitoring_changed, this, _1, _2));
_record_safe_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_safe_changed, this, _1, _2));
_record_enable_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_enable_changed, this, _1, _2));
return 0;
}
void
@ -97,9 +105,7 @@ XMLNode&
Track::state (bool full)
{
XMLNode& root (Route::state (full));
root.add_property (X_("monitoring"), enum_2_string (_monitoring));
root.add_property (X_("saved-meter-point"), enum_2_string (_saved_meter_point));
root.add_child_nocopy (_rec_enable_control->get_state());
root.add_child_nocopy (_diskstream->get_state ());
return root;
@ -137,19 +143,13 @@ Track::set_state (const XMLNode& node, int version)
XMLProperty const * prop;
if (child->name() == Controllable::xml_node_name && (prop = child->property ("name")) != 0) {
if (prop->value() == X_("recenable")) {
_rec_enable_control->set_state (*child, version);
_record_enable_control->set_state (*child, version);
}
}
}
XMLProperty const * prop;
if ((prop = node.property (X_("monitoring"))) != 0) {
_monitoring = MonitorChoice (string_2_enum (prop->value(), _monitoring));
} else {
_monitoring = MonitorAuto;
}
if ((prop = node.property (X_("saved-meter-point"))) != 0) {
_saved_meter_point = MeterPoint (string_2_enum (prop->value(), _saved_meter_point));
} else {
@ -178,62 +178,6 @@ Track::freeze_state() const
return _freeze_record.state;
}
Track::RecEnableControl::RecEnableControl (boost::shared_ptr<Track> t)
: AutomationControl (t->session(),
RecEnableAutomation,
ParameterDescriptor(Evoral::Parameter(RecEnableAutomation)),
boost::shared_ptr<AutomationList>(),
X_("recenable"))
, track (t)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(RecEnableAutomation)));
set_list (gl);
}
void
Track::RecEnableControl::set_value (double val, Controllable::GroupControlDisposition group_override)
{
if (writable()) {
_set_value (val, group_override);
}
}
void
Track::RecEnableControl::set_value_unchecked (double val)
{
if (writable()) {
_set_value (val, Controllable::NoGroup);
}
}
void
Track::RecEnableControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
{
boost::shared_ptr<Track> t = track.lock ();
if (!t) {
return;
}
t->set_record_enabled (val >= 0.5 ? true : false, group_override);
}
double
Track::RecEnableControl::get_value () const
{
boost::shared_ptr<Track> t = track.lock ();
if (!t) {
return 0;
}
return (t->record_enabled() ? 1.0 : 0.0);
}
bool
Track::record_enabled () const
{
return _diskstream && _diskstream->record_enabled ();
}
bool
Track::can_record()
{
@ -246,24 +190,15 @@ Track::can_record()
return will_record;
}
void
Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group_override)
int
Track::prep_record_enabled (bool yn)
{
if (yn && record_safe ()) {
return;
if (yn && _record_safe_control->get_value()) {
return -1;
}
if (!_session.writable()) {
return;
}
if (_freeze_record.state == Frozen) {
return;
}
if (use_group (group_override, &RouteGroup::is_recenable)) {
_route_group->apply (&Track::prep_record_enabled, yn, Controllable::NoGroup);
return;
if (!can_be_record_enabled()) {
return -1;
}
/* keep track of the meter point as it was before we rec-enabled */
@ -288,31 +223,20 @@ Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group
set_meter_point (_saved_meter_point);
}
}
return 0;
}
void
Track::set_record_enabled (bool yn, Controllable::GroupControlDisposition gcd)
Track::record_enable_changed (bool, Controllable::GroupControlDisposition)
{
if (_diskstream->record_safe ()) {
return;
}
_diskstream->set_record_enabled (_record_enable_control->get_value());
}
if (!_session.writable()) {
return;
}
if (_freeze_record.state == Frozen) {
return;
}
if (use_group (gcd, &RouteGroup::is_recenable)) {
_route_group->apply (&Track::set_record_enabled, yn, Controllable::NoGroup);
return;
}
_diskstream->set_record_enabled (yn);
_rec_enable_control->Changed (true, gcd);
void
Track::record_safe_changed (bool, Controllable::GroupControlDisposition)
{
_diskstream->set_record_safe (_record_safe_control->get_value());
}
bool
@ -336,12 +260,7 @@ Track::set_record_safe (bool yn, Controllable::GroupControlDisposition group_ove
return;
}
if (use_group (group_override, &RouteGroup::is_recenable)) {
_route_group->apply (&Track::set_record_safe, yn, Controllable::NoGroup);
return;
}
_diskstream->set_record_safe (yn);
_rec_safe_control->set_value (yn, group_override);
}
void
@ -371,7 +290,7 @@ Track::set_name (const string& str)
{
bool ret;
if (record_enabled() && _session.actively_recording()) {
if (_record_enable_control->get_value() && _session.actively_recording()) {
/* this messes things up if done while recording */
return false;
}
@ -452,7 +371,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
if (!_active) {
silence (nframes);
if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) {
_meter->reset();
}
return 0;
@ -611,8 +530,6 @@ Track::set_diskstream (boost::shared_ptr<Diskstream> ds)
ds->PlaylistChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_playlist_changed, this));
diskstream_playlist_changed ();
ds->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_enable_changed, this));
ds->RecordSafeChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_safe_changed, this));
ds->SpeedChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_speed_changed, this));
ds->AlignmentStyleChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_alignment_style_changed, this));
}
@ -623,18 +540,6 @@ Track::diskstream_playlist_changed ()
PlaylistChanged (); /* EMIT SIGNAL */
}
void
Track::diskstream_record_enable_changed ()
{
RecordEnableChanged (); /* EMIT SIGNAL */
}
void
Track::diskstream_record_safe_changed ()
{
RecordSafeChanged (); /* EMIT SIGNAL */
}
void
Track::diskstream_speed_changed ()
{
@ -1014,12 +919,13 @@ MonitorState
Track::monitoring_state () const
{
/* Explicit requests */
MonitorChoice m (_monitoring_control->monitoring_choice());
if (_monitoring & MonitorInput) {
if (m & MonitorInput) {
return MonitoringInput;
}
if (_monitoring & MonitorDisk) {
if (m & MonitorDisk) {
return MonitoringDisk;
}
@ -1086,7 +992,7 @@ Track::maybe_declick (BufferSet& bufs, framecnt_t nframes, int declick)
ditto if we are monitoring inputs.
*/
if (_have_internal_generator || monitoring_choice() == MonitorInput) {
if (_have_internal_generator || (_monitoring_control->monitoring_choice() == MonitorInput)) {
return;
}
@ -1136,22 +1042,10 @@ Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame)
}
void
Track::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd)
Track::monitoring_changed (bool, Controllable::GroupControlDisposition)
{
if (use_group (gcd, &RouteGroup::is_monitoring)) {
_route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup);
return;
}
if (mc != _monitoring) {
_monitoring = mc;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->monitoring_changed ();
}
MonitoringChanged (); /* EMIT SIGNAL */
_monitoring_control->Changed (true, gcd);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->monitoring_changed ();
}
}
@ -1161,64 +1055,10 @@ Track::metering_state () const
bool rv;
if (_session.transport_rolling ()) {
// audio_track.cc || midi_track.cc roll() runs meter IFF:
rv = _meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled());
rv = _meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled());
} else {
// track no_roll() always metering if
rv = _meter_point == MeterInput;
}
return rv ? MeteringInput : MeteringRoute;
}
Track::MonitoringControllable::MonitoringControllable (std::string name, boost::shared_ptr<Track> r)
: RouteAutomationControl (name, MonitoringAutomation, boost::shared_ptr<AutomationList>(), r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MonitoringAutomation)));
gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
void
Track::MonitoringControllable::set_value (double val, Controllable::GroupControlDisposition gcd)
{
_set_value (val, gcd);
}
void
Track::MonitoringControllable::_set_value (double val, Controllable::GroupControlDisposition gcd)
{
boost::shared_ptr<Route> r = _route.lock();
if (!r) {
return;
}
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (r);
if (!t) {
return;
}
int mc = (int) val;
if (mc < MonitorAuto || mc > MonitorDisk) {
return;
}
/* no group effect at present */
t->set_monitoring ((MonitorChoice) mc, gcd);
}
double
Track::MonitoringControllable::get_value () const
{
boost::shared_ptr<Route> r = _route.lock();
if (!r) {
return 0.0;
}
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (r);
if (!t) {
return 0.0;
}
return t->monitoring_choice();
}

View File

@ -754,3 +754,4 @@ ARDOUR::slider_position_to_gain_with_max (double g, double max_gain)
extern "C" {
void c_stacktrace() { stacktrace (cerr); }
}

View File

@ -63,6 +63,7 @@ VCA::get_next_vca_number ()
VCA::VCA (Session& s, uint32_t num, const string& name)
: Stripable (s, name)
, Muteable (s, name)
, Automatable (s)
, _number (num)
, _gain_control (new GainControl (s, Evoral::Parameter (GainAutomation), boost::shared_ptr<AutomationList> ()))
@ -74,8 +75,8 @@ VCA::VCA (Session& s, uint32_t num, const string& name)
int
VCA::init ()
{
_solo_control.reset (new VCASoloControllable (X_("solo"), shared_from_this()));
_mute_control.reset (new VCAMuteControllable (X_("mute"), shared_from_this()));
_solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
_mute_control.reset (new MuteControl (_session, X_("mute"), *this));
add_control (_gain_control);
add_control (_solo_control);
@ -159,98 +160,3 @@ VCA::muted () const
{
return _mute_requested;
}
VCA::VCASoloControllable::VCASoloControllable (string const & name, boost::shared_ptr<VCA> vca)
: AutomationControl (vca->session(), Evoral::Parameter (SoloAutomation), ParameterDescriptor (Evoral::Parameter (SoloAutomation)),
boost::shared_ptr<AutomationList>(), name)
, _vca (vca)
{
}
void
VCA::VCASoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
if (writable()) {
_set_value (val, gcd);
}
}
void
VCA::VCASoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
boost::shared_ptr<VCA> vca = _vca.lock();
if (!vca) {
return;
}
vca->set_solo (val >= 0.5);
AutomationControl::set_value (val, gcd);
}
void
VCA::VCASoloControllable::set_value_unchecked (double val)
{
/* used only by automation playback */
_set_value (val, Controllable::NoGroup);
}
double
VCA::VCASoloControllable::get_value() const
{
boost::shared_ptr<VCA> vca = _vca.lock();
if (!vca) {
return 0.0;
}
return vca->soloed() ? 1.0 : 0.0;
}
/*----*/
VCA::VCAMuteControllable::VCAMuteControllable (string const & name, boost::shared_ptr<VCA> vca)
: AutomationControl (vca->session(), Evoral::Parameter (MuteAutomation), ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
boost::shared_ptr<AutomationList>(), name)
, _vca (vca)
{
}
void
VCA::VCAMuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
if (writable()) {
_set_value (val, gcd);
}
}
void
VCA::VCAMuteControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
{
boost::shared_ptr<VCA> vca = _vca.lock();
if (!vca) {
return;
}
vca->set_mute (val >= 0.5);
AutomationControl::set_value (val, gcd);
}
void
VCA::VCAMuteControllable::set_value_unchecked (double val)
{
/* used only by automation playback */
_set_value (val, Controllable::NoGroup);
}
double
VCA::VCAMuteControllable::get_value() const
{
boost::shared_ptr<VCA> vca = _vca.lock();
if (!vca) {
return 0.0;
}
return vca->muted() ? 1.0 : 0.0;
}

View File

@ -57,6 +57,7 @@ libardour_sources = [
'chan_count.cc',
'chan_mapping.cc',
'config_text.cc',
'control_group.cc',
'control_protocol_manager.cc',
'cycle_timer.cc',
'data_type.cc',
@ -137,12 +138,15 @@ libardour_sources = [
'midi_stretch.cc',
'midi_track.cc',
'midi_ui.cc',
'mididm.cc',
'midiport_manager.cc',
'mix.cc',
'monitor_control.cc',
'monitor_processor.cc',
'mtc_slave.cc',
'mididm.cc',
'mtdm.cc',
'muteable.cc',
'mute_control.cc',
'mute_master.cc',
'note_fixer.cc',
'onset_detector.cc',
@ -154,6 +158,7 @@ libardour_sources = [
'panner_shell.cc',
'parameter_descriptor.cc',
'pcm_utils.cc',
'phase_control.cc',
'playlist.cc',
'playlist_factory.cc',
'playlist_source.cc',
@ -170,13 +175,13 @@ libardour_sources = [
'quantize.cc',
'rc_configuration.cc',
'recent_sessions.cc',
'record_enable_control.cc',
'region_factory.cc',
'resampled_source.cc',
'region.cc',
'return.cc',
'reverse.cc',
'route.cc',
'route_controls.cc',
'route_graph.cc',
'route_group.cc',
'route_group_member.cc',
@ -206,10 +211,14 @@ libardour_sources = [
'session_transport.cc',
'sidechain.cc',
'slave.cc',
'slavable_automation_control.cc',
'smf_source.cc',
'sndfile_helpers.cc',
'sndfileimportable.cc',
'sndfilesource.cc',
'solo_control.cc',
'solo_isolate_control.cc',
'solo_safe_control.cc',
'soundcloud_upload.cc',
'source.cc',
'source_factory.cc',

View File

@ -54,6 +54,7 @@ class LIBPBD_API Controllable : public PBD::StatefulDestructible {
enum Flag {
Toggle = 0x1,
GainLike = 0x2,
RealTime = 0x4
};
Controllable (const std::string& name, Flag f = Flag (0));

View File

@ -199,7 +199,7 @@ ControlProtocol::route_set_rec_enable (uint32_t table_index, bool yn)
boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(r);
if (at) {
at->set_record_enabled (yn, Controllable::NoGroup);
at->rec_enable_control()->set_value (1.0, Controllable::UseGroup);
}
}
@ -215,7 +215,7 @@ ControlProtocol::route_get_rec_enable (uint32_t table_index)
boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(r);
if (at) {
return at->record_enabled ();
return at->rec_enable_control()->get_value();
}
return false;
@ -248,7 +248,7 @@ ControlProtocol::route_set_gain (uint32_t table_index, float gain)
boost::shared_ptr<Route> r = route_table[table_index];
if (r != 0) {
r->set_gain (gain, Controllable::UseGroup);
r->gain_control()->set_value (gain, Controllable::UseGroup);
}
}
@ -298,7 +298,7 @@ ControlProtocol::route_get_muted (uint32_t table_index)
return false;
}
return r->muted ();
return r->mute_control()->muted ();
}
void
@ -311,7 +311,7 @@ ControlProtocol::route_set_muted (uint32_t table_index, bool yn)
boost::shared_ptr<Route> r = route_table[table_index];
if (r != 0) {
r->set_mute (yn, Controllable::UseGroup);
r->mute_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup);
}
}
@ -342,7 +342,7 @@ ControlProtocol::route_set_soloed (uint32_t table_index, bool yn)
boost::shared_ptr<Route> r = route_table[table_index];
if (r != 0) {
r->set_solo (yn, Controllable::UseGroup);
r->solo_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup);
}
}

View File

@ -507,7 +507,7 @@ FaderPort::fader_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
single route at a time, allow the fader to
modify the group, if appropriate.
*/
_current_route->set_gain (val, Controllable::UseGroup);
_current_route->gain_control()->set_value (val, Controllable::UseGroup);
}
}
}
@ -1147,7 +1147,7 @@ FaderPort::set_current_route (boost::shared_ptr<Route> r)
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route);
if (t) {
t->RecordEnableChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this);
t->rec_enable_control()->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this);
}
boost::shared_ptr<AutomationControl> control = _current_route->gain_control ();
@ -1227,7 +1227,7 @@ FaderPort::map_mute ()
if (_current_route->muted()) {
stop_blinking (Mute);
get_button (Mute).set_led_state (_output_port, true);
} else if (_current_route->muted_by_others()) {
} else if (_current_route->mute_control()->muted_by_others()) {
start_blinking (Mute);
} else {
stop_blinking (Mute);
@ -1252,7 +1252,7 @@ FaderPort::map_recenable ()
{
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route);
if (t) {
get_button (Rec).set_led_state (_output_port, t->record_enabled());
get_button (Rec).set_led_state (_output_port, t->rec_enable_control()->get_value());
} else {
get_button (Rec).set_led_state (_output_port, false);
}

View File

@ -129,9 +129,9 @@ FaderPort::mute ()
return;
}
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (_current_route);
session->set_mute (rl, !_current_route->muted());
boost::shared_ptr<ControlList> cl (new ControlList);
cl->push_back (_current_route->mute_control());
session->set_controls (cl, !_current_route->muted(), PBD::Controllable::UseGroup);
}
void
@ -141,14 +141,15 @@ FaderPort::solo ()
return;
}
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (_current_route);
bool yn;
if (Config->get_solo_control_is_listen_control()) {
session->set_listen (rl, !_current_route->listening_via_monitor());
yn = !_current_route->listening_via_monitor();
} else {
session->set_solo (rl, !_current_route->soloed());
yn = !_current_route->soloed();
}
_current_route->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::UseGroup);
}
void
@ -164,10 +165,7 @@ FaderPort::rec_enable ()
return;
}
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (_current_route);
session->set_record_enabled (rl, !t->record_enabled());
t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup);
}
void

View File

@ -383,7 +383,7 @@ MackieControlProtocol::save_press (Button &)
} else {
save_state ();
}
return none;
}
@ -886,15 +886,9 @@ MackieControlProtocol::clearsolo_press (Mackie::Button&)
access_action ("Editor/set-session-from-edit-range");
return none;
}
if (session) {
if (session->soloing()) {
session->set_solo (session->get_routes(), false);
} else if (session->listening()) {
session->set_listen (session->get_routes(), false);
}
session->clear_all_solo_state (session->get_routes()); // safeguard, ideally this won't do anything, check the log-window
if (session) {
session->clear_all_solo_state (session->get_routes());
}
return none;
}
@ -1063,7 +1057,7 @@ MackieControlProtocol::nudge_release (Mackie::Button&)
_modifier_state &= ~MODIFIER_NUDGE;
/* XXX these action names are stupid, because the action can affect
* regions, markers or the playhead depending on selection state.
* regions, markers or the playhead depending on selection state.
*/
if (main_modifier_state() & MODIFIER_SHIFT) {

View File

@ -37,14 +37,17 @@
#include "ardour/debug.h"
#include "ardour/midi_ui.h"
#include "ardour/meter.h"
#include "ardour/monitor_control.h"
#include "ardour/plugin_insert.h"
#include "ardour/pannable.h"
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/phase_control.h"
#include "ardour/rc_configuration.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/send.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/track.h"
#include "ardour/midi_track.h"
#include "ardour/user_bundle.h"
@ -302,7 +305,10 @@ void
Strip::notify_record_enable_changed ()
{
if (_route && _recenable) {
_surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_route);
if (trk) {
_surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
}
}
}

View File

@ -974,7 +974,7 @@ OSC::routes_list (lo_message msg)
|| boost::dynamic_pointer_cast<MidiTrack>(r)) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
lo_message_add_int32 (reply, t->record_enabled());
lo_message_add_int32 (reply, (int32_t) t->rec_enable_control()->get_value());
}
//Automatically listen to routes listed
@ -1054,7 +1054,7 @@ OSC::route_mute (int rid, int yn)
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
r->set_mute (yn, PBD::Controllable::NoGroup);
r->mute_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup);
}
return 0;
@ -1068,7 +1068,7 @@ OSC::route_solo (int rid, int yn)
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
r->solo_control()->set_value(yn, PBD::Controllable::NoGroup);
r->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup);
}
return 0;
@ -1082,7 +1082,10 @@ OSC::route_recenable (int rid, int yn)
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
r->set_record_enabled (yn, PBD::Controllable::NoGroup);
boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r);
if (trk) {
trk->rec_enable_control()->set_value (yn, PBD::Controllable::UseGroup);
}
}
return 0;
@ -1096,7 +1099,7 @@ OSC::route_set_gain_abs (int rid, float level)
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
r->set_gain (level, PBD::Controllable::NoGroup);
r->gain_control()->set_value (level, PBD::Controllable::NoGroup);
}
return 0;