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

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

#include <AudioIO.h>

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

Public Types

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

Public Member Functions

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

Static Public Member Functions

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

Private Member Functions

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

Private Attributes

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

Additional Inherited Members

- Public Attributes inherited from AudioIoCallback
int mbHasSoloTracks
 
int mCallbackReturn
 
long mNumPauseFrames
 How many frames of zeros were output due to pauses? More...
 
std::thread mAudioThread
 
std::atomic< bool > mFinishAudioThread { false }
 
ArrayOf< std::unique_ptr< Resample > > mResample
 
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
 
WritableSampleTrackArray mCaptureTracks
 
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
 
SampleTrackConstArray mPlaybackTracks
 
std::vector< OldChannelGainsmOldChannelGains
 
std::vector< SampleBuffermScratchBuffers
 
std::vector< float * > mScratchPointers
 pointing into mScratchBuffers More...
 
std::vector< std::unique_ptr< Mixer > > mPlaybackMixers
 
std::atomic< float > mMixerOutputVol { 1.0 }
 
double mFactor
 
unsigned long mMaxFramesOutput
 
bool mbMicroFades
 
double mSeek
 
PlaybackPolicy::Duration mPlaybackRingBufferSecs
 
double mCaptureRingBufferSecs
 
size_t mPlaybackSamplesToCopy
 Preferred batch size for replenishing the playback RingBuffer. More...
 
size_t mHardwarePlaybackLatencyFrames {}
 Hardware output latency in frames. More...
 
size_t mPlaybackQueueMinimum
 Occupancy of the queue we try to maintain, with bigger batches if needed. More...
 
double mMinCaptureSecsToCopy
 
bool mSoftwarePlaythrough
 
bool mPauseRec
 True if Sound Activated Recording is enabled. More...
 
float mSilenceLevel
 
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 }
 
bool mUsingJack { false }
 
wxMutex mSuspendAudioThread
 
wxAtomicInt mRecordingException {}
 
std::vector< std::pair< double, double > > mLostCaptureIntervals
 
bool mDetectDropouts { true }
 
RecordingSchedule mRecordingSchedule {}
 
PlaybackSchedule mPlaybackSchedule
 
std::unique_ptr< TransportStatempTransportState
 Holds some state for duration of playback or recording. More...
 
- Protected Attributes inherited from AudioIOBase
std::weak_ptr< AudacityProjectmOwningProject
 
std::atomic< bool > mPaused { false }
 True if audio playback is paused. More...
 
int mStreamToken { 0 }
 
double mRate
 Audio playback rate in samples per second. More...
 
PaStreammPortStreamV19
 
std::weak_ptr< MetermInputMeter {}
 
std::weak_ptr< MetermOutputMeter {}
 
bool mInputMixerWorks
 Can we control the hardware input level? More...
 
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
 
- Static Protected Attributes inherited from AudioIoCallback
static double mCachedBestRateOut
 
static bool mCachedBestRatePlaying
 
static bool mCachedBestRateCapturing
 
- Static Protected Attributes inherited from AudioIOBase
static std::unique_ptr< AudioIOBaseugAudioIO
 
static int mCachedPlaybackIndex = -1
 
static std::vector< long > mCachedPlaybackRates
 
static int mCachedCaptureIndex = -1
 
static std::vector< long > mCachedCaptureRates
 
static std::vector< long > mCachedSampleRates
 
static double mCachedBestRateIn = 0.0
 
static const int RatesToTry []
 Array of audio sample rates to try to use. More...
 
static const int NumRatesToTry = WXSIZEOF(AudioIOBase::RatesToTry)
 How many sample rates to try. More...
 

Detailed Description

AudioIO uses the PortAudio library to play and record sound.

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

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

Definition at line 423 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 486 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 233 of file AudioIO.cpp.

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

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

Referenced by Init().

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

◆ ~AudioIO()

AudioIO::~AudioIO ( )
private

Definition at line 315 of file AudioIO.cpp.

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

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

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

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

