mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-15 18:06:06 +01:00
Merge branch 'master' into mixer-snapshots
This commit is contained in:
commit
efdb93eddb
17 changed files with 472 additions and 516 deletions
|
|
@ -26,7 +26,6 @@
|
|||
#include <glibmm/threads.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "pbd/controllable.h"
|
||||
|
||||
|
|
@ -51,7 +50,6 @@ class ControlGroup;
|
|||
class LIBARDOUR_API AutomationControl
|
||||
: public PBD::Controllable
|
||||
, public Evoral::Control
|
||||
, public boost::enable_shared_from_this<AutomationControl>
|
||||
, public ControlGroupMember
|
||||
, public SessionHandleRef
|
||||
{
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ private:
|
|||
MPControl<bool>& soloed;
|
||||
|
||||
ChannelRecord (uint32_t);
|
||||
~ChannelRecord ();
|
||||
};
|
||||
|
||||
std::vector<ChannelRecord*> _channels;
|
||||
|
|
|
|||
|
|
@ -1079,7 +1079,6 @@ public:
|
|||
boost::shared_ptr<AutomationControl> automation_control_by_id (const PBD::ID&);
|
||||
|
||||
void add_controllable (boost::shared_ptr<PBD::Controllable>);
|
||||
void remove_controllable (PBD::Controllable*);
|
||||
|
||||
boost::shared_ptr<PBD::Controllable> solo_cut_control() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposi
|
|||
}
|
||||
|
||||
if (_group && _group->use_me (gcd)) {
|
||||
_group->set_group_value (shared_from_this(), val);
|
||||
_group->set_group_value (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()), val);
|
||||
} else {
|
||||
actually_set_value (val, gcd);
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ AutomationControl::set_automation_state (AutoState as)
|
|||
}
|
||||
|
||||
if (as == Write) {
|
||||
AutomationWatch::instance().add_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
} else if (as & (Touch | Latch)) {
|
||||
if (alist()->empty()) {
|
||||
Control::set_double (val, _session.current_start_sample (), true);
|
||||
|
|
@ -260,17 +260,17 @@ AutomationControl::set_automation_state (AutoState as)
|
|||
Changed (true, Controllable::NoGroup);
|
||||
}
|
||||
if (!touching()) {
|
||||
AutomationWatch::instance().remove_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
} else {
|
||||
/* this seems unlikely, but the combination of
|
||||
* a control surface and the mouse could make
|
||||
* it possible to put the control into Touch
|
||||
* mode *while* touching it.
|
||||
*/
|
||||
AutomationWatch::instance().add_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
}
|
||||
} else {
|
||||
AutomationWatch::instance().remove_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
Changed (false, Controllable::NoGroup);
|
||||
}
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ AutomationControl::start_touch (double when)
|
|||
AutomationControl::actually_set_value (get_value (), Controllable::NoGroup);
|
||||
alist()->start_touch (when);
|
||||
if (!_desc.toggled) {
|
||||
AutomationWatch::instance().add_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
}
|
||||
set_touching (true);
|
||||
}
|
||||
|
|
@ -315,7 +315,7 @@ AutomationControl::stop_touch (double when)
|
|||
if (alist()->automation_state() & (Touch | Latch)) {
|
||||
alist()->stop_touch (when);
|
||||
if (!_desc.toggled) {
|
||||
AutomationWatch::instance().remove_automation_watch (shared_from_this());
|
||||
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -378,7 +378,7 @@ AutomationControl::check_rt (double val, Controllable::GroupControlDisposition g
|
|||
{
|
||||
if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
|
||||
/* queue change in RT context */
|
||||
_session.set_control (shared_from_this(), val, gcd);
|
||||
_session.set_control (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()), val, gcd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,173 +45,180 @@ namespace ARDOUR {
|
|||
}
|
||||
|
||||
MonitorProcessor::MonitorProcessor (Session& s)
|
||||
: Processor (s, X_("MonitorOut"))
|
||||
, solo_cnt (0)
|
||||
, _monitor_active (false)
|
||||
: Processor (s, X_("MonitorOut"))
|
||||
, solo_cnt (0)
|
||||
, _monitor_active (false)
|
||||
|
||||
, _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
|
||||
, _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
|
||||
, _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
|
||||
, _dim_level_ptr (new MPControl<volatile gain_t>
|
||||
/* default is -12dB, range is -20dB to 0dB */
|
||||
(dB_to_coefficient(-12.0), _("monitor dim level"), Controllable::Flag (0),
|
||||
dB_to_coefficient(-20.0), dB_to_coefficient (0.0)))
|
||||
, _solo_boost_level_ptr (new MPControl<volatile gain_t>
|
||||
/* default is 0dB, range is 0dB to +20dB */
|
||||
(dB_to_coefficient(0.0), _("monitor solo boost level"), Controllable::Flag (0),
|
||||
dB_to_coefficient(0.0), dB_to_coefficient(10.0)))
|
||||
, _dim_all_control (_dim_all_ptr)
|
||||
, _cut_all_control (_cut_all_ptr)
|
||||
, _mono_control (_mono_ptr)
|
||||
, _dim_level_control (_dim_level_ptr)
|
||||
, _solo_boost_level_control (_solo_boost_level_ptr)
|
||||
, _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
|
||||
, _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
|
||||
, _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
|
||||
, _dim_level_ptr (new MPControl<volatile gain_t>
|
||||
/* default is -12dB, range is -20dB to 0dB */
|
||||
(dB_to_coefficient(-12.0), _("monitor dim level"), Controllable::Flag (0),
|
||||
dB_to_coefficient(-20.0), dB_to_coefficient (0.0)))
|
||||
, _solo_boost_level_ptr (new MPControl<volatile gain_t>
|
||||
/* default is 0dB, range is 0dB to +20dB */
|
||||
(dB_to_coefficient(0.0), _("monitor solo boost level"), Controllable::Flag (0),
|
||||
dB_to_coefficient(0.0), dB_to_coefficient(10.0)))
|
||||
, _dim_all_control (_dim_all_ptr)
|
||||
, _cut_all_control (_cut_all_ptr)
|
||||
, _mono_control (_mono_ptr)
|
||||
, _dim_level_control (_dim_level_ptr)
|
||||
, _solo_boost_level_control (_solo_boost_level_ptr)
|
||||
|
||||
, _dim_all (*_dim_all_ptr)
|
||||
, _cut_all (*_cut_all_ptr)
|
||||
, _mono (*_mono_ptr)
|
||||
, _dim_level (*_dim_level_ptr)
|
||||
, _solo_boost_level (*_solo_boost_level_ptr)
|
||||
, _dim_all (*_dim_all_ptr)
|
||||
, _cut_all (*_cut_all_ptr)
|
||||
, _mono (*_mono_ptr)
|
||||
, _dim_level (*_dim_level_ptr)
|
||||
, _solo_boost_level (*_solo_boost_level_ptr)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
MonitorProcessor::~MonitorProcessor ()
|
||||
{
|
||||
allocate_channels (0);
|
||||
allocate_channels (0);
|
||||
|
||||
/* special case for MPControl */
|
||||
_dim_all_control->DropReferences (); /* EMIT SIGNAL */
|
||||
_cut_all_control->DropReferences (); /* EMIT SIGNAL */
|
||||
_mono_control->DropReferences (); /* EMIT SIGNAL */
|
||||
_dim_level_control->DropReferences (); /* EMIT SIGNAL */
|
||||
_solo_boost_level_control->DropReferences (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
MonitorProcessor::allocate_channels (uint32_t size)
|
||||
{
|
||||
while (_channels.size() > size) {
|
||||
if (_channels.back()->soloed) {
|
||||
if (solo_cnt > 0) {
|
||||
--solo_cnt;
|
||||
}
|
||||
}
|
||||
ChannelRecord* cr = _channels.back();
|
||||
_channels.pop_back();
|
||||
delete cr;
|
||||
}
|
||||
while (_channels.size() > size) {
|
||||
if (_channels.back()->soloed) {
|
||||
if (solo_cnt > 0) {
|
||||
--solo_cnt;
|
||||
}
|
||||
}
|
||||
ChannelRecord* cr = _channels.back();
|
||||
_channels.pop_back();
|
||||
delete cr;
|
||||
}
|
||||
|
||||
uint32_t n = _channels.size() + 1;
|
||||
uint32_t n = _channels.size() + 1;
|
||||
|
||||
while (_channels.size() < size) {
|
||||
_channels.push_back (new ChannelRecord (n));
|
||||
}
|
||||
while (_channels.size() < size) {
|
||||
_channels.push_back (new ChannelRecord (n));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
MonitorProcessor::set_state (const XMLNode& node, int version)
|
||||
{
|
||||
int ret = Processor::set_state (node, version);
|
||||
int ret = Processor::set_state (node, version);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string type_name;
|
||||
if (!node.get_property (X_("type"), type_name)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
std::string type_name;
|
||||
if (!node.get_property (X_("type"), type_name)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (type_name != X_("monitor")) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
if (type_name != X_("monitor")) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t channels = 0;
|
||||
if (!node.get_property (X_("channels"), channels)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
uint32_t channels = 0;
|
||||
if (!node.get_property (X_("channels"), channels)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
allocate_channels (channels);
|
||||
allocate_channels (channels);
|
||||
|
||||
// need to check that these conversions are working as expected
|
||||
gain_t val;
|
||||
if (node.get_property (X_("dim-level"), val)) {
|
||||
_dim_level = val;
|
||||
}
|
||||
// need to check that these conversions are working as expected
|
||||
gain_t val;
|
||||
if (node.get_property (X_("dim-level"), val)) {
|
||||
_dim_level = val;
|
||||
}
|
||||
|
||||
if (node.get_property (X_("solo-boost-level"), val)) {
|
||||
_solo_boost_level = val;
|
||||
}
|
||||
if (node.get_property (X_("solo-boost-level"), val)) {
|
||||
_solo_boost_level = val;
|
||||
}
|
||||
|
||||
bool bool_val;
|
||||
if (node.get_property (X_("cut-all"), bool_val)) {
|
||||
_cut_all = bool_val;
|
||||
}
|
||||
bool bool_val;
|
||||
if (node.get_property (X_("cut-all"), bool_val)) {
|
||||
_cut_all = bool_val;
|
||||
}
|
||||
|
||||
if (node.get_property (X_("dim-all"), bool_val)) {
|
||||
_dim_all = bool_val;
|
||||
}
|
||||
if (node.get_property (X_("dim-all"), bool_val)) {
|
||||
_dim_all = bool_val;
|
||||
}
|
||||
|
||||
if (node.get_property (X_("mono"), bool_val)) {
|
||||
_mono = bool_val;
|
||||
}
|
||||
if (node.get_property (X_("mono"), bool_val)) {
|
||||
_mono = bool_val;
|
||||
}
|
||||
|
||||
for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
|
||||
for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
|
||||
|
||||
if ((*i)->name() == X_("Channel")) {
|
||||
if ((*i)->name() == X_("Channel")) {
|
||||
|
||||
uint32_t chn;
|
||||
if (!(*i)->get_property (X_("id"), chn)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
uint32_t chn;
|
||||
if (!(*i)->get_property (X_("id"), chn)) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chn >= _channels.size()) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
ChannelRecord& cr (*_channels[chn]);
|
||||
if (chn >= _channels.size()) {
|
||||
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
ChannelRecord& cr (*_channels[chn]);
|
||||
|
||||
bool gain_coeff_zero;
|
||||
if ((*i)->get_property ("cut", gain_coeff_zero)) {
|
||||
if (gain_coeff_zero) {
|
||||
cr.cut = GAIN_COEFF_ZERO;
|
||||
} else {
|
||||
cr.cut = GAIN_COEFF_UNITY;
|
||||
}
|
||||
}
|
||||
bool gain_coeff_zero;
|
||||
if ((*i)->get_property ("cut", gain_coeff_zero)) {
|
||||
if (gain_coeff_zero) {
|
||||
cr.cut = GAIN_COEFF_ZERO;
|
||||
} else {
|
||||
cr.cut = GAIN_COEFF_UNITY;
|
||||
}
|
||||
}
|
||||
|
||||
bool dim;
|
||||
if ((*i)->get_property ("dim", dim)) {
|
||||
cr.dim = dim;
|
||||
}
|
||||
bool dim;
|
||||
if ((*i)->get_property ("dim", dim)) {
|
||||
cr.dim = dim;
|
||||
}
|
||||
|
||||
bool invert_polarity;
|
||||
if ((*i)->get_property ("invert", invert_polarity)) {
|
||||
if (invert_polarity) {
|
||||
cr.polarity = -1.0f;
|
||||
} else {
|
||||
cr.polarity = 1.0f;
|
||||
}
|
||||
}
|
||||
bool invert_polarity;
|
||||
if ((*i)->get_property ("invert", invert_polarity)) {
|
||||
if (invert_polarity) {
|
||||
cr.polarity = -1.0f;
|
||||
} else {
|
||||
cr.polarity = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool soloed;
|
||||
if ((*i)->get_property ("solo", soloed)) {
|
||||
cr.soloed = soloed;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool soloed;
|
||||
if ((*i)->get_property ("solo", soloed)) {
|
||||
cr.soloed = soloed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* reset solo cnt */
|
||||
/* reset solo cnt */
|
||||
|
||||
solo_cnt = 0;
|
||||
solo_cnt = 0;
|
||||
|
||||
for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
|
||||
if ((*x)->soloed) {
|
||||
solo_cnt++;
|
||||
}
|
||||
}
|
||||
for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
|
||||
if ((*x)->soloed) {
|
||||
solo_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
update_monitor_state ();
|
||||
return 0;
|
||||
|
|
@ -239,7 +246,7 @@ MonitorProcessor::state ()
|
|||
uint32_t chn = 0;
|
||||
|
||||
for (vector<ChannelRecord*>::const_iterator x = _channels.begin (); x != _channels.end ();
|
||||
++x, ++chn) {
|
||||
++x, ++chn) {
|
||||
chn_node = new XMLNode (X_("Channel"));
|
||||
|
||||
chn_node->set_property ("id", chn);
|
||||
|
|
@ -259,87 +266,87 @@ MonitorProcessor::state ()
|
|||
void
|
||||
MonitorProcessor::run (BufferSet& bufs, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double /*speed*/, pframes_t nframes, bool /*result_required*/)
|
||||
{
|
||||
uint32_t chn = 0;
|
||||
gain_t target_gain;
|
||||
gain_t dim_level_this_time = _dim_level;
|
||||
gain_t global_cut = (_cut_all ? GAIN_COEFF_ZERO : GAIN_COEFF_UNITY);
|
||||
gain_t global_dim = (_dim_all ? dim_level_this_time : GAIN_COEFF_UNITY);
|
||||
gain_t solo_boost;
|
||||
uint32_t chn = 0;
|
||||
gain_t target_gain;
|
||||
gain_t dim_level_this_time = _dim_level;
|
||||
gain_t global_cut = (_cut_all ? GAIN_COEFF_ZERO : GAIN_COEFF_UNITY);
|
||||
gain_t global_dim = (_dim_all ? dim_level_this_time : GAIN_COEFF_UNITY);
|
||||
gain_t solo_boost;
|
||||
|
||||
if (_session.listening() || _session.soloing()) {
|
||||
solo_boost = _solo_boost_level;
|
||||
} else {
|
||||
solo_boost = GAIN_COEFF_UNITY;
|
||||
}
|
||||
if (_session.listening() || _session.soloing()) {
|
||||
solo_boost = _solo_boost_level;
|
||||
} else {
|
||||
solo_boost = GAIN_COEFF_UNITY;
|
||||
}
|
||||
|
||||
for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
|
||||
for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
|
||||
|
||||
/* don't double-scale by both track dim and global dim coefficients */
|
||||
/* don't double-scale by both track dim and global dim coefficients */
|
||||
|
||||
gain_t dim_level = (global_dim == GAIN_COEFF_UNITY ? (_channels[chn]->dim ? dim_level_this_time : GAIN_COEFF_UNITY) : GAIN_COEFF_UNITY);
|
||||
gain_t dim_level = (global_dim == GAIN_COEFF_UNITY ? (_channels[chn]->dim ? dim_level_this_time : GAIN_COEFF_UNITY) : GAIN_COEFF_UNITY);
|
||||
|
||||
if (_channels[chn]->soloed) {
|
||||
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
|
||||
} else {
|
||||
if (solo_cnt == 0) {
|
||||
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
|
||||
} else {
|
||||
target_gain = GAIN_COEFF_ZERO;
|
||||
}
|
||||
}
|
||||
if (_channels[chn]->soloed) {
|
||||
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
|
||||
} else {
|
||||
if (solo_cnt == 0) {
|
||||
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
|
||||
} else {
|
||||
target_gain = GAIN_COEFF_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_gain != _channels[chn]->current_gain || target_gain != GAIN_COEFF_UNITY) {
|
||||
if (target_gain != _channels[chn]->current_gain || target_gain != GAIN_COEFF_UNITY) {
|
||||
|
||||
_channels[chn]->current_gain = Amp::apply_gain (*b, _session.nominal_sample_rate(), nframes, _channels[chn]->current_gain, target_gain);
|
||||
}
|
||||
_channels[chn]->current_gain = Amp::apply_gain (*b, _session.nominal_sample_rate(), nframes, _channels[chn]->current_gain, target_gain);
|
||||
}
|
||||
|
||||
++chn;
|
||||
}
|
||||
++chn;
|
||||
}
|
||||
|
||||
if (_mono) {
|
||||
DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
|
||||
if (_mono) {
|
||||
DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
|
||||
|
||||
/* chn is now the number of channels, use as a scaling factor when mixing
|
||||
*/
|
||||
gain_t scale = 1.f / (float)chn;
|
||||
BufferSet::audio_iterator b = bufs.audio_begin();
|
||||
AudioBuffer& ab (*b);
|
||||
Sample* buf = ab.data();
|
||||
/* chn is now the number of channels, use as a scaling factor when mixing
|
||||
*/
|
||||
gain_t scale = 1.f / (float)chn;
|
||||
BufferSet::audio_iterator b = bufs.audio_begin();
|
||||
AudioBuffer& ab (*b);
|
||||
Sample* buf = ab.data();
|
||||
|
||||
/* scale the first channel */
|
||||
/* scale the first channel */
|
||||
|
||||
for (pframes_t n = 0; n < nframes; ++n) {
|
||||
buf[n] *= scale;
|
||||
}
|
||||
for (pframes_t n = 0; n < nframes; ++n) {
|
||||
buf[n] *= scale;
|
||||
}
|
||||
|
||||
/* add every other channel into the first channel's buffer */
|
||||
/* add every other channel into the first channel's buffer */
|
||||
|
||||
++b;
|
||||
for (; b != bufs.audio_end(); ++b) {
|
||||
AudioBuffer& ob (*b);
|
||||
Sample* obuf = ob.data ();
|
||||
for (pframes_t n = 0; n < nframes; ++n) {
|
||||
buf[n] += obuf[n] * scale;
|
||||
}
|
||||
}
|
||||
++b;
|
||||
for (; b != bufs.audio_end(); ++b) {
|
||||
AudioBuffer& ob (*b);
|
||||
Sample* obuf = ob.data ();
|
||||
for (pframes_t n = 0; n < nframes; ++n) {
|
||||
buf[n] += obuf[n] * scale;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy the first channel to every other channel's buffer */
|
||||
/* copy the first channel to every other channel's buffer */
|
||||
|
||||
b = bufs.audio_begin();
|
||||
++b;
|
||||
for (; b != bufs.audio_end(); ++b) {
|
||||
AudioBuffer& ob (*b);
|
||||
Sample* obuf = ob.data ();
|
||||
memcpy (obuf, buf, sizeof (Sample) * nframes);
|
||||
}
|
||||
}
|
||||
b = bufs.audio_begin();
|
||||
++b;
|
||||
for (; b != bufs.audio_end(); ++b) {
|
||||
AudioBuffer& ob (*b);
|
||||
Sample* obuf = ob.data ();
|
||||
memcpy (obuf, buf, sizeof (Sample) * nframes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::configure_io (ChanCount in, ChanCount out)
|
||||
{
|
||||
allocate_channels (in.n_audio());
|
||||
return Processor::configure_io (in, out);
|
||||
allocate_channels (in.n_audio());
|
||||
return Processor::configure_io (in, out);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -419,51 +426,49 @@ MonitorProcessor::set_dim_all (bool yn)
|
|||
bool
|
||||
MonitorProcessor::display_to_user () const
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::soloed (uint32_t chn) const
|
||||
{
|
||||
return _channels[chn]->soloed;
|
||||
return _channels[chn]->soloed;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MonitorProcessor::inverted (uint32_t chn) const
|
||||
{
|
||||
return _channels[chn]->polarity < 0.0f;
|
||||
return _channels[chn]->polarity < 0.0f;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MonitorProcessor::cut (uint32_t chn) const
|
||||
{
|
||||
return _channels[chn]->cut == GAIN_COEFF_ZERO;
|
||||
return _channels[chn]->cut == GAIN_COEFF_ZERO;
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::dimmed (uint32_t chn) const
|
||||
{
|
||||
return _channels[chn]->dim;
|
||||
return _channels[chn]->dim;
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::mono () const
|
||||
{
|
||||
return _mono;
|
||||
return _mono;
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::dim_all () const
|
||||
{
|
||||
return _dim_all;
|
||||
return _dim_all;
|
||||
}
|
||||
|
||||
bool
|
||||
MonitorProcessor::cut_all () const
|
||||
{
|
||||
return _cut_all;
|
||||
return _cut_all;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -492,37 +497,37 @@ MonitorProcessor::update_monitor_state ()
|
|||
boost::shared_ptr<Controllable>
|
||||
MonitorProcessor::channel_cut_control (uint32_t chn) const
|
||||
{
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->cut_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->cut_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<Controllable>
|
||||
MonitorProcessor::channel_dim_control (uint32_t chn) const
|
||||
{
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->dim_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->dim_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<Controllable>
|
||||
MonitorProcessor::channel_polarity_control (uint32_t chn) const
|
||||
{
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->polarity_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->polarity_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<Controllable>
|
||||
MonitorProcessor::channel_solo_control (uint32_t chn) const
|
||||
{
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->soloed_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
if (chn < _channels.size()) {
|
||||
return _channels[chn]->soloed_control;
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
}
|
||||
|
||||
MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
|
||||
|
|
@ -542,5 +547,13 @@ MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
|
|||
, polarity (*polarity_ptr)
|
||||
, soloed (*soloed_ptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MonitorProcessor::ChannelRecord::~ChannelRecord ()
|
||||
{
|
||||
/* special case for MPControl */
|
||||
cut_control->DropReferences(); /* EMIT SIGNAL */
|
||||
dim_control->DropReferences(); /* EMIT SIGNAL */
|
||||
polarity_control->DropReferences(); /* EMIT SIGNAL */
|
||||
soloed_control->DropReferences(); /* EMIT SIGNAL */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,5 +57,5 @@ PanControllable::actually_set_value (double v, Controllable::GroupControlDisposi
|
|||
std::string
|
||||
PanControllable::get_user_string () const
|
||||
{
|
||||
return owner->value_as_string (shared_from_this());
|
||||
return owner->value_as_string (boost::dynamic_pointer_cast<const AutomationControl>(shared_from_this()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4224,6 +4224,9 @@ Route::shift (samplepos_t pos, samplecnt_t samples)
|
|||
boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
|
||||
if (ac) {
|
||||
boost::shared_ptr<AutomationList> al = ac->alist();
|
||||
if (al->empty ()) {
|
||||
continue;
|
||||
}
|
||||
XMLNode &before = al->get_state ();
|
||||
al->shift (pos, samples);
|
||||
XMLNode &after = al->get_state ();
|
||||
|
|
|
|||
|
|
@ -680,6 +680,14 @@ Session::destroy ()
|
|||
|
||||
Port::PortDrop (); /* EMIT SIGNAL */
|
||||
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (controllables_lock);
|
||||
for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
|
||||
(*i)->DropReferences (); /* EMIT SIGNAL */
|
||||
}
|
||||
controllables.clear ();
|
||||
}
|
||||
|
||||
/* clear history so that no references to objects are held any more */
|
||||
|
||||
_history.clear ();
|
||||
|
|
@ -864,6 +872,10 @@ Session::destroy ()
|
|||
|
||||
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
|
||||
|
||||
#ifndef NDEBUG
|
||||
Controllable::dump_registry ();
|
||||
#endif
|
||||
|
||||
BOOST_SHOW_POINTERS ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,7 +208,6 @@ Session::pre_engine_init (string fullpath)
|
|||
SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
|
||||
PlaylistFactory::PlaylistCreated.connect_same_thread (*this, boost::bind (&Session::add_playlist, this, _1, _2));
|
||||
AutomationList::AutomationListCreated.connect_same_thread (*this, boost::bind (&Session::add_automation_list, this, _1));
|
||||
Controllable::Destroyed.connect_same_thread (*this, boost::bind (&Session::remove_controllable, this, _1));
|
||||
IO::PortCountChanged.connect_same_thread (*this, boost::bind (&Session::ensure_buffers, this, _1));
|
||||
|
||||
/* stop IO objects from doing stuff until we're ready for them */
|
||||
|
|
@ -3739,24 +3738,6 @@ Session::add_controllable (boost::shared_ptr<Controllable> c)
|
|||
controllables.insert (c);
|
||||
}
|
||||
|
||||
struct null_deleter { void operator()(void const *) const {} };
|
||||
|
||||
void
|
||||
Session::remove_controllable (Controllable* c)
|
||||
{
|
||||
if (deletion_in_progress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (controllables_lock);
|
||||
|
||||
Controllables::iterator x = controllables.find (boost::shared_ptr<Controllable>(c, null_deleter()));
|
||||
|
||||
if (x != controllables.end()) {
|
||||
controllables.erase (x);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<Controllable>
|
||||
Session::controllable_by_id (const PBD::ID& id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,16 +29,14 @@
|
|||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
PBD::Signal1<void,Controllable*> Controllable::Destroyed;
|
||||
PBD::Signal1<bool,Controllable*> Controllable::StartLearning;
|
||||
PBD::Signal1<void,Controllable*> Controllable::StopLearning;
|
||||
PBD::Signal3<void,Controllable*,int,int> Controllable::CreateBinding;
|
||||
PBD::Signal1<void,Controllable*> Controllable::DeleteBinding;
|
||||
PBD::Signal1<bool, boost::weak_ptr<PBD::Controllable> > Controllable::StartLearning;
|
||||
PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > Controllable::StopLearning;
|
||||
PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > Controllable::GUIFocusChanged;
|
||||
|
||||
Glib::Threads::RWLock Controllable::registry_lock;
|
||||
Controllable::Controllables Controllable::registry;
|
||||
PBD::ScopedConnectionList* registry_connections = 0;
|
||||
PBD::ScopedConnectionList Controllable::registry_connections;
|
||||
|
||||
const std::string Controllable::xml_node_name = X_("Controllable");
|
||||
|
||||
Controllable::Controllable (const string& name, Flag f)
|
||||
|
|
@ -49,62 +47,6 @@ Controllable::Controllable (const string& name, Flag f)
|
|||
add (*this);
|
||||
}
|
||||
|
||||
void
|
||||
Controllable::add (Controllable& ctl)
|
||||
{
|
||||
using namespace boost;
|
||||
|
||||
Glib::Threads::RWLock::WriterLock lm (registry_lock);
|
||||
registry.insert (&ctl);
|
||||
|
||||
if (!registry_connections) {
|
||||
registry_connections = new ScopedConnectionList;
|
||||
}
|
||||
|
||||
/* Controllable::remove() is static - no need to manage this connection */
|
||||
|
||||
ctl.DropReferences.connect_same_thread (*registry_connections, boost::bind (&Controllable::remove, &ctl));
|
||||
}
|
||||
|
||||
void
|
||||
Controllable::remove (Controllable* ctl)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (registry_lock);
|
||||
|
||||
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
|
||||
if ((*i) == ctl) {
|
||||
registry.erase (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controllable*
|
||||
Controllable::by_id (const ID& id)
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
|
||||
|
||||
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
|
||||
if ((*i)->id() == id) {
|
||||
return (*i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Controllable*
|
||||
Controllable::by_name (const string& str)
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
|
||||
|
||||
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
|
||||
if ((*i)->_name == str) {
|
||||
return (*i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
Controllable::get_state ()
|
||||
{
|
||||
|
|
@ -154,3 +96,47 @@ Controllable::set_flags (Flag f)
|
|||
{
|
||||
_flags = f;
|
||||
}
|
||||
|
||||
void
|
||||
Controllable::add (Controllable& ctl)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (registry_lock);
|
||||
registry.insert (&ctl);
|
||||
ctl.DropReferences.connect_same_thread (registry_connections, boost::bind (&Controllable::remove, &ctl));
|
||||
ctl.Destroyed.connect_same_thread (registry_connections, boost::bind (&Controllable::remove, &ctl));
|
||||
}
|
||||
|
||||
void
|
||||
Controllable::remove (Controllable* ctl)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (registry_lock);
|
||||
Controllables::iterator i = std::find (registry.begin(), registry.end(), ctl);
|
||||
if (i != registry.end()) {
|
||||
registry.erase (i);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<Controllable>
|
||||
Controllable::by_id (const ID& id)
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
|
||||
|
||||
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
|
||||
if ((*i)->id() == id) {
|
||||
return (*i)->shared_from_this ();
|
||||
}
|
||||
}
|
||||
return boost::shared_ptr<Controllable>();
|
||||
}
|
||||
|
||||
void
|
||||
Controllable::dump_registry ()
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
|
||||
unsigned int cnt = 0;
|
||||
cout << "-- List Of Registered Controllables\n";
|
||||
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i, ++cnt) {
|
||||
cout << "CTRL: " << (*i)->name() << "\n";
|
||||
}
|
||||
cout << "Total number of registered sontrollables: " << cnt << "\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@
|
|||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "pbd/libpbd_visibility.h"
|
||||
#include "pbd/signals.h"
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "pbd/statefuldestructible.h"
|
||||
|
||||
using std::min;
|
||||
|
|
@ -49,7 +50,8 @@ namespace PBD {
|
|||
* as a control whose value can range between 0 and 1.0.
|
||||
*
|
||||
*/
|
||||
class LIBPBD_API Controllable : public PBD::StatefulDestructible {
|
||||
class LIBPBD_API Controllable : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Controllable>
|
||||
{
|
||||
public:
|
||||
enum Flag {
|
||||
Toggle = 0x1,
|
||||
|
|
@ -59,7 +61,6 @@ public:
|
|||
};
|
||||
|
||||
Controllable (const std::string& name, Flag f = Flag (0));
|
||||
virtual ~Controllable() { Destroyed (this); }
|
||||
|
||||
/* We express Controllable values in one of three ways:
|
||||
* 1. `user' --- as presented to the user (e.g. dB, Hz, etc.)
|
||||
|
|
@ -122,13 +123,9 @@ public:
|
|||
virtual std::string get_user_string() const { return std::string(); }
|
||||
|
||||
PBD::Signal0<void> LearningFinished;
|
||||
static PBD::Signal3<void,PBD::Controllable*,int,int> CreateBinding;
|
||||
static PBD::Signal1<void,PBD::Controllable*> DeleteBinding;
|
||||
|
||||
static PBD::Signal1<bool,PBD::Controllable*> StartLearning;
|
||||
static PBD::Signal1<void,PBD::Controllable*> StopLearning;
|
||||
|
||||
static PBD::Signal1<void,Controllable*> Destroyed;
|
||||
static PBD::Signal1<bool, boost::weak_ptr<PBD::Controllable> > StartLearning;
|
||||
static PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > StopLearning;
|
||||
|
||||
static PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > GUIFocusChanged;
|
||||
|
||||
|
|
@ -137,7 +134,7 @@ public:
|
|||
int set_state (const XMLNode&, int version);
|
||||
virtual XMLNode& get_state ();
|
||||
|
||||
std::string name() const { return _name; }
|
||||
std::string name() const { return _name; }
|
||||
|
||||
bool touching () const { return _touching; }
|
||||
PBD::Signal0<void> TouchChanged;
|
||||
|
|
@ -152,8 +149,9 @@ public:
|
|||
Flag flags() const { return _flags; }
|
||||
void set_flags (Flag f);
|
||||
|
||||
static Controllable* by_id (const PBD::ID&);
|
||||
static Controllable* by_name (const std::string&);
|
||||
static boost::shared_ptr<Controllable> by_id (const PBD::ID&);
|
||||
static void dump_registry ();
|
||||
|
||||
static const std::string xml_node_name;
|
||||
|
||||
protected:
|
||||
|
|
@ -164,18 +162,19 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string _name;
|
||||
std::string _units;
|
||||
Flag _flags;
|
||||
bool _touching;
|
||||
|
||||
static void add (Controllable&);
|
||||
static void remove (Controllable*);
|
||||
|
||||
typedef std::set<PBD::Controllable*> Controllables;
|
||||
|
||||
static ScopedConnectionList registry_connections;
|
||||
static Glib::Threads::RWLock registry_lock;
|
||||
static Controllables registry;
|
||||
|
||||
static void add (Controllable&);
|
||||
static void remove (Controllable*);
|
||||
};
|
||||
|
||||
/* a utility class for the occasions when you need but do not have
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#define playback_buffer_h
|
||||
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <glibmm.h>
|
||||
|
||||
#include "pbd/libpbd_visibility.h"
|
||||
|
|
|
|||
|
|
@ -107,9 +107,7 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
|
|||
*/
|
||||
|
||||
Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
|
||||
Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
|
||||
Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
|
||||
Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
|
||||
Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
|
||||
|
||||
/* this signal is emitted by the process() callback, and if
|
||||
* send_feedback() is going to do anything, it should do it in the
|
||||
|
|
@ -345,9 +343,10 @@ GenericMidiControlProtocol::_send_feedback ()
|
|||
}
|
||||
|
||||
bool
|
||||
GenericMidiControlProtocol::start_learning (Controllable* c)
|
||||
GenericMidiControlProtocol::start_learning (boost::weak_ptr <Controllable> wc)
|
||||
{
|
||||
if (c == 0) {
|
||||
boost::shared_ptr<Controllable> c = wc.lock ();
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +400,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
|
|||
}
|
||||
|
||||
if (!mc) {
|
||||
mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
|
||||
mc = new MIDIControllable (this, *_input_port->parser(), c, false);
|
||||
own_mc = true;
|
||||
}
|
||||
|
||||
|
|
@ -443,8 +442,13 @@ GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
|
|||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::stop_learning (Controllable* c)
|
||||
GenericMidiControlProtocol::stop_learning (boost::weak_ptr<PBD::Controllable> wc)
|
||||
{
|
||||
boost::shared_ptr<Controllable> c = wc.lock ();
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
Glib::Threads::Mutex::Lock lm (pending_lock);
|
||||
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
|
||||
MIDIControllable* dptr = 0;
|
||||
|
|
@ -468,64 +472,6 @@ GenericMidiControlProtocol::stop_learning (Controllable* c)
|
|||
delete dptr;
|
||||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
|
||||
{
|
||||
if (control != 0) {
|
||||
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
|
||||
|
||||
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
|
||||
MIDIControllable* existingBinding = (*iter);
|
||||
|
||||
if (control == (existingBinding->get_controllable())) {
|
||||
delete existingBinding;
|
||||
iter = controllables.erase (iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This next function seems unused
|
||||
void
|
||||
GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
|
||||
{
|
||||
if (control != NULL) {
|
||||
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
|
||||
|
||||
MIDI::channel_t channel = (pos & 0xf);
|
||||
MIDI::byte value = control_number;
|
||||
|
||||
// Create a MIDIControllable
|
||||
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
|
||||
|
||||
// Remove any old binding for this midi channel/type/value pair
|
||||
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
|
||||
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
|
||||
MIDIControllable* existingBinding = (*iter);
|
||||
|
||||
if ((existingBinding->get_control_channel() & 0xf ) == channel &&
|
||||
existingBinding->get_control_additional() == value &&
|
||||
(existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
|
||||
|
||||
delete existingBinding;
|
||||
iter = controllables.erase (iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update the MIDI Controllable based on the the pos param
|
||||
// Here is where a table lookup for user mappings could go; for now we'll just wing it...
|
||||
mc->bind_midi(channel, MIDI::controller, value);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
|
||||
controllables.push_back (mc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::check_used_event (int pos, int control_number)
|
||||
{
|
||||
|
|
@ -537,7 +483,6 @@ GenericMidiControlProtocol::check_used_event (int pos, int control_number)
|
|||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
|
||||
|
||||
// Remove any old binding for this midi channel/type/value pair
|
||||
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
|
||||
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
|
||||
MIDIControllable* existingBinding = (*iter);
|
||||
if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
|
||||
|
|
@ -686,10 +631,10 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
|
|||
if ((*niter)->get_property ("id", id)) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
|
||||
Controllable* c = Controllable::by_id (id);
|
||||
boost::shared_ptr<PBD::Controllable> c = Controllable::by_id (id);
|
||||
|
||||
if (c) {
|
||||
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
|
||||
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), c, false);
|
||||
|
||||
if (mc->set_state (**niter, version) == 0) {
|
||||
controllables.push_back (mc);
|
||||
|
|
@ -1287,11 +1232,11 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
|
|||
ev = MIDI::program;
|
||||
} else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
|
||||
|
||||
if (prop->name() == X_("sysex")) {
|
||||
ev = MIDI::sysex;
|
||||
} else {
|
||||
ev = MIDI::any;
|
||||
}
|
||||
if (prop->name() == X_("sysex")) {
|
||||
ev = MIDI::sysex;
|
||||
} else {
|
||||
ev = MIDI::any;
|
||||
}
|
||||
|
||||
int val;
|
||||
uint32_t cnt;
|
||||
|
|
@ -1387,11 +1332,11 @@ GenericMidiControlProtocol::create_action (const XMLNode& node)
|
|||
ev = MIDI::program;
|
||||
} else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
|
||||
|
||||
if (prop->name() == X_("sysex")) {
|
||||
ev = MIDI::sysex;
|
||||
} else {
|
||||
ev = MIDI::any;
|
||||
}
|
||||
if (prop->name() == X_("sysex")) {
|
||||
ev = MIDI::sysex;
|
||||
} else {
|
||||
ev = MIDI::any;
|
||||
}
|
||||
|
||||
int val;
|
||||
uint32_t cnt;
|
||||
|
|
@ -1562,9 +1507,9 @@ GenericMidiControlProtocol::input_port() const
|
|||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::maybe_start_touch (Controllable* controllable)
|
||||
GenericMidiControlProtocol::maybe_start_touch (boost::shared_ptr<Controllable> controllable)
|
||||
{
|
||||
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
|
||||
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (controllable);
|
||||
if (actl) {
|
||||
actl->start_touch (session->audible_sample ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace ARDOUR {
|
|||
}
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
class Port;
|
||||
}
|
||||
|
||||
class MIDIControllable;
|
||||
|
|
@ -47,7 +47,7 @@ class MIDIFunction;
|
|||
class MIDIAction;
|
||||
|
||||
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
||||
public:
|
||||
public:
|
||||
GenericMidiControlProtocol (ARDOUR::Session&);
|
||||
virtual ~GenericMidiControlProtocol();
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
|||
|
||||
boost::shared_ptr<PBD::Controllable> lookup_controllable (std::string const &) const;
|
||||
|
||||
void maybe_start_touch (PBD::Controllable*);
|
||||
void maybe_start_touch (boost::shared_ptr<PBD::Controllable>);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
|
@ -110,7 +110,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
|||
|
||||
PBD::Signal0<void> ConnectionChange;
|
||||
|
||||
private:
|
||||
private:
|
||||
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
|
||||
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
|
||||
boost::shared_ptr<ARDOUR::AsyncMIDIPort> _input_port;
|
||||
|
|
@ -144,17 +144,14 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
|||
};
|
||||
typedef std::list<MIDIPendingControllable* > MIDIPendingControllables;
|
||||
MIDIPendingControllables pending_controllables;
|
||||
Glib::Threads::Mutex controllables_lock;
|
||||
Glib::Threads::Mutex pending_lock;
|
||||
Glib::Threads::Mutex controllables_lock;
|
||||
Glib::Threads::Mutex pending_lock;
|
||||
|
||||
bool start_learning (PBD::Controllable*);
|
||||
void stop_learning (PBD::Controllable*);
|
||||
bool start_learning (boost::weak_ptr<PBD::Controllable>);
|
||||
void stop_learning (boost::weak_ptr<PBD::Controllable>);
|
||||
|
||||
void learning_stopped (MIDIControllable*);
|
||||
|
||||
void create_binding (PBD::Controllable*, int, int);
|
||||
void delete_binding (PBD::Controllable*);
|
||||
|
||||
MIDIControllable* create_binding (const XMLNode&);
|
||||
MIDIFunction* create_function (const XMLNode&);
|
||||
MIDIAction* create_action (const XMLNode&);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ using namespace ARDOUR;
|
|||
|
||||
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, bool m)
|
||||
: _surface (s)
|
||||
, controllable (0)
|
||||
, _parser (p)
|
||||
, _momentary (m)
|
||||
{
|
||||
|
|
@ -65,12 +64,12 @@ MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser&
|
|||
control_additional = (MIDI::byte) -1;
|
||||
}
|
||||
|
||||
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, Controllable& c, bool m)
|
||||
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, boost::shared_ptr<PBD::Controllable> c, bool m)
|
||||
: _surface (s)
|
||||
, _parser (p)
|
||||
, _momentary (m)
|
||||
{
|
||||
set_controllable (&c);
|
||||
set_controllable (c);
|
||||
|
||||
_learned = true; /* from controllable */
|
||||
_ctltype = Ctl_Momentary;
|
||||
|
|
@ -119,28 +118,35 @@ MIDIControllable::drop_external_control ()
|
|||
control_additional = (MIDI::byte) -1;
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::set_controllable (Controllable* c)
|
||||
boost::shared_ptr<PBD::Controllable>
|
||||
MIDIControllable::get_controllable () const
|
||||
{
|
||||
if (c == controllable) {
|
||||
return _controllable;
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::set_controllable (boost::shared_ptr<PBD::Controllable> c)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (controllable_lock);
|
||||
if (c && c == _controllable) {
|
||||
return;
|
||||
}
|
||||
|
||||
controllable_death_connection.disconnect ();
|
||||
controllable_death_connections.drop_connections ();
|
||||
|
||||
controllable = c;
|
||||
|
||||
if (controllable) {
|
||||
last_controllable_value = controllable->get_value();
|
||||
if (c) {
|
||||
_controllable = c;
|
||||
last_controllable_value = c->get_value();
|
||||
} else {
|
||||
_controllable.reset();
|
||||
last_controllable_value = 0.0f; // is there a better value?
|
||||
}
|
||||
|
||||
last_incoming = 256;
|
||||
|
||||
if (controllable) {
|
||||
controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR,
|
||||
boost::bind (&MIDIControllable::drop_controllable, this, _1),
|
||||
if (c) {
|
||||
c->DropReferences.connect (controllable_death_connections, MISSING_INVALIDATOR,
|
||||
boost::bind (&MIDIControllable::drop_controllable, this),
|
||||
MidiControlUI::instance());
|
||||
}
|
||||
}
|
||||
|
|
@ -171,22 +177,26 @@ MIDIControllable::stop_learning ()
|
|||
int
|
||||
MIDIControllable::control_to_midi (float val)
|
||||
{
|
||||
if (controllable->is_gain_like()) {
|
||||
return controllable->internal_to_interface (val) * max_value_for_type ();
|
||||
if (!_controllable) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float control_min = controllable->lower ();
|
||||
float control_max = controllable->upper ();
|
||||
if (_controllable->is_gain_like()) {
|
||||
return _controllable->internal_to_interface (val) * max_value_for_type ();
|
||||
}
|
||||
|
||||
float control_min = _controllable->lower ();
|
||||
float control_max = _controllable->upper ();
|
||||
float control_range = control_max - control_min;
|
||||
|
||||
if (controllable->is_toggle()) {
|
||||
if (_controllable->is_toggle()) {
|
||||
if (val >= (control_min + (control_range/2.0f))) {
|
||||
return max_value_for_type();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
|
||||
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (_controllable);
|
||||
if (actl) {
|
||||
control_min = actl->internal_to_interface(control_min);
|
||||
control_max = actl->internal_to_interface(control_max);
|
||||
|
|
@ -202,6 +212,9 @@ MIDIControllable::control_to_midi (float val)
|
|||
float
|
||||
MIDIControllable::midi_to_control (int val)
|
||||
{
|
||||
if (!_controllable) {
|
||||
return 0;
|
||||
}
|
||||
/* fiddle with MIDI value so that we get an odd number of integer steps
|
||||
and can thus represent "middle" precisely as 0.5. this maps to
|
||||
the range 0..+1.0 (0 to 126)
|
||||
|
|
@ -209,17 +222,17 @@ MIDIControllable::midi_to_control (int val)
|
|||
|
||||
float fv = (val == 0 ? 0 : float (val - 1) / (max_value_for_type() - 1));
|
||||
|
||||
if (controllable->is_gain_like()) {
|
||||
return controllable->interface_to_internal (fv);
|
||||
if (_controllable->is_gain_like()) {
|
||||
return _controllable->interface_to_internal (fv);
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Raw value %1 float %2\n", val, fv));
|
||||
|
||||
float control_min = controllable->lower ();
|
||||
float control_max = controllable->upper ();
|
||||
float control_min = _controllable->lower ();
|
||||
float control_max = _controllable->upper ();
|
||||
float control_range = control_max - control_min;
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Min %1 Max %2 Range %3\n", control_min, control_max, control_range));
|
||||
|
||||
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
|
||||
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (_controllable);
|
||||
if (actl) {
|
||||
if (fv == 0.f) return control_min;
|
||||
if (fv == 1.f) return control_max;
|
||||
|
|
@ -256,62 +269,62 @@ MIDIControllable::lookup_controllable()
|
|||
return -1;
|
||||
}
|
||||
|
||||
set_controllable (c.get ());
|
||||
set_controllable (c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::drop_controllable (Controllable* c)
|
||||
MIDIControllable::drop_controllable ()
|
||||
{
|
||||
if (c == controllable) {
|
||||
set_controllable (0);
|
||||
}
|
||||
set_controllable (boost::shared_ptr<PBD::Controllable>());
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/)
|
||||
{
|
||||
if (!controllable) {
|
||||
if (!_controllable) {
|
||||
if (lookup_controllable()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_surface->maybe_start_touch (controllable);
|
||||
assert (_controllable);
|
||||
|
||||
if (!controllable->is_toggle()) {
|
||||
_surface->maybe_start_touch (_controllable);
|
||||
|
||||
if (!_controllable->is_toggle()) {
|
||||
if (control_additional == msg->note_number) {
|
||||
controllable->set_value (midi_to_control (msg->velocity), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (msg->velocity), Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Note %1 value %2 %3\n", (int) msg->note_number, (float) midi_to_control (msg->velocity), current_uri() ));
|
||||
}
|
||||
} else {
|
||||
if (control_additional == msg->note_number) {
|
||||
float new_value = controllable->get_value() > 0.5f ? 0.0f : 1.0f;
|
||||
controllable->set_value (new_value, Controllable::UseGroup);
|
||||
float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f;
|
||||
_controllable->set_value (new_value, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Note %1 Value %2 %3\n", (int) msg->note_number, (float) new_value, current_uri()));
|
||||
}
|
||||
}
|
||||
|
||||
last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
|
||||
last_value = (MIDI::byte) (_controllable->get_value() * 127.0); // to prevent feedback fights
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
||||
{
|
||||
if (!controllable) {
|
||||
if (!_controllable) {
|
||||
if (lookup_controllable ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert (controllable);
|
||||
assert (_controllable);
|
||||
|
||||
_surface->maybe_start_touch (controllable);
|
||||
_surface->maybe_start_touch (_controllable);
|
||||
|
||||
if (control_additional == msg->controller_number) {
|
||||
|
||||
if (!controllable->is_toggle()) {
|
||||
if (!_controllable->is_toggle()) {
|
||||
if (get_encoder() == No_enc) {
|
||||
float new_value = msg->value;
|
||||
float max_value = max(last_controllable_value, new_value);
|
||||
|
|
@ -321,8 +334,8 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
|
||||
bool const in_sync = (
|
||||
range < threshold &&
|
||||
controllable->get_value() <= midi_to_control(max_value) &&
|
||||
controllable->get_value() >= midi_to_control(min_value)
|
||||
_controllable->get_value() <= midi_to_control(max_value) &&
|
||||
_controllable->get_value() >= midi_to_control(min_value)
|
||||
);
|
||||
|
||||
/* If the surface is not motorised, we try to prevent jumps when
|
||||
|
|
@ -331,7 +344,7 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
*/
|
||||
|
||||
if (in_sync || _surface->motorised ()) {
|
||||
controllable->set_value (midi_to_control (new_value), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (new_value), Controllable::UseGroup);
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (float) midi_to_control(new_value), current_uri() ));
|
||||
|
||||
|
|
@ -341,30 +354,30 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
switch (get_encoder()) {
|
||||
case Enc_L:
|
||||
if (msg->value > 0x40) {
|
||||
controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
}
|
||||
break;
|
||||
case Enc_R:
|
||||
if (msg->value > 0x40) {
|
||||
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
|
||||
}
|
||||
break;
|
||||
case Enc_2:
|
||||
if (msg->value > 0x40) {
|
||||
controllable->set_value (midi_to_control (last_value - (0x7f - msg->value) + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value - (0x7f - msg->value) + 1), Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
}
|
||||
break;
|
||||
case Enc_B:
|
||||
if (msg->value > 0x40) {
|
||||
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (midi_to_control (last_value - (0x40 - offset)), Controllable::UseGroup);
|
||||
_controllable->set_value (midi_to_control (last_value - (0x40 - offset)), Controllable::UseGroup);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -382,9 +395,9 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
/* relax ... first incoming message */
|
||||
} else {
|
||||
if (msg->value > last_incoming) {
|
||||
controllable->set_value (1.0, Controllable::UseGroup);
|
||||
_controllable->set_value (1.0, Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (0.0, Controllable::UseGroup);
|
||||
_controllable->set_value (0.0, Controllable::UseGroup);
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("dial Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
|
||||
}
|
||||
|
|
@ -395,7 +408,7 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
* time they are pressed.
|
||||
*/
|
||||
if (msg->value >= 0x40) {
|
||||
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
|
||||
_controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("toggle Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
|
||||
}
|
||||
break;
|
||||
|
|
@ -404,70 +417,74 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
|||
maintain state (i.e. they know they were pressed) and then send zero the next time.
|
||||
*/
|
||||
if (msg->value >= 0x40) {
|
||||
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
|
||||
_controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
|
||||
} else {
|
||||
controllable->set_value (0.0, Controllable::NoGroup);
|
||||
_controllable->set_value (0.0, Controllable::NoGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_value = (MIDI::byte) (control_to_midi(controllable->get_value())); // to prevent feedback fights
|
||||
last_value = (MIDI::byte) (control_to_midi(_controllable->get_value())); // to prevent feedback fights
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::midi_sense_program_change (Parser &, MIDI::byte msg)
|
||||
{
|
||||
if (!controllable) {
|
||||
if (!_controllable) {
|
||||
if (lookup_controllable ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_surface->maybe_start_touch (controllable);
|
||||
assert (_controllable);
|
||||
|
||||
_surface->maybe_start_touch (_controllable);
|
||||
|
||||
if (msg == control_additional) {
|
||||
|
||||
if (!controllable->is_toggle()) {
|
||||
controllable->set_value (1.0, Controllable::UseGroup);
|
||||
if (!_controllable->is_toggle()) {
|
||||
_controllable->set_value (1.0, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value 1.0 %3\n", (int) msg, current_uri() ));
|
||||
} else {
|
||||
float new_value = controllable->get_value() > 0.5f ? 0.0f : 1.0f;
|
||||
controllable->set_value (new_value, Controllable::UseGroup);
|
||||
float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f;
|
||||
_controllable->set_value (new_value, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value %2 %3\n", (int) msg, (float) new_value, current_uri()));
|
||||
}
|
||||
}
|
||||
|
||||
last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
|
||||
last_value = (MIDI::byte) (_controllable->get_value() * 127.0); // to prevent feedback fights
|
||||
}
|
||||
|
||||
void
|
||||
MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
|
||||
{
|
||||
if (!controllable) {
|
||||
if (!_controllable) {
|
||||
if (lookup_controllable ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_surface->maybe_start_touch (controllable);
|
||||
assert (_controllable);
|
||||
|
||||
if (!controllable->is_toggle()) {
|
||||
controllable->set_value (midi_to_control (pb), Controllable::UseGroup);
|
||||
_surface->maybe_start_touch (_controllable);
|
||||
|
||||
if (!_controllable->is_toggle()) {
|
||||
_controllable->set_value (midi_to_control (pb), Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI pitchbend %1 value %2 %3\n", (int) control_channel, (float) midi_to_control (pb), current_uri() ));
|
||||
} else {
|
||||
if (pb > 8065.0f) {
|
||||
controllable->set_value (1, Controllable::UseGroup);
|
||||
_controllable->set_value (1, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi pitchbend %1 value 1 %2\n", (int) control_channel, current_uri()));
|
||||
} else {
|
||||
controllable->set_value (0, Controllable::UseGroup);
|
||||
_controllable->set_value (0, Controllable::UseGroup);
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi pitchbend %1 value 0 %2\n", (int) control_channel, current_uri()));
|
||||
}
|
||||
}
|
||||
|
||||
last_value = control_to_midi (controllable->get_value ());
|
||||
last_value = control_to_midi (_controllable->get_value ());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -482,8 +499,8 @@ MIDIControllable::midi_receiver (Parser &, MIDI::byte *msg, size_t /*len*/)
|
|||
_surface->check_used_event(msg[0], msg[1]);
|
||||
bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
|
||||
|
||||
if (controllable) {
|
||||
controllable->LearningFinished ();
|
||||
if (_controllable) {
|
||||
_controllable->LearningFinished ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -491,8 +508,8 @@ void
|
|||
MIDIControllable::rpn_value_change (Parser&, uint16_t rpn, float val)
|
||||
{
|
||||
if (control_rpn == rpn) {
|
||||
if (controllable) {
|
||||
controllable->set_value (val, Controllable::UseGroup);
|
||||
if (_controllable) {
|
||||
_controllable->set_value (val, Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -501,8 +518,8 @@ void
|
|||
MIDIControllable::nrpn_value_change (Parser&, uint16_t nrpn, float val)
|
||||
{
|
||||
if (control_nrpn == nrpn) {
|
||||
if (controllable) {
|
||||
controllable->set_value (val, Controllable::UseGroup);
|
||||
if (_controllable) {
|
||||
_controllable->set_value (val, Controllable::UseGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -511,9 +528,9 @@ void
|
|||
MIDIControllable::rpn_change (Parser&, uint16_t rpn, int dir)
|
||||
{
|
||||
if (control_rpn == rpn) {
|
||||
if (controllable) {
|
||||
if (_controllable) {
|
||||
/* XXX how to increment/decrement ? */
|
||||
// controllable->set_value (val);
|
||||
// _controllable->set_value (val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -522,9 +539,9 @@ void
|
|||
MIDIControllable::nrpn_change (Parser&, uint16_t nrpn, int dir)
|
||||
{
|
||||
if (control_nrpn == nrpn) {
|
||||
if (controllable) {
|
||||
if (_controllable) {
|
||||
/* XXX how to increment/decrement ? */
|
||||
// controllable->set_value (val);
|
||||
// _controllable->set_value (val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -629,11 +646,15 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
|
|||
MIDI::byte*
|
||||
MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/)
|
||||
{
|
||||
if (!controllable || !_surface->get_feedback ()) {
|
||||
Glib::Threads::Mutex::Lock lm (controllable_lock, Glib::Threads::TRY_LOCK);
|
||||
if (!lm.locked ()) {
|
||||
return buf;
|
||||
}
|
||||
if (!_controllable || !_surface->get_feedback ()) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
float val = controllable->get_value ();
|
||||
float val = _controllable->get_value ();
|
||||
|
||||
/* Note that when sending RPN/NPRN we do two things:
|
||||
*
|
||||
|
|
@ -704,7 +725,7 @@ MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*forc
|
|||
return buf;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Feedback: %1 %2\n", control_description(), current_uri()));
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Feedback: %1 %2\n", control_description(), current_uri()));
|
||||
|
||||
*buf++ = (0xF0 & control_type) | (0xF & control_channel);
|
||||
int ev_size = 3;
|
||||
|
|
@ -768,13 +789,13 @@ MIDIControllable::get_state ()
|
|||
|
||||
XMLNode* node = new XMLNode ("MIDIControllable");
|
||||
|
||||
if (_current_uri.empty()) {
|
||||
node->set_property ("id", controllable->id ());
|
||||
if (_current_uri.empty() && _controllable) {
|
||||
node->set_property ("id", _controllable->id ());
|
||||
} else {
|
||||
node->set_property ("uri", _current_uri);
|
||||
}
|
||||
|
||||
if (controllable) {
|
||||
if (_controllable) {
|
||||
snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
|
||||
node->set_property ("event", (const char *)buf);
|
||||
node->set_property ("channel", (int16_t)control_channel);
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ namespace ARDOUR {
|
|||
|
||||
class MIDIControllable : public PBD::Stateful
|
||||
{
|
||||
public:
|
||||
MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, PBD::Controllable&, bool momentary);
|
||||
MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, bool momentary = false);
|
||||
public:
|
||||
MIDIControllable (GenericMidiControlProtocol*, MIDI::Parser&, boost::shared_ptr<PBD::Controllable>, bool momentary);
|
||||
MIDIControllable (GenericMidiControlProtocol*, MIDI::Parser&, bool momentary = false);
|
||||
virtual ~MIDIControllable ();
|
||||
|
||||
int init (const std::string&);
|
||||
|
|
@ -89,8 +89,8 @@ class MIDIControllable : public PBD::Stateful
|
|||
void set_encoder (Encoder val) { _encoder = val; }
|
||||
|
||||
MIDI::Parser& get_parser() { return _parser; }
|
||||
PBD::Controllable* get_controllable() const { return controllable; }
|
||||
void set_controllable (PBD::Controllable*);
|
||||
void set_controllable (boost::shared_ptr<PBD::Controllable>);
|
||||
boost::shared_ptr<PBD::Controllable> get_controllable () const;
|
||||
const std::string& current_uri() const { return _current_uri; }
|
||||
|
||||
std::string control_description() const { return _control_description; }
|
||||
|
|
@ -108,16 +108,16 @@ class MIDIControllable : public PBD::Stateful
|
|||
MIDI::eventType get_control_type () { return control_type; }
|
||||
MIDI::byte get_control_additional () { return control_additional; }
|
||||
|
||||
int lookup_controllable();
|
||||
int lookup_controllable();
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
int max_value_for_type () const;
|
||||
|
||||
GenericMidiControlProtocol* _surface;
|
||||
PBD::Controllable* controllable;
|
||||
boost::shared_ptr<PBD::Controllable> _controllable;
|
||||
std::string _current_uri;
|
||||
MIDI::Parser& _parser;
|
||||
MIDI::Parser& _parser;
|
||||
bool setting;
|
||||
int last_value;
|
||||
int last_incoming;
|
||||
|
|
@ -130,7 +130,7 @@ class MIDIControllable : public PBD::Stateful
|
|||
int midi_msg_id; /* controller ID or note number */
|
||||
PBD::ScopedConnection midi_sense_connection[2];
|
||||
PBD::ScopedConnection midi_learn_connection;
|
||||
PBD::ScopedConnection controllable_death_connection;
|
||||
PBD::ScopedConnectionList controllable_death_connections;
|
||||
/** the type of MIDI message that is used for this control */
|
||||
MIDI::eventType control_type;
|
||||
MIDI::byte control_additional;
|
||||
|
|
@ -142,7 +142,8 @@ class MIDIControllable : public PBD::Stateful
|
|||
std::string _what;
|
||||
bool _bank_relative;
|
||||
|
||||
void drop_controllable (PBD::Controllable*);
|
||||
void drop_controllable ();
|
||||
Glib::Threads::Mutex controllable_lock;
|
||||
|
||||
void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t);
|
||||
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);
|
||||
|
|
@ -159,4 +160,3 @@ class MIDIControllable : public PBD::Stateful
|
|||
};
|
||||
|
||||
#endif // __gm_midicontrollable_h__
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ bool
|
|||
BindingProxy::button_press_handler (GdkEventButton *ev)
|
||||
{
|
||||
if ( controllable && is_bind_action(ev) ) {
|
||||
if (Controllable::StartLearning (controllable.get())) {
|
||||
if (Controllable::StartLearning (controllable)) {
|
||||
string prompt = _("operate controller now");
|
||||
if (prompter == 0) {
|
||||
prompter = new PopUp (Gtk::WIN_POS_MOUSE, 30000, false);
|
||||
|
|
@ -121,7 +121,7 @@ BindingProxy::prompter_hiding (GdkEventAny* /*ev*/)
|
|||
{
|
||||
learning_connection.disconnect ();
|
||||
if (controllable) {
|
||||
Controllable::StopLearning (controllable.get());
|
||||
Controllable::StopLearning (controllable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue