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

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

#include <AudioIO.h>

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

Public Types

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

Public Member Functions

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

Static Public Member Functions

static AudioIOGet ()
 
static bool ValidateDeviceNames (const wxString &play, const wxString &rec)
 Ensure selected device names are valid. More...
 
static void AudioThread (std::atomic< bool > &finish)
 Sits in a thread loop reading and writing audio. More...
 
static void Init ()
 
static void Deinit ()
 
- Static Public Member Functions inherited from AudioIOBase
static AudioIOBaseGet ()
 
static std::vector< long > GetSupportedPlaybackRates (int DevIndex=-1, 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...
 
bool ProcessPlaybackSlices (std::optional< RealtimeEffects::ProcessingScope > &pScope, size_t available)
 
void DrainRecordBuffers ()
 Second part of SequenceBufferExchange. More...
 
size_t GetCommonlyFreePlayback ()
 Get the number of audio samples free in all of the playback buffers. More...
 
size_t GetCommonlyAvailCapture ()
 Get the number of audio samples ready in all of the recording buffers. More...
 
bool AllocateBuffers (const AudioIOStartStreamOptions &options, const TransportSequences &sequences, double t0, double t1, double sampleRate)
 Allocate RingBuffer structures, and others, needed for playback and recording. More...
 
void StartStreamCleanup (bool bOnlyBuffers=false)
 Clean up after StartStream if it fails. More...
 

Private Attributes

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

Additional Inherited Members

- Public Attributes inherited from AudioIoCallback
int mbHasSoloSequences
 
int mCallbackReturn
 
long mNumPauseFrames
 How many frames of zeros were output due to pauses? More...
 
std::thread mAudioThread
 
std::atomic< bool > mFinishAudioThread { false }
 
std::vector< std::unique_ptr< Resample > > mResample
 
RingBuffers mCaptureBuffers
 
RecordableSequences mCaptureSequences
 
std::vector< std::vector< float > > mProcessingBuffers
 
std::vector< std::vector< float > > mMasterBuffers
 
RingBuffers mPlaybackBuffers
 
ConstPlayableSequences mPlaybackSequences
 
float mOldPlaybackGain
 
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 412 of file AudioIO.h.

Member Typedef Documentation

◆ PostRecordingAction

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

Definition at line 483 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:310
std::atomic< bool > mAudioThreadShouldCallSequenceBufferExchangeOnce
Definition: AudioIO.h:309
std::atomic< Acknowledge > mAudioThreadAcknowledge
Definition: AudioIO.h:313
std::atomic< bool > mAudioThreadSequenceBufferExchangeLoopActive
Definition: AudioIO.h:311
long mNumPauseFrames
How many frames of zeros were output due to pauses?
Definition: AudioIO.h:232
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:329
PaError mLastPaError
Definition: AudioIO.h:333
double mLastRecordingOffset
Not (yet) used; should perhaps be atomic when it is.
Definition: AudioIO.h:332
float mSilenceLevel
Definition: AudioIO.h:301
size_t mNumCaptureChannels
Definition: AudioIO.h:303
void SetMixerOutputVol(float value)
Definition: AudioIO.h:341
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101

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

Referenced by Init().

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

◆ ~AudioIO()

AudioIO::~AudioIO ( )
private

Definition at line 314 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

Member Function Documentation

◆ AddState()

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

Forwards to RealtimeEffectManager::AddState with proper init scope.

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

Definition at line 347 of file AudioIO.cpp.

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

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

Referenced by EffectUIHost::InitializeInstance().

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

◆ AllocateBuffers()

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

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

Returns true iff successful.

Definition at line 1166 of file AudioIO.cpp.

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

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

Referenced by StartStream().

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

◆ AudioThread()

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

Sits in a thread loop reading and writing audio.

Definition at line 1805 of file AudioIO.cpp.

1806{
1807 enum class State { eUndefined, eOnce, eLoopRunning, eDoNothing, eMonitoring } lastState = State::eUndefined;
1808 AudioIO *const gAudioIO = AudioIO::Get();
1809 while (!finish.load(std::memory_order_acquire)) {
1810 using Clock = std::chrono::steady_clock;
1811 auto loopPassStart = Clock::now();
1812 auto &schedule = gAudioIO->mPlaybackSchedule;
1813 const auto interval = schedule.GetPolicy().SleepInterval(schedule);
1814
1815 // Set LoopActive outside the tests to avoid race condition
1817 .store(true, std::memory_order_relaxed);
1819 .load(std::memory_order_acquire) )
1820 {
1821 gAudioIO->SequenceBufferExchange();
1823 .store(false, std::memory_order_release);
1824
1825 lastState = State::eOnce;
1826 }
1828 .load(std::memory_order_relaxed))
1829 {
1830 if (lastState != State::eLoopRunning)
1831 {
1832 // Main thread has told us to start - acknowledge that we do
1834 std::memory_order::memory_order_release);
1835 }
1836 lastState = State::eLoopRunning;
1837
1838 // We call the processing after raising the acknowledge flag, because the main thread
1839 // only needs to know that the message was seen.
1840 //
1841 // This is unlike the case with mAudioThreadShouldCallSequenceBufferExchangeOnce where the
1842 // store really means that the one-time exchange was done.
1843
1844 gAudioIO->SequenceBufferExchange();
1845 }
1846 else
1847 {
1848 if ( (lastState == State::eLoopRunning)
1849 || (lastState == State::eMonitoring ) )
1850 {
1851 // Main thread has told us to stop; (actually: to neither process "once" nor "loop running")
1852 // acknowledge that we received the order and that no more processing will be done.
1854 std::memory_order::memory_order_release);
1855 }
1856 lastState = State::eDoNothing;
1857
1858 if (gAudioIO->IsMonitoring())
1859 {
1860 lastState = State::eMonitoring;
1861 }
1862 }
1863
1865 .store(false, std::memory_order_relaxed);
1866
1867 std::this_thread::sleep_until( loopPassStart + interval );
1868 }
1869}
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:415
static AudioIO * Get()
Definition: AudioIO.cpp:126
void SequenceBufferExchange()
Definition: AudioIO.cpp:1906
virtual std::chrono::milliseconds SleepInterval(PlaybackSchedule &schedule)
How long to wait between calls to AudioIO::SequenceBufferExchange.
Definition: Dither.cpp:67

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