References floatSample, PlaybackSchedule::GetPolicy(), lrint, make_iterator_range(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mCaptureTracks, AudioIoCallback::mFactor, AudioIoCallback::mHardwarePlaybackLatencyFrames, min(), AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mOldChannelGains, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackRingBufferSecs, AudioIoCallback::mPlaybackSamplesToCopy, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mRate, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, PlaybackSchedule::mT0, PlaybackSchedule::mTimeQueue, TransportTracks::prerollTracks, ArrayOf< X >::reinit(), PlaybackSchedule::TimeQueue::Resize(), BasicUI::ShowMessageBox(), StartStreamCleanup(), PlaybackPolicy::SuggestedBufferTimes(), TimeQueueGrainSize, and XO().

Referenced by StartStream().

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

◆ AudioThread()

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

Sits in a thread loop reading and writing audio.

Definition at line 1734 of file AudioIO.cpp.

1735{
1736 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1737 AudioIO *const gAudioIO = AudioIO::Get();
1738 while (!finish.load(std::memory_order_acquire)) {
1739 using Clock = std::chrono::steady_clock;
1740 auto loopPassStart = Clock::now();
1741 auto &schedule = gAudioIO->mPlaybackSchedule;
1742 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1743
1744 // Set LoopActive outside the tests to avoid race condition
1746 .store(true, std::memory_order_relaxed);
1748 .load(std::memory_order_acquire) )
1749 {
1750 gAudioIO->TrackBufferExchange();
1752 .store(false, std::memory_order_release);
1753
1754 lastState = State::eOnce;
1755 }
1757 .load(std::memory_order_relaxed))
1758 {
1759 if (lastState != State::eLoopRunning)
1760 {
1761 // Main thread has told us to start - acknowledge that we do
1763 std::memory_order::memory_order_release);
1764 }
1765 lastState = State::eLoopRunning;
1766
1767 // We call the processing after raising the acknowledge flag, because the main thread
1768 // only needs to know that the message was seen.
1769 //
1770 // This is unlike the case with mAudioThreadShouldCallTrackBufferExchangeOnce where the
1771 // store really means that the one-time exchange was done.
1772
1773 gAudioIO->TrackBufferExchange();
1774 }
1775 else
1776 {
1777 if ( (lastState == State::eLoopRunning)
1778 || (lastState == State::eMonitoring ) )
1779 {
1780 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1781 // acknowledge that we received the order and that no more processing will be done.
1783 std::memory_order::memory_order_release);
1784 }
1785 lastState = State::eDoNothing;
1786
1787 if (gAudioIO->IsMonitoring())
1788 {
1789 lastState = State::eMonitoring;
1790 }
1791 }
1792
1794 .store(false, std::memory_order_relaxed);
1795
1796 std::this_thread::sleep_until( loopPassStart + interval );
1797 }
1798}
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:426
void TrackBufferExchange()
Definition: AudioIO.cpp:1842
static AudioIO * Get()
Definition: AudioIO.cpp:126
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 1079 of file AudioIO.cpp.

1080{
1081 if (!action)
1082 return;
1083
1084 {
1085 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1087 // Enqueue it, even if perhaps not still recording,
1088 // but it wasn't cleared yet
1090 prevAction = std::move(mPostRecordingAction),
1091 nextAction = std::move(action)
1092 ]{ prevAction(); nextAction(); };
1093 return;
1094 }
1095 else if (DelayingActions()) {
1096 mPostRecordingAction = std::move(action);
1097 return;
1098 }
1099 }
1100
1101 // Don't delay it except until idle time.
1102 // (Recording might start between now and then, but won't go far before
1103 // the action is done. So the system isn't bulletproof yet.)
1104 BasicUI::CallAfter(move(action));
1105}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:655
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:656
bool DelayingActions() const
Definition: AudioIO.cpp:1074
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208

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

Here is the call graph for this function:

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 219 of file AudioIO.cpp.

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

References AudioIOBase::ugAudioIO.

Referenced by AudacityApp::OnExit().

Here is the caller graph for this function:

◆ DelayActions()

void AudioIO::DelayActions ( bool  recording)

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

Definition at line 1069 of file AudioIO.cpp.

1070{
1071 mDelayingActions = recording;
1072}
bool mDelayingActions
Definition: AudioIO.h:658

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1074 of file AudioIO.cpp.

1075{
1077}

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

