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

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

#include <AudioIO.h>

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

Public Types

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

Public Member Functions

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

Static Public Member Functions

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

Private Member Functions

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

Private Attributes

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

Additional Inherited Members

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

Detailed Description

AudioIO uses the PortAudio library to play and record sound.

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

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

Definition at line 421 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 495 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 232 of file AudioIO.cpp.

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

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

Referenced by Init().

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

◆ ~AudioIO()

AudioIO::~AudioIO ( )
private

Definition at line 314 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

Member Function Documentation

◆ AddState()

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

Forwards to RealtimeEffectManager::AddState with proper init scope.

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

Definition at line 347 of file AudioIO.cpp.

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

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

Referenced by EffectUIHost::InitializeInstance().

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

◆ AllocateBuffers()

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

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

Returns true iff successful.

Definition at line 1170 of file AudioIO.cpp.

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

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

Referenced by StartStream().

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

◆ AudioThread()

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

Sits in a thread loop reading and writing audio.

Definition at line 1802 of file AudioIO.cpp.

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

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

Referenced by StartThread().

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

◆ CallAfterRecording()

void AudioIO::CallAfterRecording ( PostRecordingAction  action)

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

This may be called from non-main threads

Definition at line 1142 of file AudioIO.cpp.

1143{
1144 if (!action)
1145 return;
1146
1147 {
1148 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1150 // Enqueue it, even if perhaps not still recording,
1151 // but it wasn't cleared yet
1153 prevAction = std::move(mPostRecordingAction),
1154 nextAction = std::move(action)
1155 ]{ prevAction(); nextAction(); };
1156 return;
1157 }
1158 else if (DelayingActions()) {
1159 mPostRecordingAction = std::move(action);
1160 return;
1161 }
1162 }
1163
1164 // Don't delay it except until idle time.
1165 // (Recording might start between now and then, but won't go far before
1166 // the action is done. So the system isn't bulletproof yet.)
1167 BasicUI::CallAfter(move(action));
1168}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:663
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:664
bool DelayingActions() const
Definition: AudioIO.cpp:1137
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 218 of file AudioIO.cpp.

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

References AudioIOBase::ugAudioIO.

Referenced by AudacityApp::OnExit().

Here is the caller graph for this function:

◆ DelayActions()

void AudioIO::DelayActions ( bool  recording)

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

Definition at line 1132 of file AudioIO.cpp.

1133{
1134 mDelayingActions = recording;
1135}
bool mDelayingActions
Definition: AudioIO.h:666

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1137 of file AudioIO.cpp.

1138{
1140}

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

Referenced by CallAfterRecording().

Here is the caller graph for this function:

◆ DrainRecordBuffers()

void AudioIO::DrainRecordBuffers ( )
private

Second part of SequenceBufferExchange.

Definition at line 2123 of file AudioIO.cpp.

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

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

Referenced by SequenceBufferExchange().

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

◆ FillPlayBuffers()

void AudioIO::FillPlayBuffers ( )
private

First part of SequenceBufferExchange.

Definition at line 1909 of file AudioIO.cpp.

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

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

Referenced by SequenceBufferExchange().

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

◆ Get()

AudioIO * AudioIO::Get ( )
static

Definition at line 126 of file AudioIO.cpp.

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

References AudioIOBase::Get().

Referenced by AdornedRulerPanel::AdornedRulerPanel(), audacityAudioCallback(), AudioThread(), 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(), 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(), MixerBoard::OnTimer(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), anonymous_namespace{ViewMenus.cpp}::OnZoomIn(), TransportUtilities::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), TransportUtilities::PlayPlayRegionAndWait(), TransportUtilities::RecordAndWait(), ProjectAudioManager::Recording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::RemoveFromList(), ScrubbingPlaybackPolicy::RepositionPlayback(), TimerRecordDialog::RunWaitDialog(), anonymous_namespace{SelectMenus.cpp}::SeekWhenAudioActive(), MeterPanel::SetMixer(), MeterPanel::StartMonitoring(), StartMonitoring(), ControlToolBar::StartScrolling(), ProjectAudioManager::Stop(), MeterPanel::StopMonitoring(), anonymous_namespace{PluginMenus.cpp}::ToolsMenu(), TrackPanel::TrackPanel(), MeterPanel::UpdateSelectedPrefs(), and MeterPanel::UpdateSliderControl().

Here is the call graph for this function:

◆ GetBestRate()

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

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

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

Definition at line 1706 of file AudioIO.cpp.

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

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

Referenced by StartPortAudioStream().

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

◆ GetCaptureFormat()

sampleFormat AudioIO::GetCaptureFormat ( )
inline

Definition at line 535 of file AudioIO.h.

535{ return mCaptureFormat; }

◆ GetCommonlyAvailCapture()

size_t AudioIO::GetCommonlyAvailCapture ( )
private

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

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

Definition at line 1895 of file AudioIO.cpp.

1896{
1898}
static size_t MinValue(const RingBuffers &buffers, size_t(RingBuffer::*pmf)() const)
Definition: AudioIO.cpp:1868
size_t AvailForGet() const
Definition: RingBuffer.cpp:226

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

Referenced by DrainRecordBuffers().

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

◆ GetCommonlyFreePlayback()

size_t AudioIO::GetCommonlyFreePlayback ( )
private

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

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

Definition at line 1877 of file AudioIO.cpp.

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

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

