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

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

#include <AudioIO.h>

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

Public Types

using PostRecordingAction = std::function< void()>
 
- Public Types inherited from AudioIoCallback
using OldChannelGains = std::array< float, 2 >
 
- 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, Track *pTrack, const PluginID &id)
 Forwards to RealtimeEffectManager::AddState with proper init scope. More...
 
std::shared_ptr< RealtimeEffectStateReplaceState (AudacityProject &project, Track *pTrack, size_t index, const PluginID &id)
 Forwards to RealtimeEffectManager::ReplaceState with proper init scope. More...
 
void RemoveState (AudacityProject &project, Track *pTrack, 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 TransportTracks &tracks, double t0, double t1, double mixerLimit, const AudioIOStartStreamOptions &options)
 Start recording or playing back audio. More...
 
void StopStream () override
 Stop recording, playback or input monitoring. More...
 
void SeekStream (double seconds)
 Move the playback / recording position of the current stream by the specified amount from where it is now. More...
 
void CallAfterRecording (PostRecordingAction action)
 Enqueue action for main thread idle time, not before the end of any recording in progress. More...
 
wxString LastPaErrorString ()
 
wxLongLong GetLastPlaybackTime () const
 
std::shared_ptr< AudacityProjectGetOwningProject () const
 
void SetPaused (bool state)
 Pause and un-pause playback and recording. More...
 
void SetMixer (int inputSource, float inputVolume, float playbackVolume)
 
void GetMixer (int *inputSource, float *inputVolume, float *playbackVolume)
 
bool InputMixerWorks ()
 Find out if the input hardware level control is available. More...
 
wxArrayString GetInputSourceNames ()
 Get the list of inputs to the current mixer device. More...
 
sampleFormat GetCaptureFormat ()
 
unsigned GetNumPlaybackChannels () const
 
unsigned 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 track 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 CountSoloingTracks ()
 
bool TrackShouldBeSilent (const SampleTrack &wt)
 
bool TrackHasBeenFadedOut (const SampleTrack &wt, const OldChannelGains &gains)
 
bool AllTracksAlreadySilent ()
 
void CheckSoundActivatedRecordingLevel (float *inputSamples, unsigned long framesPerBuffer)
 
void AddToOutputChannel (unsigned int chan, float *outputMeterFloats, float *outputFloats, const float *tempBuf, bool drop, unsigned long len, const SampleTrack *vt, OldChannelGains &gains)
 
bool FillOutputBuffers (float *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void DrainInputBuffers (constSamplePtr inputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackFlags statusFlags, float *tempFloats)
 
void UpdateTimePosition (unsigned long framesPerBuffer)
 
void DoPlaythrough (constSamplePtr inputBuffer, float *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void SendVuInputMeterData (const float *inputSamples, unsigned long framesPerBuffer)
 
void SendVuOutputMeterData (const float *outputMeterFloats, unsigned long framesPerBuffer)
 
size_t GetCommonlyReadyPlayback ()
 Get the number of audio samples ready in all of the playback buffers. More...
 
size_t GetCommonlyWrittenForPlayback ()
 
void StartAudioThread ()
 
void WaitForAudioThreadStarted ()
 
void StopAudioThread ()
 
void WaitForAudioThreadStopped ()
 
void ProcessOnceAndWait (std::chrono::milliseconds sleepTime=std::chrono::milliseconds(50))
 
bool HasRecordingException () const
 
const std::vector< std::pair< double, double > > & LostCaptureIntervals ()
 
AudioIOExtRange Extensions ()
 
- Public Member Functions inherited from AudioIOBase
 AudioIOBase ()
 
virtual ~AudioIOBase ()
 
 AudioIOBase (const AudioIOBase &)=delete
 
AudioIOBaseoperator= (const AudioIOBase &)=delete
 
void SetCaptureMeter (const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
 
void SetPlaybackMeter (const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
 
void HandleDeviceChange ()
 update state after changing what audio devices are selected More...
 
wxString GetDeviceInfo () const
 Get diagnostic information on all the available audio I/O devices. More...
 
std::vector< AudioIODiagnosticsGetAllDeviceInfo ()
 Get diagnostic information for audio devices and also for extensions. More...
 
bool IsPaused () const
 Find out if playback / recording is currently paused. More...
 
virtual void StopStream ()=0
 
bool IsBusy () const
 Returns true if audio i/o is busy starting, stopping, playing, or recording. More...
 
bool IsStreamActive () const
 Returns true if the audio i/o is running at all, but not during cleanup. More...
 
bool IsStreamActive (int token) const
 
bool IsAudioTokenActive (int token) const
 Returns true if the stream is active, or even if audio I/O is busy cleaning up its data or writing to disk. More...
 
bool IsMonitoring () const
 Returns true if we're monitoring input (but not recording or playing actual audio) More...
 
void SetMixer (int inputSource)
 
- Public Member Functions inherited from Observer::Publisher< AudioIOEvent >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Static Public Member Functions

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

Private Member Functions

 AudioIO ()
 
 ~AudioIO ()
 
void StartThread ()
 
bool DelayingActions () const
 
void SetMeters ()
 Set the current VU meters - this should be done once after each call to StartStream currently. More...
 
bool StartPortAudioStream (const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels, sampleFormat captureFormat)
 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 TrackBufferExchange ()
 
void FillPlayBuffers ()
 First part of TrackBufferExchange. More...
 
void TransformPlayBuffers (std::optional< RealtimeEffects::ProcessingScope > &scope)
 
bool ProcessPlaybackSlices (std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
 
void DrainRecordBuffers ()
 Second part of TrackBufferExchange. 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 TransportTracks &tracks, 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 mbHasSoloTracks
 
int mCallbackReturn
 
long mNumPauseFrames
 How many frames of zeros were output due to pauses? More...
 
std::thread mAudioThread
 
std::atomic< bool > mFinishAudioThread { false }
 
ArrayOf< std::unique_ptr< Resample > > mResample
 
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
 
WritableSampleTrackArray mCaptureTracks
 
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
 
SampleTrackConstArray mPlaybackTracks
 
std::vector< OldChannelGainsmOldChannelGains
 
std::vector< SampleBuffermScratchBuffers
 
std::vector< float * > mScratchPointers
 pointing into mScratchBuffers More...
 
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
 
std::atomic< float > mMixerOutputVol { 1.0 }
 
double mFactor
 
unsigned long mMaxFramesOutput
 
bool mbMicroFades
 
double mSeek
 
PlaybackPolicy::Duration mPlaybackRingBufferSecs
 
double mCaptureRingBufferSecs
 
size_t mPlaybackSamplesToCopy
 Preferred batch size for replenishing the playback RingBuffer. More...
 
size_t 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
 
unsigned int mNumCaptureChannels
 
unsigned int mNumPlaybackChannels
 
sampleFormat mCaptureFormat
 
unsigned long long mLostSamples { 0 }
 
std::atomic< bool > mAudioThreadShouldCallTrackBufferExchangeOnce
 
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopRunning
 
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopActive
 
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 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 }
 
wxMutex mSuspendAudioThread
 
wxAtomicInt mRecordingException {}
 
std::vector< std::pair< double, double > > mLostCaptureIntervals
 
bool mDetectDropouts { true }
 
RecordingSchedule mRecordingSchedule {}
 
PlaybackSchedule mPlaybackSchedule
 
std::unique_ptr< TransportStatempTransportState
 Holds some state for duration of playback or recording. More...
 
- Protected Attributes inherited from AudioIOBase
std::weak_ptr< AudacityProjectmOwningProject
 
std::atomic< bool > mPaused { false }
 True if audio playback is paused. More...
 
int mStreamToken { 0 }
 
double mRate
 Audio playback rate in samples per second. More...
 
PaStreammPortStreamV19
 
std::weak_ptr< MetermInputMeter {}
 
std::weak_ptr< MetermOutputMeter {}
 
bool mInputMixerWorks
 Can we control the hardware input level? More...
 
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
 
- Static Protected Attributes inherited from AudioIoCallback
static double mCachedBestRateOut
 
static bool mCachedBestRatePlaying
 
static bool mCachedBestRateCapturing
 
- Static Protected Attributes inherited from AudioIOBase
static std::unique_ptr< AudioIOBaseugAudioIO
 
static int mCachedPlaybackIndex = -1
 
static std::vector< long > mCachedPlaybackRates
 
static int mCachedCaptureIndex = -1
 
static std::vector< long > mCachedCaptureRates
 
static std::vector< long > mCachedSampleRates
 
static double mCachedBestRateIn = 0.0
 
static const int RatesToTry []
 Array of audio sample rates to try to use. More...
 
static const int NumRatesToTry = WXSIZEOF(AudioIOBase::RatesToTry)
 How many sample rates to try. More...
 

Detailed Description

AudioIO uses the PortAudio library to play and record sound.

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

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

Definition at line 426 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 489 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 254 of file AudioIO.cpp.

255{
256 if (!std::atomic<double>{}.is_lock_free()) {
257 // If this check fails, then the atomic<double> members in AudioIO.h
258 // might be changed to atomic<float> to be more efficient with some
259 // loss of precision. That could be conditionally compiled depending
260 // on the platform.
261 wxASSERT(false);
262 }
263
264 // This ASSERT because of casting in the callback
265 // functions where we cast a tempFloats buffer to a (short*) buffer.
266 // We have to ASSERT in the GUI thread, if we are to see it properly.
267 wxASSERT( sizeof( short ) <= sizeof( float ));
268
270 .store(false, std::memory_order_relaxed);
272 .store(false, std::memory_order_relaxed);
274 .store(false, std::memory_order_relaxed);
275
276 mAudioThreadAcknowledge.store(Acknowledge::eNone, std::memory_order_relaxed);
277
278 mPortStreamV19 = NULL;
279
280 mNumPauseFrames = 0;
281
282#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
283 mAILAActive = false;
284#endif
285
286 mLastPaError = paNoError;
287
290 mSilenceLevel = 0.0;
291
292 mOutputMeter.reset();
293
294 PaError err = Pa_Initialize();
295
296 if (err != paNoError) {
297 auto errStr = XO("Could not find any audio devices.\n");
298 errStr += XO("You will not be able to play or record audio.\n\n");
299 wxString paErrStr = LAT1CTOWX(Pa_GetErrorText(err));
300 if (!paErrStr.empty())
301 errStr += XO("Error: %s").Format( paErrStr );
302 // XXX: we are in libaudacity, popping up dialogs not allowed! A
303 // long-term solution will probably involve exceptions
304 using namespace BasicUI;
306 errStr,
308 .Caption(XO("Error Initializing Audio"))
309 .IconStyle(Icon::Error)
310 .ButtonStyle(Button::Ok));
311
312 // Since PortAudio is not initialized, all calls to PortAudio
313 // functions will fail. This will give reasonable behavior, since
314 // the user will be able to do things not relating to audio i/o,
315 // but any attempt to play or record will simply fail.
316 }
317
318#if defined(USE_PORTMIXER)
319 mPortMixer = NULL;
320 mPreviousHWPlaythrough = -1.0;
322#else
323 mInputMixerWorks = false;
324#endif
325
327
329}
int PaError
Definition: AudioIO.h:56
DoubleSetting AudioIOPlaybackVolume
XO("Cut/Copy/Paste")
#define LAT1CTOWX(X)
Definition: Internat.h:160
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:260
PaStream * mPortStreamV19
Definition: AudioIOBase.h:257
void HandleDeviceChange()
update state after changing what audio devices are selected
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:274
std::atomic< Acknowledge > mAudioThreadAcknowledge
Definition: AudioIO.h:330
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopRunning
Definition: AudioIO.h:327
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopActive
Definition: AudioIO.h:328
long mNumPauseFrames
How many frames of zeros were output due to pauses?
Definition: AudioIO.h:260
std::atomic< bool > mAudioThreadShouldCallTrackBufferExchangeOnce
Definition: AudioIO.h:326
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:346
PaError mLastPaError
Definition: AudioIO.h:350
double mLastRecordingOffset
Not (yet) used; should perhaps be atomic when it is.
Definition: AudioIO.h:349
float mSilenceLevel
Definition: AudioIO.h:319
unsigned int mNumCaptureChannels
Definition: AudioIO.h:321
void SetMixerOutputVol(float value)
Definition: AudioIO.h:356
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
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:269
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:98

References AudioIOPlaybackVolume, BasicUI::MessageBoxOptions::Caption(), eNone, AudioIOBase::HandleDeviceChange(), LAT1CTOWX, AudioIoCallback::mAudioThreadAcknowledge, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mAudioThreadTrackBufferExchangeLoopActive, AudioIoCallback::mAudioThreadTrackBufferExchangeLoopRunning, 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 336 of file AudioIO.cpp.

337{
338 if ( !mOwningProject.expired() )
339 // Unlikely that this will be destroyed earlier than any projects, but
340 // be prepared anyway
342
343#if defined(USE_PORTMIXER)
344 if (mPortMixer) {
345 #if __WXMAC__
346 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
347 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
348 mPreviousHWPlaythrough = -1.0;
349 #endif
350 Px_CloseMixer(mPortMixer);
351 mPortMixer = NULL;
352 }
353#endif
354
355 // FIXME: ? TRAP_ERR. Pa_Terminate probably OK if err without reporting.
356 Pa_Terminate();
357
358 /* Delete is a "graceful" way to stop the thread.
359 (Kill is the not-graceful way.) */
360
361 // This causes reentrancy issues during application shutdown
362 // wxTheApp->Yield();
363
364 mFinishAudioThread.store(true, std::memory_order_release);
365 mAudioThread.join();
366}
std::weak_ptr< AudacityProject > mOwningProject
Definition: AudioIOBase.h:245
void ResetOwningProject()
Definition: AudioIO.cpp:706
std::atomic< bool > mFinishAudioThread
Definition: AudioIO.h:280
std::thread mAudioThread
Definition: AudioIO.h:279

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,
Track pTrack,
const PluginID id 
)

Forwards to RealtimeEffectManager::AddState with proper init scope.

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

Definition at line 369 of file AudioIO.cpp.

370{
372 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
373 if (auto pProject = GetOwningProject(); pProject.get() == &project)
374 pInit = &*mpTransportState->mpRealtimeInitialization;
375 return RealtimeEffectManager::Get(project).AddState(pInit, pTrack, id);
376}
std::shared_ptr< AudacityProject > GetOwningProject() const
Definition: AudioIO.h:499
std::unique_ptr< TransportState > mpTransportState
Holds some state for duration of playback or recording.
Definition: AudioIO.h:411
std::shared_ptr< RealtimeEffectState > AddState(RealtimeEffects::InitializationScope *pScope, Track *pTrack, const PluginID &id)
Main thread appends a global or per-track effect.
static RealtimeEffectManager & Get(AudacityProject &project)
Brackets processing setup and cleanup in the main thread.

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

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 TransportTracks tracks,
double  t0,
double  t1,
double  sampleRate 
)
private

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

Returns true iff successful.

Definition at line 1092 of file AudioIO.cpp.

1095{
1096 bool success = false;
1097 auto cleanup = finally([&]{
1098 if (!success) StartStreamCleanup( false );
1099 });
1100
1101 auto &policy = mPlaybackSchedule.GetPolicy();
1102 auto times = policy.SuggestedBufferTimes(mPlaybackSchedule);
1103
1104 //
1105 // The (audio) stream has been opened successfully (assuming we tried
1106 // to open it). We now proceed to
1107 // allocate the memory structures the stream will need.
1108 //
1109
1110 //
1111 // The RingBuffer sizes, and the max amount of the buffer to
1112 // fill at a time, both grow linearly with the number of
1113 // tracks. This allows us to scale up to many tracks without
1114 // killing performance.
1115 //
1116
1117 // real playback time to produce with each filling of the buffers
1118 // by the Audio thread (except at the end of playback):
1119 // usually, make fillings fewer and longer for less CPU usage.
1120 // What Audio thread produces for playback is then consumed by the PortAudio
1121 // thread, in many smaller pieces.
1122 double playbackTime = lrint(times.batchSize.count() * mRate) / mRate;
1123
1124 wxASSERT( playbackTime >= 0 );
1125 mPlaybackSamplesToCopy = playbackTime * mRate;
1126
1127 // Capacity of the playback buffer.
1128 mPlaybackRingBufferSecs = times.ringBufferDelay;
1129
1131 4.5 + 0.5 * std::min(size_t(16), mCaptureTracks.size());
1133 0.2 + 0.2 * std::min(size_t(16), mCaptureTracks.size());
1134
1135 bool bDone;
1136 do
1137 {
1138 bDone = true; // assume success
1139 try
1140 {
1141 if( mNumPlaybackChannels > 0 ) {
1142 // Allocate output buffers. For every output track we allocate
1143 // a ring buffer of ten seconds
1144 auto playbackBufferSize =
1145 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1146
1147 // Always make at least one playback buffer
1149 std::max<size_t>(1, mPlaybackTracks.size()));
1150 // Number of scratch buffers depends on device playback channels
1151 if (mNumPlaybackChannels > 0) {
1152 mScratchBuffers.resize(mNumPlaybackChannels * 2 + 1);
1153 mScratchPointers.clear();
1154 for (auto &buffer : mScratchBuffers) {
1155 buffer.Allocate(playbackBufferSize, floatSample);
1156 mScratchPointers.push_back(
1157 reinterpret_cast<float*>(buffer.ptr()));
1158 }
1159 }
1160 mPlaybackMixers.clear();
1161
1162 const auto &warpOptions =
1163 policy.MixerWarpOptions(mPlaybackSchedule);
1164
1165 mPlaybackQueueMinimum = lrint( mRate * times.latency.count() );
1167 std::min( mPlaybackQueueMinimum, playbackBufferSize );
1168
1169 if (mPlaybackTracks.empty())
1170 // Make at least one playback buffer
1171 mPlaybackBuffers[0] =
1172 std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1173
1174 mOldChannelGains.resize(mPlaybackTracks.size());
1175 for (unsigned int i = 0; i < mPlaybackTracks.size(); i++) {
1176 const auto &pTrack = mPlaybackTracks[i];
1177 // Bug 1763 - We must fade in from zero to avoid a click on starting.
1178 mOldChannelGains[i][0] = 0.0;
1179 mOldChannelGains[i][1] = 0.0;
1180
1181 mPlaybackBuffers[i] =
1182 std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1183
1184 if (pTrack->IsLeader()) {
1185 // use track time for the end time, not real time!
1186 double startTime, endTime;
1187 if (!tracks.prerollTracks.empty())
1188 startTime = mPlaybackSchedule.mT0;
1189 else
1190 startTime = t0;
1191
1193 .contains(pTrack))
1194 // Stop playing this track after pre-roll
1195 endTime = t0;
1196 else
1197 // Pass t1 -- not mT1 as may have been adjusted for latency
1198 // -- so that overdub recording stops playing back samples
1199 // at the right time, though transport may continue to
1200 // record
1201 endTime = t1;
1202
1203 Mixer::Inputs mixTracks;
1204 const auto range =
1205 TrackList::Channels<const SampleTrack>(pTrack.get());
1206 for (auto channel : range)
1207 mixTracks.push_back(
1208 channel->SharedPointer<const SampleTrack>());
1209 mPlaybackMixers.emplace_back(std::make_unique<Mixer>(
1210 move(mixTracks),
1211 // Don't throw for read errors, just play silence:
1212 false,
1213 warpOptions, startTime, endTime, range.size(),
1215 false, // not interleaved
1217 false, // low quality dithering and resampling
1218 nullptr, // no custom mix-down
1219 false // don't apply track gains
1220 ));
1221 }
1222 }
1223
1224 const auto timeQueueSize = 1 +
1225 (playbackBufferSize + TimeQueueGrainSize - 1)
1227 mPlaybackSchedule.mTimeQueue.Resize( timeQueueSize );
1228 }
1229
1230 if( mNumCaptureChannels > 0 )
1231 {
1232 // Allocate input buffers. For every input track we allocate
1233 // a ring buffer of five seconds
1234 auto captureBufferSize =
1235 (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
1236
1237 // In the extraordinarily rare case that we can't even afford
1238 // 100 samples, just give up.
1239 if(captureBufferSize < 100)
1240 {
1241 BasicUI::ShowMessageBox( XO("Out of memory!") );
1242 return false;
1243 }
1244
1247 mFactor = sampleRate / mRate;
1248
1249 for( unsigned int i = 0; i < mCaptureTracks.size(); i++ )
1250 {
1251 mCaptureBuffers[i] = std::make_unique<RingBuffer>(
1252 mCaptureTracks[i]->GetSampleFormat(), captureBufferSize );
1253 mResample[i] =
1254 std::make_unique<Resample>(true, mFactor, mFactor);
1255 // constant rate resampling
1256 }
1257 }
1258 }
1259 catch(std::bad_alloc&)
1260 {
1261 // Oops! Ran out of memory. This is pretty rare, so we'll just
1262 // try deleting everything, halving our buffer size, and try again.
1263 StartStreamCleanup(true);
1267 mMinCaptureSecsToCopy *= 0.5;
1268 bDone = false;
1269
1270 // In the extraordinarily rare case that we can't even afford 100
1271 // samples, just give up.
1272 auto playbackBufferSize =
1273 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1274 if(playbackBufferSize < 100 || mPlaybackSamplesToCopy < 100)
1275 {
1276 BasicUI::ShowMessageBox( XO("Out of memory!") );
1277 return false;
1278 }
1279 }
1280 } while(!bDone);
1281
1282 success = true;
1283 return true;
1284}
int min(int a, int b)
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:431
constexpr size_t TimeQueueGrainSize
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:255
void StartStreamCleanup(bool bOnlyBuffers=false)
Clean up after StartStream if it fails.
Definition: AudioIO.cpp:1286
PlaybackSchedule mPlaybackSchedule
Definition: AudioIO.h:409
std::vector< float * > mScratchPointers
pointing into mScratchBuffers
Definition: AudioIO.h:293
WritableSampleTrackArray mCaptureTracks
Definition: AudioIO.h:284
size_t mPlaybackSamplesToCopy
Preferred batch size for replenishing the playback RingBuffer.
Definition: AudioIO.h:309
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
Definition: AudioIO.h:295
PlaybackPolicy::Duration mPlaybackRingBufferSecs
Definition: AudioIO.h:305
unsigned int mNumPlaybackChannels
Definition: AudioIO.h:323
std::vector< OldChannelGains > mOldChannelGains
Definition: AudioIO.h:290
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
Definition: AudioIO.h:283
size_t mPlaybackQueueMinimum
Occupancy of the queue we try to maintain, with bigger batches if needed.
Definition: AudioIO.h:311
ArrayOf< std::unique_ptr< Resample > > mResample
Definition: AudioIO.h:282
double mCaptureRingBufferSecs
Definition: AudioIO.h:306
double mFactor
Definition: AudioIO.h:299
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
Definition: AudioIO.h:286
double mMinCaptureSecsToCopy
Definition: AudioIO.h:313
std::vector< SampleBuffer > mScratchBuffers
Definition: AudioIO.h:292
SampleTrackConstArray mPlaybackTracks
Definition: AudioIO.h:287
std::vector< Input > Inputs
Definition: Mix.h:43
virtual BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule)
Provide hints for construction of playback RingBuffer objects.
#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()
SampleTrackConstArray prerollTracks
Definition: AudioIO.h:92

References floatSample, PlaybackSchedule::GetPolicy(), lrint, make_iterator_range(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mCaptureTracks, AudioIoCallback::mFactor, min(), AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mOldChannelGains, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackRingBufferSecs, AudioIoCallback::mPlaybackSamplesToCopy, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mRate, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, PlaybackSchedule::mT0, PlaybackSchedule::mTimeQueue, TransportTracks::prerollTracks, ArrayOf< X >::reinit(), PlaybackSchedule::TimeQueue::Resize(), 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 1704 of file AudioIO.cpp.

1705{
1706 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1707 AudioIO *const gAudioIO = AudioIO::Get();
1708 while (!finish.load(std::memory_order_acquire)) {
1709 using Clock = std::chrono::steady_clock;
1710 auto loopPassStart = Clock::now();
1711 auto &schedule = gAudioIO->mPlaybackSchedule;
1712 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1713
1714 // Set LoopActive outside the tests to avoid race condition
1716 .store(true, std::memory_order_relaxed);
1718 .load(std::memory_order_acquire) )
1719 {
1720 gAudioIO->TrackBufferExchange();
1722 .store(false, std::memory_order_release);
1723
1724 lastState = State::eOnce;
1725 }
1727 .load(std::memory_order_relaxed))
1728 {
1729 if (lastState != State::eLoopRunning)
1730 {
1731 // Main thread has told us to start - acknowledge that we do
1733 std::memory_order::memory_order_release);
1734 }
1735 lastState = State::eLoopRunning;
1736
1737 // We call the processing after raising the acknowledge flag, because the main thread
1738 // only needs to know that the message was seen.
1739 //
1740 // This is unlike the case with mAudioThreadShouldCallTrackBufferExchangeOnce where the
1741 // store really means that the one-time exchange was done.
1742
1743 gAudioIO->TrackBufferExchange();
1744 }
1745 else
1746 {
1747 if ( (lastState == State::eLoopRunning)
1748 || (lastState == State::eMonitoring ) )
1749 {
1750 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1751 // acknowledge that we received the order and that no more processing will be done.
1753 std::memory_order::memory_order_release);
1754 }
1755 lastState = State::eDoNothing;
1756
1757 if (gAudioIO->IsMonitoring())
1758 {
1759 lastState = State::eMonitoring;
1760 }
1761 }
1762
1764 .store(false, std::memory_order_relaxed);
1765
1766 std::this_thread::sleep_until( loopPassStart + interval );
1767 }
1768}
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:429
void TrackBufferExchange()
Definition: AudioIO.cpp:1812
static AudioIO * Get()
Definition: AudioIO.cpp:147
virtual std::chrono::milliseconds SleepInterval(PlaybackSchedule &schedule)
How long to wait between calls to AudioIO::TrackBufferExchange.
Definition: Dither.cpp:67

References eStart, eStop, Get(), PlaybackSchedule::GetPolicy(), AudioIOBase::IsMonitoring(), AudioIoCallback::mAudioThreadAcknowledge, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mAudioThreadTrackBufferExchangeLoopActive, AudioIoCallback::mAudioThreadTrackBufferExchangeLoopRunning, AudioIoCallback::mPlaybackSchedule, PlaybackPolicy::SleepInterval(), and TrackBufferExchange().

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

1065{
1066 if (!action)
1067 return;
1068
1069 {
1070 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1072 // Enqueue it, even if perhaps not still recording,
1073 // but it wasn't cleared yet
1075 prevAction = std::move(mPostRecordingAction),
1076 nextAction = std::move(action)
1077 ]{ prevAction(); nextAction(); };
1078 return;
1079 }
1080 else if (DelayingActions()) {
1081 mPostRecordingAction = std::move(action);
1082 return;
1083 }
1084 }
1085
1086 // Don't delay it except until idle time.
1087 // (Recording might start between now and then, but won't go far before
1088 // the action is done. So the system isn't bulletproof yet.)
1089 BasicUI::CallAfter(move(action));
1090}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:658
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:659
bool DelayingActions() const
Definition: AudioIO.cpp:1059
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:206

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

Here is the call graph for this function:

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 240 of file AudioIO.cpp.

241{
242 ugAudioIO.reset();
243}
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:241

References AudioIOBase::ugAudioIO.

Referenced by AudacityApp::OnExit().

Here is the caller graph for this function:

◆ DelayActions()

void AudioIO::DelayActions ( bool  recording)

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

Definition at line 1054 of file AudioIO.cpp.

1055{
1056 mDelayingActions = recording;
1057}
bool mDelayingActions
Definition: AudioIO.h:661

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1059 of file AudioIO.cpp.

1060{
1062}

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 TrackBufferExchange.

Definition at line 2026 of file AudioIO.cpp.

2027{
2028 if (mRecordingException || mCaptureTracks.empty())
2029 return;
2030
2031 auto delayedHandler = [this] ( AudacityException * pException ) {
2032 // In the main thread, stop recording
2033 // This is one place where the application handles disk
2034 // exhaustion exceptions from wave track operations, without rolling
2035 // back to the last pushed undo state. Instead, partial recording
2036 // results are pushed as a NEW undo state. For this reason, as
2037 // commented elsewhere, we want an exception safety guarantee for
2038 // the output wave tracks, after the failed append operation, that
2039 // the tracks remain as they were after the previous successful
2040 // (block-level) appends.
2041
2042 // Note that the Flush in StopStream() may throw another exception,
2043 // but StopStream() contains that exception, and the logic in
2044 // AudacityException::DelayedHandlerAction prevents redundant message
2045 // boxes.
2046 StopStream();
2047 DefaultDelayedHandlerAction( pException );
2048 };
2049
2050 GuardedCall( [&] {
2051 // start record buffering
2052 const auto avail = GetCommonlyAvailCapture(); // samples
2053 const auto remainingTime =
2054 std::max(0.0, mRecordingSchedule.ToConsume());
2055 // This may be a very big double number:
2056 const auto remainingSamples = remainingTime * mRate;
2057 bool latencyCorrected = true;
2058
2059 double deltat = avail / mRate;
2060
2062 .load(std::memory_order_relaxed) ||
2063 deltat >= mMinCaptureSecsToCopy)
2064 {
2065 bool newBlocks = false;
2066
2067 // Append captured samples to the end of the WaveTracks.
2068 // The WaveTracks have their own buffering for efficiency.
2069 auto numChannels = mCaptureTracks.size();
2070
2071 for( size_t i = 0; i < numChannels; i++ )
2072 {
2073 sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
2074
2075 size_t discarded = 0;
2076
2078 const auto correction = mRecordingSchedule.TotalCorrection();
2079 if (correction >= 0) {
2080 // Rightward shift
2081 // Once only (per track per recording), insert some initial
2082 // silence.
2083 size_t size = floor( correction * mRate * mFactor);
2084 SampleBuffer temp(size, trackFormat);
2085 ClearSamples(temp.ptr(), trackFormat, 0, size);
2086 mCaptureTracks[i]->Append(temp.ptr(), trackFormat, size, 1,
2087 // Do not dither recordings
2089 }
2090 else {
2091 // Leftward shift
2092 // discard some samples from the ring buffers.
2093 size_t size = floor(
2095
2096 // The ring buffer might have grown concurrently -- don't discard more
2097 // than the "avail" value noted above.
2098 discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2099
2100 if (discarded < size)
2101 // We need to visit this again to complete the
2102 // discarding.
2103 latencyCorrected = false;
2104 }
2105 }
2106
2107 const float *pCrossfadeSrc = nullptr;
2108 size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2109 if (i < mRecordingSchedule.mCrossfadeData.size())
2110 {
2111 // Do crossfading
2112 // The supplied crossfade samples are at the same rate as the track
2113 const auto &data = mRecordingSchedule.mCrossfadeData[i];
2114 totalCrossfadeLength = data.size();
2115 if (totalCrossfadeLength) {
2116 crossfadeStart =
2117 floor(mRecordingSchedule.Consumed() * mCaptureTracks[i]->GetRate());
2118 if (crossfadeStart < totalCrossfadeLength)
2119 pCrossfadeSrc = data.data() + crossfadeStart;
2120 }
2121 }
2122
2123 wxASSERT(discarded <= avail);
2124 size_t toGet = avail - discarded;
2125 SampleBuffer temp;
2126 size_t size;
2128 if( mFactor == 1.0 )
2129 {
2130 // Take captured samples directly
2131 size = toGet;
2132 if (pCrossfadeSrc)
2133 // Change to float for crossfade calculation
2135 else
2136 format = trackFormat;
2137 temp.Allocate(size, format);
2138 const auto got =
2139 mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2140 // wxASSERT(got == toGet);
2141 // but we can't assert in this thread
2142 wxUnusedVar(got);
2143 if (double(size) > remainingSamples)
2144 size = floor(remainingSamples);
2145 }
2146 else
2147 {
2148 size = lrint(toGet * mFactor);
2150 SampleBuffer temp1(toGet, floatSample);
2151 temp.Allocate(size, format);
2152 const auto got =
2153 mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
2154 // wxASSERT(got == toGet);
2155 // but we can't assert in this thread
2156 wxUnusedVar(got);
2157 /* we are re-sampling on the fly. The last resampling call
2158 * must flush any samples left in the rate conversion buffer
2159 * so that they get recorded
2160 */
2161 if (toGet > 0 ) {
2162 if (double(toGet) > remainingSamples)
2163 toGet = floor(remainingSamples);
2164 const auto results =
2165 mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
2166 !IsStreamActive(), (float *)temp.ptr(), size);
2167 size = results.second;
2168 }
2169 }
2170
2171 if (pCrossfadeSrc) {
2172 wxASSERT(format == floatSample);
2173 size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
2174 if (crossfadeLength) {
2175 auto ratio = double(crossfadeStart) / totalCrossfadeLength;
2176 auto ratioStep = 1.0 / totalCrossfadeLength;
2177 auto pCrossfadeDst = (float*)temp.ptr();
2178
2179 // Crossfade loop here
2180 for (size_t ii = 0; ii < crossfadeLength; ++ii) {
2181 *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
2182 ++pCrossfadeSrc, ++pCrossfadeDst;
2183 ratio += ratioStep;
2184 }
2185 }
2186 }
2187
2188 // Now append
2189 // see comment in second handler about guarantee
2190 newBlocks = mCaptureTracks[i]->Append(
2191 temp.ptr(), format, size, 1,
2192 // Do not dither recordings
2194 ) || newBlocks;
2195 } // end loop over capture channels
2196
2197 // Now update the recording schedule position
2199 mRecordingSchedule.mLatencyCorrected = latencyCorrected;
2200
2201 auto pListener = GetListener();
2202 if (pListener && newBlocks)
2203 pListener->OnAudioIONewBlocks(&mCaptureTracks);
2204
2205 }
2206 // end of record buffering
2207 },
2208 // handler
2209 [this] ( AudacityException *pException ) {
2210 if ( pException ) {
2211 // So that we don't attempt to fill the recording buffer again
2212 // before the main thread stops recording
2214 return ;
2215 }
2216 else
2217 // Don't want to intercept other exceptions (?)
2218 throw;
2219 },
2220 delayedHandler );
2221}
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...
int format
Definition: ExportPCM.cpp:53
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:1323
size_t GetCommonlyAvailCapture()
Get the number of audio samples ready in all of the recording buffers.
Definition: AudioIO.cpp:1800
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:185
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:408
void SetRecordingException()
Definition: AudioIO.h:386
wxAtomicInt mRecordingException
Definition: AudioIO.h:385
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:138
samplePtr ptr() const
Definition: SampleFormat.h:152
double TotalCorrection() const
double ToDiscard() const
double ToConsume() const
double Consumed() const
PRCrossfadeData mCrossfadeData

References SampleBuffer::Allocate(), ClearSamples(), RecordingSchedule::Consumed(), DefaultDelayedHandlerAction(), floatSample, format, GetCommonlyAvailCapture(), AudioIoCallback::GetListener(), GuardedCall(), AudioIOBase::IsStreamActive(), lrint, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, RecordingSchedule::mCrossfadeData, AudioIoCallback::mFactor, min(), RecordingSchedule::mLatencyCorrected, AudioIoCallback::mMinCaptureSecsToCopy, 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 TrackBufferExchange().

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

◆ FillPlayBuffers()

void AudioIO::FillPlayBuffers ( )
private

First part of TrackBufferExchange.

Definition at line 1818 of file AudioIO.cpp.

1819{
1820 std::optional<RealtimeEffects::ProcessingScope> pScope;
1821 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
1822 pScope.emplace(
1823 *mpTransportState->mpRealtimeInitialization, mOwningProject);
1824
1825 if (mNumPlaybackChannels == 0)
1826 return;
1827
1828 // It is possible that some buffers will have more samples available than
1829 // others. This could happen if we hit this code during the PortAudio
1830 // callback. Also, if in a previous pass, unequal numbers of samples were
1831 // discarded from ring buffers for differing latencies.
1832
1833 // To keep things simple, we write no more data than is vacant in
1834 // ALL buffers, and advance the global time by that much.
1835 auto nAvailable = GetCommonlyFreePlayback();
1836
1837 // Don't fill the buffers at all unless we can do
1838 // at least mPlaybackSamplesToCopy. This improves performance
1839 // by not always trying to process tiny chunks, eating the
1840 // CPU unnecessarily.
1841 if (nAvailable < mPlaybackSamplesToCopy)
1842 return;
1843
1844 // More than mPlaybackSamplesToCopy might be copied:
1845 // May produce a larger amount when initially priming the buffer, or
1846 // perhaps again later in play to avoid underfilling the queue and
1847 // falling behind the real-time demand on the consumer side in the
1848 // callback.
1849 auto GetNeeded = [&]() -> size_t {
1850 // Note that reader might concurrently consume between loop passes below
1851 // So this might not be nondecreasing
1852 auto nReady = GetCommonlyWrittenForPlayback();
1854 };
1855 auto nNeeded = GetNeeded();
1856
1857 // wxASSERT( nNeeded <= nAvailable );
1858
1859 auto Flush = [&]{
1860 /* The flushing of all the Puts to the RingBuffers is lifted out of the
1861 do-loop in ProcessPlaybackSlices, and also after transformation of the
1862 stream for realtime effects.
1863
1864 It's only here that a release is done on the atomic variable that
1865 indicates the readiness of sample data to the consumer. That atomic
1866 also synchronizes the use of the TimeQueue.
1867 */
1868 for (size_t i = 0; i < std::max(size_t{1}, mPlaybackTracks.size()); ++i)
1869 mPlaybackBuffers[i]->Flush();
1870 };
1871
1872 while (true) {
1873 // Limit maximum buffer size (increases performance)
1874 auto available = std::min( nAvailable,
1875 std::max( nNeeded, mPlaybackSamplesToCopy ) );
1876
1877 // After each loop pass or after break
1878 Finally Do{ Flush };
1879
1880 if (!ProcessPlaybackSlices(pScope, available))
1881 // We are not making progress. May fail to satisfy the minimum but
1882 // won't loop forever
1883 break;
1884
1885 // Loop again to satisfy the minimum queue requirement in case there
1886 // was discarding of processed data for effect latencies
1887 nNeeded = GetNeeded();
1888 if (nNeeded == 0)
1889 break;
1890
1891 // Might increase because the reader consumed some
1892 nAvailable = GetCommonlyFreePlayback();
1893 }
1894}
bool ProcessPlaybackSlices(std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
Definition: AudioIO.cpp:1896
size_t GetCommonlyFreePlayback()
Get the number of audio samples free in all of the playback buffers.
Definition: AudioIO.cpp:1771
size_t GetCommonlyWrittenForPlayback()
Definition: AudioIO.cpp:1791
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:173

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

Referenced by TrackBufferExchange().

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

◆ Get()

AudioIO * AudioIO::Get ( )
static

Definition at line 147 of file AudioIO.cpp.

148{
149 return static_cast< AudioIO* >( AudioIOBase::Get() );
150}
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:91

References AudioIOBase::Get().

Referenced by AdornedRulerPanel::AdornedRulerPanel(), audacityAudioCallback(), AudioThread(), ProjectAudioManager::CanStopAudioStream(), CaptureNotBusyFlag(), EffectUIHost::CleanupRealtime(), DefaultSpeedPlayOptions(), anonymous_namespace{SelectMenus.cpp}::DoBoundaryMove(), PlayIndicatorOverlayBase::DoGetRectangle(), ProjectAudioManager::DoPlayStopSelect(), ProjectAudioManager::DoRecord(), PlayIndicatorOverlayBase::Draw(), ControlToolBar::EnableDisableButtons(), TranscriptionToolBar::EnableDisableButtons(), ScrubbingPlaybackPolicy::GetPlaybackSlice(), UpdateManager::GetUpdates(), HistoryDialog::HistoryDialog(), EffectUIHost::InitializeInstance(), LyricsPanel::LyricsPanel(), MixerBoard::MixerBoard(), 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(), SelectionBar::OnIdle(), TimeToolBar::OnIdle(), AudacityApp::OnKeyDown(), ProjectAudioManager::OnPause(), SelectActions::Handler::OnSelectCursorStoredCursor(), SelectUtilities::OnSetRegion(), anonymous_namespace{PluginMenus.cpp}::OnSimulateRecordingErrors(), ProjectAudioManager::OnSoundActivationThreshold(), ProjectWindow::PlaybackScroller::OnTimer(), LyricsWindow::OnTimer(), MixerBoard::OnTimer(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), TransportUtilities::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), TransportUtilities::PlayPlayRegionAndWait(), EffectBase::Preview(), TransportUtilities::RecordAndWait(), ProjectAudioManager::Recording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::RemoveFromList(), ScrubbingPlaybackPolicy::RepositionPlayback(), TimerRecordDialog::RunWaitDialog(), anonymous_namespace{SelectMenus.cpp}::SeekWhenAudioActive(), MeterPanel::SetMixer(), MeterPanel::StartMonitoring(), StartMonitoring(), ControlToolBar::StartScrolling(), ProjectAudioManager::Stop(), MeterPanel::StopMonitoring(), EffectUIHost::StopPlayback(), anonymous_namespace{PluginMenus.cpp}::ToolsMenu(), TrackPanel::TrackPanel(), AdornedRulerPanel::UpdateButtonStates(), MeterPanel::UpdateSelectedPrefs(), MeterPanel::UpdateSliderControl(), and ProjectWindow::ZoomInByFactor().

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

1609{
1610 // Check if we can use the cached value
1611 if (mCachedBestRateIn != 0.0 && mCachedBestRateIn == sampleRate
1612 && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
1613 return mCachedBestRateOut;
1614 }
1615
1616 // In order to cache the value, all early returns should instead set retval
1617 // and jump to finished
1618 double retval;
1619
1620 std::vector<long> rates;
1621 if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
1622 if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
1623 wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
1624
1625 if (capturing && !playing) {
1626 rates = GetSupportedCaptureRates(-1, sampleRate);
1627 }
1628 else if (playing && !capturing) {
1629 rates = GetSupportedPlaybackRates(-1, sampleRate);
1630 }
1631 else { // we assume capturing and playing - the alternative would be a
1632 // bit odd
1633 rates = GetSupportedSampleRates(-1, -1, sampleRate);
1634 }
1635 /* rem rates is the array of hardware-supported sample rates (in the current
1636 * configuration), sampleRate is the Project Rate (desired sample rate) */
1637 long rate = (long)sampleRate;
1638
1639 if (make_iterator_range(rates).contains(rate)) {
1640 wxLogDebug(wxT("GetBestRate() Returning %.0ld Hz"), rate);
1641 retval = rate;
1642 goto finished;
1643 /* the easy case - the suggested rate (project rate) is in the list, and
1644 * we can just accept that and send back to the caller. This should be
1645 * the case for most users most of the time (all of the time on
1646 * Win MME as the OS does resampling) */
1647 }
1648
1649 /* if we get here, there is a problem - the project rate isn't supported
1650 * on our hardware, so we can't us it. Need to come up with an alternative
1651 * rate to use. The process goes like this:
1652 * * If there are no rates to pick from, we're stuck and return 0 (error)
1653 * * If there are some rates, we pick the next one higher than the requested
1654 * rate to use.
1655 * * If there aren't any higher, we use the highest available rate */
1656
1657 if (rates.empty()) {
1658 /* we're stuck - there are no supported rates with this hardware. Error */
1659 wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
1660 retval = 0.0;
1661 goto finished;
1662 }
1663 int i;
1664 for (i = 0; i < (int)rates.size(); i++) // for each supported rate
1665 {
1666 if (rates[i] > rate) {
1667 // supported rate is greater than requested rate
1668 wxLogDebug(wxT("GetBestRate() Returning next higher rate - %.0ld Hz"), rates[i]);
1669 retval = rates[i];
1670 goto finished;
1671 }
1672 }
1673
1674 wxLogDebug(wxT("GetBestRate() Returning highest rate - %.0ld Hz"), rates.back());
1675 retval = rates.back(); // the highest available rate
1676 goto finished;
1677
1678finished:
1679 mCachedBestRateIn = sampleRate;
1680 mCachedBestRateOut = retval;
1681 mCachedBestRatePlaying = playing;
1682 mCachedBestRateCapturing = capturing;
1683 return retval;
1684}
wxT("CloseDown"))
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1, double rate=0.0)
Get a list of sample rates the input (recording) device supports.
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1, double rate=0.0)
Get a list of sample rates the output (playback) device supports.
static double mCachedBestRateIn
Definition: AudioIOBase.h:282
static std::vector< long > GetSupportedSampleRates(int playDevice=-1, int recDevice=-1, double rate=0.0)
Get a list of sample rates the current input/output device combination supports.
static double mCachedBestRateOut
Definition: AudioIO.h:367
static bool mCachedBestRatePlaying
Definition: AudioIO.h:368
static bool mCachedBestRateCapturing
Definition: AudioIO.h:369

References AudioIOBase::GetSupportedCaptureRates(), AudioIOBase::GetSupportedPlaybackRates(), AudioIOBase::GetSupportedSampleRates(), make_iterator_range(), AudioIoCallback::mCachedBestRateCapturing, AudioIOBase::mCachedBestRateIn, AudioIoCallback::mCachedBestRateOut, AudioIoCallback::mCachedBestRatePlaying, 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 529 of file AudioIO.h.

529{ return mCaptureFormat; }
sampleFormat mCaptureFormat
Definition: AudioIO.h:324

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

1801{
1802 auto commonlyAvail = mCaptureBuffers[0]->AvailForGet();
1803 for (unsigned i = 1; i < mCaptureTracks.size(); ++i)
1804 commonlyAvail = std::min(commonlyAvail,
1805 mCaptureBuffers[i]->AvailForGet());
1806 return commonlyAvail;
1807}

References AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, and min().

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

1772{
1773 auto commonlyAvail = mPlaybackBuffers[0]->AvailForPut();
1774 for (unsigned i = 1; i < mPlaybackTracks.size(); ++i)
1775 commonlyAvail = std::min(commonlyAvail,
1776 mPlaybackBuffers[i]->AvailForPut());
1777 // MB: subtract a few samples because the code in TrackBufferExchange has rounding
1778 // errors
1779 return commonlyAvail - std::min(size_t(10), commonlyAvail);
1780}

References min(), AudioIoCallback::mPlaybackBuffers, and AudioIoCallback::mPlaybackTracks.

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

453{
454#if defined(USE_PORTMIXER)
455
456 wxArrayString deviceNames;
457
458 if( mPortMixer )
459 {
460 int numSources = Px_GetNumInputSources(mPortMixer);
461 for( int source = 0; source < numSources; source++ )
462 deviceNames.push_back(wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(mPortMixer, source))));
463 }
464 else
465 {
466 wxLogDebug(wxT("AudioIO::GetInputSourceNames(): PortMixer not initialised!"));
467 }
468
469 return deviceNames;
470
471#else
472
473 wxArrayString blank;
474
475 return blank;
476
477#endif
478}

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 498 of file AudioIO.h.

498{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 420 of file AudioIO.cpp.

422{
423 *playbackVolume = GetMixerOutputVol();
424
425#if defined(USE_PORTMIXER)
426
427 PxMixer *mixer = mPortMixer;
428
429 if( mixer )
430 {
431 *recordDevice = Px_GetCurrentInputSource(mixer);
432
434 *recordVolume = Px_GetInputVolume(mixer);
435 else
436 *recordVolume = 1.0f;
437
438 return;
439 }
440
441#endif
442
443 *recordDevice = 0;
444 *recordVolume = 1.0f;
445}
float GetMixerOutputVol()
Definition: AudioIO.h:354

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

unsigned AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 531 of file AudioIO.h.

531{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

unsigned AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 530 of file AudioIO.h.

530{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 499 of file AudioIO.h.

500 { return mOwningProject.lock(); }

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

Here is the caller graph for this function:

◆ GetStreamTime()

double AudioIO::GetStreamTime ( )

During playback, the track 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 1686 of file AudioIO.cpp.

1687{
1688 // Track time readout for the main thread
1689
1690 if( !IsStreamActive() )
1691 return BAD_STREAM_TIME;
1692
1694}
#define BAD_STREAM_TIME
Definition: AudioIOBase.h:38
double GetTrackTime() const
Get current track time value, unadjusted.

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

Here is the call graph for this function:

◆ Init()

void AudioIO::Init ( )
static

Definition at line 212 of file AudioIO.cpp.

213{
214 auto pAudioIO = safenew AudioIO();
215 ugAudioIO.reset(pAudioIO);
216 pAudioIO->StartThread();
217
218 // Make sure device prefs are initialized
219 if (gPrefs->Read(wxT("AudioIO/RecordingDevice"), wxT("")).empty()) {
220 int i = getRecordDevIndex();
221 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
222 if (info) {
225 }
226 }
227
228 if (gPrefs->Read(wxT("AudioIO/PlaybackDevice"), wxT("")).empty()) {
229 int i = getPlayDevIndex();
230 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
231 if (info) {
234 }
235 }
236
237 gPrefs->Flush();
238}
StringSetting AudioIOPlaybackDevice
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:70
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:80
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:73
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:254
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252

References AudioIO(), AudioIOHost, AudioIOPlaybackDevice, AudioIORecordingDevice, AudioIOBase::DeviceName(), FileConfig::Flush(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), gPrefs, AudioIOBase::HostName(), 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 447 of file AudioIO.cpp.

448{
449 return mInputMixerWorks;
450}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1309 of file AudioIO.cpp.

1310{
1311 auto pOwningProject = mOwningProject.lock();
1312 return !pOwningProject || pOwningProject.get() == &project;
1313}

References AudioIOBase::mOwningProject.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3292 of file AudioIO.cpp.

3293{
3294 // Includes a test of mTime, used in the main thread
3295 return IsStreamActive() &&
3296 GetNumCaptureChannels() > 0 &&
3299}
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:531

References PlaybackSchedule::GetTrackTime(), 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 690 of file AudioIO.cpp.

691{
692 return wxString::Format(wxT("%d %s."), (int) mLastPaError, Pa_GetErrorText(mLastPaError));
693}

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

1898{
1899 auto &policy = mPlaybackSchedule.GetPolicy();
1900
1901 // msmeyer: When playing a very short selection in looped
1902 // mode, the selection must be copied to the buffer multiple
1903 // times, to ensure, that the buffer has a reasonable size
1904 // This is the purpose of this loop.
1905 // PRL: or, when scrubbing, we may get work repeatedly from the
1906 // user interface.
1907 bool done = false;
1908 bool progress = false;
1909 do {
1910 const auto slice =
1911 policy.GetPlaybackSlice(mPlaybackSchedule, available);
1912 const auto &[frames, toProduce] = slice;
1913 progress = progress || toProduce > 0;
1914
1915 // Update the time queue. This must be done before writing to the
1916 // ring buffers of samples, for proper synchronization with the
1917 // consumer side in the PortAudio thread, which reads the time
1918 // queue after reading the sample queues. The sample queues use
1919 // atomic variables, the time queue doesn't.
1921
1922 size_t i = 0;
1923 for (auto &mixer : mPlaybackMixers) {
1924 // The mixer here isn't actually mixing: it's just doing
1925 // resampling, format conversion, and possibly time track
1926 // warping
1927 if (frames > 0) {
1928 size_t produced = 0;
1929 if ( toProduce )
1930 produced = mixer->Process( toProduce );
1931 //wxASSERT(produced <= toProduce);
1932 for(size_t j = 0, nChannels =
1934 j < nChannels; ++i, ++j
1935 ) {
1936 auto warpedSamples = mixer->GetBuffer(j);
1937 const auto put = mPlaybackBuffers[i]->Put(
1938 warpedSamples, floatSample, produced, frames - produced);
1939 // wxASSERT(put == frames);
1940 // but we can't assert in this thread
1941 wxUnusedVar(put);
1942 }
1943 }
1944 }
1945
1946 if (mPlaybackTracks.empty())
1947 // Produce silence in the single ring buffer
1948 mPlaybackBuffers[0]->Put(nullptr, floatSample, 0, frames);
1949
1950 available -= frames;
1951 // wxASSERT(available >= 0); // don't assert on this thread
1952
1953 done = policy.RepositionPlayback( mPlaybackSchedule, mPlaybackMixers,
1954 frames, available );
1955 } while (available && !done);
1956
1957 // Do any realtime effect processing, more efficiently in at most
1958 // two buffers per track, after all the little slices have been written.
1959 TransformPlayBuffers(pScope);
1960 return progress;
1961}
void TransformPlayBuffers(std::optional< RealtimeEffects::ProcessingScope > &scope)
Definition: AudioIO.cpp:1964
virtual PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available)
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
void Producer(PlaybackSchedule &schedule, PlaybackSlice slice)
Enqueue track time value advanced by the slice according to schedule's PlaybackPolicy.
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1541

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

Referenced by FillPlayBuffers().

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

◆ RemoveState()

void AudioIO::RemoveState ( AudacityProject project,
Track pTrack,
std::shared_ptr< RealtimeEffectState pState 
)

Forwards to RealtimeEffectManager::RemoveState with proper init scope.

Definition at line 390 of file AudioIO.cpp.

392{
394 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
395 if (auto pProject = GetOwningProject(); pProject.get() == &project)
396 pInit = &*mpTransportState->mpRealtimeInitialization;
397 RealtimeEffectManager::Get(project).RemoveState(pInit, pTrack, pState);
398}
void RemoveState(RealtimeEffects::InitializationScope *pScope, Track *pTrack, std::shared_ptr< RealtimeEffectState > pState)
Main thread removes a global or per-track effect.

References RealtimeEffectManager::Get(), GetOwningProject(), AudioIoCallback::mpTransportState, 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,
Track pTrack,
size_t  index,
const PluginID id 
)

Forwards to RealtimeEffectManager::ReplaceState with proper init scope.

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

Definition at line 379 of file AudioIO.cpp.

381{
383 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
384 if (auto pProject = GetOwningProject(); pProject.get() == &project)
385 pInit = &*mpTransportState->mpRealtimeInitialization;
386 return RealtimeEffectManager::Get(project)
387 .ReplaceState(pInit, pTrack, index, id);
388}
std::shared_ptr< RealtimeEffectState > ReplaceState(RealtimeEffects::InitializationScope *pScope, Track *pTrack, size_t index, const PluginID &id)
Main thread replaces a global or per-track effect.

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

Here is the call graph for this function:

◆ ResetOwningProject()

void AudioIO::ResetOwningProject ( )
private

Definition at line 706 of file AudioIO.cpp.

707{
708 mOwningProject.reset();
709}

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

487{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:304

◆ SetMeters()

void AudioIO::SetMeters ( )
private

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

Definition at line 1315 of file AudioIO.cpp.

1316{
1317 if (auto pInputMeter = mInputMeter.lock())
1318 pInputMeter->Reset(mRate, true);
1319 if (auto pOutputMeter = mOutputMeter.lock())
1320 pOutputMeter->Reset(mRate, true);
1321}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:259

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

Referenced by StartPortAudioStream().

Here is the caller graph for this function:

◆ SetMixer()

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

Definition at line 400 of file AudioIO.cpp.

402{
403 SetMixerOutputVol(playbackVolume);
404 AudioIOPlaybackVolume.Write(playbackVolume);
405
406#if defined(USE_PORTMIXER)
407 PxMixer *mixer = mPortMixer;
408 if( !mixer )
409 return;
410
411 float oldRecordVolume = Px_GetInputVolume(mixer);
412
413 AudioIoCallback::SetMixer(inputSource);
414 if( oldRecordVolume != recordVolume )
415 Px_SetInputVolume(mixer, recordVolume);
416
417#endif
418}
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 695 of file AudioIO.cpp.

697{
698 if ( !mOwningProject.expired() ) {
699 wxASSERT(false);
701 }
702
703 mOwningProject = pProject;
704}

References AudioIOBase::mOwningProject, and ResetOwningProject().

Referenced by StartPortAudioStream().

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

◆ SetPaused()

void AudioIO::SetPaused ( bool  state)

Pause and un-pause playback and recording.

Definition at line 1593 of file AudioIO.cpp.

1594{
1595 if (state != IsPaused())
1596 {
1597 if (auto pOwningProject = mOwningProject.lock()) {
1598 // The realtime effects manager may remain "active" but becomes
1599 // "suspended" or "resumed".
1600 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1601 em.SetSuspended(state);
1602 }
1603 }
1604
1605 mPaused.store(state, std::memory_order_relaxed);
1606}
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:248
bool IsPaused() const
Find out if playback / recording is currently paused.

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

Here is the call graph for this function:

◆ StartMonitoring()

void AudioIO::StartMonitoring ( const AudioIOStartStreamOptions options)

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

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

Definition at line 711 of file AudioIO.cpp.

712{
714 return;
715
716 bool success;
717 auto captureFormat = QualitySettings::SampleFormatChoice();
718 auto captureChannels = AudioIORecordChannels.Read();
719 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
720 int playbackChannels = 0;
721
723 playbackChannels = 2;
724
725 // FIXME: TRAP_ERR StartPortAudioStream (a PaError may be present)
726 // but StartPortAudioStream function only returns true or false.
727 mUsingAlsa = false;
728 success = StartPortAudioStream(options, (unsigned int)playbackChannels,
729 (unsigned int)captureChannels,
730 captureFormat);
731
732 auto pOwningProject = mOwningProject.lock();
733 if (!success) {
734 using namespace BasicUI;
735 auto msg = XO("Error opening recording device.\nError code: %s")
736 .Format( Get()->LastPaErrorString() );
737 ShowErrorDialog( *ProjectFramePlacement( pOwningProject.get() ),
738 XO("Error"), msg, wxT("Error_opening_sound_device"),
739 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
740 return;
741 }
742
743 Publish({ pOwningProject.get(), AudioIOEvent::MONITOR, true });
744
745 // FIXME: TRAP_ERR PaErrorCode 'noted' but not reported in StartMonitoring.
746 // Now start the PortAudio stream!
747 // TODO: ? Factor out and reuse error reporting code from end of
748 // AudioIO::StartStream?
749 mLastPaError = Pa_StartStream( mPortStreamV19 );
750
751 // Update UI display only now, after all possibilities for error are past.
752 auto pListener = GetListener();
753 if ((mLastPaError == paNoError) && pListener) {
754 // advertise the chosen I/O sample rate to the UI
755 pListener->OnAudioIORate((int)mRate);
756 }
757}
IntSetting AudioIORecordChannels
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
int mStreamToken
Definition: AudioIOBase.h:251
bool StartPortAudioStream(const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels, sampleFormat captureFormat)
Opens the portaudio stream(s) used to do playback or recording (or both) through.
Definition: AudioIO.cpp:493
wxString LastPaErrorString()
Definition: AudioIO.cpp:690
bool mSoftwarePlaythrough
Definition: AudioIO.h:315
bool mUsingAlsa
Definition: AudioIO.h:364
CallbackReturn Publish(const AudioIOEvent &message)
Send a message to connected callbacks.
Definition: Observer.h:207
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:254
PROJECT_RATE_API sampleFormat SampleFormatChoice()
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49

References AudioIORecordChannels, Get(), AudioIoCallback::GetListener(), gPrefs, LastPaErrorString(), AudioIoCallback::mLastPaError, AudioIOEvent::MONITOR, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, AudioIoCallback::mUsingAlsa, ProjectFramePlacement(), Observer::Publisher< AudioIOEvent >::Publish(), 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,
sampleFormat  captureFormat 
)
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 493 of file AudioIO.cpp.

497{
498 auto sampleRate = options.rate;
499 mNumPauseFrames = 0;
500 SetOwningProject( options.pProject );
501 bool success = false;
502 auto cleanup = finally([&]{
503 if (!success)
505 });
506
507 // PRL: Protection from crash reported by David Bailes, involving starting
508 // and stopping with frequent changes of active window, hard to reproduce
509 if (mOwningProject.expired())
510 return false;
511
512 mInputMeter.reset();
513 mOutputMeter.reset();
514
515 mLastPaError = paNoError;
516 // pick a rate to do the audio I/O at, from those available. The project
517 // rate is suggested, but we may get something else if it isn't supported
518 mRate = GetBestRate(numCaptureChannels > 0, numPlaybackChannels > 0, sampleRate);
519
520 // July 2016 (Carsten and Uwe)
521 // BUG 193: Tell PortAudio sound card will handle 24 bit (under DirectSound) using
522 // userData.
523 auto captureFormat_saved = captureFormat;
524 // Special case: Our 24-bit sample format is different from PortAudio's
525 // 3-byte packed format. So just make PortAudio return float samples,
526 // since we need float values anyway to apply the gain.
527 // ANSWER-ME: So we *never* actually handle 24-bit?! This causes mCapture to
528 // be set to floatSample below.
529 // JKC: YES that's right. Internally Audacity uses float, and float has space for
530 // 24 bits as well as exponent. Actual 24 bit would require packing and
531 // unpacking unaligned bytes and would be inefficient.
532 // ANSWER ME: is floatSample 64 bit on 64 bit machines?
533 if (captureFormat == int24Sample)
534 captureFormat = floatSample;
535
536 mNumPlaybackChannels = numPlaybackChannels;
537 mNumCaptureChannels = numCaptureChannels;
538
539 bool usePlayback = false, useCapture = false;
540 PaStreamParameters playbackParameters{};
541 PaStreamParameters captureParameters{};
542
543 auto latencyDuration = AudioIOLatencyDuration.Read();
544
545 if( numPlaybackChannels > 0)
546 {
547 usePlayback = true;
548
549 // this sets the device index to whatever is "right" based on preferences,
550 // then defaults
551 playbackParameters.device = getPlayDevIndex();
552
553 const PaDeviceInfo *playbackDeviceInfo;
554 playbackDeviceInfo = Pa_GetDeviceInfo( playbackParameters.device );
555
556 if( playbackDeviceInfo == NULL )
557 return false;
558
559 // regardless of source formats, we always mix to float
560 playbackParameters.sampleFormat = paFloat32;
561 playbackParameters.hostApiSpecificStreamInfo = NULL;
562 playbackParameters.channelCount = mNumPlaybackChannels;
563
565 playbackParameters.suggestedLatency =
566 playbackDeviceInfo->defaultLowOutputLatency;
567 else {
568 // When using WASAPI, the suggested latency does not affect
569 // the latency of the playback, but the position of playback is given as if
570 // there was the suggested latency. This results in the last "suggested latency"
571 // of a selection not being played. So for WASAPI use 0.0 for the suggested
572 // latency regardless of user setting. See bug 1949.
573 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(playbackDeviceInfo->hostApi);
574 bool isWASAPI = (hostInfo && hostInfo->type == paWASAPI);
575 playbackParameters.suggestedLatency = isWASAPI ? 0.0 : latencyDuration/1000.0;
576 }
577
578 mOutputMeter = options.playbackMeter;
579 }
580
581 if( numCaptureChannels > 0)
582 {
583 useCapture = true;
584 mCaptureFormat = captureFormat;
585
586 const PaDeviceInfo *captureDeviceInfo;
587 // retrieve the index of the device set in the prefs, or a sensible
588 // default if it isn't set/valid
589 captureParameters.device = getRecordDevIndex();
590
591 captureDeviceInfo = Pa_GetDeviceInfo( captureParameters.device );
592
593 if( captureDeviceInfo == NULL )
594 return false;
595
596 captureParameters.sampleFormat =
598
599 captureParameters.hostApiSpecificStreamInfo = NULL;
600 captureParameters.channelCount = mNumCaptureChannels;
601
603 captureParameters.suggestedLatency =
604 captureDeviceInfo->defaultHighInputLatency;
605 else
606 captureParameters.suggestedLatency = latencyDuration/1000.0;
607
609 }
610
611 SetMeters();
612
613#ifdef USE_PORTMIXER
614#ifdef __WXMSW__
615 //mchinen nov 30 2010. For some reason Pa_OpenStream resets the input volume on windows.
616 //so cache and restore after it.
617 //The actual problem is likely in portaudio's pa_win_wmme.c OpenStream().
618 float oldRecordVolume = Px_GetInputVolume(mPortMixer);
619#endif
620#endif
621
622 // July 2016 (Carsten and Uwe)
623 // BUG 193: Possibly tell portAudio to use 24 bit with DirectSound.
624 int userData = 24;
625 int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
626
627 // (Linux, bug 1885) After scanning devices it takes a little time for the
628 // ALSA device to be available, so allow retries.
629 // On my test machine, no more than 3 attempts are required.
630 unsigned int maxTries = 1;
631#ifdef __WXGTK__
632 {
633 using namespace std::chrono;
635 maxTries = 5;
636 }
637#endif
638
639 for (unsigned int tries = 0; tries < maxTries; tries++) {
640 mLastPaError = Pa_OpenStream( &mPortStreamV19,
641 useCapture ? &captureParameters : NULL,
642 usePlayback ? &playbackParameters : NULL,
643 mRate, paFramesPerBufferUnspecified,
644 paNoFlag,
645 audacityAudioCallback, lpUserData );
646 if (mLastPaError == paNoError) {
647 break;
648 }
649 wxLogDebug("Attempt %u to open capture stream failed with: %d", 1 + tries, mLastPaError);
650 using namespace std::chrono;
651 std::this_thread::sleep_for(1s);
652 }
653
654
655#if USE_PORTMIXER
656#ifdef __WXMSW__
657 Px_SetInputVolume(mPortMixer, oldRecordVolume);
658#endif
659 if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
660
661 #ifdef __WXMAC__
662 if (mPortMixer) {
663 if (Px_SupportsPlaythrough(mPortMixer)) {
664 bool playthrough = false;
665
666 mPreviousHWPlaythrough = Px_GetPlaythrough(mPortMixer);
667
668 // Bug 388. Feature not supported.
669 //gPrefs->Read(wxT("/AudioIO/Playthrough"), &playthrough, false);
670 if (playthrough)
671 Px_SetPlaythrough(mPortMixer, 1.0);
672 else
673 Px_SetPlaythrough(mPortMixer, 0.0);
674 }
675 }
676 #endif
677 }
678#endif
679
680#if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
681 // Don't want the system to sleep while audio I/O is active
682 if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
683 wxPowerResource::Acquire(wxPOWER_RESOURCE_SCREEN, _("Audacity Audio"));
684 }
685#endif
686
687 return (success = (mLastPaError == paNoError));
688}
int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
Definition: AudioIO.cpp:2423
static PaSampleFormat AudacityToPortAudioSampleFormat(sampleFormat format)
Definition: AudioIO.cpp:480
DoubleSetting AudioIOLatencyDuration
#define _(s)
Definition: Internat.h:75
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:695
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:1608
void SetMeters()
Set the current VU meters - this should be done once after each call to StartStream currently.
Definition: AudioIO.cpp:1315
static DeviceManager * Instance()
Gets the singleton instance.
std::chrono::duration< float > GetTimeSinceRescan()
std::shared_ptr< AudacityProject > pProject
Definition: AudioIOBase.h:53
std::weak_ptr< Meter > captureMeter
Definition: AudioIOBase.h:54
std::weak_ptr< Meter > playbackMeter
Definition: AudioIOBase.h:54

References _, audacityAudioCallback(), AudacityToPortAudioSampleFormat(), AudioIOLatencyDuration, AudioIOStartStreamOptions::captureMeter, floatSample, GetBestRate(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), DeviceManager::GetTimeSinceRescan(), DeviceManager::Instance(), int24Sample, AudioIoCallback::mCaptureFormat, AudioIOBase::mInputMeter, AudioIoCallback::mLastPaError, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPauseFrames, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIOStartStreamOptions::playbackMeter, AudioIOStartStreamOptions::pProject, AudioIOStartStreamOptions::rate, Setting< T >::Read(), ResetOwningProject(), 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 TransportTracks tracks,
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()

Parameters
mixerLimitTime at which mixer stops producing, maybe > t1

Definition at line 759 of file AudioIO.cpp.

762{
763 const auto &pStartTime = options.pStartTime;
764 t1 = std::min(t1, mixerLimit);
765
766 mLostSamples = 0;
767 mLostCaptureIntervals.clear();
769 gPrefs->Read( WarningDialogKey(wxT("DropoutDetected")), true ) != 0;
770 auto cleanup = finally ( [this] { ClearRecordingException(); } );
771
772 if( IsBusy() )
773 return 0;
774
775 // We just want to set mStreamToken to -1 - this way avoids
776 // an extremely rare but possible race condition, if two functions
777 // somehow called StartStream at the same time...
778 mStreamToken--;
779 if (mStreamToken != -1)
780 return 0;
781
782 // TODO: we don't really need to close and reopen stream if the
783 // format matches; however it's kind of tricky to keep it open...
784 //
785 // if (sampleRate == mRate &&
786 // playbackChannels == mNumPlaybackChannels &&
787 // captureChannels == mNumCaptureChannels &&
788 // captureFormat == mCaptureFormat) {
789
790 if (mPortStreamV19) {
791 StopStream();
792 while(mPortStreamV19) {
793 using namespace std::chrono;
794 std::this_thread::sleep_for(50ms);
795 }
796 }
797
798#ifdef __WXGTK__
799 // Detect whether ALSA is the chosen host, and do the various involved MIDI
800 // timing compensations only then.
801 mUsingAlsa = (AudioIOHost.Read() == L"ALSA");
802#endif
803
804 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
805 gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &mPauseRec, false);
806 gPrefs->Read(wxT("/AudioIO/Microfades"), &mbMicroFades, false);
807 int silenceLevelDB;
808 gPrefs->Read(wxT("/AudioIO/SilenceLevel"), &silenceLevelDB, -50);
809 int dBRange = DecibelScaleCutoff.Read();
810 if(silenceLevelDB < -dBRange)
811 {
812 silenceLevelDB = -dBRange + 3;
813 // meter range was made smaller than SilenceLevel
814 // so set SilenceLevel reasonable
815
816 // PRL: update prefs, or correct it only in-session?
817 // The behavior (as of 2.3.1) was the latter, the code suggested that
818 // the intent was the former; I preserve the behavior, but uncomment
819 // this if you disagree.
820 // gPrefs->Write(wxT("/AudioIO/SilenceLevel"), silenceLevelDB);
821 // gPrefs->Flush();
822 }
823 mSilenceLevel = DB_TO_LINEAR(silenceLevelDB); // meter goes -dBRange dB -> 0dB
824
825 // Clamp pre-roll so we don't play before time 0
826 const auto preRoll = std::max(0.0, std::min(t0, options.preRoll));
832 if (options.pCrossfadeData)
834
835 mListener = options.listener;
836 mRate = options.rate;
837
838 mSeek = 0;
842
843 bool commit = false;
844 auto cleanupTracks = finally([&]{
845 if (!commit) {
846 // Don't keep unnecessary shared pointers to tracks
847 mPlaybackTracks.clear();
848 mCaptureTracks.clear();
849 for(auto &ext : Extensions())
850 ext.AbortOtherStream();
851
852 // Don't cause a busy wait in the audio thread after stopping scrubbing
854 }
855 });
856
857 mPlaybackBuffers.reset();
858 mScratchBuffers.clear();
859 mScratchPointers.clear();
860 mPlaybackMixers.clear();
861 mCaptureBuffers.reset();
862 mResample.reset();
864
866 t0, t1, options, mCaptureTracks.empty() ? nullptr : &mRecordingSchedule );
867
868 unsigned int playbackChannels = 0;
869 unsigned int captureChannels = 0;
870 sampleFormat captureFormat = floatSample;
871
872 auto pListener = GetListener();
873
874 if (tracks.playbackTracks.size() > 0
875 || tracks.otherPlayableTracks.size() > 0)
876 playbackChannels = 2;
877
879 playbackChannels = 2;
880
881 if (tracks.captureTracks.size() > 0)
882 {
883 // For capture, every input channel gets its own track
884 captureChannels = mCaptureTracks.size();
885 // I don't deal with the possibility of the capture tracks
886 // having different sample formats, since it will never happen
887 // with the current code. This code wouldn't *break* if this
888 // assumption was false, but it would be sub-optimal. For example,
889 // if the first track was 16-bit and the second track was 24-bit,
890 // we would set the sound card to capture in 16 bits and the second
891 // track wouldn't get the benefit of all 24 bits the card is capable
892 // of.
893 captureFormat = mCaptureTracks[0]->GetSampleFormat();
894
895 // Tell project that we are about to start recording
896 if (pListener)
897 pListener->OnAudioIOStartRecording();
898 }
899
900 bool successAudio;
901
902 successAudio = StartPortAudioStream(options, playbackChannels,
903 captureChannels, captureFormat);
904
905 // Call this only after reassignment of mRate that might happen in the
906 // previous call.
908
909#ifdef EXPERIMENTAL_MIDI_OUT
910 auto range = Extensions();
911 successAudio = successAudio &&
912 std::all_of(range.begin(), range.end(),
913 [this, &tracks, t0](auto &ext){
914 return ext.StartOtherStream( tracks,
915 (mPortStreamV19 != NULL && mLastPaError == paNoError)
916 ? Pa_GetStreamInfo(mPortStreamV19) : nullptr,
917 t0, mRate ); });
918#endif
919
920 if (!successAudio) {
921 if (pListener && captureChannels > 0)
922 pListener->OnAudioIOStopRecording();
923 mStreamToken = 0;
924
925 return 0;
926 }
927
928 {
929 double mixerStart = t0;
930 if (pStartTime)
931 mixerStart = std::min( mixerStart, *pStartTime );
932 if ( ! AllocateBuffers( options, tracks,
933 mixerStart, mixerLimit, options.rate ) )
934 return 0;
935 }
936
937 mpTransportState = std::make_unique<TransportState>( mOwningProject,
939
940#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
941 AILASetStartTime();
942#endif
943
944 if (pStartTime)
945 {
946 // Calculate the NEW time position
947 const auto time = *pStartTime;
948
949 // Main thread's initialization of mTime
952
953 // Reset mixer positions for all playback tracks
954 for (auto &mixer : mPlaybackMixers)
955 mixer->Reposition( time );
956 }
957
958 // Now that we are done with AllocateBuffers() and SetTrackTime():
960 // else recording only without overdub
961
962 // We signal the audio thread to call TrackBufferExchange, to prime the RingBuffers
963 // so that they will have data in them when the stream starts. Having the
964 // audio thread call TrackBufferExchange here makes the code more predictable, since
965 // TrackBufferExchange will ALWAYS get called from the Audio thread.
967 .store(true, std::memory_order_release);
968
970 .load(std::memory_order_acquire)) {
971 using namespace std::chrono;
972 auto interval = 50ms;
973 if (options.playbackStreamPrimer) {
974 interval = options.playbackStreamPrimer();
975 }
976 std::this_thread::sleep_for(interval);
977 }
978
980
981#ifdef REALTIME_ALSA_THREAD
982 // PRL: Do this in hope of less thread scheduling jitter in calls to
983 // audacityAudioCallback.
984 // Not needed to make audio playback work smoothly.
985 // But needed in case we also play MIDI, so that the variable "offset"
986 // in AudioIO::MidiTime() is a better approximation of the duration
987 // between the call of audacityAudioCallback and the actual output of
988 // the first audio sample.
989 // (Which we should be able to determine from fields of
990 // PaStreamCallbackTimeInfo, but that seems not to work as documented with
991 // ALSA.)
992 if (mUsingAlsa)
993 // Perhaps we should do this only if also playing MIDI ?
994 PaAlsa_EnableRealtimeScheduling( mPortStreamV19, 1 );
995#endif
996
997 //
998 // Generate a unique value each time, to be returned to
999 // clients accessing the AudioIO API, so they can query if they
1000 // are the ones who have reserved AudioIO or not.
1001 //
1002 // It is important to set this before setting the portaudio stream in
1003 // motion -- otherwise it may play an unspecified number of leading
1004 // zeroes.
1006
1007 // This affects AudioThread (not the portaudio callback).
1008 // Probably not needed so urgently before portaudio thread start for usual
1009 // playback, since our ring buffers have been primed already with 4 sec
1010 // of audio, but then we might be scrubbing, so do it.
1012
1013 mForceFadeOut.store(false, std::memory_order_relaxed);
1014
1015 // Now start the PortAudio stream!
1016 PaError err;
1017 err = Pa_StartStream( mPortStreamV19 );
1018
1019 if( err != paNoError )
1020 {
1021 mStreamToken = 0;
1022
1024
1025 if (pListener && mNumCaptureChannels > 0)
1026 pListener->OnAudioIOStopRecording();
1028 // PRL: PortAudio error messages are sadly not internationalized
1030 Verbatim( LAT1CTOWX(Pa_GetErrorText(err)) ) );
1031 return 0;
1032 }
1033 }
1034
1035 // Update UI display only now, after all possibilities for error are past.
1036 if (pListener) {
1037 // advertise the chosen I/O sample rate to the UI
1038 pListener->OnAudioIORate((int)mRate);
1039 }
1040
1041 auto pOwningProject = mOwningProject.lock();
1042 if (mNumPlaybackChannels > 0)
1043 Publish({ pOwningProject.get(), AudioIOEvent::PLAYBACK, true });
1044 if (mNumCaptureChannels > 0)
1045 Publish({ pOwningProject.get(), AudioIOEvent::CAPTURE, true });
1046
1047 commit = true;
1048
1050
1051 return mStreamToken;
1052}
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:543
wxString WarningDialogKey(const wxString &internalDialogName)
Definition: Prefs.cpp:468
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 TransportTracks &tracks, double t0, double t1, double sampleRate)
Allocate RingBuffer structures, and others, needed for playback and recording.
Definition: AudioIO.cpp:1092
static int mNextStreamToken
Definition: AudioIO.h:298
bool mbMicroFades
Definition: AudioIO.h:302
AudioIOExtRange Extensions()
Definition: AudioIO.h:177
void ClearRecordingException()
Definition: AudioIO.h:388
std::atomic< bool > mForceFadeOut
Definition: AudioIO.h:344
bool mDetectDropouts
Definition: AudioIO.h:393
void StopAudioThread()
Definition: AudioIO.cpp:3262
void WaitForAudioThreadStarted()
Definition: AudioIO.cpp:3252
void StartAudioThread()
Definition: AudioIO.cpp:3247
std::weak_ptr< AudioIOListener > mListener
Definition: AudioIO.h:362
std::vector< std::pair< double, double > > mLostCaptureIntervals
Definition: AudioIO.h:391
bool mPauseRec
True if Sound Activated Recording is enabled.
Definition: AudioIO.h:318
unsigned long long mLostSamples
Definition: AudioIO.h:325
virtual double OffsetTrackTime(PlaybackSchedule &schedule, double offset)
Called when the play head needs to jump a certain distance.
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
void Prime(double time)
Empty the queue and reassign the last produced time.
std::function< std::chrono::milliseconds() > playbackStreamPrimer
Definition: AudioIOBase.h:69
PRCrossfadeData * pCrossfadeData
Definition: AudioIOBase.h:64
std::optional< double > pStartTime
Definition: AudioIOBase.h:58
std::shared_ptr< AudioIOListener > listener
Definition: AudioIOBase.h:56
void Init(double t0, double t1, const AudioIOStartStreamOptions &options, const RecordingSchedule *pRecordingSchedule)
void SetTrackTime(double time)
Set current track time value, unadjusted.
PlayableTrackConstArray otherPlayableTracks
Definition: AudioIO.h:89
WritableSampleTrackArray captureTracks
Definition: AudioIO.h:88
SampleTrackConstArray playbackTracks
Definition: AudioIO.h:87

References AllocateBuffers(), AudioIOHost, AudioIOLatencyCorrection, AudioIOEvent::CAPTURE, TransportTracks::captureTracks, PlaybackSchedule::TimeQueue::Clear(), AudioIoCallback::ClearRecordingException(), DB_TO_LINEAR, DecibelScaleCutoff, AudioIoCallback::Extensions(), floatSample, AudioIoCallback::GetListener(), PlaybackSchedule::GetPolicy(), PlaybackSchedule::GetTrackTime(), gPrefs, PlaybackSchedule::Init(), PlaybackPolicy::Initialize(), AudioIOBase::IsBusy(), LAT1CTOWX, AudioIOStartStreamOptions::listener, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, 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::mPlaybackTracks, 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::OffsetTrackTime(), TransportTracks::otherPlayableTracks, AudioIOStartStreamOptions::pCrossfadeData, AudioIOEvent::PLAYBACK, AudioIOStartStreamOptions::playbackStreamPrimer, TransportTracks::playbackTracks, AudioIOStartStreamOptions::preRoll, PlaybackSchedule::TimeQueue::Prime(), AudioIOStartStreamOptions::pStartTime, Observer::Publisher< AudioIOEvent >::Publish(), AudioIOStartStreamOptions::rate, Setting< T >::Read(), PlaybackSchedule::ResetMode(), PlaybackSchedule::SetTrackTime(), BasicUI::ShowMessageBox(), 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 1286 of file AudioIO.cpp.

1287{
1288 mpTransportState.reset();
1289
1290 mPlaybackBuffers.reset();
1291 mScratchBuffers.clear();
1292 mScratchPointers.clear();
1293 mPlaybackMixers.clear();
1294 mCaptureBuffers.reset();
1295 mResample.reset();
1297
1298 if(!bOnlyBuffers)
1299 {
1300 Pa_AbortStream( mPortStreamV19 );
1301 Pa_CloseStream( mPortStreamV19 );
1302 mPortStreamV19 = NULL;
1303 mStreamToken = 0;
1304 }
1305
1307}
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 331 of file AudioIO.cpp.

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

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 wave tracks, and applies latency correction to recorded tracks if necessary

Implements AudioIOBase.

Definition at line 1323 of file AudioIO.cpp.

1324{
1325 auto cleanup = finally ( [this] {
1327 mRecordingSchedule.mCrossfadeData.clear(); // free arrays
1328 } );
1329
1330 if( mPortStreamV19 == NULL )
1331 return;
1332
1333 // DV: This code seems to be unnecessary.
1334 // We do not leave mPortStreamV19 open in stopped
1335 // state. (Do we?)
1336 // This breaks WASAPI backend, as it sets the `running`
1337 // flag to `false` asynchronously.
1338 // Previously we have patched PortAudio and the patch
1339 // was breaking IsStreamStopped() == !IsStreamActive()
1340 // invariant.
1341 /*
1342 if ( Pa_IsStreamStopped(mPortStreamV19) )
1343 return;
1344 */
1345
1346#if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
1347 // Re-enable system sleep
1348 wxPowerResource::Release(wxPOWER_RESOURCE_SCREEN);
1349#endif
1350
1352 .load(std::memory_order_relaxed) )
1353 {
1354 // PortAudio callback can use the information that we are stopping to fade
1355 // out the audio. Give PortAudio callback a chance to do so.
1356 mForceFadeOut.store(true, std::memory_order_relaxed);
1357 auto latency = static_cast<long>(AudioIOLatencyDuration.Read());
1358 // If we can gracefully fade out in 200ms, with the faded-out play buffers making it through
1359 // the sound card, then do so. If we can't, don't wait around. Just stop quickly and accept
1360 // there will be a click.
1361 if( mbMicroFades && (latency < 150 )) {
1362 using namespace std::chrono;
1363 std::this_thread::sleep_for(milliseconds{latency + 50});
1364 }
1365 }
1366
1367 wxMutexLocker locker(mSuspendAudioThread);
1368
1369 //
1370 // We got here in one of two ways:
1371 //
1372 // 1. The user clicked the stop button and we therefore want to stop
1373 // as quickly as possible. So we use AbortStream(). If this is
1374 // the case the portaudio stream is still in the Running state
1375 // (see PortAudio state machine docs).
1376 //
1377 // 2. The callback told PortAudio to stop the stream since it had
1378 // reached the end of the selection. The UI thread discovered
1379 // this by noticing that AudioIO::IsActive() returned false.
1380 // IsActive() (which calls Pa_GetStreamActive()) will not return
1381 // false until all buffers have finished playing, so we can call
1382 // AbortStream without losing any samples. If this is the case
1383 // we are in the "callback finished state" (see PortAudio state
1384 // machine docs).
1385 //
1386 // The moral of the story: We can call AbortStream safely, without
1387 // losing samples.
1388 //
1389 // DMM: This doesn't seem to be true; it seems to be necessary to
1390 // call StopStream if the callback brought us here, and AbortStream
1391 // if the user brought us here.
1392 //
1393 // DV: Seems that Pa_CloseStream calls Pa_AbortStream internally,
1394 // at least for PortAudio 19.7.0+
1395
1397
1398 // Turn off HW playthrough if PortMixer is being used
1399
1400 #if defined(USE_PORTMIXER)
1401 if( mPortMixer ) {
1402 #if __WXMAC__
1403 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
1404 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
1405 mPreviousHWPlaythrough = -1.0;
1406 #endif
1407 }
1408 #endif
1409
1410 if (mPortStreamV19) {
1411 // DV: Pa_CloseStream will close Pa_AbortStream internally,
1412 // but it doesn't hurt to do it ourselves.
1413 // PA_AbortStream will silently fail if stream is stopped.
1414 if (!Pa_IsStreamStopped( mPortStreamV19 ))
1415 Pa_AbortStream( mPortStreamV19 );
1416
1417 Pa_CloseStream( mPortStreamV19 );
1418
1419 mPortStreamV19 = NULL;
1420 }
1421
1422
1423
1424 // We previously told AudioThread to stop processing, now let's
1425 // be sure it has really stopped before resetting mpTransportState
1427
1428
1429 for( auto &ext : Extensions() )
1430 ext.StopOtherStream();
1431
1432 auto pListener = GetListener();
1433
1434 // If there's no token, we were just monitoring, so we can
1435 // skip this next part...
1436 if (mStreamToken > 0) {
1437 // In either of the above cases, we want to make sure that any
1438 // capture data that made it into the PortAudio callback makes it
1439 // to the target WaveTrack. To do this, we ask the audio thread to
1440 // call TrackBufferExchange one last time (it normally would not do so since
1441 // Pa_GetStreamActive() would now return false
1443 }
1444
1445 // No longer need effects processing. This must be done after the stream is stopped
1446 // to prevent the callback from being invoked after the effects are finalized.
1447 mpTransportState.reset();
1448
1449 //
1450 // Everything is taken care of. Now, just free all the resources
1451 // we allocated in StartStream()
1452 //
1453 if (mPlaybackTracks.size() > 0)
1454 {
1455 mPlaybackBuffers.reset();
1456 mScratchBuffers.clear();
1457 mScratchPointers.clear();
1458 mPlaybackMixers.clear();
1460 }
1461
1462 if (mStreamToken > 0)
1463 {
1464 //
1465 // Offset all recorded tracks to account for latency
1466 //
1467 if (mCaptureTracks.size() > 0)
1468 {
1469 mCaptureBuffers.reset();
1470 mResample.reset();
1471
1472 //
1473 // We only apply latency correction when we actually played back
1474 // tracks during the recording. If we did not play back tracks,
1475 // there's nothing we could be out of sync with. This also covers the
1476 // case that we do not apply latency correction when recording the
1477 // first track in a project.
1478 //
1479
1480 for (unsigned int i = 0; i < mCaptureTracks.size(); i++) {
1481 // The calls to Flush
1482 // may cause exceptions because of exhaustion of disk space.
1483 // Stop those exceptions here, or else they propagate through too
1484 // many parts of Audacity that are not effects or editing
1485 // operations. GuardedCall ensures that the user sees a warning.
1486
1487 // Also be sure to Flush each track, at the top of the guarded call,
1488 // relying on the guarantee that the track will be left in a flushed
1489 // state, though the append buffer may be lost.
1490
1491 GuardedCall( [&] {
1492 auto track = mCaptureTracks[i].get();
1493
1494 // use No-fail-guarantee that track is flushed,
1495 // Partial-guarantee that some initial length of the recording
1496 // is saved.
1497 // See comments in TrackBufferExchange().
1498 track->Flush();
1499 } );
1500 }
1501
1502
1503 if (!mLostCaptureIntervals.empty())
1504 {
1505 // This scope may combine many splittings of wave tracks
1506 // into one transaction, lessening the number of checkpoints
1507 std::optional<TransactionScope> pScope;
1508 if (auto pOwningProject = mOwningProject.lock())
1509 pScope.emplace(*pOwningProject, "Dropouts");
1510 for (auto &interval : mLostCaptureIntervals) {
1511 auto &start = interval.first;
1512 auto duration = interval.second;
1513 for (auto &track : mCaptureTracks) {
1514 GuardedCall([&] {
1515 track->SyncLockAdjust(start, start + duration);
1516 });
1517 }
1518 }
1519 if (pScope)
1520 pScope->Commit();
1521 }
1522
1523 if (pListener)
1524 pListener->OnCommitRecording();
1525 }
1526 }
1527
1528
1529
1530 if (auto pInputMeter = mInputMeter.lock())
1531 pInputMeter->Reset(mRate, false);
1532
1533 if (auto pOutputMeter = mOutputMeter.lock())
1534 pOutputMeter->Reset(mRate, false);
1535
1536 mInputMeter.reset();
1537 mOutputMeter.reset();
1539
1540 if (pListener && mNumCaptureChannels > 0)
1541 pListener->OnAudioIOStopRecording();
1542
1543 BasicUI::CallAfter([this]{
1545 // Recording was restarted between StopStream and idle time
1546 // So the actions can keep waiting
1547 return;
1548 // In case some other thread was waiting on the mutex too:
1549 std::this_thread::yield();
1550 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1554 }
1555 DelayActions(false);
1556 });
1557
1558 //
1559 // Only set token to 0 after we're totally finished with everything
1560 //
1561 bool wasMonitoring = mStreamToken == 0;
1562 mStreamToken = 0;
1563
1564 {
1565 auto pOwningProject = mOwningProject.lock();
1566 if (mNumPlaybackChannels > 0)
1567 Publish({ pOwningProject.get(), AudioIOEvent::PLAYBACK, false });
1568 if (mNumCaptureChannels > 0)
1569 Publish({ pOwningProject.get(),
1570 wasMonitoring
1573 false });
1574 }
1575
1578
1579 mPlaybackTracks.clear();
1580 mCaptureTracks.clear();
1581
1583
1584 if (pListener) {
1585 // Tell UI to hide sample rate
1586 pListener->OnAudioIORate(0);
1587 }
1588
1589 // Don't cause a busy wait in the audio thread after stopping scrubbing
1591}
void DelayActions(bool recording)
Definition: AudioIO.cpp:1054
wxMutex mSuspendAudioThread
Definition: AudioIO.h:373
void ProcessOnceAndWait(std::chrono::milliseconds sleepTime=std::chrono::milliseconds(50))
Definition: AudioIO.cpp:3277
void WaitForAudioThreadStopped()
Definition: AudioIO.cpp:3267
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::mAudioThreadTrackBufferExchangeLoopRunning, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, 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::mPlaybackTracks, 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:

◆ TrackBufferExchange()

void AudioIO::TrackBufferExchange ( )
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 1812 of file AudioIO.cpp.

1813{
1816}
void DrainRecordBuffers()
Second part of TrackBufferExchange.
Definition: AudioIO.cpp:2026
void FillPlayBuffers()
First part of TrackBufferExchange.
Definition: AudioIO.cpp:1818

References DrainRecordBuffers(), and FillPlayBuffers().

Referenced by AudioThread().

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

◆ TransformPlayBuffers()

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

Definition at line 1964 of file AudioIO.cpp.

1966{
1967 // Transform written but un-flushed samples in the RingBuffers in-place.
1968
1969 // Avoiding std::vector
1970 auto pointers =
1971 static_cast<float**>(alloca(mNumPlaybackChannels * sizeof(float*)));
1972
1973 const auto numPlaybackTracks = mPlaybackTracks.size();
1974 for (unsigned t = 0; t < numPlaybackTracks; ++t) {
1975 const auto vt = mPlaybackTracks[t].get();
1976 if (!(vt && vt->IsLeader()))
1977 continue;
1978 // vt is mono, or is the first of its group of channels
1979 const auto nChannels = std::min<size_t>(
1981
1982 // Loop over the blocks of unflushed data, at most two
1983 for (unsigned iBlock : {0, 1}) {
1984 size_t len = 0;
1985 size_t iChannel = 0;
1986 for (; iChannel < nChannels; ++iChannel) {
1987 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
1988 const auto pair =
1989 ringBuffer.GetUnflushed(iBlock);
1990 // Playback RingBuffers have float format: see AllocateBuffers
1991 pointers[iChannel] = reinterpret_cast<float*>(pair.first);
1992 // The lengths of corresponding unflushed blocks should be
1993 // the same for all channels
1994 if (len == 0)
1995 len = pair.second;
1996 else
1997 assert(len == pair.second);
1998 }
1999
2000 // Are there more output device channels than channels of vt?
2001 // Such as when a mono track is processed for stereo play?
2002 // Then supply some non-null fake input buffers, because the
2003 // various ProcessBlock overrides of effects may crash without it.
2004 // But it would be good to find the fixes to make this unnecessary.
2005 float **scratch = &mScratchPointers[mNumPlaybackChannels + 1];
2006 while (iChannel < mNumPlaybackChannels)
2007 memset((pointers[iChannel++] = *scratch++), 0, len * sizeof(float));
2008
2009 if (len && pScope) {
2010 auto discardable = pScope->Process( *vt, &pointers[0],
2011 mScratchPointers.data(),
2012 // The single dummy output buffer:
2015 iChannel = 0;
2016 for (; iChannel < nChannels; ++iChannel) {
2017 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
2018 auto discarded = ringBuffer.Unput(discardable);
2019 // assert(discarded == discardable);
2020 }
2021 }
2022 }
2023 }
2024}

References TrackList::Channels(), AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackTracks, AudioIoCallback::mScratchPointers, and size.

Referenced by ProcessPlaybackSlices().

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

246{
247 const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(getPlayDevIndex(play));
248 const PaDeviceInfo *rInfo = Pa_GetDeviceInfo(getRecordDevIndex(rec));
249
250 // Valid iff both defined and the same api.
251 return pInfo != nullptr && rInfo != nullptr && pInfo->hostApi == rInfo->hostApi;
252}

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

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 659 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 658 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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