Audacity 3.2.0
Public Types | Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | List of all members
AudioIO Class Referencefinal

AudioIO uses the PortAudio library to play and record sound. More...

#include <AudioIO.h>

Inheritance diagram for AudioIO:
[legend]
Collaboration diagram for AudioIO:
[legend]

Public Types

using PostRecordingAction = std::function< void()>
 
- Public Types inherited from AudioIoCallback
using OldChannelGains = std::array< float, 2 >
 
using RingBuffers = std::vector< std::unique_ptr< RingBuffer > >
 
- Public Types inherited from Observer::Publisher< AudioIOEvent >
using message_type = AudioIOEvent
 
using CallbackReturn = std::conditional_t< true, void, bool >
 
using Callback = std::function< CallbackReturn(const AudioIOEvent &) >
 Type of functions that can be connected to the Publisher. More...
 

Public Member Functions

std::shared_ptr< RealtimeEffectStateAddState (AudacityProject &project, ChannelGroup *pGroup, const PluginID &id)
 Forwards to RealtimeEffectManager::AddState with proper init scope. More...
 
std::shared_ptr< RealtimeEffectStateReplaceState (AudacityProject &project, ChannelGroup *pGroup, size_t index, const PluginID &id)
 Forwards to RealtimeEffectManager::ReplaceState with proper init scope. More...
 
void RemoveState (AudacityProject &project, ChannelGroup *pGroup, std::shared_ptr< RealtimeEffectState > pState)
 Forwards to RealtimeEffectManager::RemoveState with proper init scope. More...
 
void StartMonitoring (const AudioIOStartStreamOptions &options)
 Start up Portaudio for capture and recording as needed for input monitoring and software playthrough only. More...
 
int StartStream (const TransportSequences &sequences, double t0, double t1, double mixerLimit, const AudioIOStartStreamOptions &options)
 Start recording or playing back audio. More...
 
void StopStream () override
 Stop recording, playback or input monitoring. More...
 
void SeekStream (double seconds)
 Move the playback / recording position of the current stream by the specified amount from where it is now. More...
 
void CallAfterRecording (PostRecordingAction action)
 Enqueue action for main thread idle time, not before the end of any recording in progress. More...
 
wxString LastPaErrorString ()
 
wxLongLong GetLastPlaybackTime () const
 
std::shared_ptr< AudacityProjectGetOwningProject () const
 
void SetPaused (bool state)
 Pause and un-pause playback and recording. More...
 
void SetMixer (int inputSource, float inputVolume, float playbackVolume)
 
void GetMixer (int *inputSource, float *inputVolume, float *playbackVolume)
 
bool InputMixerWorks ()
 Find out if the input hardware level control is available. More...
 
wxArrayString GetInputSourceNames ()
 Get the list of inputs to the current mixer device. More...
 
sampleFormat GetCaptureFormat ()
 
size_t GetNumPlaybackChannels () const
 
size_t GetNumCaptureChannels () const
 
bool IsCapturing () const
 
bool IsAvailable (AudacityProject &project) const
 Function to automatically set an acceptable volume. More...
 
double GetBestRate (bool capturing, bool playing, double sampleRate)
 Return a valid sample rate that is supported by the current I/O device(s). More...
 
double GetStreamTime ()
 During playback, the sequence time most recently played. More...
 
void DelayActions (bool recording)
 
- Public Member Functions inherited from AudioIoCallback
 AudioIoCallback ()
 
 ~AudioIoCallback ()
 
int AudioCallback (constSamplePtr inputBuffer, float *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags, void *userData)
 
std::shared_ptr< AudioIOListenerGetListener () const
 
void SetListener (const std::shared_ptr< AudioIOListener > &listener)
 
int CallbackDoSeek ()
 
void CallbackCheckCompletion (int &callbackReturn, unsigned long len)
 
unsigned CountSoloingSequences ()
 
bool SequenceShouldBeSilent (const PlayableSequence &ps)
 
bool SequenceHasBeenFadedOut (const OldChannelGains &gains)
 Returns true when playback buffer data from both channels is discardable. More...
 
bool AllSequencesAlreadySilent ()
 
void CheckSoundActivatedRecordingLevel (float *inputSamples, unsigned long framesPerBuffer)
 
void AddToOutputChannel (unsigned int chan, float *outputMeterFloats, float *outputFloats, const float *tempBuf, bool drop, unsigned long len, const PlayableSequence &ps, float &channelGain)
 
bool FillOutputBuffers (float *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void DrainInputBuffers (constSamplePtr inputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackFlags statusFlags, float *tempFloats)
 
void UpdateTimePosition (unsigned long framesPerBuffer)
 
void DoPlaythrough (constSamplePtr inputBuffer, float *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void SendVuInputMeterData (const float *inputSamples, unsigned long framesPerBuffer)
 
void SendVuOutputMeterData (const float *outputMeterFloats, unsigned long framesPerBuffer)
 
size_t GetCommonlyReadyPlayback ()
 Get the number of audio samples ready in all of the playback buffers. More...
 
size_t GetCommonlyWrittenForPlayback ()
 
void StartAudioThread ()
 
void WaitForAudioThreadStarted ()
 
void StopAudioThread ()
 
void WaitForAudioThreadStopped ()
 
void ProcessOnceAndWait (std::chrono::milliseconds sleepTime=std::chrono::milliseconds(50))
 
bool HasRecordingException () const
 
const std::vector< std::pair< double, double > > & LostCaptureIntervals ()
 
AudioIOExtRange Extensions ()
 
- Public Member Functions inherited from AudioIOBase
 AudioIOBase ()
 
virtual ~AudioIOBase ()
 
 AudioIOBase (const AudioIOBase &)=delete
 
AudioIOBaseoperator= (const AudioIOBase &)=delete
 
void SetCaptureMeter (const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
 
void SetPlaybackMeter (const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
 
void HandleDeviceChange ()
 update state after changing what audio devices are selected More...
 
wxString GetDeviceInfo () const
 Get diagnostic information on all the available audio I/O devices. More...
 
std::vector< AudioIODiagnosticsGetAllDeviceInfo ()
 Get diagnostic information for audio devices and also for extensions. More...
 
bool IsPaused () const
 Find out if playback / recording is currently paused. More...
 
virtual void StopStream ()=0
 
bool IsBusy () const
 Returns true if audio i/o is busy starting, stopping, playing, or recording. More...
 
bool IsStreamActive () const
 Returns true if the audio i/o is running at all, but not during cleanup. More...
 
bool IsStreamActive (int token) const
 
bool IsAudioTokenActive (int token) const
 Returns true if the stream is active, or even if audio I/O is busy cleaning up its data or writing to disk. More...
 
bool IsMonitoring () const
 Returns true if we're monitoring input (but not recording or playing actual audio) More...
 
void SetMixer (int inputSource)
 
- Public Member Functions inherited from Observer::Publisher< AudioIOEvent >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Static Public Member Functions

static AudioIOGet ()
 
static bool ValidateDeviceNames (const wxString &play, const wxString &rec)
 Ensure selected device names are valid. More...
 
static void AudioThread (std::atomic< bool > &finish)
 Sits in a thread loop reading and writing audio. More...
 
static void Init ()
 
static void Deinit ()
 
- Static Public Member Functions inherited from AudioIOBase
static AudioIOBaseGet ()
 
static std::vector< long > GetSupportedPlaybackRates (int DevIndex=-1, double rate=0.0)
 Get a list of sample rates the output (playback) device supports. More...
 
static std::vector< long > GetSupportedCaptureRates (int devIndex=-1, double rate=0.0)
 Get a list of sample rates the input (recording) device supports. More...
 
static std::vector< long > GetSupportedSampleRates (int playDevice=-1, int recDevice=-1, double rate=0.0)
 Get a list of sample rates the current input/output device combination supports. More...
 
static int GetOptimalSupportedSampleRate ()
 Get a supported sample rate which can be used a an optimal default. More...
 

Private Member Functions

 AudioIO ()
 
 ~AudioIO ()
 
void StartThread ()
 
bool DelayingActions () const
 
void SetMeters ()
 Set the current VU meters - this should be done once after each call to StartStream currently. More...
 
bool StartPortAudioStream (const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels)
 Opens the portaudio stream(s) used to do playback or recording (or both) through. More...
 
void SetOwningProject (const std::shared_ptr< AudacityProject > &pProject)
 
void ResetOwningProject ()
 
void SequenceBufferExchange ()
 
void FillPlayBuffers ()
 First part of SequenceBufferExchange. More...
 
void TransformPlayBuffers (std::optional< RealtimeEffects::ProcessingScope > &scope)
 
bool ProcessPlaybackSlices (std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
 
void DrainRecordBuffers ()
 Second part of SequenceBufferExchange. More...
 
size_t GetCommonlyFreePlayback ()
 Get the number of audio samples free in all of the playback buffers. More...
 
size_t GetCommonlyAvailCapture ()
 Get the number of audio samples ready in all of the recording buffers. More...
 
bool AllocateBuffers (const AudioIOStartStreamOptions &options, const TransportSequences &sequences, double t0, double t1, double sampleRate)
 Allocate RingBuffer structures, and others, needed for playback and recording. More...
 
void StartStreamCleanup (bool bOnlyBuffers=false)
 Clean up after StartStream if it fails. More...
 

Private Attributes

std::mutex mPostRecordingActionMutex
 
PostRecordingAction mPostRecordingAction
 
bool mDelayingActions { false }
 

Additional Inherited Members

- Public Attributes inherited from AudioIoCallback
int mbHasSoloSequences
 
int mCallbackReturn
 
long mNumPauseFrames
 How many frames of zeros were output due to pauses? More...
 
std::thread mAudioThread
 
std::atomic< bool > mFinishAudioThread { false }
 
std::vector< std::unique_ptr< Resample > > mResample
 
RingBuffers mCaptureBuffers
 
RecordableSequences mCaptureSequences
 
RingBuffers mPlaybackBuffers
 
ConstPlayableSequences mPlaybackSequences
 
std::vector< OldChannelGainsmOldChannelGains
 
std::vector< SampleBuffermScratchBuffers
 
std::vector< float * > mScratchPointers
 pointing into mScratchBuffers More...
 
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
 
std::atomic< float > mMixerOutputVol { 1.0 }
 
double mFactor
 
unsigned long mMaxFramesOutput
 
bool mbMicroFades
 
double mSeek
 
PlaybackPolicy::Duration mPlaybackRingBufferSecs
 
double mCaptureRingBufferSecs
 
size_t mPlaybackSamplesToCopy
 Preferred batch size for replenishing the playback RingBuffer. More...
 
size_t mHardwarePlaybackLatencyFrames {}
 Hardware output latency in frames. More...
 
size_t mPlaybackQueueMinimum
 Occupancy of the queue we try to maintain, with bigger batches if needed. More...
 
double mMinCaptureSecsToCopy
 
bool mSoftwarePlaythrough
 
bool mPauseRec
 True if Sound Activated Recording is enabled. More...
 
float mSilenceLevel
 
size_t mNumCaptureChannels
 
size_t mNumPlaybackChannels
 
sampleFormat mCaptureFormat
 
double mCaptureRate {}
 
unsigned long long mLostSamples { 0 }
 
std::atomic< bool > mAudioThreadShouldCallSequenceBufferExchangeOnce
 
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopRunning
 
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopActive
 
std::atomic< AcknowledgemAudioThreadAcknowledge
 
std::atomic< bool > mForceFadeOut { false }
 
wxLongLong mLastPlaybackTimeMillis
 
double mLastRecordingOffset
 Not (yet) used; should perhaps be atomic when it is. More...
 
PaError mLastPaError
 
bool mSimulateRecordingErrors { false }
 
std::atomic< bool > mDetectUpstreamDropouts { true }
 
- Static Public Attributes inherited from AudioIoCallback
static int mNextStreamToken = 0
 
- Static Public Attributes inherited from AudioIOBase
static const int StandardRates []
 Array of common audio sample rates. More...
 
static const int NumStandardRates = WXSIZEOF(AudioIOBase::StandardRates)
 How many standard sample rates there are. More...
 
- Static Public Attributes inherited from Observer::Publisher< AudioIOEvent >
static constexpr bool notifies_all
 
- Protected Member Functions inherited from AudioIoCallback
float GetMixerOutputVol ()
 
void SetMixerOutputVol (float value)
 
void SetRecordingException ()
 
void ClearRecordingException ()
 
- Protected Member Functions inherited from Observer::Publisher< AudioIOEvent >
CallbackReturn Publish (const AudioIOEvent &message)
 Send a message to connected callbacks. More...
 
- Static Protected Member Functions inherited from AudioIoCallback
static size_t MinValue (const RingBuffers &buffers, size_t(RingBuffer::*pmf)() const)
 
- Static Protected Member Functions inherited from AudioIOBase
static wxString DeviceName (const PaDeviceInfo *info)
 
static wxString HostName (const PaDeviceInfo *info)
 
static int getRecordDevIndex (const wxString &devName={})
 get the index of the supplied (named) recording device, or the device selected in the preferences if none given. More...
 
static int getPlayDevIndex (const wxString &devName={})
 get the index of the device selected in the preferences. More...
 
- Protected Attributes inherited from AudioIoCallback
std::weak_ptr< AudioIOListenermListener
 
bool mUsingAlsa { false }
 
bool mUsingJack { false }
 
wxMutex mSuspendAudioThread
 
wxAtomicInt mRecordingException {}
 
std::vector< std::pair< double, double > > mLostCaptureIntervals
 
bool mDetectDropouts { true }
 
RecordingSchedule mRecordingSchedule {}
 
PlaybackSchedule mPlaybackSchedule
 
std::unique_ptr< TransportStatempTransportState
 Holds some state for duration of playback or recording. More...
 
- Protected Attributes inherited from AudioIOBase
std::weak_ptr< AudacityProjectmOwningProject
 
std::atomic< bool > mPaused { false }
 True if audio playback is paused. More...
 
int mStreamToken { 0 }
 
double mRate
 Audio playback rate in samples per second. More...
 
PaStreammPortStreamV19
 
std::weak_ptr< MetermInputMeter {}
 
std::weak_ptr< MetermOutputMeter {}
 
bool mInputMixerWorks
 Can we control the hardware input level? More...
 
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
 
- Static Protected Attributes inherited from AudioIoCallback
static double mCachedBestRateOut
 
static bool mCachedBestRatePlaying
 
static bool mCachedBestRateCapturing
 
- Static Protected Attributes inherited from AudioIOBase
static std::unique_ptr< AudioIOBaseugAudioIO
 
static int mCachedPlaybackIndex = -1
 
static std::vector< long > mCachedPlaybackRates
 
static int mCachedCaptureIndex = -1
 
static std::vector< long > mCachedCaptureRates
 
static std::vector< long > mCachedSampleRates
 
static double mCachedBestRateIn = 0.0
 
static const int RatesToTry []
 Array of audio sample rates to try to use. More...
 
static const int NumRatesToTry = WXSIZEOF(AudioIOBase::RatesToTry)
 How many sample rates to try. More...
 

Detailed Description

AudioIO uses the PortAudio library to play and record sound.

Great care and attention to detail are necessary for understanding and modifying this system. The code in this file is run from three different thread contexts: the UI thread, the disk thread (which this file creates and maintains; in the code, this is called the Audio Thread), and the PortAudio callback thread. To highlight this deliniation, the file is divided into three parts based on what thread context each function is intended to run in.

Todo:
run through all functions called from audio and portaudio threads to verify they are thread-safe. Note that synchronization of the style: "A sets flag to signal B, B clears flag to acknowledge completion" is not thread safe in a general multiple-CPU context. For example, B can write to a buffer and set a completion flag. The flag write can occur before the buffer write due to out-of-order execution. Then A can see the flag and read the buffer before buffer writes complete.

Definition at line 421 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

using AudioIO::PostRecordingAction = std::function<void()>

Definition at line 492 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 232 of file AudioIO.cpp.

233{
234 if (!std::atomic<double>{}.is_lock_free()) {
235 // If this check fails, then the atomic<double> members in AudioIO.h
236 // might be changed to atomic<float> to be more efficient with some
237 // loss of precision. That could be conditionally compiled depending
238 // on the platform.
239 wxASSERT(false);
240 }
241
242 // This ASSERT because of casting in the callback
243 // functions where we cast a tempFloats buffer to a (short*) buffer.
244 // We have to ASSERT in the GUI thread, if we are to see it properly.
245 wxASSERT( sizeof( short ) <= sizeof( float ));
246
248 .store(false, std::memory_order_relaxed);
250 .store(false, std::memory_order_relaxed);
252 .store(false, std::memory_order_relaxed);
253
254 mAudioThreadAcknowledge.store(Acknowledge::eNone, std::memory_order_relaxed);
255
256 mPortStreamV19 = NULL;
257
258 mNumPauseFrames = 0;
259
260#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
261 mAILAActive = false;
262#endif
263
264 mLastPaError = paNoError;
265
268 mSilenceLevel = 0.0;
269
270 mOutputMeter.reset();
271
272 PaError err = Pa_Initialize();
273
274 if (err != paNoError) {
275 auto errStr = XO("Could not find any audio devices.\n");
276 errStr += XO("You will not be able to play or record audio.\n\n");
277 wxString paErrStr = LAT1CTOWX(Pa_GetErrorText(err));
278 if (!paErrStr.empty())
279 errStr += XO("Error: %s").Format( paErrStr );
280 // XXX: we are in libaudacity, popping up dialogs not allowed! A
281 // long-term solution will probably involve exceptions
282 using namespace BasicUI;
284 errStr,
286 .Caption(XO("Error Initializing Audio"))
287 .IconStyle(Icon::Error)
288 .ButtonStyle(Button::Ok));
289
290 // Since PortAudio is not initialized, all calls to PortAudio
291 // functions will fail. This will give reasonable behavior, since
292 // the user will be able to do things not relating to audio i/o,
293 // but any attempt to play or record will simply fail.
294 }
295
296#if defined(USE_PORTMIXER)
297 mPortMixer = NULL;
298 mPreviousHWPlaythrough = -1.0;
300#else
301 mInputMixerWorks = false;
302#endif
303
305
307}
int PaError
Definition: AudioIO.h:45
DoubleSetting AudioIOPlaybackVolume
XO("Cut/Copy/Paste")
#define LAT1CTOWX(X)
Definition: Internat.h:158
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:260
PaStream * mPortStreamV19
Definition: AudioIOBase.h:257
void HandleDeviceChange()
update state after changing what audio devices are selected
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:274
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopRunning
Definition: AudioIO.h:319
std::atomic< bool > mAudioThreadShouldCallSequenceBufferExchangeOnce
Definition: AudioIO.h:318
std::atomic< Acknowledge > mAudioThreadAcknowledge
Definition: AudioIO.h:322
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopActive
Definition: AudioIO.h:320
long mNumPauseFrames
How many frames of zeros were output due to pauses?
Definition: AudioIO.h:247
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:338
PaError mLastPaError
Definition: AudioIO.h:342
double mLastRecordingOffset
Not (yet) used; should perhaps be atomic when it is.
Definition: AudioIO.h:341
float mSilenceLevel
Definition: AudioIO.h:310
size_t mNumCaptureChannels
Definition: AudioIO.h:312
void SetMixerOutputVol(float value)
Definition: AudioIO.h:350
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:279
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101

References AudioIOPlaybackVolume, BasicUI::MessageBoxOptions::Caption(), eNone, AudioIOBase::HandleDeviceChange(), LAT1CTOWX, AudioIoCallback::mAudioThreadAcknowledge, AudioIoCallback::mAudioThreadSequenceBufferExchangeLoopActive, AudioIoCallback::mAudioThreadSequenceBufferExchangeLoopRunning, AudioIoCallback::mAudioThreadShouldCallSequenceBufferExchangeOnce, AudioIOBase::mInputMixerWorks, AudioIoCallback::mLastPaError, AudioIoCallback::mLastPlaybackTimeMillis, AudioIoCallback::mLastRecordingOffset, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPauseFrames, AudioIOBase::mOutputMeter, AudioIOBase::mPortStreamV19, AudioIoCallback::mSilenceLevel, Setting< T >::Read(), AudioIoCallback::SetMixerOutputVol(), BasicUI::ShowMessageBox(), and XO().

Referenced by Init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ~AudioIO()

AudioIO::~AudioIO ( )
private

Definition at line 314 of file AudioIO.cpp.

315{
316 if ( !mOwningProject.expired() )
317 // Unlikely that this will be destroyed earlier than any projects, but
318 // be prepared anyway
320
321#if defined(USE_PORTMIXER)
322 if (mPortMixer) {
323 #if __WXMAC__
324 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
325 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
326 mPreviousHWPlaythrough = -1.0;
327 #endif
328 Px_CloseMixer(mPortMixer);
329 mPortMixer = NULL;
330 }
331#endif
332
333 // FIXME: ? TRAP_ERR. Pa_Terminate probably OK if err without reporting.
334 Pa_Terminate();
335
336 /* Delete is a "graceful" way to stop the thread.
337 (Kill is the not-graceful way.) */
338
339 // This causes reentrancy issues during application shutdown
340 // wxTheApp->Yield();
341
342 mFinishAudioThread.store(true, std::memory_order_release);
343 mAudioThread.join();
344}
std::weak_ptr< AudacityProject > mOwningProject
Definition: AudioIOBase.h:245
void ResetOwningProject()
Definition: AudioIO.cpp:770
std::atomic< bool > mFinishAudioThread
Definition: AudioIO.h:267
std::thread mAudioThread
Definition: AudioIO.h:266

References AudioIoCallback::mAudioThread, AudioIoCallback::mFinishAudioThread, AudioIOBase::mOwningProject, and ResetOwningProject().

Here is the call graph for this function:

Member Function Documentation

◆ AddState()

std::shared_ptr< RealtimeEffectState > AudioIO::AddState ( AudacityProject project,
ChannelGroup pGroup,
const PluginID id 
)

Forwards to RealtimeEffectManager::AddState with proper init scope.

Postcondition
result: !result || result->GetEffect() != nullptr

Definition at line 347 of file AudioIO.cpp.

349{
351 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
352 if (auto pProject = GetOwningProject(); pProject.get() == &project)
353 pInit = &*mpTransportState->mpRealtimeInitialization;
354 return RealtimeEffectManager::Get(project).AddState(pInit, pGroup, id);
355}
const auto project
std::shared_ptr< AudacityProject > GetOwningProject() const
Definition: AudioIO.h:502
std::unique_ptr< TransportState > mpTransportState
Holds some state for duration of playback or recording.
Definition: AudioIO.h:406
static RealtimeEffectManager & Get(AudacityProject &project)
std::shared_ptr< RealtimeEffectState > AddState(RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, const PluginID &id)
Main thread appends a global or per-group effect.
Brackets processing setup and cleanup in the main thread.

References RealtimeEffectManager::AddState(), RealtimeEffectManager::Get(), GetOwningProject(), AudioIoCallback::mpTransportState, and project.

Referenced by EffectUIHost::InitializeInstance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AllocateBuffers()

bool AudioIO::AllocateBuffers ( const AudioIOStartStreamOptions options,
const TransportSequences sequences,
double  t0,
double  t1,
double  sampleRate 
)
private

Allocate RingBuffer structures, and others, needed for playback and recording.

Returns true iff successful.

Definition at line 1166 of file AudioIO.cpp.

1169{
1170 bool success = false;
1171 auto cleanup = finally([&]{
1172 if (!success) StartStreamCleanup( false );
1173 });
1174
1175 auto &policy = mPlaybackSchedule.GetPolicy();
1176 auto times = policy.SuggestedBufferTimes(mPlaybackSchedule);
1177
1178 //
1179 // The (audio) stream has been opened successfully (assuming we tried
1180 // to open it). We now proceed to
1181 // allocate the memory structures the stream will need.
1182 //
1183
1184 //
1185 // The RingBuffer sizes, and the max amount of the buffer to
1186 // fill at a time, both grow linearly with the number of
1187 // sequences. This allows us to scale up to many sequences without
1188 // killing performance.
1189 //
1190
1191 // real playback time to produce with each filling of the buffers
1192 // by the Audio thread (except at the end of playback):
1193 // usually, make fillings fewer and longer for less CPU usage.
1194 // What Audio thread produces for playback is then consumed by the PortAudio
1195 // thread, in many smaller pieces.
1196 double playbackTime = lrint(times.batchSize.count() * mRate) / mRate;
1197
1198 wxASSERT( playbackTime >= 0 );
1199 mPlaybackSamplesToCopy = playbackTime * mRate;
1200
1201 // Capacity of the playback buffer.
1202 mPlaybackRingBufferSecs = times.ringBufferDelay;
1203
1205 4.5 + 0.5 * std::min(size_t(16), mNumCaptureChannels);
1207 0.2 + 0.2 * std::min(size_t(16), mNumCaptureChannels);
1208
1209 bool bDone;
1210 do
1211 {
1212 bDone = true; // assume success
1213 try
1214 {
1215 if( mNumPlaybackChannels > 0 ) {
1216 // Allocate output buffers.
1217 // Allow at least 2x of the buffer latency.
1218 auto playbackBufferSize =
1219 std::max((size_t)lrint(mRate * mPlaybackRingBufferSecs.count()), mHardwarePlaybackLatencyFrames * 2);
1220
1221 // Make playbackBufferSize a multiple of mPlaybackSamplesToCopy
1222 playbackBufferSize = mPlaybackSamplesToCopy *
1223 ((playbackBufferSize + mPlaybackSamplesToCopy - 1) / mPlaybackSamplesToCopy);
1224
1225 // Adjust mPlaybackRingBufferSecs correspondingly
1226 mPlaybackRingBufferSecs = PlaybackPolicy::Duration { playbackBufferSize / mRate };
1227
1228 const size_t totalWidth = std::accumulate(
1229 mPlaybackSequences.begin(), mPlaybackSequences.end(), 0,
1230 [](size_t acc, const auto &pSequence){
1231 return acc + pSequence->NChannels(); });
1232
1233 // mPlaybackBuffers buffers correspond many-to-one with
1234 // mPlaybackSequences
1235 // Except, always make at least one playback buffer, in case of
1236 // MIDI playback without any audio
1237 mPlaybackBuffers.resize(0);
1238 mPlaybackBuffers.resize(
1239 std::max<size_t>(1, totalWidth));
1240 // Number of scratch buffers depends on device playback channels
1241 if (mNumPlaybackChannels > 0) {
1242 mScratchBuffers.resize(mNumPlaybackChannels * 2 + 1);
1243 mScratchPointers.clear();
1244 for (auto &buffer : mScratchBuffers) {
1245 buffer.Allocate(playbackBufferSize, floatSample);
1246 mScratchPointers.push_back(
1247 reinterpret_cast<float*>(buffer.ptr()));
1248 }
1249 }
1250 mPlaybackMixers.clear();
1251
1252 const auto &warpOptions =
1253 policy.MixerWarpOptions(mPlaybackSchedule);
1254
1255 mPlaybackQueueMinimum = lrint( mRate * times.latency.count() );
1257 std::min( mPlaybackQueueMinimum, playbackBufferSize );
1258
1259 // Limit the mPlaybackQueueMinimum to the hardware latency
1262
1263 // Make mPlaybackQueueMinimum a multiple of mPlaybackSamplesToCopy
1266
1267 if (mPlaybackSequences.empty())
1268 // Make at least one playback buffer
1269 mPlaybackBuffers[0] =
1270 std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1271
1272 mOldChannelGains.resize(mPlaybackSequences.size());
1273 size_t iBuffer = 0;
1274 for (unsigned int i = 0; i < mPlaybackSequences.size(); i++) {
1275 const auto &pSequence = mPlaybackSequences[i];
1276 // Bug 1763 - We must fade in from zero to avoid a click on starting.
1277 mOldChannelGains[i][0] = 0.0;
1278 mOldChannelGains[i][1] = 0.0;
1279
1280 for (size_t jj = 0, nChannels = pSequence->NChannels();
1281 jj < nChannels; ++jj
1282 )
1283 mPlaybackBuffers[iBuffer++] = std::make_unique<RingBuffer>(
1284 floatSample, playbackBufferSize);
1285
1286 // By the precondition of StartStream which is sole caller of
1287 // this function:
1288 assert(pSequence->FindChannelGroup());
1289 // use sequence time for the end time, not real time!
1290 double startTime, endTime;
1291 if (!sequences.prerollSequences.empty())
1292 startTime = mPlaybackSchedule.mT0;
1293 else
1294 startTime = t0;
1295
1297 .contains(pSequence))
1298 // Stop playing this sequence after pre-roll
1299 endTime = t0;
1300 else
1301 // Pass t1 -- not mT1 as may have been adjusted for latency
1302 // -- so that overdub recording stops playing back samples
1303 // at the right time, though transport may continue to
1304 // record
1305 endTime = t1;
1306
1307 Mixer::Inputs mixSequences;
1308 mixSequences.push_back(Mixer::Input{ pSequence });
1309 mPlaybackMixers.emplace_back(std::make_unique<Mixer>(
1310 move(mixSequences),
1311 // Don't throw for read errors, just play silence:
1312 false,
1313 warpOptions, startTime, endTime, pSequence->NChannels(),
1315 false, // not interleaved
1317 false, // low quality dithering and resampling
1318 nullptr, // no custom mix-down
1319 Mixer::ApplyGain::Discard // don't apply gains
1320 ));
1321 }
1322
1323 const auto timeQueueSize = 1 +
1324 (playbackBufferSize + TimeQueueGrainSize - 1)
1326 mPlaybackSchedule.mTimeQueue.Resize( timeQueueSize );
1327 }
1328
1329 if( mNumCaptureChannels > 0 )
1330 {
1331 // Allocate input buffers. For every input sequence we allocate
1332 // a ring buffer of five seconds
1333 auto captureBufferSize =
1334 (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
1335
1336 // In the extraordinarily rare case that we can't even afford
1337 // 100 samples, just give up.
1338 if(captureBufferSize < 100)
1339 {
1340 BasicUI::ShowMessageBox( XO("Out of memory!") );
1341 return false;
1342 }
1343
1344 mCaptureBuffers.resize(0);
1346 mResample.resize(0);
1349
1350 for (unsigned int i = 0; i < mNumCaptureChannels; ++i) {
1351 mCaptureBuffers[i] = std::make_unique<RingBuffer>(
1352 mCaptureFormat, captureBufferSize);
1353 mResample[i] =
1354 std::make_unique<Resample>(true, mFactor, mFactor);
1355 // constant rate resampling
1356 }
1357 }
1358 }
1359 catch(std::bad_alloc&)
1360 {
1361 // Oops! Ran out of memory. This is pretty rare, so we'll just
1362 // try deleting everything, halving our buffer size, and try again.
1363 StartStreamCleanup(true);
1367 mMinCaptureSecsToCopy *= 0.5;
1368 bDone = false;
1369
1370 // In the extraordinarily rare case that we can't even afford 100
1371 // samples, just give up.
1372 auto playbackBufferSize =
1373 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1374 if(playbackBufferSize < 100 || mPlaybackSamplesToCopy < 100)
1375 {
1376 BasicUI::ShowMessageBox( XO("Out of memory!") );
1377 return false;
1378 }
1379 }
1380 } while(!bDone);
1381
1382 success = true;
1383 return true;
1384}
int min(int a, int b)
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
constexpr size_t TimeQueueGrainSize
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:255
void StartStreamCleanup(bool bOnlyBuffers=false)
Clean up after StartStream if it fails.
Definition: AudioIO.cpp:1386
size_t mNumPlaybackChannels
Definition: AudioIO.h:314
std::vector< std::unique_ptr< Resample > > mResample
Definition: AudioIO.h:269
PlaybackSchedule mPlaybackSchedule
Definition: AudioIO.h:404
std::vector< float * > mScratchPointers
pointing into mScratchBuffers
Definition: AudioIO.h:282
size_t mPlaybackSamplesToCopy
Preferred batch size for replenishing the playback RingBuffer.
Definition: AudioIO.h:298
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
Definition: AudioIO.h:284
PlaybackPolicy::Duration mPlaybackRingBufferSecs
Definition: AudioIO.h:294
size_t mHardwarePlaybackLatencyFrames
Hardware output latency in frames.
Definition: AudioIO.h:300
RingBuffers mCaptureBuffers
Definition: AudioIO.h:272
std::vector< OldChannelGains > mOldChannelGains
Definition: AudioIO.h:279
RingBuffers mPlaybackBuffers
Definition: AudioIO.h:275
ConstPlayableSequences mPlaybackSequences
Definition: AudioIO.h:276
size_t mPlaybackQueueMinimum
Occupancy of the queue we try to maintain, with bigger batches if needed.
Definition: AudioIO.h:302
double mCaptureRingBufferSecs
Definition: AudioIO.h:295
double mFactor
Definition: AudioIO.h:288
double mMinCaptureSecsToCopy
Definition: AudioIO.h:304
std::vector< SampleBuffer > mScratchBuffers
Definition: AudioIO.h:281
sampleFormat mCaptureFormat
Definition: AudioIO.h:315
std::vector< Input > Inputs
Definition: Mix.h:45
virtual BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule)
Provide hints for construction of playback RingBuffer objects.
std::chrono::duration< double > Duration
#define lrint(dbl)
Definition: float_cast.h:169
double mT0
Playback starts at offset of mT0, which is measured in seconds.
class AUDIO_IO_API PlaybackSchedule::TimeQueue mTimeQueue
PlaybackPolicy & GetPolicy()
ConstPlayableSequences prerollSequences
Definition: AudioIO.h:76

References Mixer::Discard, floatSample, PlaybackSchedule::GetPolicy(), lrint, make_iterator_range(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureFormat, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mFactor, AudioIoCallback::mHardwarePlaybackLatencyFrames, min(), AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mOldChannelGains, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackRingBufferSecs, AudioIoCallback::mPlaybackSamplesToCopy, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, AudioIOBase::mRate, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, PlaybackSchedule::mT0, PlaybackSchedule::mTimeQueue, TransportSequences::prerollSequences, PlaybackSchedule::TimeQueue::Resize(), anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, BasicUI::ShowMessageBox(), StartStreamCleanup(), PlaybackPolicy::SuggestedBufferTimes(), TimeQueueGrainSize, and XO().

Referenced by StartStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AudioThread()

void AudioIO::AudioThread ( std::atomic< bool > &  finish)
static

Sits in a thread loop reading and writing audio.

Definition at line 1797 of file AudioIO.cpp.

1798{
1799 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1800 AudioIO *const gAudioIO = AudioIO::Get();
1801 while (!finish.load(std::memory_order_acquire)) {
1802 using Clock = std::chrono::steady_clock;
1803 auto loopPassStart = Clock::now();
1804 auto &schedule = gAudioIO->mPlaybackSchedule;
1805 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1806
1807 // Set LoopActive outside the tests to avoid race condition
1809 .store(true, std::memory_order_relaxed);
1811 .load(std::memory_order_acquire) )
1812 {
1813 gAudioIO->SequenceBufferExchange();
1815 .store(false, std::memory_order_release);
1816
1817 lastState = State::eOnce;
1818 }
1820 .load(std::memory_order_relaxed))
1821 {
1822 if (lastState != State::eLoopRunning)
1823 {
1824 // Main thread has told us to start - acknowledge that we do
1826 std::memory_order::memory_order_release);
1827 }
1828 lastState = State::eLoopRunning;
1829
1830 // We call the processing after raising the acknowledge flag, because the main thread
1831 // only needs to know that the message was seen.
1832 //
1833 // This is unlike the case with mAudioThreadShouldCallSequenceBufferExchangeOnce where the
1834 // store really means that the one-time exchange was done.
1835
1836 gAudioIO->SequenceBufferExchange();
1837 }
1838 else
1839 {
1840 if ( (lastState == State::eLoopRunning)
1841 || (lastState == State::eMonitoring ) )
1842 {
1843 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1844 // acknowledge that we received the order and that no more processing will be done.
1846 std::memory_order::memory_order_release);
1847 }
1848 lastState = State::eDoNothing;
1849
1850 if (gAudioIO->IsMonitoring())
1851 {
1852 lastState = State::eMonitoring;
1853 }
1854 }
1855
1857 .store(false, std::memory_order_relaxed);
1858
1859 std::this_thread::sleep_until( loopPassStart + interval );
1860 }
1861}
std::chrono::system_clock Clock
bool IsMonitoring() const
Returns true if we're monitoring input (but not recording or playing actual audio)
AudioIO uses the PortAudio library to play and record sound.
Definition: AudioIO.h:424
static AudioIO * Get()
Definition: AudioIO.cpp:126
void SequenceBufferExchange()
Definition: AudioIO.cpp:1898
virtual std::chrono::milliseconds SleepInterval(PlaybackSchedule &schedule)
How long to wait between calls to AudioIO::SequenceBufferExchange.
Definition: Dither.cpp:67

