a start at custom MIDI learn for trigger slots
This commit is contained in:
parent
a130041547
commit
730064277d
@ -36,6 +36,8 @@
|
||||
#include "pbd/ringbuffer.h"
|
||||
#include "pbd/stateful.h"
|
||||
|
||||
#include "midi++/types.h"
|
||||
|
||||
#include "temporal/beats.h"
|
||||
#include "temporal/bbt_time.h"
|
||||
#include "temporal/tempo.h"
|
||||
@ -58,6 +60,10 @@ namespace RubberBand {
|
||||
class RubberBandStretcher;
|
||||
}
|
||||
|
||||
namespace MIDI {
|
||||
class Parser;
|
||||
}
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Session;
|
||||
@ -802,12 +808,22 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
enum TriggerMidiMapMode {
|
||||
AbletonPush,
|
||||
SequentialNote,
|
||||
ByMidiChannel
|
||||
ByMidiChannel,
|
||||
Custom,
|
||||
};
|
||||
|
||||
/* This is null for TriggerBoxen constructed with DataType::AUDIO */
|
||||
MidiStateTracker* tracker;
|
||||
|
||||
static bool lookup_custom_midi_binding (int id, int& x, int& y);
|
||||
static void add_custom_midi_binding (int id, int x, int y);
|
||||
static void remove_custom_midi_binding (int x, int y);
|
||||
static void clear_custom_midi_bindings ();
|
||||
|
||||
void begin_midi_learn (int index);
|
||||
void midi_unlearn (int index);
|
||||
void stop_midi_learn ();
|
||||
|
||||
static Temporal::BBT_Offset assumed_trigger_duration () { return _assumed_trigger_duration; }
|
||||
static void set_assumed_trigger_duration (Temporal::BBT_Offset const &);
|
||||
|
||||
@ -919,6 +935,16 @@ class LIBARDOUR_API TriggerBox : public Processor
|
||||
|
||||
PBD::ScopedConnection stop_all_connection;
|
||||
|
||||
typedef std::map<int,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 bool _learning;
|
||||
static std::pair<int,int> learning_for;
|
||||
static MIDI::Parser* learning_parser;
|
||||
static PBD::Signal0<void> TriggerMIDILearned;
|
||||
|
||||
static void init_pool();
|
||||
|
||||
static std::atomic<int> active_trigger_boxes;
|
||||
|
@ -206,6 +206,8 @@ MidiPort::read_and_parse_entire_midi_buffer_with_no_speed_adjustment (pframes_t
|
||||
*
|
||||
* As of July 2018, this is only used by TransportMasters which
|
||||
* read MIDI before the process() cycle really gets started.
|
||||
*
|
||||
* October 2022: Now also used by trigger custom midi learn.
|
||||
*/
|
||||
|
||||
if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
|
||||
@ -303,7 +305,6 @@ MidiPort::flush_buffers (pframes_t nframes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// event times are in samples, relative to cycle start
|
||||
|
||||
#ifndef NDEBUG
|
||||
@ -383,18 +384,18 @@ MidiPort::reset ()
|
||||
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
|
||||
}
|
||||
|
||||
void
|
||||
MidiPort::set_input_active (bool yn)
|
||||
{
|
||||
_input_active = yn;
|
||||
}
|
||||
|
||||
void
|
||||
MidiPort::set_trace (MIDI::Parser * p)
|
||||
{
|
||||
_trace_parser = p;
|
||||
}
|
||||
|
||||
void
|
||||
MidiPort::set_input_active (bool yn)
|
||||
{
|
||||
_input_active = yn;
|
||||
}
|
||||
|
||||
int
|
||||
MidiPort::add_shadow_port (string const & name, MidiFilter mf)
|
||||
{
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "temporal/tempo.h"
|
||||
|
||||
#include "ardour/async_midi_port.h"
|
||||
#include "ardour/auditioner.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/audioregion.h"
|
||||
@ -3032,8 +3033,7 @@ Trigger::make_property_quarks ()
|
||||
}
|
||||
|
||||
Temporal::BBT_Offset TriggerBox::_assumed_trigger_duration (4, 0, 0);
|
||||
//TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::AbletonPush);
|
||||
TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::SequentialNote);
|
||||
TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::Custom);
|
||||
int TriggerBox::_first_midi_note = 60;
|
||||
std::atomic<int> TriggerBox::active_trigger_boxes (0);
|
||||
TriggerBoxThread* TriggerBox::worker = 0;
|
||||
@ -3041,6 +3041,12 @@ CueRecords TriggerBox::cue_records (256);
|
||||
std::atomic<bool> TriggerBox::_cue_recording (false);
|
||||
PBD::Signal0<void> TriggerBox::CueRecordingChanged;
|
||||
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;
|
||||
|
||||
typedef std::map <boost::shared_ptr<Region>, boost::shared_ptr<Trigger::UIState>> RegionStateMap;
|
||||
RegionStateMap enqueued_state_map;
|
||||
@ -3105,7 +3111,13 @@ TriggerBox::parameter_changed (std::string const & param)
|
||||
{
|
||||
if (param == X_("default-trigger-input-port")) {
|
||||
|
||||
reconnect_to_default ();
|
||||
if (!Config->get_default_trigger_input_port().empty()) {
|
||||
if (!_sidechain) {
|
||||
add_midi_sidechain ();
|
||||
} else {
|
||||
reconnect_to_default ();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (param == "cue-behavior") {
|
||||
const bool follow = (_session.config.get_cue_behavior() & FollowCues);
|
||||
@ -3826,6 +3838,8 @@ 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) {
|
||||
|
||||
@ -3850,6 +3864,17 @@ TriggerBox::note_to_trigger (int midi_note, int channel)
|
||||
first_note = 3;
|
||||
break;
|
||||
|
||||
case Custom:
|
||||
if (!_learning) {
|
||||
if (lookup_custom_midi_binding (channel * 16 + midi_note, x, y)) {
|
||||
if (x == _order) {
|
||||
std::cerr << "yes, slot " << y << std::endl;
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
@ -3858,6 +3883,111 @@ TriggerBox::note_to_trigger (int midi_note, int channel)
|
||||
return midi_note;
|
||||
}
|
||||
|
||||
bool
|
||||
TriggerBox::lookup_custom_midi_binding (int id, int& x, int& y)
|
||||
{
|
||||
CustomMidiMap::iterator i = _custom_midi_map.find (id);
|
||||
|
||||
if (i == _custom_midi_map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
x = i->second.first;
|
||||
y = i->second.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz, samplecnt_t)
|
||||
{
|
||||
if (!_learning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ev[0] & 0xf0) == MIDI::on) {
|
||||
int channel = ev[0] & 0xf;
|
||||
int note = ev[1] & 0x7f;
|
||||
add_custom_midi_binding (channel * 16 + note, learning_for.first, learning_for.second);
|
||||
TriggerMIDILearned (); /* emit signal */
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::midi_unlearn (int index)
|
||||
{
|
||||
remove_custom_midi_binding (order(), index);
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::clear_custom_midi_bindings ()
|
||||
{
|
||||
_custom_midi_map.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::add_custom_midi_binding (int id, int x, int y)
|
||||
{
|
||||
_custom_midi_map.insert (std::make_pair (id, std::make_pair (x, y)));
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::remove_custom_midi_binding (int x, int y)
|
||||
{
|
||||
/* this searches the whole map in case there are multiple entries
|
||||
*(keyed by note/channel) for the same pad (x,y)
|
||||
*/
|
||||
|
||||
for (CustomMidiMap::iterator i = _custom_midi_map.begin(); i != _custom_midi_map.end(); ++i) {
|
||||
if (i->second.first == x && i->second.second == y) {
|
||||
_custom_midi_map.erase (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::process_midi_trigger_requests (BufferSet& bufs)
|
||||
{
|
||||
@ -3903,10 +4033,12 @@ TriggerBox::process_midi_trigger_requests (BufferSet& bufs)
|
||||
*/
|
||||
t->set_velocity_gain (1.0 - (t->velocity_effect() * (*ev).velocity() / 127.f));
|
||||
}
|
||||
std:: cerr << "bang\n";
|
||||
t->bang ();
|
||||
|
||||
} else if ((*ev).is_note_off()) {
|
||||
|
||||
std:: cerr << "unbang\n";
|
||||
t->unbang ();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user