Referenced by StartThread().

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

◆ CallAfterRecording()

void AudioIO::CallAfterRecording ( PostRecordingAction  action)

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

This may be called from non-main threads

Definition at line 1138 of file AudioIO.cpp.

1139{
1140 if (!action)
1141 return;
1142
1143 {
1144 std::lock_guard<std::mutex> guard{ mPostRecordingActionMutex };
1146 // Enqueue it, even if perhaps not still recording,
1147 // but it wasn't cleared yet
1149 prevAction = std::move(mPostRecordingAction),
1150 nextAction = std::move(action)
1151 ]{ prevAction(); nextAction(); };
1152 return;
1153 }
1154 else if (DelayingActions()) {
1155 mPostRecordingAction = std::move(action);
1156 return;
1157 }
1158 }
1159
1160 // Don't delay it except until idle time.
1161 // (Recording might start between now and then, but won't go far before
1162 // the action is done. So the system isn't bulletproof yet.)
1163 BasicUI::CallAfter(move(action));
1164}
std::mutex mPostRecordingActionMutex
Definition: AudioIO.h:650
PostRecordingAction mPostRecordingAction
Definition: AudioIO.h:651
bool DelayingActions() const
Definition: AudioIO.cpp:1133
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214

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

Here is the call graph for this function:

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 218 of file AudioIO.cpp.

219{
220 ugAudioIO.reset();
221}
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h: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 1128 of file AudioIO.cpp.

1129{
1130 mDelayingActions = recording;
1131}
bool mDelayingActions
Definition: AudioIO.h:653

References mDelayingActions.

Referenced by StopStream().

Here is the caller graph for this function:

◆ DelayingActions()

bool AudioIO::DelayingActions ( ) const
private

Definition at line 1133 of file AudioIO.cpp.

1134{
1136}

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

Referenced by CallAfterRecording().

Here is the caller graph for this function:

◆ DrainRecordBuffers()

void AudioIO::DrainRecordBuffers ( )
private

Second part of SequenceBufferExchange.

Definition at line 2250 of file AudioIO.cpp.