References eStart, eStop, Get(), PlaybackSchedule::GetPolicy(), AudioIOBase::IsMonitoring(), AudioIoCallback::mAudioThreadAcknowledge, AudioIoCallback::mAudioThreadSequenceBufferExchangeLoopActive, AudioIoCallback::mAudioThreadSequenceBufferExchangeLoopRunning, AudioIoCallback::mAudioThreadShouldCallSequenceBufferExchangeOnce, AudioIoCallback::mPlaybackSchedule, SequenceBufferExchange(), and PlaybackPolicy::SleepInterval().

Referenced by StartThread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CallAfterRecording()

void AudioIO::CallAfterRecording ( PostRecordingAction  action)

Enqueue action for main thread idle time, not before the end of any recording in progress.

This may be called from non-main threads

Definition at line 1138 of file AudioIO.cpp.

1139{
1140 if (!action)
1141 return;
1142
1143 {
1144 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1146 // Enqueue it, even if perhaps not still recording,
1147 // but it wasn't cleared yet
1149 prevAction = std::move(mPostRecordingAction),
1150 nextAction = std::move(action)
1151 ]{ prevAction(); nextAction(); };
1152 return;
1153 }
1154 else if (DelayingActions()) {
1155 mPostRecordingAction = std::move(action);
1156 return;
1157 }
1158 }
1159
1160 // Don't delay it except until idle time.
1161 // (Recording might start between now and then, but won't go far before
1162 // the action is done. So the system isn't bulletproof yet.)
1163 BasicUI::CallAfter(move(action));
1164}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:660
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:661
bool DelayingActions() const
Definition: AudioIO.cpp:1133
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:213

References BasicUI::CallAfter(), DelayingActions(), mPostRecordingAction, and mPostRecordingActionMutex.

Here is the call graph for this function:

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 218 of file AudioIO.cpp.

219{
220 ugAudioIO.reset();
221}
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:241

References AudioIOBase::ugAudioIO.

Referenced by AudacityApp::OnExit().

Here is the caller graph for this function:

◆ DelayActions()

void AudioIO::DelayActions ( bool  recording)

For purposes of CallAfterRecording, treat time from now as if recording (when argument is true) or not necessarily so (false)

Definition at line 1128 of file AudioIO.cpp.

1129{
1130 mDelayingActions = recording;
1131}
bool mDelayingActions
Definition: AudioIO.h:663

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1133 of file AudioIO.cpp.

1134{
1136}

References mDelayingActions, AudioIoCallback::mNumCaptureChannels, and AudioIOBase::mPortStreamV19.

Referenced by CallAfterRecording().

Here is the caller graph for this function:

◆ DrainRecordBuffers()

void AudioIO::DrainRecordBuffers ( )
private

Second part of SequenceBufferExchange.

Definition at line 2118 of file AudioIO.cpp.

2119{
2121 return;
2122
2123 auto delayedHandler = [this] ( AudacityException * pException ) {
2124 // In the main thread, stop recording
2125 // This is one place where the application handles disk
2126 // exhaustion exceptions from RecordableSequence operations, without
2127 // rolling back to the last pushed undo state. Instead, partial recording
2128 // results are pushed as a NEW undo state. For this reason, as
2129 // commented elsewhere, we want an exception safety guarantee for
2130 // the output RecordableSequences, after the failed append operation, that
2131 // the sequences remain as they were after the previous successful
2132 // (block-level) appends.
2133
2134 // Note that the Flush in StopStream() may throw another exception,
2135 // but StopStream() contains that exception, and the logic in
2136 // AudacityException::DelayedHandlerAction prevents redundant message
2137 // boxes.
2138 StopStream();
2139 DefaultDelayedHandlerAction( pException );
2140 for (auto &pSequence: mCaptureSequences)
2141 pSequence->RepairChannels();
2142 };
2143
2144 GuardedCall( [&] {
2145 // start record buffering
2146 const auto avail = GetCommonlyAvailCapture(); // samples
2147 const auto remainingTime =
2148 std::max(0.0, mRecordingSchedule.ToConsume());
2149 // This may be a very big double number:
2150 const auto remainingSamples = remainingTime * mRate;
2151 bool latencyCorrected = true;
2152
2153 double deltat = avail / mRate;
2154
2156 .load(std::memory_order_relaxed) ||
2157 deltat >= mMinCaptureSecsToCopy)
2158 {
2159 bool newBlocks = false;
2160
2161 // Append captured samples to the end of the RecordableSequences.
2162 // (WaveTracks have their own buffering for efficiency.)
2163 auto iter = mCaptureSequences.begin();
2164 auto width = (*iter)->NChannels();
2165 size_t iChannel = 0;
2166 for (size_t i = 0; i < mNumCaptureChannels; ++i) {
2167 Finally Do {[&]{
2168 if (++iChannel == width) {
2169 ++iter;
2170 iChannel = 0;
2171 if (iter != mCaptureSequences.end())
2172 width = (*iter)->NChannels();
2173 }
2174 }};
2175 size_t discarded = 0;
2176
2178 const auto correction = mRecordingSchedule.TotalCorrection();
2179 if (correction >= 0) {
2180 // Rightward shift
2181 // Once only (per sequence per recording), insert some initial
2182 // silence.
2183 size_t size = floor( correction * mRate * mFactor);
2185 ClearSamples(temp.ptr(), mCaptureFormat, 0, size);
2186 (*iter)->Append(iChannel, temp.ptr(), mCaptureFormat, size, 1,
2187 // Do not dither recordings
2189 }
2190 else {
2191 // Leftward shift
2192 // discard some samples from the ring buffers.
2193 size_t size = floor(
2195
2196 // The ring buffer might have grown concurrently -- don't discard more
2197 // than the "avail" value noted above.
2198 discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2199
2200 if (discarded < size)
2201 // We need to visit this again to complete the
2202 // discarding.
2203 latencyCorrected = false;
2204 }
2205 }
2206
2207 const float *pCrossfadeSrc = nullptr;
2208 size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2209 if (i < mRecordingSchedule.mCrossfadeData.size())
2210 {
2211 // Do crossfading
2212 // The supplied crossfade samples are at the same rate as the
2213 // sequence
2214 const auto &data = mRecordingSchedule.mCrossfadeData[i];
2215 totalCrossfadeLength = data.size();
2216 if (totalCrossfadeLength) {
2217 crossfadeStart =
2219 if (crossfadeStart < totalCrossfadeLength)
2220 pCrossfadeSrc = data.data() + crossfadeStart;
2221 }
2222 }
2223
2224 wxASSERT(discarded <= avail);
2225 size_t toGet = avail - discarded;
2226 SampleBuffer temp;
2227 size_t size;
2229 if( mFactor == 1.0 )
2230 {
2231 // Take captured samples directly
2232 size = toGet;
2233 if (pCrossfadeSrc)
2234 // Change to float for crossfade calculation
2236 else
2238 temp.Allocate(size, format);
2239 const auto got =
2240 mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2241 // wxASSERT(got == toGet);
2242 // but we can't assert in this thread
2243 wxUnusedVar(got);
2244 if (double(size) > remainingSamples)
2245 size = floor(remainingSamples);
2246 }
2247 else
2248 {
2249 size = lrint(toGet * mFactor);
2251 SampleBuffer temp1(toGet, floatSample);
2252 temp.Allocate(size, format);
2253 const auto got =
2254 mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
2255 // wxASSERT(got == toGet);
2256 // but we can't assert in this thread
2257 wxUnusedVar(got);
2258 /* we are re-sampling on the fly. The last resampling call
2259 * must flush any samples left in the rate conversion buffer
2260 * so that they get recorded
2261 */
2262 if (toGet > 0 ) {
2263 if (double(toGet) > remainingSamples)
2264 toGet = floor(remainingSamples);
2265 const auto results =
2266 mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
2267 !IsStreamActive(), (float *)temp.ptr(), size);
2268 size = results.second;
2269 }
2270 }
2271
2272 if (pCrossfadeSrc) {
2273 wxASSERT(format == floatSample);
2274 size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
2275 if (crossfadeLength) {
2276 auto ratio = double(crossfadeStart) / totalCrossfadeLength;
2277 auto ratioStep = 1.0 / totalCrossfadeLength;
2278 auto pCrossfadeDst = (float*)temp.ptr();
2279
2280 // Crossfade loop here
2281 for (size_t ii = 0; ii < crossfadeLength; ++ii) {
2282 *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
2283 ++pCrossfadeSrc, ++pCrossfadeDst;
2284 ratio += ratioStep;
2285 }
2286 }
2287 }
2288
2289 // Now append
2290 // see comment in second handler about guarantee
2291 newBlocks = (*iter)->Append(iChannel,
2292 temp.ptr(), format, size, 1,
2293 // Do not dither recordings
2295 ) || newBlocks;
2296 } // end loop over capture channels
2297
2298 // Now update the recording schedule position
2300 mRecordingSchedule.mLatencyCorrected = latencyCorrected;
2301
2302 auto pListener = GetListener();
2303 if (pListener && newBlocks)
2304 pListener->OnAudioIONewBlocks();
2305
2306 }
2307 // end of record buffering
2308 },
2309 // handler
2310 [this] ( AudacityException *pException ) {
2311 if ( pException ) {
2312 // So that we don't attempt to fill the recording buffer again
2313 // before the main thread stops recording
2315 return ;
2316 }
2317 else
2318 // Don't want to intercept other exceptions (?)
2319 throw;
2320 },
2321 delayedHandler );
2322}
void DefaultDelayedHandlerAction(AudacityException *pException)
A default template parameter for GuardedCall.
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
Base class for exceptions specially processed by the application.
bool IsStreamActive() const
Returns true if the audio i/o is running at all, but not during cleanup.
void StopStream() override
Stop recording, playback or input monitoring.
Definition: AudioIO.cpp:1423
size_t GetCommonlyAvailCapture()
Get the number of audio samples ready in all of the recording buffers.
Definition: AudioIO.cpp:1890
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:169
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:403
void SetRecordingException()
Definition: AudioIO.h:381
RecordableSequences mCaptureSequences
Definition: AudioIO.h:273
double mCaptureRate
Definition: AudioIO.h:316
wxAtomicInt mRecordingException
Definition: AudioIO.h:380
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:151
samplePtr ptr() const
Definition: SampleFormat.h:165
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:175
double TotalCorrection() const
double ToDiscard() const
double ToConsume() const
double Consumed() const
PRCrossfadeData mCrossfadeData

