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/ringbuffer.h"
|
||||||
#include "pbd/stateful.h"
|
#include "pbd/stateful.h"
|
||||||
|
|
||||||
|
#include "midi++/types.h"
|
||||||
|
|
||||||
#include "temporal/beats.h"
|
#include "temporal/beats.h"
|
||||||
#include "temporal/bbt_time.h"
|
#include "temporal/bbt_time.h"
|
||||||
#include "temporal/tempo.h"
|
#include "temporal/tempo.h"
|
||||||
@ -58,6 +60,10 @@ namespace RubberBand {
|
|||||||
class RubberBandStretcher;
|
class RubberBandStretcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MIDI {
|
||||||
|
class Parser;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
@ -802,12 +808,22 @@ class LIBARDOUR_API TriggerBox : public Processor
|
|||||||
enum TriggerMidiMapMode {
|
enum TriggerMidiMapMode {
|
||||||
AbletonPush,
|
AbletonPush,
|
||||||
SequentialNote,
|
SequentialNote,
|
||||||
ByMidiChannel
|
ByMidiChannel,
|
||||||
|
Custom,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is null for TriggerBoxen constructed with DataType::AUDIO */
|
/* This is null for TriggerBoxen constructed with DataType::AUDIO */
|
||||||
MidiStateTracker* tracker;
|
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 Temporal::BBT_Offset assumed_trigger_duration () { return _assumed_trigger_duration; }
|
||||||
static void set_assumed_trigger_duration (Temporal::BBT_Offset const &);
|
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;
|
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 void init_pool();
|
||||||
|
|
||||||
static std::atomic<int> active_trigger_boxes;
|
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
|
* As of July 2018, this is only used by TransportMasters which
|
||||||
* read MIDI before the process() cycle really gets started.
|
* 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) {
|
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
|
// event times are in samples, relative to cycle start
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@ -383,18 +384,18 @@ MidiPort::reset ()
|
|||||||
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
|
_buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MidiPort::set_input_active (bool yn)
|
|
||||||
{
|
|
||||||
_input_active = yn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MidiPort::set_trace (MIDI::Parser * p)
|
MidiPort::set_trace (MIDI::Parser * p)
|
||||||
{
|
{
|
||||||
_trace_parser = p;
|
_trace_parser = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MidiPort::set_input_active (bool yn)
|
||||||
|
{
|
||||||
|
_input_active = yn;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
MidiPort::add_shadow_port (string const & name, MidiFilter mf)
|
MidiPort::add_shadow_port (string const & name, MidiFilter mf)
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "temporal/tempo.h"
|
#include "temporal/tempo.h"
|
||||||
|
|
||||||
|
#include "ardour/async_midi_port.h"
|
||||||
#include "ardour/auditioner.h"
|
#include "ardour/auditioner.h"
|
||||||
#include "ardour/audioengine.h"
|
#include "ardour/audioengine.h"
|
||||||
#include "ardour/audioregion.h"
|
#include "ardour/audioregion.h"
|
||||||
@ -3032,8 +3033,7 @@ Trigger::make_property_quarks ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Temporal::BBT_Offset TriggerBox::_assumed_trigger_duration (4, 0, 0);
|
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::Custom);
|
||||||
TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::SequentialNote);
|
|
||||||
int TriggerBox::_first_midi_note = 60;
|
int TriggerBox::_first_midi_note = 60;
|
||||||
std::atomic<int> TriggerBox::active_trigger_boxes (0);
|
std::atomic<int> TriggerBox::active_trigger_boxes (0);
|
||||||
TriggerBoxThread* TriggerBox::worker = 0;
|
TriggerBoxThread* TriggerBox::worker = 0;
|
||||||
@ -3041,6 +3041,12 @@ CueRecords TriggerBox::cue_records (256);
|
|||||||
std::atomic<bool> TriggerBox::_cue_recording (false);
|
std::atomic<bool> TriggerBox::_cue_recording (false);
|
||||||
PBD::Signal0<void> TriggerBox::CueRecordingChanged;
|
PBD::Signal0<void> TriggerBox::CueRecordingChanged;
|
||||||
bool TriggerBox::roll_requested = false;
|
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;
|
typedef std::map <boost::shared_ptr<Region>, boost::shared_ptr<Trigger::UIState>> RegionStateMap;
|
||||||
RegionStateMap enqueued_state_map;
|
RegionStateMap enqueued_state_map;
|
||||||
@ -3105,7 +3111,13 @@ TriggerBox::parameter_changed (std::string const & param)
|
|||||||
{
|
{
|
||||||
if (param == X_("default-trigger-input-port")) {
|
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") {
|
} else if (param == "cue-behavior") {
|
||||||
const bool follow = (_session.config.get_cue_behavior() & FollowCues);
|
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;
|
const int column = _order;
|
||||||
int first_note;
|
int first_note;
|
||||||
int top;
|
int top;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
switch (_midi_map_mode) {
|
switch (_midi_map_mode) {
|
||||||
|
|
||||||
@ -3850,6 +3864,17 @@ TriggerBox::note_to_trigger (int midi_note, int channel)
|
|||||||
first_note = 3;
|
first_note = 3;
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3858,6 +3883,111 @@ TriggerBox::note_to_trigger (int midi_note, int channel)
|
|||||||
return midi_note;
|
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
|
void
|
||||||
TriggerBox::process_midi_trigger_requests (BufferSet& bufs)
|
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));
|
t->set_velocity_gain (1.0 - (t->velocity_effect() * (*ev).velocity() / 127.f));
|
||||||
}
|
}
|
||||||
|
std:: cerr << "bang\n";
|
||||||
t->bang ();
|
t->bang ();
|
||||||
|
|
||||||
} else if ((*ev).is_note_off()) {
|
} else if ((*ev).is_note_off()) {
|
||||||
|
|
||||||
|
std:: cerr << "unbang\n";
|
||||||
t->unbang ();
|
t->unbang ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user