mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-16 08:34:10 +02:00
When recording into region selection, stop it precisely
This commit is contained in:
parent
92d27c618b
commit
0887d6d01e
@ -1933,10 +1933,21 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks,
|
|||||||
mTimeTrack = options.timeTrack;
|
mTimeTrack = options.timeTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
mListener = options.listener;
|
|
||||||
mRate = sampleRate;
|
|
||||||
mT0 = t0;
|
mT0 = t0;
|
||||||
mT1 = t1;
|
mT1 = t1;
|
||||||
|
mRecordingSchedule = {};
|
||||||
|
mRecordingSchedule.mLatencyCorrection =
|
||||||
|
(gPrefs->Read(wxT("/AudioIO/LatencyCorrection"),
|
||||||
|
DEFAULT_LATENCY_CORRECTION))
|
||||||
|
/ 1000.0;
|
||||||
|
mRecordingSchedule.mDuration = mT1 - mT0;
|
||||||
|
if (captureTracks.size() > 0 && playbackTracks.size() > 0)
|
||||||
|
// adjust mT1 so that we don't give paComplete too soon to fill up the
|
||||||
|
// desired length of recording
|
||||||
|
mT1 -= mRecordingSchedule.mLatencyCorrection;
|
||||||
|
|
||||||
|
mListener = options.listener;
|
||||||
|
mRate = sampleRate;
|
||||||
mTime = t0;
|
mTime = t0;
|
||||||
mSeek = 0;
|
mSeek = 0;
|
||||||
mLastRecordingOffset = 0;
|
mLastRecordingOffset = 0;
|
||||||
@ -2150,7 +2161,12 @@ int AudioIO::StartStream(const WaveTrackConstArray &playbackTracks,
|
|||||||
// Don't throw for read errors, just play silence:
|
// Don't throw for read errors, just play silence:
|
||||||
false,
|
false,
|
||||||
warpOptions,
|
warpOptions,
|
||||||
mT0, mT1, 1,
|
mT0,
|
||||||
|
// Pass t1 -- not mT1 as may have been adjusted for latency
|
||||||
|
// -- so that overdub recording stops playing back samples
|
||||||
|
// at the right time, though transport may continue to record
|
||||||
|
t1,
|
||||||
|
1,
|
||||||
playbackMixBufferSize, false,
|
playbackMixBufferSize, false,
|
||||||
mRate, floatSample, false);
|
mRate, floatSample, false);
|
||||||
mPlaybackMixers[i]->ApplyTrackGains(false);
|
mPlaybackMixers[i]->ApplyTrackGains(false);
|
||||||
@ -2724,11 +2740,10 @@ void AudioIO::StopStream()
|
|||||||
// case that we do not apply latency correction when recording the
|
// case that we do not apply latency correction when recording the
|
||||||
// first track in a project.
|
// first track in a project.
|
||||||
//
|
//
|
||||||
double latencyCorrection = DEFAULT_LATENCY_CORRECTION;
|
|
||||||
gPrefs->Read(wxT("/AudioIO/LatencyCorrection"), &latencyCorrection);
|
|
||||||
|
|
||||||
double recordingOffset =
|
double recordingOffset =
|
||||||
mLastRecordingOffset + latencyCorrection / 1000.0;
|
mLastRecordingOffset +
|
||||||
|
mRecordingSchedule.mLatencyCorrection;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mCaptureTracks.size(); i++) {
|
for (unsigned int i = 0; i < mCaptureTracks.size(); i++) {
|
||||||
// The calls to Flush, and (less likely) Clear and InsertSilence,
|
// The calls to Flush, and (less likely) Clear and InsertSilence,
|
||||||
@ -4016,12 +4031,16 @@ void AudioIO::FillBuffers()
|
|||||||
mCaptureTracks.size() > 0)
|
mCaptureTracks.size() > 0)
|
||||||
GuardedCall( [&] {
|
GuardedCall( [&] {
|
||||||
// start record buffering
|
// start record buffering
|
||||||
auto commonlyAvail = GetCommonlyAvailCapture();
|
const auto avail = GetCommonlyAvailCapture(); // samples
|
||||||
|
const auto remainingTime =
|
||||||
|
std::max(0.0, mRecordingSchedule.Remaining());
|
||||||
|
// This may be a very big double number:
|
||||||
|
const auto remainingSamples = remainingTime * mRate;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Determine how much this will add to captured tracks
|
// Determine how much this will add to captured tracks
|
||||||
//
|
//
|
||||||
double deltat = commonlyAvail / mRate;
|
double deltat = avail / mRate;
|
||||||
|
|
||||||
if (mAudioThreadShouldCallFillBuffersOnce ||
|
if (mAudioThreadShouldCallFillBuffersOnce ||
|
||||||
deltat >= mMinCaptureSecsToCopy)
|
deltat >= mMinCaptureSecsToCopy)
|
||||||
@ -4033,7 +4052,6 @@ void AudioIO::FillBuffers()
|
|||||||
|
|
||||||
for( i = 0; i < numChannels; i++ )
|
for( i = 0; i < numChannels; i++ )
|
||||||
{
|
{
|
||||||
auto avail = commonlyAvail;
|
|
||||||
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
|
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
|
||||||
|
|
||||||
AutoSaveFile appendLog;
|
AutoSaveFile appendLog;
|
||||||
@ -4042,12 +4060,16 @@ void AudioIO::FillBuffers()
|
|||||||
{
|
{
|
||||||
SampleBuffer temp(avail, trackFormat);
|
SampleBuffer temp(avail, trackFormat);
|
||||||
const auto got =
|
const auto got =
|
||||||
mCaptureBuffers[i]->Get(temp.ptr(), trackFormat, avail);
|
mCaptureBuffers[i]->Get(temp.ptr(), trackFormat, avail);
|
||||||
// wxASSERT(got == avail);
|
// wxASSERT(got == avail);
|
||||||
// but we can't assert in this thread
|
// but we can't assert in this thread
|
||||||
wxUnusedVar(got);
|
wxUnusedVar(got);
|
||||||
// see comment in second handler about guarantee
|
// see comment in second handler about guarantee
|
||||||
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat, avail, 1,
|
size_t size = avail;
|
||||||
|
if (double(size) > remainingSamples)
|
||||||
|
size = floor(remainingSamples);
|
||||||
|
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat,
|
||||||
|
size, 1,
|
||||||
&appendLog);
|
&appendLog);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -4056,7 +4078,7 @@ void AudioIO::FillBuffers()
|
|||||||
SampleBuffer temp1(avail, floatSample);
|
SampleBuffer temp1(avail, floatSample);
|
||||||
SampleBuffer temp2(size, floatSample);
|
SampleBuffer temp2(size, floatSample);
|
||||||
const auto got =
|
const auto got =
|
||||||
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, avail);
|
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, avail);
|
||||||
// wxASSERT(got == avail);
|
// wxASSERT(got == avail);
|
||||||
// but we can't assert in this thread
|
// but we can't assert in this thread
|
||||||
wxUnusedVar(got);
|
wxUnusedVar(got);
|
||||||
@ -4070,7 +4092,10 @@ void AudioIO::FillBuffers()
|
|||||||
!IsStreamActive(), (float *)temp2.ptr(), size);
|
!IsStreamActive(), (float *)temp2.ptr(), size);
|
||||||
size = results.second;
|
size = results.second;
|
||||||
// see comment in second handler about guarantee
|
// see comment in second handler about guarantee
|
||||||
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample, size, 1,
|
if (double(size) > remainingSamples)
|
||||||
|
size = floor(remainingSamples);
|
||||||
|
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample,
|
||||||
|
size, 1,
|
||||||
&appendLog);
|
&appendLog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4084,7 +4109,9 @@ void AudioIO::FillBuffers()
|
|||||||
blockFileLog.WriteSubTree(appendLog);
|
blockFileLog.WriteSubTree(appendLog);
|
||||||
blockFileLog.EndTag(wxT("recordingrecovery"));
|
blockFileLog.EndTag(wxT("recordingrecovery"));
|
||||||
}
|
}
|
||||||
}
|
} // end loop over capture channels
|
||||||
|
|
||||||
|
mRecordingSchedule.mPosition += avail / mRate;
|
||||||
|
|
||||||
if (mListener && !blockFileLog.IsEmpty())
|
if (mListener && !blockFileLog.IsEmpty())
|
||||||
mListener->OnAudioIONewBlockFiles(blockFileLog);
|
mListener->OnAudioIONewBlockFiles(blockFileLog);
|
||||||
|
@ -814,6 +814,19 @@ public:
|
|||||||
// Whether to check the error code passed to audacityAudioCallback to
|
// Whether to check the error code passed to audacityAudioCallback to
|
||||||
// detect more dropouts
|
// detect more dropouts
|
||||||
bool mDetectUpstreamDropouts{ true };
|
bool mDetectUpstreamDropouts{ true };
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct RecordingSchedule {
|
||||||
|
double mLatencyCorrection{};
|
||||||
|
double mDuration{};
|
||||||
|
|
||||||
|
// This is initialized to 0 by the main thread, then updated
|
||||||
|
// only by the thread calling FillBuffers:
|
||||||
|
double mPosition{};
|
||||||
|
|
||||||
|
double Remaining() const
|
||||||
|
{ return mDuration - mLatencyCorrection - mPosition; }
|
||||||
|
} mRecordingSchedule{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user