continued work on getting audio clip bounds editing to work

This commit is contained in:
Paul Davis
2025-11-21 22:39:04 -07:00
parent c6b3694441
commit 48f625e7ef
2 changed files with 88 additions and 76 deletions

View File

@@ -436,7 +436,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
static PBD::Signal<void(PBD::PropertyChange,Trigger*)> TriggerPropertyChange;
void region_property_change (PBD::PropertyChange const &);
virtual void bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end);
virtual void bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & len);
protected:
struct UIRequests {
@@ -507,12 +507,11 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
virtual void _arm (Temporal::BBT_Offset const &);
struct PendingSwap {
Temporal::Beats play_start;
Temporal::Beats play_end;
Temporal::Beats loop_start;
Temporal::Beats loop_end;
Temporal::Beats length;
timepos_t play_start;
timepos_t play_end;
timepos_t loop_start;
timepos_t loop_end;
timecnt_t length;
virtual ~PendingSwap() {}
};
@@ -520,7 +519,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
std::atomic<PendingSwap*> pending_swap;
std::atomic<PendingSwap*> old_pending_swap;
virtual void adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool from_region) = 0;
virtual void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region) = 0;
};
class LIBARDOUR_API AudioTrigger : public Trigger {
@@ -551,6 +550,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
void set_end (timepos_t const &);
void set_legato_offset (timepos_t const &);
void set_length (timecnt_t const &);
void set_user_data_length (samplecnt_t);
timepos_t start_offset () 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 */
@@ -594,15 +594,17 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
Sample const * audio_data (size_t n) const;
size_t data_length() const { return data.length; }
samplecnt_t user_data_length() const { return _user_data_length; }
void check_edit_swap (timepos_t const &, bool playing, BufferSet&);
protected:
void retrigger ();
void adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool from_region);
void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region);
private:
AudioData data;
samplecnt_t _user_data_length;
RubberBand::RubberBandStretcher* _stretcher;
samplepos_t _start_offset;
@@ -703,7 +705,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
protected:
void retrigger ();
void _arm (Temporal::BBT_Offset const &);
void adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool from_region);
void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region);
private:
PBD::ID data_source;

View File