2057{
2058 if (mRecordingException || mCaptureTracks.empty())
2059 return;
2060
2061 auto delayedHandler = [this] ( AudacityException * pException ) {
2062 // In the main thread, stop recording
2063 // This is one place where the application handles disk
2064 // exhaustion exceptions from wave track operations, without rolling
2065 // back to the last pushed undo state. Instead, partial recording
2066 // results are pushed as a NEW undo state. For this reason, as
2067 // commented elsewhere, we want an exception safety guarantee for
2068 // the output wave tracks, after the failed append operation, that
2069 // the tracks remain as they were after the previous successful
2070 // (block-level) appends.
2071
2072 // Note that the Flush in StopStream() may throw another exception,
2073 // but StopStream() contains that exception, and the logic in
2074 // AudacityException::DelayedHandlerAction prevents redundant message
2075 // boxes.
2076 StopStream();
2077 DefaultDelayedHandlerAction( pException );
2078 };
2079
2080 GuardedCall( [&] {
2081 // start record buffering
2082 const auto avail = GetCommonlyAvailCapture(); // samples
2083 const auto remainingTime =
2084 std::max(0.0, mRecordingSchedule.ToConsume());
2085 // This may be a very big double number:
2086 const auto remainingSamples = remainingTime * mRate;
2087 bool latencyCorrected = true;
2088
2089 double deltat = avail / mRate;
2090
2092 .load(std::memory_order_relaxed) ||
2093 deltat >= mMinCaptureSecsToCopy)
2094 {
2095 bool newBlocks = false;
2096
2097 // Append captured samples to the end of the WaveTracks.
2098 // The WaveTracks have their own buffering for efficiency.
2099 auto numChannels = mCaptureTracks.size();
2100
2101 for( size_t i = 0; i < numChannels; i++ )
2102 {
2103 sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
2104
2105 size_t discarded = 0;
2106
2108 const auto correction = mRecordingSchedule.TotalCorrection();
2109 if (correction >= 0) {
2110 // Rightward shift
2111 // Once only (per track per recording), insert some initial
2112 // silence.
2113 size_t size = floor( correction * mRate * mFactor);
2114 SampleBuffer temp(size, trackFormat);
2115 ClearSamples(temp.ptr(), trackFormat, 0, size);
2116 mCaptureTracks[i]->Append(temp.ptr(), trackFormat, size, 1,
2117 // Do not dither recordings
2119 }
2120 else {
2121 // Leftward shift
2122 // discard some samples from the ring buffers.
2123 size_t size = floor(
2125
2126 // The ring buffer might have grown concurrently -- don't discard more
2127 // than the "avail" value noted above.
2128 discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2129
2130 if (discarded < size)
2131 // We need to visit this again to complete the
2132 // discarding.
2133 latencyCorrected = false;
2134 }
2135 }
2136
2137 const float *pCrossfadeSrc = nullptr;
2138 size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2139 if (i < mRecordingSchedule.mCrossfadeData.size())
2140 {
2141 // Do crossfading
2142 // The supplied crossfade samples are at the same rate as the track
2143 const auto &data = mRecordingSchedule.mCrossfadeData[i];
2144 totalCrossfadeLength = data.size();
2145 if (totalCrossfadeLength) {
2146 crossfadeStart =
2147 floor(mRecordingSchedule.Consumed() * mCaptureTracks[i]->GetRate());
2148 if (crossfadeStart < totalCrossfadeLength)
2149 pCrossfadeSrc = data.data() + crossfadeStart;
2150 }
2151 }
2152
2153 wxASSERT(discarded <= avail);
2154 size_t toGet = avail - discarded;
2155 SampleBuffer temp;
2156 size_t size;
2158 if( mFactor == 1.0 )
2159 {
2160 // Take captured samples directly
2161 size = toGet;
2162 if (pCrossfadeSrc)
2163 // Change to float for crossfade calculation
2165 else
2166 format = trackFormat;
2167 temp.Allocate(size, format);
2168 const auto got =
2169 mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2170 // wxASSERT(got == toGet);
2171 // but we can't assert in this thread
2172 wxUnusedVar(got);
2173 if (double(size) > remainingSamples)
2174 size = floor(remainingSamples);
2175 }
2176 else
2177 {
2178 size = lrint(toGet * mFactor);
2180 SampleBuffer temp1(toGet, floatSample);
2181 temp.Allocate(size, format);
2182 const auto got =
2183 mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
2184 // wxASSERT(got == toGet);
2185 // but we can't assert in this thread
2186 wxUnusedVar(got);
2187 /* we are re-sampling on the fly. The last resampling call
2188 * must flush any samples left in the rate conversion buffer
2189 * so that they get recorded
2190 */
2191 if (toGet > 0 ) {
2192 if (double(toGet) > remainingSamples)
2193 toGet = floor(remainingSamples);
2194 const auto results =
2195 mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
2196 !IsStreamActive(), (float *)temp.ptr(), size);
2197 size = results.second;
2198 }
2199 }
2200
2201 if (pCrossfadeSrc) {
2202 wxASSERT(format == floatSample);
2203 size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
2204 if (crossfadeLength) {
2205 auto ratio = double(crossfadeStart) / totalCrossfadeLength;
2206 auto ratioStep = 1.0 / totalCrossfadeLength;
2207 auto pCrossfadeDst = (float*)temp.ptr();
2208
2209 // Crossfade loop here
2210 for (size_t ii = 0; ii < crossfadeLength; ++ii) {
2211 *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
2212 ++pCrossfadeSrc, ++pCrossfadeDst;
2213 ratio += ratioStep;
2214 }
2215 }
2216 }
2217
2218 // Now append
2219 // see comment in second handler about guarantee
2220 newBlocks = mCaptureTracks[i]->Append(
2221 temp.ptr(), format, size, 1,
2222 // Do not dither recordings
2224 ) || newBlocks;
2225 } // end loop over capture channels
2226
2227 // Now update the recording schedule position
2229 mRecordingSchedule.mLatencyCorrected = latencyCorrected;
2230
2231 auto pListener = GetListener();
2232 if (pListener && newBlocks)
2233 pListener->OnAudioIONewBlocks(&mCaptureTracks);
2234
2235 }
2236 // end of record buffering
2237 },
2238 // handler
2239 [this] ( AudacityException *pException ) {
2240 if ( pException ) {
2241 // So that we don't attempt to fill the recording buffer again
2242 // before the main thread stops recording
2244 return ;
2245 }
2246 else
2247 // Don't want to intercept other exceptions (?)
2248 throw;
2249 },
2250 delayedHandler );
2251}
void DefaultDelayedHandlerAction(AudacityException *pException)
A default template parameter for GuardedCall.
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
int format
Definition: ExportPCM.cpp:53
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
Base class for exceptions specially processed by the application.
bool IsStreamActive() const
Returns true if the audio i/o is running at all, but not during cleanup.
void StopStream() override
Stop recording, playback or input monitoring.
Definition: AudioIO.cpp:1353
size_t GetCommonlyAvailCapture()
Get the number of audio samples ready in all of the recording buffers.
Definition: AudioIO.cpp:1830
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:179
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:405
void SetRecordingException()
Definition: AudioIO.h:383
wxAtomicInt mRecordingException
Definition: AudioIO.h:382
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:138
samplePtr ptr() const
Definition: SampleFormat.h:152
double TotalCorrection() const
double ToDiscard() const
double ToConsume() const
double Consumed() const
PRCrossfadeData mCrossfadeData