References SampleBuffer::Allocate(), ClearSamples(), RecordingSchedule::Consumed(), DefaultDelayedHandlerAction(), floatSample, anonymous_namespace{ExportPCM.cpp}::format, GetCommonlyAvailCapture(), AudioIoCallback::GetListener(), GuardedCall(), anonymous_namespace{StretchingSequenceIntegrationTest.cpp}::iChannel, AudioIOBase::IsStreamActive(), lrint, AudioIoCallback::mAudioThreadShouldCallSequenceBufferExchangeOnce, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureFormat, AudioIoCallback::mCaptureRate, AudioIoCallback::mCaptureSequences, RecordingSchedule::mCrossfadeData, AudioIoCallback::mFactor, min(), RecordingSchedule::mLatencyCorrected, AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, RecordingSchedule::mPosition, AudioIOBase::mRate, AudioIoCallback::mRecordingException, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, narrowestSampleFormat, SampleBuffer::ptr(), AudioIoCallback::SetRecordingException(), size, StopStream(), RecordingSchedule::ToConsume(), RecordingSchedule::ToDiscard(), and RecordingSchedule::TotalCorrection().

Referenced by SequenceBufferExchange().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FillPlayBuffers()

void AudioIO::FillPlayBuffers ( )
private

First part of SequenceBufferExchange.

Definition at line 1904 of file AudioIO.cpp.

1905{
1906 std::optional<RealtimeEffects::ProcessingScope> pScope;
1907 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
1908 pScope.emplace(
1909 *mpTransportState->mpRealtimeInitialization, mOwningProject);
1910
1911 if (mNumPlaybackChannels == 0)
1912 return;
1913
1914 // It is possible that some buffers will have more samples available than
1915 // others. This could happen if we hit this code during the PortAudio
1916 // callback. Also, if in a previous pass, unequal numbers of samples were
1917 // discarded from ring buffers for differing latencies.
1918
1919 // To keep things simple, we write no more data than is vacant in
1920 // ALL buffers, and advance the global time by that much.
1921 auto nAvailable = GetCommonlyFreePlayback();
1922
1923 // Don't fill the buffers at all unless we can do
1924 // at least mPlaybackSamplesToCopy. This improves performance
1925 // by not always trying to process tiny chunks, eating the
1926 // CPU unnecessarily.
1927 if (nAvailable < mPlaybackSamplesToCopy)
1928 return;
1929
1930 // More than mPlaybackSamplesToCopy might be copied:
1931 // May produce a larger amount when initially priming the buffer, or
1932 // perhaps again later in play to avoid underfilling the queue and
1933 // falling behind the real-time demand on the consumer side in the
1934 // callback.
1935 auto GetNeeded = [&]() -> size_t {
1936 // Note that reader might concurrently consume between loop passes below
1937 // So this might not be nondecreasing
1938 auto nReady = GetCommonlyWrittenForPlayback();
1940 };
1941 auto nNeeded = GetNeeded();
1942
1943 // wxASSERT( nNeeded <= nAvailable );
1944
1945 auto Flush = [&]{
1946 /* The flushing of all the Puts to the RingBuffers is lifted out of the
1947 do-loop in ProcessPlaybackSlices, and also after transformation of the
1948 stream for realtime effects.
1949
1950 It's only here that a release is done on the atomic variable that
1951 indicates the readiness of sample data to the consumer. That atomic
1952 also synchronizes the use of the TimeQueue.
1953 */
1954 for (const auto &pBuffer : mPlaybackBuffers)
1955 pBuffer->Flush();
1956 };
1957
1958 while (true) {
1959 // Limit maximum buffer size (increases performance)
1960 auto available = std::min( nAvailable,
1961 std::max( nNeeded, mPlaybackSamplesToCopy ) );
1962
1963 // After each loop pass or after break
1964 Finally Do{ Flush };
1965
1966 if (!ProcessPlaybackSlices(pScope, available))
1967 // We are not making progress. May fail to satisfy the minimum but
1968 // won't loop forever
1969 break;
1970
1971 // Loop again to satisfy the minimum queue requirement in case there
1972 // was discarding of processed data for effect latencies
1973 nNeeded = GetNeeded();
1974 if (nNeeded == 0)
1975 break;
1976
1977 // Might increase because the reader consumed some
1978 nAvailable = GetCommonlyFreePlayback();
1979 }
1980}
bool ProcessPlaybackSlices(std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
Definition: AudioIO.cpp:1982
size_t GetCommonlyFreePlayback()
Get the number of audio samples free in all of the playback buffers.
Definition: AudioIO.cpp:1872
size_t GetCommonlyWrittenForPlayback()
Definition: AudioIO.cpp:1885

References GetCommonlyFreePlayback(), AudioIoCallback::GetCommonlyWrittenForPlayback(), min(), AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOwningProject, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackSamplesToCopy, AudioIoCallback::mpTransportState, and ProcessPlaybackSlices().

Referenced by SequenceBufferExchange().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Get()

AudioIO * AudioIO::Get ( )
static

Definition at line 126 of file AudioIO.cpp.

127{
128 return static_cast< AudioIO* >( AudioIOBase::Get() );
129}
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94

References AudioIOBase::Get().

Referenced by AdornedRulerPanel::AdornedRulerPanel(), audacityAudioCallback(), AudioThread(), Scrubber::CanScrub(), ProjectAudioManager::CanStopAudioStream(), CaptureNotBusyFlag(), EffectUIHost::CleanupRealtime(), Scrubber::ContinueScrubbingPoll(), DefaultSpeedPlayOptions(), anonymous_namespace{SelectMenus.cpp}::DoBoundaryMove(), PlayIndicatorOverlayBase::DoGetRectangle(), Scrubber::DoKeyboardScrub(), ProjectAudioManager::DoPlayStopSelect(), ProjectAudioManager::DoRecord(), PlayIndicatorOverlayBase::Draw(), EffectPreview(), ControlToolBar::EnableDisableButtons(), TranscriptionToolBar::EnableDisableButtons(), ScrubbingPlaybackPolicy::GetPlaybackSlice(), UpdateManager::GetUpdates(), HistoryDialog::HistoryDialog(), EffectUIHost::InitializeInstance(), Scrubber::MaybeStartScrubbing(), ProjectManager::New(), RealtimeEffectListWindow::OnAddEffectClicked(), anonymous_namespace{LabelMenus.cpp}::OnAddLabelPlaying(), ProjectAudioManager::OnAudioIOStopRecording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::OnChangeButtonClicked(), ProjectManager::OnCloseWindow(), SelectActions::Handler::OnCursorPositionStore(), anonymous_namespace{PluginMenus.cpp}::OnDetectUpstreamDropouts(), TimeToolBar::OnIdle(), AudacityApp::OnKeyDown(), ProjectAudioManager::OnPause(), anonymous_namespace{TransportMenus.cpp}::OnPunchAndRoll(), SelectActions::Handler::OnSelectCursorStoredCursor(), SelectUtilities::OnSetRegion(), anonymous_namespace{PluginMenus.cpp}::OnSimulateRecordingErrors(), ProjectAudioManager::OnSoundActivationThreshold(), ProjectWindow::PlaybackScroller::OnTimer(), MixerBoard::OnTimer(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), anonymous_namespace{ViewMenus.cpp}::OnZoomIn(), TransportUtilities::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), TransportUtilities::PlayPlayRegionAndWait(), TransportUtilities::RecordAndWait(), ProjectAudioManager::Recording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::RemoveFromList(), ScrubbingPlaybackPolicy::RepositionPlayback(), TimerRecordDialog::RunWaitDialog(), anonymous_namespace{SelectMenus.cpp}::SeekWhenAudioActive(), MeterPanel::SetMixer(), Scrubber::StartKeyboardScrubbing(), MeterPanel::StartMonitoring(), StartMonitoring(), ControlToolBar::StartScrolling(), ProjectAudioManager::Stop(), MeterPanel::StopMonitoring(), Scrubber::StopScrubbing(), anonymous_namespace{PluginMenus.cpp}::ToolsMenu(), TrackPanel::TrackPanel(), MeterPanel::UpdateSelectedPrefs(), and MeterPanel::UpdateSliderControl().

Here is the call graph for this function:

◆ GetBestRate()

double AudioIO::GetBestRate ( bool  capturing,
bool  playing,
double  sampleRate 
)

Return a valid sample rate that is supported by the current I/O device(s).

The return from this function is used to determine the sample rate that audacity actually runs the audio I/O stream at. if there is no suitable rate available from the hardware, it returns 0. The sampleRate argument gives the desired sample rate (the rate of the audio to be handled, i.e. the currently Project Rate). capturing is true if the stream is capturing one or more audio channels, and playing is true if one or more channels are being played.

Definition at line 1701 of file AudioIO.cpp.

1702{
1703 // Check if we can use the cached value
1705 && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
1706 return mCachedBestRateOut;
1707 }
1708
1709 // In order to cache the value, all early returns should instead set retval
1710 // and jump to finished
1711 double retval;
1712
1713 std::vector<long> rates;
1714 if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
1715 if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
1716 wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
1717
1718 if (capturing && !playing) {
1720 }
1721 else if (playing && !capturing) {
1723 }
1724 else { // we assume capturing and playing - the alternative would be a
1725 // bit odd
1726 rates = GetSupportedSampleRates(-1, -1, sampleRate);
1727 }
1728 /* rem rates is the array of hardware-supported sample rates (in the current
1729 * configuration), sampleRate is the Project Rate (desired sample rate) */
1730 long rate = (long)sampleRate;
1731
1732 if (make_iterator_range(rates).contains(rate)) {
1733 wxLogDebug(wxT("GetBestRate() Returning %.0ld Hz"), rate);
1734 retval = rate;
1735 goto finished;
1736 /* the easy case - the suggested rate (project rate) is in the list, and
1737 * we can just accept that and send back to the caller. This should be
1738 * the case for most users most of the time (all of the time on
1739 * Win MME as the OS does resampling) */
1740 }
1741
1742 /* if we get here, there is a problem - the project rate isn't supported
1743 * on our hardware, so we can't us it. Need to come up with an alternative
1744 * rate to use. The process goes like this:
1745 * * If there are no rates to pick from, we're stuck and return 0 (error)
1746 * * If there are some rates, we pick the next one higher than the requested
1747 * rate to use.
1748 * * If there aren't any higher, we use the highest available rate */
1749
1750 if (rates.empty()) {
1751 /* we're stuck - there are no supported rates with this hardware. Error */
1752 wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
1753 retval = 0.0;
1754 goto finished;
1755 }
1756 int i;
1757 for (i = 0; i < (int)rates.size(); i++) // for each supported rate
1758 {
1759 if (rates[i] > rate) {
1760 // supported rate is greater than requested rate
1761 wxLogDebug(wxT("GetBestRate() Returning next higher rate - %.0ld Hz"), rates[i]);
1762 retval = rates[i];
1763 goto finished;
1764 }
1765 }
1766
1767 wxLogDebug(wxT("GetBestRate() Returning highest rate - %.0ld Hz"), rates.back());
1768 retval = rates.back(); // the highest available rate
1769 goto finished;
1770
1771finished:
1773 mCachedBestRateOut = retval;
1774 mCachedBestRatePlaying = playing;
1775 mCachedBestRateCapturing = capturing;
1776 return retval;
1777}
wxT("CloseDown"))
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1, double rate=0.0)
Get a list of sample rates the input (recording) device supports.
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1, double rate=0.0)
Get a list of sample rates the output (playback) device supports.
static double mCachedBestRateIn
Definition: AudioIOBase.h:282
static std::vector< long > GetSupportedSampleRates(int playDevice=-1, int recDevice=-1, double rate=0.0)
Get a list of sample rates the current input/output device combination supports.
static double mCachedBestRateOut
Definition: AudioIO.h:362
static bool mCachedBestRatePlaying
Definition: AudioIO.h:363
static bool mCachedBestRateCapturing
Definition: AudioIO.h:364

References AudioIOBase::GetSupportedCaptureRates(), AudioIOBase::GetSupportedPlaybackRates(), AudioIOBase::GetSupportedSampleRates(), make_iterator_range(), AudioIoCallback::mCachedBestRateCapturing, AudioIOBase::mCachedBestRateIn, AudioIoCallback::mCachedBestRateOut, AudioIoCallback::mCachedBestRatePlaying, anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, and wxT().

Referenced by StartPortAudioStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetCaptureFormat()

sampleFormat AudioIO::GetCaptureFormat ( )
inline

Definition at line 532 of file AudioIO.h.

532{ return mCaptureFormat; }

◆ GetCommonlyAvailCapture()

size_t AudioIO::GetCommonlyAvailCapture ( )
private

Get the number of audio samples ready in all of the recording buffers.

Returns the smallest of the number of samples available for storage in the recording buffers (i.e. the number of samples that can be read from all record buffers without underflow).

Definition at line 1890 of file AudioIO.cpp.

1891{
1893}
static size_t MinValue(const RingBuffers &buffers, size_t(RingBuffer::*pmf)() const)
Definition: AudioIO.cpp:1863
size_t AvailForGet() const
Definition: RingBuffer.cpp:226

References RingBuffer::AvailForGet(), AudioIoCallback::mCaptureBuffers, and AudioIoCallback::MinValue().

Referenced by DrainRecordBuffers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetCommonlyFreePlayback()

size_t AudioIO::GetCommonlyFreePlayback ( )
private

Get the number of audio samples free in all of the playback buffers.

Returns the smallest of the buffer free space values in the event that they are different.

Definition at line 1872 of file AudioIO.cpp.

1873{
1874 auto commonlyAvail = MinValue(mPlaybackBuffers, &RingBuffer::AvailForPut);
1875 // MB: subtract a few samples because the code in SequenceBufferExchange has rounding
1876 // errors
1877 return commonlyAvail - std::min(size_t(10), commonlyAvail);
1878}
size_t AvailForPut() const
Definition: RingBuffer.cpp:64

References RingBuffer::AvailForPut(), min(), AudioIoCallback::MinValue(), and AudioIoCallback::mPlaybackBuffers.

Referenced by FillPlayBuffers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetInputSourceNames()

wxArrayString AudioIO::GetInputSourceNames ( )

Get the list of inputs to the current mixer device.

Returns an array of strings giving the names of the inputs to the soundcard mixer (driven by PortMixer)

Definition at line 432 of file AudioIO.cpp.

433{
434#if defined(USE_PORTMIXER)
435
436 wxArrayString deviceNames;
437
438 if( mPortMixer )
439 {
440 int numSources = Px_GetNumInputSources(mPortMixer);
441 for( int source = 0; source < numSources; source++ )
442 deviceNames.push_back(wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(mPortMixer, source))));
443 }
444 else
445 {
446 wxLogDebug(wxT("AudioIO::GetInputSourceNames(): PortMixer not initialised!"));
447 }
448
449 return deviceNames;
450
451#else
452
453 wxArrayString blank;
454
455 return blank;
456
457#endif
458}

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 501 of file AudioIO.h.

