a variety of fixes for MIDI clip recording, and extended API to specify capture period

This commit is contained in:
Paul Davis 2025-03-21 20:52:42 -06:00
parent 62d47d63a6
commit bb2812f272
2 changed files with 62 additions and 37 deletions

View file

@ -297,7 +297,9 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
double position_as_fraction() const;
virtual void captured (SlotArmInfo&, BufferSet&) {}
virtual void arm();
void arm (Temporal::BBT_Offset duration = Temporal::BBT_Offset()) {
_arm (duration);
}
virtual void disarm ();
bool armed() const { return _armed; }
PBD::Signal<void()> ArmChanged;
@ -501,6 +503,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
bool internal_use_follow_length() const;
void send_property_change (PBD::PropertyChange pc);
virtual void _arm (Temporal::BBT_Offset const &);
};
class LIBARDOUR_API AudioTrigger : public Trigger {
@ -612,7 +616,6 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
bool playable() const { return rt_midibuffer.load() || _region; }
void captured (SlotArmInfo&, BufferSet&);
void arm();
void disarm ();
template<bool actually_run> pframes_t midi_run (BufferSet&, samplepos_t start_sample, samplepos_t end_sample,
@ -682,6 +685,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
protected:
void retrigger ();
void _arm (Temporal::BBT_Offset const &);
private:
PBD::ID data_source;
@ -804,10 +808,12 @@ struct CueRecord {
typedef PBD::RingBuffer<CueRecord> CueRecords;
struct SlotArmInfo {
SlotArmInfo (Trigger& s);
SlotArmInfo ();
~SlotArmInfo();
Trigger& slot;
void reset (Trigger&);
Trigger* slot;
Temporal::Beats start_beats;
samplepos_t start_samples;
Temporal::Beats end_beats;
@ -841,7 +847,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
PBD::Signal<void()> RecEnableChanged;
static PBD::Signal<void()> TriggerRecEnableChanged;
void arm_from_another_thread (Trigger& slot, samplepos_t, uint32_t chans);
void arm_from_another_thread (Trigger& slot, samplepos_t, uint32_t chans, Temporal::BBT_Offset const &);
void disarm();
void disarm_all();
bool armed() const { return (bool) _arm_info.load(); }
@ -1062,6 +1068,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
PBD::ScopedConnection stop_all_connection;
std::atomic<SlotArmInfo*> _arm_info;
SlotArmInfo _the_arm_info;
/** A buffer that we use to put newly-arrived MIDI data in for
* the GUI to read (so that it can update itself).

View file

@ -36,6 +36,7 @@
#include "pbd/unwind.h"
#include "temporal/tempo.h"
#include "temporal/bbt_time.h"
#include "ardour/amp.h"
#include "ardour/async_midi_port.h"
@ -253,7 +254,7 @@ Trigger::Trigger (uint32_t n, TriggerBox& b)
, _beatcnt (0.)
, _meter (4, 4)
, expected_end_sample (0)
, _pending ((Trigger*) 0)
, _pending (nullptr)
, last_property_generation (0)
{
add_property (_launch_style);
@ -290,7 +291,7 @@ Trigger::request_trigger_delete (Trigger* t)
}
void
Trigger::arm ()
Trigger::_arm (Temporal::BBT_Offset const & duration)
{
if (_box.record_enabled() == Recording) {
return;
@ -309,7 +310,7 @@ Trigger::arm ()
chns = 0;
}
_box.arm_from_another_thread (*this, _box.session().transport_sample(), chns);
_box.arm_from_another_thread (*this, _box.session().transport_sample(), chns, duration);
_armed = true;
ArmChanged(); /* EMIT SIGNAL */
TriggerArmChanged (this); /* EMIT SIGNAL */
@ -1945,7 +1946,6 @@ AudioTrigger::captured (SlotArmInfo& ai, BufferSet&)
/* Nothing captured */
_armed = false;
ArmChanged (); /* EMIT SIGNAL */
delete &ai;
return;
}
@ -1977,14 +1977,10 @@ AudioTrigger::captured (SlotArmInfo& ai, BufferSet&)
/* adopt the previously allocated stretcher, with a ratio of 1.0 (no stretch) */
delete _stretcher;
_stretcher = ai.stretcher;
std::swap (_stretcher, ai.stretcher);
_stretcher->setMaxProcessSize (rb_blocksize);
_stretcher->setTimeRatio (1.0);
ai.stretcher = nullptr;
delete &ai; // XXX delete is not RT-safe
_box.queue_explict (index());
TriggerBox::worker->request_build_source (this, timecnt_t (data.length));
@ -2477,7 +2473,7 @@ MIDITrigger::setup_event_indices ()
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 first index %3 last index %4 of %5\n", _box.order(), index(), first_event_index, last_event_index, rt->size()));
}
void
void
MIDITrigger::adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool from_region)
{
if (!from_region && _region) {
@ -2504,9 +2500,9 @@ MIDITrigger::adjust_bounds (Temporal::Beats const & start, Temporal::Beats const
}
void
MIDITrigger::arm ()
MIDITrigger::_arm (Temporal::BBT_Offset const & duration)
{
Trigger::arm ();
Trigger::_arm (duration);
}
void
@ -2521,16 +2517,14 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
if (ai.midi_buf->size() == 0) {
_armed = false;
ArmChanged(); /* EMIT SIGNAL */
delete &ai;
return;
}
/* Note: the original MIDI buffer in ai is now invalid, all data has
* been moved to rtmb.
/* Move ownership of the MIDI buffer from the SlotArmInfo (where it was
* captured) to our own rt_midibuffer pointer.
*/
#warning what to do about the old RT midi buffer
// old_rt_midibuffer = rt_midibuffer.exchange (ai.midi_buf);
ai.midi_buf = rt_midibuffer.exchange (ai.midi_buf);
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
timecnt_t dur = tmap->convert_duration (timecnt_t (ai.captured), timepos_t (ai.start_samples), Temporal::BeatTime);
@ -2540,10 +2534,6 @@ MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs)
iter = 0;
_follow_action0 = FollowAction::Again;
/* Mark ai.midi_buf as null so that it is not deleted */
ai.midi_buf = nullptr;
delete &ai;
/* start playing */
_box.queue_explict (index());
@ -3543,8 +3533,8 @@ Trigger::make_property_quarks ()
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for queued = %1\n", Properties::queued.property_id));
}
SlotArmInfo::SlotArmInfo (Trigger& s)
: slot (s)
SlotArmInfo::SlotArmInfo ()
: slot (nullptr)
, start_samples (0)
, end_samples (0)
, captured (0)
@ -3559,6 +3549,22 @@ SlotArmInfo::~SlotArmInfo()
delete stretcher;
}
void
SlotArmInfo::reset (Trigger& s)
{
slot = &s;
delete midi_buf;
midi_buf = nullptr;
delete stretcher;
stretcher = nullptr;
start_samples = 0;
end_samples = 0;
start_beats = Temporal::Beats();
end_beats = Temporal::Beats();
captured = 0;
}
Temporal::BBT_Offset TriggerBox::_assumed_trigger_duration (4, 0, 0);
TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::Custom);
int TriggerBox::_first_midi_note = 60;
@ -3652,11 +3658,17 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
}
void
TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t chans)
TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t chans, Temporal::BBT_Offset const & duration)
{
using namespace Temporal;
SlotArmInfo* ai = new SlotArmInfo (slot);
SlotArmInfo* ai = &_the_arm_info;
/* Delete any dangling RTMidiBuffer and Stretcher from previous capture
* passes
*/
ai->reset (slot);
if (_data_type == DataType::MIDI) {
ai->midi_buf = new RTMidiBufferBeats;
@ -3682,7 +3694,12 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
ai->start_samples = t_samples;
ai->start_beats = t_beats;
// std::cerr << "Will start at " << t_beats.str() << std::endl;
if (!duration) {
timepos_t sb (ai->start_beats);
sb += duration;
ai->end_beats = sb.beats ();
ai->end_samples = timepos_t (ai->end_beats).samples();
}
ai->captured = 0;
@ -3692,7 +3709,6 @@ TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, uint32_t ch
void
TriggerBox::disarm ()
{
delete _arm_info;
_arm_info = nullptr;
}
@ -3713,7 +3729,7 @@ TriggerBox::finish_recording (BufferSet& bufs)
/* This transfers responsibility for the SlotArmInfo object to the
trigger
*/
ai->slot.captured (*ai, bufs);
ai->slot->captured (*ai, bufs);
_arm_info = nullptr;
_record_state = Enabled;
}
@ -3732,8 +3748,8 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
pframes_t offset = 0;
bool reached_end = false;
if (!ai->slot.armed()) {
/* since _arm_info is set, we have been capturing for a lot,
if (!ai->slot->armed()) {
/* since _arm_info is set, we have been capturing for a slot,
but now the slot is no longer armed.
*/
if (!ai->end_samples) {
@ -3751,8 +3767,8 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
TempoMap::SharedPtr tmap (TempoMap::use());
Beats now_beats = tmap->quarters_at (timepos_t (start_sample));
ai->slot.compute_quantized_transition (start_sample, now_beats, std::numeric_limits<Beats>::max(),
t_bbt, t_beats, t_samples, tmap, ai->slot.quantization());
ai->slot->compute_quantized_transition (start_sample, now_beats, std::numeric_limits<Beats>::max(),
t_bbt, t_beats, t_samples, tmap, ai->slot->quantization());
ai->end_samples = t_samples;
ai->end_beats = t_beats;
@ -3769,6 +3785,8 @@ TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_
return;
}
std::cerr << "Ok, maybe here we go ...\n";
if (ai->end_samples != 0 && (start_sample > ai->end_samples)) {
return;
}