References SampleBuffer::Allocate(), ClearSamples(), RecordingSchedule::Consumed(), DefaultDelayedHandlerAction(), floatSample, format, GetCommonlyAvailCapture(), AudioIoCallback::GetListener(), GuardedCall(), AudioIOBase::IsStreamActive(), lrint, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, RecordingSchedule::mCrossfadeData, AudioIoCallback::mFactor, min(), RecordingSchedule::mLatencyCorrected, AudioIoCallback::mMinCaptureSecsToCopy, RecordingSchedule::mPosition, AudioIOBase::mRate, AudioIoCallback::mRecordingException, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, narrowestSampleFormat, SampleBuffer::ptr(), AudioIoCallback::SetRecordingException(), size, StopStream(), RecordingSchedule::ToConsume(), RecordingSchedule::ToDiscard(), and RecordingSchedule::TotalCorrection().

Referenced by TrackBufferExchange().

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

◆ FillPlayBuffers()

void AudioIO::FillPlayBuffers ( )
private

First part of TrackBufferExchange.

Definition at line 1848 of file AudioIO.cpp.

1849{
1850 std::optional<RealtimeEffects::ProcessingScope> pScope;
1851 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
1852 pScope.emplace(
1853 *mpTransportState->mpRealtimeInitialization, mOwningProject);
1854
1855 if (mNumPlaybackChannels == 0)
1856 return;
1857
1858 // It is possible that some buffers will have more samples available than
1859 // others. This could happen if we hit this code during the PortAudio
1860 // callback. Also, if in a previous pass, unequal numbers of samples were
1861 // discarded from ring buffers for differing latencies.
1862
1863 // To keep things simple, we write no more data than is vacant in
1864 // ALL buffers, and advance the global time by that much.
1865 auto nAvailable = GetCommonlyFreePlayback();
1866
1867 // Don't fill the buffers at all unless we can do
1868 // at least mPlaybackSamplesToCopy. This improves performance
1869 // by not always trying to process tiny chunks, eating the
1870 // CPU unnecessarily.
1871 if (nAvailable < mPlaybackSamplesToCopy)
1872 return;
1873
1874 // More than mPlaybackSamplesToCopy might be copied:
1875 // May produce a larger amount when initially priming the buffer, or
1876 // perhaps again later in play to avoid underfilling the queue and
1877 // falling behind the real-time demand on the consumer side in the
1878 // callback.
1879 auto GetNeeded = [&]() -> size_t {
1880 // Note that reader might concurrently consume between loop passes below
1881 // So this might not be nondecreasing
1882 auto nReady = GetCommonlyWrittenForPlayback();
1884 };
1885 auto nNeeded = GetNeeded();
1886
1887 // wxASSERT( nNeeded <= nAvailable );
1888
1889 auto Flush = [&]{
1890 /* The flushing of all the Puts to the RingBuffers is lifted out of the
1891 do-loop in ProcessPlaybackSlices, and also after transformation of the
1892 stream for realtime effects.
1893
1894 It's only here that a release is done on the atomic variable that
1895 indicates the readiness of sample data to the consumer. That atomic
1896 also synchronizes the use of the TimeQueue.
1897 */
1898 for (size_t i = 0; i < std::max(size_t{1}, mPlaybackTracks.size()); ++i)
1899 mPlaybackBuffers[i]->Flush();
1900 };
1901
1902 while (true) {
1903 // Limit maximum buffer size (increases performance)
1904 auto available = std::min( nAvailable,
1905 std::max( nNeeded, mPlaybackSamplesToCopy ) );
1906
1907 // After each loop pass or after break
1908 Finally Do{ Flush };
1909
1910 if (!ProcessPlaybackSlices(pScope, available))
1911 // We are not making progress. May fail to satisfy the minimum but
1912 // won't loop forever
1913 break;
1914
1915 // Loop again to satisfy the minimum queue requirement in case there
1916 // was discarding of processed data for effect latencies
1917 nNeeded = GetNeeded();
1918 if (nNeeded == 0)
1919 break;
1920
1921 // Might increase because the reader consumed some
1922 nAvailable = GetCommonlyFreePlayback();
1923 }
1924}
bool ProcessPlaybackSlices(std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
Definition: AudioIO.cpp:1926
size_t GetCommonlyFreePlayback()
Get the number of audio samples free in all of the playback buffers.
Definition: AudioIO.cpp:1801
size_t GetCommonlyWrittenForPlayback()
Definition: AudioIO.cpp:1821
"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 126 of file AudioIO.cpp.

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

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(), EffectPreview(), ControlToolBar::EnableDisableButtons(), TranscriptionToolBar::EnableDisableButtons(), ScrubbingPlaybackPolicy::GetPlaybackSlice(), UpdateManager::GetUpdates(), HistoryDialog::HistoryDialog(), EffectUIHost::InitializeInstance(), LyricsPanel::LyricsPanel(), MixerBoard::MixerBoard(), ProjectManager::New(), RealtimeEffectListWindow::OnAddEffectClicked(), anonymous_namespace{LabelMenus.cpp}::OnAddLabelPlaying(), ProjectAudioManager::OnAudioIOStopRecording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::OnChangeButtonClicked(), ProjectManager::OnCloseWindow(), SelectActions::Handler::OnCursorPositionStore(), anonymous_namespace{PluginMenus.cpp}::OnDetectUpstreamDropouts(), TimeToolBar::OnIdle(), AudacityApp::OnKeyDown(), ProjectAudioManager::OnPause(), SelectActions::Handler::OnSelectCursorStoredCursor(), SelectUtilities::OnSetRegion(), anonymous_namespace{PluginMenus.cpp}::OnSimulateRecordingErrors(), ProjectAudioManager::OnSoundActivationThreshold(), ProjectWindow::PlaybackScroller::OnTimer(), LyricsWindow::OnTimer(), MixerBoard::OnTimer(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), TransportUtilities::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), TransportUtilities::PlayPlayRegionAndWait(), 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(), 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 1638 of file AudioIO.cpp.