501{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

void AudioIO::GetMixer ( int *  inputSource,
float *  inputVolume,
float *  playbackVolume 
)

Definition at line 400 of file AudioIO.cpp.

402{
403 *playbackVolume = GetMixerOutputVol();
404
405#if defined(USE_PORTMIXER)
406
407 PxMixer *mixer = mPortMixer;
408
409 if( mixer )
410 {
411 *recordDevice = Px_GetCurrentInputSource(mixer);
412
414 *recordVolume = Px_GetInputVolume(mixer);
415 else
416 *recordVolume = 1.0f;
417
418 return;
419 }
420
421#endif
422
423 *recordDevice = 0;
424 *recordVolume = 1.0f;
425}
float GetMixerOutputVol()
Definition: AudioIO.h:348

References AudioIoCallback::GetMixerOutputVol(), and AudioIOBase::mInputMixerWorks.

Here is the call graph for this function:

◆ GetNumCaptureChannels()

size_t AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 534 of file AudioIO.h.

534{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

size_t AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 533 of file AudioIO.h.

533{ return mNumPlaybackChannels; }

◆ GetOwningProject()

std::shared_ptr< AudacityProject > AudioIO::GetOwningProject ( ) const
inline

Definition at line 502 of file AudioIO.h.

503 { return mOwningProject.lock(); }

Referenced by AddState(), RemoveState(), and ReplaceState().

Here is the caller graph for this function:

◆ GetStreamTime()

double AudioIO::GetStreamTime ( )

During playback, the sequence time most recently played.

When playing looped, this will start from t0 again, too. So the returned time should be always between t0 and t1

Definition at line 1779 of file AudioIO.cpp.

1780{
1781 // Sequence time readout for the main thread
1782
1783 if( !IsStreamActive() )
1784 return BAD_STREAM_TIME;
1785
1787}
#define BAD_STREAM_TIME
Definition: AudioIOBase.h:38
double GetSequenceTime() const
Get current track time value, unadjusted.

References BAD_STREAM_TIME, PlaybackSchedule::GetSequenceTime(), AudioIOBase::IsStreamActive(), and AudioIoCallback::mPlaybackSchedule.

Here is the call graph for this function:

◆ Init()

void AudioIO::Init ( )
static

Definition at line 190 of file AudioIO.cpp.

191{
192 auto pAudioIO = safenew AudioIO();
193 ugAudioIO.reset(pAudioIO);
194 pAudioIO->StartThread();
195
196 // Make sure device prefs are initialized
197 if (gPrefs->Read(wxT("AudioIO/RecordingDevice"), wxT("")).empty()) {
198 int i = getRecordDevIndex();
199 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
200 if (info) {
203 }
204 }
205
206 if (gPrefs->Read(wxT("AudioIO/PlaybackDevice"), wxT("")).empty()) {
207 int i = getPlayDevIndex();
208 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
209 if (info) {
212 }
213 }
214
215 gPrefs->Flush();
216}
StringSetting AudioIOPlaybackDevice
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
#define safenew
Definition: MemoryX.h:10
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:83
static int getPlayDevIndex(const wxString &devName={})
get the index of the device selected in the preferences.
static wxString DeviceName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:76
static int getRecordDevIndex(const wxString &devName={})
get the index of the supplied (named) recording device, or the device selected in the preferences if ...
AudioIO()
Definition: AudioIO.cpp:232
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
virtual bool Flush() noexcept=0
virtual bool Read(const wxString &key, bool *value) const =0

References AudioIO(), AudioIOHost, AudioIOPlaybackDevice, AudioIORecordingDevice, AudioIOBase::DeviceName(), audacity::BasicSettings::Flush(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), gPrefs, AudioIOBase::HostName(), audacity::BasicSettings::Read(), safenew, AudioIOBase::ugAudioIO, Setting< T >::Write(), and wxT().

Referenced by AudacityApp::InitPart2().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ InputMixerWorks()

bool AudioIO::InputMixerWorks ( )

Find out if the input hardware level control is available.

Checks the mInputMixerWorks variable, which is set up in AudioIOBase::HandleDeviceChange(). External people care, because we want to disable the UI if it doesn't work.

Definition at line 427 of file AudioIO.cpp.

428{
429 return mInputMixerWorks;
430}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1409 of file AudioIO.cpp.

1410{
1411 auto pOwningProject = mOwningProject.lock();
1412 return !pOwningProject || pOwningProject.get() == &project;
1413}

References AudioIOBase::mOwningProject, and project.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3345 of file AudioIO.cpp.

3346{
3347 // Includes a test of mTime, used in the main thread
3348 return IsStreamActive() &&
3349 GetNumCaptureChannels() > 0 &&
3352}
size_t GetNumCaptureChannels() const
Definition: AudioIO.h:534

References PlaybackSchedule::GetSequenceTime(), AudioIOBase::IsStreamActive(), AudioIoCallback::mPlaybackSchedule, RecordingSchedule::mPreRoll, AudioIoCallback::mRecordingSchedule, and PlaybackSchedule::mT0.

Here is the call graph for this function:

◆ LastPaErrorString()

wxString AudioIO::LastPaErrorString ( )

Definition at line 754 of file AudioIO.cpp.

755{
756 return wxString::Format(wxT("%d %s."), (int) mLastPaError, Pa_GetErrorText(mLastPaError));
757}

References AudioIoCallback::mLastPaError, and wxT().

Referenced by StartMonitoring().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProcessPlaybackSlices()

bool AudioIO::ProcessPlaybackSlices ( std::optional< RealtimeEffects::ProcessingScope > &  pScope,
size_t  available 
)
private

Definition at line 1982 of file AudioIO.cpp.

1984{
1985 auto &policy = mPlaybackSchedule.GetPolicy();
1986
1987 // msmeyer: When playing a very short selection in looped
1988 // mode, the selection must be copied to the buffer multiple
1989 // times, to ensure, that the buffer has a reasonable size
1990 // This is the purpose of this loop.
1991 // PRL: or, when scrubbing, we may get work repeatedly from the
1992 // user interface.
1993 bool done = false;
1994 bool progress = false;
1995 do {
1996 const auto slice =
1997 policy.GetPlaybackSlice(mPlaybackSchedule, available);
1998 const auto &[frames, toProduce] = slice;
1999 progress = progress || toProduce > 0;
2000
2001 // Update the time queue. This must be done before writing to the
2002 // ring buffers of samples, for proper synchronization with the
2003 // consumer side in the PortAudio thread, which reads the time
2004 // queue after reading the sample queues. The sample queues use
2005 // atomic variables, the time queue doesn't.
2007
2008 // mPlaybackMixers correspond one-to-one with mPlaybackSequences
2009 size_t iSequence = 0;
2010 // mPlaybackBuffers correspond many-to-one with mPlaybackSequences
2011 size_t iBuffer = 0;
2012 for (auto &mixer : mPlaybackMixers) {
2013 // The mixer here isn't actually mixing: it's just doing
2014 // resampling, format conversion, and possibly time track
2015 // warping
2016 if (frames > 0) {
2017 size_t produced = 0;
2018 if (toProduce)
2019 produced = mixer->Process(toProduce);
2020 //wxASSERT(produced <= toProduce);
2021 // Copy (non-interleaved) mixer outputs to one or more ring buffers
2022 const auto nChannels = mPlaybackSequences[iSequence++]->NChannels();
2023 for (size_t j = 0; j < nChannels; ++j) {
2024 auto warpedSamples = mixer->GetBuffer(j);
2025 const auto put = mPlaybackBuffers[iBuffer++]->Put(
2026 warpedSamples, floatSample, produced, frames - produced);
2027 // wxASSERT(put == frames);
2028 // but we can't assert in this thread
2029 wxUnusedVar(put);
2030 }
2031 }
2032 }
2033
2034 if (mPlaybackSequences.empty())
2035 // Produce silence in the single ring buffer
2036 mPlaybackBuffers[0]->Put(nullptr, floatSample, 0, frames);
2037
2038 available -= frames;
2039 // wxASSERT(available >= 0); // don't assert on this thread
2040
2041 done = policy.RepositionPlayback( mPlaybackSchedule, mPlaybackMixers,
2042 frames, available );
2043 } while (available && !done);
2044
2045 // Do any realtime effect processing, more efficiently in at most
2046 // two buffers per sequence, after all the little slices have been written.
2047 TransformPlayBuffers(pScope);
2048 return progress;
2049}
void TransformPlayBuffers(std::optional< RealtimeEffects::ProcessingScope > &scope)
Definition: AudioIO.cpp:2053
virtual PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available)
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
void Producer(PlaybackSchedule &schedule, PlaybackSlice slice)
Enqueue track time value advanced by the slice according to schedule's PlaybackPolicy.

References floatSample, PlaybackPolicy::GetPlaybackSlice(), PlaybackSchedule::GetPolicy(), AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, PlaybackSchedule::mTimeQueue, PlaybackSchedule::TimeQueue::Producer(), and TransformPlayBuffers().

Referenced by FillPlayBuffers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ RemoveState()

void AudioIO::RemoveState ( AudacityProject project,
ChannelGroup pGroup,
std::shared_ptr< RealtimeEffectState pState 
)

Forwards to RealtimeEffectManager::RemoveState with proper init scope.

Definition at line 369 of file AudioIO.cpp.

372{
374 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
375 if (auto pProject = GetOwningProject(); pProject.get() == &project)
376 pInit = &*mpTransportState->mpRealtimeInitialization;
377 RealtimeEffectManager::Get(project).RemoveState(pInit, pGroup, pState);
378}
void RemoveState(RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, std::shared_ptr< RealtimeEffectState > pState)
Main thread removes a global or per-group effect.

References RealtimeEffectManager::Get(), GetOwningProject(), AudioIoCallback::mpTransportState, project, and RealtimeEffectManager::RemoveState().

Referenced by EffectUIHost::CleanupRealtime(), and anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::RemoveFromList().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReplaceState()

std::shared_ptr< RealtimeEffectState > AudioIO::ReplaceState ( AudacityProject project,
ChannelGroup pGroup,
size_t  index,
const PluginID id 
)

Forwards to RealtimeEffectManager::ReplaceState with proper init scope.

Postcondition
result: !result || result->GetEffect() != nullptr

Definition at line 358 of file AudioIO.cpp.

360{
362 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
363 if (auto pProject = GetOwningProject(); pProject.get() == &project)
364 pInit = &*mpTransportState->mpRealtimeInitialization;
366 .ReplaceState(pInit, pGroup, index, id);
367}
std::shared_ptr< RealtimeEffectState > ReplaceState(RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, size_t index, const PluginID &id)
Main thread replaces a global or per-group effect.

References RealtimeEffectManager::Get(), GetOwningProject(), AudioIoCallback::mpTransportState, project, and RealtimeEffectManager::ReplaceState().

Here is the call graph for this function:

◆ ResetOwningProject()

void AudioIO::ResetOwningProject ( )
private

Definition at line 770 of file AudioIO.cpp.

771{
772 mOwningProject.reset();
773}

References AudioIOBase::mOwningProject.

Referenced by SetOwningProject(), StartPortAudioStream(), StopStream(), and ~AudioIO().

Here is the caller graph for this function:

◆ SeekStream()

void AudioIO::SeekStream ( double  seconds)
inline

Move the playback / recording position of the current stream by the specified amount from where it is now.

Definition at line 490 of file AudioIO.h.

490{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:293

◆ SequenceBufferExchange()

void AudioIO::SequenceBufferExchange ( )
private

Called in a loop from another worker thread that does not have the low-latency constraints of the PortAudio callback thread. Does less frequent and larger batches of work that may include memory allocations and database operations. RingBuffer objects mediate the transfer between threads, to overcome the mismatch of their batch sizes.

Definition at line 1898 of file AudioIO.cpp.

1899{
1902}
void DrainRecordBuffers()
Second part of SequenceBufferExchange.
Definition: AudioIO.cpp:2118
void FillPlayBuffers()
First part of SequenceBufferExchange.
Definition: AudioIO.cpp:1904

References DrainRecordBuffers(), and FillPlayBuffers().

Referenced by AudioThread().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetMeters()

void AudioIO::SetMeters ( )
private

Set the current VU meters - this should be done once after each call to StartStream currently.

Definition at line 1415 of file AudioIO.cpp.

1416{
1417 if (auto pInputMeter = mInputMeter.lock())
1418 pInputMeter->Reset(mRate, true);
1419 if (auto pOutputMeter = mOutputMeter.lock())
1420 pOutputMeter->Reset(mRate, true);
1421}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:259

References AudioIOBase::mInputMeter, AudioIOBase::mOutputMeter, and AudioIOBase::mRate.

Referenced by StartPortAudioStream().

Here is the caller graph for this function:

◆ SetMixer()

void AudioIO::SetMixer ( int  inputSource,
float  inputVolume,
float  playbackVolume 
)

Definition at line 380 of file AudioIO.cpp.

382{
383 SetMixerOutputVol(playbackVolume);
384 AudioIOPlaybackVolume.Write(playbackVolume);
385
386#if defined(USE_PORTMIXER)
387 PxMixer *mixer = mPortMixer;
388 if( !mixer )
389 return;
390
391 float oldRecordVolume = Px_GetInputVolume(mixer);
392
393 AudioIoCallback::SetMixer(inputSource);
394 if( oldRecordVolume != recordVolume )
395 Px_SetInputVolume(mixer, recordVolume);
396
397#endif
398}
void SetMixer(int inputSource)

References AudioIOPlaybackVolume, AudioIOBase::SetMixer(), AudioIoCallback::SetMixerOutputVol(), and Setting< T >::Write().

Here is the call graph for this function:

◆ SetOwningProject()

void AudioIO::SetOwningProject ( const std::shared_ptr< AudacityProject > &  pProject)
private

Definition at line 759 of file AudioIO.cpp.

761{
762 if ( !mOwningProject.expired() ) {
763 wxASSERT(false);
765 }
766
767 mOwningProject = pProject;
768}

References AudioIOBase::mOwningProject, and ResetOwningProject().

Referenced by StartPortAudioStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetPaused()

void AudioIO::SetPaused ( bool  state)

Pause and un-pause playback and recording.

Definition at line 1686 of file AudioIO.cpp.

1687{
1688 if (state != IsPaused())
1689 {
1690 if (auto pOwningProject = mOwningProject.lock()) {
1691 // The realtime effects manager may remain "active" but becomes
1692 // "suspended" or "resumed".
1693 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1694 em.SetSuspended(state);
1695 }
1696 }
1697
1698 mPaused.store(state, std::memory_order_relaxed);
1699}
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:248
bool IsPaused() const
Find out if playback / recording is currently paused.

References RealtimeEffectManager::Get(), AudioIOBase::IsPaused(), AudioIOBase::mOwningProject, and AudioIOBase::mPaused.

Here is the call graph for this function:

◆ StartMonitoring()

void AudioIO::StartMonitoring ( const AudioIOStartStreamOptions options)

Start up Portaudio for capture and recording as needed for input monitoring and software playthrough only.

This uses the Default project sample format, current sample rate, and selected number of input channels to open the recording device and start reading input data. If software playthrough is enabled, it also opens the output device in stereo to play the data through

Definition at line 775 of file AudioIO.cpp.

776{
778 return;
779
780 bool success;
781 auto captureFormat = QualitySettings::SampleFormatChoice();
782 auto captureChannels = AudioIORecordChannels.Read();
783 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
784 int playbackChannels = 0;
785
787 playbackChannels = 2;
788
789 // FIXME: TRAP_ERR StartPortAudioStream (a PaError may be present)
790 // but StartPortAudioStream function only returns true or false.
791 mUsingAlsa = false;
792 mCaptureFormat = captureFormat;
793 mCaptureRate = 44100.0; // Shouldn't matter
794 success = StartPortAudioStream(options,
795 static_cast<unsigned int>(playbackChannels),
796 static_cast<unsigned int>(captureChannels));
797
798 auto pOwningProject = mOwningProject.lock();
799 if (!success) {
800 using namespace BasicUI;
801 auto msg = XO("Error opening recording device.\nError code: %s")
802 .Format( Get()->LastPaErrorString() );
803 ShowErrorDialog( *ProjectFramePlacement( pOwningProject.get() ),
804 XO("Error"), msg, wxT("Error_opening_sound_device"),
805 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
806 return;
807 }
808
809 Publish({ pOwningProject.get(), AudioIOEvent::MONITOR, true });
810
811 // FIXME: TRAP_ERR PaErrorCode 'noted' but not reported in StartMonitoring.
812 // Now start the PortAudio stream!
813 // TODO: ? Factor out and reuse error reporting code from end of
814 // AudioIO::StartStream?
815 mLastPaError = Pa_StartStream( mPortStreamV19 );
816
817 // Update UI display only now, after all possibilities for error are past.
818 auto pListener = GetListener();
819 if ((mLastPaError == paNoError) && pListener) {
820 // advertise the chosen I/O sample rate to the UI
821 pListener->OnAudioIORate((int)mRate);
822 }
823}
IntSetting AudioIORecordChannels
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
int mStreamToken
Definition: AudioIOBase.h:251
bool StartPortAudioStream(const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels)
Opens the portaudio stream(s) used to do playback or recording (or both) through.
Definition: AudioIO.cpp:473
wxString LastPaErrorString()
Definition: AudioIO.cpp:754
bool mSoftwarePlaythrough
Definition: AudioIO.h:306
bool mUsingAlsa
Definition: AudioIO.h:358
CallbackReturn Publish(const AudioIOEvent &message)
Send a message to connected callbacks.
Definition: Observer.h:207
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:264
PROJECT_RATE_API sampleFormat SampleFormatChoice()
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:52

References AudioIORecordChannels, Get(), AudioIoCallback::GetListener(), gPrefs, LastPaErrorString(), AudioIoCallback::mCaptureFormat, AudioIoCallback::mCaptureRate, AudioIoCallback::mLastPaError, AudioIOEvent::MONITOR, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, AudioIoCallback::mUsingAlsa, ProjectFramePlacement(), Observer::Publisher< AudioIOEvent >::Publish(), audacity::BasicSettings::Read(), Setting< T >::Read(), QualitySettings::SampleFormatChoice(), BasicUI::ShowErrorDialog(), StartPortAudioStream(), wxT(), and XO().

Here is the call graph for this function:

◆ StartPortAudioStream()

bool AudioIO::StartPortAudioStream ( const AudioIOStartStreamOptions options,
unsigned int  numPlaybackChannels,
unsigned int  numCaptureChannels 
)
private

Opens the portaudio stream(s) used to do playback or recording (or both) through.

The sampleRate passed is the Project Rate of the active project. It may or may not be actually supported by playback or recording hardware currently in use (for many reasons). The number of Capture and Playback channels requested includes an allocation for doing software playthrough if necessary. The captureFormat is used for recording only, the playback being floating point always. Returns true if the stream opened successfully and false if it did not.

Definition at line 473 of file AudioIO.cpp.

475{
476 auto sampleRate = options.rate;
477 mNumPauseFrames = 0;
478 SetOwningProject( options.pProject );
479 bool success = false;
480 auto cleanup = finally([&]{
481 if (!success)
483 });
484
485 // PRL: Protection from crash reported by David Bailes, involving starting
486 // and stopping with frequent changes of active window, hard to reproduce
487 if (mOwningProject.expired())
488 return false;
489
490 mInputMeter.reset();
491 mOutputMeter.reset();
492
493 mLastPaError = paNoError;
494 // pick a rate to do the audio I/O at, from those available. The project
495 // rate is suggested, but we may get something else if it isn't supported
496 mRate = GetBestRate(numCaptureChannels > 0, numPlaybackChannels > 0, sampleRate);
497
498 // GetBestRate() will return 0.0 for bidirectional streams when there is no
499 // common sample rate supported by both the input and output devices.
500 // Bidirectional streams are used when recording while overdub or
501 // software playthrough options are enabled.
502
503 // Pa_OpenStream() will return paInvalidSampleRate when trying to create the
504 // bidirectional stream with a sampleRate of 0.0
505 bool isStreamBidirectional = (numCaptureChannels > 0) && (numPlaybackChannels > 0);
506 bool isUnsupportedSampleRate = isStreamBidirectional && (mRate == 0.0);
507
508 // July 2016 (Carsten and Uwe)
509 // BUG 193: Tell PortAudio sound card will handle 24 bit (under DirectSound) using
510 // userData.
511 auto captureFormat = mCaptureFormat;
512 auto captureFormat_saved = captureFormat;
513 // Special case: Our 24-bit sample format is different from PortAudio's
514 // 3-byte packed format. So just make PortAudio return float samples,
515 // since we need float values anyway to apply the gain.
516 // ANSWER-ME: So we *never* actually handle 24-bit?! This causes mCapture to
517 // be set to floatSample below.
518 // JKC: YES that's right. Internally Audacity uses float, and float has space for
519 // 24 bits as well as exponent. Actual 24 bit would require packing and
520 // unpacking unaligned bytes and would be inefficient.
521 // ANSWER ME: is floatSample 64 bit on 64 bit machines?
522 if (captureFormat == int24Sample)
523 {
524 captureFormat = floatSample;
525 // mCaptureFormat is overwritten before the call to
526 // StartPortAudioStream. So the hack with captureFormat_saved is
527 // still working (assuming it worked before).
528 mCaptureFormat = captureFormat;
529 }
530
531 mNumPlaybackChannels = numPlaybackChannels;
532 mNumCaptureChannels = numCaptureChannels;
533
534 bool usePlayback = false, useCapture = false;
535 PaStreamParameters playbackParameters{};
536 PaStreamParameters captureParameters{};
537
538 #ifdef __WXMSW__
539 PaWasapiStreamInfo wasapiStreamInfo{};
540 #endif
541
542 auto latencyDuration = AudioIOLatencyDuration.Read();
543
544 if( numPlaybackChannels > 0)
545 {
546 usePlayback = true;
547
548 // this sets the device index to whatever is "right" based on preferences,
549 // then defaults
550 playbackParameters.device = getPlayDevIndex();
551
552 const PaDeviceInfo *playbackDeviceInfo;
553 playbackDeviceInfo = Pa_GetDeviceInfo( playbackParameters.device );
554
555 if( playbackDeviceInfo == NULL )
556 return false;
557
558 // regardless of source formats, we always mix to float
559 playbackParameters.sampleFormat = paFloat32;
560 playbackParameters.hostApiSpecificStreamInfo = NULL;
561 playbackParameters.channelCount = mNumPlaybackChannels;
562
563 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(playbackDeviceInfo->hostApi);
564 bool isWASAPI = (hostInfo && hostInfo->type == paWASAPI);
565
566 #ifdef __WXMSW__
567 // If the host API is WASAPI, the stream is bidirectional and there is no
568 // supported sample rate enable the WASAPI Sample Rate Conversion
569 // for the playback device.
570 if (isWASAPI && isUnsupportedSampleRate)
571 {
572 wasapiStreamInfo.size = sizeof(PaWasapiStreamInfo);
573 wasapiStreamInfo.hostApiType = paWASAPI;
574 wasapiStreamInfo.version = 1;
575 wasapiStreamInfo.flags = paWinWasapiAutoConvert;
576
577 playbackParameters.hostApiSpecificStreamInfo = &wasapiStreamInfo;
578 }
579 #endif
580
582 playbackParameters.suggestedLatency =
583 playbackDeviceInfo->defaultLowOutputLatency;
584 else {
585 // When using WASAPI, the suggested latency does not affect
586 // the latency of the playback, but the position of playback is given as if
587 // there was the suggested latency. This results in the last "suggested latency"
588 // of a selection not being played. So for WASAPI use 0.0 for the suggested
589 // latency regardless of user setting. See bug 1949.
590 playbackParameters.suggestedLatency = isWASAPI ? 0.0 : latencyDuration/1000.0;
591 }
592
593 mOutputMeter = options.playbackMeter;
594 }
595
596 if( numCaptureChannels > 0)
597 {
598 useCapture = true;
599
600 const PaDeviceInfo *captureDeviceInfo;
601 // retrieve the index of the device set in the prefs, or a sensible
602 // default if it isn't set/valid
603 captureParameters.device = getRecordDevIndex();
604
605 captureDeviceInfo = Pa_GetDeviceInfo( captureParameters.device );
606
607 if( captureDeviceInfo == NULL )
608 return false;
609
610 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(captureDeviceInfo->hostApi);
611 bool isWASAPI = (hostInfo && hostInfo->type == paWASAPI);
612
613 // If the stream is bidirectional and there is no supported sample rate
614 // set mRate to the value supported by the capture device.
615 if (isWASAPI && isUnsupportedSampleRate)
616 mRate = captureDeviceInfo->defaultSampleRate;
617
618 captureParameters.sampleFormat =
620
621 captureParameters.hostApiSpecificStreamInfo = NULL;
622 captureParameters.channelCount = mNumCaptureChannels;
623
625 captureParameters.suggestedLatency =
626 captureDeviceInfo->defaultHighInputLatency;
627 else
628 captureParameters.suggestedLatency = latencyDuration/1000.0;
629
631 }
632
633 const auto deviceInfo = usePlayback ?
634 Pa_GetDeviceInfo(playbackParameters.device) :
635 Pa_GetDeviceInfo(captureParameters.device);
636
637 if (deviceInfo != nullptr)
638 {
639 const auto hostApiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
640
641 if (hostApiInfo)
642 {
643 mUsingAlsa = hostApiInfo->type == paALSA;
644 mUsingJack = hostApiInfo->type == paJACK;
645 }
646 }
647
648 SetMeters();
649
650#ifdef USE_PORTMIXER
651#ifdef __WXMSW__
652 //mchinen nov 30 2010. For some reason Pa_OpenStream resets the input volume on windows.
653 //so cache and restore after it.
654 //The actual problem is likely in portaudio's pa_win_wmme.c OpenStream().
655 float oldRecordVolume = Px_GetInputVolume(mPortMixer);
656#endif
657#endif
658
659 // July 2016 (Carsten and Uwe)
660 // BUG 193: Possibly tell portAudio to use 24 bit with DirectSound.
661 int userData = 24;
662 int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
663
664 // (Linux, bug 1885) After scanning devices it takes a little time for the
665 // ALSA device to be available, so allow retries.
666 // On my test machine, no more than 3 attempts are required.
667 unsigned int maxTries = 1;
668#ifdef __WXGTK__
669 {
670 using namespace std::chrono;
672 maxTries = 5;
673 }
674#endif
675
676 for (unsigned int tries = 0; tries < maxTries; tries++) {
677 mLastPaError = Pa_OpenStream( &mPortStreamV19,
678 useCapture ? &captureParameters : NULL,
679 usePlayback ? &playbackParameters : NULL,
680 mRate, paFramesPerBufferUnspecified,
681 paNoFlag,
682 audacityAudioCallback, lpUserData );
683 if (mLastPaError == paNoError) {
684 const auto stream = Pa_GetStreamInfo(mPortStreamV19);
685 // Use the reported latency as a hint about the hardware buffer size
686 // required for uninterrupted playback.
687 const auto outputLatency =
688 mUsingJack ?
689 // When using Jack as a host, PA calculates the wrong latency
690 // if a non system port is used. Assume, that Jack provides a very
691 // low latency, lower than user requested
692 // (https://github.com/audacity/audacity/issues/4646)
693 (latencyDuration / 1000.0) :
694 // Otherwise, use the (likely incorrect) latency reported by PA
695 stream->outputLatency;
696
697 mHardwarePlaybackLatencyFrames = lrint(outputLatency * mRate);
698#ifdef __WXGTK__
699 // DV: When using ALSA PortAudio does not report the buffer size.
700 // Instead, it reports periodSize * (periodsCount - 1). It is impossible
701 // to retrieve periodSize or periodsCount either. By default PA sets
702 // periodsCount to 4. However it was observed, that PA reports back ~100msec
703 // latency and expects a buffer of ~200msecs on Audacity default settings
704 // which suggests that ALSA changes the periodsCount to suit its needs.
705 //
706 // Why 3? 2 doesn't work for me, 3 does :-) So similar to PA - this
707 // is the value that works for author setup.
708 if (mUsingAlsa)
710#endif
711 break;
712 }
713 wxLogDebug("Attempt %u to open capture stream failed with: %d", 1 + tries, mLastPaError);
714 using namespace std::chrono;
715 std::this_thread::sleep_for(1s);
716 }
717
718
719#if USE_PORTMIXER
720#ifdef __WXMSW__
721 Px_SetInputVolume(mPortMixer, oldRecordVolume);
722#endif
723 if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
724
725 #ifdef __WXMAC__
726 if (mPortMixer) {
727 if (Px_SupportsPlaythrough(mPortMixer)) {
728 bool playthrough = false;
729
730 mPreviousHWPlaythrough = Px_GetPlaythrough(mPortMixer);
731
732 // Bug 388. Feature not supported.
733 //gPrefs->Read(wxT("/AudioIO/Playthrough"), &playthrough, false);
734 if (playthrough)
735 Px_SetPlaythrough(mPortMixer, 1.0);
736 else
737 Px_SetPlaythrough(mPortMixer, 0.0);
738 }
739 }
740 #endif
741 }
742#endif
743
744#if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
745 // Don't want the system to sleep while audio I/O is active
746 if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
747 wxPowerResource::Acquire(wxPOWER_RESOURCE_SCREEN, _("Audacity Audio"));
748 }
749#endif
750
751 return (success = (mLastPaError == paNoError));
752}
int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
Definition: AudioIO.cpp:2522
static PaSampleFormat AudacityToPortAudioSampleFormat(sampleFormat format)
Definition: AudioIO.cpp:460
DoubleSetting AudioIOLatencyDuration
#define _(s)
Definition: Internat.h:73
void SetCaptureMeter(const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
void SetOwningProject(const std::shared_ptr< AudacityProject > &pProject)
Definition: AudioIO.cpp:759
double GetBestRate(bool capturing, bool playing, double sampleRate)
Return a valid sample rate that is supported by the current I/O device(s).
Definition: AudioIO.cpp:1701
void SetMeters()
Set the current VU meters - this should be done once after each call to StartStream currently.
Definition: AudioIO.cpp:1415
bool mUsingJack
Definition: AudioIO.h:359
static DeviceManager * Instance()
Gets the singleton instance.
std::chrono::duration< float > GetTimeSinceRescan()
std::shared_ptr< AudacityProject > pProject
Definition: AudioIOBase.h:53
std::weak_ptr< Meter > captureMeter
Definition: AudioIOBase.h:54
std::weak_ptr< Meter > playbackMeter
Definition: AudioIOBase.h:54

References _, audacityAudioCallback(), AudacityToPortAudioSampleFormat(), AudioIOLatencyDuration, AudioIOStartStreamOptions::captureMeter, floatSample, GetBestRate(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), DeviceManager::GetTimeSinceRescan(), DeviceManager::Instance(), int24Sample, lrint, AudioIoCallback::mCaptureFormat, AudioIoCallback::mHardwarePlaybackLatencyFrames, AudioIOBase::mInputMeter, AudioIoCallback::mLastPaError, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPauseFrames, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIoCallback::mUsingAlsa, AudioIoCallback::mUsingJack, AudioIOStartStreamOptions::playbackMeter, AudioIOStartStreamOptions::pProject, AudioIOStartStreamOptions::rate, Setting< T >::Read(), ResetOwningProject(), anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, AudioIOBase::SetCaptureMeter(), SetMeters(), and SetOwningProject().

Referenced by StartMonitoring(), and StartStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ StartStream()

int AudioIO::StartStream ( const TransportSequences sequences,
double  t0,
double  t1,
double  mixerLimit,
const AudioIOStartStreamOptions options 
)

Start recording or playing back audio.

Allocates buffers for recording and playback, gets the Audio thread to fill them, and sets the stream rolling. If successful, returns a token identifying this particular stream instance. For use with IsStreamActive()

Precondition
p && p->FindChannelGroup() for all pointers p in sequences.playbackSequences
p != nullptr for all pointers p in sequences.captureSequences
Parameters
mixerLimitTime at which mixer stops producing, maybe > t1

Definition at line 825 of file AudioIO.cpp.

828{
829 // precondition
830 assert(std::all_of(
831 sequences.playbackSequences.begin(), sequences.playbackSequences.end(),
832 [](const auto &pSequence){
833 const auto pGroup =
834 pSequence ? pSequence->FindChannelGroup() : nullptr;
835 return pGroup; }
836 ));
837
838 const auto &pStartTime = options.pStartTime;
839 t1 = std::min(t1, mixerLimit);
840
841 mLostSamples = 0;
842 mLostCaptureIntervals.clear();
844 gPrefs->Read( WarningDialogKey(wxT("DropoutDetected")), true ) != 0;
845 auto cleanup = finally ( [this] { ClearRecordingException(); } );
846
847 if( IsBusy() )
848 return 0;
849
850 // We just want to set mStreamToken to -1 - this way avoids
851 // an extremely rare but possible race condition, if two functions
852 // somehow called StartStream at the same time...
853 mStreamToken--;
854 if (mStreamToken != -1)
855 return 0;
856
857 // TODO: we don't really need to close and reopen stream if the
858 // format matches; however it's kind of tricky to keep it open...
859 //
860 // if (sampleRate == mRate &&
861 // playbackChannels == mNumPlaybackChannels &&
862 // captureChannels == mNumCaptureChannels &&
863 // captureFormat == mCaptureFormat) {
864
865 if (mPortStreamV19) {
866 StopStream();
867 while(mPortStreamV19) {
868 using namespace std::chrono;
869 std::this_thread::sleep_for(50ms);
870 }
871 }
872
873 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
875 gPrefs->Read(wxT("/AudioIO/Microfades"), &mbMicroFades, false);
876 int silenceLevelDB;
877 gPrefs->Read(wxT("/AudioIO/SilenceLevel"), &silenceLevelDB, -50);
878 int dBRange = DecibelScaleCutoff.Read();
879 if(silenceLevelDB < -dBRange)
880 {
881 silenceLevelDB = -dBRange + 3;
882 // meter range was made smaller than SilenceLevel
883 // so set SilenceLevel reasonable
884
885 // PRL: update prefs, or correct it only in-session?
886 // The behavior (as of 2.3.1) was the latter, the code suggested that
887 // the intent was the former; I preserve the behavior, but uncomment
888 // this if you disagree.
889 // gPrefs->Write(wxT("/AudioIO/SilenceLevel"), silenceLevelDB);
890 // gPrefs->Flush();
891 }
892 mSilenceLevel = DB_TO_LINEAR(silenceLevelDB); // meter goes -dBRange dB -> 0dB
893
894 // Clamp pre-roll so we don't play before time 0
895 const auto preRoll = std::max(0.0, std::min(t0, options.preRoll));
901 if (options.pCrossfadeData)
903
904 mListener = options.listener;
905 mRate = options.rate;
906
907 mSeek = 0;
911
912 bool commit = false;
913 auto cleanupSequences = finally([&]{
914 if (!commit) {
915 // Don't keep unnecessary shared pointers to sequences
916 mPlaybackSequences.clear();
917 mCaptureSequences.clear();
918 for(auto &ext : Extensions())
919 ext.AbortOtherStream();
920
921 // Don't cause a busy wait in the audio thread after stopping scrubbing
923 }
924 });
925
926 mPlaybackBuffers.clear();
927 mScratchBuffers.clear();
928 mScratchPointers.clear();
929 mPlaybackMixers.clear();
930 mCaptureBuffers.clear();
931 mResample.clear();
933
935 t0, t1, options, mCaptureSequences.empty() ? nullptr : &mRecordingSchedule );
936
937 unsigned int playbackChannels = 0;
938 size_t numCaptureChannels = 0;
939 sampleFormat captureFormat = floatSample;
940 double captureRate = 44100.0;
941
942 auto pListener = GetListener();
943
944 if (sequences.playbackSequences.size() > 0
945 || sequences.otherPlayableSequences.size() > 0)
946 playbackChannels = 2;
947
949 playbackChannels = 2;
950
951 if (mCaptureSequences.size() > 0) {
952 numCaptureChannels = accumulate(
953 mCaptureSequences.begin(), mCaptureSequences.end(), size_t{},
954 [](auto acc, const auto &pSequence) {
955 return acc + pSequence->NChannels();
956 });
957 // I don't deal with the possibility of the capture sequences
958 // having different sample formats, since it will never happen
959 // with the current code. This code wouldn't *break* if this
960 // assumption was false, but it would be sub-optimal. For example,
961 // if the first sequence was 16-bit and the second sequence was 24-bit,
962 // we would set the sound card to capture in 16 bits and the second
963 // sequence wouldn't get the benefit of all 24 bits the card is capable
964 // of.
965 const auto &sequence0 = mCaptureSequences[0];
966 captureFormat = sequence0->GetSampleFormat();
967 captureRate = sequence0->GetRate();
968
969 // Tell project that we are about to start recording
970 if (pListener)
971 pListener->OnAudioIOStartRecording();
972 }
973
974 bool successAudio;
975
976 mCaptureFormat = captureFormat;
977 mCaptureRate = captureRate;
978 successAudio =
979 StartPortAudioStream(options, playbackChannels, numCaptureChannels);
980
981 // Call this only after reassignment of mRate that might happen in the
982 // previous call.
984
985 auto range = Extensions();
986 successAudio = successAudio &&
987 std::all_of(range.begin(), range.end(),
988 [this, &sequences, t0](auto &ext){
989 return ext.StartOtherStream(sequences,
990 (mPortStreamV19 != NULL && mLastPaError == paNoError)
991 ? Pa_GetStreamInfo(mPortStreamV19) : nullptr,
992 t0, mRate ); });
993
994 if (!successAudio) {
995 if (pListener && numCaptureChannels > 0)
996 pListener->OnAudioIOStopRecording();
997 mStreamToken = 0;
998
999 return 0;
1000 }
1001
1002 {
1003 double mixerStart = t0;
1004 if (pStartTime)
1005 mixerStart = std::min( mixerStart, *pStartTime );
1006 if (!AllocateBuffers(options, sequences,
1007 mixerStart, mixerLimit, options.rate))
1008 return 0;
1009 }
1010
1011 mpTransportState = std::make_unique<TransportState>(mOwningProject,
1013
1014#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1015 AILASetStartTime();
1016#endif
1017
1018 if (pStartTime)
1019 {
1020 // Calculate the NEW time position
1021 const auto time = *pStartTime;
1022
1023 // Main thread's initialization of mTime
1026
1027 // Reset mixer positions for all playback sequences
1028 for (auto &mixer : mPlaybackMixers)
1029 mixer->Reposition( time );
1030 }
1031
1032 // Now that we are done with AllocateBuffers() and SetSequenceTime():
1034 // else recording only without overdub
1035
1036 // We signal the audio thread to call SequenceBufferExchange, to prime the RingBuffers
1037 // so that they will have data in them when the stream starts. Having the
1038 // audio thread call SequenceBufferExchange here makes the code more predictable, since
1039 // SequenceBufferExchange will ALWAYS get called from the Audio thread.
1041 .store(true, std::memory_order_release);
1042
1044 .load(std::memory_order_acquire)) {
1045 using namespace std::chrono;
1046 auto interval = 50ms;
1047 if (options.playbackStreamPrimer) {
1048 interval = options.playbackStreamPrimer();
1049 }
1050 std::this_thread::sleep_for(interval);
1051 }
1052
1054
1055#ifdef REALTIME_ALSA_THREAD
1056 // PRL: Do this in hope of less thread scheduling jitter in calls to
1057 // audacityAudioCallback.
1058 // Not needed to make audio playback work smoothly.
1059 // But needed in case we also play MIDI, so that the variable "offset"
1060 // in AudioIO::MidiTime() is a better approximation of the duration
1061 // between the call of audacityAudioCallback and the actual output of
1062 // the first audio sample.
1063 // (Which we should be able to determine from fields of
1064 // PaStreamCallbackTimeInfo, but that seems not to work as documented with
1065 // ALSA.)
1066 if (mUsingAlsa)
1067 // Perhaps we should do this only if also playing MIDI ?
1068 PaAlsa_EnableRealtimeScheduling( mPortStreamV19, 1 );
1069#endif
1070
1071 //
1072 // Generate a unique value each time, to be returned to
1073 // clients accessing the AudioIO API, so they can query if they
1074 // are the ones who have reserved AudioIO or not.
1075 //
1076 // It is important to set this before setting the portaudio stream in
1077 // motion -- otherwise it may play an unspecified number of leading
1078 // zeroes.
1080
1081 // This affects AudioThread (not the portaudio callback).
1082 // Probably not needed so urgently before portaudio thread start for usual
1083 // playback, since our ring buffers have been primed already with 4 sec
1084 // of audio, but then we might be scrubbing, so do it.
1086
1087 mForceFadeOut.store(false, std::memory_order_relaxed);
1088
1089 // Now start the PortAudio stream!
1090 PaError err;
1091 err = Pa_StartStream( mPortStreamV19 );
1092
1093 if( err != paNoError )
1094 {
1095 mStreamToken = 0;
1096
1098
1099 if (pListener && mNumCaptureChannels > 0)
1100 pListener->OnAudioIOStopRecording();
1102 // PRL: PortAudio error messages are sadly not internationalized
1104 Verbatim( LAT1CTOWX(Pa_GetErrorText(err)) ) );
1105 return 0;
1106 }
1107 }
1108
1109 // Update UI display only now, after all possibilities for error are past.
1110 if (pListener) {
1111 // advertise the chosen I/O sample rate to the UI
1112 pListener->OnAudioIORate((int)mRate);
1113 }
1114
1115 auto pOwningProject = mOwningProject.lock();
1116 if (mNumPlaybackChannels > 0)
1117 Publish({ pOwningProject.get(), AudioIOEvent::PLAYBACK, true });
1118 if (mNumCaptureChannels > 0)
1119 Publish({ pOwningProject.get(), AudioIOEvent::CAPTURE, true });
1120
1121 commit = true;
1122
1124
1125 return mStreamToken;
1126}
BoolSetting SoundActivatedRecord
Definition: AudioIO.cpp:3354
DoubleSetting AudioIOLatencyCorrection
IntSetting DecibelScaleCutoff
Negation of this value is the lowest dB level that should be shown in dB scales.
Definition: Decibels.cpp:12
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:338
wxString WarningDialogKey(const wxString &internalDialogName)
Definition: Prefs.cpp:510
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
bool IsBusy() const
Returns true if audio i/o is busy starting, stopping, playing, or recording.
bool AllocateBuffers(const AudioIOStartStreamOptions &options, const TransportSequences &sequences, double t0, double t1, double sampleRate)
Allocate RingBuffer structures, and others, needed for playback and recording.
Definition: AudioIO.cpp:1166
static int mNextStreamToken
Definition: AudioIO.h:287
bool mbMicroFades
Definition: AudioIO.h:291
AudioIOExtRange Extensions()
Definition: AudioIO.h:161
void ClearRecordingException()
Definition: AudioIO.h:383
std::atomic< bool > mForceFadeOut
Definition: AudioIO.h:336
bool mDetectDropouts
Definition: AudioIO.h:388
void StopAudioThread()
Definition: AudioIO.cpp:3315
void WaitForAudioThreadStarted()
Definition: AudioIO.cpp:3305
void StartAudioThread()
Definition: AudioIO.cpp:3300
std::weak_ptr< AudioIOListener > mListener
Definition: AudioIO.h:356
std::vector< std::pair< double, double > > mLostCaptureIntervals
Definition: AudioIO.h:386
bool mPauseRec
True if Sound Activated Recording is enabled.
Definition: AudioIO.h:309
unsigned long long mLostSamples
Definition: AudioIO.h:317
virtual double OffsetSequenceTime(PlaybackSchedule &schedule, double offset)
Called when the play head needs to jump a certain distance.
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
void Prime(double time)
Empty the queue and reassign the last produced time.
std::function< std::chrono::milliseconds() > playbackStreamPrimer
Definition: AudioIOBase.h:69
PRCrossfadeData * pCrossfadeData
Definition: AudioIOBase.h:64
std::optional< double > pStartTime
Definition: AudioIOBase.h:58
std::shared_ptr< AudioIOListener > listener
Definition: AudioIOBase.h:56
void Init(double t0, double t1, const AudioIOStartStreamOptions &options, const RecordingSchedule *pRecordingSchedule)
void SetSequenceTime(double time)
Set current track time value, unadjusted.
RecordableSequences captureSequences
Definition: AudioIO.h:71
std::vector< std::shared_ptr< const OtherPlayableSequence > > otherPlayableSequences
Definition: AudioIO.h:73
ConstPlayableSequences playbackSequences
Definition: AudioIO.h:70

