subtle changes to accomplish two goals (1) playhead should stop where the user pressed stopped (2) captured regions should end where the playhead ends

This commit is contained in:
Paul Davis 2014-10-10 13:22:45 -04:00
parent d1e303247b
commit fa9780ba67
10 changed files with 127 additions and 93 deletions

View File

@ -255,7 +255,7 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
virtual void set_align_style_from_io() {}
virtual void setup_destructive_playlist () {}
virtual void use_destructive_playlist () {}
virtual void prepare_to_stop (framepos_t pos);
virtual void prepare_to_stop (framepos_t transport_pos, framepos_t audible_frame);
void engage_record_enable ();
void disengage_record_enable ();

View File

@ -60,7 +60,7 @@ public:
virtual void transport_stopped_wallclock (struct tm &, time_t, bool) = 0;
virtual bool pending_overwrite () const = 0;
virtual double speed () const = 0;
virtual void prepare_to_stop (framepos_t) = 0;
virtual void prepare_to_stop (framepos_t,framepos_t) = 0;
virtual void set_slaved (bool) = 0;
virtual ChanCount n_channels () = 0;
virtual framepos_t get_capture_start_frame (uint32_t n = 0) const = 0;

View File

@ -1323,7 +1323,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
void start_locate (framepos_t, bool with_roll, bool with_flush, bool with_loop=false, bool force=false);
void force_locate (framepos_t frame, bool with_roll = false);
void set_track_speed (Track *, double speed);
void set_transport_speed (double speed, bool abort = false, bool clear_state = false, bool as_default = false);
void set_transport_speed (double speed, framepos_t destination_frame, bool abort = false, bool clear_state = false, bool as_default = false);
void stop_transport (bool abort = false, bool clear_state = false);
void start_transport ();
void realtime_stop (bool abort, bool clear_state);

View File

@ -143,7 +143,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream
void transport_stopped_wallclock (struct tm &, time_t, bool);
bool pending_overwrite () const;
double speed () const;
void prepare_to_stop (framepos_t);
void prepare_to_stop (framepos_t, framepos_t);
void set_slaved (bool);
ChanCount n_channels ();
framepos_t get_capture_start_frame (uint32_t n = 0) const;

View File

@ -462,6 +462,8 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 frames, offset %4\n", _name, rec_nframes, nframes, rec_offset));
if (rec_nframes && !was_recording) {
capture_captured = 0;
was_recording = true;
@ -1546,9 +1548,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
}
_last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
// cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
_playlist->clear_changes ();
_playlist->set_capture_insertion_in_progress (true);
_playlist->freeze ();
@ -1703,8 +1703,8 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
accessors, so that invalidation will not occur (both non-realtime).
*/
// cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames));
capture_info.push_back (ci);
capture_captured = 0;

View File

@ -252,8 +252,18 @@ Diskstream::set_capture_offset ()
return;
}
_capture_offset = _io->latency();
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2\n", name(), _capture_offset));
switch (_alignment_style) {
case ExistingMaterial:
_capture_offset = _io->latency();
break;
case CaptureTime:
default:
_capture_offset = 0;
break;
}
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style)));
}
@ -266,6 +276,7 @@ Diskstream::set_align_style (AlignStyle a, bool force)
if ((a != _alignment_style) || force) {
_alignment_style = a;
set_capture_offset ();
AlignmentStyleChanged ();
}
}
@ -613,7 +624,7 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
return;
}
framecnt_t existing_material_offset = _session.worst_playback_latency();
const framecnt_t existing_material_offset = _session.worst_playback_latency();
if (possibly_recording == fully_rec_enabled) {
@ -728,9 +739,26 @@ Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport
}
void
Diskstream::prepare_to_stop (framepos_t pos)
Diskstream::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame)
{
last_recordable_frame = pos + _capture_offset;
switch (_alignment_style) {
case ExistingMaterial:
last_recordable_frame = transport_frame + _capture_offset;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to %2 + %3 = %4\n", _name, transport_frame, _capture_offset, last_recordable_frame));
break;
case CaptureTime:
last_recordable_frame = audible_frame; // note that capture_offset is zero
/* we may already have captured audio before the last_recordable_frame (audible frame),
so deal with this.
*/
if (last_recordable_frame > capture_start_frame) {
capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame);
}
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame));
break;
}
}
void

