summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2014-10-10 13:22:45 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2014-10-10 13:22:50 -0400
commitfa9780ba676e1b128d412d9daf8b51a48b209d90 (patch)
treed2761f35c75d76c536bd41152ea0afb1e9d81205 /libs/ardour
parentd1e303247b56b4e0b1a9564969723989beaa0290 (diff)
subtle changes to accomplish two goals (1) playhead should stop where the user pressed stopped (2) captured regions should end where the playhead ends
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/diskstream.h2
-rw-r--r--libs/ardour/ardour/public_diskstream.h2
-rw-r--r--libs/ardour/ardour/session.h2
-rw-r--r--libs/ardour/ardour/track.h2
-rw-r--r--libs/ardour/audio_diskstream.cc10
-rw-r--r--libs/ardour/diskstream.cc38
-rw-r--r--libs/ardour/session_export.cc2
-rw-r--r--libs/ardour/session_process.cc11
-rw-r--r--libs/ardour/session_transport.cc141
-rw-r--r--libs/ardour/track.cc4
10 files changed, 124 insertions, 90 deletions
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
index 055a298d84..c960e925c8 100644
--- a/libs/ardour/ardour/diskstream.h
+++ b/libs/ardour/ardour/diskstream.h
@@ -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 ();
diff --git a/libs/ardour/ardour/public_diskstream.h b/libs/ardour/ardour/public_diskstream.h
index 4700e7b6be..1fc93a6674 100644
--- a/libs/ardour/ardour/public_diskstream.h
+++ b/libs/ardour/ardour/public_diskstream.h
@@ -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;
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 333911cb92..0368ecb0c0 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -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);
diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h
index d33e24e4e6..5e05ec373c 100644
--- a/libs/ardour/ardour/track.h
+++ b/libs/ardour/ardour/track.h
@@ -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;
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 60b21bbc22..cf0e587ec6 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -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;
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
index 0e05ffabf4..e24e678959 100644
--- a/libs/ardour/diskstream.cc
+++ b/libs/ardour/diskstream.cc
@@ -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
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 62eb61ab83..ecfcb98b5c 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -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 ();
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index a9e9e2eb55..497fbd4d2e 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -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;
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index dbf6af6f1e..3e853a5005 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -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.
-
- 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.
- */
-
- 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;
- }
-
- if ((transport_sub_state & PendingDeclickOut) == 0) {
+ 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 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);
- 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);
- }
- }
+ /* 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;
}
- transport_sub_state |= PendingDeclickOut;
+
/* we'll be called again after the declick */
+ transport_sub_state = SubState (transport_sub_state|new_bits);
pending_abort = abort;
+
return;
- }
- realtime_stop (abort, clear_state);
- _butler->schedule_transport_work ();
+ } 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.
+ */
+
+ realtime_stop (abort, clear_state);
+ _butler->schedule_transport_work ();
+ }
}
/** Called from the process thread */
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
index d463be598f..222c2d81ba 100644
--- a/libs/ardour/track.cc
+++ b/libs/ardour/track.cc
@@ -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