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 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, bool publish=false)
 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)
 
void CheckSoundActivatedRecordingLevel (float *inputSamples, unsigned long framesPerBuffer)
 
bool FillOutputBuffers (float *outputFloats, 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)
 Get a list of sample rates the output (playback) device supports. More...
 
static long GetClosestSupportedPlaybackRate (int devIndex, long rate)
 Find the closest supported sample rate for given playback device. More...
 
static std::vector< long > GetSupportedCaptureRates (int devIndex=-1)
 Get a list of sample rates the input (recording) device supports. More...
 
static long GetClosestSupportedCaptureRate (int devIndex, long rate)
 Find the closest supported sample rate for given recording device. More...
 
static std::vector< long > GetSupportedSampleRates (int playDevice=-1, int recDevice=-1)
 Get a list of sample rates the current input/output device combination supports. More...
 
static long GetClosestSupportedSampleRate (int playDevice, int recDevice, long rate)
 Find the closest supported sample rate for given playback and recording devices. More...
 
static int GetOptimalSupportedSampleRate ()
 Get a supported sample rate which can be used a an optimal default. More...
 
static bool IsPlaybackRateSupported (int devIndex, long rate)
 Check if the specified playback rate is supported by a device. More...
 
static bool IsCaptureRateSupported (int devIndex, long rate)
 Check if the specified sample rate is supported by a device. 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...
 
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
 
std::vector< std::vector< float > > mProcessingBuffers
 
std::vector< std::vector< float > > mMasterBuffers
 
RingBuffers mPlaybackBuffers
 
ConstPlayableSequences mPlaybackSequences
 
float mOldPlaybackVolume
 
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 std::map< int, std::vector< long > > mCachedPlaybackRates
 
static std::map< int, std::vector< long > > mCachedCaptureRates
 
static std::map< std::pair< int, int >, std::vector< long > > mCachedSampleRates
 
static int mCurrentPlaybackIndex { -1 }
 