2251{
2253 return;
2254
2255 auto delayedHandler = [this] ( AudacityException * pException ) {
2256 // In the main thread, stop recording
2257 // This is one place where the application handles disk
2258 // exhaustion exceptions from RecordableSequence operations, without
2259 // rolling back to the last pushed undo state. Instead, partial recording
2260 // results are pushed as a NEW undo state. For this reason, as
2261 // commented elsewhere, we want an exception safety guarantee for
2262 // the output RecordableSequences, after the failed append operation, that
2263 // the sequences remain as they were after the previous successful
2264 // (block-level) appends.
2265
2266 // Note that the Flush in StopStream() may throw another exception,
2267 // but StopStream() contains that exception, and the logic in
2268 // AudacityException::DelayedHandlerAction prevents redundant message
2269 // boxes.
2270 StopStream();
2271 DefaultDelayedHandlerAction( pException );
2272 for (auto &pSequence: mCaptureSequences)
2273 pSequence->RepairChannels();
2274 };
2275
2276 GuardedCall( [&] {
2277 // start record buffering
2278 const auto avail = GetCommonlyAvailCapture(); // samples
2279 const auto remainingTime =
2280 std::max(0.0, mRecordingSchedule.ToConsume());
2281 // This may be a very big double number:
2282 const auto remainingSamples = remainingTime * mRate;
2283 bool latencyCorrected = true;
2284
2285 double deltat = avail / mRate;
2286
2288 .load(std::memory_order_relaxed) ||
2289 deltat >= mMinCaptureSecsToCopy)
2290 {
2291 bool newBlocks = false;
2292
2293 // Append captured samples to the end of the RecordableSequences.
2294 // (WaveTracks have their own buffering for efficiency.)
2295 auto iter = mCaptureSequences.begin();
2296 auto width = (*iter)->NChannels();
2297 size_t iChannel = 0;
2298 for (size_t i = 0; i < mNumCaptureChannels; ++i) {
2299 Finally Do {[&]{
2300 if (++iChannel == width) {
2301 ++iter;
2302 iChannel = 0;
2303 if (iter != mCaptureSequences.end())
2304 width = (*iter)->NChannels();
2305 }
2306 }};
2307 size_t discarded = 0;
2308
2310 const auto correction = mRecordingSchedule.TotalCorrection();
2311 if (correction >= 0) {
2312 // Rightward shift
2313 // Once only (per sequence per recording), insert some initial
2314 // silence.
2315 size_t size = floor( correction * mRate * mFactor);
2317 ClearSamples(temp.ptr(), mCaptureFormat, 0, size);
2318 (*iter)->Append(iChannel, temp.ptr(), mCaptureFormat, size, 1,
2319 // Do not dither recordings
2321 }
2322 else {
2323 // Leftward shift
2324 // discard some samples from the ring buffers.
2325 size_t size = floor(
2327
2328 // The ring buffer might have grown concurrently -- don't discard more
2329 // than the "avail" value noted above.
2330 discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2331
2332 if (discarded < size)
2333 // We need to visit this again to complete the
2334 // discarding.
2335 latencyCorrected = false;
2336 }
2337 }
2338
2339 const float *pCrossfadeSrc = nullptr;
2340 size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2341 if (i < mRecordingSchedule.mCrossfadeData.size())
2342 {
2343 // Do crossfading
2344 // The supplied crossfade samples are at the same rate as the
2345 // sequence
2346 const auto &data = mRecordingSchedule.mCrossfadeData[i];
2347 totalCrossfadeLength = data.size();
2348 if (totalCrossfadeLength) {
2349 crossfadeStart =
2351 if (crossfadeStart < totalCrossfadeLength)
2352 pCrossfadeSrc = data.data() + crossfadeStart;
2353 }
2354 }
2355
2356 wxASSERT(discarded <= avail);
2357 size_t toGet = avail - discarded;
2358 SampleBuffer temp;
2359 size_t size;
2361 if( mFactor == 1.0 )
2362 {
2363 // Take captured samples directly
2364 size = toGet;
2365 if (pCrossfadeSrc)
2366 // Change to float for crossfade calculation
2368 else
2370 temp.Allocate(size, format);
2371 const auto got =
2372 mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2373 // wxASSERT(got == toGet);
2374 // but we can't assert in this thread
2375 wxUnusedVar(got);
2376 if (double(size) > remainingSamples)
2377 size = floor(remainingSamples);
2378 }
2379 else
2380 {
2381 size = lrint(toGet * mFactor);
2383 SampleBuffer temp1(toGet, floatSample);
2384 temp.Allocate(size, format);
2385 const auto got =
2386 mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
2387 // wxASSERT(got == toGet);
2388 // but we can't assert in this thread
2389 wxUnusedVar(got);
2390 /* we are re-sampling on the fly. The last resampling call
2391 * must flush any samples left in the rate conversion buffer
2392 * so that they get recorded
2393 */
2394 if (toGet > 0 ) {
2395 if (double(toGet) > remainingSamples)
2396 toGet = floor(remainingSamples);
2397 const auto results =
2398 mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
2399 !IsStreamActive(), (float *)temp.ptr(), size);
2400 size = results.second;
2401 }
2402 }
2403
2404 if (pCrossfadeSrc) {
2405 wxASSERT(format == floatSample);
2406 size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
2407 if (crossfadeLength) {
2408 auto ratio = double(crossfadeStart) / totalCrossfadeLength;
2409 auto ratioStep = 1.0 / totalCrossfadeLength;
2410 auto pCrossfadeDst = (float*)temp.ptr();
2411
2412 // Crossfade loop here
2413 for (size_t ii = 0; ii < crossfadeLength; ++ii) {
2414 *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
2415 ++pCrossfadeSrc, ++pCrossfadeDst;
2416 ratio += ratioStep;
2417 }
2418 }
2419 }
2420
2421 // Now append
2422 // see comment in second handler about guarantee
2423 newBlocks = (*iter)->Append(iChannel,
2424 temp.ptr(), format, size, 1,
2425 // Do not dither recordings
2427 ) || newBlocks;
2428 } // end loop over capture channels
2429
2430 // Now update the recording schedule position
2432 mRecordingSchedule.mLatencyCorrected = latencyCorrected;
2433
2434 auto pListener = GetListener();
2435 if (pListener && newBlocks)
2436 pListener->OnAudioIONewBlocks();
2437
2438 }
2439 // end of record buffering
2440 },
2441 // handler
2442 [this] ( AudacityException *pException ) {
2443 if ( pException ) {
2444 // So that we don't attempt to fill the recording buffer again
2445 // before the main thread stops recording
2447 return ;
2448 }
2449 else
2450 // Don't want to intercept other exceptions (?)
2451 throw;
2452 },
2453 delayedHandler );
2454}
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:1898
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:170
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:394
void SetRecordingException()
Definition: AudioIO.h:372
RecordableSequences mCaptureSequences
Definition: AudioIO.h:258
double mCaptureRate
Definition: AudioIO.h:307
wxAtomicInt mRecordingException
Definition: AudioIO.h:371
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:151
samplePtr ptr() const
Definition: SampleFormat.h:165
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:175
double TotalCorrection() const
double ToDiscard() const
double ToConsume() const
double Consumed() const
PRCrossfadeData mCrossfadeData

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