References AllocateBuffers(), AudioIOLatencyCorrection, AudioIOEvent::CAPTURE, TransportSequences::captureSequences, PlaybackSchedule::TimeQueue::Clear(), AudioIoCallback::ClearRecordingException(), DB_TO_LINEAR, DecibelScaleCutoff, AudioIoCallback::Extensions(), floatSample, AudioIoCallback::GetListener(), PlaybackSchedule::GetPolicy(), PlaybackSchedule::GetSequenceTime(), gPrefs, PlaybackSchedule::Init(), PlaybackPolicy::Initialize(), AudioIOBase::IsBusy(), LAT1CTOWX, AudioIOStartStreamOptions::listener, AudioIoCallback::mAudioThreadShouldCallSequenceBufferExchangeOnce, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureFormat, AudioIoCallback::mCaptureRate, AudioIoCallback::mCaptureSequences, RecordingSchedule::mCrossfadeData, AudioIoCallback::mDetectDropouts, RecordingSchedule::mDuration, AudioIoCallback::mForceFadeOut, min(), AudioIoCallback::mLastRecordingOffset, RecordingSchedule::mLatencyCorrection, AudioIoCallback::mListener, AudioIoCallback::mLostCaptureIntervals, AudioIoCallback::mLostSamples, AudioIoCallback::mNextStreamToken, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOwningProject, AudioIoCallback::mPauseRec, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, AudioIOBase::mPortStreamV19, RecordingSchedule::mPreRoll, AudioIoCallback::mpTransportState, AudioIOBase::mRate, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, AudioIoCallback::mSeek, AudioIoCallback::mSilenceLevel, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, PlaybackSchedule::mTimeQueue, AudioIoCallback::mUsingAlsa, PlaybackPolicy::OffsetSequenceTime(), TransportSequences::otherPlayableSequences, AudioIOStartStreamOptions::pCrossfadeData, AudioIOEvent::PLAYBACK, TransportSequences::playbackSequences, AudioIOStartStreamOptions::playbackStreamPrimer, AudioIOStartStreamOptions::preRoll, PlaybackSchedule::TimeQueue::Prime(), AudioIOStartStreamOptions::pStartTime, Observer::Publisher< AudioIOEvent >::Publish(), AudioIOStartStreamOptions::rate, audacity::BasicSettings::Read(), Setting< T >::Read(), PlaybackSchedule::ResetMode(), PlaybackSchedule::SetSequenceTime(), BasicUI::ShowMessageBox(), SoundActivatedRecord, AudioIoCallback::StartAudioThread(), StartPortAudioStream(), StartStreamCleanup(), AudioIoCallback::StopAudioThread(), StopStream(), Verbatim(), AudioIoCallback::WaitForAudioThreadStarted(), WarningDialogKey(), and wxT().

