1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-04 14:39:08 +02:00

Recording options allow crossfade data for start of recording

This commit is contained in:
Paul Licameli 2018-05-25 08:01:14 -04:00 committed by Paul Licameli
parent f9cd5595d5
commit a0256e935c
2 changed files with 76 additions and 16 deletions

View File

@ -1944,6 +1944,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
// adjust mT1 so that we don't give paComplete too soon to fill up the
// desired length of recording
mT1 -= mRecordingSchedule.mLatencyCorrection;
if (options.pCrossfadeData)
mRecordingSchedule.mCrossfadeData.swap( *options.pCrossfadeData );
mListener = options.listener;
mRate = sampleRate;
@ -2559,7 +2561,10 @@ void AudioIO::SetMeters()
void AudioIO::StopStream()
{
auto cleanup = finally ( [this] { ClearRecordingException(); } );
auto cleanup = finally ( [this] {
ClearRecordingException();
mRecordingSchedule = {}; // free arrays
} );
if( mPortStreamV19 == NULL
#ifdef EXPERIMENTAL_MIDI_OUT
@ -4032,28 +4037,50 @@ void AudioIO::FillBuffers()
}
}
const float *pCrossfadeSrc = nullptr;
size_t crossfadeStart = 0, totalCrossfadeLength = 0;
if (i < mRecordingSchedule.mCrossfadeData.size())
{
// Do crossfading
// The supplied crossfade samples are at the same rate as the track
const auto &data = mRecordingSchedule.mCrossfadeData[i];
totalCrossfadeLength = data.size();
if (totalCrossfadeLength) {
crossfadeStart =
floor(mRecordingSchedule.Consumed() * mCaptureTracks[i]->GetRate());
if (crossfadeStart < totalCrossfadeLength)
pCrossfadeSrc = data.data() + crossfadeStart;
}
}
size_t toGet = avail - discarded;
SampleBuffer temp;
size_t size;
sampleFormat format;
if( mFactor == 1.0 )
{
SampleBuffer temp(toGet, trackFormat);
// Take captured samples directly
size = toGet;
if (pCrossfadeSrc)
// Change to float for crossfade calculation
format = floatSample;
else
format = trackFormat;
temp.Allocate(size, format);
const auto got =
mCaptureBuffers[i]->Get(temp.ptr(), trackFormat, toGet);
mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
// wxASSERT(got == toGet);
// but we can't assert in this thread
wxUnusedVar(got);
// see comment in second handler about guarantee
size_t size = toGet;
if (double(size) > remainingSamples)
size = floor(remainingSamples);
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat,
size, 1,
&appendLog);
}
else
{
size_t size = lrint(toGet * mFactor);
size = lrint(toGet * mFactor);
format = floatSample;
SampleBuffer temp1(toGet, floatSample);
SampleBuffer temp2(size, floatSample);
temp.Allocate(size, format);
const auto got =
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
// wxASSERT(got == toGet);
@ -4068,15 +4095,34 @@ void AudioIO::FillBuffers()
toGet = floor(remainingSamples);
const auto results =
mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
!IsStreamActive(), (float *)temp2.ptr(), size);
!IsStreamActive(), (float *)temp.ptr(), size);
size = results.second;
// see comment in second handler about guarantee
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample,
size, 1,
&appendLog);
}
}
if (pCrossfadeSrc) {
wxASSERT(format == floatSample);
size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
if (crossfadeLength) {
auto ratio = double(crossfadeStart) / totalCrossfadeLength;
auto ratioStep = 1.0 / totalCrossfadeLength;
auto pCrossfadeDst = (float*)temp.ptr();
// Crossfade loop here
for (size_t ii = 0; ii < crossfadeLength; ++ii) {
*pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
++pCrossfadeSrc, ++pCrossfadeDst;
ratio += ratioStep;
}
}
}
// Now append
// see comment in second handler about guarantee
mCaptureTracks[i]->Append(temp.ptr(), format,
size, 1,
&appendLog);
if (!appendLog.IsEmpty())
{
blockFileLog.StartTag(wxT("recordingrecovery"));
@ -5414,7 +5460,12 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
double AudioIO::RecordingSchedule::ToConsume() const
{
return mDuration - TotalCorrection() - mPosition;
return mDuration - Consumed();
}
double AudioIO::RecordingSchedule::Consumed() const
{
return std::max( 0.0, mPosition + TotalCorrection() );
}
double AudioIO::RecordingSchedule::ToDiscard() const

View File

@ -111,6 +111,8 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
struct ScrubbingOptions;
using PRCrossfadeData = std::vector< std::vector < float > >;
// To avoid growing the argument list of StartStream, add fields here
struct AudioIOStartStreamOptions
{
@ -141,6 +143,9 @@ struct AudioIOStartStreamOptions
// are all incompatible with scrubbing):
ScrubbingOptions *pScrubbingOptions {};
#endif
// contents may get swapped with empty vector
PRCrossfadeData *pCrossfadeData{};
};
struct TransportTracks {
@ -549,6 +554,8 @@ private:
* If bOnlyBuffers is specified, it only cleans up the buffers. */
void StartStreamCleanup(bool bOnlyBuffers = false);
PRCrossfadeData mCrossfadeData{};
#ifdef EXPERIMENTAL_MIDI_OUT
// MIDI_PLAYBACK:
PmStream *mMidiStream;
@ -831,6 +838,7 @@ private:
double mPreRoll{};
double mLatencyCorrection{}; // negative value usually
double mDuration{};
PRCrossfadeData mCrossfadeData;
// These are initialized by the main thread, then updated
// only by the thread calling FillBuffers:
@ -839,6 +847,7 @@ private:
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
double ToConsume() const;
double Consumed() const;
double ToDiscard() const;
} mRecordingSchedule{};
};