static int mCurrentCaptureIndex { -1 }
 
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 413 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 484 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:46
DoubleSetting AudioIOPlaybackVolume
XO("Cut/Copy/Paste")
#define LAT1CTOWX(X)
Definition: Internat.h:158
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:319
PaStream * mPortStreamV19
Definition: AudioIOBase.h:316
void HandleDeviceChange()
update state after changing what audio devices are selected
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:333
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopRunning
Definition: AudioIO.h:311
std::atomic< bool > mAudioThreadShouldCallSequenceBufferExchangeOnce
Definition: AudioIO.h:310
std::atomic< Acknowledge > mAudioThreadAcknowledge
Definition: AudioIO.h:314
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopActive
Definition: AudioIO.h:312
long mNumPauseFrames
How many frames of zeros were output due to pauses?
Definition: AudioIO.h:233
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:330
PaError mLastPaError
Definition: AudioIO.h:334
double mLastRecordingOffset
Not (yet) used; should perhaps be atomic when it is.
Definition: AudioIO.h:333
float mSilenceLevel
Definition: AudioIO.h:302
size_t mNumCaptureChannels
Definition: AudioIO.h:304
void SetMixerOutputVol(float value)
Definition: AudioIO.h:342
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:287
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:304
void ResetOwningProject()
Definition: AudioIO.cpp:770
std::atomic< bool > mFinishAudioThread
Definition: AudioIO.h:253
std::thread mAudioThread
Definition: AudioIO.h:252

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:494
std::unique_ptr< TransportState > mpTransportState
Holds some state for duration of playback or recording.
Definition: AudioIO.h:398
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 mPlaybackBuffers.resize(0);
1229 mProcessingBuffers.resize(0);
1230 mMasterBuffers.resize(0);
1231
1232 // Always make at least one playback buffer, in case of
1233 // MIDI playback without any audio
1234 if(mPlaybackSequences.empty())
1235 mPlaybackBuffers.resize(1);
1236 else
1237 {
1239 mProcessingBuffers.resize(std::accumulate(
1240 mPlaybackSequences.begin(),
1241 mPlaybackSequences.end(),
1242 0, [](int n, auto& seq) { return n + seq->NChannels(); }
1243 ));
1244 for(auto& buffer : mProcessingBuffers)
1245 buffer.reserve(playbackBufferSize);
1246
1248 for(auto& buffer : mMasterBuffers)
1249 buffer.reserve(playbackBufferSize);
1250
1251 // Number of scratch buffers depends on device playback channels
1252 if (mNumPlaybackChannels > 0) {
1253 mScratchBuffers.resize(mNumPlaybackChannels * 2 + 1);
1254 mScratchPointers.clear();
1255 for (auto &buffer : mScratchBuffers) {
1256 buffer.Allocate(playbackBufferSize, floatSample);
1257 mScratchPointers.push_back(
1258 reinterpret_cast<float*>(buffer.ptr()));
1259 }
1260 }
1261 }
1262
1263 std::generate(
1264 mPlaybackBuffers.begin(),
1265 mPlaybackBuffers.end(),
1266 [=]{ return std::make_unique<RingBuffer>(floatSample, playbackBufferSize); }
1267 );
1268
1269 mPlaybackMixers.clear();
1270
1271 const auto &warpOptions =
1272 policy.MixerWarpOptions(mPlaybackSchedule);
1273
1274 mPlaybackQueueMinimum = lrint( mRate * times.latency.count() );
1276 std::min( mPlaybackQueueMinimum, playbackBufferSize );
1277
1278 // Limit the mPlaybackQueueMinimum to the hardware latency
1281
1282 // Make mPlaybackQueueMinimum a multiple of mPlaybackSamplesToCopy
1285
1286 // Bug 1763 - We must fade in from zero to avoid a click on starting.
1287 mOldPlaybackVolume = 0.0f;
1288 for (unsigned int i = 0; i < mPlaybackSequences.size(); i++) {
1289 const auto &pSequence = mPlaybackSequences[i];
1290
1291 // By the precondition of StartStream which is sole caller of
1292 // this function:
1293 assert(pSequence->FindChannelGroup());
1294 // use sequence time for the end time, not real time!
1295 double startTime, endTime;
1296 if (!sequences.prerollSequences.empty())
1297 startTime = mPlaybackSchedule.mT0;
1298 else
1299 startTime = t0;
1300
1302 .contains(pSequence))
1303 // Stop playing this sequence after pre-roll
1304 endTime = t0;
1305 else
1306 // Pass t1 -- not mT1 as may have been adjusted for latency
1307 // -- so that overdub recording stops playing back samples
1308 // at the right time, though transport may continue to
1309 // record
1310 endTime = t1;
1311
1312 Mixer::Inputs mixSequences;
1313 mixSequences.push_back(Mixer::Input{ pSequence });
1314 mPlaybackMixers.emplace_back(std::make_unique<Mixer>(
1315 std::move(mixSequences), std::nullopt,
1316 // Don't throw for read errors, just play silence:
1317 false, warpOptions, startTime, endTime,
1318 pSequence->NChannels(),
1320 false, // not interleaved
1322 false, // low quality dithering and resampling
1323 nullptr, // no custom mix-down
1324 Mixer::ApplyVolume::Discard // don't apply volume
1325 ));
1326 }
1327
1328 const auto timeQueueSize = 1 +
1329 (playbackBufferSize + TimeQueueGrainSize - 1)
1331 mPlaybackSchedule.mTimeQueue.Init( timeQueueSize );
1332 }
1333
1334 if( mNumCaptureChannels > 0 )
1335 {
1336 // Allocate input buffers. For every input sequence we allocate
1337 // a ring buffer of five seconds
1338 auto captureBufferSize =
1339 (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
1340
1341 // In the extraordinarily rare case that we can't even afford
1342 // 100 samples, just give up.
1343 if(captureBufferSize < 100)
1344 {
1345 BasicUI::ShowMessageBox( XO("Out of memory!") );
1346 return false;
1347 }
1348
1349 mCaptureBuffers.resize(0);
1351 mResample.resize(0);
1354
1355 for (unsigned int i = 0; i < mNumCaptureChannels; ++i) {
1356 mCaptureBuffers[i] = std::make_unique<RingBuffer>(
1357 mCaptureFormat, captureBufferSize);
1358 mResample[i] =
1359 std::make_unique<Resample>(true, mFactor, mFactor);
1360 // constant rate resampling
1361 }
1362 }
1363 }
1364 catch(std::bad_alloc&)
1365 {
1366 // Oops! Ran out of memory. This is pretty rare, so we'll just
1367 // try deleting everything, halving our buffer size, and try again.
1368 StartStreamCleanup(true);
1372 mMinCaptureSecsToCopy *= 0.5;
1373 bDone = false;
1374
1375 // In the extraordinarily rare case that we can't even afford 100
1376 // samples, just give up.
1377 auto playbackBufferSize =
1378 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1379 if(playbackBufferSize < 100 || mPlaybackSamplesToCopy < 100)
1380 {
1381 BasicUI::ShowMessageBox( XO("Out of memory!") );
1382 return false;
1383 }
1384 }
1385 } while(!bDone);
1386
1387 success = true;
1388 return true;
1389}
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:314
void StartStreamCleanup(bool bOnlyBuffers=false)
Clean up after StartStream if it fails.
Definition: AudioIO.cpp:1391
size_t mNumPlaybackChannels
Definition: AudioIO.h:306
std::vector< std::unique_ptr< Resample > > mResample
Definition: AudioIO.h:255
PlaybackSchedule mPlaybackSchedule
Definition: AudioIO.h:396
std::vector< float * > mScratchPointers
pointing into mScratchBuffers
Definition: AudioIO.h:274
size_t mPlaybackSamplesToCopy
Preferred batch size for replenishing the playback RingBuffer.
Definition: AudioIO.h:290
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
Definition: AudioIO.h:276
PlaybackPolicy::Duration mPlaybackRingBufferSecs
Definition: AudioIO.h:286
std::vector< std::vector< float > > mProcessingBuffers
Definition: AudioIO.h:262
size_t mHardwarePlaybackLatencyFrames
Hardware output latency in frames.
Definition: AudioIO.h:292
RingBuffers mCaptureBuffers
Definition: AudioIO.h:258
RingBuffers mPlaybackBuffers
Definition: AudioIO.h:267
float mOldPlaybackVolume
Definition: AudioIO.h:271
ConstPlayableSequences mPlaybackSequences
Definition: AudioIO.h:268
size_t mPlaybackQueueMinimum
Occupancy of the queue we try to maintain, with bigger batches if needed.
Definition: AudioIO.h:294
double mCaptureRingBufferSecs
Definition: AudioIO.h:287
std::vector< std::vector< float > > mMasterBuffers
Definition: AudioIO.h:265
double mFactor
Definition: AudioIO.h:280
double mMinCaptureSecsToCopy
Definition: AudioIO.h:296
std::vector< SampleBuffer > mScratchBuffers
Definition: AudioIO.h:273
sampleFormat mCaptureFormat
Definition: AudioIO.h:307
std::vector< Input > Inputs
Definition: Mix.h:49
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:78