Here is the call graph for this function:

◆ StartStreamCleanup()

void AudioIO::StartStreamCleanup ( bool  bOnlyBuffers = false)
private

Clean up after StartStream if it fails.

If bOnlyBuffers is specified, it only cleans up the buffers.

Definition at line 1386 of file AudioIO.cpp.

1387{
1388 mpTransportState.reset();
1389
1390 mPlaybackBuffers.clear();
1391 mScratchBuffers.clear();
1392 mScratchPointers.clear();
1393 mPlaybackMixers.clear();
1394 mCaptureBuffers.clear();
1395 mResample.clear();
1397
1398 if(!bOnlyBuffers)
1399 {
1400 Pa_AbortStream( mPortStreamV19 );
1401 Pa_CloseStream( mPortStreamV19 );
1402 mPortStreamV19 = NULL;
1403 mStreamToken = 0;
1404 }
1405
1407}
virtual void Finalize(PlaybackSchedule &schedule)
Called after stopping of an audio stream or an unsuccessful start.

References PlaybackSchedule::TimeQueue::Clear(), PlaybackPolicy::Finalize(), PlaybackSchedule::GetPolicy(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIOBase::mPortStreamV19, AudioIoCallback::mpTransportState, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, AudioIOBase::mStreamToken, and PlaybackSchedule::mTimeQueue.

Referenced by AllocateBuffers(), and StartStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ StartThread()

void AudioIO::StartThread ( )
private

Definition at line 309 of file AudioIO.cpp.

310{
311 mAudioThread = std::thread(AudioThread, ref(mFinishAudioThread));
312}
static void AudioThread(std::atomic< bool > &finish)
Sits in a thread loop reading and writing audio.
Definition: AudioIO.cpp:1797

References AudioThread(), AudioIoCallback::mAudioThread, and AudioIoCallback::mFinishAudioThread.

Here is the call graph for this function:

◆ StopStream()

void AudioIO::StopStream ( )
overridevirtual

Stop recording, playback or input monitoring.

Does quite a bit of housekeeping, including switching off monitoring, flushing recording buffers out to RecordableSequences, and applies latency correction to recorded sequences if necessary

Implements AudioIOBase.

Definition at line 1423 of file AudioIO.cpp.

1424{
1425 auto cleanup = finally ( [this] {
1427 mRecordingSchedule.mCrossfadeData.clear(); // free arrays
1428 } );
1429
1430 if( mPortStreamV19 == NULL )
1431 return;
1432
1433 // DV: This code seems to be unnecessary.
1434 // We do not leave mPortStreamV19 open in stopped
1435 // state. (Do we?)
1436 // This breaks WASAPI backend, as it sets the `running`
1437 // flag to `false` asynchronously.
1438 // Previously we have patched PortAudio and the patch
1439 // was breaking IsStreamStopped() == !IsStreamActive()
1440 // invariant.
1441 /*
1442 if ( Pa_IsStreamStopped(mPortStreamV19) )
1443 return;
1444 */
1445
1446#if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
1447 // Re-enable system sleep
1448 wxPowerResource::Release(wxPOWER_RESOURCE_SCREEN);
1449#endif
1450
1452 .load(std::memory_order_relaxed) )
1453 {
1454 // PortAudio callback can use the information that we are stopping to fade
1455 // out the audio. Give PortAudio callback a chance to do so.
1456 mForceFadeOut.store(true, std::memory_order_relaxed);
1457 auto latency = static_cast<long>(AudioIOLatencyDuration.Read());
1458 // If we can gracefully fade out in 200ms, with the faded-out play buffers making it through
1459 // the sound card, then do so. If we can't, don't wait around. Just stop quickly and accept
1460 // there will be a click.
1461 if( mbMicroFades && (latency < 150 )) {
1462 using namespace std::chrono;
1463 std::this_thread::sleep_for(milliseconds{latency + 50});
1464 }
1465 }
1466
1467 wxMutexLocker locker(mSuspendAudioThread);
1468
1469 //
1470 // We got here in one of two ways:
1471 //
1472 // 1. The user clicked the stop button and we therefore want to stop
1473 // as quickly as possible. So we use AbortStream(). If this is
1474 // the case the portaudio stream is still in the Running state
1475 // (see PortAudio state machine docs).
1476 //
1477 // 2. The callback told PortAudio to stop the stream since it had
1478 // reached the end of the selection. The UI thread discovered
1479 // this by noticing that AudioIO::IsActive() returned false.
1480 // IsActive() (which calls Pa_GetStreamActive()) will not return
1481 // false until all buffers have finished playing, so we can call
1482 // AbortStream without losing any samples. If this is the case
1483 // we are in the "callback finished state" (see PortAudio state
1484 // machine docs).
1485 //
1486 // The moral of the story: We can call AbortStream safely, without
1487 // losing samples.
1488 //
1489 // DMM: This doesn't seem to be true; it seems to be necessary to
1490 // call StopStream if the callback brought us here, and AbortStream
1491 // if the user brought us here.
1492 //
1493 // DV: Seems that Pa_CloseStream calls Pa_AbortStream internally,
1494 // at least for PortAudio 19.7.0+
1495
1497
1498 // Turn off HW playthrough if PortMixer is being used
1499
1500 #if defined(USE_PORTMIXER)
1501 if( mPortMixer ) {
1502 #if __WXMAC__
1503 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
1504 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
1505 mPreviousHWPlaythrough = -1.0;
1506 #endif
1507 }
1508 #endif
1509
1510 if (mPortStreamV19) {
1511 // DV: Pa_CloseStream will close Pa_AbortStream internally,
1512 // but it doesn't hurt to do it ourselves.
1513 // PA_AbortStream will silently fail if stream is stopped.
1514 if (!Pa_IsStreamStopped( mPortStreamV19 ))
1515 Pa_AbortStream( mPortStreamV19 );
1516
1517 Pa_CloseStream( mPortStreamV19 );
1518
1519 mPortStreamV19 = NULL;
1520 }
1521
1522
1523
1524 // We previously told AudioThread to stop processing, now let's
1525 // be sure it has really stopped before resetting mpTransportState
1527
1528
1529 for( auto &ext : Extensions() )
1530 ext.StopOtherStream();
1531
1532 auto pListener = GetListener();
1533
1534 // If there's no token, we were just monitoring, so we can
1535 // skip this next part...
1536 if (mStreamToken > 0) {
1537 // In either of the above cases, we want to make sure that any
1538 // capture data that made it into the PortAudio callback makes it
1539 // to the target RecordableSequence. To do this, we ask the audio thread
1540 // to call SequenceBufferExchange one last time (it normally would not do
1541 // so since Pa_GetStreamActive() would now return false
1543 }
1544
1545 // No longer need effects processing. This must be done after the stream is stopped
1546 // to prevent the callback from being invoked after the effects are finalized.
1547 mpTransportState.reset();
1548
1549 //
1550 // Everything is taken care of. Now, just free all the resources
1551 // we allocated in StartStream()
1552 //
1553 mPlaybackBuffers.clear();
1554 mScratchBuffers.clear();
1555 mScratchPointers.clear();
1556 mPlaybackMixers.clear();
1558
1559 if (mStreamToken > 0)
1560 {
1561 //
1562 // Offset all recorded sequences to account for latency
1563 //
1564 if (mCaptureSequences.size() > 0) {
1565 mCaptureBuffers.clear();
1566 mResample.clear();
1567
1568 //
1569 // We only apply latency correction when we actually played back
1570 // sequences during the recording. If we did not play back sequences,
1571 // there's nothing we could be out of sync with. This also covers the
1572 // case that we do not apply latency correction when recording the
1573 // first sequence in a project.
1574 //
1575
1576 for (auto &sequence : mCaptureSequences) {
1577 // The calls to Flush
1578 // may cause exceptions because of exhaustion of disk space.
1579 // Stop those exceptions here, or else they propagate through too
1580 // many parts of Audacity that are not effects or editing
1581 // operations. GuardedCall ensures that the user sees a warning.
1582
1583 // Also be sure to Flush each sequence, at the top of the
1584 // guarded call, relying on the guarantee that the sequence will be
1585 // left in a flushed state, though the append buffer may be lost.
1586
1587 GuardedCall( [&] {
1588 // use No-fail-guarantee that sequence is flushed,
1589 // Partial-guarantee that some initial length of the recording
1590 // is saved.
1591 // See comments in SequenceBufferExchange().
1592 sequence->Flush();
1593 } );
1594 }
1595
1596
1597 if (!mLostCaptureIntervals.empty()) {
1598 // This scope may combine many insertions of silence
1599 // into one transaction, lessening the number of checkpoints
1600 std::optional<TransactionScope> pScope;
1601 if (auto pOwningProject = mOwningProject.lock())
1602 pScope.emplace(*pOwningProject, "Dropouts");
1603 for (auto &interval : mLostCaptureIntervals) {
1604 auto &start = interval.first;
1605 auto duration = interval.second;
1606 for (auto &sequence : mCaptureSequences)
1607 GuardedCall([&] {
1608 sequence->InsertSilence(start, duration);
1609 });
1610 }
1611 if (pScope)
1612 pScope->Commit();
1613 }
1614
1615 if (pListener)
1616 pListener->OnCommitRecording();
1617 }
1618 }
1619
1620
1621
1622 if (auto pInputMeter = mInputMeter.lock())
1623 pInputMeter->Reset(mRate, false);
1624
1625 if (auto pOutputMeter = mOutputMeter.lock())
1626 pOutputMeter->Reset(mRate, false);
1627
1628 mInputMeter.reset();
1629 mOutputMeter.reset();
1630
1631 if (pListener && mNumCaptureChannels > 0)
1632 pListener->OnAudioIOStopRecording();
1633
1634 BasicUI::CallAfter([this]{
1636 // Recording was restarted between StopStream and idle time
1637 // So the actions can keep waiting
1638 return;
1639 // In case some other thread was waiting on the mutex too:
1640 std::this_thread::yield();
1641 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1645 }
1646 DelayActions(false);
1647 });
1648
1649 //
1650 // Only set token to 0 after we're totally finished with everything
1651 //
1652 bool wasMonitoring = mStreamToken == 0;
1653 mStreamToken = 0;
1654
1655 {
1656 auto pOwningProject = mOwningProject.lock();
1657 if (mNumPlaybackChannels > 0)
1658 Publish({ pOwningProject.get(), AudioIOEvent::PLAYBACK, false });
1659 if (mNumCaptureChannels > 0)
1660 Publish({ pOwningProject.get(),
1661 wasMonitoring
1664 false });
1665 }
1666
1668
1671
1672 mPlaybackSequences.clear();
1673 mCaptureSequences.clear();
1674
1676
1677 if (pListener) {
1678 // Tell UI to hide sample rate
1679 pListener->OnAudioIORate(0);
1680 }
1681
1682 // Don't cause a busy wait in the audio thread after stopping scrubbing
1684}
void DelayActions(bool recording)
Definition: AudioIO.cpp:1128
wxMutex mSuspendAudioThread
Definition: AudioIO.h:368
void ProcessOnceAndWait(std::chrono::milliseconds sleepTime=std::chrono::milliseconds(50))
Definition: AudioIO.cpp:3330
void WaitForAudioThreadStopped()
Definition: AudioIO.cpp:3320
void Release(wxWindow *handler)

