triggerbox: cleanup parts of Trigger API and finish initial pass of MidiTrigger implementation

This commit is contained in:
Paul Davis 2021-10-24 09:27:14 -06:00
parent 2b754568a0
commit 8fd25e15c0
3 changed files with 88 additions and 37 deletions

View file

@ -80,7 +80,6 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
virtual void set_start (timepos_t const &) = 0; virtual void set_start (timepos_t const &) = 0;
virtual void set_end (timepos_t const &) = 0; virtual void set_end (timepos_t const &) = 0;
/* this accepts timepos_t because the origin is assumed to be the start */
virtual void set_length (timecnt_t const &) = 0; virtual void set_length (timecnt_t const &) = 0;
virtual void tempo_map_change () = 0; virtual void tempo_map_change () = 0;
@ -90,7 +89,6 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
bool use_follow() const { return _use_follow; } bool use_follow() const { return _use_follow; }
timepos_t start_offset () const; /* offset from start of data */ timepos_t start_offset () const; /* offset from start of data */
timepos_t end() const; /* offset from start of data */
virtual timepos_t current_length() const = 0; /* offset from start() */ virtual timepos_t current_length() const = 0; /* offset from start() */
virtual timepos_t natural_length() const = 0; /* offset from start() */ virtual timepos_t natural_length() const = 0; /* offset from start() */
@ -230,10 +228,8 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
void set_end (timepos_t const &); void set_end (timepos_t const &);
void set_legato_offset (timepos_t const &); void set_legato_offset (timepos_t const &);
timepos_t current_pos() 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 &); void set_length (timecnt_t const &);
timepos_t start_offset () const { return timepos_t (_start_offset); } /* offset from start of data */ timepos_t start_offset () const { return timepos_t (_start_offset); } /* offset from start of data */
timepos_t end() const; /* offset from start of data */
timepos_t current_length() 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 */ timepos_t natural_length() const; /* offset from start of data */
@ -281,7 +277,6 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
void set_end (timepos_t const &); void set_end (timepos_t const &);
void set_legato_offset (timepos_t const &); void set_legato_offset (timepos_t const &);
timepos_t current_pos() 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 &); void set_length (timecnt_t const &);
timepos_t start_offset () const; timepos_t start_offset () const;
timepos_t end() const; /* offset from start of data */ timepos_t end() const; /* offset from start of data */
@ -306,13 +301,16 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
private: private:
PBD::ID data_source; PBD::ID data_source;
RTMidiBuffer* data; RTMidiBuffer data;
Temporal::Beats last_read;
Temporal::Beats data_length; size_t read_index; /* index into data */
samplepos_t start_run_sample;
samplepos_t end_run_sample;
samplecnt_t data_length; /* using timestamps from data */
samplecnt_t usable_length; /* using timestamps from data */
Temporal::BBT_Offset _start_offset; Temporal::BBT_Offset _start_offset;
Temporal::BBT_Offset _legato_offset; Temporal::BBT_Offset _legato_offset;
Temporal::Beats usable_length;
Temporal::Beats last;
void drop_data (); void drop_data ();
int load_data (boost::shared_ptr<MidiRegion>); int load_data (boost::shared_ptr<MidiRegion>);

View file