1639{
1640 // Check if we can use the cached value
1641 if (mCachedBestRateIn != 0.0 && mCachedBestRateIn == sampleRate
1642 && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
1643 return mCachedBestRateOut;
1644 }
1645
1646 // In order to cache the value, all early returns should instead set retval
1647 // and jump to finished
1648 double retval;
1649
1650 std::vector<long> rates;
1651 if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
1652 if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
1653 wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
1654
1655 if (capturing && !playing) {
1656 rates = GetSupportedCaptureRates(-1, sampleRate);
1657 }
1658 else if (playing && !capturing) {
1659 rates = GetSupportedPlaybackRates(-1, sampleRate);
1660 }
1661 else { // we assume capturing and playing - the alternative would be a
1662 // bit odd
1663 rates = GetSupportedSampleRates(-1, -1, sampleRate);
1664 }
1665 /* rem rates is the array of hardware-supported sample rates (in the current
1666 * configuration), sampleRate is the Project Rate (desired sample rate) */
1667 long rate = (long)sampleRate;
1668
1669 if (make_iterator_range(rates).contains(rate)) {
1670 wxLogDebug(wxT("GetBestRate() Returning %.0ld Hz"), rate);
1671 retval = rate;
1672 goto finished;
1673 /* the easy case - the suggested rate (project rate) is in the list, and
1674 * we can just accept that and send back to the caller. This should be
1675 * the case for most users most of the time (all of the time on
1676 * Win MME as the OS does resampling) */
1677 }
1678
1679 /* if we get here, there is a problem - the project rate isn't supported
1680 * on our hardware, so we can't us it. Need to come up with an alternative
1681 * rate to use. The process goes like this:
1682 * * If there are no rates to pick from, we're stuck and return 0 (error)
1683 * * If there are some rates, we pick the next one higher than the requested
1684 * rate to use.
1685 * * If there aren't any higher, we use the highest available rate */
1686
1687 if (rates.empty()) {
1688 /* we're stuck - there are no supported rates with this hardware. Error */
1689 wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
1690 retval = 0.0;
1691 goto finished;
1692 }
1693 int i;
1694 for (i = 0; i < (int)rates.size(); i++) // for each supported rate
1695 {
1696 if (rates[i] > rate) {
1697 // supported rate is greater than requested rate
1698 wxLogDebug(wxT("GetBestRate() Returning next higher rate - %.0ld Hz"), rates[i]);
1699 retval = rates[i];
1700 goto finished;
1701 }
1702 }
1703
1704 wxLogDebug(wxT("GetBestRate() Returning highest rate - %.0ld Hz"), rates.back());
1705 retval = rates.back(); // the highest available rate
1706 goto finished;
1707
1708finished:
1709 mCachedBestRateIn = sampleRate;
1710 mCachedBestRateOut = retval;
1711 mCachedBestRatePlaying = playing;
1712 mCachedBestRateCapturing = capturing;
1713 return retval;
1714}
wxT("CloseDown"))
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1, double rate=0.0)
Get a list of sample rates the input (recording) device supports.
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1, double rate=0.0)
Get a list of sample rates the output (playback) device supports.
static double mCachedBestRateIn
Definition: AudioIOBase.h:282
static std::vector< long > GetSupportedSampleRates(int playDevice=-1, int recDevice=-1, double rate=0.0)
Get a list of sample rates the current input/output device combination supports.
static double mCachedBestRateOut
Definition: AudioIO.h:364
static bool mCachedBestRatePlaying
Definition: AudioIO.h:365
static bool mCachedBestRateCapturing
Definition: AudioIO.h:366

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

526{ return mCaptureFormat; }
sampleFormat mCaptureFormat
Definition: AudioIO.h:320

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

1831{
1832 auto commonlyAvail = mCaptureBuffers[0]->AvailForGet();
1833 for (unsigned i = 1; i < mCaptureTracks.size(); ++i)
1834 commonlyAvail = std::min(commonlyAvail,
1835 mCaptureBuffers[i]->AvailForGet());
1836 return commonlyAvail;
1837}

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

1802{
1803 auto commonlyAvail = mPlaybackBuffers[0]->AvailForPut();
1804 for (unsigned i = 1; i < mPlaybackTracks.size(); ++i)
1805 commonlyAvail = std::min(commonlyAvail,
1806 mPlaybackBuffers[i]->AvailForPut());
1807 // MB: subtract a few samples because the code in TrackBufferExchange has rounding
1808 // errors
1809 return commonlyAvail - std::min(size_t(10), commonlyAvail);
1810}

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

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

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 495 of file AudioIO.h.