@@ -757,11 +757,37 @@ Trigger::set_region_internal (std::shared_ptr<Region> r)
void
Trigger::region_property_change (PropertyChange const & what_changed)
{
//std::cerr << "region prop change\n";
if (!_region) {
return;
}
if (what_changed.contains (Properties::start) || what_changed.contains (Properties::length)) {
//std::cerr << "bounds changed\n";
//PBD::stacktrace (std::cerr, 23);
bounds_changed (_region->start(), _region->end());
bounds_changed (_region->start(), _region->end(), _region->length());
}
}
void
Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & len)
{
PendingSwap* pending = new PendingSwap;
pending->play_start = start;
pending->play_end = end;
pending->loop_start = pending->play_start;
pending->loop_end = pending->play_end;
pending->length = len;
/* And set it. RT thread will find this and do what needs to be done */
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 pushed pending swap @ %3 for bounds change\n", _box.order(), index(), pending));
pending_swap.store (pending);
/* Clean up a previous RT midi buffer swap (if there is one) */
PendingSwap* old = old_pending_swap.exchange (nullptr);
if (old) {
delete old;
}
}
@@ -1345,31 +1371,6 @@ Trigger::start_and_roll_to (samplepos_t start_pos, samplepos_t end_position, Tri
}
}
void
Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end)
{
PendingSwap* pending = new PendingSwap;
pending->play_start = start.beats();
pending->play_end = end.beats();
pending->loop_start = pending->play_start;
pending->loop_end = pending->play_end;
pending->length = pending->play_end - pending->play_start;
/* And set it. RT thread will find this and do what needs to be done */
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 pushed pending swap @ %3 for bounds change\n", _box.order(), index(), pending));
pending_swap.store (pending);
/* Clean up a previous RT midi buffer swap (if there is one) */
PendingSwap* old = old_pending_swap.exchange (nullptr);
if (old) {
delete old;
}
}
/*--------------------*/
AudioTrigger::AudioData::~AudioData ()
@@ -1607,8 +1608,8 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
/* Our task here is to set:
expected_end_sample: (TIMELINE!) the sample position where the data for the clip should run out (taking stretch into account)
last_readable_sample: the sample in the data where we stop reading
final_processed_sample: the sample where the trigger stops and the follow action if any takes effect
last_readable_sample: (DATA RELATIVE!) the sample in the data where we stop reading
final_processed_sample: (DATA RELATIVE!) the sample where the trigger stops and the follow action if any takes effect
Things that affect these values:
@@ -1620,19 +1621,27 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
const Temporal::BBT_Argument transition_bba (superclock_t (0), transition_bbt);
samplepos_t end_by_follow_length = tmap->sample_at (tmap->bbt_walk (transition_bba, _follow_length));
samplepos_t end_by_data_length = transition_sample + (data.length - _start_offset);
/* this could still blow up if the data is less than 1 tick long, but
we should handle that elsewhere.
*/
const Temporal::Beats bc (Temporal::Beats::from_double (_beatcnt));
samplepos_t end_by_follow_length = tmap->sample_at (tmap->bbt_walk (transition_bba, _follow_length));
samplepos_t end_by_beatcnt = tmap->sample_at (tmap->bbt_walk (transition_bba, Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks())));
samplepos_t end_by_user_data_length = transition_sample + (_user_data_length - _start_offset);
samplepos_t end_by_data_length = transition_sample + (data.length - _start_offset);
samplepos_t end_by_fixed_samples = std::min (end_by_user_data_length, end_by_data_length);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 SO %9 @ %2 / %3 / %4 ends: FL %5 (from %6) BC %7 DL %8\n",
index(), transition_sample, transition_beats, transition_bbt,
end_by_follow_length, _follow_length, end_by_beatcnt, end_by_data_length, _start_offset));
if (stretching()) {
/* NOTES: beatcnt here will reflect the stretch, which is OK
Because we are stretching. But .. that's wrong when we're not
stretching. So we really need another value: something like
data_length but user-definable.
*/
if (internal_use_follow_length()) {
expected_end_sample = std::min (end_by_follow_length, end_by_beatcnt);
} else {
@@ -1640,24 +1649,20 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
}
} else {
if (internal_use_follow_length()) {
expected_end_sample = std::min (end_by_follow_length, end_by_data_length);
expected_end_sample = std::min (end_by_follow_length, end_by_fixed_samples);
} else {
expected_end_sample = end_by_data_length;
expected_end_sample = end_by_fixed_samples;
}
}
if (internal_use_follow_length()) {
final_processed_sample = end_by_follow_length - transition_sample;
} else {
final_processed_sample = expected_end_sample - transition_sample;
}
final_processed_sample = expected_end_sample - transition_sample;
samplecnt_t usable_length;
if (internal_use_follow_length() && (end_by_follow_length < end_by_data_length)) {
usable_length = end_by_follow_length - transition_samples;
} else {
usable_length = (data.length - _start_offset);
usable_length = std::min ((end_by_beatcnt - transition_samples), (data.length - _start_offset));
}
/* called from compute_end() when we know the time (audio &
@@ -1696,7 +1701,14 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
void
AudioTrigger::set_length (timecnt_t const & newlen)
{
/* XXX what? */
/* XXX what */
}
void
AudioTrigger::set_user_data_length (samplecnt_t s)
{
_user_data_length = s;
send_property_change (ARDOUR::Properties::length);
}
timepos_t
@@ -1763,7 +1775,7 @@ AudioTrigger::set_region_in_worker_thread_internal (std::shared_ptr<Region> r, b
/* given an initial tempo guess, we need to set our operating tempo and beat_cnt value.
* this may be reset momentarily with user-settings (UIState) from a d+d operation */
set_segment_tempo(_estimated_tempo);
set_segment_tempo (_estimated_tempo);
if (!from_capture) {
setup_stretcher ();
@@ -1808,11 +1820,9 @@ AudioTrigger::set_region_in_worker_thread_internal (std::shared_ptr<Region> r, b
void
AudioTrigger::estimate_tempo ()
{
double beatcount;
ARDOUR::estimate_audio_tempo_region (_region, data[0], data.length, _box.session().sample_rate(), _estimated_tempo, _meter, beatcount);
ARDOUR::estimate_audio_tempo_region (_region, data[0], data.length, _box.session().sample_rate(), _estimated_tempo, _meter, _beatcnt);
/* initialize our follow_length to match the beatcnt ... user can later change this value to have the clip end sooner or later than its data length */
set_follow_length(Temporal::BBT_Offset( 0, rint(beatcount), 0));
set_follow_length (Temporal::BBT_Offset ( 0, floor (_beatcnt), 0));
}
bool
@@ -1821,8 +1831,8 @@ AudioTrigger::probably_oneshot () const
assert (_segment_tempo != 0.);
if ((data.length < (_box.session().sample_rate()/2)) || //less than 1/2 second
(_segment_tempo > 140) || //minibpm thinks this is really fast
(_segment_tempo < 60)) { //minibpm thinks this is really slow
(_segment_tempo > 140) || //minibpm thinks this is really fast
(_segment_tempo < 60)) { //minibpm thinks this is really slow
return true;
}
@@ -1902,6 +1912,7 @@ AudioTrigger::captured (SlotArmInfo& ai)
data.length = ai.audio_buf.length;
data.capacity = ai.audio_buf.capacity;
_user_data_length = data.length;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 captured a total of %3\n", _box.order(), _index, data.length));
@@ -1958,6 +1969,7 @@ AudioTrigger::load_data (std::shared_ptr<AudioRegion> ar)
}
data.length = len;
_user_data_length = len;
set_name (ar->name());
} catch (...) {
@@ -2324,21 +2336,19 @@ AudioTrigger::check_edit_swap (timepos_t const & time, bool playing, BufferSet&
}
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 noticed pending swap @ %3\n", _box.order(), index(), pending));
adjust_bounds (pending->play_start, pending->play_end, pending->length, true);
old_pending_swap.store (pending);
}
void
AudioTrigger::adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool)
AudioTrigger::adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool)
{
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
set_start (timepos_t (tmap->sample_at (start)));
std::cerr << "Old beatcnt: " << _beatcnt;
_beatcnt = Temporal::DoubleableBeats (end - start).to_double();
std::cerr << " new " << _beatcnt << std::endl;
set_start (timepos_t (start.samples()));
set_user_data_length (length.samples());
/* XXX not 100% sure that we should set this here or not */
_beatcnt = Temporal::DoubleableBeats (length.beats()).to_double();
}
/*--------------------*/
@@ -2461,29 +2471,29 @@ MIDITrigger::setup_event_indices ()
}
void
MIDITrigger::adjust_bounds (Temporal::Beats const & start, Temporal::Beats const & end, Temporal::Beats const & length, bool from_region)
MIDITrigger::adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region)
{
if (!from_region && _region) {
_region->set_length (timecnt_t (length, timepos_t (start)));
}
_play_start = start;
_play_end = end;
_play_start = start.beats();
_play_end = end.beats();
/* Note that in theory we may be able to get loop start/end from the
* SMF and it could different from the data start/end
*/
_loop_start = start;
_loop_end = end;
_loop_start = _play_start;
_loop_end = _play_end;
data_length = length;
_follow_length = Temporal::BBT_Offset (0, length.get_beats(), 0);
data_length = length.beats();
_follow_length = Temporal::BBT_Offset (0, data_length.get_beats(), 0);
set_length (timecnt_t (length));
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 new bounds %3..%4 %5..%6 len %7 of %7\n", _box.order(), index(), _play_start, _play_end, _loop_start, _loop_end, length, rt_midibuffer.load()->size()));
setup_event_indices ();
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 new bounds %3..%4 %5..%6 len %7 of %7\n", _box.order(), index(), _play_start, _play_end, _loop_start, _loop_end, length, rt_midibuffer.load()->size()));
}
void
@@ -2516,7 +2526,7 @@ MIDITrigger::captured (SlotArmInfo& ai)
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
timecnt_t dur = tmap->convert_duration (timecnt_t (ai.captured), timepos_t (ai.start_samples), Temporal::BeatTime);
adjust_bounds (Temporal::Beats(), dur.beats(), dur.beats(), false);
adjust_bounds (timepos_t::zero (Temporal::BeatTime), timepos_t (dur.beats()), dur, false);
iter = 0;
_follow_action0 = FollowAction::Again;