Referenced by SequenceBufferExchange().

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

◆ FillPlayBuffers()

void AudioIO::FillPlayBuffers ( )
private

First part of SequenceBufferExchange.

Definition at line 1912 of file AudioIO.cpp.

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

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

Referenced by SequenceBufferExchange().

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

◆ Get()

AudioIO * AudioIO::Get ( )
static

Definition at line 126 of file AudioIO.cpp.

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

References AudioIOBase::Get().

Referenced by AdornedRulerPanel::AdornedRulerPanel(), audacityAudioCallback(), AudioThread(), Scrubber::CanScrub(), ProjectAudioManager::CanStopAudioStream(), CaptureNotBusyFlag(), EffectUIHost::CleanupRealtime(), Scrubber::ContinueScrubbingPoll(), DefaultSpeedPlayOptions(), anonymous_namespace{SelectMenus.cpp}::DoBoundaryMove(), PlayIndicatorOverlayBase::DoGetRectangle(), Scrubber::DoKeyboardScrub(), ProjectAudioManager::DoPlayStopSelect(), ProjectAudioManager::DoRecord(), PlayIndicatorOverlayBase::Draw(), EffectPreview(), ControlToolBar::EnableDisableButtons(), TranscriptionToolBar::EnableDisableButtons(), ScrubbingPlaybackPolicy::GetPlaybackSlice(), UpdateManager::GetUpdates(), HistoryDialog::HistoryDialog(), EffectUIHost::InitializeInstance(), Scrubber::MaybeStartScrubbing(), ProjectManager::New(), RealtimeEffectListWindow::OnAddEffectClicked(), anonymous_namespace{LabelMenus.cpp}::OnAddLabelPlaying(), ProjectAudioManager::OnAudioIOStopRecording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::OnChangeButtonClicked(), ProjectManager::OnCloseWindow(), SelectActions::Handler::OnCursorPositionStore(), anonymous_namespace{PluginMenus.cpp}::OnDetectUpstreamDropouts(), TimeToolBar::OnIdle(), AudacityApp::OnKeyDown(), ProjectAudioManager::OnPause(), anonymous_namespace{TransportMenus.cpp}::OnPunchAndRoll(), SelectActions::Handler::OnSelectCursorStoredCursor(), SelectUtilities::OnSetRegion(), anonymous_namespace{PluginMenus.cpp}::OnSimulateRecordingErrors(), ProjectAudioManager::OnSoundActivationThreshold(), ProjectWindow::PlaybackScroller::OnTimer(), MixerBoard::OnTimer(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), anonymous_namespace{ViewMenus.cpp}::OnZoomIn(), TransportUtilities::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), TransportUtilities::PlayPlayRegionAndWait(), TransportUtilities::RecordAndWait(), ProjectAudioManager::Recording(), anonymous_namespace{RealtimeEffectPanel.cpp}::RealtimeEffectControl::RemoveFromList(), ScrubbingPlaybackPolicy::RepositionPlayback(), TimerRecordDialog::RunWaitDialog(), anonymous_namespace{SelectMenus.cpp}::SeekWhenAudioActive(), MeterPanel::SetMixer(), Scrubber::StartKeyboardScrubbing(), MeterPanel::StartMonitoring(), StartMonitoring(), ControlToolBar::StartScrolling(), ProjectAudioManager::Stop(), MeterPanel::StopMonitoring(), Scrubber::StopScrubbing(), anonymous_namespace{PluginMenus.cpp}::ToolsMenu(), TrackPanel::TrackPanel(), MeterPanel::UpdateSelectedPrefs(), and MeterPanel::UpdateSliderControl().