495{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 399 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

unsigned AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 528 of file AudioIO.h.

528{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

unsigned AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 527 of file AudioIO.h.

527{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 496 of file AudioIO.h.

497 { 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 1716 of file AudioIO.cpp.

1717{
1718 // Track time readout for the main thread
1719
1720 if( !IsStreamActive() )
1721 return BAD_STREAM_TIME;
1722
1724}
#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 191 of file AudioIO.cpp.

192{
193 auto pAudioIO = safenew AudioIO();
194 ugAudioIO.reset(pAudioIO);
195 pAudioIO->StartThread();
196
197 // Make sure device prefs are initialized
198 if (gPrefs->Read(wxT("AudioIO/RecordingDevice"), wxT("")).empty()) {
199 int i = getRecordDevIndex();
200 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
201 if (info) {
204 }
205 }
206
207 if (gPrefs->Read(wxT("AudioIO/PlaybackDevice"), wxT("")).empty()) {
208 int i = getPlayDevIndex();
209 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
210 if (info) {
213 }
214 }
215
216 gPrefs->Flush();
217}
StringSetting AudioIOPlaybackDevice
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:70
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:82
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:75
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:233
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 426 of file AudioIO.cpp.

427{
428 return mInputMixerWorks;
429}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1339 of file AudioIO.cpp.

1340{
1341 auto pOwningProject = mOwningProject.lock();
1342 return !pOwningProject || pOwningProject.get() == &project;
1343}

References AudioIOBase::mOwningProject.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3322 of file AudioIO.cpp.

3323{
3324 // Includes a test of mTime, used in the main thread
3325 return IsStreamActive() &&
3326 GetNumCaptureChannels() > 0 &&
3329}
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:528

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

712{
713 return wxString::Format(wxT("%d %s."), (int) mLastPaError, Pa_GetErrorText(mLastPaError));
714}

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

1928{
1929 auto &policy = mPlaybackSchedule.GetPolicy();
1930
1931 // msmeyer: When playing a very short selection in looped
1932 // mode, the selection must be copied to the buffer multiple
1933 // times, to ensure, that the buffer has a reasonable size
1934 // This is the purpose of this loop.
1935 // PRL: or, when scrubbing, we may get work repeatedly from the
1936 // user interface.
1937 bool done = false;
1938 bool progress = false;
1939 do {
1940 const auto slice =
1941 policy.GetPlaybackSlice(mPlaybackSchedule, available);
1942 const auto &[frames, toProduce] = slice;
1943 progress = progress || toProduce > 0;
1944
1945 // Update the time queue. This must be done before writing to the
1946 // ring buffers of samples, for proper synchronization with the
1947 // consumer side in the PortAudio thread, which reads the time
1948 // queue after reading the sample queues. The sample queues use
1949 // atomic variables, the time queue doesn't.
1951
1952 size_t i = 0;
1953 for (auto &mixer : mPlaybackMixers) {
1954 // The mixer here isn't actually mixing: it's just doing
1955 // resampling, format conversion, and possibly time track
1956 // warping
1957 if (frames > 0) {
1958 size_t produced = 0;
1959 if ( toProduce )
1960 produced = mixer->Process( toProduce );
1961 //wxASSERT(produced <= toProduce);
1962 for(size_t j = 0, nChannels =
1964 j < nChannels; ++i, ++j
1965 ) {
1966 auto warpedSamples = mixer->GetBuffer(j);
1967 const auto put = mPlaybackBuffers[i]->Put(
1968 warpedSamples, floatSample, produced, frames - produced);
1969 // wxASSERT(put == frames);
1970 // but we can't assert in this thread
1971 wxUnusedVar(put);
1972 }
1973 }
1974 }
1975
1976 if (mPlaybackTracks.empty())
1977 // Produce silence in the single ring buffer
1978 mPlaybackBuffers[0]->Put(nullptr, floatSample, 0, frames);
1979
1980 available -= frames;
1981 // wxASSERT(available >= 0); // don't assert on this thread
1982
1983 done = policy.RepositionPlayback( mPlaybackSchedule, mPlaybackMixers,
1984 frames, available );
1985 } while (available && !done);
1986
1987 // Do any realtime effect processing, more efficiently in at most
1988 // two buffers per track, after all the little slices have been written.
1989 TransformPlayBuffers(pScope);
1990 return progress;
1991}
void TransformPlayBuffers(std::optional< RealtimeEffects::ProcessingScope > &scope)
Definition: AudioIO.cpp:1994
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.
size_t NChannels() const
Definition: Track.cpp:922

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

Referenced by FillPlayBuffers().

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

◆ RemoveState()

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

Forwards to RealtimeEffectManager::RemoveState with proper init scope.

Definition at line 369 of file AudioIO.cpp.

371{
373 if (mpTransportState && mpTransportState->mpRealtimeInitialization)
374 if (auto pProject = GetOwningProject(); pProject.get() == &project)
375 pInit = &*mpTransportState->mpRealtimeInitialization;
376 RealtimeEffectManager::Get(project).RemoveState(pInit, pTrack, pState);
377}
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 358 of file AudioIO.cpp.

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

728{
729 mOwningProject.reset();
730}

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

484{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:298

◆ SetMeters()

void AudioIO::SetMeters ( )
private

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

Definition at line 1345 of file AudioIO.cpp.

1346{
1347 if (auto pInputMeter = mInputMeter.lock())
1348 pInputMeter->Reset(mRate, true);
1349 if (auto pOutputMeter = mOutputMeter.lock())
1350 pOutputMeter->Reset(mRate, true);
1351}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:259

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

Referenced by StartPortAudioStream().

Here is the caller graph for this function:

◆ SetMixer()

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

Definition at line 379 of file AudioIO.cpp.

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

718{
719 if ( !mOwningProject.expired() ) {
720 wxASSERT(false);
722 }
723
724 mOwningProject = pProject;
725}

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

1624{
1625 if (state != IsPaused())
1626 {
1627 if (auto pOwningProject = mOwningProject.lock()) {
1628 // The realtime effects manager may remain "active" but becomes
1629 // "suspended" or "resumed".
1630 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1631 em.SetSuspended(state);
1632 }
1633 }
1634
1635 mPaused.store(state, std::memory_order_relaxed);
1636}
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:248
bool IsPaused() const
Find out if playback / recording is currently paused.

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

Here is the call graph for this function:

◆ StartMonitoring()

void AudioIO::StartMonitoring ( const AudioIOStartStreamOptions options)

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

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

Definition at line 732 of file AudioIO.cpp.

733{
735 return;
736
737 bool success;
738 auto captureFormat = QualitySettings::SampleFormatChoice();
739 auto captureChannels = AudioIORecordChannels.Read();
740 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
741 int playbackChannels = 0;
742
744 playbackChannels = 2;
745
746 // FIXME: TRAP_ERR StartPortAudioStream (a PaError may be present)
747 // but StartPortAudioStream function only returns true or false.
748 mUsingAlsa = false;
749 success = StartPortAudioStream(options, (unsigned int)playbackChannels,
750 (unsigned int)captureChannels,
751 captureFormat);
752
753 auto pOwningProject = mOwningProject.lock();
754 if (!success) {
755 using namespace BasicUI;
756 auto msg = XO("Error opening recording device.\nError code: %s")
757 .Format( Get()->LastPaErrorString() );
758 ShowErrorDialog( *ProjectFramePlacement( pOwningProject.get() ),
759 XO("Error"), msg, wxT("Error_opening_sound_device"),
760 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
761 return;
762 }
763
764 Publish({ pOwningProject.get(), AudioIOEvent::MONITOR, true });
765
766 // FIXME: TRAP_ERR PaErrorCode 'noted' but not reported in StartMonitoring.
767 // Now start the PortAudio stream!
768 // TODO: ? Factor out and reuse error reporting code from end of
769 // AudioIO::StartStream?
770 mLastPaError = Pa_StartStream( mPortStreamV19 );
771
772 // Update UI display only now, after all possibilities for error are past.
773 auto pListener = GetListener();
774 if ((mLastPaError == paNoError) && pListener) {
775 // advertise the chosen I/O sample rate to the UI
776 pListener->OnAudioIORate((int)mRate);
777 }
778}
IntSetting AudioIORecordChannels
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
int mStreamToken
Definition: AudioIOBase.h:251
bool StartPortAudioStream(const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels, sampleFormat captureFormat)
Opens the portaudio stream(s) used to do playback or recording (or both) through.
Definition: AudioIO.cpp:472
wxString LastPaErrorString()
Definition: AudioIO.cpp:711
bool mSoftwarePlaythrough
Definition: AudioIO.h:311
bool mUsingAlsa
Definition: AudioIO.h:360
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:259
PROJECT_RATE_API sampleFormat SampleFormatChoice()
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:51

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

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

References _, audacityAudioCallback(), AudacityToPortAudioSampleFormat(), AudioIOLatencyDuration, AudioIOStartStreamOptions::captureMeter, floatSample, GetBestRate(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), DeviceManager::GetTimeSinceRescan(), DeviceManager::Instance(), int24Sample, lrint, AudioIoCallback::mCaptureFormat, AudioIoCallback::mHardwarePlaybackLatencyFrames, AudioIOBase::mInputMeter, AudioIoCallback::mLastPaError, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPauseFrames, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIoCallback::mUsingAlsa, AudioIoCallback::mUsingJack, AudioIOStartStreamOptions::playbackMeter, AudioIOStartStreamOptions::pProject, AudioIOStartStreamOptions::rate, Setting< T >::Read(), ResetOwningProject(), 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 780 of file AudioIO.cpp.

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

References AllocateBuffers(), AudioIOLatencyCorrection, AudioIOEvent::CAPTURE, TransportTracks::captureTracks, PlaybackSchedule::TimeQueue::Clear(), AudioIoCallback::ClearRecordingException(), DB_TO_LINEAR, DecibelScaleCutoff, AudioIoCallback::Extensions(), floatSample, AudioIoCallback::GetListener(), PlaybackSchedule::GetPolicy(), PlaybackSchedule::GetTrackTime(), gPrefs, PlaybackSchedule::Init(), PlaybackPolicy::Initialize(), AudioIOBase::IsBusy(), LAT1CTOWX, AudioIOStartStreamOptions::listener, AudioIoCallback::mAudioThreadShouldCallTrackBufferExchangeOnce, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, RecordingSchedule::mCrossfadeData, AudioIoCallback::mDetectDropouts, RecordingSchedule::mDuration, AudioIoCallback::mForceFadeOut, min(), AudioIoCallback::mLastRecordingOffset, RecordingSchedule::mLatencyCorrection, AudioIoCallback::mListener, AudioIoCallback::mLostCaptureIntervals, AudioIoCallback::mLostSamples, AudioIoCallback::mNextStreamToken, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOwningProject, AudioIoCallback::mPauseRec, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mPortStreamV19, RecordingSchedule::mPreRoll, AudioIoCallback::mpTransportState, AudioIOBase::mRate, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIoCallback::mScratchBuffers, AudioIoCallback::mScratchPointers, AudioIoCallback::mSeek, AudioIoCallback::mSilenceLevel, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, PlaybackSchedule::mTimeQueue, AudioIoCallback::mUsingAlsa, PlaybackPolicy::OffsetTrackTime(), TransportTracks::otherPlayableTracks, AudioIOStartStreamOptions::pCrossfadeData, AudioIOEvent::PLAYBACK, AudioIOStartStreamOptions::playbackStreamPrimer, TransportTracks::playbackTracks, AudioIOStartStreamOptions::preRoll, PlaybackSchedule::TimeQueue::Prime(), AudioIOStartStreamOptions::pStartTime, Observer::Publisher< AudioIOEvent >::Publish(), AudioIOStartStreamOptions::rate, Setting< T >::Read(), PlaybackSchedule::ResetMode(), PlaybackSchedule::SetTrackTime(), BasicUI::ShowMessageBox(), SoundActivatedRecord, AudioIoCallback::StartAudioThread(), StartPortAudioStream(), StartStreamCleanup(), AudioIoCallback::StopAudioThread(), StopStream(), Verbatim(), AudioIoCallback::WaitForAudioThreadStarted(), WarningDialogKey(), and wxT().

Here is the call graph for this function:

◆ StartStreamCleanup()

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

Clean up after StartStream if it fails.

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

Definition at line 1316 of file AudioIO.cpp.

1317{
1318 mpTransportState.reset();
1319
1320 mPlaybackBuffers.reset();
1321 mScratchBuffers.clear();
1322 mScratchPointers.clear();
1323 mPlaybackMixers.clear();
1324 mCaptureBuffers.reset();
1325 mResample.reset();
1327
1328 if(!bOnlyBuffers)
1329 {
1330 Pa_AbortStream( mPortStreamV19 );
1331 Pa_CloseStream( mPortStreamV19 );
1332 mPortStreamV19 = NULL;
1333 mStreamToken = 0;
1334 }
1335
1337}
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 310 of file AudioIO.cpp.

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

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

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

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

Referenced by DrainRecordBuffers(), and StartStream().

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

◆ TrackBufferExchange()

void AudioIO::TrackBufferExchange ( )
private

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

Definition at line 1842 of file AudioIO.cpp.

1843{
1846}
void DrainRecordBuffers()
Second part of TrackBufferExchange.
Definition: AudioIO.cpp:2056
void FillPlayBuffers()
First part of TrackBufferExchange.
Definition: AudioIO.cpp:1848

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

1996{
1997 // Transform written but un-flushed samples in the RingBuffers in-place.
1998
1999 // Avoiding std::vector
2000 auto pointers =
2001 static_cast<float**>(alloca(mNumPlaybackChannels * sizeof(float*)));
2002
2003 const auto numPlaybackTracks = mPlaybackTracks.size();
2004 for (unsigned t = 0; t < numPlaybackTracks; ++t) {
2005 const auto vt = mPlaybackTracks[t].get();
2006 if (!(vt && vt->IsLeader()))
2007 continue;
2008 // vt is mono, or is the first of its group of channels
2009 const auto nChannels = std::min<size_t>(
2011
2012 // Loop over the blocks of unflushed data, at most two
2013 for (unsigned iBlock : {0, 1}) {
2014 size_t len = 0;
2015 size_t iChannel = 0;
2016 for (; iChannel < nChannels; ++iChannel) {
2017 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
2018 const auto pair =
2019 ringBuffer.GetUnflushed(iBlock);
2020 // Playback RingBuffers have float format: see AllocateBuffers
2021 pointers[iChannel] = reinterpret_cast<float*>(pair.first);
2022 // The lengths of corresponding unflushed blocks should be
2023 // the same for all channels
2024 if (len == 0)
2025 len = pair.second;
2026 else
2027 assert(len == pair.second);
2028 }
2029
2030 // Are there more output device channels than channels of vt?
2031 // Such as when a mono track is processed for stereo play?
2032 // Then supply some non-null fake input buffers, because the
2033 // various ProcessBlock overrides of effects may crash without it.
2034 // But it would be good to find the fixes to make this unnecessary.
2035 float **scratch = &mScratchPointers[mNumPlaybackChannels + 1];
2036 while (iChannel < mNumPlaybackChannels)
2037 memset((pointers[iChannel++] = *scratch++), 0, len * sizeof(float));
2038
2039 if (len && pScope) {
2040 auto discardable = pScope->Process( *vt, &pointers[0],
2041 mScratchPointers.data(),
2042 // The single dummy output buffer:
2045 iChannel = 0;
2046 for (; iChannel < nChannels; ++iChannel) {
2047 auto &ringBuffer = *mPlaybackBuffers[t + iChannel];
2048 auto discarded = ringBuffer.Unput(discardable);
2049 // assert(discarded == discardable);
2050 }
2051 }
2052 }
2053 }
2054}

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

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

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

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

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 656 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 655 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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