triggers: remove sidechain and use global port for trigger control

This commit is contained in:
Paul Davis 2022-11-19 10:12:14 -07:00
parent c8e3d95109
commit d7895ab1d2
5 changed files with 75 additions and 250 deletions

View File

@ -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();

View File

@ -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) {

View File

@ -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 */

View File

@ -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 ()) {

View File

@ -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