triggers: remove sidechain and use global port for trigger control
This commit is contained in:
parent
c8e3d95109
commit
d7895ab1d2
@ -71,6 +71,7 @@ class AudioRegion;
|
||||
class MidiRegion;
|
||||
class TriggerBox;
|
||||
class SideChain;
|
||||
class MidiPort;
|
||||
|
||||
typedef uint32_t color_t;
|
||||
|
||||
@ -786,9 +787,6 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
TriggerPtr get_next_trigger ();
|
||||
TriggerPtr peek_next_trigger ();
|
||||
|
||||
void add_midi_sidechain ();
|
||||
void update_sidechain_name ();
|
||||
|
||||
void request_reload (int32_t slot, void*);
|
||||
void set_region (uint32_t slot, boost::shared_ptr<Region> region);
|
||||
|
||||
@ -837,6 +835,7 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
static void set_first_midi_note (int n);
|
||||
|
||||
static void init ();
|
||||
static void static_init (Session&);
|
||||
static void begin_process_cycle ();
|
||||
|
||||
static TriggerBoxThread* worker;
|
||||
@ -874,8 +873,6 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
bool _cancel_locate_armed;
|
||||
bool _fast_forwarding;
|
||||
|
||||
boost::shared_ptr<SideChain> _sidechain;
|
||||
|
||||
PBD::PCGRand _pcg;
|
||||
|
||||
/* These four are accessed (read/write) only from process() context */
|
||||
@ -888,13 +885,8 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
|
||||
void maybe_swap_pending (uint32_t);
|
||||
|
||||
int note_to_trigger (int node, int channel);
|
||||
|
||||
void note_on (int note_number, int velocity);
|
||||
void note_off (int note_number, int velocity);
|
||||
|
||||
void reconnect_to_default ();
|
||||
void parameter_changed (std::string const &);
|
||||
static void static_parameter_changed (std::string const &);
|
||||
|
||||
static int _first_midi_note;
|
||||
static TriggerMidiMapMode _midi_map_mode;
|
||||
@ -941,11 +933,15 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
typedef std::map<std::vector<uint8_t>,std::pair<int,int> > CustomMidiMap;
|
||||
static CustomMidiMap _custom_midi_map;
|
||||
|
||||
static void midi_learn_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t);
|
||||
static PBD::ScopedConnectionList midi_learn_connections;
|
||||
static void midi_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t);
|
||||
static MIDI::Parser* input_parser;
|
||||
static PBD::ScopedConnection midi_input_connection;
|
||||
static void input_port_check ();
|
||||
static PBD::ScopedConnectionList static_connections;
|
||||
static boost::shared_ptr<MidiPort> current_input;
|
||||
|
||||
static bool _learning;
|
||||
static std::pair<int,int> learning_for;
|
||||
static MIDI::Parser* learning_parser;
|
||||
static PBD::Signal0<void> TriggerMIDILearned;
|
||||
|
||||
static void init_pool();
|
||||
|
@ -4630,10 +4630,6 @@ Route::set_name (const string& str)
|
||||
pi->update_sidechain_name ();
|
||||
}
|
||||
|
||||
if (_triggerbox) {
|
||||
_triggerbox->update_sidechain_name ();
|
||||
}
|
||||
|
||||
bool ret = (_input->set_name(newname) && _output->set_name(newname));
|
||||
|
||||
if (ret) {
|
||||
|
@ -399,6 +399,9 @@ Session::post_engine_init ()
|
||||
|
||||
Port::set_connecting_blocked (false);
|
||||
|
||||
/* Can't do this until the trigger input MIDI port is set up */
|
||||
TriggerBox::static_init (*this);
|
||||
|
||||
set_clean ();
|
||||
|
||||
/* Now, finally, we can [ask the butler to] fill the playback buffers */
|
||||
|
@ -91,7 +91,6 @@ Track::init ()
|
||||
if (!is_auditioner()) {
|
||||
_triggerbox = boost::shared_ptr<TriggerBox> (new TriggerBox (_session, data_type ()));
|
||||
_triggerbox->set_owner (this);
|
||||
_triggerbox->add_midi_sidechain ();
|
||||
}
|
||||
|
||||
if (Route::init ()) {
|
||||
|
@ -3045,10 +3045,13 @@ bool TriggerBox::roll_requested = false;
|
||||
bool TriggerBox::_learning = false;
|
||||
TriggerBox::CustomMidiMap TriggerBox::_custom_midi_map;
|
||||
std::pair<int,int> TriggerBox::learning_for;
|
||||
PBD::ScopedConnectionList TriggerBox::midi_learn_connections;
|
||||
MIDI::Parser* TriggerBox::learning_parser = 0;
|
||||
PBD::Signal0<void> TriggerBox::TriggerMIDILearned;
|
||||
|
||||
MIDI::Parser* TriggerBox::input_parser (new MIDI::Parser); /* leak */
|
||||
PBD::ScopedConnectionList TriggerBox::static_connections;
|
||||
PBD::ScopedConnection TriggerBox::midi_input_connection;
|
||||
boost::shared_ptr<MidiPort> TriggerBox::current_input;
|
||||
|
||||
typedef std::map <boost::shared_ptr<Region>, boost::shared_ptr<Trigger::UIState>> RegionStateMap;
|
||||
RegionStateMap enqueued_state_map;
|
||||
|
||||
@ -3060,6 +3063,15 @@ TriggerBox::init ()
|
||||
init_pool ();
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::static_init (Session & s)
|
||||
{
|
||||
Config->ParameterChanged.connect_same_thread (static_connections, boost::bind (&TriggerBox::static_parameter_changed, _1));
|
||||
input_parser->any.connect_same_thread (midi_input_connection, boost::bind (&TriggerBox::midi_input_handler, _1, _2, _3, _4));
|
||||
boost::dynamic_pointer_cast<MidiPort> (s.trigger_input_port())->set_trace (input_parser);
|
||||
s.trigger_input_port()->connect (Config->get_default_trigger_input_port());
|
||||
}
|
||||
|
||||
TriggerBox::TriggerBox (Session& s, DataType dt)
|
||||
: Processor (s, _("TriggerBox"), Temporal::BeatTime)
|
||||
, tracker (dt == DataType::MIDI ? new MidiStateTracker : 0)
|
||||
@ -3108,19 +3120,34 @@ TriggerBox::set_cue_recording (bool yn)
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::parameter_changed (std::string const & param)
|
||||
TriggerBox::input_port_check ()
|
||||
{
|
||||
if (Config->get_default_trigger_input_port().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Session* session = AudioEngine::instance()->session();
|
||||
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "Reconnect to " << Config->get_default_trigger_input_port() << std::endl;
|
||||
session->trigger_input_port()->connect (Config->get_default_trigger_input_port());
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::static_parameter_changed (std::string const & param)
|
||||
{
|
||||
if (param == X_("default-trigger-input-port")) {
|
||||
input_port_check ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config->get_default_trigger_input_port().empty()) {
|
||||
if (!_sidechain) {
|
||||
add_midi_sidechain ();
|
||||
} else {
|
||||
reconnect_to_default ();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (param == "cue-behavior") {
|
||||
void
|
||||
TriggerBox::parameter_changed (std::string const & param)
|
||||
{
|
||||
if (param == "cue-behavior") {
|
||||
const bool follow = (_session.config.get_cue_behavior() & FollowCues);
|
||||
if (!follow) {
|
||||
cancel_locate_armed ();
|
||||
@ -3751,36 +3778,6 @@ TriggerBox::trigger (Triggers::size_type n)
|
||||
return all_triggers[n];
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::add_midi_sidechain ()
|
||||
{
|
||||
assert (owner());
|
||||
if (!_sidechain) {
|
||||
_sidechain.reset (new SideChain (_session, string_compose ("%1/%2", owner()->name(), name ())));
|
||||
_sidechain->activate ();
|
||||
_sidechain->input()->add_port ("", owner(), DataType::MIDI); // add a port, don't connect.
|
||||
boost::shared_ptr<Port> p = _sidechain->input()->nth (0);
|
||||
|
||||
if (p) {
|
||||
if (!Config->get_default_trigger_input_port().empty ()) {
|
||||
p->connect (Config->get_default_trigger_input_port());
|
||||
}
|
||||
} else {
|
||||
error << _("Could not create port for trigger side-chain") << endmsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::update_sidechain_name ()
|
||||
{
|
||||
if (!_sidechain) {
|
||||
return;
|
||||
}
|
||||
assert (owner());
|
||||
_sidechain->set_name (string_compose ("%1/%2", owner()->name(), name ()));
|
||||
}
|
||||
|
||||
bool
|
||||
TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out)
|
||||
{
|
||||
@ -3800,10 +3797,6 @@ TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out)
|
||||
bool
|
||||
TriggerBox::configure_io (ChanCount in, ChanCount out)
|
||||
{
|
||||
if (_sidechain) {
|
||||
_sidechain->configure_io (in, out + ChanCount (DataType::MIDI, 1));
|
||||
}
|
||||
|
||||
bool ret = Processor::configure_io (in, out);
|
||||
|
||||
if (ret) {
|
||||
@ -3833,59 +3826,6 @@ TriggerBox::set_first_midi_note (int n)
|
||||
_first_midi_note = n;
|
||||
}
|
||||
|
||||
int
|
||||
TriggerBox::note_to_trigger (int midi_note, int channel)
|
||||
{
|
||||
const int column = _order;
|
||||
int first_note;
|
||||
int top;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
switch (_midi_map_mode) {
|
||||
|
||||
case AbletonPush:
|
||||
/* the top row of pads generate MIDI note 92, 93, 94 and so on.
|
||||
Each lower row generates notes 8 below the one above it.
|
||||
*/
|
||||
top = 92 + column;
|
||||
for (int row = 0; row < 8; ++row) {
|
||||
if (midi_note == top - (row * 8)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case SequentialNote:
|
||||
first_note = _first_midi_note + (column * all_triggers.size());
|
||||
return midi_note - first_note; /* direct access to row */
|
||||
|
||||
case ByMidiChannel:
|
||||
first_note = 3;
|
||||
break;
|
||||
|
||||
case Custom:
|
||||
if (!_learning) {
|
||||
|
||||
std::vector<uint8_t> msg { uint8_t (MIDI::on | channel), (uint8_t) midi_note };
|
||||
|
||||
if (lookup_custom_midi_binding (msg, x, y)) {
|
||||
if (x == _order) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return midi_note;
|
||||
}
|
||||
|
||||
bool
|
||||
TriggerBox::lookup_custom_midi_binding (std::vector<uint8_t> const & msg, int& x, int& y)
|
||||
{
|
||||
@ -3902,18 +3842,32 @@ TriggerBox::lookup_custom_midi_binding (std::vector<uint8_t> const & msg, int& x
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz, samplecnt_t)
|
||||
TriggerBox::midi_input_handler (MIDI::Parser&, MIDI::byte* buf, size_t sz, samplecnt_t)
|
||||
{
|
||||
if (!_learning) {
|
||||
if (_learning) {
|
||||
|
||||
if ((buf[0] & 0xf0) == MIDI::on) {
|
||||
/* throw away velocity */
|
||||
std::vector<uint8_t> msg { buf[0], buf[1] };
|
||||
add_custom_midi_binding (msg, learning_for.first, learning_for.second);
|
||||
_learning = false;
|
||||
TriggerMIDILearned (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ev[0] & 0xf0) == MIDI::on) {
|
||||
/* throw away velocity */
|
||||
std::vector<uint8_t> msg { ev[0], ev[1] };
|
||||
add_custom_midi_binding (msg, learning_for.first, learning_for.second);
|
||||
TriggerMIDILearned (); /* emit signal */
|
||||
return;
|
||||
Evoral::Event<samplepos_t> ev (Evoral::MIDI_EVENT, 0, sz, buf);
|
||||
|
||||
if (ev.is_note_on()) {
|
||||
|
||||
std::vector<uint8_t> msg { uint8_t (MIDI::on | ev.channel()), (uint8_t) ev.note() };
|
||||
int x;
|
||||
int y;
|
||||
|
||||
if (lookup_custom_midi_binding (msg, x, y)) {
|
||||
AudioEngine::instance()->session()->bang_trigger_at (x, y);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@ -3922,41 +3876,15 @@ TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz,
|
||||
void
|
||||
TriggerBox::begin_midi_learn (int index)
|
||||
{
|
||||
if (!_sidechain) {
|
||||
return;
|
||||
}
|
||||
|
||||
learning_for.first = order(); /* x */
|
||||
learning_for.second = index; /* y */
|
||||
_learning = true;
|
||||
|
||||
|
||||
boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (_sidechain->input()->nth(0));
|
||||
if (!mp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!learning_parser) {
|
||||
learning_parser = new MIDI::Parser;
|
||||
}
|
||||
|
||||
TriggerMIDILearned.connect_same_thread (midi_learn_connections, boost::bind (&TriggerBox::stop_midi_learn, this));
|
||||
learning_parser->any.connect_same_thread (midi_learn_connections, boost::bind (&TriggerBox::midi_learn_input_handler, _1, _2, _3, _4));
|
||||
mp->set_trace (learning_parser);
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::stop_midi_learn ()
|
||||
{
|
||||
if (_learning) {
|
||||
_learning = false;
|
||||
midi_learn_connections.drop_connections ();
|
||||
boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (_sidechain->input()->nth(0));
|
||||
|
||||
if (mp) {
|
||||
mp->set_trace (0);
|
||||
}
|
||||
}
|
||||
_learning = false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -4085,59 +4013,6 @@ TriggerBox::remove_custom_midi_binding (int x, int y)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::process_midi_trigger_requests (BufferSet& bufs)
|
||||
{
|
||||
/* check MIDI port input buffer for triggers. This is always the last
|
||||
* MIDI buffer of the BufferSet
|
||||
*/
|
||||
|
||||
MidiBuffer& mb (bufs.get_midi (bufs.count().n_midi() - 1 /* due to zero-based index*/));
|
||||
|
||||
for (MidiBuffer::iterator ev = mb.begin(); ev != mb.end(); ++ev) {
|
||||
|
||||
if (!(*ev).is_note()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int trigger_number = note_to_trigger ((*ev).note(), (*ev).channel());
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("note %1 received on %2, translated to trigger num %3\n", (int) (*ev).note(), (int) (*ev).channel(), trigger_number));
|
||||
|
||||
if (trigger_number < 0) {
|
||||
/* not for us */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trigger_number >= (int) all_triggers.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TriggerPtr t = all_triggers[trigger_number];
|
||||
|
||||
if (!t) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((*ev).is_note_on()) {
|
||||
|
||||
if (t->velocity_effect() != 0.0) {
|
||||
/* if MVE is zero, MIDI velocity has no
|
||||
impact on gain. If it is small, it
|
||||
has a small effect on gain. As it
|
||||
approaches 1.0, it has full control
|
||||
over the trigger gain.
|
||||
*/
|
||||
t->set_velocity_gain (1.0 - (t->velocity_effect() * (*ev).velocity() / 127.f));
|
||||
}
|
||||
t->bang ();
|
||||
|
||||
} else if ((*ev).is_note_off()) {
|
||||
t->unbang ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::maybe_request_roll (Session& s)
|
||||
{
|
||||
@ -4232,18 +4107,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||
#endif
|
||||
|
||||
bool allstop = _requests.stop_all.exchange (false);
|
||||
|
||||
|
||||
/* STEP THREE: triggers in audio tracks need a MIDI sidechain to be
|
||||
* able to receive inbound MIDI for triggering etc. This needs to run
|
||||
* before anything else, since we may need data just received to launch
|
||||
* a trigger (or stop it)
|
||||
*/
|
||||
|
||||
if (_sidechain) {
|
||||
_sidechain->run (bufs, start_sample, end_sample, speed, nframes, true);
|
||||
}
|
||||
|
||||
bool was_recorded;
|
||||
int32_t cue_bang = _session.first_cue_within (start_sample, end_sample, was_recorded);
|
||||
|
||||
@ -4293,11 +4156,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||
|
||||
process_requests (bufs);
|
||||
|
||||
/* STEP FIVE: handle any incoming MIDI requests
|
||||
*/
|
||||
|
||||
process_midi_trigger_requests (bufs);
|
||||
|
||||
/* STEP SEVEN: let each slot process any individual state requests
|
||||
*/
|
||||
|
||||
@ -4754,10 +4612,6 @@ TriggerBox::get_state () const
|
||||
|
||||
node.add_child_nocopy (*trigger_child);
|
||||
|
||||
if (_sidechain) {
|
||||
node.add_child_nocopy (_sidechain->get_state ());
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -4803,18 +4657,6 @@ TriggerBox::set_state (const XMLNode& node, int version)
|
||||
}
|
||||
}
|
||||
|
||||
/* sidechain is a Processor (IO) */
|
||||
XMLNode* scnode = node.child (Processor::state_node_name.c_str ());
|
||||
if (scnode) {
|
||||
add_midi_sidechain ();
|
||||
assert (_sidechain);
|
||||
if (!regenerate_xml_or_string_ids ()) {
|
||||
_sidechain->set_state (*scnode, version);
|
||||
} else {
|
||||
update_sidechain_name ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Since _active_slots may have changed, we could consider sending
|
||||
* EmptyStatusChanged, but for now we don't consider ::set_state() to
|
||||
* be used except at session load.
|
||||
@ -4823,17 +4665,6 @@ TriggerBox::set_state (const XMLNode& node, int version)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::reconnect_to_default ()
|
||||
{
|
||||
if (!_sidechain) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sidechain->input()->nth (0)->disconnect_all ();
|
||||
_sidechain->input()->nth (0)->connect (Config->get_default_trigger_input_port());
|
||||
}
|
||||
|
||||
MultiAllocSingleReleasePool* TriggerBox::Request::pool;
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user