Here is the call graph for this function:

◆ GetBestRate()

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

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

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

Definition at line 1709 of file AudioIO.cpp.

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

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

523{ 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 1898 of file AudioIO.cpp.

1899{
1901}
static size_t MinValue(const RingBuffers &buffers, size_t(RingBuffer::*pmf)() const)
Definition: AudioIO.cpp:1871
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 1880 of file AudioIO.cpp.

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

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

Referenced by FillPlayBuffers().

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

◆ GetInputSourceNames()

wxArrayString AudioIO::GetInputSourceNames ( )

Get the list of inputs to the current mixer device.

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

Definition at line 432 of file AudioIO.cpp.

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

References wxT().

Here is the call graph for this function:

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 492 of file AudioIO.h.

492{ return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 400 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ GetNumCaptureChannels()

size_t AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 525 of file AudioIO.h.

525{ return mNumCaptureChannels; }

◆ GetNumPlaybackChannels()

size_t AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 524 of file AudioIO.h.

524{ return mNumPlaybackChannels; }

◆ GetOwningProject()

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

Definition at line 493 of file AudioIO.h.

494 { 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 1787 of file AudioIO.cpp.

1788{
1789 // Sequence time readout for the main thread
1790
1791 if( !IsStreamActive() )
1792 return BAD_STREAM_TIME;
1793
1795}
#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:10
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:83
static int getPlayDevIndex(const wxString &devName={})
get the index of the device selected in the preferences.
static wxString DeviceName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:76
static int getRecordDevIndex(const wxString &devName={})
get the index of the supplied (named) recording device, or the device selected in the preferences if ...
AudioIO()
Definition: AudioIO.cpp:232
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
virtual bool Flush() noexcept=0
virtual bool Read(const wxString &key, bool *value) const =0

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

Referenced by AudacityApp::InitPart2().

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

◆ InputMixerWorks()

bool AudioIO::InputMixerWorks ( )

Find out if the input hardware level control is available.

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

Definition at line 427 of file AudioIO.cpp.

428{
429 return mInputMixerWorks;
430}

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject project) const

Function to automatically set an acceptable volume.

Definition at line 1414 of file AudioIO.cpp.

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

References AudioIOBase::mOwningProject, and project.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 3398 of file AudioIO.cpp.

3399{
3400 // Includes a test of mTime, used in the main thread
3401 return IsStreamActive() &&
3402 GetNumCaptureChannels() > 0 &&
3405}
size_t GetNumCaptureChannels() const
Definition: AudioIO.h:525

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

Here is the call graph for this function:

◆ LastPaErrorString()

wxString AudioIO::LastPaErrorString ( )

Definition at line 754 of file AudioIO.cpp.

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

References AudioIoCallback::mLastPaError, and wxT().

Referenced by StartMonitoring().

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

◆ ProcessPlaybackSlices()

bool AudioIO::ProcessPlaybackSlices ( std::optional< RealtimeEffects::ProcessingScope > &  pScope,
size_t  available 
)
private

Definition at line 1992 of file AudioIO.cpp.

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

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

Referenced by FillPlayBuffers().

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

◆ RemoveState()

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

Forwards to RealtimeEffectManager::RemoveState with proper init scope.

Definition at line 369 of file AudioIO.cpp.

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

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

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

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

◆ ReplaceState()

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

Forwards to RealtimeEffectManager::ReplaceState with proper init scope.

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

Definition at line 358 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ ResetOwningProject()

void AudioIO::ResetOwningProject ( )
private

Definition at line 770 of file AudioIO.cpp.

771{
772 mOwningProject.reset();
773}

References AudioIOBase::mOwningProject.

Referenced by SetOwningProject(), StartPortAudioStream(), StopStream(), and ~AudioIO().

Here is the caller graph for this function:

◆ SeekStream()

void AudioIO::SeekStream ( double  seconds)
inline

Move the playback / recording position of the current stream by the specified amount from where it is now.

Definition at line 481 of file AudioIO.h.

481{ mSeek = seconds; }
double mSeek
Definition: AudioIO.h:284

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

1907{
1910}
void DrainRecordBuffers()
Second part of SequenceBufferExchange.
Definition: AudioIO.cpp:2250
void FillPlayBuffers()
First part of SequenceBufferExchange.
Definition: AudioIO.cpp:1912

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

382{
383 SetMixerOutputVol(playbackVolume);
384 AudioIOPlaybackVolume.Write(playbackVolume);
385
386#if defined(USE_PORTMIXER)
387 PxMixer *mixer = mPortMixer;
388 if( !mixer )
389 return;
390
391 float oldRecordVolume = Px_GetInputVolume(mixer);
392
393 AudioIoCallback::SetMixer(inputSource);
394 if( oldRecordVolume != recordVolume )
395 Px_SetInputVolume(mixer, recordVolume);
396
397#endif
398}
void SetMixer(int inputSource)

References AudioIOPlaybackVolume, AudioIOBase::SetMixer(), AudioIoCallback::SetMixerOutputVol(), and Setting< T >::Write().

Here is the call graph for this function:

◆ SetOwningProject()

void AudioIO::SetOwningProject ( const std::shared_ptr< AudacityProject > &  pProject)
private

Definition at line 759 of file AudioIO.cpp.

761{
762 if ( !mOwningProject.expired() ) {
763 wxASSERT(false);
765 }
766
767 mOwningProject = pProject;
768}

References AudioIOBase::mOwningProject, and ResetOwningProject().

Referenced by StartPortAudioStream().

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

◆ SetPaused()

void AudioIO::SetPaused ( bool  state,
bool  publish = false 
)

Pause and un-pause playback and recording.

Definition at line 1691 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ StartMonitoring()

void AudioIO::StartMonitoring ( const AudioIOStartStreamOptions options)

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

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

Definition at line 775 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

◆ StartPortAudioStream()

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

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

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

Definition at line 473 of file AudioIO.cpp.

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

Definition at line 825 of file AudioIO.cpp.

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

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

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

Here is the call graph for this function:

◆ StopStream()

void AudioIO::StopStream ( )
overridevirtual

Stop recording, playback or input monitoring.

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

Implements AudioIOBase.

Definition at line 1428 of file AudioIO.cpp.

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

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

Referenced by DrainRecordBuffers(), and StartStream().

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

◆ ValidateDeviceNames()

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

Ensure selected device names are valid.

Definition at line 223 of file AudioIO.cpp.

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

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

Here is the call graph for this function:

Member Data Documentation

◆ mDelayingActions

bool AudioIO::mDelayingActions { false }
private

Definition at line 653 of file AudioIO.h.

Referenced by DelayActions(), and DelayingActions().

◆ mPostRecordingAction

PostRecordingAction AudioIO::mPostRecordingAction
private

Definition at line 651 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().

◆ mPostRecordingActionMutex

std::mutex AudioIO::mPostRecordingActionMutex
private

Definition at line 650 of file AudioIO.h.

Referenced by CallAfterRecording(), and StopStream().


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