From d4ef49b504dfe1328bd9a61e2466837ca490f78b Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 20 Oct 2021 16:19:09 -0600 Subject: [PATCH] triggerbox: skeleton framework for MIDI triggers (non-functional) --- libs/ardour/ardour/triggerbox.h | 55 ++++++++ libs/ardour/triggerbox.cc | 222 ++++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 2681e0828a..2aa634fc3b 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -37,6 +37,8 @@ #include "temporal/tempo.h" #include "ardour/processor.h" +#include "ardour/rt_midibuffer.h" + #include "ardour/libardour_visibility.h" class XMLNode; @@ -46,6 +48,7 @@ namespace ARDOUR { class Session; class AudioRegion; +class MidiRegion; class TriggerBox; class SideChain; @@ -266,6 +269,58 @@ class LIBARDOUR_API AudioTrigger : public Trigger { void compute_and_set_length (); }; + +class LIBARDOUR_API MIDITrigger : public Trigger { + public: + MIDITrigger (uint64_t index, TriggerBox&); + ~MIDITrigger (); + + int run (BufferSet&, pframes_t nframes, pframes_t offset, bool first); + + void set_start (timepos_t const &); + void set_end (timepos_t const &); + void set_legato_offset (timepos_t const &); + timepos_t current_pos() const; + /* this accepts timepos_t because the origin is assumed to be the start */ + void set_length (timecnt_t const &); + timepos_t start_offset () const; + timepos_t end() const; /* offset from start of data */ + timepos_t current_length() const; /* offset from start of data */ + timepos_t natural_length() const; /* offset from start of data */ + + double position_as_fraction() const; + + int set_region (boost::shared_ptr); + void startup (); + void jump_start (); + void jump_stop (); + + XMLNode& get_state (void); + int set_state (const XMLNode&, int version); + + void tempo_map_change (); + + protected: + void retrigger (); + void set_usable_length (); + + private: + PBD::ID data_source; + RTMidiBuffer* data; + Temporal::Beats last_read; + Temporal::Beats data_length; + Temporal::BBT_Offset _start_offset; + Temporal::BBT_Offset _legato_offset; + Temporal::Beats usable_length; + Temporal::Beats last; + + void drop_data (); + int load_data (boost::shared_ptr); + RunResult at_end (); + void compute_and_set_length (); +}; + + class LIBARDOUR_API TriggerBox : public Processor { public: diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 48e9e3fbd6..b93c63f354 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -16,6 +16,7 @@ #include "ardour/audio_buffer.h" #include "ardour/debug.h" #include "ardour/midi_buffer.h" +#include "ardour/midi_region.h" #include "ardour/minibpm.h" #include "ardour/port.h" #include "ardour/region_factory.h" @@ -944,6 +945,227 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo return 0; } + +/*--------------------*/ + +MIDITrigger::MIDITrigger (uint64_t n, TriggerBox& b) + : Trigger (n, b) + , data (0) + , last_read (0, 0) + , data_length (0, 0) + , _start_offset (0, 0, 0) + , _legato_offset (0, 0, 0) + , usable_length (0, 0) + , last (0, 0) +{ +} + +MIDITrigger::~MIDITrigger () +{ + delete data; +} + +void +MIDITrigger::startup () +{ + Trigger::startup (); + retrigger (); +} + +void +MIDITrigger::jump_start () +{ + Trigger::jump_start (); + retrigger (); +} + +void +MIDITrigger::jump_stop () +{ + Trigger::jump_stop (); + retrigger (); +} + +double +MIDITrigger::position_as_fraction () const +{ + if (!active()) { + return 0.0; + } + + Temporal::DoubleableBeats lr (last_read); + Temporal::DoubleableBeats ul (usable_length); + + return lr.to_double() / ul.to_double(); +} + +XMLNode& +MIDITrigger::get_state (void) +{ + XMLNode& node (Trigger::get_state()); + + node.set_property (X_("start"), start_offset()); + node.set_property (X_("length"), timepos_t (usable_length)); + + return node; +} + +int +MIDITrigger::set_state (const XMLNode& node, int version) +{ + timepos_t t; + + if (!Trigger::set_state (node, version)) { + return -1; + } + + node.get_property (X_("start"), t); + Temporal::Beats b (t.beats()); + /* XXX need to deal with bar offsets */ + _start_offset = Temporal::BBT_Offset (0, b.get_beats(), b.get_ticks()); + + node.get_property (X_("length"), t); + usable_length = t.beats(); + + return 0; +} + +void +MIDITrigger::set_start (timepos_t const & s) +{ + /* XXX need to handle bar offsets */ + Temporal::Beats b (s.beats()); + _start_offset = Temporal::BBT_Offset (0, b.get_beats(), b.get_ticks()); +} + +void +MIDITrigger::set_end (timepos_t const & e) +{ + /* XXX need to handle bar offsets */ + set_length (timecnt_t (e.beats() - Temporal::Beats (_start_offset.beats, _start_offset.ticks), start_offset())); +} + +void +MIDITrigger::set_legato_offset (timepos_t const & offset) +{ + /* XXX need to handle bar offsets */ + Temporal::Beats b (offset.beats()); + _legato_offset = Temporal::BBT_Offset (0, b.get_beats(), b.get_ticks()); +} + +timepos_t +MIDITrigger::current_pos() const +{ + return timepos_t (last_read); +} + +timepos_t +MIDITrigger::end() const +{ + /* XXX need to handle bar offsets */ + return timepos_t (Temporal::Beats (_start_offset.beats, _start_offset.ticks) + usable_length); +} + +void +MIDITrigger::set_length (timecnt_t const & newlen) +{ +} + +void +MIDITrigger::set_usable_length () +{ + if (!_region) { + return; + } + + switch (_launch_style) { + case Repeat: + break; + default: + usable_length = data_length; + return; + } + + if (_quantization == Temporal::BBT_Offset ()) { + usable_length = data_length; + return; + } + + /* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */ + + timecnt_t len (Temporal::Beats (_quantization.beats, _quantization.ticks), timepos_t (Temporal::Beats())); + usable_length = len.beats(); +} + +timepos_t +MIDITrigger::current_length() const +{ + if (_region) { + return timepos_t (data_length); + } + return timepos_t (Temporal::BeatTime); +} + +timepos_t +MIDITrigger::natural_length() const +{ + if (_region) { + return timepos_t::from_ticks (_region->length().magnitude()); + } + return timepos_t (Temporal::BeatTime); +} + +int +MIDITrigger::set_region (boost::shared_ptr r) +{ + boost::shared_ptr mr = boost::dynamic_pointer_cast (r); + + if (!mr) { + return -1; + } + + set_region_internal (r); + load_data (mr); + set_length (mr->length()); + + PropertyChanged (ARDOUR::Properties::name); + + return 0; +} + +void +MIDITrigger::tempo_map_change () +{ +} + +void +MIDITrigger::drop_data () +{ + delete data; +} + +int +MIDITrigger::load_data (boost::shared_ptr mr) +{ + return 0; +} + +void +MIDITrigger::retrigger () +{ + /* XXX need to deal with bar offsets */ + const Temporal::BBT_Offset o = _start_offset + _legato_offset; + last_read = Temporal::Beats (o.beats, o.ticks); + _legato_offset = Temporal::BBT_Offset (); + DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, last_read)); +} + +int +MIDITrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bool first) +{ + return 0; +} + /**************/ void