@ -294,7 +294,6 @@ MidiRegion::render (Evoral::EventSink<samplepos_t>& dst,
return 0; /* read nothing */ return 0; /* read nothing */
} }
/* render() pulls from zero to infinity ... */ /* render() pulls from zero to infinity ... */
if (!position().zero()) { if (!position().zero()) {

View file

@ -563,12 +563,6 @@ AudioTrigger::current_pos() const
return timepos_t (read_index); return timepos_t (read_index);
} }
timepos_t
AudioTrigger::end() const
{
return timepos_t (_start_offset + usable_length);
}
void void
AudioTrigger::set_length (timecnt_t const & newlen) AudioTrigger::set_length (timecnt_t const & newlen)
{ {
@ -948,21 +942,47 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo
/*--------------------*/ /*--------------------*/
/* Design notes:
for the ::run() call, where we are in process() context, we will use an
RTMidiBuffer as the data structure holding our MIDI. The events here form a
simple array of sample-timestamped MIDI events (though with the extra
complication of having to handle 2/3 byte messages *slightly* differently
from larger messages).
This allows us to actually use a simple integer array index to record where
we are during playback and know when we've reached the end.
However, attributes of the trigger like start_offset are kept in BBT_Offsets
or Beats as appropriate, because those are the correct temporal
semantics. This means that we need to refer back to the Region for some
things, since it will be the place where we can look at MIDI events with
musical time stamps (unlike the sample timestamps in the RTMidiBuffer).
To keep things simple, this means that we will only render the actual clip
into the RTMidiBuffer - if start_offset/length reduce the clip from the
Region bounds, we will not place the "excess" in the RTMidiBuffer.
However, if we do any UI display of the clip, we will use the Region for
that, partly because it has music time timestamps and partly because we
already have GUI objects that can operate on MIDIRegions.
*/
MIDITrigger::MIDITrigger (uint64_t n, TriggerBox& b) MIDITrigger::MIDITrigger (uint64_t n, TriggerBox& b)
: Trigger (n, b) : Trigger (n, b)
, data (0) , read_index (0)
, last_read (0, 0) , start_run_sample (0)
, data_length (0, 0) , end_run_sample (0)
, data_length (0)
, usable_length (0)
, _start_offset (0, 0, 0) , _start_offset (0, 0, 0)
, _legato_offset (0, 0, 0) , _legato_offset (0, 0, 0)
, usable_length (0, 0)
, last (0, 0)
{ {
} }
MIDITrigger::~MIDITrigger () MIDITrigger::~MIDITrigger ()
{ {
delete data;
} }
void void
@ -993,10 +1013,13 @@ MIDITrigger::position_as_fraction () const
return 0.0; return 0.0;
} }
Temporal::DoubleableBeats lr (last_read); if (data.size() == 0) {
Temporal::DoubleableBeats ul (usable_length); return 0.0;
}
return lr.to_double() / ul.to_double(); const samplepos_t l = data[read_index].timestamp;
return l / (double) usable_length;
} }
XMLNode& XMLNode&
@ -1025,7 +1048,7 @@ MIDITrigger::set_state (const XMLNode& node, int version)
_start_offset = Temporal::BBT_Offset (0, b.get_beats(), b.get_ticks()); _start_offset = Temporal::BBT_Offset (0, b.get_beats(), b.get_ticks());
node.get_property (X_("length"), t); node.get_property (X_("length"), t);
usable_length = t.beats(); usable_length = t.samples();
return 0; return 0;
} }
@ -1054,21 +1077,24 @@ MIDITrigger::set_legato_offset (timepos_t const & offset)
} }
timepos_t timepos_t
MIDITrigger::current_pos() const MIDITrigger::start_offset () const
{ {
return timepos_t (last_read); /* XXX single meter assumption */
Temporal::Meter const &m = Temporal::TempoMap::use()->meter_at (Temporal::Beats (0, 0));
return timepos_t (m.to_quarters (_start_offset));
} }
timepos_t timepos_t
MIDITrigger::end() const MIDITrigger::current_pos() const
{ {
/* XXX need to handle bar offsets */ return timepos_t (data[read_index].timestamp);
return timepos_t (Temporal::Beats (_start_offset.beats, _start_offset.ticks) + usable_length);
} }
void void
MIDITrigger::set_length (timecnt_t const & newlen) MIDITrigger::set_length (timecnt_t const & newlen)
{ {
} }
void void
@ -1094,7 +1120,7 @@ MIDITrigger::set_usable_length ()
/* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */ /* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */
timecnt_t len (Temporal::Beats (_quantization.beats, _quantization.ticks), timepos_t (Temporal::Beats())); timecnt_t len (Temporal::Beats (_quantization.beats, _quantization.ticks), timepos_t (Temporal::Beats()));
usable_length = len.beats(); usable_length = len.samples ();
} }
timepos_t timepos_t
@ -1141,12 +1167,15 @@ MIDITrigger::tempo_map_change ()
void void
MIDITrigger::drop_data () MIDITrigger::drop_data ()
{ {
delete data; data.clear ();
} }
int int
MIDITrigger::load_data (boost::shared_ptr<MidiRegion> mr) MIDITrigger::load_data (boost::shared_ptr<MidiRegion> mr)
{ {
drop_data ();
mr->render (data, 0, Sustained, 0);
data_length = data.span();
return 0; return 0;
} }
@ -1154,15 +1183,40 @@ void
MIDITrigger::retrigger () MIDITrigger::retrigger ()
{ {
/* XXX need to deal with bar offsets */ /* XXX need to deal with bar offsets */
const Temporal::BBT_Offset o = _start_offset + _legato_offset; // const Temporal::BBT_Offset o = _start_offset + _legato_offset;
last_read = Temporal::Beats (o.beats, o.ticks); read_index = 0;
end_run_sample = 0;
_legato_offset = Temporal::BBT_Offset (); _legato_offset = Temporal::BBT_Offset ();
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, last_read)); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, read_index));
} }
int int
MIDITrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bool first) MIDITrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bool first)
{ {
MidiBuffer& mb (bufs.get_midi (0));
end_run_sample += nframes;
while (read_index < data.size()) {
RTMidiBuffer::Item const & item (data[read_index]);
samplepos_t effective_time = start_run_sample + item.timestamp;
if (effective_time >= end_run_sample) {
break;
}
uint32_t sz;
uint8_t const * bytes = data.bytes (item, sz);
const Evoral::Event<MidiBuffer::TimeType> ev (Evoral::MIDI_EVENT, item.timestamp, sz, const_cast<uint8_t*>(bytes), false);
mb.insert_event (ev);
// _tracker.track (bytes);
read_index++;
}
return 0; return 0;
} }