modify MIDITrigger to use an RTMidiBuffer<Beats,Beats> for playback, not a MidiModel
Creating an iterator on a MidiModel (Sequence) creates a read-lock on the same, which exists until the iterator is destroyed. This new designs renders the model/source to an RTMidiBuffer, then atomically swaps in a new RTMidiBuffer in an RT context. Not yet implemented in this temporary branch is handling required state-changing messages when the buffer is swapped.
This commit is contained in:
parent
a4a277c141
commit
e3d790207f
@ -48,6 +48,7 @@
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/processor.h"
|
||||
#include "ardour/rt_midibuffer.h"
|
||||
#include "ardour/segment_descriptor.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/types_convert.h"
|
||||
@ -415,6 +416,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
||||
void get_ui_state (UIState &state) const;
|
||||
void set_ui_state (UIState &state);
|
||||
|
||||
virtual void check_edit_swap (timepos_t const & time, bool playing, BufferSet& bufs) {}
|
||||
|
||||
static PBD::Signal2<void,PBD::PropertyChange,Trigger*> TriggerPropertyChange;
|
||||
|
||||
protected:
|
||||
@ -624,6 +627,8 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
||||
int channel_map (int channel);
|
||||
std::vector<int> const & channel_map() const { return _channel_map; }
|
||||
|
||||
void check_edit_swap (timepos_t const &, bool playing, BufferSet&);
|
||||
|
||||
protected:
|
||||
void retrigger ();
|
||||
|
||||
@ -640,9 +645,14 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
||||
Temporal::BBT_Offset _start_offset;
|
||||
Temporal::BBT_Offset _legato_offset;
|
||||
|
||||
std::shared_ptr<MidiModel> model;
|
||||
MidiModel::const_iterator iter;
|
||||
bool map_change;
|
||||
typedef RTMidiBufferBase<Temporal::Beats,Temporal::Beats> RTMidiBufferBeats;
|
||||
|
||||
std::atomic<RTMidiBufferBeats*> rt_midibuffer;
|
||||
std::atomic<RTMidiBufferBeats*> pending_rt_midibuffer;
|
||||
std::atomic<RTMidiBufferBeats*> old_rt_midibuffer;
|
||||
uint32_t iter;
|
||||
|
||||
bool map_change;
|
||||
|
||||
int load_data (std::shared_ptr<MidiRegion>);
|
||||
void compute_and_set_length ();
|
||||
|
@ -44,7 +44,9 @@
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/import_status.h"
|
||||
#include "ardour/midi_buffer.h"
|
||||
#include "ardour/midi_cursor.h"
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/midi_region.h"
|
||||
#include "ardour/minibpm.h"
|
||||
#include "ardour/port.h"
|
||||
@ -1270,6 +1272,31 @@ AudioTrigger::~AudioTrigger ()
|
||||
delete _stretcher;
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::check_edit_swap (timepos_t const & time, bool playing, BufferSet& bufs)
|
||||
{
|
||||
RTMidiBufferBeats* pending = pending_rt_midibuffer.load();
|
||||
|
||||
if (!pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if (playing) {
|
||||
MidiBuffer& mbuf (bufs.get_midi (0));
|
||||
MidiStateTracker post_edit_state;
|
||||
|
||||
const timepos_t region_start_time = _region->start();
|
||||
const Temporal::Beats region_start = region_start_time.beats();
|
||||
pending->track_state (transition_beats + (time.beats() - region_start), post_edit_state);
|
||||
// _box.tracker->resolve_diff (post_edit_state, mbuf, time.samples());
|
||||
// }
|
||||
|
||||
old_rt_midibuffer = rt_midibuffer.exchange (pending);
|
||||
std::cerr << "Swapped in new pending RT MB\n";
|
||||
pending_rt_midibuffer = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioTrigger::set_stretch_mode (Trigger::StretchMode sm)
|
||||
{
|
||||
@ -2174,6 +2201,9 @@ MIDITrigger::MIDITrigger (uint32_t n, TriggerBox& b)
|
||||
, last_event_samples (0)
|
||||
, _start_offset (0, 0, 0)
|
||||
, _legato_offset (0, 0, 0)
|
||||
, rt_midibuffer (nullptr)
|
||||
, pending_rt_midibuffer (nullptr)
|
||||
, old_rt_midibuffer (nullptr)
|
||||
, map_change (false)
|
||||
{
|
||||
_channel_map.assign (16, -1);
|
||||
@ -2664,7 +2694,13 @@ MIDITrigger::set_region_in_worker_thread (std::shared_ptr<Region> r)
|
||||
data_length = mr->length().beats();
|
||||
_follow_length = Temporal::BBT_Offset (0, data_length.get_beats(), 0);
|
||||
set_length (mr->length());
|
||||
model = mr->model ();
|
||||
|
||||
pending_rt_midibuffer = new RTMidiBufferBeats;
|
||||
|
||||
{
|
||||
Source::ReaderLock lm (mr->midi_source()->mutex());
|
||||
mr->midi_source()->render (lm, *pending_rt_midibuffer);
|
||||
}
|
||||
|
||||
estimate_midi_patches ();
|
||||
|
||||
@ -2687,11 +2723,11 @@ MIDITrigger::retrigger ()
|
||||
|
||||
/* XXX need to deal with bar offsets */
|
||||
// const Temporal::BBT_Offset o = _start_offset + _legato_offset;
|
||||
iter = model->begin();
|
||||
iter = 0;
|
||||
_legato_offset = Temporal::BBT_Offset ();
|
||||
last_event_beats = Temporal::Beats();
|
||||
last_event_samples = 0;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2, ts = %3\n", _index, iter->time(), transition_beats));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to start, ts = %2\n", _index, transition_beats));
|
||||
}
|
||||
|
||||
void
|
||||
@ -2706,12 +2742,13 @@ MIDITrigger::tempo_map_changed ()
|
||||
* on an active trigger.
|
||||
*/
|
||||
|
||||
iter = model->begin();
|
||||
iter = 0;
|
||||
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
|
||||
const timepos_t region_start_time = _region->start();
|
||||
const Temporal::Beats region_start = region_start_time.beats();
|
||||
RTMidiBufferBeats* rtmb (rt_midibuffer.load());
|
||||
|
||||
while (iter != model->end()) {
|
||||
while (iter < rtmb->size()) {
|
||||
|
||||
/* Find the first event whose sample time is equal-to or
|
||||
* greater than the last played event sample. That is the
|
||||
@ -2722,7 +2759,9 @@ MIDITrigger::tempo_map_changed ()
|
||||
* one.
|
||||
*/
|
||||
|
||||
const Temporal::Beats iter_timeline_beats = transition_beats + ((*iter).time() - region_start);
|
||||
RTMidiBufferBeats::Item const & item ((*rtmb)[iter]);
|
||||
// const Temporal::Beats iter_timeline_beats =
|
||||
Temporal::Beats iter_timeline_beats = transition_beats + (item.timestamp - region_start);
|
||||
samplepos_t iter_timeline_samples = tmap->sample_at (iter_timeline_beats);
|
||||
|
||||
if (iter_timeline_samples >= last_event_samples) {
|
||||
@ -2732,7 +2771,7 @@ MIDITrigger::tempo_map_changed ()
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (iter != model->end()) {
|
||||
if (iter < rtmb->size()) {
|
||||
Temporal::Beats elen_ignored;
|
||||
(void) compute_end (tmap, _transition_bbt, transition_samples, elen_ignored);
|
||||
}
|
||||
@ -2746,6 +2785,8 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
Temporal::Beats const & start_beats, Temporal::Beats const & end_beats,
|
||||
pframes_t nframes, pframes_t dest_offset, double bpm, pframes_t& quantize_offset)
|
||||
{
|
||||
assert (rt_midibuffer);
|
||||
|
||||
MidiBuffer* mb (in_process_context? &bufs.get_midi (0) : 0);
|
||||
typedef Evoral::Event<MidiModel::TimeType> MidiEvent;
|
||||
const timepos_t region_start_time = _region->start();
|
||||
@ -2758,8 +2799,9 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
quantize_offset = 0;
|
||||
maybe_compute_next_transition (start_sample, start_beats, end_beats, nframes, quantize_offset);
|
||||
const pframes_t orig_nframes = nframes;
|
||||
RTMidiBufferBeats* rtmb (rt_midibuffer.load());
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 after checking for transition, state = %2\n", name(), enum_2_string (_state)));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 after checking for transition, state = %2 iter @ %3 total %4\n", name(), enum_2_string (_state), iter, rtmb->size()));
|
||||
|
||||
switch (_state) {
|
||||
case Stopped:
|
||||
@ -2775,18 +2817,17 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
|
||||
Temporal::Beats last_event_timeline_beats = final_beat; /* will indicate "done" if there is nothing to do */
|
||||
|
||||
while (iter != model->end() && !_playout) {
|
||||
while (iter < rtmb->size() && !_playout) {
|
||||
|
||||
MidiEvent const & event (*iter);
|
||||
RTMidiBufferBeats::Item const & item ((*rtmb)[iter]);
|
||||
|
||||
/* Event times are in beats, relative to start of source
|
||||
* file. We need to convert to region-relative time, and then
|
||||
* file. We need to conv+ert to region-relative time, and then
|
||||
* a session timeline time, which is defined by the time at
|
||||
* which we last transitioned (in this case, to being active)
|
||||
*/
|
||||
|
||||
Temporal::Beats maybe_last_event_timeline_beats = transition_beats + (event.time() - region_start);
|
||||
|
||||
Temporal::Beats maybe_last_event_timeline_beats = transition_beats + (item.timestamp - region_start);
|
||||
|
||||
/* check that the event is within the bounds for this run() call */
|
||||
|
||||
@ -2795,7 +2836,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
}
|
||||
|
||||
if (maybe_last_event_timeline_beats > final_beat) {
|
||||
iter = model->end();
|
||||
iter = rtmb->size();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2807,6 +2848,9 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
|
||||
const samplepos_t timeline_samples = tmap->sample_at (maybe_last_event_timeline_beats);
|
||||
|
||||
uint32_t evsize;
|
||||
uint8_t const * buf = rtmb->bytes (item, evsize);
|
||||
|
||||
if (in_process_context) { /* compile-time const expr */
|
||||
|
||||
/* Now we have to convert to a position within the buffer we
|
||||
@ -2864,7 +2908,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
|
||||
assert (buffer_samples >= 0);
|
||||
|
||||
Evoral::Event<MidiBuffer::TimeType> ev (Evoral::MIDI_EVENT, buffer_samples, event.size(), const_cast<uint8_t*>(event.buffer()), false);
|
||||
Evoral::Event<MidiBuffer::TimeType> ev (Evoral::MIDI_EVENT, buffer_samples, evsize, const_cast<uint8_t*>(buf), false);
|
||||
|
||||
if (_gain != 1.0f && ev.is_note()) {
|
||||
ev.scale_velocity (_gain);
|
||||
@ -2900,9 +2944,9 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
mb->insert_event (ev);
|
||||
}
|
||||
|
||||
_box.tracker->track (event.buffer());
|
||||
_box.tracker->track (buf);
|
||||
|
||||
last_event_beats = event.time();
|
||||
last_event_beats = item.timestamp;
|
||||
last_event_timeline_beats = maybe_last_event_timeline_beats;
|
||||
|
||||
++iter;
|
||||
@ -2914,7 +2958,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
_box.tracker->resolve_notes (*mb, nframes-1);
|
||||
}
|
||||
|
||||
if (iter == model->end()) {
|
||||
if (iter >= rtmb->size()) {
|
||||
|
||||
/* We reached the end */
|
||||
|
||||
@ -2922,8 +2966,8 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
|
||||
/* "final_beat" is an inclusive end of the trigger, not
|
||||
* exclusive, so we must use <= here. That is, any last event
|
||||
* (remember, iter == model->end() here, so we have already read
|
||||
* through the entire MIDI model) that is up to AND INCLUDING
|
||||
* (remember, iter == rt_midibuffer->size() here, so we have already read
|
||||
* through the entire RTMidiBuffer) that is up to AND INCLUDING
|
||||
* final_beat counts as "haven't reached the end".
|
||||
*/
|
||||
|
||||
@ -2981,7 +3025,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
/* we didn't reach the end of the MIDI data, ergo we covered
|
||||
the entire timespan passed into us.
|
||||
*/
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 did not reach end, nframes left at %2, next event is %3\n", index(), nframes, *iter));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 did not reach end, nframes left at %2, next event is %3\n", index(), nframes, iter));
|
||||
nframes = 0;
|
||||
}
|
||||
|
||||
@ -4217,6 +4261,10 @@ TriggerBox::run_cycle (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
||||
}
|
||||
}
|
||||
|
||||
for (uint64_t n = 0; n < all_triggers.size(); ++n) {
|
||||
all_triggers[n]->check_edit_swap (timepos_t (start_sample), _currently_playing == all_triggers[n], bufs);
|
||||
}
|
||||
|
||||
/* STEP FOUR: handle any incoming requests from the GUI or other
|
||||
* non-MIDI UIs
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user