References DownmixStage::Discard, floatSample, PlaybackSchedule::GetPolicy(), PlaybackSchedule::TimeQueue::Init(), lrint, make_iterator_range(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureFormat, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mFactor, AudioIoCallback::mHardwarePlaybackLatencyFrames, min(), AudioIoCallback::mMasterBuffers, AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mOldPlaybackVolume, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackRingBufferSecs, AudioIoCallback::mPlaybackSamplesToCopy, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, AudioIoCallback::mProcessingBuffers, AudioIOBase::mRate, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, PlaybackSchedule::mT0, PlaybackSchedule::mTimeQueue, TransportSequences::prerollSequences, 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 1771 of file AudioIO.cpp.

1772{
1773 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1774 AudioIO *const gAudioIO = AudioIO::Get();
1775 while (!finish.load(std::memory_order_acquire)) {
1776 using Clock = std::chrono::steady_clock;
1777 auto loopPassStart = Clock::now();
1778 auto &schedule = gAudioIO->mPlaybackSchedule;
1779 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1780
1781 // Set LoopActive outside the tests to avoid race condition
1783 .store(true, std::memory_order_relaxed);
1785 .load(std::memory_order_acquire) )
1786 {
1787 gAudioIO->SequenceBufferExchange();
1789 .store(false, std::memory_order_release);
1790
1791 lastState = State::eOnce;
1792 }
1794 .load(std::memory_order_relaxed))
1795 {
1796 if (lastState != State::eLoopRunning)
1797 {
1798 // Main thread has told us to start - acknowledge that we do
1800 std::memory_order::memory_order_release);
1801 }
1802 lastState = State::eLoopRunning;
1803
1804 // We call the processing after raising the acknowledge flag, because the main thread
1805 // only needs to know that the message was seen.
1806 //
1807 // This is unlike the case with mAudioThreadShouldCallSequenceBufferExchangeOnce where the
1808 // store really means that the one-time exchange was done.
1809
1810 gAudioIO->SequenceBufferExchange();
1811 }
1812 else
1813 {
1814 if ( (lastState == State::eLoopRunning)
1815 || (lastState == State::eMonitoring ) )
1816 {
1817 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1818 // acknowledge that we received the order and that no more processing will be done.
1820 std::memory_order::memory_order_release);
1821 }
1822 lastState = State::eDoNothing;
1823
1824 if (gAudioIO->IsMonitoring())
1825 {
1826 lastState = State::eMonitoring;
1827 }
1828 }
1829
1831 .store(false, std::memory_order_relaxed);
1832
1833 std::this_thread::sleep_until( loopPassStart + interval );
1834 }
1835}
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:416
static AudioIO * Get()
Definition: AudioIO.cpp:126
void SequenceBufferExchange()
Definition: AudioIO.cpp:1872
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:651
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:652
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:214

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:300

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:654

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 2213 of file AudioIO.cpp.

2214{
2216 return;
2217
2218 auto delayedHandler = [this] ( AudacityException * pException ) {
2219 // In the main thread, stop recording
2220 // This is one place where the application handles disk
2221 // exhaustion exceptions from RecordableSequence operations, without
2222 // rolling back to the last pushed undo state. Instead, partial recording
2223 // results are pushed as a NEW undo state. For this reason, as
2224 // commented elsewhere, we want an exception safety guarantee for
2225 // the output RecordableSequences, after the failed append operation, that
2226 // the sequences remain as they were after the previous successful
2227 // (block-level) appends.
2228
2229 // Note that the Flush in StopStream() may throw another exception,
2230 // but StopStream() contains that exception, and the logic in
2231 // AudacityException::DelayedHandlerAction prevents redundant message
2232 // boxes.
2233 StopStream();
2234 DefaultDelayedHandlerAction( pException );
2235 for (auto &pSequence: mCaptureSequences)
2236 pSequence->RepairChannels();
2237 };
2238
2239 GuardedCall( [&] {
2240 // start record buffering
2241 const auto avail = GetCommonlyAvailCapture(); // samples
2242 const auto remainingTime =
2243 std::max(0.0, mRecordingSchedule.ToConsume());
2244 // This may be a very big double number:
2245 const auto remainingSamples = remainingTime * mRate;
2246 bool latencyCorrected = true;
2247
2248 double deltat = avail / mRate;
2249
2251 .load(std::memory_order_relaxed) ||
2252 deltat >= mMinCaptureSecsToCopy)
2253 {
2254 bool newBlocks = false;
2255
2256 // Append captured samples to the end of the RecordableSequences.
2257 // (WaveTracks have their own buffering for efficiency.)
2258 auto iter = mCaptureSequences.begin();
2259 auto width = (*iter)->NChannels();
2260 size_t iChannel = 0;
2261 for (size_t i = 0; i < mNumCaptureChannels; ++i) {
2262 Finally Do {[&]{
2263 if (++iChannel == width) {
2264 ++iter;
2265 iChannel = 0;
2266 if (iter != mCaptureSequences.end())
2267 width = (*iter)->NChannels();
2268 }
2269 }};
2270 size_t discarded = 0;
2271
2273 const auto correction = mRecordingSchedule.TotalCorrection();
2274 if (correction >= 0) {
2275 // Rightward shift
2276 // Once only (per sequence per recording), insert some initial
2277 // silence.
2278 size_t size = floor( correction * mRate * mFactor);
2280 ClearSamples(temp.ptr(), mCaptureFormat, 0, size);
2281 (*iter)->Append(iChannel, temp.ptr(), mCaptureFormat, size, 1,
2282 // Do not dither recordings
2284 }
2285 else {
2286 // Leftward shift
2287 // discard some samples from the ring buffers.
2288 size_t size = floor(
2290
2291 // The ring buffer might have grown concurrently -- don't discard more
2292 // than the "avail" value noted above.
2293 discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2294
2295 if (discarded < size)
2296 // We need to visit this again to complete the
2297 // discarding.
2298 latencyCorrected = false;
2299 }
2300 }
2301
2302 const float *pCrossfadeSrc = nullptr;
2303 size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2304 if (i < mRecordingSchedule.mCrossfadeData.size())
2305 {
2306 // Do crossfading
2307 // The supplied crossfade samples are at the same rate as the
2308 // sequence
2309 const auto &data = mRecordingSchedule.mCrossfadeData[i];
2310 totalCrossfadeLength = data.size();
2311 if (totalCrossfadeLength) {
2312 crossfadeStart =
2314 if (crossfadeStart < totalCrossfadeLength)
2315 pCrossfadeSrc = data.data() + crossfadeStart;
2316 }
2317 }
2318
2319 wxASSERT(discarded <= avail);
2320 size_t toGet = avail - discarded;
2321 SampleBuffer temp;
2322 size_t size;
2324 if( mFactor == 1.0 )
2325 {
2326 // Take captured samples directly
2327 size = toGet;
2328 if (pCrossfadeSrc)
2329 // Change to float for crossfade calculation
2331 else
2333 temp.Allocate(size, format);
2334 const auto got =
2335 mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2336 // wxASSERT(got == toGet);
2337 // but we can't assert in this thread
2338 wxUnusedVar(got);
2339 if (double(size) > remainingSamples)
2340 size = floor(remainingSamples);
2341 }
2342 else
2343 {
2344 size = lrint(toGet * mFactor);
2346 SampleBuffer temp1(toGet, floatSample);
2347 temp.Allocate(size, format);
2348 const auto got =
2349 mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
2350 // wxASSERT(got == toGet);
2351 // but we can't assert in this thread
2352 wxUnusedVar(got);
2353 /* we are re-sampling on the fly. The last resampling call
2354 * must flush any samples left in the rate conversion buffer
2355 * so that they get recorded
2356 */
2357 if (toGet > 0 ) {
2358 if (double(toGet) > remainingSamples)
2359 toGet = floor(remainingSamples);
2360 const auto results =
2361 mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
2362 !IsStreamActive(), (float *)temp.ptr(), size);
2363 size = results.second;
2364 }
2365 }
2366
2367 if (pCrossfadeSrc) {
2368 wxASSERT(format == floatSample);
2369 size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
2370 if (crossfadeLength) {
2371 auto ratio = double(crossfadeStart) / totalCrossfadeLength;
2372 auto ratioStep = 1.0 / totalCrossfadeLength;
2373 auto pCrossfadeDst = (float*)temp.ptr();
2374
2375 // Crossfade loop here
2376 for (size_t ii = 0; ii < crossfadeLength; ++ii) {
2377 *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
2378 ++pCrossfadeSrc, ++pCrossfadeDst;
2379 ratio += ratioStep;
2380 }
2381 }
2382 }
2383
2384 // Now append
2385 // see comment in second handler about guarantee
2386 newBlocks = (*iter)->Append(iChannel,
2387 temp.ptr(), format, size, 1,
2388 // Do not dither recordings
2390 ) || newBlocks;
2391 } // end loop over capture channels
2392
2393 // Now update the recording schedule position
2395 mRecordingSchedule.mLatencyCorrected = latencyCorrected;
2396
2397 auto pListener = GetListener();
2398 if (pListener && newBlocks)
2399 pListener->OnAudioIONewBlocks();
2400
2401 }
2402 // end of record buffering
2403 },
2404 // handler
2405 [this] ( AudacityException *pException ) {
2406 if ( pException ) {
2407 // So that we don't attempt to fill the recording buffer again
2408 // before the main thread stops recording
2410 return ;
2411 }
2412 else
2413 // Don't want to intercept other exceptions (?)
2414 throw;
2415 },
2416 delayedHandler );
2417}
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:1428
size_t GetCommonlyAvailCapture()
Get the number of audio samples ready in all of the recording buffers.
Definition: AudioIO.cpp:1864
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:171
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:395
void SetRecordingException()
Definition: AudioIO.h:373
RecordableSequences mCaptureSequences
Definition: AudioIO.h:259
double mCaptureRate
Definition: AudioIO.h:308
wxAtomicInt mRecordingException
Definition: AudioIO.h:372
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 1878 of file AudioIO.cpp.

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

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 1709 of file AudioIO.cpp.

