mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-01 08:29:27 +02:00
Split out AudioIO::AllocateBuffers
This commit is contained in:
parent
eeb5f1ec20
commit
8e151aba8d
303
src/AudioIO.cpp
303
src/AudioIO.cpp
@ -1995,8 +1995,6 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
mCaptureBuffers.reset();
|
mCaptureBuffers.reset();
|
||||||
mResample.reset();
|
mResample.reset();
|
||||||
|
|
||||||
double playbackTime = 4.0;
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
streamStartTime = 0;
|
streamStartTime = 0;
|
||||||
streamStartTime = SystemTime(mUsingAlsa);
|
streamStartTime = SystemTime(mUsingAlsa);
|
||||||
@ -2005,32 +2003,6 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
mPlaybackSchedule.Init(
|
mPlaybackSchedule.Init(
|
||||||
t0, t1, options, mCaptureTracks.empty() ? nullptr : &mRecordingSchedule );
|
t0, t1, options, mCaptureTracks.empty() ? nullptr : &mRecordingSchedule );
|
||||||
const bool scrubbing = mPlaybackSchedule.Interactive();
|
const bool scrubbing = mPlaybackSchedule.Interactive();
|
||||||
if (scrubbing)
|
|
||||||
playbackTime =
|
|
||||||
lrint(options.pScrubbingOptions->delay * sampleRate) / sampleRate;
|
|
||||||
|
|
||||||
//
|
|
||||||
// The RingBuffer sizes, and the max amount of the buffer to
|
|
||||||
// fill at a time, both grow linearly with the number of
|
|
||||||
// tracks. This allows us to scale up to many tracks without
|
|
||||||
// killing performance.
|
|
||||||
//
|
|
||||||
|
|
||||||
// real playback time to produce with each filling of the buffers
|
|
||||||
// by the Audio thread (except at the end of playback):
|
|
||||||
// usually, make fillings fewer and longer for less CPU usage.
|
|
||||||
// But for useful scrubbing, we can't run too far ahead without checking
|
|
||||||
// mouse input, so make fillings more and shorter.
|
|
||||||
// What Audio thread produces for playback is then consumed by the PortAudio
|
|
||||||
// thread, in many smaller pieces.
|
|
||||||
wxASSERT( playbackTime >= 0 );
|
|
||||||
mPlaybackSamplesToCopy = playbackTime * mRate;
|
|
||||||
|
|
||||||
// Capacity of the playback buffer.
|
|
||||||
mPlaybackRingBufferSecs = 10.0;
|
|
||||||
|
|
||||||
mCaptureRingBufferSecs = 4.5 + 0.5 * std::min(size_t(16), mCaptureTracks.size());
|
|
||||||
mMinCaptureSecsToCopy = 0.2 + 0.2 * std::min(size_t(16), mCaptureTracks.size());
|
|
||||||
|
|
||||||
unsigned int playbackChannels = 0;
|
unsigned int playbackChannels = 0;
|
||||||
unsigned int captureChannels = 0;
|
unsigned int captureChannels = 0;
|
||||||
@ -2092,123 +2064,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
if ( ! AllocateBuffers( options, tracks, t0, t1, sampleRate, scrubbing ) )
|
||||||
// The (audio) stream has been opened successfully (assuming we tried
|
return 0;
|
||||||
// to open it). We now proceed to
|
|
||||||
// allocate the memory structures the stream will need.
|
|
||||||
//
|
|
||||||
|
|
||||||
bool bDone;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
bDone = true; // assume success
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if( mNumPlaybackChannels > 0 ) {
|
|
||||||
// Allocate output buffers. For every output track we allocate
|
|
||||||
// a ring buffer of five seconds
|
|
||||||
auto playbackBufferSize =
|
|
||||||
(size_t)lrint(mRate * mPlaybackRingBufferSecs);
|
|
||||||
auto playbackMixBufferSize =
|
|
||||||
mPlaybackSamplesToCopy;
|
|
||||||
|
|
||||||
mPlaybackBuffers.reinit(mPlaybackTracks.size());
|
|
||||||
mPlaybackMixers.reinit(mPlaybackTracks.size());
|
|
||||||
|
|
||||||
const Mixer::WarpOptions &warpOptions =
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
|
||||||
scrubbing
|
|
||||||
? Mixer::WarpOptions
|
|
||||||
(ScrubbingOptions::MinAllowedScrubSpeed(),
|
|
||||||
ScrubbingOptions::MaxAllowedScrubSpeed())
|
|
||||||
:
|
|
||||||
#endif
|
|
||||||
Mixer::WarpOptions(mPlaybackSchedule.mTimeTrack);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mPlaybackTracks.size(); i++)
|
|
||||||
{
|
|
||||||
// Bug 1763 - We must fade in from zero to avoid a click on starting.
|
|
||||||
mPlaybackTracks[i]->SetOldChannelGain(0, 0.0);
|
|
||||||
mPlaybackTracks[i]->SetOldChannelGain(1, 0.0);
|
|
||||||
mPlaybackBuffers[i] = std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
|
|
||||||
|
|
||||||
// use track time for the end time, not real time!
|
|
||||||
WaveTrackConstArray mixTracks;
|
|
||||||
mixTracks.push_back(mPlaybackTracks[i]);
|
|
||||||
|
|
||||||
double endTime;
|
|
||||||
if (make_iterator_range(tracks.prerollTracks).contains(mPlaybackTracks[i]))
|
|
||||||
// Stop playing this track after pre-roll
|
|
||||||
endTime = t0;
|
|
||||||
else
|
|
||||||
// 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
|
|
||||||
endTime = t1;
|
|
||||||
|
|
||||||
mPlaybackMixers[i] = std::make_unique<Mixer>
|
|
||||||
(mixTracks,
|
|
||||||
// Don't throw for read errors, just play silence:
|
|
||||||
false,
|
|
||||||
warpOptions,
|
|
||||||
mPlaybackSchedule.mT0,
|
|
||||||
endTime,
|
|
||||||
1,
|
|
||||||
playbackMixBufferSize, false,
|
|
||||||
mRate, floatSample, false);
|
|
||||||
mPlaybackMixers[i]->ApplyTrackGains(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( mNumCaptureChannels > 0 )
|
|
||||||
{
|
|
||||||
// Allocate input buffers. For every input track we allocate
|
|
||||||
// a ring buffer of five seconds
|
|
||||||
auto captureBufferSize = (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
|
|
||||||
|
|
||||||
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
|
||||||
if(captureBufferSize < 100)
|
|
||||||
{
|
|
||||||
StartStreamCleanup();
|
|
||||||
AudacityMessageBox(_("Out of memory!"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCaptureBuffers.reinit(mCaptureTracks.size());
|
|
||||||
mResample.reinit(mCaptureTracks.size());
|
|
||||||
mFactor = sampleRate / mRate;
|
|
||||||
|
|
||||||
for( unsigned int i = 0; i < mCaptureTracks.size(); i++ )
|
|
||||||
{
|
|
||||||
mCaptureBuffers[i] = std::make_unique<RingBuffer>
|
|
||||||
( mCaptureTracks[i]->GetSampleFormat(),
|
|
||||||
captureBufferSize );
|
|
||||||
mResample[i] = std::make_unique<Resample>(true, mFactor, mFactor); // constant rate resampling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(std::bad_alloc&)
|
|
||||||
{
|
|
||||||
// Oops! Ran out of memory. This is pretty rare, so we'll just
|
|
||||||
// try deleting everything, halving our buffer size, and try again.
|
|
||||||
StartStreamCleanup(true);
|
|
||||||
mPlaybackRingBufferSecs *= 0.5;
|
|
||||||
mPlaybackSamplesToCopy /= 2;
|
|
||||||
mCaptureRingBufferSecs *= 0.5;
|
|
||||||
mMinCaptureSecsToCopy *= 0.5;
|
|
||||||
bDone = false;
|
|
||||||
|
|
||||||
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
|
||||||
auto playbackBufferSize = (size_t)lrint(mRate * mPlaybackRingBufferSecs);
|
|
||||||
auto playbackMixBufferSize = mPlaybackSamplesToCopy;
|
|
||||||
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
|
|
||||||
{
|
|
||||||
StartStreamCleanup();
|
|
||||||
AudacityMessageBox(_("Out of memory!"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while(!bDone);
|
|
||||||
|
|
||||||
if (mNumPlaybackChannels > 0)
|
if (mNumPlaybackChannels > 0)
|
||||||
{
|
{
|
||||||
@ -2365,6 +2222,162 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
return mStreamToken;
|
return mStreamToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioIO::AllocateBuffers(
|
||||||
|
const AudioIOStartStreamOptions &options,
|
||||||
|
const TransportTracks &tracks, double t0, double t1, double sampleRate,
|
||||||
|
bool scrubbing )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The (audio) stream has been opened successfully (assuming we tried
|
||||||
|
// to open it). We now proceed to
|
||||||
|
// allocate the memory structures the stream will need.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// The RingBuffer sizes, and the max amount of the buffer to
|
||||||
|
// fill at a time, both grow linearly with the number of
|
||||||
|
// tracks. This allows us to scale up to many tracks without
|
||||||
|
// killing performance.
|
||||||
|
//
|
||||||
|
|
||||||
|
// real playback time to produce with each filling of the buffers
|
||||||
|
// by the Audio thread (except at the end of playback):
|
||||||
|
// usually, make fillings fewer and longer for less CPU usage.
|
||||||
|
// But for useful scrubbing, we can't run too far ahead without checking
|
||||||
|
// mouse input, so make fillings more and shorter.
|
||||||
|
// What Audio thread produces for playback is then consumed by the PortAudio
|
||||||
|
// thread, in many smaller pieces.
|
||||||
|
|
||||||
|
double playbackTime = 4.0;
|
||||||
|
if (scrubbing)
|
||||||
|
playbackTime =
|
||||||
|
lrint(options.pScrubbingOptions->delay * sampleRate) / sampleRate;
|
||||||
|
|
||||||
|
wxASSERT( playbackTime >= 0 );
|
||||||
|
mPlaybackSamplesToCopy = playbackTime * mRate;
|
||||||
|
|
||||||
|
// Capacity of the playback buffer.
|
||||||
|
mPlaybackRingBufferSecs = 10.0;
|
||||||
|
|
||||||
|
mCaptureRingBufferSecs = 4.5 + 0.5 * std::min(size_t(16), mCaptureTracks.size());
|
||||||
|
mMinCaptureSecsToCopy = 0.2 + 0.2 * std::min(size_t(16), mCaptureTracks.size());
|
||||||
|
|
||||||
|
bool bDone;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bDone = true; // assume success
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( mNumPlaybackChannels > 0 ) {
|
||||||
|
// Allocate output buffers. For every output track we allocate
|
||||||
|
// a ring buffer of ten seconds
|
||||||
|
auto playbackBufferSize =
|
||||||
|
(size_t)lrint(mRate * mPlaybackRingBufferSecs);
|
||||||
|
auto playbackMixBufferSize =
|
||||||
|
mPlaybackSamplesToCopy;
|
||||||
|
|
||||||
|
mPlaybackBuffers.reinit(mPlaybackTracks.size());
|
||||||
|
mPlaybackMixers.reinit(mPlaybackTracks.size());
|
||||||
|
|
||||||
|
const Mixer::WarpOptions &warpOptions =
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
|
scrubbing
|
||||||
|
? Mixer::WarpOptions
|
||||||
|
(ScrubbingOptions::MinAllowedScrubSpeed(),
|
||||||
|
ScrubbingOptions::MaxAllowedScrubSpeed())
|
||||||
|
:
|
||||||
|
#endif
|
||||||
|
Mixer::WarpOptions(mPlaybackSchedule.mTimeTrack);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mPlaybackTracks.size(); i++)
|
||||||
|
{
|
||||||
|
// Bug 1763 - We must fade in from zero to avoid a click on starting.
|
||||||
|
mPlaybackTracks[i]->SetOldChannelGain(0, 0.0);
|
||||||
|
mPlaybackTracks[i]->SetOldChannelGain(1, 0.0);
|
||||||
|
|
||||||
|
mPlaybackBuffers[i] = std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
|
||||||
|
|
||||||
|
// use track time for the end time, not real time!
|
||||||
|
WaveTrackConstArray mixTracks;
|
||||||
|
mixTracks.push_back(mPlaybackTracks[i]);
|
||||||
|
|
||||||
|
double endTime;
|
||||||
|
if (make_iterator_range(tracks.prerollTracks).contains(mPlaybackTracks[i]))
|
||||||
|
// Stop playing this track after pre-roll
|
||||||
|
endTime = t0;
|
||||||
|
else
|
||||||
|
// 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
|
||||||
|
endTime = t1;
|
||||||
|
|
||||||
|
mPlaybackMixers[i] = std::make_unique<Mixer>
|
||||||
|
(mixTracks,
|
||||||
|
// Don't throw for read errors, just play silence:
|
||||||
|
false,
|
||||||
|
warpOptions,
|
||||||
|
mPlaybackSchedule.mT0,
|
||||||
|
endTime,
|
||||||
|
1,
|
||||||
|
playbackMixBufferSize, false,
|
||||||
|
mRate, floatSample, false);
|
||||||
|
mPlaybackMixers[i]->ApplyTrackGains(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( mNumCaptureChannels > 0 )
|
||||||
|
{
|
||||||
|
// Allocate input buffers. For every input track we allocate
|
||||||
|
// a ring buffer of five seconds
|
||||||
|
auto captureBufferSize = (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
|
||||||
|
|
||||||
|
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
||||||
|
if(captureBufferSize < 100)
|
||||||
|
{
|
||||||
|
StartStreamCleanup();
|
||||||
|
AudacityMessageBox(_("Out of memory!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCaptureBuffers.reinit(mCaptureTracks.size());
|
||||||
|
mResample.reinit(mCaptureTracks.size());
|
||||||
|
mFactor = sampleRate / mRate;
|
||||||
|
|
||||||
|
for( unsigned int i = 0; i < mCaptureTracks.size(); i++ )
|
||||||
|
{
|
||||||
|
mCaptureBuffers[i] = std::make_unique<RingBuffer>
|
||||||
|
( mCaptureTracks[i]->GetSampleFormat(),
|
||||||
|
captureBufferSize );
|
||||||
|
mResample[i] = std::make_unique<Resample>(true, mFactor, mFactor); // constant rate resampling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(std::bad_alloc&)
|
||||||
|
{
|
||||||
|
// Oops! Ran out of memory. This is pretty rare, so we'll just
|
||||||
|
// try deleting everything, halving our buffer size, and try again.
|
||||||
|
StartStreamCleanup(true);
|
||||||
|
mPlaybackRingBufferSecs *= 0.5;
|
||||||
|
mPlaybackSamplesToCopy /= 2;
|
||||||
|
mCaptureRingBufferSecs *= 0.5;
|
||||||
|
mMinCaptureSecsToCopy *= 0.5;
|
||||||
|
bDone = false;
|
||||||
|
|
||||||
|
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
||||||
|
auto playbackBufferSize = (size_t)lrint(mRate * mPlaybackRingBufferSecs);
|
||||||
|
auto playbackMixBufferSize = mPlaybackSamplesToCopy;
|
||||||
|
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
|
||||||
|
{
|
||||||
|
StartStreamCleanup();
|
||||||
|
AudacityMessageBox(_("Out of memory!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(!bDone);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioIO::StartStreamCleanup(bool bOnlyBuffers)
|
void AudioIO::StartStreamCleanup(bool bOnlyBuffers)
|
||||||
{
|
{
|
||||||
if (mNumPlaybackChannels > 0)
|
if (mNumPlaybackChannels > 0)
|
||||||
|
@ -581,6 +581,16 @@ private:
|
|||||||
/** \brief How many sample rates to try */
|
/** \brief How many sample rates to try */
|
||||||
static const int NumRatesToTry;
|
static const int NumRatesToTry;
|
||||||
|
|
||||||
|
/** \brief Allocate RingBuffer structures, and others, needed for playback
|
||||||
|
* and recording.
|
||||||
|
*
|
||||||
|
* Returns true iff successful.
|
||||||
|
*/
|
||||||
|
bool AllocateBuffers(
|
||||||
|
const AudioIOStartStreamOptions &options,
|
||||||
|
const TransportTracks &tracks, double t0, double t1, double sampleRate,
|
||||||
|
bool scrubbing );
|
||||||
|
|
||||||
/** \brief Clean up after StartStream if it fails.
|
/** \brief Clean up after StartStream if it fails.
|
||||||
*
|
*
|
||||||
* If bOnlyBuffers is specified, it only cleans up the buffers. */
|
* If bOnlyBuffers is specified, it only cleans up the buffers. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user