mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-04 22:49:07 +02:00
Recording options allow crossfade data for start of recording
This commit is contained in:
parent
f9cd5595d5
commit
a0256e935c
@ -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
|
// adjust mT1 so that we don't give paComplete too soon to fill up the
|
||||||
// desired length of recording
|
// desired length of recording
|
||||||
mT1 -= mRecordingSchedule.mLatencyCorrection;
|
mT1 -= mRecordingSchedule.mLatencyCorrection;
|
||||||
|
if (options.pCrossfadeData)
|
||||||
|
mRecordingSchedule.mCrossfadeData.swap( *options.pCrossfadeData );
|
||||||
|
|
||||||
mListener = options.listener;
|
mListener = options.listener;
|
||||||
mRate = sampleRate;
|
mRate = sampleRate;
|
||||||
@ -2559,7 +2561,10 @@ void AudioIO::SetMeters()
|
|||||||
|
|
||||||
void AudioIO::StopStream()
|
void AudioIO::StopStream()
|
||||||
{
|
{
|
||||||
auto cleanup = finally ( [this] { ClearRecordingException(); } );
|
auto cleanup = finally ( [this] {
|
||||||
|
ClearRecordingException();
|
||||||
|
mRecordingSchedule = {}; // free arrays
|
||||||
|
} );
|
||||||
|
|
||||||
if( mPortStreamV19 == NULL
|
if( mPortStreamV19 == NULL
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#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;
|
size_t toGet = avail - discarded;
|
||||||
|
SampleBuffer temp;
|
||||||
|
size_t size;
|
||||||
|
sampleFormat format;
|
||||||
if( mFactor == 1.0 )
|
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 =
|
const auto got =
|
||||||
mCaptureBuffers[i]->Get(temp.ptr(), trackFormat, toGet);
|
mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
|
||||||
// wxASSERT(got == toGet);
|
// wxASSERT(got == toGet);
|
||||||
// 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
|
|
||||||
size_t size = toGet;
|
|
||||||
if (double(size) > remainingSamples)
|
if (double(size) > remainingSamples)
|
||||||
size = floor(remainingSamples);
|
size = floor(remainingSamples);
|
||||||
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat,
|
|
||||||
size, 1,
|
|
||||||
&appendLog);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size_t size = lrint(toGet * mFactor);
|
size = lrint(toGet * mFactor);
|
||||||
|
format = floatSample;
|
||||||
SampleBuffer temp1(toGet, floatSample);
|
SampleBuffer temp1(toGet, floatSample);
|
||||||
SampleBuffer temp2(size, floatSample);
|
temp.Allocate(size, format);
|
||||||
const auto got =
|
const auto got =
|
||||||
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
|
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
|
||||||
// wxASSERT(got == toGet);
|
// wxASSERT(got == toGet);
|
||||||
@ -4068,14 +4095,33 @@ void AudioIO::FillBuffers()
|
|||||||
toGet = floor(remainingSamples);
|
toGet = floor(remainingSamples);
|
||||||
const auto results =
|
const auto results =
|
||||||
mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
|
mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
|
||||||
!IsStreamActive(), (float *)temp2.ptr(), size);
|
!IsStreamActive(), (float *)temp.ptr(), size);
|
||||||
size = results.second;
|
size = results.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// see comment in second handler about guarantee
|
||||||
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample,
|
mCaptureTracks[i]->Append(temp.ptr(), format,
|
||||||
size, 1,
|
size, 1,
|
||||||
&appendLog);
|
&appendLog);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!appendLog.IsEmpty())
|
if (!appendLog.IsEmpty())
|
||||||
{
|
{
|
||||||
@ -5414,7 +5460,12 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
|
|
||||||
double AudioIO::RecordingSchedule::ToConsume() const
|
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
|
double AudioIO::RecordingSchedule::ToDiscard() const
|
||||||
|
@ -111,6 +111,8 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
|||||||
|
|
||||||
struct ScrubbingOptions;
|
struct ScrubbingOptions;
|
||||||
|
|
||||||
|
using PRCrossfadeData = std::vector< std::vector < float > >;
|
||||||
|
|
||||||
// To avoid growing the argument list of StartStream, add fields here
|
// To avoid growing the argument list of StartStream, add fields here
|
||||||
struct AudioIOStartStreamOptions
|
struct AudioIOStartStreamOptions
|
||||||
{
|
{
|
||||||
@ -141,6 +143,9 @@ struct AudioIOStartStreamOptions
|
|||||||
// are all incompatible with scrubbing):
|
// are all incompatible with scrubbing):
|
||||||
ScrubbingOptions *pScrubbingOptions {};
|
ScrubbingOptions *pScrubbingOptions {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// contents may get swapped with empty vector
|
||||||
|
PRCrossfadeData *pCrossfadeData{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TransportTracks {
|
struct TransportTracks {
|
||||||
@ -549,6 +554,8 @@ private:
|
|||||||
* If bOnlyBuffers is specified, it only cleans up the buffers. */
|
* If bOnlyBuffers is specified, it only cleans up the buffers. */
|
||||||
void StartStreamCleanup(bool bOnlyBuffers = false);
|
void StartStreamCleanup(bool bOnlyBuffers = false);
|
||||||
|
|
||||||
|
PRCrossfadeData mCrossfadeData{};
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
// MIDI_PLAYBACK:
|
// MIDI_PLAYBACK:
|
||||||
PmStream *mMidiStream;
|
PmStream *mMidiStream;
|
||||||
@ -831,6 +838,7 @@ private:
|
|||||||
double mPreRoll{};
|
double mPreRoll{};
|
||||||
double mLatencyCorrection{}; // negative value usually
|
double mLatencyCorrection{}; // negative value usually
|
||||||
double mDuration{};
|
double mDuration{};
|
||||||
|
PRCrossfadeData mCrossfadeData;
|
||||||
|
|
||||||
// These are initialized by the main thread, then updated
|
// These are initialized by the main thread, then updated
|
||||||
// only by the thread calling FillBuffers:
|
// only by the thread calling FillBuffers:
|
||||||
@ -839,6 +847,7 @@ private:
|
|||||||
|
|
||||||
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
|
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
|
||||||
double ToConsume() const;
|
double ToConsume() const;
|
||||||
|
double Consumed() const;
|
||||||
double ToDiscard() const;
|
double ToDiscard() const;
|
||||||
} mRecordingSchedule{};
|
} mRecordingSchedule{};
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user