subtle but deep and possibly profound(ly wrong) changes to record-keeping for recording alignment and related matters. needs careful and exhaustive testing with combinations of differing signal routing, latent plugins, port inserts, etc. etc.
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@6883 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
130b656589
commit
1d1de9b669
|
@ -233,6 +233,7 @@ class Diskstream : public PBD::StatefulDestructible
|
|||
virtual void set_align_style_from_io() {}
|
||||
virtual void setup_destructive_playlist () = 0;
|
||||
virtual void use_destructive_playlist () = 0;
|
||||
virtual void prepare_to_stop (nframes_t);
|
||||
|
||||
static nframes_t disk_io_chunk_frames;
|
||||
std::vector<CaptureInfo*> capture_info;
|
||||
|
|
|
@ -387,6 +387,7 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram
|
|||
const int transport_rolling = 0x4;
|
||||
const int track_rec_enabled = 0x2;
|
||||
const int global_rec_enabled = 0x1;
|
||||
const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
|
||||
|
||||
/* merge together the 3 factors that affect record status, and compute
|
||||
what has changed.
|
||||
|
@ -396,32 +397,26 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram
|
|||
possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record;
|
||||
change = possibly_recording ^ last_possibly_recording;
|
||||
|
||||
if (possibly_recording == last_possibly_recording) {
|
||||
return;
|
||||
}
|
||||
if (possibly_recording == fully_rec_enabled) {
|
||||
|
||||
/* change state */
|
||||
|
||||
/* if per-track or global rec-enable turned on while the other was already on, we've started recording */
|
||||
|
||||
if (((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record)) ||
|
||||
((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled()))) {
|
||||
|
||||
/* starting to record: compute first+last frames */
|
||||
if (last_possibly_recording == fully_rec_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* we transitioned to recording. lets see if its transport based or a punch */
|
||||
|
||||
first_recordable_frame = transport_frame + _capture_offset;
|
||||
// cerr << _name << " set FRF = " << transport_frame << " + " << _capture_offset << " = " << first_recordable_frame << endl;
|
||||
last_recordable_frame = max_frames;
|
||||
capture_start_frame = transport_frame;
|
||||
|
||||
if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) {
|
||||
|
||||
/* was stopped, now rolling (and recording) */
|
||||
if (change & transport_rolling) {
|
||||
|
||||
/* transport-change (started rolling) */
|
||||
|
||||
if (_alignment_style == ExistingMaterial) {
|
||||
|
||||
|
||||
/* there are two delays happening:
|
||||
|
||||
|
||||
1) inbound, represented by _capture_offset
|
||||
2) outbound, represented by _session.worst_output_latency()
|
||||
|
||||
|
@ -433,38 +428,43 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram
|
|||
*/
|
||||
|
||||
if (_capture_offset < _session.worst_output_latency()) {
|
||||
// cerr << "\tA FRF += " << (_session.worst_output_latency () - _capture_offset) << endl;
|
||||
first_recordable_frame += (_session.worst_output_latency() - _capture_offset);
|
||||
}
|
||||
} else {
|
||||
// cerr << "\tB FRF += " << _roll_delay<< endl;
|
||||
first_recordable_frame += _roll_delay;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
} else {
|
||||
|
||||
/* was rolling, but record state changed */
|
||||
/* punch in */
|
||||
|
||||
if (_alignment_style == ExistingMaterial) {
|
||||
|
||||
/* manual punch in happens at the correct transport frame
|
||||
because the user hit a button. but to get alignment correct
|
||||
we have to back up the position of the new region to the
|
||||
appropriate spot given the roll delay.
|
||||
*/
|
||||
/* There are two kinds of punch:
|
||||
|
||||
manual punch in happens at the correct transport frame
|
||||
because the user hit a button. but to get alignment correct
|
||||
we have to back up the position of the new region to the
|
||||
appropriate spot given the roll delay.
|
||||
|
||||
|
||||
/* autopunch toggles recording at the precise
|
||||
autopunch toggles recording at the precise
|
||||
transport frame, and then the DS waits
|
||||
to start recording for a time that depends
|
||||
on the output latency.
|
||||
|
||||
XXX: BUT THIS CODE DOESN'T DIFFERENTIATE !!!
|
||||
|
||||
*/
|
||||
|
||||
first_recordable_frame += _session.worst_output_latency();
|
||||
if (_capture_offset < _session.worst_output_latency()) {
|
||||
/* see comment in ExistingMaterial block above */
|
||||
first_recordable_frame += (_session.worst_output_latency() - _capture_offset);
|
||||
}
|
||||
|
||||
} else {
|
||||
capture_start_frame -= _roll_delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recordable() && destructive()) {
|
||||
boost::shared_ptr<ChannelList> c = channels.reader();
|
||||
|
@ -486,18 +486,31 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram
|
|||
}
|
||||
}
|
||||
|
||||
} else if (!record_enabled() || !can_record) {
|
||||
|
||||
/* stop recording */
|
||||
|
||||
last_recordable_frame = transport_frame + _capture_offset;
|
||||
|
||||
if (_alignment_style == ExistingMaterial) {
|
||||
last_recordable_frame += _session.worst_output_latency();
|
||||
} else {
|
||||
last_recordable_frame += _roll_delay;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (last_possibly_recording == fully_rec_enabled) {
|
||||
|
||||
/* we were recording last time */
|
||||
|
||||
if (change & transport_rolling) {
|
||||
/* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop() */
|
||||
|
||||
} else {
|
||||
/* punch out */
|
||||
|
||||
last_recordable_frame = transport_frame + _capture_offset;
|
||||
|
||||
if (_alignment_style == ExistingMaterial) {
|
||||
if (_session.worst_output_latency() > _capture_offset) {
|
||||
last_recordable_frame += (_session.worst_output_latency() - _capture_offset);
|
||||
}
|
||||
} else {
|
||||
last_recordable_frame += _roll_delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_possibly_recording = possibly_recording;
|
||||
}
|
||||
|
@ -511,8 +524,6 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can
|
|||
int ret = -1;
|
||||
nframes_t rec_offset = 0;
|
||||
nframes_t rec_nframes = 0;
|
||||
bool nominally_recording;
|
||||
bool re = record_enabled ();
|
||||
bool collect_playback = false;
|
||||
|
||||
/* if we've already processed the frames corresponding to this call,
|
||||
|
@ -536,7 +547,11 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can
|
|||
|
||||
check_record_status (transport_frame, nframes, can_record);
|
||||
|
||||
nominally_recording = (can_record && re);
|
||||
#if 0
|
||||
cerr << _name << " can record " << can_record << " re " << record_enabled()
|
||||
<< " FRF " << first_recordable_frame << " LRF " << last_recordable_frame
|
||||
<< endl;
|
||||
#endif
|
||||
|
||||
if (nframes == 0) {
|
||||
_processed = true;
|
||||
|
@ -560,90 +575,67 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can
|
|||
(*chan)->current_playback_buffer = 0;
|
||||
}
|
||||
|
||||
/* two conditions to test for here:
|
||||
|
||||
A: this track is rec-enabled, and the session has confirmed that we can record
|
||||
B: this track is rec-enabled, has been recording, and we are set up for auto-punch
|
||||
|
||||
The second test is necessary to capture the extra material that arrives AFTER the transport
|
||||
frame has left the punch range (which will cause the "can_record" argument to be false).
|
||||
*/
|
||||
|
||||
#if 0
|
||||
cerr << _name << " can record " << can_record << " re " << re << " nomrec " << nominally_recording
|
||||
<< " FRF " << first_recordable_frame << " LRF " << last_recordable_frame
|
||||
<< endl;
|
||||
#endif
|
||||
|
||||
if (nominally_recording ||
|
||||
(re && was_recording && _session.get_record_enabled() &&
|
||||
(Config->get_punch_out() || Config->get_punch_in()))) {
|
||||
|
||||
OverlapType ot;
|
||||
OverlapType ot;
|
||||
|
||||
// Safeguard against situations where process() goes haywire
|
||||
// when autopunching and last_recordable_frame < first_recordable_frame
|
||||
|
||||
if (last_recordable_frame < first_recordable_frame) {
|
||||
last_recordable_frame = max_frames;
|
||||
|
||||
}
|
||||
// Safeguard against situations where process() goes haywire
|
||||
// when autopunching and last_recordable_frame < first_recordable_frame
|
||||
|
||||
if (last_recordable_frame < first_recordable_frame) {
|
||||
last_recordable_frame = max_frames;
|
||||
}
|
||||
|
||||
ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
|
||||
|
||||
switch (ot) {
|
||||
case OverlapNone:
|
||||
rec_nframes = 0;
|
||||
break;
|
||||
|
||||
case OverlapInternal:
|
||||
ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
|
||||
|
||||
switch (ot) {
|
||||
case OverlapNone:
|
||||
rec_nframes = 0;
|
||||
break;
|
||||
|
||||
case OverlapInternal:
|
||||
/* ---------- recrange
|
||||
|---| transrange
|
||||
|---| transrange
|
||||
*/
|
||||
rec_nframes = nframes;
|
||||
rec_offset = 0;
|
||||
break;
|
||||
|
||||
case OverlapStart:
|
||||
/* |--------| recrange
|
||||
-----| transrange
|
||||
*/
|
||||
rec_nframes = transport_frame + nframes - first_recordable_frame;
|
||||
if (rec_nframes) {
|
||||
rec_offset = first_recordable_frame - transport_frame;
|
||||
}
|
||||
break;
|
||||
|
||||
case OverlapEnd:
|
||||
/* |--------| recrange
|
||||
|-------- transrange
|
||||
*/
|
||||
rec_nframes = last_recordable_frame - transport_frame;
|
||||
rec_offset = 0;
|
||||
break;
|
||||
|
||||
case OverlapExternal:
|
||||
/* |--------| recrange
|
||||
-------------- transrange
|
||||
*/
|
||||
rec_nframes = last_recordable_frame - first_recordable_frame;
|
||||
rec_offset = first_recordable_frame - transport_frame;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rec_nframes && !was_recording) {
|
||||
capture_captured = 0;
|
||||
was_recording = true;
|
||||
}
|
||||
|
||||
//cerr << "\trec nframes will be " << rec_nframes << endl;
|
||||
}
|
||||
|
||||
rec_nframes = nframes;
|
||||
rec_offset = 0;
|
||||
break;
|
||||
|
||||
case OverlapStart:
|
||||
/* |--------| recrange
|
||||
-----| transrange
|
||||
*/
|
||||
rec_nframes = transport_frame + nframes - first_recordable_frame;
|
||||
if (rec_nframes) {
|
||||
rec_offset = first_recordable_frame - transport_frame;
|
||||
}
|
||||
break;
|
||||
|
||||
case OverlapEnd:
|
||||
/* |--------| recrange
|
||||
|-------- transrange
|
||||
*/
|
||||
rec_nframes = last_recordable_frame - transport_frame;
|
||||
rec_offset = 0;
|
||||
break;
|
||||
|
||||
case OverlapExternal:
|
||||
/* |--------| recrange
|
||||
-------------- transrange
|
||||
*/
|
||||
rec_nframes = last_recordable_frame - first_recordable_frame;
|
||||
rec_offset = first_recordable_frame - transport_frame;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rec_nframes && !was_recording) {
|
||||
capture_captured = 0;
|
||||
was_recording = true;
|
||||
}
|
||||
|
||||
if (can_record && !_last_capture_regions.empty()) {
|
||||
_last_capture_regions.clear ();
|
||||
}
|
||||
|
||||
if (nominally_recording || rec_nframes) {
|
||||
if (rec_nframes) {
|
||||
|
||||
uint32_t limit = _io->n_inputs ();
|
||||
|
||||
|
@ -724,7 +716,7 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can
|
|||
|
||||
adjust_capture_position = rec_nframes;
|
||||
|
||||
} else if (nominally_recording) {
|
||||
} else if (can_record && record_enabled()) {
|
||||
|
||||
/* can't do actual capture yet - waiting for latency effects to finish before we start*/
|
||||
|
||||
|
@ -877,7 +869,6 @@ AudioDiskstream::commit (nframes_t nframes)
|
|||
|
||||
if (adjust_capture_position != 0) {
|
||||
capture_captured += adjust_capture_position;
|
||||
// cerr << "bump capture_captured by " << adjust_capture_position << " to " << capture_captured << endl;
|
||||
adjust_capture_position = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -410,3 +410,8 @@ Diskstream::remove_region_from_last_capture (boost::weak_ptr<Region> wregion)
|
|||
_last_capture_regions.remove (region);
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::prepare_to_stop (nframes_t pos)
|
||||
{
|
||||
last_recordable_frame = pos + _capture_offset;
|
||||
}
|
||||
|
|
|
@ -1364,9 +1364,7 @@ Session::disable_record (bool rt_context, bool force)
|
|||
void
|
||||
Session::step_back_from_record ()
|
||||
{
|
||||
/* XXX really atomic compare+swap here */
|
||||
if (g_atomic_int_get (&_record_status) == Recording) {
|
||||
g_atomic_int_set (&_record_status, Enabled);
|
||||
if (g_atomic_int_compare_and_exchange (&_record_status, Recording, Enabled)) {
|
||||
|
||||
if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
|
||||
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
||||
|
|
|
@ -168,17 +168,22 @@ Session::realtime_stop (bool abort, bool clear_state)
|
|||
|
||||
/* move the transport position back to where the
|
||||
request for a stop was noticed. we rolled
|
||||
past that point to pick up delayed input.
|
||||
past that point to pick up delayed input (and to declick).
|
||||
*/
|
||||
|
||||
#ifndef LEAVE_TRANSPORT_UNADJUSTED
|
||||
decrement_transport_position (_worst_output_latency);
|
||||
#endif
|
||||
if (_worst_output_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_output_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);
|
||||
}
|
||||
}
|
||||
|
||||
if (abort) {
|
||||
todo = PostTransportWork (todo | PostTransportAbort);
|
||||
|
@ -952,11 +957,15 @@ Session::stop_transport (bool abort, bool clear_state)
|
|||
if (_transport_speed == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (actively_recording() && !(transport_sub_state & StopPendingCapture) &&
|
||||
_worst_output_latency > current_block_size)
|
||||
{
|
||||
|
||||
|
||||
if (actively_recording() && !(transport_sub_state & StopPendingCapture) && (_worst_output_latency > current_block_size)) {
|
||||
|
||||
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
||||
|
||||
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
||||
(*i)->prepare_to_stop (_transport_frame);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
||||
|
@ -977,6 +986,15 @@ Session::stop_transport (bool abort, bool clear_state)
|
|||
}
|
||||
|
||||
if ((transport_sub_state & PendingDeclickOut) == 0) {
|
||||
|
||||
if (!(transport_sub_state & StopPendingCapture)) {
|
||||
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
|
||||
|
||||
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
|
||||
(*i)->prepare_to_stop (_transport_frame);
|
||||
}
|
||||
}
|
||||
|
||||
transport_sub_state |= PendingDeclickOut;
|
||||
/* we'll be called again after the declick */
|
||||
pending_abort = abort;
|
||||
|
|
Loading…
Reference in New Issue