References AudioIOLatencyDuration, BasicUI::CallAfter(), AudioIOEvent::CAPTURE, PlaybackSchedule::TimeQueue::Clear(), AudioIoCallback::ClearRecordingException(), DelayActions(), AudioIoCallback::Extensions(), PlaybackPolicy::Finalize(), AudioIoCallback::GetListener(), PlaybackSchedule::GetPolicy(), GuardedCall(), AudioIoCallback::mAudioThreadSequenceBufferExchangeLoopRunning, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureSequences, RecordingSchedule::mCrossfadeData, AudioIoCallback::mForceFadeOut, AudioIOBase::mInputMeter, AudioIoCallback::mLostCaptureIntervals, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOEvent::MONITOR, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, AudioIOBase::mPortStreamV19, mPostRecordingAction, mPostRecordingActionMutex, AudioIoCallback::mpTransportState, AudioIOBase::mRate, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, AudioIOBase::mStreamToken, AudioIoCallback::mSuspendAudioThread, PlaybackSchedule::mTimeQueue, AudioIOEvent::PLAYBACK, AudioIoCallback::ProcessOnceAndWait(), Observer::Publisher< AudioIOEvent >::Publish(), Setting< T >::Read(), KeyboardCapture::Release(), PlaybackSchedule::ResetMode(), ResetOwningProject(), AudioIoCallback::StopAudioThread(), and AudioIoCallback::WaitForAudioThreadStopped().

Referenced by DrainRecordBuffers(), and StartStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ TransformPlayBuffers()

void AudioIO::TransformPlayBuffers ( std::optional< RealtimeEffects::ProcessingScope > &  scope)
private

Definition at line 2053 of file AudioIO.cpp.

2055{
2056 // Transform written but un-flushed samples in the RingBuffers in-place.
2057
2058 // Avoiding std::vector
2059 const auto pointers = stackAllocate(float*, mNumPlaybackChannels);
2060
2061 const auto numPlaybackSequences = mPlaybackSequences.size();
2062 // mPlaybackBuffers correspond many-to-one with mPlaybackSequences
2063 size_t iBuffer = 0;
2064 for (const auto vt : mPlaybackSequences) {
2065 if (!vt)
2066 continue;
2067 const auto pGroup = vt->FindChannelGroup();
2068 if (!pGroup)
2069 continue;
2070 // vt is mono, or is the first of its group of channels
2071 const auto nChannels = std::min<size_t>(
2072 mNumPlaybackChannels, vt->NChannels());
2073
2074 // Loop over the blocks of unflushed data, at most two
2075 for (unsigned iBlock : {0, 1}) {
2076 size_t len = 0;
2077 size_t iChannel = 0;
2078 for (; iChannel < nChannels; ++iChannel) {
2079 auto &ringBuffer = *mPlaybackBuffers[iBuffer + iChannel];
2080 const auto pair = ringBuffer.GetUnflushed(iBlock);
2081 // Playback RingBuffers have float format: see AllocateBuffers
2082 pointers[iChannel] = reinterpret_cast<float*>(pair.first);
2083 // The lengths of corresponding unflushed blocks should be
2084 // the same for all channels
2085 if (len == 0)
2086 len = pair.second;
2087 else
2088 assert(len == pair.second);
2089 }
2090
2091 // Are there more output device channels than channels of vt?
2092 // Such as when a mono sequence is processed for stereo play?
2093 // Then supply some non-null fake input buffers, because the
2094 // various ProcessBlock overrides of effects may crash without it.
2095 // But it would be good to find the fixes to make this unnecessary.
2096 float **scratch = &mScratchPointers[mNumPlaybackChannels + 1];
2098 memset((pointers[iChannel++] = *scratch++), 0, len * sizeof(float));
2099
2100 if (len && pScope) {
2101 auto discardable = pScope->Process(*pGroup, &pointers[0],
2102 mScratchPointers.data(),
2103 // The single dummy output buffer:
2106 iChannel = 0;
2107 for (; iChannel < nChannels; ++iChannel) {
2108 auto &ringBuffer = *mPlaybackBuffers[iBuffer + iChannel];
2109 auto discarded = ringBuffer.Unput(discardable);
2110 // assert(discarded == discardable);
2111 }
2112 }
2113 }
2114 iBuffer += vt->NChannels();
2115 }
2116}
#define stackAllocate(T, count)
Definition: AudioIO.cpp:2051

References anonymous_namespace{StretchingSequenceIntegrationTest.cpp}::iChannel, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackSequences, AudioIoCallback::mScratchPointers, and stackAllocate.

Referenced by ProcessPlaybackSlices().

Here is the caller graph for this function:

◆ ValidateDeviceNames()

bool AudioIO::ValidateDeviceNames ( const wxString &  play,
const wxString &  rec 
)
static

Ensure selected device names are valid.

Definition at line 223 of file AudioIO.cpp.

224{
225 const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(getPlayDevIndex(play));
226 const PaDeviceInfo *rInfo = Pa_GetDeviceInfo(getRecordDevIndex(rec));
227
228 // Valid iff both defined and the same api.
229 return pInfo != nullptr && rInfo != nullptr && pInfo->hostApi == rInfo->hostApi;
230}

References AudioIOBase::getPlayDevIndex(), and AudioIOBase::getRecordDevIndex().

Here is the call graph for this function:

Member Data Documentation

◆ mDelayingActions

bool AudioIO::mDelayingActions { false }
private

Definition at line 663 of file AudioIO.h.

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 661 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 660 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


The documentation for this class was generated from the following files: