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 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 WaveTrack &wt)
 
bool TrackHasBeenFadedOut (const WaveTrack &wt)
 
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, WaveTrack *vt)
 
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 StartAudioThreadAndWait ()
 
void StopAudioThreadAndWait ()
 
void StartAudioThread ()
 
void WaitForAudioThreadStarted ()
 
void StopAudioThread ()
 
void WaitForAudioThreadStopped ()
 
void ProcessOnceAndWait (std::chrono::milliseconds sleepTime=std::chrono::milliseconds(50))
 
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
 
WaveTrackArray mCaptureTracks
 
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
 
WaveTrackArray mPlaybackTracks
 
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 407 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 470 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 240 of file AudioIO.cpp.

241{
242 if (!std::atomic<double>{}.is_lock_free()) {
243 // If this check fails, then the atomic<double> members in AudioIO.h
244 // might be changed to atomic<float> to be more efficient with some
245 // loss of precision. That could be conditionally compiled depending
246 // on the platform.
247 wxASSERT(false);
248 }
249
250 // This ASSERT because of casting in the callback
251 // functions where we cast a tempFloats buffer to a (short*) buffer.
252 // We have to ASSERT in the GUI thread, if we are to see it properly.
253 wxASSERT( sizeof( short ) <= sizeof( float ));
254
256 .store(false, std::memory_order_relaxed);
258 .store(false, std::memory_order_relaxed);
260 .store(false, std::memory_order_relaxed);
261
262 mAudioThreadAcknowledge.store(Acknowledge::eNone, std::memory_order_relaxed);
263
264 mPortStreamV19 = NULL;
265
266 mNumPauseFrames = 0;
267
268#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
269 mAILAActive = false;
270#endif
271
272 mLastPaError = paNoError;
273
276 mSilenceLevel = 0.0;
277
278 mOutputMeter.reset();
279
280 PaError err = Pa_Initialize();
281
282 if (err != paNoError) {
283 auto errStr = XO("Could not find any audio devices.\n");
284 errStr += XO("You will not be able to play or record audio.\n\n");
285 wxString paErrStr = LAT1CTOWX(Pa_GetErrorText(err));
286 if (!paErrStr.empty())
287 errStr += XO("Error: %s").Format( paErrStr );
288 // XXX: we are in libaudacity, popping up dialogs not allowed! A
289 // long-term solution will probably involve exceptions
291 errStr,
292 XO("Error Initializing Audio"),
293 wxICON_ERROR|wxOK);
294
295 // Since PortAudio is not initialized, all calls to PortAudio
296 // functions will fail. This will give reasonable behavior, since
297 // the user will be able to do things not relating to audio i/o,
298 // but any attempt to play or record will simply fail.
299 }
300
301#if defined(USE_PORTMIXER)
302 mPortMixer = NULL;
303 mPreviousHWPlaythrough = -1.0;
305#else
306 mInputMixerWorks = false;
307#endif
308
310
312}
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
int PaError
Definition: AudioIO.h:54
DoubleSetting AudioIOPlaybackVolume
#define LAT1CTOWX(X)
Definition: Internat.h:160
#define XO(s)
Definition: Internat.h:31
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:261
PaStream * mPortStreamV19
Definition: AudioIOBase.h:258
void HandleDeviceChange()
update state after changing what audio devices are selected
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:275
std::atomic< Acknowledge > mAudioThreadAcknowledge
Definition: AudioIO.h:314
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopRunning
Definition: AudioIO.h:311
std::atomic< bool > mAudioThreadTrackBufferExchangeLoopActive
Definition: AudioIO.h:312
long mNumPauseFrames
How many frames of zeros were output due to pauses?
Definition: AudioIO.h:247
std::atomic< bool > mAudioThreadShouldCallTrackBufferExchangeOnce
Definition: AudioIO.h:310
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:334
PaError mLastPaError
Definition: AudioIO.h:338
double mLastRecordingOffset
Not (yet) used; should perhaps be atomic when it is.
Definition: AudioIO.h:337
float mSilenceLevel
Definition: AudioIO.h:303
unsigned int mNumCaptureChannels
Definition: AudioIO.h:305
void SetMixerOutputVol(float value)
Definition: AudioIO.h:344
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200

References AudacityMessageBox(), AudioIOPlaybackVolume, 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(), 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 319 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

Member Function Documentation

◆ AddState()

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

Forwards to RealtimeEffectManager::AddState with proper init scope.

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

Definition at line 352 of file AudioIO.cpp.

353{
355 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
356 if (auto pProject = GetOwningProject(); pProject.get() == &project)
357 pInit = &*mpTransportState->mpRealtimeInitialization;
358 return RealtimeEffectManager::Get(project).AddState(pInit, pTrack, id);
359}
std::shared_ptr< AudacityProject > GetOwningProject() const
Definition: AudioIO.h:480
std::unique_ptr< TransportState > mpTransportState
Holds some state for duration of playback or recording.
Definition: AudioIO.h:392
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 1072 of file AudioIO.cpp.

1075{
1076 bool success = false;
1077 auto cleanup = finally([&]{
1078 if (!success) StartStreamCleanup( false );
1079 });
1080
1081 auto &policy = mPlaybackSchedule.GetPolicy();
1082 auto times = policy.SuggestedBufferTimes(mPlaybackSchedule);
1083
1084 //
1085 // The (audio) stream has been opened successfully (assuming we tried
1086 // to open it). We now proceed to
1087 // allocate the memory structures the stream will need.
1088 //
1089
1090 //
1091 // The RingBuffer sizes, and the max amount of the buffer to
1092 // fill at a time, both grow linearly with the number of
1093 // tracks. This allows us to scale up to many tracks without
1094 // killing performance.
1095 //
1096
1097 // real playback time to produce with each filling of the buffers
1098 // by the Audio thread (except at the end of playback):
1099 // usually, make fillings fewer and longer for less CPU usage.
1100 // What Audio thread produces for playback is then consumed by the PortAudio
1101 // thread, in many smaller pieces.
1102 double playbackTime = lrint(times.batchSize.count() * mRate) / mRate;
1103
1104 wxASSERT( playbackTime >= 0 );
1105 mPlaybackSamplesToCopy = playbackTime * mRate;
1106
1107 // Capacity of the playback buffer.
1108 mPlaybackRingBufferSecs = times.ringBufferDelay;
1109
1111 4.5 + 0.5 * std::min(size_t(16), mCaptureTracks.size());
1113 0.2 + 0.2 * std::min(size_t(16), mCaptureTracks.size());
1114
1115 bool bDone;
1116 do
1117 {
1118 bDone = true; // assume success
1119 try
1120 {
1121 if( mNumPlaybackChannels > 0 ) {
1122 // Allocate output buffers. For every output track we allocate
1123 // a ring buffer of ten seconds
1124 auto playbackBufferSize =
1125 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1126
1127 // Always make at least one playback buffer
1129 std::max<size_t>(1, mPlaybackTracks.size()));
1130 // Number of scratch buffers depends on device playback channels
1131 if (mNumPlaybackChannels > 0) {
1133 mScratchPointers.clear();
1134 for (auto &buffer : mScratchBuffers) {
1135 buffer.Allocate(playbackBufferSize, floatSample);
1136 mScratchPointers.push_back(
1137 reinterpret_cast<float*>(buffer.ptr()));
1138 }
1139 }
1140 mPlaybackMixers.clear();
1141
1142 const auto &warpOptions =
1143 policy.MixerWarpOptions(mPlaybackSchedule);
1144
1145 mPlaybackQueueMinimum = lrint( mRate * times.latency.count() );
1147 std::min( mPlaybackQueueMinimum, playbackBufferSize );
1148
1149 if (mPlaybackTracks.empty())
1150 // Make at least one playback buffer
1151 mPlaybackBuffers[0] =
1152 std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1153
1154 for (unsigned int i = 0; i < mPlaybackTracks.size(); i++) {
1155 const auto &pTrack = mPlaybackTracks[i];
1156 // Bug 1763 - We must fade in from zero to avoid a click on starting.
1157 pTrack->SetOldChannelGain(0, 0.0);
1158 pTrack->SetOldChannelGain(1, 0.0);
1159
1160 mPlaybackBuffers[i] =
1161 std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1162
1163 if (pTrack->IsLeader()) {
1164 // use track time for the end time, not real time!
1165 double startTime, endTime;
1166 if (!tracks.prerollTracks.empty())
1167 startTime = mPlaybackSchedule.mT0;
1168 else
1169 startTime = t0;
1170
1172 .contains(pTrack))
1173 // Stop playing this track after pre-roll
1174 endTime = t0;
1175 else
1176 // Pass t1 -- not mT1 as may have been adjusted for latency
1177 // -- so that overdub recording stops playing back samples
1178 // at the right time, though transport may continue to
1179 // record
1180 endTime = t1;
1181
1182 Mixer::Inputs mixTracks;
1183 const auto range =
1184 TrackList::Channels<const SampleTrack>(pTrack.get());
1185 for (auto channel : range)
1186 mixTracks.push_back(
1187 channel->SharedPointer<const SampleTrack>());
1188 mPlaybackMixers.emplace_back(std::make_unique<Mixer>(
1189 move(mixTracks),
1190 // Don't throw for read errors, just play silence:
1191 false,
1192 warpOptions, startTime, endTime, range.size(),
1194 false, // not interleaved
1196 false, // low quality dithering and resampling
1197 nullptr, // no custom mix-down
1198 false // don't apply track gains
1199 ));
1200 }
1201 }
1202
1203 const auto timeQueueSize = 1 +
1204 (playbackBufferSize + TimeQueueGrainSize - 1)
1206 mPlaybackSchedule.mTimeQueue.Resize( timeQueueSize );
1207 }
1208
1209 if( mNumCaptureChannels > 0 )
1210 {
1211 // Allocate input buffers. For every input track we allocate
1212 // a ring buffer of five seconds
1213 auto captureBufferSize =
1214 (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
1215
1216 // In the extraordinarily rare case that we can't even afford
1217 // 100 samples, just give up.
1218 if(captureBufferSize < 100)
1219 {
1220 AudacityMessageBox( XO("Out of memory!") );
1221 return false;
1222 }
1223
1226 mFactor = sampleRate / mRate;
1227
1228 for( unsigned int i = 0; i < mCaptureTracks.size(); i++ )
1229 {
1230 mCaptureBuffers[i] = std::make_unique<RingBuffer>(
1231 mCaptureTracks[i]->GetSampleFormat(), captureBufferSize );
1232 mResample[i] =
1233 std::make_unique<Resample>(true, mFactor, mFactor);
1234 // constant rate resampling
1235 }
1236 }
1237 }
1238 catch(std::bad_alloc&)
1239 {
1240 // Oops! Ran out of memory. This is pretty rare, so we'll just
1241 // try deleting everything, halving our buffer size, and try again.
1242 StartStreamCleanup(true);
1246 mMinCaptureSecsToCopy *= 0.5;
1247 bDone = false;
1248
1249 // In the extraordinarily rare case that we can't even afford 100
1250 // samples, just give up.
1251 auto playbackBufferSize =
1252 (size_t)lrint(mRate * mPlaybackRingBufferSecs.count());
1253 if(playbackBufferSize < 100 || mPlaybackSamplesToCopy < 100)
1254 {
1255 AudacityMessageBox( XO("Out of memory!") );
1256 return false;
1257 }
1258 }
1259 } while(!bDone);
1260
1261 success = true;
1262 return true;
1263}
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
@ floatSample
Definition: SampleFormat.h:34
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:256
void StartStreamCleanup(bool bOnlyBuffers=false)
Clean up after StartStream if it fails.
Definition: AudioIO.cpp:1265
PlaybackSchedule mPlaybackSchedule
Definition: AudioIO.h:390
std::vector< float * > mScratchPointers
pointing into mScratchBuffers
Definition: AudioIO.h:277
size_t mPlaybackSamplesToCopy
Preferred batch size for replenishing the playback RingBuffer.
Definition: AudioIO.h:293
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
Definition: AudioIO.h:279
PlaybackPolicy::Duration mPlaybackRingBufferSecs
Definition: AudioIO.h:289
unsigned int mNumPlaybackChannels
Definition: AudioIO.h:307
WaveTrackArray mPlaybackTracks
Definition: AudioIO.h:274
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
Definition: AudioIO.h:270
WaveTrackArray mCaptureTracks
Definition: AudioIO.h:271
size_t mPlaybackQueueMinimum
Occupancy of the queue we try to maintain, with bigger batches if needed.
Definition: AudioIO.h:295
ArrayOf< std::unique_ptr< Resample > > mResample
Definition: AudioIO.h:269
double mCaptureRingBufferSecs
Definition: AudioIO.h:290
double mFactor
Definition: AudioIO.h:283
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
Definition: AudioIO.h:273
double mMinCaptureSecsToCopy
Definition: AudioIO.h:297
std::vector< SampleBuffer > mScratchBuffers
Definition: AudioIO.h:276
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 PlaybackSchedule::TimeQueue mTimeQueue
PlaybackPolicy & GetPolicy()
WaveTrackConstArray prerollTracks
Definition: AudioIO.h:84

References AudacityMessageBox(), floatSample, PlaybackSchedule::GetPolicy(), lrint, make_iterator_range(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mCaptureTracks, AudioIoCallback::mFactor, min(), AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, 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(), 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 1683 of file AudioIO.cpp.

1684{
1685 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1686 AudioIO *const gAudioIO = AudioIO::Get();
1687 while (!finish.load(std::memory_order_acquire)) {
1688 using Clock = std::chrono::steady_clock;
1689 auto loopPassStart = Clock::now();
1690 auto &schedule = gAudioIO->mPlaybackSchedule;
1691 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1692
1693 // Set LoopActive outside the tests to avoid race condition
1695 .store(true, std::memory_order_relaxed);
1697 .load(std::memory_order_acquire) )
1698 {
1699 gAudioIO->TrackBufferExchange();
1701 .store(false, std::memory_order_release);
1702
1703 lastState = State::eOnce;
1704 }
1706 .load(std::memory_order_relaxed))
1707 {
1708 if (lastState != State::eLoopRunning)
1709 {
1710 // Main thread has told us to start - acknowledge that we do
1712 std::memory_order::memory_order_release);
1713 }
1714 lastState = State::eLoopRunning;
1715
1716 // We call the processing after raising the acknowledge flag, because the main thread
1717 // only needs to know that the message was seen.
1718 //
1719 // This is unlike the case with mAudioThreadShouldCallTrackBufferExchangeOnce where the
1720 // store really means that the one-time exchange was done.
1721
1722 gAudioIO->TrackBufferExchange();
1723 }
1724 else
1725 {
1726 if ( (lastState == State::eLoopRunning)
1727 || (lastState == State::eMonitoring ) )
1728 {
1729 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1730 // acknowledge that we received the order and that no more processing will be done.
1732 std::memory_order::memory_order_release);
1733 }
1734 lastState = State::eDoNothing;
1735
1736 if (gAudioIO->IsMonitoring())
1737 {
1738 lastState = State::eMonitoring;
1739 }
1740 }
1741
1743 .store(false, std::memory_order_relaxed);
1744
1745 std::this_thread::sleep_until( loopPassStart + interval );
1746 }
1747}
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:410
void TrackBufferExchange()
Definition: AudioIO.cpp:1791
static AudioIO * Get()
Definition: AudioIO.cpp:133
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 1044 of file AudioIO.cpp.

1045{
1046 if (!action)
1047 return;
1048
1049 {
1050 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1052 // Enqueue it, even if perhaps not still recording,
1053 // but it wasn't cleared yet
1055 prevAction = std::move(mPostRecordingAction),
1056 nextAction = std::move(action)
1057 ]{ prevAction(); nextAction(); };
1058 return;
1059 }
1060 else if (DelayingActions()) {
1061 mPostRecordingAction = std::move(action);
1062 return;
1063 }
1064 }
1065
1066 // Don't delay it except until idle time.
1067 // (Recording might start between now and then, but won't go far before
1068 // the action is done. So the system isn't bulletproof yet.)
1069 wxTheApp->CallAfter(std::move(action));
1070}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:639
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:640
bool DelayingActions() const
Definition: AudioIO.cpp:1039

References DelayingActions(), mPostRecordingAction, and mPostRecordingActionMutex.

Here is the call graph for this function:

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 226 of file AudioIO.cpp.

227{
228 ugAudioIO.reset();
229}
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:242

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

1035{
1036 mDelayingActions = recording;
1037}
bool mDelayingActions
Definition: AudioIO.h:642

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1039 of file AudioIO.cpp.

1040{
1042}

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

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

1798{
1799 std::optional<RealtimeEffects::ProcessingScope> pScope;
1800 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
1801 pScope.emplace(
1802 *mpTransportState->mpRealtimeInitialization, mOwningProject);
1803
1804 if (mNumPlaybackChannels == 0)
1805 return;
1806
1807 // It is possible that some buffers will have more samples available than
1808 // others. This could happen if we hit this code during the PortAudio
1809 // callback. Also, if in a previous pass, unequal numbers of samples were
1810 // discarded from ring buffers for differing latencies.
1811
1812 // To keep things simple, we write no more data than is vacant in
1813 // ALL buffers, and advance the global time by that much.
1814 auto nAvailable = GetCommonlyFreePlayback();
1815
1816 // Don't fill the buffers at all unless we can do
1817 // at least mPlaybackSamplesToCopy. This improves performance
1818 // by not always trying to process tiny chunks, eating the
1819 // CPU unnecessarily.
1820 if (nAvailable < mPlaybackSamplesToCopy)
1821 return;
1822
1823 // More than mPlaybackSamplesToCopy might be copied:
1824 // May produce a larger amount when initially priming the buffer, or
1825 // perhaps again later in play to avoid underfilling the queue and
1826 // falling behind the real-time demand on the consumer side in the
1827 // callback.
1828 auto GetNeeded = [&]() -> size_t {
1829 // Note that reader might concurrently consume between loop passes below
1830 // So this might not be nondecreasing
1831 auto nReady = GetCommonlyWrittenForPlayback();
1833 };
1834 auto nNeeded = GetNeeded();
1835
1836 // wxASSERT( nNeeded <= nAvailable );
1837
1838 auto Flush = [&]{
1839 /* The flushing of all the Puts to the RingBuffers is lifted out of the
1840 do-loop in ProcessPlaybackSlices, and also after transformation of the
1841 stream for realtime effects.
1842
1843 It's only here that a release is done on the atomic variable that
1844 indicates the readiness of sample data to the consumer. That atomic
1845 also synchronizes the use of the TimeQueue.
1846 */
1847 for (size_t i = 0; i < std::max(size_t{1}, mPlaybackTracks.size()); ++i)
1848 mPlaybackBuffers[i]->Flush();
1849 };
1850
1851 while (true) {
1852 // Limit maximum buffer size (increases performance)
1853 auto available = std::min( nAvailable,
1854 std::max( nNeeded, mPlaybackSamplesToCopy ) );
1855
1856 // After each loop pass or after break
1857 Finally Do{ Flush };
1858
1859 if (!ProcessPlaybackSlices(pScope, available))
1860 // We are not making progress. May fail to satisfy the minimum but
1861 // won't loop forever
1862 break;
1863
1864 // Loop again to satisfy the minimum queue requirement in case there
1865 // was discarding of processed data for effect latencies
1866 nNeeded = GetNeeded();
1867 if (nNeeded == 0)
1868 break;
1869
1870 // Might increase because the reader consumed some
1871 nAvailable = GetCommonlyFreePlayback();
1872 }
1873}
bool ProcessPlaybackSlices(std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
Definition: AudioIO.cpp:1875
size_t GetCommonlyFreePlayback()
Get the number of audio samples free in all of the playback buffers.
Definition: AudioIO.cpp:1750
size_t GetCommonlyWrittenForPlayback()
Definition: AudioIO.cpp:1770
"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 133 of file AudioIO.cpp.

134{
135 return static_cast< AudioIO* >( AudioIOBase::Get() );
136}
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(), LabelEditActions::Handler::OnAddLabelPlaying(), ProjectAudioManager::OnAudioIOStopRecording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::OnChangeButtonClicked(), ProjectManager::OnCloseWindow(), SelectActions::Handler::OnCursorPositionStore(), PluginActions::Handler::OnDetectUpstreamDropouts(), SelectionBar::OnIdle(), TimeToolBar::OnIdle(), AudacityApp::OnKeyDown(), MeterPanel::OnMeterUpdate(), ProjectAudioManager::OnPause(), SelectActions::Handler::OnSelectCursorStoredCursor(), SelectUtilities::OnSetRegion(), PluginActions::Handler::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 1587 of file AudioIO.cpp.

1588{
1589 // Check if we can use the cached value
1590 if (mCachedBestRateIn != 0.0 && mCachedBestRateIn == sampleRate
1591 && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
1592 return mCachedBestRateOut;
1593 }
1594
1595 // In order to cache the value, all early returns should instead set retval
1596 // and jump to finished
1597 double retval;
1598
1599 std::vector<long> rates;
1600 if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
1601 if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
1602 wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
1603
1604 if (capturing && !playing) {
1605 rates = GetSupportedCaptureRates(-1, sampleRate);
1606 }
1607 else if (playing && !capturing) {
1608 rates = GetSupportedPlaybackRates(-1, sampleRate);
1609 }
1610 else { // we assume capturing and playing - the alternative would be a
1611 // bit odd
1612 rates = GetSupportedSampleRates(-1, -1, sampleRate);
1613 }
1614 /* rem rates is the array of hardware-supported sample rates (in the current
1615 * configuration), sampleRate is the Project Rate (desired sample rate) */
1616 long rate = (long)sampleRate;
1617
1618 if (make_iterator_range(rates).contains(rate)) {
1619 wxLogDebug(wxT("GetBestRate() Returning %.0ld Hz"), rate);
1620 retval = rate;
1621 goto finished;
1622 /* the easy case - the suggested rate (project rate) is in the list, and
1623 * we can just accept that and send back to the caller. This should be
1624 * the case for most users most of the time (all of the time on
1625 * Win MME as the OS does resampling) */
1626 }
1627
1628 /* if we get here, there is a problem - the project rate isn't supported
1629 * on our hardware, so we can't us it. Need to come up with an alternative
1630 * rate to use. The process goes like this:
1631 * * If there are no rates to pick from, we're stuck and return 0 (error)
1632 * * If there are some rates, we pick the next one higher than the requested
1633 * rate to use.
1634 * * If there aren't any higher, we use the highest available rate */
1635
1636 if (rates.empty()) {
1637 /* we're stuck - there are no supported rates with this hardware. Error */
1638 wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
1639 retval = 0.0;
1640 goto finished;
1641 }
1642 int i;
1643 for (i = 0; i < (int)rates.size(); i++) // for each supported rate
1644 {
1645 if (rates[i] > rate) {
1646 // supported rate is greater than requested rate
1647 wxLogDebug(wxT("GetBestRate() Returning next higher rate - %.0ld Hz"), rates[i]);
1648 retval = rates[i];
1649 goto finished;
1650 }
1651 }
1652
1653 wxLogDebug(wxT("GetBestRate() Returning highest rate - %.0ld Hz"), rates.back());
1654 retval = rates.back(); // the highest available rate
1655 goto finished;
1656
1657finished:
1658 mCachedBestRateIn = sampleRate;
1659 mCachedBestRateOut = retval;
1660 mCachedBestRatePlaying = playing;
1661 mCachedBestRateCapturing = capturing;
1662 return retval;
1663}
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:283
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:355
static bool mCachedBestRatePlaying
Definition: AudioIO.h:356
static bool mCachedBestRateCapturing
Definition: AudioIO.h:357

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

510{ return mCaptureFormat; }
sampleFormat mCaptureFormat
Definition: AudioIO.h:308

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

1780{
1781 auto commonlyAvail = mCaptureBuffers[0]->AvailForGet();
1782 for (unsigned i = 1; i < mCaptureTracks.size(); ++i)
1783 commonlyAvail = std::min(commonlyAvail,
1784 mCaptureBuffers[i]->AvailForGet());
1785 return commonlyAvail;
1786}

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

1751{
1752 auto commonlyAvail = mPlaybackBuffers[0]->AvailForPut();
1753 for (unsigned i = 1; i < mPlaybackTracks.size(); ++i)
1754 commonlyAvail = std::min(commonlyAvail,
1755 mPlaybackBuffers[i]->AvailForPut());
1756 // MB: subtract a few samples because the code in TrackBufferExchange has rounding
1757 // errors
1758 return commonlyAvail - std::min(size_t(10), commonlyAvail);
1759}

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

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

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 479 of file AudioIO.h.

479{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 403 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

unsigned AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 512 of file AudioIO.h.

512{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

unsigned AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 511 of file AudioIO.h.

511{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 480 of file AudioIO.h.

481 { 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 1665 of file AudioIO.cpp.

1666{
1667 // Track time readout for the main thread
1668
1669 if( !IsStreamActive() )
1670 return BAD_STREAM_TIME;
1671
1673}
#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 198 of file AudioIO.cpp.

199{
200 auto pAudioIO = safenew AudioIO();
201 ugAudioIO.reset(pAudioIO);
202 pAudioIO->StartThread();
203
204 // Make sure device prefs are initialized
205 if (gPrefs->Read(wxT("AudioIO/RecordingDevice"), wxT("")).empty()) {
206 int i = getRecordDevIndex();
207 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
208 if (info) {
211 }
212 }
213
214 if (gPrefs->Read(wxT("AudioIO/PlaybackDevice"), wxT("")).empty()) {
215 int i = getPlayDevIndex();
216 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
217 if (info) {
220 }
221 }
222
223 gPrefs->Flush();
224}
StringSetting AudioIOPlaybackDevice
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:71
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:240
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 430 of file AudioIO.cpp.

431{
432 return mInputMixerWorks;
433}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1288 of file AudioIO.cpp.

1289{
1290 auto pOwningProject = mOwningProject.lock();
1291 return !pOwningProject || pOwningProject.get() == &project;
1292}

References AudioIOBase::mOwningProject.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3272 of file AudioIO.cpp.

3273{
3274 // Includes a test of mTime, used in the main thread
3275 return IsStreamActive() &&
3276 GetNumCaptureChannels() > 0 &&
3279}
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:512

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

674{
675 return wxString::Format(wxT("%d %s."), (int) mLastPaError, Pa_GetErrorText(mLastPaError));
676}

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

1877{
1878 auto &policy = mPlaybackSchedule.GetPolicy();
1879
1880 // msmeyer: When playing a very short selection in looped
1881 // mode, the selection must be copied to the buffer multiple
1882 // times, to ensure, that the buffer has a reasonable size
1883 // This is the purpose of this loop.
1884 // PRL: or, when scrubbing, we may get work repeatedly from the
1885 // user interface.
1886 bool done = false;
1887 bool progress = false;
1888 do {
1889 const auto slice =
1890 policy.GetPlaybackSlice(mPlaybackSchedule, available);
1891 const auto &[frames, toProduce] = slice;
1892 progress = progress || toProduce > 0;
1893
1894 // Update the time queue. This must be done before writing to the
1895 // ring buffers of samples, for proper synchronization with the
1896 // consumer side in the PortAudio thread, which reads the time
1897 // queue after reading the sample queues. The sample queues use
1898 // atomic variables, the time queue doesn't.
1900
1901 size_t i = 0;
1902 for (auto &mixer : mPlaybackMixers) {
1903 // The mixer here isn't actually mixing: it's just doing
1904 // resampling, format conversion, and possibly time track
1905 // warping
1906 if (frames > 0) {
1907 size_t produced = 0;
1908 if ( toProduce )
1909 produced = mixer->Process( toProduce );
1910 //wxASSERT(produced <= toProduce);
1911 for(size_t j = 0, nChannels =
1913 j < nChannels; ++i, ++j
1914 ) {
1915 auto warpedSamples = mixer->GetBuffer(j);
1916 const auto put = mPlaybackBuffers[i]->Put(
1917 warpedSamples, floatSample, produced, frames - produced);
1918 // wxASSERT(put == frames);
1919 // but we can't assert in this thread
1920 wxUnusedVar(put);
1921 }
1922 }
1923 }
1924
1925 if (mPlaybackTracks.empty())
1926 // Produce silence in the single ring buffer
1927 mPlaybackBuffers[0]->Put(nullptr, floatSample, 0, frames);
1928
1929 available -= frames;
1930 // wxASSERT(available >= 0); // don't assert on this thread
1931
1932 done = policy.RepositionPlayback( mPlaybackSchedule, mPlaybackMixers,
1933 frames, available );
1934 } while (available && !done);
1935
1936 // Do any realtime effect processing, more efficiently in at most
1937 // two buffers per track, after all the little slices have been written.
1938 TransformPlayBuffers(pScope);
1939 return progress;
1940}
void TransformPlayBuffers(std::optional< RealtimeEffects::ProcessingScope > &scope)
Definition: AudioIO.cpp:1943
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 373 of file AudioIO.cpp.

375{
377 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
378 if (auto pProject = GetOwningProject(); pProject.get() == &project)
379 pInit = &*mpTransportState->mpRealtimeInitialization;
380 RealtimeEffectManager::Get(project).RemoveState(pInit, pTrack, pState);
381}
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 362 of file AudioIO.cpp.

364{
366 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
367 if (auto pProject = GetOwningProject(); pProject.get() == &project)
368 pInit = &*mpTransportState->mpRealtimeInitialization;
369 return RealtimeEffectManager::Get(project)
370 .ReplaceState(pInit, pTrack, index, id);
371}
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 689 of file AudioIO.cpp.

690{
691 mOwningProject.reset();
692}

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

468{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:288

◆ SetMeters()

void AudioIO::SetMeters ( )
private

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

Definition at line 1294 of file AudioIO.cpp.

1295{
1296 if (auto pInputMeter = mInputMeter.lock())
1297 pInputMeter->Reset(mRate, true);
1298 if (auto pOutputMeter = mOutputMeter.lock())
1299 pOutputMeter->Reset(mRate, true);
1300}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:260

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

385{
386 SetMixerOutputVol(playbackVolume);
387 AudioIOPlaybackVolume.Write(playbackVolume);
388
389#if defined(USE_PORTMIXER)
390 PxMixer *mixer = mPortMixer;
391 if( !mixer )
392 return;
393
394 float oldRecordVolume = Px_GetInputVolume(mixer);
395
396 AudioIoCallback::SetMixer(inputSource);
397 if( oldRecordVolume != recordVolume )
398 Px_SetInputVolume(mixer, recordVolume);
399
400#endif
401}
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 678 of file AudioIO.cpp.

680{
681 if ( !mOwningProject.expired() ) {
682 wxASSERT(false);
684 }
685
686 mOwningProject = pProject;
687}

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

1573{
1574 if (state != IsPaused())
1575 {
1576 if (auto pOwningProject = mOwningProject.lock()) {
1577 // The realtime effects manager may remain "active" but becomes
1578 // "suspended" or "resumed".
1579 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1580 em.SetSuspended(state);
1581 }
1582 }
1583
1584 mPaused.store(state, std::memory_order_relaxed);
1585}
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:249
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 694 of file AudioIO.cpp.

695{
697 return;
698
699 bool success;
700 auto captureFormat = QualitySettings::SampleFormatChoice();
701 auto captureChannels = AudioIORecordChannels.Read();
702 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
703 int playbackChannels = 0;
704
706 playbackChannels = 2;
707
708 // FIXME: TRAP_ERR StartPortAudioStream (a PaError may be present)
709 // but StartPortAudioStream function only returns true or false.
710 mUsingAlsa = false;
711 success = StartPortAudioStream(options, (unsigned int)playbackChannels,
712 (unsigned int)captureChannels,
713 captureFormat);
714
715 auto pOwningProject = mOwningProject.lock();
716 if (!success) {
717 using namespace BasicUI;
718 auto msg = XO("Error opening recording device.\nError code: %s")
719 .Format( Get()->LastPaErrorString() );
720 ShowErrorDialog( *ProjectFramePlacement( pOwningProject.get() ),
721 XO("Error"), msg, wxT("Error_opening_sound_device"),
722 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
723 return;
724 }
725
726 Publish({ pOwningProject.get(), AudioIOEvent::MONITOR, true });
727
728 // FIXME: TRAP_ERR PaErrorCode 'noted' but not reported in StartMonitoring.
729 // Now start the PortAudio stream!
730 // TODO: ? Factor out and reuse error reporting code from end of
731 // AudioIO::StartStream?
732 mLastPaError = Pa_StartStream( mPortStreamV19 );
733
734 // Update UI display only now, after all possibilities for error are past.
735 auto pListener = GetListener();
736 if ((mLastPaError == paNoError) && pListener) {
737 // advertise the chosen I/O sample rate to the UI
738 pListener->OnAudioIORate((int)mRate);
739 }
740}
IntSetting AudioIORecordChannels
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
int mStreamToken
Definition: AudioIOBase.h:252
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:476
wxString LastPaErrorString()
Definition: AudioIO.cpp:673
bool mSoftwarePlaythrough
Definition: AudioIO.h:299
bool mUsingAlsa
Definition: AudioIO.h:352
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 476 of file AudioIO.cpp.

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

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

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

References AllocateBuffers(), AudacityMessageBox(), 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(), AudioIoCallback::StartAudioThread(), StartPortAudioStream(), StartStreamCleanup(), AudioIoCallback::StopAudioThread(), StopStream(), Verbatim(), 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 1265 of file AudioIO.cpp.

1266{
1267 mpTransportState.reset();
1268
1269 mPlaybackBuffers.reset();
1270 mScratchBuffers.clear();
1271 mScratchPointers.clear();
1272 mPlaybackMixers.clear();
1273 mCaptureBuffers.reset();
1274 mResample.reset();
1276
1277 if(!bOnlyBuffers)
1278 {
1279 Pa_AbortStream( mPortStreamV19 );
1280 Pa_CloseStream( mPortStreamV19 );
1281 mPortStreamV19 = NULL;
1282 mStreamToken = 0;
1283 }
1284
1286}
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 314 of file AudioIO.cpp.

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

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

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

References AudioIOLatencyDuration, AudioIOEvent::CAPTURE, PlaybackSchedule::TimeQueue::Clear(), AudioIoCallback::ClearRecordingException(), DelayActions(), AudioIoCallback::Extensions(), PlaybackPolicy::Finalize(), WaveTrack::Flush(), 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 1791 of file AudioIO.cpp.

1792{
1795}
void DrainRecordBuffers()
Second part of TrackBufferExchange.
Definition: AudioIO.cpp:2002
void FillPlayBuffers()
First part of TrackBufferExchange.
Definition: AudioIO.cpp:1797

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

1945{
1946 // Transform written but un-flushed samples in the RingBuffers in-place.
1947
1948 // Avoiding std::vector
1949 auto pointers =
1950 static_cast<float**>(alloca(mNumPlaybackChannels * sizeof(float*)));
1951
1952 const auto numPlaybackTracks = mPlaybackTracks.size();
1953 for (unsigned t = 0; t < numPlaybackTracks; ++t) {
1954 const auto vt = mPlaybackTracks[t].get();
1955 if (!(vt && vt->IsLeader()))
1956 continue;
1957 // vt is mono, or is the first of its group of channels
1958 const auto nChannels = std::min<size_t>(
1960
1961 // Loop over the blocks of unflushed data, at most two
1962 for (unsigned iBlock : {0, 1}) {
1963 size_t len = 0;
1964 size_t iChannel = 0;
1965 for (; iChannel < nChannels; ++iChannel) {
1966 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
1967 const auto pair =
1968 ringBuffer.GetUnflushed(iBlock);
1969 // Playback RingBuffers have float format: see AllocateBuffers
1970 pointers[iChannel] = reinterpret_cast<float*>(pair.first);
1971 // The lengths of corresponding unflushed blocks should be
1972 // the same for all channels
1973 if (len == 0)
1974 len = pair.second;
1975 else
1976 assert(len == pair.second);
1977 }
1978
1979 // Are there more output device channels than channels of vt?
1980 // Such as when a mono track is processed for stereo play?
1981 // Then supply some non-null fake input buffers, because the
1982 // various ProcessBlock overrides of effects may crash without it.
1983 // But it would be good to find the fixes to make this unnecessary.
1984 float **scratch = &mScratchPointers[mNumPlaybackChannels];
1985 while (iChannel < mNumPlaybackChannels)
1986 memset((pointers[iChannel++] = *scratch++), 0, len * sizeof(float));
1987
1988 if (len && pScope) {
1989 auto discardable = pScope->Process( *vt, &pointers[0],
1991 iChannel = 0;
1992 for (; iChannel < nChannels; ++iChannel) {
1993 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
1994 auto discarded = ringBuffer.Unput(discardable);
1995 // assert(discarded == discardable);
1996 }
1997 }
1998 }
1999 }
2000}

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

232{
233 const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(getPlayDevIndex(play));
234 const PaDeviceInfo *rInfo = Pa_GetDeviceInfo(getRecordDevIndex(rec));
235
236 // Valid iff both defined and the same api.
237 return pInfo != nullptr && rInfo != nullptr && pInfo->hostApi == rInfo->hostApi;
238}

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

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 640 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 639 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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