1710{
1711 // Check if we can use the cached value
1713 && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
1714 return mCachedBestRateOut;
1715 }
1716
1717 if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
1718 if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
1719 wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
1720
1721 long requestedRate = static_cast<long>(sampleRate);
1722 long supportedRate = 0;
1723
1724 if (capturing && !playing) {
1725 supportedRate = GetClosestSupportedCaptureRate(-1, sampleRate);
1726 }
1727 else if (playing && !capturing) {
1728 supportedRate = GetClosestSupportedPlaybackRate(-1, sampleRate);
1729 }
1730 else { // we assume capturing and playing - the alternative would be a
1731 // bit odd
1732 supportedRate = GetClosestSupportedSampleRate(-1, -1, sampleRate);
1733 }
1734
1735 /* if we get here, there is a problem - the project rate isn't supported
1736 * on our hardware, so we can't use it. */
1737
1738 if (supportedRate == 0) {
1739 /* we're stuck - there are no supported rates with this hardware. Error */
1740 wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
1741 }
1742 else if (supportedRate != requestedRate) {
1743 wxLogDebug(wxT("GetBestRate() Returning highest supported rate - %.0ld Hz"), supportedRate);
1744 }
1745
1747 mCachedBestRateOut = supportedRate;
1748 mCachedBestRatePlaying = playing;
1749 mCachedBestRateCapturing = capturing;
1750 return supportedRate;
1751}
wxT("CloseDown"))
static long GetClosestSupportedPlaybackRate(int devIndex, long rate)
Find the closest supported sample rate for given playback device.
static double mCachedBestRateIn
Definition: AudioIOBase.h:341
static long GetClosestSupportedSampleRate(int playDevice, int recDevice, long rate)
Find the closest supported sample rate for given playback and recording devices.
static long GetClosestSupportedCaptureRate(int devIndex, long rate)
Find the closest supported sample rate for given recording device.
static double mCachedBestRateOut
Definition: AudioIO.h:354
static bool mCachedBestRatePlaying
Definition: AudioIO.h:355
static bool mCachedBestRateCapturing
Definition: AudioIO.h:356

References AudioIOBase::GetClosestSupportedCaptureRate(), AudioIOBase::GetClosestSupportedPlaybackRate(), AudioIOBase::GetClosestSupportedSampleRate(), 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 524 of file AudioIO.h.