View File

@ -192,7 +192,7 @@ Session::process_export_fw (pframes_t nframes)
{
if (!_export_started) {
_export_started = true;
set_transport_speed (1.0, false);
set_transport_speed (1.0, 0, false);
butler_transport_work ();
g_atomic_int_set (&_butler->should_do_transport_work, 0);
post_transport ();

View File

@ -154,11 +154,6 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
int declick = get_transport_declick_required();
boost::shared_ptr<RouteList> r = routes.reader ();
if (transport_sub_state & StopPendingCapture) {
/* force a declick out */
declick = -1;
}
const framepos_t start_frame = _transport_frame;
const framepos_t end_frame = _transport_frame + floor (nframes * _transport_speed);
@ -581,7 +576,7 @@ Session::follow_slave (pframes_t nframes)
#endif
if (_slave->give_slave_full_control_over_transport_speed()) {
set_transport_speed (slave_speed, false, false);
set_transport_speed (slave_speed, 0, false, false);
//std::cout << "set speed = " << slave_speed << "\n";
} else {
float adjusted_speed = slave_speed + (1.5 * (delta / float(_current_frame_rate)));
@ -1078,7 +1073,7 @@ Session::process_event (SessionEvent* ev)
case SessionEvent::SetTransportSpeed:
set_transport_speed (ev->speed, ev->yes_or_no, ev->second_yes_or_no, ev->third_yes_or_no);
set_transport_speed (ev->speed, ev->target_frame, ev->yes_or_no, ev->second_yes_or_no, ev->third_yes_or_no);
break;
case SessionEvent::PunchIn:
@ -1101,8 +1096,8 @@ Session::process_event (SessionEvent* ev)
case SessionEvent::StopOnce:
if (!non_realtime_work_pending()) {
stop_transport (ev->yes_or_no);
_clear_event_type (SessionEvent::StopOnce);
stop_transport (ev->yes_or_no);
}
remove = false;
del = false;

View File

@ -139,8 +139,8 @@ Session::request_track_speed (Track* tr, double speed)
void
Session::request_stop (bool abort, bool clear_state)
{
SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0, abort, clear_state);
DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport stop, abort = %1, clear state = %2\n", abort, clear_state));
SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, audible_frame(), 0.0, abort, clear_state);
DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport stop, audible %3 transport %4 abort = %1, clear state = %2\n", abort, clear_state, audible_frame(), _transport_frame));
queue_event (ev);
}
@ -257,27 +257,12 @@ Session::realtime_stop (bool abort, bool clear_state)
for (RouteList::iterator i = r->begin (); i != r->end(); ++i) {
(*i)->realtime_handle_transport_stopped ();
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("stop complete, auto-return scheduled for return to %1\n", _requested_return_frame));
if (actively_recording()) {
/* move the transport position back to where the
request for a stop was noticed. we rolled
past that point to pick up delayed input (and/or to delick)
*/
if (worst_playback_latency() > current_block_size) {
/* we rolled past the stop point to pick up data that had
not yet arrived. move back to where the stop occured.
*/
decrement_transport_position (current_block_size + (worst_input_latency() - current_block_size));
} else {
decrement_transport_position (current_block_size);
}
/* the duration change is not guaranteed to have happened, but is likely */
todo = PostTransportWork (todo | PostTransportDuration);
}
/* the duration change is not guaranteed to have happened, but is likely */
todo = PostTransportWork (todo | PostTransportDuration);
if (abort) {
todo = PostTransportWork (todo | PostTransportAbort);
@ -750,8 +735,10 @@ Session::check_declick_out ()
start_locate (pending_locate_frame, pending_locate_roll, pending_locate_flush);
transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
} else {
stop_transport (pending_abort);
transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
if (!(transport_sub_state & StopPendingCapture)) {
stop_transport (pending_abort);
transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
}
}
} else if (transport_sub_state & PendingLoopDeclickOut) {
@ -952,7 +939,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
if (!force && _transport_frame == target_frame && !loop_changing && !for_seamless_loop) {
if (with_roll) {
set_transport_speed (1.0, false);
set_transport_speed (1.0, 0, false);
}
loop_changing = false;
Located (); /* EMIT SIGNAL */
@ -1097,7 +1084,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
* @param speed New speed
*/
void
Session::set_transport_speed (double speed, bool abort, bool clear_state, bool as_default)
Session::set_transport_speed (double speed, framepos_t destination_frame, bool abort, bool clear_state, bool as_default)
{
DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4 as_default %6\n",
speed, abort, clear_state, _transport_speed, _transport_frame, as_default));
@ -1135,7 +1122,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
if (Config->get_monitoring_model() == HardwareMonitoring) {
set_track_monitor_input_status (true);
}
if (synced_to_engine ()) {
if (clear_state) {
/* do this here because our response to the slave won't
@ -1146,6 +1133,12 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
}
_engine.transport_stop ();
} else {
bool const auto_return_enabled = (!config.get_external_sync() && (config.get_auto_return() || abort));
if (!auto_return_enabled) {
_requested_return_frame = destination_frame;
}
stop_transport (abort);
}
@ -1251,59 +1244,77 @@ Session::stop_transport (bool abort, bool clear_state)
return;
}
if (actively_recording() && !(transport_sub_state & StopPendingCapture) && worst_input_latency() > current_block_size) {
if (!get_transport_declick_required()) {
/* stop has not yet been scheduled */
boost::shared_ptr<RouteList> rl = routes.reader();
framepos_t stop_target = audible_frame();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->prepare_to_stop (_transport_frame);
tr->prepare_to_stop (_transport_frame, stop_target);
}
}
/* we need to capture the audio that has still not yet been received by the system
at the time the stop is requested, so we have to roll past that time.
SubState new_bits;
if (actively_recording() && /* we are recording */
worst_input_latency() > current_block_size) { /* input latency exceeds block size, so simple 1 cycle delay before stop is not enough */
/* we need to capture the audio that is still somewhere in the pipeline between
wherever it was generated and the process callback. This means that even though
the user (or something else) has asked us to stop, we have to roll
past this point and then reset the playhead/transport location to
the position at which the stop was requested.
we want to declick before stopping, so schedule the autostop for one
block before the actual end. we'll declick in the subsequent block,
and then we'll really be stopped.
we still need playback to "stop" now, however, which is why we schedule
a declick below.
*/
DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 = %3, abort = %4\n",
_transport_frame, _worst_input_latency,
_transport_frame + _worst_input_latency,
abort));
SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
_transport_frame + _worst_input_latency,
0, 0, abort);
merge_event (ev);
/* request a declick at the start of the next process cycle() so that playback ceases.
It will remain silent until we actually stop (at the StopOnce event somewhere in
the future). The extra flag (StopPendingCapture) is set to ensure that check_declick_out()
does not stop the transport too early.
*/
new_bits = SubState (PendingDeclickOut|StopPendingCapture);
} else {
/* Not recording, schedule a declick in the next process() cycle and then stop at its end */
new_bits = PendingDeclickOut;
}
/* we'll be called again after the declick */
transport_sub_state = SubState (transport_sub_state|new_bits);
pending_abort = abort;
return;
} else {
/* declick was scheduled, but we've been called again, which means it is really time to stop
XXX: we should probably split this off into its own method and call it explicitly.
*/
DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 - %3 = %4, abort = %5\n",
_transport_frame, _worst_input_latency, current_block_size,
_transport_frame - _worst_input_latency - current_block_size,
abort));
SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
_transport_frame + _worst_input_latency - current_block_size,
0, 0, abort);
merge_event (ev);
transport_sub_state |= StopPendingCapture;
pending_abort = abort;
return;
realtime_stop (abort, clear_state);
_butler->schedule_transport_work ();
}
if ((transport_sub_state & PendingDeclickOut) == 0) {
if (!(transport_sub_state & StopPendingCapture)) {
boost::shared_ptr<RouteList> rl = routes.reader();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
tr->prepare_to_stop (_transport_frame);
}
}
}
transport_sub_state |= PendingDeclickOut;
/* we'll be called again after the declick */
pending_abort = abort;
return;
}
realtime_stop (abort, clear_state);
_butler->schedule_transport_work ();
}
/** Called from the process thread */

View File

@ -766,9 +766,9 @@ Track::speed () const
}
void
Track::prepare_to_stop (framepos_t p)
Track::prepare_to_stop (framepos_t t, framepos_t a)
{
_diskstream->prepare_to_stop (p);
_diskstream->prepare_to_stop (t, a);
}
void