Referenced by FillPlayBuffers().

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

◆ GetInputSourceNames()

wxArrayString AudioIO::GetInputSourceNames ( )

Get the list of inputs to the current mixer device.

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

Definition at line 434 of file AudioIO.cpp.

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

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 504 of file AudioIO.h.

504{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 402 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

size_t AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 537 of file AudioIO.h.

537{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

size_t AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 536 of file AudioIO.h.

536{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 505 of file AudioIO.h.

506 { return mOwningProject.lock(); }

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

Here is the caller graph for this function:

◆ GetStreamTime()

double AudioIO::GetStreamTime ( )

During playback, the sequence time most recently played.

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

Definition at line 1784 of file AudioIO.cpp.

1785{
1786 // Sequence time readout for the main thread
1787
1788 if( !IsStreamActive() )
1789 return BAD_STREAM_TIME;
1790
1792}
#define BAD_STREAM_TIME
Definition: AudioIOBase.h:38
double GetSequenceTime() const
Get current track time value, unadjusted.

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

Here is the call graph for this function:

◆ Init()

void AudioIO::Init ( )
static

Definition at line 190 of file AudioIO.cpp.

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

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

Referenced by AudacityApp::InitPart2().

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

◆ InputMixerWorks()

bool AudioIO::InputMixerWorks ( )

Find out if the input hardware level control is available.

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

Definition at line 429 of file AudioIO.cpp.

430{
431 return mInputMixerWorks;
432}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1414 of file AudioIO.cpp.

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

References AudioIOBase::mOwningProject, and project.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3348 of file AudioIO.cpp.

3349{
3350 // Includes a test of mTime, used in the main thread
3351 return IsStreamActive() &&
3352 GetNumCaptureChannels() > 0 &&
3355}
size_t GetNumCaptureChannels() const
Definition: AudioIO.h:537

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

Here is the call graph for this function:

◆ LastPaErrorString()

wxString AudioIO::LastPaErrorString ( )

Definition at line 756 of file AudioIO.cpp.

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

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

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

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

Referenced by FillPlayBuffers().

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

◆ RemoveState()

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

Forwards to RealtimeEffectManager::RemoveState with proper init scope.

Definition at line 371 of file AudioIO.cpp.

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

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

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

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

◆ ReplaceState()

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

Forwards to RealtimeEffectManager::ReplaceState with proper init scope.

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

Definition at line 359 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ ResetOwningProject()

void AudioIO::ResetOwningProject ( )
private

Definition at line 772 of file AudioIO.cpp.

773{
774 mOwningProject.reset();
775}

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

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

◆ SequenceBufferExchange()

void AudioIO::SequenceBufferExchange ( )
private

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

Definition at line 1903 of file AudioIO.cpp.

1904{
1907}
void DrainRecordBuffers()
Second part of SequenceBufferExchange.
Definition: AudioIO.cpp:2123
void FillPlayBuffers()
First part of SequenceBufferExchange.
Definition: AudioIO.cpp:1909

References DrainRecordBuffers(), and FillPlayBuffers().

Referenced by AudioThread().

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

◆ SetMeters()

void AudioIO::SetMeters ( )
private

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

Definition at line 1420 of file AudioIO.cpp.

1421{
1422 if (auto pInputMeter = mInputMeter.lock())
1423 pInputMeter->Reset(mRate, true);
1424 if (auto pOutputMeter = mOutputMeter.lock())
1425 pOutputMeter->Reset(mRate, true);
1426}
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h: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 382 of file AudioIO.cpp.

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

763{
764 if ( !mOwningProject.expired() ) {
765 wxASSERT(false);
767 }
768
769 mOwningProject = pProject;
770}

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

1692{
1693 if (state != IsPaused())
1694 {
1695 if (auto pOwningProject = mOwningProject.lock()) {
1696 // The realtime effects manager may remain "active" but becomes
1697 // "suspended" or "resumed".
1698 auto &em = RealtimeEffectManager::Get(*pOwningProject);
1699 em.SetSuspended(state);
1700 }
1701 }
1702
1703 mPaused.store(state, std::memory_order_relaxed);
1704}
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 777 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ StartPortAudioStream()

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

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

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

Definition at line 475 of file AudioIO.cpp.

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

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

Referenced by StartMonitoring(), and StartStream().

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

◆ StartStream()

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

Start recording or playing back audio.

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

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

Definition at line 827 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ StartStreamCleanup()

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

Clean up after StartStream if it fails.

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

Definition at line 1391 of file AudioIO.cpp.

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

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

Referenced by AllocateBuffers(), and StartStream().

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

◆ StartThread()

void AudioIO::StartThread ( )
private

Definition at line 309 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ StopStream()

void AudioIO::StopStream ( )
overridevirtual

Stop recording, playback or input monitoring.

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

Implements AudioIOBase.

Definition at line 1428 of file AudioIO.cpp.

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

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

Referenced by DrainRecordBuffers(), and StartStream().

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

◆ TransformPlayBuffers()

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

Definition at line 2058 of file AudioIO.cpp.

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

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

Referenced by ProcessPlaybackSlices().

Here is the caller graph for this function:

◆ ValidateDeviceNames()

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

Ensure selected device names are valid.

Definition at line 223 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

Member Data Documentation

◆ mDelayingActions

bool AudioIO::mDelayingActions { false }
private

Definition at line 666 of file AudioIO.h.

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 664 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 663 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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