524{ 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 1864 of file AudioIO.cpp.

1865{
1867}
static size_t MinValue(const RingBuffers &buffers, size_t(RingBuffer::*pmf)() const)
Definition: AudioIO.cpp:1837
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 1846 of file AudioIO.cpp.

1847{
1848 auto commonlyAvail = MinValue(mPlaybackBuffers, &RingBuffer::AvailForPut);
1849 // MB: subtract a few samples because the code in SequenceBufferExchange has rounding
1850 // errors
1851 return commonlyAvail - std::min(size_t(10), commonlyAvail);
1852}
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 493 of file AudioIO.h.

493{ 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:340

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

size_t AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 526 of file AudioIO.h.

526{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

size_t AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 525 of file AudioIO.h.

525{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 494 of file AudioIO.h.

495 { 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 1753 of file AudioIO.cpp.

1754{
1755 // Sequence time readout for the main thread
1756
1757 if( !IsStreamActive() )
1758 return BAD_STREAM_TIME;
1759
1761}
#define BAD_STREAM_TIME
Definition: AudioIOBase.h:40
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 1414 of file AudioIO.cpp.

1415{
1416 auto pOwningProject = mOwningProject.lock();
1417 return !pOwningProject || pOwningProject.get() == &project;
1418}

References AudioIOBase::mOwningProject, and project.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3361 of file AudioIO.cpp.

3362{
3363 // Includes a test of mTime, used in the main thread
3364 return IsStreamActive() &&
3365 GetNumCaptureChannels() > 0 &&
3368}
size_t GetNumCaptureChannels() const
Definition: AudioIO.h:526

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 1958 of file AudioIO.cpp.

1960{
1961 auto &policy = mPlaybackSchedule.GetPolicy();
1962
1963 // msmeyer: When playing a very short selection in looped
1964 // mode, the selection must be copied to the buffer multiple
1965 // times, to ensure, that the buffer has a reasonable size
1966 // This is the purpose of this loop.
1967 // PRL: or, when scrubbing, we may get work repeatedly from the
1968 // user interface.
1969 bool done = false;
1970 bool progress = false;
1971
1972 // remember initial processing buffer offsets
1973 // they may be different depending on latencies
1974 const auto processingBufferOffsets = stackAllocate(size_t, mProcessingBuffers.size());
1975 for(unsigned n = 0; n < mProcessingBuffers.size(); ++n)
1976 processingBufferOffsets[n] = mProcessingBuffers[n].size();
1977
1978 do {
1979 const auto slice =
1980 policy.GetPlaybackSlice(mPlaybackSchedule, available);
1981 const auto &[frames, toProduce] = slice;
1982 progress = progress || toProduce > 0;
1983
1984 // Update the time queue. This must be done before writing to the
1985 // ring buffers of samples, for proper synchronization with the
1986 // consumer side in the PortAudio thread, which reads the time
1987 // queue after reading the sample queues. The sample queues use
1988 // atomic variables, the time queue doesn't.
1990
1991 // mPlaybackMixers correspond one-to-one with mPlaybackSequences
1992 size_t iSequence = 0;
1993 // mPlaybackBuffers correspond many-to-one with mPlaybackSequences
1994 size_t iBuffer = 0;
1995 for (auto &mixer : mPlaybackMixers) {
1996 // The mixer here isn't actually mixing: it's just doing
1997 // resampling, format conversion, and possibly time track
1998 // warping
1999 if (frames > 0) {
2000 size_t produced = 0;
2001
2002 if (toProduce)
2003 produced = mixer->Process(toProduce);
2004
2005 //wxASSERT(produced <= toProduce);
2006 // Copy (non-interleaved) mixer outputs to one or more ring buffers
2007 const auto nChannels = mPlaybackSequences[iSequence]->NChannels();
2008
2009 const auto appendPos = mProcessingBuffers[iBuffer].size();
2010 for (size_t j = 0; j < nChannels; ++j)
2011 {
2012 // mPlaybackBuffers correspond many-to-one with mPlaybackSequences
2013 auto& buffer = mProcessingBuffers[iBuffer + j];
2014 //Sufficient size should have been reserved in AllocateBuffers
2015 //But for some latency values (> aprox. 100ms) pre-allocated
2016 //buffer could be not large enough.
2017 //Preserve what was written to the buffer during previous pass, don't discard
2018 buffer.resize(buffer.size() + frames, 0);
2019
2020 const auto warpedSamples = mixer->GetBuffer(j);
2021 std::copy_n(
2022 reinterpret_cast<const float*>(warpedSamples),
2023 produced,
2024 buffer.data() + appendPos);
2025 std::fill_n(
2026 buffer.data() + appendPos + produced,
2027 frames - produced,
2028 .0f);
2029 }
2030
2031 iBuffer += nChannels;
2032 ++iSequence;
2033 }
2034 }
2035
2036 available -= frames;
2037 // wxASSERT(available >= 0); // don't assert on this thread
2038 if(mPlaybackSequences.empty())
2039 // Produce silence in the single ring buffer
2040 mPlaybackBuffers[0]->Put(nullptr, floatSample, 0, frames);
2041
2042 done = policy.RepositionPlayback( mPlaybackSchedule, mPlaybackMixers,
2043 frames, available );
2044 } while (available && !done);
2045
2046 //stop here if there are no sample sources to process...
2047 if(mPlaybackSequences.empty())
2048 return progress;
2049
2050 // Do any realtime effect processing for each individual sample source,
2051 // after all the little slices have been written.
2052 if (pScope)
2053 {
2054 const auto pointers = stackAllocate(float*, mNumPlaybackChannels);
2055
2056 int bufferIndex = 0;
2057 for(const auto& seq : mPlaybackSequences)
2058 {
2059 if(!seq)
2060 continue;//no similar check in convert-to-float part
2061 const auto channelGroup = seq->FindChannelGroup();
2062 if(!channelGroup)
2063 continue;
2064
2065 // Are there more output device channels than channels of vt?
2066 // Such as when a mono sequence is processed for stereo play?
2067 // Then supply some non-null fake input buffers, because the
2068 // various ProcessBlock overrides of effects may crash without it.
2069 // But it would be good to find the fixes to make this unnecessary.
2070 auto scratch = &mScratchPointers[mNumPlaybackChannels + 1];
2071
2072 //skip samples that are already processed
2073 const auto offset = processingBufferOffsets[bufferIndex];
2074 //number of newly written samples
2075 const auto len = mProcessingBuffers[bufferIndex].size() - offset;
2076
2077 if(len > 0)
2078 {
2079 for(unsigned i = 0, cnt = std::min(seq->NChannels(), mNumPlaybackChannels); i < cnt; ++i)
2080 pointers[i] = mProcessingBuffers[bufferIndex + i].data() + offset;
2081
2082 for(unsigned i = seq->NChannels(); i < mNumPlaybackChannels; ++i)
2083 {
2084
2085 pointers[i] = *scratch++;
2086 std::fill_n(pointers[i], len, .0f);
2087 }
2088
2089 const auto discardable = pScope->Process(channelGroup, &pointers[0],
2090 mScratchPointers.data(),
2091 // The single dummy output buffer:
2094 // Check for asynchronous user changes in mute, solo status
2095 const auto silenced = SequenceShouldBeSilent(*seq);
2096 for(int i = 0; i < seq->NChannels(); ++i)
2097 {
2098 auto& buffer = mProcessingBuffers[bufferIndex + i];
2099 buffer.erase(buffer.begin() + offset, buffer.begin() + offset + discardable);
2100 if(silenced)
2101 {
2102 //TODO: fade out smoothly
2103 std::fill_n(buffer.data() + offset, len - discardable, 0);
2104 }
2105 }
2106 }
2107
2108 bufferIndex += seq->NChannels();
2109 }
2110 }
2111
2112 //samples at the beginning could have been discarded
2113 //in the previous step, proceed with the number of samples
2114 //equal to the shortest buffer size available
2115 auto samplesAvailable = std::min_element(
2116 mProcessingBuffers.begin(),
2117 mProcessingBuffers.end(),
2118 [](auto& first, auto& second) { return first.size() < second.size(); }
2119 )->size();
2120
2121 //introduced latency may be too high...
2122 //but we don't discard what already was written
2123 if(samplesAvailable == 0)
2124 return progress;
2125
2126 //Prepare master buffers.
2127 auto cleanup = finally([=] {
2128 for(auto& buffer : mMasterBuffers)
2129 buffer.clear();
2130 });
2131
2132 for(auto& buffer : mMasterBuffers)
2133 {
2134 //assert(buffer.size() == 0);
2135 //assert(buffer.capacity() >= samplesAvailable);
2136 buffer.resize(samplesAvailable, 0);
2137 }
2138
2139 {
2140 unsigned bufferIndex = 0;
2141 for(const auto& seq : mPlaybackSequences)
2142 {
2143 //TODO: apply micro-fades
2144 const auto numChannels = seq->NChannels();
2145 if(numChannels > 1)
2146 {
2147 for(unsigned n = 0, cnt = std::min(numChannels, mNumPlaybackChannels); n < cnt; ++n)
2148 {
2149 const auto volume = seq->GetChannelVolume(n);
2150 for(unsigned i = 0; i < samplesAvailable; ++i)
2151 mMasterBuffers[n][i] += mProcessingBuffers[bufferIndex + n][i] * volume;
2152 }
2153 }
2154 else if(numChannels == 1)
2155 {
2156 //mono source is duplicated into every output channel
2157 for(unsigned n = 0; n < mNumPlaybackChannels; ++n)
2158 {
2159 const auto volume = seq->GetChannelVolume(n);
2160 for(unsigned i = 0; i < samplesAvailable; ++i)
2161 mMasterBuffers[n][i] += mProcessingBuffers[bufferIndex][i] * volume;
2162 }
2163 }
2164 bufferIndex += seq->NChannels();
2165 }
2166 }
2167
2168 //remove only samples that were processed in previous step
2169 for(auto& buffer : mProcessingBuffers)
2170 buffer.erase(buffer.begin(), buffer.begin() + samplesAvailable);
2171
2172 // Do any realtime effect processing, after all the little
2173 // slices have been written. This time we use mixed source created in
2174 // previous step
2175 size_t masterBufferOffset = 0;//The amount of samples to be discarded
2176 if(pScope)
2177 {
2178 const auto pointers = stackAllocate(float*, mNumPlaybackChannels);
2179 for(unsigned i = 0; i < mNumPlaybackChannels; ++i)
2180 pointers[i] = mMasterBuffers[i].data();
2181
2182 masterBufferOffset = pScope->Process(
2184 &pointers[0],
2185 mScratchPointers.data(),
2186 // The single dummy output buffer:
2188 mNumPlaybackChannels, samplesAvailable);
2189
2190 // wxASSERT(samplesAvailable >= masterBufferOffset); // don't assert on this thread
2191 samplesAvailable -= masterBufferOffset;
2192 }
2193
2194 if(samplesAvailable == 0)
2195 return progress;
2196
2197 {
2198 unsigned bufferIndex = 0;
2199 for(auto& buffer : mMasterBuffers)
2200 {
2201 mPlaybackBuffers[bufferIndex++]->Put(
2202 reinterpret_cast<constSamplePtr>(buffer.data()) + masterBufferOffset * sizeof(float),
2204 samplesAvailable,
2205 0
2206 );
2207 }
2208 }
2209
2210 return progress;
2211}
#define stackAllocate(T, count)
Definition: AudioIO.cpp:1956
const char * constSamplePtr
Definition: SampleFormat.h:58
bool SequenceShouldBeSilent(const PlayableSequence &ps)
Definition: AudioIO.cpp:3097
void Producer(PlaybackSchedule &schedule, PlaybackSlice slice)
Enqueue track time value advanced by the slice according to schedule's PlaybackPolicy.
static constexpr ChannelGroup * MasterGroup

References floatSample, PlaybackSchedule::GetPolicy(), RealtimeEffectManager::MasterGroup, min(), AudioIoCallback::mMasterBuffers, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackSequences, AudioIoCallback::mProcessingBuffers, AudioIoCallback::mScratchPointers, PlaybackSchedule::mTimeQueue, PlaybackSchedule::TimeQueue::Producer(), AudioIoCallback::SequenceShouldBeSilent(), size, and stackAllocate.

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 482 of file AudioIO.h.

482{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:285

◆ 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 1872 of file AudioIO.cpp.

1873{
1876}
void DrainRecordBuffers()
Second part of SequenceBufferExchange.
Definition: AudioIO.cpp:2213
void FillPlayBuffers()
First part of SequenceBufferExchange.
Definition: AudioIO.cpp:1878

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 1420 of file AudioIO.cpp.

1421{
1422 if (auto pInputMeter = mInputMeter.lock())
1423 pInputMeter->Reset(mRate, true);
1424 if (auto pOutputMeter = mOutputMeter.lock())
1425 pOutputMeter->Reset(mRate, true);
1426}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:318

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,
bool  publish = false 
)

Pause and un-pause playback and recording.

Definition at line 1691 of file AudioIO.cpp.

1692{
1693 if (state != IsPaused())
1694 {
1695 if (auto pOwningProject = mOwningProject.lock()) {
1696 // The realtime effects manager may remain "active" but becomes
1697 // "suspended" or "resumed".
1698 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1699 em.SetSuspended(state);
1700 }
1701 }
1702
1703 mPaused.store(state, std::memory_order_relaxed);
1704
1705 if (publish)
1706 Publish({ mOwningProject.lock().get(), AudioIOEvent::PAUSE, state });
1707}
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:307
bool IsPaused() const
Find out if playback / recording is currently paused.
CallbackReturn Publish(const AudioIOEvent &message)
Send a message to connected callbacks.
Definition: Observer.h:207

References RealtimeEffectManager::Get(), AudioIOBase::IsPaused(), AudioIOBase::mOwningProject, AudioIOBase::mPaused, AudioIOEvent::PAUSE, and Observer::Publisher< AudioIOEvent >::Publish().

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:310
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:298
bool mUsingAlsa
Definition: AudioIO.h:350
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:272
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 volume.
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:2617
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:1709
void SetMeters()
Set the current VU meters - this should be done once after each call to StartStream currently.
Definition: AudioIO.cpp:1420
bool mUsingJack
Definition: AudioIO.h:351
static DeviceManager * Instance()
Gets the singleton instance.
std::chrono::duration< float > GetTimeSinceRescan()
std::shared_ptr< AudacityProject > pProject
Definition: AudioIOBase.h:55
std::weak_ptr< Meter > captureMeter
Definition: AudioIOBase.h:56
std::weak_ptr< Meter > playbackMeter
Definition: AudioIOBase.h:56

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:3370
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:279
bool mbMicroFades
Definition: AudioIO.h:283
AudioIOExtRange Extensions()
Definition: AudioIO.h:163
void ClearRecordingException()
Definition: AudioIO.h:375
std::atomic< bool > mForceFadeOut
Definition: AudioIO.h:328
bool mDetectDropouts
Definition: AudioIO.h:380
void StopAudioThread()
Definition: AudioIO.cpp:3331
void WaitForAudioThreadStarted()
Definition: AudioIO.cpp:3321
void StartAudioThread()
Definition: AudioIO.cpp:3316
std::weak_ptr< AudioIOListener > mListener
Definition: AudioIO.h:348
std::vector< std::pair< double, double > > mLostCaptureIntervals
Definition: AudioIO.h:378
bool mPauseRec
True if Sound Activated Recording is enabled.
Definition: AudioIO.h:301
unsigned long long mLostSamples
Definition: AudioIO.h:309
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:71
PRCrossfadeData * pCrossfadeData
Definition: AudioIOBase.h:66
std::optional< double > pStartTime
Definition: AudioIOBase.h:60
std::shared_ptr< AudioIOListener > listener
Definition: AudioIOBase.h:58
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:73
std::vector< std::shared_ptr< const OtherPlayableSequence > > otherPlayableSequences
Definition: AudioIO.h:75
ConstPlayableSequences playbackSequences
Definition: AudioIO.h:72

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 1391 of file AudioIO.cpp.

1392{
1393 mpTransportState.reset();
1394
1395 mPlaybackBuffers.clear();
1396 mScratchBuffers.clear();
1397 mScratchPointers.clear();
1398 mPlaybackMixers.clear();
1399 mCaptureBuffers.clear();
1400 mResample.clear();
1402
1403 if(!bOnlyBuffers)
1404 {
1405 Pa_AbortStream( mPortStreamV19 );
1406 Pa_CloseStream( mPortStreamV19 );
1407 mPortStreamV19 = NULL;
1408 mStreamToken = 0;
1409 }
1410
1412}
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:1771

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 1428 of file AudioIO.cpp.

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

◆ 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 654 of file AudioIO.h.

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 652 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 651 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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