Audacity  3.0.3
Public Member Functions | Static Public Member Functions | Private Member Functions | Friends | 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 Member Functions

void StartMonitoring (const AudioIOStartStreamOptions &options)
 Start up Portaudio for capture and recording as needed for input monitoring and software playthrough only. More...
 
int StartStream (const TransportTracks &tracks, double t0, double t1, 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...
 
wxString LastPaErrorString ()
 
wxLongLong GetLastPlaybackTime () const
 
AudacityProjectGetOwningProject () const
 
void SetPaused (bool state)
 Pause and un-pause playback and recording. More...
 
void SetMixer (int inputSource, float inputVolume, float playbackVolume)
 
void GetMixer (int *inputSource, float *inputVolume, float *playbackVolume)
 
bool InputMixerWorks ()
 Find out if the input hardware level control is available. More...
 
bool OutputMixerEmulated ()
 Find out if the output level control is being emulated via software attenuation. More...
 
wxArrayString GetInputSourceNames ()
 Get the list of inputs to the current mixer device. More...
 
sampleFormat GetCaptureFormat ()
 
unsigned GetNumPlaybackChannels () const
 
unsigned GetNumCaptureChannels () const
 
bool IsCapturing () const
 
bool IsAvailable (AudacityProject *projecT) const
 Function to automatically set an acceptable volume. More...
 
double GetBestRate (bool capturing, bool playing, double sampleRate)
 Return a valid sample rate that is supported by the current I/O device(s). More...
 
- Public Member Functions inherited from AudioIoCallback
 AudioIoCallback ()
 
 ~AudioIoCallback ()
 
int AudioCallback (const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags, void *userData)
 
std::shared_ptr< AudioIOListenerGetListener () const
 
void SetListener (const std::shared_ptr< AudioIOListener > &listener)
 
int CallbackDoSeek ()
 
void CallbackCheckCompletion (int &callbackReturn, unsigned long len)
 
unsigned CountSoloingTracks ()
 
bool TrackShouldBeSilent (const WaveTrack &wt)
 
bool TrackHasBeenFadedOut (const WaveTrack &wt)
 
bool AllTracksAlreadySilent ()
 
void ComputeMidiTimings (const PaStreamCallbackTimeInfo *timeInfo, unsigned long framesPerBuffer)
 
void CheckSoundActivatedRecordingLevel (float *inputSamples, unsigned long framesPerBuffer)
 
void AddToOutputChannel (unsigned int chan, float *outputMeterFloats, float *outputFloats, float *tempBuf, bool drop, unsigned long len, WaveTrack *vt)
 
bool FillOutputBuffers (void *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void FillInputBuffers (const void *inputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackFlags statusFlags, float *tempFloats)
 
void UpdateTimePosition (unsigned long framesPerBuffer)
 
void DoPlaythrough (const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, float *outputMeterFloats)
 
void SendVuInputMeterData (float *inputSamples, unsigned long framesPerBuffer)
 
void SendVuOutputMeterData (float *outputMeterFloats, unsigned long framesPerBuffer)
 
size_t GetCommonlyReadyPlayback ()
 Get the number of audio samples ready in all of the playback buffers. More...
 
const std::vector< std::pair< double, double > > & LostCaptureIntervals ()
 
- Public Member Functions inherited from AudioIOBase
virtual ~AudioIOBase ()
 
void SetCaptureMeter (AudacityProject *project, MeterPanelBase *meter)
 
void SetPlaybackMeter (AudacityProject *project, MeterPanelBase *meter)
 
void HandleDeviceChange ()
 update state after changing what audio devices are selected More...
 
double GetStreamTime ()
 During playback, the track time most recently played. More...
 
wxString GetDeviceInfo ()
 Get diagnostic information on all the available audio I/O devices. More...
 
bool IsPaused () const
 Find out if playback / recording is currently paused. More...
 
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)
 

Static Public Member Functions

static AudioIOGet ()
 
static bool ValidateDeviceNames (const wxString &play, const wxString &rec)
 Ensure selected device names are valid. 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 SetMeters ()
 Set the current VU meters - this should be done once after each call to StartStream currently. More...
 
bool StartPortAudioStream (const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels, sampleFormat captureFormat)
 Opens the portaudio stream(s) used to do playback or recording (or both) through. More...
 
void FillBuffers ()
 
size_t GetCommonlyFreePlayback ()
 Get the number of audio samples free in all of the playback buffers. More...
 
size_t GetCommonlyAvailCapture ()
 Get the number of audio samples ready in all of the recording buffers. More...
 
bool AllocateBuffers (const AudioIOStartStreamOptions &options, const TransportTracks &tracks, double t0, double t1, double sampleRate, bool scrubbing)
 Allocate RingBuffer structures, and others, needed for playback and recording. More...
 
void StartStreamCleanup (bool bOnlyBuffers=false)
 Clean up after StartStream if it fails. More...
 

Friends

class AudioThread
 

Additional Inherited Members

- Public Attributes inherited from AudioIoCallback
int mbHasSoloTracks
 
int mCallbackReturn
 
std::unique_ptr< AudioThreadmThread
 
ArrayOf< std::unique_ptr< Resample > > mResample
 
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
 
WaveTrackArray mCaptureTracks
 
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
 
WaveTrackArray mPlaybackTracks
 
ArrayOf< std::unique_ptr< Mixer > > mPlaybackMixers
 
double mFactor
 
unsigned long mMaxFramesOutput
 
bool mbMicroFades
 
double mSeek
 
double mPlaybackRingBufferSecs
 
double mCaptureRingBufferSecs
 
size_t mPlaybackSamplesToCopy
 Preferred batch size for replenishing the playback RingBuffer. More...
 
size_t mPlaybackQueueMinimum
 Occupancy of the queue we try to maintain, with bigger batches if needed. More...
 
double mMinCaptureSecsToCopy
 
bool mSoftwarePlaythrough
 
bool mPauseRec
 True if Sound Activated Recording is enabled. More...
 
float mSilenceLevel
 
unsigned int mNumCaptureChannels
 
unsigned int mNumPlaybackChannels
 
sampleFormat mCaptureFormat
 
unsigned long long mLostSamples { 0 }
 
volatile bool mAudioThreadShouldCallFillBuffersOnce
 
volatile bool mAudioThreadFillBuffersLoopRunning
 
volatile bool mAudioThreadFillBuffersLoopActive
 
wxLongLong mLastPlaybackTimeMillis
 
volatile double mLastRecordingOffset
 
PaError mLastPaError
 
bool mSimulateRecordingErrors { false }
 
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...
 
- Protected Member Functions inherited from AudioIoCallback
void SetRecordingException ()
 
void ClearRecordingException ()
 
- 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
bool mUpdateMeters
 
volatile bool mUpdatingMeters
 
std::weak_ptr< AudioIOListenermListener
 
bool mUsingAlsa { false }
 
wxMutex mSuspendAudioThread
 
wxAtomicInt mRecordingException {}
 
std::vector< std::pair< double, double > > mLostCaptureIntervals
 
bool mDetectDropouts { true }
 
RecordingSchedule mRecordingSchedule {}
 
struct AudioIoCallback::TimeQueue mTimeQueue
 
- Protected Attributes inherited from AudioIOBase
AudacityProjectmOwningProject
 
bool mPaused
 True if audio playback is paused. More...
 
bool mMidiOutputComplete { true }
 True when output reaches mT1. More...
 
bool mMidiStreamActive
 mMidiStreamActive tells when mMidiStream is open for output More...
 
volatile int mStreamToken
 
double mRate
 Audio playback rate in samples per second. More...
 
PaStreammPortStreamV19
 
wxWeakRef< MeterPanelBasemInputMeter {}
 
wxWeakRef< MeterPanelBasemOutputMeter {}
 
bool mEmulateMixerOutputVol
 
bool mInputMixerWorks
 Can we control the hardware input level? More...
 
float mMixerOutputVol
 
struct AudioIOBase::PlaybackSchedule mPlaybackSchedule
 
- 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.

EXPERIMENTAL_MIDI_OUT
If EXPERIMENTAL_MIDI_OUT is defined, this class also manages MIDI playback. The reason for putting MIDI here rather than in, say, class MidiIO, is that there is no high-level synchronization and transport architecture, so Audio and MIDI must be coupled in order to start/stop/pause and synchronize them.
MIDI With Audio
When Audio and MIDI play simultaneously, MIDI synchronizes to Audio. This is necessary because the Audio sample clock is not the same hardware as the system time used to schedule MIDI messages. MIDI is synchronized to Audio because it is simple to pause or rush the dispatch of MIDI messages, but generally impossible to pause or rush synchronous audio samples (without distortion).
MIDI output is driven by yet another thread. In principle, we could output timestamped MIDI data at the same time we fill audio buffers from disk, but audio buffers are filled far in advance of playback time, and there is a lower latency thread (PortAudio's callback) that actually sends samples to the output device. The relatively low latency to the output device allows Audacity to stop audio output quickly. We want the same behavior for MIDI, but there is not periodic callback from PortMidi (because MIDI is asynchronous), so this function is performed by the MidiThread class.
When Audio is running, MIDI is synchronized to Audio. Globals are set in the Audio callback (audacityAudioCallback) for use by a time function that reports milliseconds to PortMidi. (Details below.)
MIDI Without Audio
When Audio is not running, PortMidi uses its own millisecond timer since there is no audio to synchronize to. (Details below.)
Implementation Notes and Details for MIDI
When opening devices, successAudio and successMidi indicate errors if false, so normally both are true. Use playbackChannels, captureChannels and mMidiPlaybackTracks.empty() to determine if Audio or MIDI is actually in use.
Audio Time
Normally, the current time during playback is given by the variable mTime. mTime normally advances by frames / samplerate each time an audio buffer is output by the audio callback. However, Audacity has a speed control that can perform continuously variable time stretching on audio. This is achieved in two places: the playback "mixer" that generates the samples for output processes the audio according to the speed control. In a separate algorithm, the audio callback updates mTime by (frames / samplerate) * factor, where factor reflects the speed at mTime. This effectively integrates speed to get position. Negative speeds are allowed too, for instance in scrubbing.
The Big Picture
Sample
Time (in seconds, = total_sample_count / sample_rate)
  ^
  |                                             /         /
  |             y=x-mSystemTimeMinusAudioTime /         /
  |                                         /     #   /
  |                                       /         /
  |                                     /   # <- callbacks (#) showing
  |                                   /#        /   lots of timing jitter.
  |       top line is "full buffer" /         /     Some are later,
  |                     condition /         /       indicating buffer is
  |                             /         /         getting low. Plot
  |                           /     #   /           shows sample time
  |                         /    #    /             (based on how many
  |                       /    #    /               samples previously
  |                     /         /                 *written*) vs. real
  |                   / #       /                   time.
  |                 /<------->/ audio latency
  |               /#       v/
  |             /         / bottom line is "empty buffer"
  |           /   #     /      condition = DAC output time =
  |         /         /
  |       /      # <-- rapid callbacks as buffer is filled
  |     /         /
0 +...+---------#---------------------------------------------------->
  0 ^ |         |                                            real time
    | |         first callback time
    | mSystemMinusAudioTime
    |
    Probably the actual real times shown in this graph are very large
    in practice (> 350,000 sec.), so the X "origin" might be when
    the computer was booted or 1970 or something.

To estimate the true DAC time (needed to synchronize MIDI), we need a mapping from track time to DAC time. The estimate is the theoretical time of the full buffer (top diagonal line) + audio latency. To estimate the top diagonal line, we "draw" the line to be at least as high as any sample time corresponding to a callback (#), and we slowly lower the line in case the sample clock is slow or the system clock is fast, preventing the estimated line from drifting too far from the actual callback observations. The line is occasionally "bumped" up by new callback observations, but continuously "lowered" at a very low rate. All adjustment is accomplished by changing mSystemMinusAudioTime, shown here as the X-intercept.
theoreticalFullBufferTime = realTime - mSystemMinusAudioTime
To estimate audio latency, notice that the first callback happens on an empty buffer, but the buffer soon fills up. This will cause a rapid re-estimation of mSystemMinusAudioTime. (The first estimate of mSystemMinusAudioTime will simply be the real time of the first callback time.) By watching these changes, which happen within ms of starting, we can estimate the buffer size and thus audio latency. So, to map from track time to real time, we compute:
DACoutputTime = trackTime + mSystemMinusAudioTime
There are some additional details to avoid counting samples while paused or while waiting for initialization, MIDI latency, etc. Also, in the code, track time is measured with respect to the track origin, so there's an extra term to add (mT0) if you start somewhere in the middle of the track. Finally, when a callback occurs, you might expect there is room in the output buffer for the requested frames, so maybe the "full buffer" sample time should be based not on the first sample of the callback, but the last sample time + 1 sample. I suspect, at least on Linux, that the callback occurs as soon as the last callback completes, so the buffer is really full, and the callback thread is going to block waiting for space in the output buffer.

Midi Time
MIDI is not warped according to the speed control. This might be something that should be changed. (Editorial note: Wouldn't it make more sense to display audio at the correct time and allow users to stretch audio the way they can stretch MIDI?) For now, MIDI plays at 1 second per second, so it requires an unwarped clock. In fact, MIDI time synchronization requires a millisecond clock that does not pause. Note that mTime will stop progress when the Pause button is pressed, even though audio samples (zeros) continue to be output.
Therefore, we define the following interface for MIDI timing:
  • AudioTime() is the time based on all samples written so far, including zeros output during pauses. AudioTime() is based on the start location mT0, not zero.
  • PauseTime() is the amount of time spent paused, based on a count of zero-padding samples output.
  • MidiTime() is an estimate in milliseconds of the current audio output time + 1s. In other words, what audacity track time corresponds to the audio (plus pause insertions) at the DAC output?
AudioTime() and PauseTime() computation
AudioTime() is simply mT0 + mNumFrames / mRate. mNumFrames is incremented in each audio callback. Similarly, PauseTime() is mNumPauseFrames / mRate. mNumPauseFrames is also incremented in each audio callback when a pause is in effect or audio output is ready to start.
MidiTime() computation
MidiTime() is computed based on information from PortAudio's callback, which estimates the system time at which the current audio buffer will be output. Consider the (unimplemented) function RealToTrack() that maps real audio write time to track time. If writeTime is the system time for the first sample of the current output buffer, and if we are in the callback, so AudioTime() also refers to the first sample of the buffer, then
RealToTrack(writeTime) = AudioTime() - PauseTime()
We want to know RealToTrack of the current time (when we are not in the callback, so we use this approximation for small d:
RealToTrack(t + d) = RealToTrack(t) + d, or
Letting t = writeTime and d = (systemTime - writeTime), we can substitute to get:
RealToTrack(systemTime) = RealToTrack(writeTime) + systemTime - writeTime
= AudioTime() - PauseTime() + (systemTime - writeTime)
MidiTime() should include pause time, so that it increases smoothly, and audioLatency so that MidiTime() corresponds to the time of audio output rather than audio write times. Also MidiTime() is offset by 1 second to avoid negative time at startup, so add 1:
MidiTime(systemTime) in seconds
= RealToTrack(systemTime) + PauseTime() - audioLatency + 1
= AudioTime() + (systemTime - writeTime) - audioLatency + 1
(Note that audioLatency is called mAudioOutLatency in the code.) When we schedule a MIDI event with track time TT, we need to map TT to a PortMidi timestamp. The PortMidi timestamp is exactly MidiTime(systemTime) in ms units, and
MidiTime(x) = RealToTrack(x) + PauseTime() + 1, so
timestamp = TT + PauseTime() + 1 - midiLatency
Note 1: The timestamp is incremented by the PortMidi stream latency (midiLatency) so we subtract midiLatency here for the timestamp passed to PortMidi.
Note 2: Here, we're setting x to the time at which RealToTrack(x) = TT, so then MidiTime(x) is the desired timestamp. To be completely correct, we should assume that MidiTime(x + d) = MidiTime(x) + d, and consider that we compute MidiTime(systemTime) based on the current* system time, but we really want the MidiTime(x) for some future time corresponding when MidiTime(x) = TT.)
Also, we should assume PortMidi was opened with mMidiLatency, and that MIDI messages become sound with a delay of mSynthLatency. Therefore, the final timestamp calculation is:
timestamp = TT + PauseTime() + 1 - (mMidiLatency + mSynthLatency)
(All units here are seconds; some conversion is needed in the code.)
The difference AudioTime() - PauseTime() is the time "cursor" for MIDI. When the speed control is used, MIDI and Audio will become unsynchronized. In particular, MIDI will not be synchronized with the visual cursor, which moves with scaled time reported in mTime.
Timing in Linux
It seems we cannot get much info from Linux. We can read the time when we get a callback, and we get a variable frame count (it changes from one callback to the next). Returning to the RealToTrack() equations above:
RealToTrack(outputTime) = AudioTime() - PauseTime() - bufferDuration
where outputTime should be PortAudio's estimate for the most recent output buffer, but at least on my Dell Latitude E7450, PortAudio is getting zero from ALSA, so we need to find a proxy for this.
Estimating outputTime (Plan A, assuming double-buffered, fixed-size buffers, please skip to Plan B)
One can expect the audio callback to happen as soon as there is room in the output for another block of samples, so we could just measure system time at the top of the callback. Then we could add the maximum delay buffered in the system. E.g. if there is simple double buffering and the callback is computing one of the buffers, the callback happens just as one of the buffers empties, meaning the other buffer is full, so we have exactly one buffer delay before the next computed sample is output.

If computation falls behind a bit, the callback will be later, so the delay to play the next computed sample will be less. I think a reasonable way to estimate the actual output time is to assume that the computer is mostly keeping up and that most callbacks will occur immediately when there is space. Note that the most likely reason for the high-priority audio thread to fall behind is the callback itself, but the start of the callback should be pretty consistently keeping up.

Also, we do not have to have a perfect estimate of the time. Suppose we estimate a linear mapping from sample count to system time by saying that the sample count maps to the system time at the most recent callback, and set the slope to 1% slower than real time (as if the sample clock is slow). Now, at each callback, if the callback seems to occur earlier than expected, we can adjust the mapping to be earlier. The earlier the callback, the more accurate it must be. On the other hand, if the callback is later than predicted, it must be a delayed callback (or else the sample clock is more than 1% slow, which is really a hardware problem.) How bad can this be? Assuming callbacks every 30ms (this seems to be what I'm observing in a default setup), you'll be a maximum of 1ms off even if 2 out of 3 callbacks are late. This is pretty reasonable given that PortMIDI clock precision is 1ms. If buffers are larger and callback timing is more erratic, errors will be larger, but even a few ms error is probably OK.

Estimating outputTime (Plan B, variable framesPerBuffer in callback, please skip to Plan C)
ALSA is complicated because we get varying values of framesPerBuffer from callback to callback. Assume you get more frames when the callback is later (because there is more accumulated input to deliver and more more accumulated room in the output buffers). So take the current time and subtract the duration of the frame count in the current callback. This should be a time position that is relatively jitter free (because we estimated the lateness by frame count and subtracted that out). This time position intuitively represents the current ADC time, or if no input, the time of the tail of the output buffer. If we wanted DAC time, we'd have to add the total output buffer duration, which should be reported by PortAudio. (If PortAudio is wrong, we'll be systematically shifted in time by the error.)

Since there is still bound to be jitter, we can smooth these estimates. First, we will assume a linear mapping from system time to audio time with slope = 1, so really it's just the offset we need, which is going to be a double that we can read/write atomically without locks or anything fancy. (Maybe it should be "volatile".)

To improve the estimate, we get a new offset every callback, so we can create a "smooth" offset by using a simple regression model (also this could be seen as a first order filter). The following formula updates smooth_offset with a new offset estimate in the callback: smooth_offset = smooth_offset * 0.9 + new_offset_estimate * 0.1 Since this is smooth, we'll have to be careful to give it a good initial value to avoid a long convergence.

Estimating outputTime (Plan C)
ALSA is complicated because we get varying values of framesPerBuffer from callback to callback. It seems there is a lot of variation in callback times and buffer space. One solution would be to go to fixed size double buffer, but Audacity seems to work better as is, so Plan C is to rely on one invariant which is that the output buffer cannot overflow, so there's a limit to how far ahead of the DAC time we can be writing samples into the buffer. Therefore, we'll assume that the audio clock runs slow by about 0.2% and we'll assume we're computing at that rate. If the actual output position is ever ahead of the computed position, we'll increase the computed position to the actual position. Thus whenever the buffer is less than near full, we'll stay ahead of DAC time, falling back at a rate of about 0.2% until eventually there's another near-full buffer callback that will push the time back ahead.
Interaction between MIDI, Audio, and Pause
When Pause is used, PauseTime() will increase at the same rate as AudioTime(), and no more events will be output. Because of the time advance of mAudioOutputLatency + MIDI_SLEEP + latency and the fact that AudioTime() advances stepwise by mAudioBufferDuration, some extra MIDI might be output, but the same is true of audio: something like mAudioOutputLatency audio samples will be in the output buffer (with up to mAudioBufferDuration additional samples, depending on when the Pause takes effect). When playback is resumed, there will be a slight delay corresponding to the extra data previously sent. Again, the same is true of audio. Audio and MIDI will not pause and resume at exactly the same times, but their pause and resume times will be within the low tens of milliseconds, and the streams will be synchronized in any case. I.e. if audio pauses 10ms earlier than MIDI, it will resume 10ms earlier as well.
PortMidi Latency Parameter
PortMidi has a "latency" parameter that is added to all timestamps. This value must be greater than zero to enable timestamp-based timing, but serves no other function, so we will set it to 1. All timestamps must then be adjusted down by 1 before messages are sent. This adjustment is on top of all the calculations described above. It just seem too complicated to describe everything in complete detail in one place.
Midi with a time track
When a variable-speed time track is present, MIDI events are output with the times used by the time track (rather than the raw times). This ensures MIDI is synchronized with audio.
Midi While Recording Only or Without Audio Playback
To reduce duplicate code and to ensure recording is synchronised with MIDI, a portaudio stream will always be used, even when there is no actual audio output. For recording, this ensures that the recorded audio will by synchronized with the MIDI (otherwise, it gets out-of- sync if played back with correct timing).
NoteTrack PlayLooped
When mPlayLooped is true, output is supposed to loop from mT0 to mT1. For NoteTracks, we interpret this to mean that any note-on or control change in the range mT0 <= t < mT1 is sent (notes that start before mT0 are not played even if they extend beyond mT0). Then, all notes are turned off. Events in the range mT0 <= t < mT1 are then repeated, offset by (mT1 - mT0), etc. We do NOT go back to the beginning and play all control changes (update events) up to mT0, nor do we "undo" any state changes between mT0 and mT1.
NoteTrack PlayLooped Implementation
The mIterator object (an Alg_iterator) returns NULL when there are no more events scheduled before mT1. At mT1, we want to output all notes off messages, but the FillMidiBuffers() loop will exit if mNextEvent is NULL, so we create a "fake" mNextEvent for this special "event" of sending all notes off. After that, we destroy the iterator and use PrepareMidiIterator() to set up a NEW one. At each iteration, time must advance by (mT1 - mT0), so the accumulated complete loop time (in "unwarped," track time) is computed by MidiLoopOffset().
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 587 of file AudioIO.h.

Constructor & Destructor Documentation

◆ AudioIO()

AudioIO::AudioIO ( )
private

Definition at line 957 of file AudioIO.cpp.

958 {
959  if (!std::atomic<double>{}.is_lock_free()) {
960  // If this check fails, then the atomic<double> members in AudioIO.h
961  // might be changed to atomic<float> to be more efficient with some
962  // loss of precision. That could be conditionally compiled depending
963  // on the platform.
964  wxASSERT(false);
965  }
966 
967  // This ASSERT because of casting in the callback
968  // functions where we cast a tempFloats buffer to a (short*) buffer.
969  // We have to ASSERT in the GUI thread, if we are to see it properly.
970  wxASSERT( sizeof( short ) <= sizeof( float ));
971 
975  mPortStreamV19 = NULL;
976 
977 #ifdef EXPERIMENTAL_MIDI_OUT
978  mMidiStream = NULL;
979  mMidiThreadFillBuffersLoopRunning = false;
980  mMidiThreadFillBuffersLoopActive = false;
981  mMidiStreamActive = false;
982  mSendMidiState = false;
983  mIterator = NULL;
984 
985  mNumFrames = 0;
986  mNumPauseFrames = 0;
987 #endif
988 
989 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
990  mAILAActive = false;
991 #endif
992  mStreamToken = 0;
993 
994  mLastPaError = paNoError;
995 
996  mLastRecordingOffset = 0.0;
998  mPaused = false;
999  mSilenceLevel = 0.0;
1000 
1001  mUpdateMeters = false;
1002  mUpdatingMeters = false;
1003 
1004  mOwningProject = NULL;
1005  mOutputMeter.Release();
1006 
1007  PaError err = Pa_Initialize();
1008 
1009  if (err != paNoError) {
1010  auto errStr = XO("Could not find any audio devices.\n");
1011  errStr += XO("You will not be able to play or record audio.\n\n");
1012  wxString paErrStr = LAT1CTOWX(Pa_GetErrorText(err));
1013  if (!paErrStr.empty())
1014  errStr += XO("Error: %s").Format( paErrStr );
1015  // XXX: we are in libaudacity, popping up dialogs not allowed! A
1016  // long-term solution will probably involve exceptions
1018  errStr,
1019  XO("Error Initializing Audio"),
1020  wxICON_ERROR|wxOK);
1021 
1022  // Since PortAudio is not initialized, all calls to PortAudio
1023  // functions will fail. This will give reasonable behavior, since
1024  // the user will be able to do things not relating to audio i/o,
1025  // but any attempt to play or record will simply fail.
1026  }
1027 
1028 #ifdef EXPERIMENTAL_MIDI_OUT
1029  PmError pmErr = Pm_Initialize();
1030 
1031  if (pmErr != pmNoError) {
1032  auto errStr =
1033  XO("There was an error initializing the midi i/o layer.\n");
1034  errStr += XO("You will not be able to play midi.\n\n");
1035  wxString pmErrStr = LAT1CTOWX(Pm_GetErrorText(pmErr));
1036  if (!pmErrStr.empty())
1037  errStr += XO("Error: %s").Format( pmErrStr );
1038  // XXX: we are in libaudacity, popping up dialogs not allowed! A
1039  // long-term solution will probably involve exceptions
1041  errStr,
1042  XO("Error Initializing Midi"),
1043  wxICON_ERROR|wxOK);
1044 
1045  // Same logic for PortMidi as described above for PortAudio
1046  }
1047 
1048 #ifdef USE_MIDI_THREAD
1049  mMidiThread = std::make_unique<MidiThread>();
1050  mMidiThread->Create();
1051 #endif
1052 
1053 #endif
1054 
1055  // Start thread
1056  mThread = std::make_unique<AudioThread>();
1057  mThread->Create();
1058 
1059 #if defined(USE_PORTMIXER)
1060  mPortMixer = NULL;
1061  mPreviousHWPlaythrough = -1.0;
1063 #else
1064  mEmulateMixerOutputVol = true;
1065  mMixerOutputVol = 1.0;
1066  mInputMixerWorks = false;
1067 #endif
1068 
1070 
1071 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
1072  mScrubState = NULL;
1073  mScrubDuration = 0;
1074  mSilentScrub = false;
1075 #endif
1076 }

References AudacityMessageBox(), AudioIOBase::HandleDeviceChange(), LAT1CTOWX, AudioIoCallback::mAudioThreadFillBuffersLoopActive, AudioIoCallback::mAudioThreadFillBuffersLoopRunning, AudioIoCallback::mAudioThreadShouldCallFillBuffersOnce, AudioIOBase::mEmulateMixerOutputVol, AudioIOBase::mInputMixerWorks, AudioIoCallback::mLastPaError, AudioIoCallback::mLastPlaybackTimeMillis, AudioIoCallback::mLastRecordingOffset, AudioIOBase::mMidiStreamActive, AudioIOBase::mMixerOutputVol, AudioIoCallback::mNumCaptureChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIOBase::mPaused, AudioIOBase::mPortStreamV19, AudioIoCallback::mSilenceLevel, AudioIOBase::mStreamToken, AudioIoCallback::mThread, AudioIoCallback::mUpdateMeters, AudioIoCallback::mUpdatingMeters, 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 1078 of file AudioIO.cpp.

1079 {
1080 #if defined(USE_PORTMIXER)
1081  if (mPortMixer) {
1082  #if __WXMAC__
1083  if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
1084  Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
1085  mPreviousHWPlaythrough = -1.0;
1086  #endif
1087  Px_CloseMixer(mPortMixer);
1088  mPortMixer = NULL;
1089  }
1090 #endif
1091 
1092  // FIXME: ? TRAP_ERR. Pa_Terminate probably OK if err without reporting.
1093  Pa_Terminate();
1094 
1095 #ifdef EXPERIMENTAL_MIDI_OUT
1096  Pm_Terminate();
1097 
1098  /* Delete is a "graceful" way to stop the thread.
1099  (Kill is the not-graceful way.) */
1100 
1101 #ifdef USE_MIDI_THREAD
1102  mMidiThread->Delete();
1103  mMidiThread.reset();
1104 #endif
1105 
1106 #endif
1107 
1108  /* Delete is a "graceful" way to stop the thread.
1109  (Kill is the not-graceful way.) */
1110 
1111  // This causes reentrancy issues during application shutdown
1112  // wxTheApp->Yield();
1113 
1114  mThread->Delete();
1115  mThread.reset();
1116 }

References AudioIoCallback::mThread.

Member Function Documentation

◆ AllocateBuffers()

bool AudioIO::AllocateBuffers ( const AudioIOStartStreamOptions options,
const TransportTracks tracks,
double  t0,
double  t1,
double  sampleRate,
bool  scrubbing 
)
private

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

Returns true iff successful.

Definition at line 1830 of file AudioIO.cpp.

1834 {
1835  bool success = false;
1836  auto cleanup = finally([&]{
1837  if (!success) StartStreamCleanup( false );
1838  });
1839 
1840  //
1841  // The (audio) stream has been opened successfully (assuming we tried
1842  // to open it). We now proceed to
1843  // allocate the memory structures the stream will need.
1844  //
1845 
1846  //
1847  // The RingBuffer sizes, and the max amount of the buffer to
1848  // fill at a time, both grow linearly with the number of
1849  // tracks. This allows us to scale up to many tracks without
1850  // killing performance.
1851  //
1852 
1853  // real playback time to produce with each filling of the buffers
1854  // by the Audio thread (except at the end of playback):
1855  // usually, make fillings fewer and longer for less CPU usage.
1856  // But for useful scrubbing, we can't run too far ahead without checking
1857  // mouse input, so make fillings more and shorter.
1858  // What Audio thread produces for playback is then consumed by the PortAudio
1859  // thread, in many smaller pieces.
1860  double playbackTime = 4.0;
1861  if (scrubbing)
1862  // Specify a very short minimum batch for non-seek scrubbing, to allow
1863  // more frequent polling of the mouse
1864  playbackTime =
1865  lrint(options.pScrubbingOptions->delay * mRate) / mRate;
1866 
1867  wxASSERT( playbackTime >= 0 );
1868  mPlaybackSamplesToCopy = playbackTime * mRate;
1869 
1870  // Capacity of the playback buffer.
1871  mPlaybackRingBufferSecs = 10.0;
1872 
1874  4.5 + 0.5 * std::min(size_t(16), mCaptureTracks.size());
1876  0.2 + 0.2 * std::min(size_t(16), mCaptureTracks.size());
1877 
1878  mTimeQueue.mHead = {};
1879  mTimeQueue.mTail = {};
1880  bool bDone;
1881  do
1882  {
1883  bDone = true; // assume success
1884  try
1885  {
1886  if( mNumPlaybackChannels > 0 ) {
1887  // Allocate output buffers. For every output track we allocate
1888  // a ring buffer of ten seconds
1889  auto playbackBufferSize =
1890  (size_t)lrint(mRate * mPlaybackRingBufferSecs);
1891 
1894 
1895  const Mixer::WarpOptions &warpOptions =
1896 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
1897  scrubbing
1901  :
1902 #endif
1903  Mixer::WarpOptions(mPlaybackSchedule.mEnvelope);
1904 
1906  if (scrubbing)
1907  // Specify enough playback RingBuffer latency so we can refill
1908  // once every seek stutter without falling behind the demand.
1909  // (Scrub might switch in and out of seeking with left mouse
1910  // presses in the ruler)
1912  2 * options.pScrubbingOptions->minStutterTime * mRate );
1914  std::min( mPlaybackQueueMinimum, playbackBufferSize );
1915 
1916  for (unsigned int i = 0; i < mPlaybackTracks.size(); i++)
1917  {
1918  // Bug 1763 - We must fade in from zero to avoid a click on starting.
1919  mPlaybackTracks[i]->SetOldChannelGain(0, 0.0);
1920  mPlaybackTracks[i]->SetOldChannelGain(1, 0.0);
1921 
1922  mPlaybackBuffers[i] =
1923  std::make_unique<RingBuffer>(floatSample, playbackBufferSize);
1924  const auto timeQueueSize = 1 +
1925  (playbackBufferSize + TimeQueueGrainSize - 1)
1927  mTimeQueue.mData.reinit( timeQueueSize );
1928  mTimeQueue.mSize = timeQueueSize;
1929 
1930  // use track time for the end time, not real time!
1931  WaveTrackConstArray mixTracks;
1932  mixTracks.push_back(mPlaybackTracks[i]);
1933 
1934  double endTime;
1936  .contains(mPlaybackTracks[i]))
1937  // Stop playing this track after pre-roll
1938  endTime = t0;
1939  else
1940  // Pass t1 -- not mT1 as may have been adjusted for latency
1941  // -- so that overdub recording stops playing back samples
1942  // at the right time, though transport may continue to record
1943  endTime = t1;
1944 
1945  mPlaybackMixers[i] = std::make_unique<Mixer>
1946  (mixTracks,
1947  // Don't throw for read errors, just play silence:
1948  false,
1949  warpOptions,
1951  endTime,
1952  1,
1954  false,
1955  mRate, floatSample,
1956  false, // low quality dithering and resampling
1957  nullptr,
1958  false // don't apply track gains
1959  );
1960  }
1961  }
1962 
1963  if( mNumCaptureChannels > 0 )
1964  {
1965  // Allocate input buffers. For every input track we allocate
1966  // a ring buffer of five seconds
1967  auto captureBufferSize =
1968  (size_t)(mRate * mCaptureRingBufferSecs + 0.5);
1969 
1970  // In the extraordinarily rare case that we can't even afford
1971  // 100 samples, just give up.
1972  if(captureBufferSize < 100)
1973  {
1974  AudacityMessageBox( XO("Out of memory!") );
1975  return false;
1976  }
1977 
1980  mFactor = sampleRate / mRate;
1981 
1982  for( unsigned int i = 0; i < mCaptureTracks.size(); i++ )
1983  {
1984  mCaptureBuffers[i] = std::make_unique<RingBuffer>(
1985  mCaptureTracks[i]->GetSampleFormat(), captureBufferSize );
1986  mResample[i] =
1987  std::make_unique<Resample>(true, mFactor, mFactor);
1988  // constant rate resampling
1989  }
1990  }
1991  }
1992  catch(std::bad_alloc&)
1993  {
1994  // Oops! Ran out of memory. This is pretty rare, so we'll just
1995  // try deleting everything, halving our buffer size, and try again.
1996  StartStreamCleanup(true);
1997  mPlaybackRingBufferSecs *= 0.5;
1999  mCaptureRingBufferSecs *= 0.5;
2000  mMinCaptureSecsToCopy *= 0.5;
2001  bDone = false;
2002 
2003  // In the extraordinarily rare case that we can't even afford 100
2004  // samples, just give up.
2005  auto playbackBufferSize =
2006  (size_t)lrint(mRate * mPlaybackRingBufferSecs);
2007  if(playbackBufferSize < 100 || mPlaybackSamplesToCopy < 100)
2008  {
2009  AudacityMessageBox( XO("Out of memory!") );
2010  return false;
2011  }
2012  }
2013  } while(!bDone);
2014 
2015  success = true;
2016  return true;
2017 }

References AudacityMessageBox(), floatSample, lrint, make_iterator_range(), ScrubbingOptions::MaxAllowedScrubSpeed(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureRingBufferSecs, AudioIoCallback::mCaptureTracks, AudioIoCallback::TimeQueue::mData, AudioIOBase::PlaybackSchedule::mEnvelope, AudioIoCallback::mFactor, AudioIoCallback::TimeQueue::mHead, min(), ScrubbingOptions::MinAllowedScrubSpeed(), AudioIoCallback::mMinCaptureSecsToCopy, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackRingBufferSecs, AudioIoCallback::mPlaybackSamplesToCopy, AudioIOBase::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mRate, AudioIoCallback::mResample, AudioIoCallback::TimeQueue::mSize, AudioIOBase::PlaybackSchedule::mT0, AudioIoCallback::TimeQueue::mTail, AudioIoCallback::mTimeQueue, TransportTracks::prerollTracks, ArrayOf< X >::reinit(), StartStreamCleanup(), TimeQueueGrainSize, and XO.

Referenced by StartStream().

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

◆ Deinit()

void AudioIO::Deinit ( )
static

Definition at line 943 of file AudioIO.cpp.

944 {
945  ugAudioIO.reset();
946 }

References AudioIOBase::ugAudioIO.

Referenced by AudacityApp::OnExit().

Here is the caller graph for this function:

◆ FillBuffers()

void AudioIO::FillBuffers ( )
private

Definition at line 2688 of file AudioIO.cpp.

2689 {
2690  unsigned int i;
2691 
2692  auto delayedHandler = [this] ( AudacityException * pException ) {
2693  // In the main thread, stop recording
2694  // This is one place where the application handles disk
2695  // exhaustion exceptions from wave track operations, without rolling
2696  // back to the last pushed undo state. Instead, partial recording
2697  // results are pushed as a NEW undo state. For this reason, as
2698  // commented elsewhere, we want an exception safety guarantee for
2699  // the output wave tracks, after the failed append operation, that
2700  // the tracks remain as they were after the previous successful
2701  // (block-level) appends.
2702 
2703  // Note that the Flush in StopStream() may throw another exception,
2704  // but StopStream() contains that exception, and the logic in
2705  // AudacityException::DelayedHandlerAction prevents redundant message
2706  // boxes.
2707  StopStream();
2708  DefaultDelayedHandlerAction{}( pException );
2709  };
2710 
2711  if (mPlaybackTracks.size() > 0)
2712  {
2713  // Though extremely unlikely, it is possible that some buffers
2714  // will have more samples available than others. This could happen
2715  // if we hit this code during the PortAudio callback. To keep
2716  // things simple, we only write as much data as is vacant in
2717  // ALL buffers, and advance the global time by that much.
2718  auto nAvailable = GetCommonlyFreePlayback();
2719 
2720  //
2721  // Don't fill the buffers at all unless we can do the
2722  // full mMaxPlaybackSecsToCopy. This improves performance
2723  // by not always trying to process tiny chunks, eating the
2724  // CPU unnecessarily.
2725  //
2726  // The exception is if we're at the end of the selected
2727  // region - then we should just fill the buffer.
2728  //
2729  // May produce a larger amount when initially priming the buffer, or
2730  // perhaps again later in play to avoid underfilling the queue and falling
2731  // behind the real-time demand on the consumer side in the callback.
2732  auto nReady = GetCommonlyReadyPlayback();
2733  auto nNeeded =
2735 
2736  // wxASSERT( nNeeded <= nAvailable );
2737 
2738  auto realTimeRemaining = mPlaybackSchedule.RealTimeRemaining();
2739  if (nAvailable >= mPlaybackSamplesToCopy ||
2741  nAvailable / mRate >= realTimeRemaining))
2742  {
2743  // Limit maximum buffer size (increases performance)
2744  auto available = std::min( nAvailable,
2745  std::max( nNeeded, mPlaybackSamplesToCopy ) );
2746 
2747  // msmeyer: When playing a very short selection in looped
2748  // mode, the selection must be copied to the buffer multiple
2749  // times, to ensure, that the buffer has a reasonable size
2750  // This is the purpose of this loop.
2751  // PRL: or, when scrubbing, we may get work repeatedly from the
2752  // user interface.
2753  bool done = false;
2754  do {
2755  // How many samples to produce for each channel.
2756  auto frames = available;
2757  bool progress = true;
2758  auto toProcess = frames;
2759 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
2761  // scrubbing and play-at-speed are not limited by the real time
2762  // and length accumulators
2763  toProcess =
2764  frames = limitSampleBufferSize(frames, mScrubDuration);
2765  else
2766 #endif
2767  {
2768  double deltat = frames / mRate;
2769  if (deltat > realTimeRemaining)
2770  {
2771  frames = realTimeRemaining * mRate;
2772  toProcess = frames;
2773  // Don't fall into an infinite loop, if loop-playing a selection
2774  // that is so short, it has no samples: detect that case
2775  progress =
2776  !(mPlaybackSchedule.Looping() &&
2777  mPlaybackSchedule.mWarpedTime == 0.0 && frames == 0);
2778  mPlaybackSchedule.RealTimeAdvance( realTimeRemaining );
2779  }
2780  else
2782  realTimeRemaining = mPlaybackSchedule.RealTimeRemaining();
2783  }
2784 
2785  if (!progress)
2786  frames = available, toProcess = 0;
2787 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
2788  else if ( mPlaybackSchedule.Interactive() && mSilentScrub)
2789  toProcess = 0;
2790 #endif
2791 
2792  // Update the time queue. This must be done before writing to the
2793  // ring buffers of samples, for proper synchronization with the
2794  // consumer side in the PortAudio thread, which reads the time
2795  // queue after reading the sample queues. The sample queues use
2796  // atomic variables, the time queue doesn't.
2798  (mPlaybackSchedule.Interactive() ? mScrubSpeed : 1.0),
2799  frames);
2800 
2801  for (i = 0; i < mPlaybackTracks.size(); i++)
2802  {
2803  // The mixer here isn't actually mixing: it's just doing
2804  // resampling, format conversion, and possibly time track
2805  // warping
2806  samplePtr warpedSamples;
2807 
2808  if (frames > 0)
2809  {
2810  size_t processed = 0;
2811  if ( toProcess )
2812  processed = mPlaybackMixers[i]->Process( toProcess );
2813  //wxASSERT(processed <= toProcess);
2814  warpedSamples = mPlaybackMixers[i]->GetBuffer();
2815  const auto put = mPlaybackBuffers[i]->Put(
2816  warpedSamples, floatSample, processed, frames - processed);
2817  // wxASSERT(put == frames);
2818  // but we can't assert in this thread
2819  wxUnusedVar(put);
2820  }
2821  }
2822 
2823  available -= frames;
2824  wxASSERT(available >= 0);
2825 
2826  switch (mPlaybackSchedule.mPlayMode)
2827  {
2828 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
2829  case PlaybackSchedule::PLAY_SCRUB:
2830  case PlaybackSchedule::PLAY_AT_SPEED:
2831  case PlaybackSchedule::PLAY_KEYBOARD_SCRUB:
2832  {
2833  mScrubDuration -= frames;
2834  wxASSERT(mScrubDuration >= 0);
2835  done = (available == 0);
2836  if (!done && mScrubDuration <= 0)
2837  {
2838  sampleCount startSample, endSample;
2839  mScrubState->Get(
2840  startSample, endSample, available, mScrubDuration);
2841  if (mScrubDuration < 0)
2842  {
2843  // Can't play anything
2844  // Stop even if we don't fill up available
2845  mScrubDuration = 0;
2846  done = true;
2847  }
2848  else
2849  {
2850  mSilentScrub = (endSample == startSample);
2851  double startTime, endTime;
2852  startTime = startSample.as_double() / mRate;
2853  endTime = endSample.as_double() / mRate;
2854  auto diff = (endSample - startSample).as_long_long();
2855  if (mScrubDuration == 0)
2856  mScrubSpeed = 0;
2857  else
2858  mScrubSpeed =
2859  double(diff) / mScrubDuration.as_double();
2860  if (!mSilentScrub)
2861  {
2862  for (i = 0; i < mPlaybackTracks.size(); i++) {
2863  if (mPlaybackSchedule.mPlayMode == PlaybackSchedule::PLAY_AT_SPEED)
2864  mPlaybackMixers[i]->SetSpeedForPlayAtSpeed(mScrubSpeed);
2865  else if (mPlaybackSchedule.mPlayMode == PlaybackSchedule::PLAY_KEYBOARD_SCRUB)
2866  mPlaybackMixers[i]->SetSpeedForKeyboardScrubbing(mScrubSpeed, startTime);
2867  else
2868  mPlaybackMixers[i]->SetTimesAndSpeed(
2869  startTime, endTime, fabs( mScrubSpeed ));
2870  }
2871  }
2872  mTimeQueue.mLastTime = startTime;
2873  }
2874  }
2875  }
2876  break;
2877 #endif
2879  {
2880  done = !progress || (available == 0);
2881  // msmeyer: If playing looped, check if we are at the end of the buffer
2882  // and if yes, restart from the beginning.
2883  if (realTimeRemaining <= 0)
2884  {
2885  for (i = 0; i < mPlaybackTracks.size(); i++)
2886  mPlaybackMixers[i]->Restart();
2888  realTimeRemaining = mPlaybackSchedule.RealTimeRemaining();
2889  }
2890  }
2891  break;
2892  default:
2893  done = true;
2894  break;
2895  }
2896  } while (!done);
2897  }
2898  } // end of playback buffering
2899 
2900  if (!mRecordingException &&
2901  mCaptureTracks.size() > 0)
2902  GuardedCall( [&] {
2903  // start record buffering
2904  const auto avail = GetCommonlyAvailCapture(); // samples
2905  const auto remainingTime =
2906  std::max(0.0, mRecordingSchedule.ToConsume());
2907  // This may be a very big double number:
2908  const auto remainingSamples = remainingTime * mRate;
2909  bool latencyCorrected = true;
2910 
2911  double deltat = avail / mRate;
2912 
2914  deltat >= mMinCaptureSecsToCopy)
2915  {
2916  // This scope may combine many appendings of wave tracks,
2917  // and also an autosave, into one transaction,
2918  // lessening the number of checkpoints
2920  if (mOwningProject) {
2921  auto &pIO = ProjectFileIO::Get(*mOwningProject);
2922  pScope.emplace(pIO.GetConnection(), "Recording");
2923  }
2924 
2925  bool newBlocks = false;
2926 
2927  // Append captured samples to the end of the WaveTracks.
2928  // The WaveTracks have their own buffering for efficiency.
2929  auto numChannels = mCaptureTracks.size();
2930 
2931  for( i = 0; i < numChannels; i++ )
2932  {
2933  sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
2934 
2935  size_t discarded = 0;
2936 
2938  const auto correction = mRecordingSchedule.TotalCorrection();
2939  if (correction >= 0) {
2940  // Rightward shift
2941  // Once only (per track per recording), insert some initial
2942  // silence.
2943  size_t size = floor( correction * mRate * mFactor);
2944  SampleBuffer temp(size, trackFormat);
2945  ClearSamples(temp.ptr(), trackFormat, 0, size);
2946  mCaptureTracks[i]->Append(temp.ptr(), trackFormat, size, 1);
2947  }
2948  else {
2949  // Leftward shift
2950  // discard some samples from the ring buffers.
2951  size_t size = floor(
2953 
2954  // The ring buffer might have grown concurrently -- don't discard more
2955  // than the "avail" value noted above.
2956  discarded = mCaptureBuffers[i]->Discard(std::min(avail, size));
2957 
2958  if (discarded < size)
2959  // We need to visit this again to complete the
2960  // discarding.
2961  latencyCorrected = false;
2962  }
2963  }
2964 
2965  const float *pCrossfadeSrc = nullptr;
2966  size_t crossfadeStart = 0, totalCrossfadeLength = 0;
2967  if (i < mRecordingSchedule.mCrossfadeData.size())
2968  {
2969  // Do crossfading
2970  // The supplied crossfade samples are at the same rate as the track
2971  const auto &data = mRecordingSchedule.mCrossfadeData[i];
2972  totalCrossfadeLength = data.size();
2973  if (totalCrossfadeLength) {
2974  crossfadeStart =
2975  floor(mRecordingSchedule.Consumed() * mCaptureTracks[i]->GetRate());
2976  if (crossfadeStart < totalCrossfadeLength)
2977  pCrossfadeSrc = data.data() + crossfadeStart;
2978  }
2979  }
2980 
2981  wxASSERT(discarded <= avail);
2982  size_t toGet = avail - discarded;
2983  SampleBuffer temp;
2984  size_t size;
2986  if( mFactor == 1.0 )
2987  {
2988  // Take captured samples directly
2989  size = toGet;
2990  if (pCrossfadeSrc)
2991  // Change to float for crossfade calculation
2992  format = floatSample;
2993  else
2994  format = trackFormat;
2995  temp.Allocate(size, format);
2996  const auto got =
2997  mCaptureBuffers[i]->Get(temp.ptr(), format, toGet);
2998  // wxASSERT(got == toGet);
2999  // but we can't assert in this thread
3000  wxUnusedVar(got);
3001  if (double(size) > remainingSamples)
3002  size = floor(remainingSamples);
3003  }
3004  else
3005  {
3006  size = lrint(toGet * mFactor);
3007  format = floatSample;
3008  SampleBuffer temp1(toGet, floatSample);
3009  temp.Allocate(size, format);
3010  const auto got =
3011  mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, toGet);
3012  // wxASSERT(got == toGet);
3013  // but we can't assert in this thread
3014  wxUnusedVar(got);
3015  /* we are re-sampling on the fly. The last resampling call
3016  * must flush any samples left in the rate conversion buffer
3017  * so that they get recorded
3018  */
3019  if (toGet > 0 ) {
3020  if (double(toGet) > remainingSamples)
3021  toGet = floor(remainingSamples);
3022  const auto results =
3023  mResample[i]->Process(mFactor, (float *)temp1.ptr(), toGet,
3024  !IsStreamActive(), (float *)temp.ptr(), size);
3025  size = results.second;
3026  }
3027  }
3028 
3029  if (pCrossfadeSrc) {
3030  wxASSERT(format == floatSample);
3031  size_t crossfadeLength = std::min(size, totalCrossfadeLength - crossfadeStart);
3032  if (crossfadeLength) {
3033  auto ratio = double(crossfadeStart) / totalCrossfadeLength;
3034  auto ratioStep = 1.0 / totalCrossfadeLength;
3035  auto pCrossfadeDst = (float*)temp.ptr();
3036 
3037  // Crossfade loop here
3038  for (size_t ii = 0; ii < crossfadeLength; ++ii) {
3039  *pCrossfadeDst = ratio * *pCrossfadeDst + (1.0 - ratio) * *pCrossfadeSrc;
3040  ++pCrossfadeSrc, ++pCrossfadeDst;
3041  ratio += ratioStep;
3042  }
3043  }
3044  }
3045 
3046  // Now append
3047  // see comment in second handler about guarantee
3048  newBlocks = mCaptureTracks[i]->Append(temp.ptr(), format, size, 1)
3049  || newBlocks;
3050  } // end loop over capture channels
3051 
3052  // Now update the recording schedule position
3053  mRecordingSchedule.mPosition += avail / mRate;
3054  mRecordingSchedule.mLatencyCorrected = latencyCorrected;
3055 
3056  auto pListener = GetListener();
3057  if (pListener && newBlocks)
3058  pListener->OnAudioIONewBlocks(&mCaptureTracks);
3059 
3060  if (pScope)
3061  pScope->Commit();
3062  }
3063  // end of record buffering
3064  },
3065  // handler
3066  [this] ( AudacityException *pException ) {
3067  if ( pException ) {
3068  // So that we don't attempt to fill the recording buffer again
3069  // before the main thread stops recording
3071  return ;
3072  }
3073  else
3074  // Don't want to intercept other exceptions (?)
3075  throw;
3076  },
3077  delayedHandler
3078  );
3079 }

References SampleBuffer::Allocate(), sampleCount::as_double(), ClearSamples(), TransactionScope::Commit(), AudioIOBase::RecordingSchedule::Consumed(), Optional< X >::emplace(), floatSample, format, ProjectFileIO::Get(), GetCommonlyAvailCapture(), GetCommonlyFreePlayback(), AudioIoCallback::GetCommonlyReadyPlayback(), AudioIoCallback::GetListener(), GuardedCall(), AudioIOBase::PlaybackSchedule::Interactive(), AudioIOBase::IsStreamActive(), limitSampleBufferSize(), AudioIOBase::PlaybackSchedule::Looping(), lrint, AudioIoCallback::mAudioThreadShouldCallFillBuffersOnce, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, AudioIOBase::RecordingSchedule::mCrossfadeData, AudioIoCallback::mFactor, min(), AudioIoCallback::TimeQueue::mLastTime, AudioIOBase::RecordingSchedule::mLatencyCorrected, AudioIoCallback::mMinCaptureSecsToCopy, AudioIOBase::mOwningProject, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIoCallback::mPlaybackQueueMinimum, AudioIoCallback::mPlaybackSamplesToCopy, AudioIOBase::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::RecordingSchedule::mPosition, AudioIOBase::mRate, AudioIoCallback::mRecordingException, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIoCallback::mTimeQueue, AudioIOBase::PlaybackSchedule::mWarpedTime, AudioIOBase::PlaybackSchedule::PLAY_LOOPED, AudioIOBase::PlaybackSchedule::PlayingStraight(), AudioIoCallback::TimeQueue::Producer(), SampleBuffer::ptr(), AudioIOBase::PlaybackSchedule::RealTimeAdvance(), AudioIOBase::PlaybackSchedule::RealTimeRemaining(), AudioIOBase::PlaybackSchedule::RealTimeRestart(), AudioIoCallback::SetRecordingException(), StopStream(), AudioIOBase::RecordingSchedule::ToConsume(), AudioIOBase::RecordingSchedule::ToDiscard(), and AudioIOBase::RecordingSchedule::TotalCorrection().

Referenced by AudioThread::Entry().

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

◆ Get()

AudioIO * AudioIO::Get ( )
static

Definition at line 505 of file AudioIO.cpp.

506 {
507  return static_cast< AudioIO* >( AudioIOBase::Get() );
508 }

References AudioIOBase::Get().

Referenced by audacityAudioCallback(), ProjectAudioManager::CanStopAudioStream(), CaptureNotBusyFlag(), DefaultSpeedPlayOptions(), anonymous_namespace{SelectMenus.cpp}::DoBoundaryMove(), PlayIndicatorOverlayBase::DoGetRectangle(), ProjectAudioManager::DoPlayStopSelect(), ProjectAudioManager::DoRecord(), PlayIndicatorOverlayBase::Draw(), ControlToolBar::EnableDisableButtons(), TranscriptionToolBar::EnableDisableButtons(), AudioThread::Entry(), Init(), EffectUIHost::Initialize(), ProjectManager::New(), ProjectAudioManager::OnAudioIOStopRecording(), ProjectManager::OnCloseWindow(), SelectActions::Handler::OnCursorPositionStore(), PluginActions::Handler::OnDetectUpstreamDropouts(), EffectUIHost::OnFFwd(), AudacityApp::OnKeyDown(), MeterPanel::OnMeterUpdate(), ProjectAudioManager::OnPause(), EffectUIHost::OnPlay(), EffectUIHost::OnRewind(), SelectActions::Handler::OnSelectCursorStoredCursor(), PluginActions::Handler::OnSimulateRecordingErrors(), ProjectAudioManager::OnSoundActivationThreshold(), PlayIndicatorOverlay::OnTimer(), ProjectManager::OnTimer(), TrackPanel::OnTimer(), ProjectAudioManager::Pause(), anonymous_namespace{TransportMenus.cpp}::PlayCurrentRegionAndWait(), ProjectAudioManager::Playing(), ProjectAudioManager::PlayPlayRegion(), anonymous_namespace{TransportMenus.cpp}::PlayPlayRegionAndWait(), MixerToolBar::Populate(), Effect::Preview(), anonymous_namespace{TransportMenus.cpp}::RecordAndWait(), ProjectAudioManager::Recording(), anonymous_namespace{SelectMenus.cpp}::SeekWhenAudioActive(), MixerToolBar::SetMixer(), MixerToolBar::SetToolTips(), MeterPanel::StartMonitoring(), StartMonitoring(), ControlToolBar::StartScrolling(), ProjectAudioManager::Stop(), MeterPanel::StopMonitoring(), anonymous_namespace{PluginMenus.cpp}::ToolsMenu(), AdornedRulerPanel::UpdateButtonStates(), MixerToolBar::UpdateControls(), and MixerToolBar::UpdatePrefs().

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

2513 {
2514  // Check if we can use the cached value
2515  if (mCachedBestRateIn != 0.0 && mCachedBestRateIn == sampleRate
2516  && mCachedBestRatePlaying == playing && mCachedBestRateCapturing == capturing) {
2517  return mCachedBestRateOut;
2518  }
2519 
2520  // In order to cache the value, all early returns should instead set retval
2521  // and jump to finished
2522  double retval;
2523 
2524  std::vector<long> rates;
2525  if (capturing) wxLogDebug(wxT("AudioIO::GetBestRate() for capture"));
2526  if (playing) wxLogDebug(wxT("AudioIO::GetBestRate() for playback"));
2527  wxLogDebug(wxT("GetBestRate() suggested rate %.0lf Hz"), sampleRate);
2528 
2529  if (capturing && !playing) {
2530  rates = GetSupportedCaptureRates(-1, sampleRate);
2531  }
2532  else if (playing && !capturing) {
2533  rates = GetSupportedPlaybackRates(-1, sampleRate);
2534  }
2535  else { // we assume capturing and playing - the alternative would be a
2536  // bit odd
2537  rates = GetSupportedSampleRates(-1, -1, sampleRate);
2538  }
2539  /* rem rates is the array of hardware-supported sample rates (in the current
2540  * configuration), sampleRate is the Project Rate (desired sample rate) */
2541  long rate = (long)sampleRate;
2542 
2543  if (make_iterator_range(rates).contains(rate)) {
2544  wxLogDebug(wxT("GetBestRate() Returning %.0ld Hz"), rate);
2545  retval = rate;
2546  goto finished;
2547  /* the easy case - the suggested rate (project rate) is in the list, and
2548  * we can just accept that and send back to the caller. This should be
2549  * the case for most users most of the time (all of the time on
2550  * Win MME as the OS does resampling) */
2551  }
2552 
2553  /* if we get here, there is a problem - the project rate isn't supported
2554  * on our hardware, so we can't us it. Need to come up with an alternative
2555  * rate to use. The process goes like this:
2556  * * If there are no rates to pick from, we're stuck and return 0 (error)
2557  * * If there are some rates, we pick the next one higher than the requested
2558  * rate to use.
2559  * * If there aren't any higher, we use the highest available rate */
2560 
2561  if (rates.empty()) {
2562  /* we're stuck - there are no supported rates with this hardware. Error */
2563  wxLogDebug(wxT("GetBestRate() Error - no supported sample rates"));
2564  retval = 0.0;
2565  goto finished;
2566  }
2567  int i;
2568  for (i = 0; i < (int)rates.size(); i++) // for each supported rate
2569  {
2570  if (rates[i] > rate) {
2571  // supported rate is greater than requested rate
2572  wxLogDebug(wxT("GetBestRate() Returning next higher rate - %.0ld Hz"), rates[i]);
2573  retval = rates[i];
2574  goto finished;
2575  }
2576  }
2577 
2578  wxLogDebug(wxT("GetBestRate() Returning highest rate - %.0ld Hz"), rates.back());
2579  retval = rates.back(); // the highest available rate
2580  goto finished;
2581 
2582 finished:
2583  mCachedBestRateIn = sampleRate;
2584  mCachedBestRateOut = retval;
2585  mCachedBestRatePlaying = playing;
2586  mCachedBestRateCapturing = capturing;
2587  return retval;
2588 }

References AudioIOBase::GetSupportedCaptureRates(), AudioIOBase::GetSupportedPlaybackRates(), AudioIOBase::GetSupportedSampleRates(), make_iterator_range(), AudioIoCallback::mCachedBestRateCapturing, AudioIOBase::mCachedBestRateIn, AudioIoCallback::mCachedBestRateOut, and AudioIoCallback::mCachedBestRatePlaying.

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

686 { 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 2676 of file AudioIO.cpp.

2677 {
2678  auto commonlyAvail = mCaptureBuffers[0]->AvailForGet();
2679  for (unsigned i = 1; i < mCaptureTracks.size(); ++i)
2680  commonlyAvail = std::min(commonlyAvail,
2681  mCaptureBuffers[i]->AvailForGet());
2682  return commonlyAvail;
2683 }

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

Referenced by FillBuffers().

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

2654 {
2655  auto commonlyAvail = mPlaybackBuffers[0]->AvailForPut();
2656  for (unsigned i = 1; i < mPlaybackTracks.size(); ++i)
2657  commonlyAvail = std::min(commonlyAvail,
2658  mPlaybackBuffers[i]->AvailForPut());
2659  // MB: subtract a few samples because the code in FillBuffers has rounding
2660  // errors
2661  return commonlyAvail - std::min(size_t(10), commonlyAvail);
2662 }

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

Referenced by FillBuffers().

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

1181 {
1182 #if defined(USE_PORTMIXER)
1183 
1184  wxArrayString deviceNames;
1185 
1186  if( mPortMixer )
1187  {
1188  int numSources = Px_GetNumInputSources(mPortMixer);
1189  for( int source = 0; source < numSources; source++ )
1190  deviceNames.push_back(wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(mPortMixer, source))));
1191  }
1192  else
1193  {
1194  wxLogDebug(wxT("AudioIO::GetInputSourceNames(): PortMixer not initialised!"));
1195  }
1196 
1197  return deviceNames;
1198 
1199 #else
1200 
1201  wxArrayString blank;
1202 
1203  return blank;
1204 
1205 #endif
1206 }

◆ GetLastPlaybackTime()

wxLongLong AudioIO::GetLastPlaybackTime ( ) const
inline

Definition at line 648 of file AudioIO.h.

648 { return mLastPlaybackTimeMillis; }

◆ GetMixer()

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

Definition at line 1139 of file AudioIO.cpp.

1141 {
1142 #if defined(USE_PORTMIXER)
1143 
1144  PxMixer *mixer = mPortMixer;
1145 
1146  if( mixer )
1147  {
1148  *recordDevice = Px_GetCurrentInputSource(mixer);
1149 
1150  if (mInputMixerWorks)
1151  *recordVolume = Px_GetInputVolume(mixer);
1152  else
1153  *recordVolume = 1.0f;
1154 
1156  *playbackVolume = mMixerOutputVol;
1157  else
1158  *playbackVolume = Px_GetPCMOutputVolume(mixer);
1159 
1160  return;
1161  }
1162 
1163 #endif
1164 
1165  *recordDevice = 0;
1166  *recordVolume = 1.0f;
1167  *playbackVolume = mMixerOutputVol;
1168 }

References AudioIOBase::mEmulateMixerOutputVol, AudioIOBase::mInputMixerWorks, and AudioIOBase::mMixerOutputVol.

◆ GetNumCaptureChannels()

unsigned AudioIO::GetNumCaptureChannels ( ) const
inline

Definition at line 688 of file AudioIO.h.

688 { return mNumCaptureChannels; }

References AudioIoCallback::mCaptureFormat.

◆ GetNumPlaybackChannels()

unsigned AudioIO::GetNumPlaybackChannels ( ) const
inline

Definition at line 687 of file AudioIO.h.

687 { return mNumPlaybackChannels; }

◆ GetOwningProject()

AudacityProject* AudioIO::GetOwningProject ( ) const
inline

Definition at line 649 of file AudioIO.h.

649 { return mOwningProject; }

References AudioIoCallback::mLastPlaybackTimeMillis.

◆ Init()

void AudioIO::Init ( )
static

Definition at line 911 of file AudioIO.cpp.

912 {
913  ugAudioIO.reset(safenew AudioIO());
914  Get()->mThread->Run();
915 #ifdef EXPERIMENTAL_MIDI_OUT
916 #ifdef USE_MIDI_THREAD
917  Get()->mMidiThread->Run();
918 #endif
919 #endif
920 
921  // Make sure device prefs are initialized
922  if (gPrefs->Read(wxT("AudioIO/RecordingDevice"), wxT("")).empty()) {
923  int i = getRecordDevIndex();
924  const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
925  if (info) {
926  gPrefs->Write(wxT("/AudioIO/RecordingDevice"), DeviceName(info));
927  gPrefs->Write(wxT("/AudioIO/Host"), HostName(info));
928  }
929  }
930 
931  if (gPrefs->Read(wxT("AudioIO/PlaybackDevice"), wxT("")).empty()) {
932  int i = getPlayDevIndex();
933  const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
934  if (info) {
935  gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), DeviceName(info));
936  gPrefs->Write(wxT("/AudioIO/Host"), HostName(info));
937  }
938  }
939 
940  gPrefs->Flush();
941 }

References AudioIO(), AudioIOBase::DeviceName(), FileConfig::Flush(), Get(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), gPrefs, AudioIOBase::HostName(), AudioIoCallback::mThread, safenew, and AudioIOBase::ugAudioIO.

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

1171 {
1172  return mInputMixerWorks;
1173 }

References AudioIOBase::mInputMixerWorks.

◆ IsAvailable()

bool AudioIO::IsAvailable ( AudacityProject projecT) const

Function to automatically set an acceptable volume.

Definition at line 2148 of file AudioIO.cpp.

2149 {
2150  return mOwningProject == NULL || mOwningProject == project;
2151 }

References AudioIOBase::mOwningProject.

◆ IsCapturing()

bool AudioIO::IsCapturing ( ) const

Definition at line 4598 of file AudioIO.cpp.

4599 {
4600  // Includes a test of mTime, used in the main thread
4601  return IsStreamActive() &&
4602  GetNumCaptureChannels() > 0 &&
4605 }

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

Here is the call graph for this function:

◆ LastPaErrorString()

wxString AudioIO::LastPaErrorString ( )

Definition at line 1438 of file AudioIO.cpp.

1439 {
1440  return wxString::Format(wxT("%d %s."), (int) mLastPaError, Pa_GetErrorText(mLastPaError));
1441 }

References AudioIoCallback::mLastPaError.

Referenced by StartMonitoring().

Here is the caller graph for this function:

◆ OutputMixerEmulated()

bool AudioIO::OutputMixerEmulated ( )

Find out if the output level control is being emulated via software attenuation.

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

Definition at line 1175 of file AudioIO.cpp.

1176 {
1177  return mEmulateMixerOutputVol;
1178 }

References AudioIOBase::mEmulateMixerOutputVol.

Referenced by MixerToolBar::SetToolTips().

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

626 { mSeek = seconds; }

◆ SetMeters()

void AudioIO::SetMeters ( )
private

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

Definition at line 2153 of file AudioIO.cpp.

2154 {
2155  if (mInputMeter)
2156  mInputMeter->Reset(mRate, true);
2157  if (mOutputMeter)
2158  mOutputMeter->Reset(mRate, true);
2159 
2160  mUpdateMeters = true;
2161 }

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

Referenced by StartPortAudioStream().

Here is the caller graph for this function:

◆ SetMixer()

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

Definition at line 1118 of file AudioIO.cpp.

1120 {
1121  mMixerOutputVol = playbackVolume;
1122 #if defined(USE_PORTMIXER)
1123  PxMixer *mixer = mPortMixer;
1124  if( !mixer )
1125  return;
1126 
1127  float oldRecordVolume = Px_GetInputVolume(mixer);
1128  float oldPlaybackVolume = Px_GetPCMOutputVolume(mixer);
1129 
1130  AudioIoCallback::SetMixer(inputSource);
1131  if( oldRecordVolume != recordVolume )
1132  Px_SetInputVolume(mixer, recordVolume);
1133  if( oldPlaybackVolume != playbackVolume )
1134  Px_SetPCMOutputVolume(mixer, playbackVolume);
1135 
1136 #endif
1137 }

References AudioIOBase::mMixerOutputVol, and AudioIOBase::SetMixer().

Here is the call graph for this function:

◆ SetPaused()

void AudioIO::SetPaused ( bool  state)

Pause and un-pause playback and recording.

Definition at line 2468 of file AudioIO.cpp.

2469 {
2470  if (state != mPaused)
2471  {
2472  if (state)
2473  {
2475  }
2476  else
2477  {
2479  }
2480  }
2481 
2482  mPaused = state;
2483 }

References RealtimeEffectManager::Get(), AudioIOBase::mPaused, RealtimeEffectManager::RealtimeResume(), and RealtimeEffectManager::RealtimeSuspend().

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

1444 {
1445  if ( mPortStreamV19 || mStreamToken )
1446  return;
1447 
1448  bool success;
1449  long captureChannels;
1450  auto captureFormat = QualityPrefs::SampleFormatChoice();
1451  gPrefs->Read(wxT("/AudioIO/RecordChannels"), &captureChannels, 2L);
1452  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
1453  int playbackChannels = 0;
1454 
1456  playbackChannels = 2;
1457 
1458  // FIXME: TRAP_ERR StartPortAudioStream (a PaError may be present)
1459  // but StartPortAudioStream function only returns true or false.
1460  mUsingAlsa = false;
1461  success = StartPortAudioStream(options, (unsigned int)playbackChannels,
1462  (unsigned int)captureChannels,
1463  captureFormat);
1464 
1465  if (!success) {
1466  auto msg = XO("Error opening recording device.\nError code: %s")
1467  .Format( Get()->LastPaErrorString() );
1469  XO("Error"), msg, wxT("Error_opening_sound_device"));
1470  return;
1471  }
1472 
1473  wxCommandEvent e(EVT_AUDIOIO_MONITOR);
1474  e.SetEventObject(mOwningProject);
1475  e.SetInt(true);
1476  wxTheApp->ProcessEvent(e);
1477 
1478  // FIXME: TRAP_ERR PaErrorCode 'noted' but not reported in StartMonitoring.
1479  // Now start the PortAudio stream!
1480  // TODO: ? Factor out and reuse error reporting code from end of
1481  // AudioIO::StartStream?
1482  mLastPaError = Pa_StartStream( mPortStreamV19 );
1483 
1484  // Update UI display only now, after all possibilities for error are past.
1485  auto pListener = GetListener();
1486  if ((mLastPaError == paNoError) && pListener) {
1487  // advertise the chosen I/O sample rate to the UI
1488  pListener->OnAudioIORate((int)mRate);
1489  }
1490 }

References FindProjectFrame(), Get(), AudioIoCallback::GetListener(), gPrefs, LastPaErrorString(), AudioIoCallback::mLastPaError, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, AudioIoCallback::mUsingAlsa, QualityPrefs::SampleFormatChoice(), ShowErrorDialog(), StartPortAudioStream(), and XO.

Here is the call graph for this function:

◆ StartPortAudioStream()

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

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

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

Definition at line 1221 of file AudioIO.cpp.

1225 {
1226  auto sampleRate = options.rate;
1227 #ifdef EXPERIMENTAL_MIDI_OUT
1228  mNumFrames = 0;
1229  mNumPauseFrames = 0;
1230  // we want this initial value to be way high. It should be
1231  // sufficient to assume AudioTime is zero and therefore
1232  // mSystemMinusAudioTime is SystemTime(), but we'll add 1000s
1233  // for good measure. On the first callback, this should be
1234  // reduced to SystemTime() - mT0, and note that mT0 is always
1235  // positive.
1236  mSystemMinusAudioTimePlusLatency =
1237  mSystemMinusAudioTime = SystemTime(mUsingAlsa) + 1000;
1238  mAudioOutLatency = 0.0; // set when stream is opened
1239  mCallbackCount = 0;
1240  mAudioFramesPerBuffer = 0;
1241 #endif
1242  mOwningProject = options.pProject;
1243 
1244  // PRL: Protection from crash reported by David Bailes, involving starting
1245  // and stopping with frequent changes of active window, hard to reproduce
1246  if (!mOwningProject)
1247  return false;
1248 
1249  mInputMeter.Release();
1250  mOutputMeter.Release();
1251 
1252  mLastPaError = paNoError;
1253  // pick a rate to do the audio I/O at, from those available. The project
1254  // rate is suggested, but we may get something else if it isn't supported
1255  mRate = GetBestRate(numCaptureChannels > 0, numPlaybackChannels > 0, sampleRate);
1256 
1257  // July 2016 (Carsten and Uwe)
1258  // BUG 193: Tell PortAudio sound card will handle 24 bit (under DirectSound) using
1259  // userData.
1260  int captureFormat_saved = captureFormat;
1261  // Special case: Our 24-bit sample format is different from PortAudio's
1262  // 3-byte packed format. So just make PortAudio return float samples,
1263  // since we need float values anyway to apply the gain.
1264  // ANSWER-ME: So we *never* actually handle 24-bit?! This causes mCapture to
1265  // be set to floatSample below.
1266  // JKC: YES that's right. Internally Audacity uses float, and float has space for
1267  // 24 bits as well as exponent. Actual 24 bit would require packing and
1268  // unpacking unaligned bytes and would be inefficient.
1269  // ANSWER ME: is floatSample 64 bit on 64 bit machines?
1270  if (captureFormat == int24Sample)
1271  captureFormat = floatSample;
1272 
1273  mNumPlaybackChannels = numPlaybackChannels;
1274  mNumCaptureChannels = numCaptureChannels;
1275 
1276  bool usePlayback = false, useCapture = false;
1277  PaStreamParameters playbackParameters{};
1278  PaStreamParameters captureParameters{};
1279 
1280  double latencyDuration = DEFAULT_LATENCY_DURATION;
1281  gPrefs->Read(wxT("/AudioIO/LatencyDuration"), &latencyDuration);
1282 
1283  if( numPlaybackChannels > 0)
1284  {
1285  usePlayback = true;
1286 
1287  // this sets the device index to whatever is "right" based on preferences,
1288  // then defaults
1289  playbackParameters.device = getPlayDevIndex();
1290 
1291  const PaDeviceInfo *playbackDeviceInfo;
1292  playbackDeviceInfo = Pa_GetDeviceInfo( playbackParameters.device );
1293 
1294  if( playbackDeviceInfo == NULL )
1295  return false;
1296 
1297  // regardless of source formats, we always mix to float
1298  playbackParameters.sampleFormat = paFloat32;
1299  playbackParameters.hostApiSpecificStreamInfo = NULL;
1300  playbackParameters.channelCount = mNumPlaybackChannels;
1301 
1303  playbackParameters.suggestedLatency =
1304  playbackDeviceInfo->defaultLowOutputLatency;
1305  else {
1306  // When using WASAPI, the suggested latency does not affect
1307  // the latency of the playback, but the position of playback is given as if
1308  // there was the suggested latency. This results in the last "suggested latency"
1309  // of a selection not being played. So for WASAPI use 0.0 for the suggested
1310  // latency regardless of user setting. See bug 1949.
1311  const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(playbackDeviceInfo->hostApi);
1312  bool isWASAPI = (hostInfo && hostInfo->type == paWASAPI);
1313  playbackParameters.suggestedLatency = isWASAPI ? 0.0 : latencyDuration/1000.0;
1314  }
1315 
1316  if ( options.playbackMeter )
1317  mOutputMeter = options.playbackMeter;
1318  else
1319  mOutputMeter.Release();
1320  }
1321 
1322  if( numCaptureChannels > 0)
1323  {
1324  useCapture = true;
1325  mCaptureFormat = captureFormat;
1326 
1327  const PaDeviceInfo *captureDeviceInfo;
1328  // retrieve the index of the device set in the prefs, or a sensible
1329  // default if it isn't set/valid
1330  captureParameters.device = getRecordDevIndex();
1331 
1332  captureDeviceInfo = Pa_GetDeviceInfo( captureParameters.device );
1333 
1334  if( captureDeviceInfo == NULL )
1335  return false;
1336 
1337  captureParameters.sampleFormat =
1339 
1340  captureParameters.hostApiSpecificStreamInfo = NULL;
1341  captureParameters.channelCount = mNumCaptureChannels;
1342 
1344  captureParameters.suggestedLatency =
1345  captureDeviceInfo->defaultHighInputLatency;
1346  else
1347  captureParameters.suggestedLatency = latencyDuration/1000.0;
1348 
1350  }
1351 
1352  SetMeters();
1353 
1354 #ifdef USE_PORTMIXER
1355 #ifdef __WXMSW__
1356  //mchinen nov 30 2010. For some reason Pa_OpenStream resets the input volume on windows.
1357  //so cache and restore after it.
1358  //The actual problem is likely in portaudio's pa_win_wmme.c OpenStream().
1359  float oldRecordVolume = Px_GetInputVolume(mPortMixer);
1360 #endif
1361 #endif
1362 
1363  // July 2016 (Carsten and Uwe)
1364  // BUG 193: Possibly tell portAudio to use 24 bit with DirectSound.
1365  int userData = 24;
1366  int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
1367 
1368  // (Linux, bug 1885) After scanning devices it takes a little time for the
1369  // ALSA device to be available, so allow retries.
1370  // On my test machine, no more than 3 attempts are required.
1371  unsigned int maxTries = 1;
1372 #ifdef __WXGTK__
1374  maxTries = 5;
1375 #endif
1376 
1377  for (unsigned int tries = 0; tries < maxTries; tries++) {
1378  mLastPaError = Pa_OpenStream( &mPortStreamV19,
1379  useCapture ? &captureParameters : NULL,
1380  usePlayback ? &playbackParameters : NULL,
1381  mRate, paFramesPerBufferUnspecified,
1382  paNoFlag,
1383  audacityAudioCallback, lpUserData );
1384  if (mLastPaError == paNoError) {
1385  break;
1386  }
1387  wxLogDebug("Attempt %u to open capture stream failed with: %d", 1 + tries, mLastPaError);
1388  wxMilliSleep(1000);
1389  }
1390 
1391 
1392 #if USE_PORTMIXER
1393 #ifdef __WXMSW__
1394  Px_SetInputVolume(mPortMixer, oldRecordVolume);
1395 #endif
1396  if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
1397 
1398  #ifdef __WXMAC__
1399  if (mPortMixer) {
1400  if (Px_SupportsPlaythrough(mPortMixer)) {
1401  bool playthrough = false;
1402 
1403  mPreviousHWPlaythrough = Px_GetPlaythrough(mPortMixer);
1404 
1405  // Bug 388. Feature not supported.
1406  //gPrefs->Read(wxT("/AudioIO/Playthrough"), &playthrough, false);
1407  if (playthrough)
1408  Px_SetPlaythrough(mPortMixer, 1.0);
1409  else
1410  Px_SetPlaythrough(mPortMixer, 0.0);
1411  }
1412  }
1413  #endif
1414  }
1415 #endif
1416 
1417 #ifdef EXPERIMENTAL_MIDI_OUT
1418  // We use audio latency to estimate how far ahead of DACS we are writing
1419  if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
1420  const PaStreamInfo* info = Pa_GetStreamInfo(mPortStreamV19);
1421  // this is an initial guess, but for PA/Linux/ALSA it's wrong and will be
1422  // updated with a better value:
1423  mAudioOutLatency = info->outputLatency;
1424  mSystemMinusAudioTimePlusLatency += mAudioOutLatency;
1425  }
1426 #endif
1427 
1428 #if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
1429  // Don't want the system to sleep while audio I/O is active
1430  if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
1431  wxPowerResource::Acquire(wxPOWER_RESOURCE_SCREEN, _("Audacity Audio"));
1432  }
1433 #endif
1434 
1435  return (mLastPaError == paNoError);
1436 }

References _, audacityAudioCallback(), AudacityToPortAudioSampleFormat(), AudioIOStartStreamOptions::captureMeter, DEFAULT_LATENCY_DURATION, floatSample, GetBestRate(), AudioIOBase::getPlayDevIndex(), AudioIOBase::getRecordDevIndex(), DeviceManager::GetTimeSinceRescan(), gPrefs, DeviceManager::Instance(), int24Sample, AudioIoCallback::mCaptureFormat, AudioIOBase::mInputMeter, AudioIoCallback::mLastPaError, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mSoftwarePlaythrough, AudioIoCallback::mUsingAlsa, AudioIOStartStreamOptions::playbackMeter, AudioIOStartStreamOptions::pProject, AudioIOStartStreamOptions::rate, AudioIOBase::SetCaptureMeter(), and SetMeters().

Referenced by StartMonitoring(), and StartStream().

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

◆ StartStream()

int AudioIO::StartStream ( const TransportTracks tracks,
double  t0,
double  t1,
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()

Definition at line 1492 of file AudioIO.cpp.

1495 {
1496  mLostSamples = 0;
1497  mLostCaptureIntervals.clear();
1498  mDetectDropouts =
1499  gPrefs->Read( WarningDialogKey(wxT("DropoutDetected")), true ) != 0;
1500  auto cleanup = finally ( [this] { ClearRecordingException(); } );
1501 
1502  if( IsBusy() )
1503  return 0;
1504 
1505  // We just want to set mStreamToken to -1 - this way avoids
1506  // an extremely rare but possible race condition, if two functions
1507  // somehow called StartStream at the same time...
1508  mStreamToken--;
1509  if (mStreamToken != -1)
1510  return 0;
1511 
1512  // TODO: we don't really need to close and reopen stream if the
1513  // format matches; however it's kind of tricky to keep it open...
1514  //
1515  // if (sampleRate == mRate &&
1516  // playbackChannels == mNumPlaybackChannels &&
1517  // captureChannels == mNumCaptureChannels &&
1518  // captureFormat == mCaptureFormat) {
1519 
1520  if (mPortStreamV19) {
1521  StopStream();
1522  while(mPortStreamV19)
1523  wxMilliSleep( 50 );
1524  }
1525 
1526 #ifdef __WXGTK__
1527  // Detect whether ALSA is the chosen host, and do the various involved MIDI
1528  // timing compensations only then.
1529  mUsingAlsa = (gPrefs->Read(wxT("/AudioIO/Host"), wxT("")) == "ALSA");
1530 #endif
1531 
1532  gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &mSoftwarePlaythrough, false);
1533  gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &mPauseRec, false);
1534  gPrefs->Read(wxT("/AudioIO/Microfades"), &mbMicroFades, false);
1535  int silenceLevelDB;
1536  gPrefs->Read(wxT("/AudioIO/SilenceLevel"), &silenceLevelDB, -50);
1537  int dBRange;
1538  dBRange = gPrefs->Read(ENV_DB_KEY, ENV_DB_RANGE);
1539  if(silenceLevelDB < -dBRange)
1540  {
1541  silenceLevelDB = -dBRange + 3;
1542  // meter range was made smaller than SilenceLevel
1543  // so set SilenceLevel reasonable
1544 
1545  // PRL: update prefs, or correct it only in-session?
1546  // The behavior (as of 2.3.1) was the latter, the code suggested that
1547  // the intent was the former; I preserve the behavior, but uncomment
1548  // this if you disagree.
1549  // gPrefs->Write(wxT("/AudioIO/SilenceLevel"), silenceLevelDB);
1550  // gPrefs->Flush();
1551  }
1552  mSilenceLevel = DB_TO_LINEAR(silenceLevelDB); // meter goes -dBRange dB -> 0dB
1553 
1554  // Clamp pre-roll so we don't play before time 0
1555  const auto preRoll = std::max(0.0, std::min(t0, options.preRoll));
1556  mRecordingSchedule = {};
1557  mRecordingSchedule.mPreRoll = preRoll;
1559  (gPrefs->ReadDouble(wxT("/AudioIO/LatencyCorrection"),
1561  / 1000.0;
1562  mRecordingSchedule.mDuration = t1 - t0;
1563  if (options.pCrossfadeData)
1565 
1566  mListener = options.listener;
1567  mRate = options.rate;
1568 
1569  mSeek = 0;
1571  mCaptureTracks = tracks.captureTracks;
1573 #ifdef EXPERIMENTAL_MIDI_OUT
1574  mMidiPlaybackTracks = tracks.midiTracks;
1575 #endif
1576 
1577  bool commit = false;
1578  auto cleanupTracks = finally([&]{
1579  if (!commit) {
1580  // Don't keep unnecessary shared pointers to tracks
1581  mPlaybackTracks.clear();
1582  mCaptureTracks.clear();
1583 #ifdef EXPERIMENTAL_MIDI_OUT
1584  mMidiPlaybackTracks.clear();
1585 #endif
1586 
1587  // Don't cause a busy wait in the audio thread after stopping scrubbing
1589  }
1590  });
1591 
1592  mPlaybackBuffers.reset();
1593  mPlaybackMixers.reset();
1594  mCaptureBuffers.reset();
1595  mResample.reset();
1596  mTimeQueue.mData.reset();
1597 
1598 #ifdef EXPERIMENTAL_MIDI_OUT
1599  streamStartTime = 0;
1600  streamStartTime = SystemTime(mUsingAlsa);
1601 #endif
1602 
1604  t0, t1, options, mCaptureTracks.empty() ? nullptr : &mRecordingSchedule );
1605  const bool scrubbing = mPlaybackSchedule.Interactive();
1606 
1607  unsigned int playbackChannels = 0;
1608  unsigned int captureChannels = 0;
1609  sampleFormat captureFormat = floatSample;
1610 
1611  auto pListener = GetListener();
1612 
1613  if (tracks.playbackTracks.size() > 0
1614 #ifdef EXPERIMENTAL_MIDI_OUT
1615  || tracks.midiTracks.size() > 0
1616 #endif
1617  )
1618  playbackChannels = 2;
1619 
1621  playbackChannels = 2;
1622 
1623  if (tracks.captureTracks.size() > 0)
1624  {
1625  // For capture, every input channel gets its own track
1626  captureChannels = mCaptureTracks.size();
1627  // I don't deal with the possibility of the capture tracks
1628  // having different sample formats, since it will never happen
1629  // with the current code. This code wouldn't *break* if this
1630  // assumption was false, but it would be sub-optimal. For example,
1631  // if the first track was 16-bit and the second track was 24-bit,
1632  // we would set the sound card to capture in 16 bits and the second
1633  // track wouldn't get the benefit of all 24 bits the card is capable
1634  // of.
1635  captureFormat = mCaptureTracks[0]->GetSampleFormat();
1636 
1637  // Tell project that we are about to start recording
1638  if (pListener)
1639  pListener->OnAudioIOStartRecording();
1640  }
1641 
1642  bool successAudio;
1643 
1644  successAudio = StartPortAudioStream(options, playbackChannels,
1645  captureChannels, captureFormat);
1646 #ifdef EXPERIMENTAL_MIDI_OUT
1647 
1648  // TODO: it may be that midi out will not work unless audio in or out is
1649  // active -- this would be a bug and may require a change in the
1650  // logic here.
1651 
1652  bool successMidi = true;
1653 
1654  if(!mMidiPlaybackTracks.empty()){
1655  successMidi = StartPortMidiStream();
1656  }
1657 
1658  // On the other hand, if MIDI cannot be opened, we will not complain
1659 #endif
1660 
1661  if (!successAudio) {
1662  if (pListener && captureChannels > 0)
1663  pListener->OnAudioIOStopRecording();
1664  mStreamToken = 0;
1665 
1666  return 0;
1667  }
1668 
1669  if ( ! AllocateBuffers( options, tracks, t0, t1, options.rate, scrubbing ) )
1670  return 0;
1671 
1672  if (mNumPlaybackChannels > 0)
1673  {
1674  auto & em = RealtimeEffectManager::Get();
1675  // Setup for realtime playback at the rate of the realtime
1676  // stream, not the rate of the track.
1677  em.RealtimeInitialize(mRate);
1678 
1679  // The following adds a NEW effect processor for each logical track and the
1680  // group determination should mimic what is done in audacityAudioCallback()
1681  // when calling RealtimeProcess().
1682  int group = 0;
1683  for (size_t i = 0, cnt = mPlaybackTracks.size(); i < cnt;)
1684  {
1685  const WaveTrack *vt = mPlaybackTracks[i].get();
1686 
1687  // TODO: more-than-two-channels
1688  unsigned chanCnt = TrackList::Channels(vt).size();
1689  i += chanCnt;
1690 
1691  // Setup for realtime playback at the rate of the realtime
1692  // stream, not the rate of the track.
1693  em.RealtimeAddProcessor(group++, std::min(2u, chanCnt), mRate);
1694  }
1695  }
1696 
1697 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1698  AILASetStartTime();
1699 #endif
1700 
1701  if (options.pStartTime)
1702  {
1703  // Calculate the NEW time position
1704  const auto time = mPlaybackSchedule.ClampTrackTime( *options.pStartTime );
1705 
1706  // Main thread's initialization of mTime
1708 
1709  // Reset mixer positions for all playback tracks
1710  unsigned numMixers = mPlaybackTracks.size();
1711  for (unsigned ii = 0; ii < numMixers; ++ii)
1712  mPlaybackMixers[ii]->Reposition( time );
1714  }
1715 
1716  // Now that we are done with SetTrackTime():
1718  if (mTimeQueue.mData)
1720  // else recording only without overdub
1721 
1722 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
1723  if (scrubbing)
1724  {
1725  const auto &scrubOptions = *options.pScrubbingOptions;
1726  mScrubState =
1727  std::make_unique<ScrubState>(
1729  mRate,
1730  scrubOptions);
1731  mScrubDuration = 0;
1732  mSilentScrub = false;
1733  }
1734  else
1735  mScrubState.reset();
1736 #endif
1737 
1738  // We signal the audio thread to call FillBuffers, to prime the RingBuffers
1739  // so that they will have data in them when the stream starts. Having the
1740  // audio thread call FillBuffers here makes the code more predictable, since
1741  // FillBuffers will ALWAYS get called from the Audio thread.
1743 
1745  auto interval = 50ull;
1746  if (options.playbackStreamPrimer) {
1747  interval = options.playbackStreamPrimer();
1748  }
1749  wxMilliSleep( interval );
1750  }
1751 
1752  if(mNumPlaybackChannels > 0 || mNumCaptureChannels > 0) {
1753 
1754 #ifdef REALTIME_ALSA_THREAD
1755  // PRL: Do this in hope of less thread scheduling jitter in calls to
1756  // audacityAudioCallback.
1757  // Not needed to make audio playback work smoothly.
1758  // But needed in case we also play MIDI, so that the variable "offset"
1759  // in AudioIO::MidiTime() is a better approximation of the duration
1760  // between the call of audacityAudioCallback and the actual output of
1761  // the first audio sample.
1762  // (Which we should be able to determine from fields of
1763  // PaStreamCallbackTimeInfo, but that seems not to work as documented with
1764  // ALSA.)
1765  if (mUsingAlsa)
1766  // Perhaps we should do this only if also playing MIDI ?
1767  PaAlsa_EnableRealtimeScheduling( mPortStreamV19, 1 );
1768 #endif
1769 
1770  //
1771  // Generate a unique value each time, to be returned to
1772  // clients accessing the AudioIO API, so they can query if they
1773  // are the ones who have reserved AudioIO or not.
1774  //
1775  // It is important to set this before setting the portaudio stream in
1776  // motion -- otherwise it may play an unspecified number of leading
1777  // zeroes.
1779 
1780  // This affects the AudioThread (not the portaudio callback).
1781  // Probably not needed so urgently before portaudio thread start for usual
1782  // playback, since our ring buffers have been primed already with 4 sec
1783  // of audio, but then we might be scrubbing, so do it.
1785 
1786  // Now start the PortAudio stream!
1787  PaError err;
1788  err = Pa_StartStream( mPortStreamV19 );
1789 
1790  if( err != paNoError )
1791  {
1792  mStreamToken = 0;
1794  if (pListener && mNumCaptureChannels > 0)
1795  pListener->OnAudioIOStopRecording();
1797  // PRL: PortAudio error messages are sadly not internationalized
1799  Verbatim( LAT1CTOWX(Pa_GetErrorText(err)) ) );
1800  return 0;
1801  }
1802  }
1803 
1804  // Update UI display only now, after all possibilities for error are past.
1805  if (pListener) {
1806  // advertise the chosen I/O sample rate to the UI
1807  pListener->OnAudioIORate((int)mRate);
1808  }
1809 
1810  if (mNumPlaybackChannels > 0)
1811  {
1812  wxCommandEvent e(EVT_AUDIOIO_PLAYBACK);
1813  e.SetEventObject(mOwningProject);
1814  e.SetInt(true);
1815  wxTheApp->ProcessEvent(e);
1816  }
1817 
1818  if (mNumCaptureChannels > 0)
1819  {
1820  wxCommandEvent e(EVT_AUDIOIO_CAPTURE);
1821  e.SetEventObject(mOwningProject);
1822  e.SetInt(true);
1823  wxTheApp->ProcessEvent(e);
1824  }
1825 
1826  commit = true;
1827  return mStreamToken;
1828 }

References AllocateBuffers(), AudacityMessageBox(), TransportTracks::captureTracks, TrackList::Channels(), AudioIOBase::PlaybackSchedule::ClampTrackTime(), AudioIoCallback::ClearRecordingException(), DB_TO_LINEAR(), DEFAULT_LATENCY_CORRECTION, ENV_DB_KEY, ENV_DB_RANGE, floatSample, RealtimeEffectManager::Get(), AudioIoCallback::GetListener(), AudioIOBase::PlaybackSchedule::GetTrackTime(), gPrefs, AudioIOBase::PlaybackSchedule::Init(), AudioIOBase::PlaybackSchedule::Interactive(), AudioIOBase::IsBusy(), LAT1CTOWX, AudioIOStartStreamOptions::listener, AudioIoCallback::mAudioThreadFillBuffersLoopRunning, AudioIoCallback::mAudioThreadShouldCallFillBuffersOnce, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, AudioIOBase::RecordingSchedule::mCrossfadeData, AudioIoCallback::TimeQueue::mData, AudioIoCallback::mDetectDropouts, AudioIOBase::RecordingSchedule::mDuration, min(), AudioIoCallback::mLastRecordingOffset, AudioIoCallback::TimeQueue::mLastTime, AudioIOBase::RecordingSchedule::mLatencyCorrection, AudioIoCallback::mListener, AudioIoCallback::mLostCaptureIntervals, AudioIoCallback::mLostSamples, AudioIoCallback::mNextStreamToken, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOwningProject, AudioIoCallback::mPauseRec, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIOBase::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mPortStreamV19, AudioIOBase::RecordingSchedule::mPreRoll, AudioIOBase::mRate, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIoCallback::mSeek, AudioIoCallback::mSilenceLevel, AudioIoCallback::mSoftwarePlaythrough, AudioIOBase::mStreamToken, AudioIOBase::PlaybackSchedule::mT0, AudioIoCallback::mTimeQueue, AudioIoCallback::mUsingAlsa, AudioIOStartStreamOptions::pCrossfadeData, AudioIOStartStreamOptions::playbackStreamPrimer, TransportTracks::playbackTracks, AudioIOStartStreamOptions::preRoll, AudioIOStartStreamOptions::pStartTime, AudioIOStartStreamOptions::rate, AudioIOBase::PlaybackSchedule::RealTimeInit(), AudioIOBase::PlaybackSchedule::ResetMode(), AudioIOBase::PlaybackSchedule::SetTrackTime(), StartPortAudioStream(), StartStreamCleanup(), StopStream(), Verbatim(), and WarningDialogKey().

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

2020 {
2021  if (mNumPlaybackChannels > 0)
2022  {
2024  }
2025 
2026  mPlaybackBuffers.reset();
2027  mPlaybackMixers.reset();
2028  mCaptureBuffers.reset();
2029  mResample.reset();
2030  mTimeQueue.mData.reset();
2031 
2032  if(!bOnlyBuffers)
2033  {
2034  Pa_AbortStream( mPortStreamV19 );
2035  Pa_CloseStream( mPortStreamV19 );
2036  mPortStreamV19 = NULL;
2037  mStreamToken = 0;
2038  }
2039 
2040 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
2041  mScrubState.reset();
2042 #endif
2043 }

References RealtimeEffectManager::Get(), AudioIoCallback::mCaptureBuffers, AudioIoCallback::TimeQueue::mData, AudioIoCallback::mNumPlaybackChannels, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIOBase::mPortStreamV19, AudioIoCallback::mResample, AudioIOBase::mStreamToken, AudioIoCallback::mTimeQueue, and RealtimeEffectManager::RealtimeFinalize().

Referenced by AllocateBuffers(), and StartStream().

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

◆ StopStream()

void AudioIO::StopStream ( )
overridevirtual

Stop recording, playback or input monitoring.

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

Implements AudioIOBase.

Definition at line 2163 of file AudioIO.cpp.

2164 {
2165  auto cleanup = finally ( [this] {
2167  mRecordingSchedule.mCrossfadeData.clear(); // free arrays
2168  } );
2169 
2170  if( mPortStreamV19 == NULL
2171 #ifdef EXPERIMENTAL_MIDI_OUT
2172  && mMidiStream == NULL
2173 #endif
2174  )
2175  return;
2176 
2177  if( Pa_IsStreamStopped( mPortStreamV19 )
2178 #ifdef EXPERIMENTAL_MIDI_OUT
2179  && !mMidiStreamActive
2180 #endif
2181  )
2182  return;
2183 
2184 #if (defined(__WXMAC__) || defined(__WXMSW__)) && wxCHECK_VERSION(3,1,0)
2185  // Re-enable system sleep
2186  wxPowerResource::Release(wxPOWER_RESOURCE_SCREEN);
2187 #endif
2188 
2190  {
2191  // PortAudio callback can use the information that we are stopping to fade
2192  // out the audio. Give PortAudio callback a chance to do so.
2194  long latency;
2195  gPrefs->Read( wxT("/AudioIO/LatencyDuration"), &latency, DEFAULT_LATENCY_DURATION );
2196  // If we can gracefully fade out in 200ms, with the faded-out play buffers making it through
2197  // the sound card, then do so. If we can't, don't wait around. Just stop quickly and accept
2198  // there will be a click.
2199  if( mbMicroFades && (latency < 150 ))
2200  wxMilliSleep( latency + 50);
2201  }
2202 
2203  wxMutexLocker locker(mSuspendAudioThread);
2204 
2205  // No longer need effects processing
2206  if (mNumPlaybackChannels > 0)
2207  {
2209  }
2210 
2211  //
2212  // We got here in one of two ways:
2213  //
2214  // 1. The user clicked the stop button and we therefore want to stop
2215  // as quickly as possible. So we use AbortStream(). If this is
2216  // the case the portaudio stream is still in the Running state
2217  // (see PortAudio state machine docs).
2218  //
2219  // 2. The callback told PortAudio to stop the stream since it had
2220  // reached the end of the selection. The UI thread discovered
2221  // this by noticing that AudioIO::IsActive() returned false.
2222  // IsActive() (which calls Pa_GetStreamActive()) will not return
2223  // false until all buffers have finished playing, so we can call
2224  // AbortStream without losing any samples. If this is the case
2225  // we are in the "callback finished state" (see PortAudio state
2226  // machine docs).
2227  //
2228  // The moral of the story: We can call AbortStream safely, without
2229  // losing samples.
2230  //
2231  // DMM: This doesn't seem to be true; it seems to be necessary to
2232  // call StopStream if the callback brought us here, and AbortStream
2233  // if the user brought us here.
2234  //
2235 
2237 
2238  // Audacity can deadlock if it tries to update meters while
2239  // we're stopping PortAudio (because the meter updating code
2240  // tries to grab a UI mutex while PortAudio tries to join a
2241  // pthread). So we tell the callback to stop updating meters,
2242  // and wait until the callback has left this part of the code
2243  // if it was already there.
2244  mUpdateMeters = false;
2245  while(mUpdatingMeters) {
2246  ::wxSafeYield();
2247  wxMilliSleep( 50 );
2248  }
2249 
2250  // Turn off HW playthrough if PortMixer is being used
2251 
2252  #if defined(USE_PORTMIXER)
2253  if( mPortMixer ) {
2254  #if __WXMAC__
2255  if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
2256  Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
2257  mPreviousHWPlaythrough = -1.0;
2258  #endif
2259  }
2260  #endif
2261 
2262  if (mPortStreamV19) {
2263  Pa_AbortStream( mPortStreamV19 );
2264  Pa_CloseStream( mPortStreamV19 );
2265  mPortStreamV19 = NULL;
2266  }
2267 
2268 #ifdef EXPERIMENTAL_MIDI_OUT
2269  /* Stop Midi playback */
2270  if ( mMidiStream ) {
2271 
2272  mMidiStreamActive = false;
2273 
2274 #ifdef USE_MIDI_THREAD
2275  mMidiThreadFillBuffersLoopRunning = false; // stop output to stream
2276  // but output is in another thread. Wait for output to stop...
2277  while (mMidiThreadFillBuffersLoopActive) {
2278  wxMilliSleep(1);
2279  }
2280 #endif
2281 
2282  mMidiOutputComplete = true;
2283 
2284  // now we can assume "ownership" of the mMidiStream
2285  // if output in progress, send all off, etc.
2286  AllNotesOff();
2287  // AllNotesOff() should be sufficient to stop everything, but
2288  // in Linux, if you Pm_Close() immediately, it looks like
2289  // messages are dropped. ALSA then seems to send All Sound Off
2290  // and Reset All Controllers messages, but not all synthesizers
2291  // respond to these messages. This is probably a bug in PortMidi
2292  // if the All Off messages do not get out, but for security,
2293  // delay a bit so that messages can be delivered before closing
2294  // the stream. Add 2ms of "padding" to avoid any rounding errors.
2295  while (mMaxMidiTimestamp + 2 > MidiTime()) {
2296  wxMilliSleep(1); // deliver the all-off messages
2297  }
2298  Pm_Close(mMidiStream);
2299  mMidiStream = NULL;
2300  mIterator->end();
2301 
2302  // set in_use flags to false
2303  int nTracks = mMidiPlaybackTracks.size();
2304  for (int i = 0; i < nTracks; i++) {
2305  const auto t = mMidiPlaybackTracks[i].get();
2306  Alg_seq_ptr seq = &t->GetSeq();
2307  seq->set_in_use(false);
2308  }
2309 
2310  mIterator.reset(); // just in case someone tries to reference it
2311  }
2312 #endif
2313 
2314  auto pListener = GetListener();
2315 
2316  // If there's no token, we were just monitoring, so we can
2317  // skip this next part...
2318  if (mStreamToken > 0) {
2319  // In either of the above cases, we want to make sure that any
2320  // capture data that made it into the PortAudio callback makes it
2321  // to the target WaveTrack. To do this, we ask the audio thread to
2322  // call FillBuffers one last time (it normally would not do so since
2323  // Pa_GetStreamActive() would now return false
2325 
2327  {
2328  // LLL: Experienced recursive yield here...once.
2329  wxTheApp->Yield(true); // Pass true for onlyIfNeeded to avoid recursive call error.
2330  wxMilliSleep( 50 );
2331  }
2332 
2333  //
2334  // Everything is taken care of. Now, just free all the resources
2335  // we allocated in StartStream()
2336  //
2337 
2338  if (mPlaybackTracks.size() > 0)
2339  {
2340  mPlaybackBuffers.reset();
2341  mPlaybackMixers.reset();
2342  mTimeQueue.mData.reset();
2343  }
2344 
2345  //
2346  // Offset all recorded tracks to account for latency
2347  //
2348  if (mCaptureTracks.size() > 0)
2349  {
2350  mCaptureBuffers.reset();
2351  mResample.reset();
2352 
2353  //
2354  // We only apply latency correction when we actually played back
2355  // tracks during the recording. If we did not play back tracks,
2356  // there's nothing we could be out of sync with. This also covers the
2357  // case that we do not apply latency correction when recording the
2358  // first track in a project.
2359  //
2360 
2361  for (unsigned int i = 0; i < mCaptureTracks.size(); i++) {
2362  // The calls to Flush
2363  // may cause exceptions because of exhaustion of disk space.
2364  // Stop those exceptions here, or else they propagate through too
2365  // many parts of Audacity that are not effects or editing
2366  // operations. GuardedCall ensures that the user sees a warning.
2367 
2368  // Also be sure to Flush each track, at the top of the guarded call,
2369  // relying on the guarantee that the track will be left in a flushed
2370  // state, though the append buffer may be lost.
2371 
2372  GuardedCall( [&] {
2373  WaveTrack* track = mCaptureTracks[i].get();
2374 
2375  // use No-fail-guarantee that track is flushed,
2376  // Partial-guarantee that some initial length of the recording
2377  // is saved.
2378  // See comments in FillBuffers().
2379  track->Flush();
2380  } );
2381  }
2382 
2383 
2384  if (!mLostCaptureIntervals.empty())
2385  {
2386  // This scope may combine many splittings of wave tracks
2387  // into one transaction, lessening the number of checkpoints
2389  if (mOwningProject) {
2390  auto &pIO = ProjectFileIO::Get(*mOwningProject);
2391  pScope.emplace(pIO.GetConnection(), "Dropouts");
2392  }
2393  for (auto &interval : mLostCaptureIntervals) {
2394  auto &start = interval.first;
2395  auto duration = interval.second;
2396  for (auto &track : mCaptureTracks) {
2397  GuardedCall([&] {
2398  track->SyncLockAdjust(start, start + duration);
2399  });
2400  }
2401  }
2402  if (pScope)
2403  pScope->Commit();
2404  }
2405 
2406  if (pListener)
2407  pListener->OnCommitRecording();
2408  }
2409  }
2410 
2411  if (mInputMeter)
2412  mInputMeter->Reset(mRate, false);
2413 
2414  if (mOutputMeter)
2415  mOutputMeter->Reset(mRate, false);
2416 
2417  mInputMeter.Release();
2418  mOutputMeter.Release();
2419  mOwningProject = nullptr;
2420 
2421  if (pListener && mNumCaptureChannels > 0)
2422  pListener->OnAudioIOStopRecording();
2423 
2424  //
2425  // Only set token to 0 after we're totally finished with everything
2426  //
2427  bool wasMonitoring = mStreamToken == 0;
2428  mStreamToken = 0;
2429 
2430  if (mNumPlaybackChannels > 0)
2431  {
2432  wxCommandEvent e(EVT_AUDIOIO_PLAYBACK);
2433  e.SetEventObject(mOwningProject);
2434  e.SetInt(false);
2435  wxTheApp->ProcessEvent(e);
2436  }
2437 
2438  if (mNumCaptureChannels > 0)
2439  {
2440  wxCommandEvent e(wasMonitoring ? EVT_AUDIOIO_MONITOR : EVT_AUDIOIO_CAPTURE);
2441  e.SetEventObject(mOwningProject);
2442  e.SetInt(false);
2443  wxTheApp->ProcessEvent(e);
2444  }
2445 
2446  mNumCaptureChannels = 0;
2448 
2449  mPlaybackTracks.clear();
2450  mCaptureTracks.clear();
2451 #ifdef USE_MIDI
2452  mMidiPlaybackTracks.clear();
2453 #endif
2454 
2455 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
2456  mScrubState.reset();
2457 #endif
2458 
2459  if (pListener) {
2460  // Tell UI to hide sample rate
2461  pListener->OnAudioIORate(0);
2462  }
2463 
2464  // Don't cause a busy wait in the audio thread after stopping scrubbing
2466 }

References AudioIoCallback::ClearRecordingException(), TransactionScope::Commit(), DEFAULT_LATENCY_DURATION, Optional< X >::emplace(), WaveTrack::Flush(), RealtimeEffectManager::Get(), ProjectFileIO::Get(), AudioIoCallback::GetListener(), gPrefs, GuardedCall(), AudioIoCallback::mAudioThreadFillBuffersLoopRunning, AudioIoCallback::mAudioThreadShouldCallFillBuffersOnce, AudioIoCallback::mbMicroFades, AudioIoCallback::mCaptureBuffers, AudioIoCallback::mCaptureTracks, AudioIOBase::RecordingSchedule::mCrossfadeData, AudioIoCallback::TimeQueue::mData, AudioIOBase::mInputMeter, AudioIoCallback::mLostCaptureIntervals, AudioIOBase::mMidiOutputComplete, AudioIOBase::mMidiStreamActive, AudioIoCallback::mNumCaptureChannels, AudioIoCallback::mNumPlaybackChannels, AudioIOBase::mOutputMeter, AudioIOBase::mOwningProject, AudioIoCallback::mPlaybackBuffers, AudioIoCallback::mPlaybackMixers, AudioIOBase::mPlaybackSchedule, AudioIoCallback::mPlaybackTracks, AudioIOBase::mPortStreamV19, AudioIOBase::mRate, AudioIoCallback::mRecordingSchedule, AudioIoCallback::mResample, AudioIOBase::mStreamToken, AudioIoCallback::mSuspendAudioThread, AudioIoCallback::mTimeQueue, AudioIoCallback::mUpdateMeters, AudioIoCallback::mUpdatingMeters, RealtimeEffectManager::RealtimeFinalize(), KeyboardCapture::Release(), and AudioIOBase::PlaybackSchedule::ResetMode().

Referenced by FillBuffers(), 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 948 of file AudioIO.cpp.

949 {
950  const PaDeviceInfo *pInfo = Pa_GetDeviceInfo(getPlayDevIndex(play));
951  const PaDeviceInfo *rInfo = Pa_GetDeviceInfo(getRecordDevIndex(rec));
952 
953  // Valid iff both defined and the same api.
954  return pInfo != nullptr && rInfo != nullptr && pInfo->hostApi == rInfo->hostApi;
955 }

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

Here is the call graph for this function:

Friends And Related Function Documentation

◆ AudioThread

friend class AudioThread
friend

Definition at line 724 of file AudioIO.h.


The documentation for this class was generated from the following files:
AudioIoCallback::mPlaybackQueueMinimum
size_t mPlaybackQueueMinimum
Occupancy of the queue we try to maintain, with bigger batches if needed.
Definition: AudioIO.h:479
Optional::emplace
X & emplace(Args &&... args)
Definition: MemoryX.h:259
AudioIoCallback::mCaptureFormat
sampleFormat mCaptureFormat
Definition: AudioIO.h:488
ClearSamples
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
Definition: SampleFormat.cpp:77
AudioIOBase::mPlaybackSchedule
struct AudioIOBase::PlaybackSchedule mPlaybackSchedule
WaveTrackConstArray
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: AudioIO.h:63
TransportTracks::playbackTracks
WaveTrackArray playbackTracks
Definition: AudioIO.h:92
WaveTrack::Flush
void Flush()
Flush must be called after last Append.
Definition: WaveTrack.cpp:1608
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:68
AudioIOBase::PlaybackSchedule::mT0
double mT0
Playback starts at offset of mT0, which is measured in seconds.
Definition: AudioIOBase.h:330
AudioIoCallback::mPlaybackTracks
WaveTrackArray mPlaybackTracks
Definition: AudioIO.h:464
AudioIOStartStreamOptions::rate
double rate
Definition: AudioIOBase.h:93
AudioIoCallback::mbMicroFades
bool mbMicroFades
Definition: AudioIO.h:470
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:68
AudioIOBase::DeviceName
static wxString DeviceName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:78
AudioIO::AllocateBuffers
bool AllocateBuffers(const AudioIOStartStreamOptions &options, const TransportTracks &tracks, double t0, double t1, double sampleRate, bool scrubbing)
Allocate RingBuffer structures, and others, needed for playback and recording.
Definition: AudioIO.cpp:1830
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
AudioIOBase::RecordingSchedule::TotalCorrection
double TotalCorrection() const
Definition: AudioIOBase.h:322
make_iterator_range
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:625
DEFAULT_LATENCY_CORRECTION
#define DEFAULT_LATENCY_CORRECTION
Definition: RecordingPrefs.h:34
AudioIO::LastPaErrorString
wxString LastPaErrorString()
Definition: AudioIO.cpp:1438
AudioIOStartStreamOptions::listener
std::shared_ptr< AudioIOListener > listener
Definition: AudioIOBase.h:92
Optional
Like a smart pointer, allows for object to not exist (nullptr)
Definition: MemoryX.h:210
AudioIoCallback::mThread
std::unique_ptr< AudioThread > mThread
Definition: AudioIO.h:454
AudioIoCallback::mAudioThreadShouldCallFillBuffersOnce
volatile bool mAudioThreadShouldCallFillBuffersOnce
Definition: AudioIO.h:490
AudioIOBase::RecordingSchedule::mLatencyCorrected
bool mLatencyCorrected
Definition: AudioIOBase.h:320
AudioIOBase::mOwningProject
AudacityProject * mOwningProject
Definition: AudioIOBase.h:266
ScrubbingOptions::MaxAllowedScrubSpeed
static double MaxAllowedScrubSpeed()
Definition: AudioIOBase.h:68
AudioIOBase::mMidiOutputComplete
bool mMidiOutputComplete
True when output reaches mT1.
Definition: AudioIOBase.h:272
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
AudioIOBase::mPortStreamV19
PaStream * mPortStreamV19
Definition: AudioIOBase.h:282
AudacityToPortAudioSampleFormat
static PaSampleFormat AudacityToPortAudioSampleFormat(sampleFormat format)
Definition: AudioIO.cpp:1208
ENV_DB_RANGE
#define ENV_DB_RANGE
Definition: GUISettings.h:16
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
AudioIOBase::RecordingSchedule::ToConsume
double ToConsume() const
Definition: AudioIOBase.cpp:1340
TransportTracks::captureTracks
WaveTrackArray captureTracks
Definition: AudioIO.h:93
AudioIoCallback::mNumPlaybackChannels
unsigned int mNumPlaybackChannels
Definition: AudioIO.h:487
AudioIOBase::mInputMixerWorks
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:300
AudioIoCallback::mNumCaptureChannels
unsigned int mNumCaptureChannels
Definition: AudioIO.h:486
int24Sample
@ int24Sample
Definition: Types.h:713
AudioIOStartStreamOptions::playbackMeter
MeterPanelBase * playbackMeter
Definition: AudioIOBase.h:90
AudioIOBase::getPlayDevIndex
static int getPlayDevIndex(const wxString &devName={})
get the index of the device selected in the preferences.
Definition: AudioIOBase.cpp:727
Mixer::WarpOptions
Definition: Mix.h:83
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1467
AudioIO::GetBestRate
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:2512
AudioIoCallback::mUpdatingMeters
volatile bool mUpdatingMeters
Definition: AudioIO.h:507
ArrayOf::reinit
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:112
AudioIOBase::getRecordDevIndex
static int getRecordDevIndex(const wxString &devName={})
get the index of the supplied (named) recording device, or the device selected in the preferences if ...
Definition: AudioIOBase.cpp:784
AudioIoCallback::mPlaybackSamplesToCopy
size_t mPlaybackSamplesToCopy
Preferred batch size for replenishing the playback RingBuffer.
Definition: AudioIO.h:477
XO
#define XO(s)
Definition: Internat.h:32
ProjectFileIO::Get
static ProjectFileIO & Get(AudacityProject &project)
Definition: ProjectFileIO.cpp:267
AudioIOBase::IsStreamActive
bool IsStreamActive() const
Returns true if the audio i/o is running at all, but not during cleanup.
Definition: AudioIOBase.cpp:353
AudioIOBase::SetCaptureMeter
void SetCaptureMeter(AudacityProject *project, MeterPanelBase *meter)
Definition: AudioIOBase.cpp:312
ScrubbingOptions::MinAllowedScrubSpeed
static double MinAllowedScrubSpeed()
Definition: AudioIOBase.h:70
AudioIOStartStreamOptions::preRoll
double preRoll
Definition: AudioIOBase.h:98
AudioIoCallback::mPlaybackBuffers
ArrayOf< std::unique_ptr< RingBuffer > > mPlaybackBuffers
Definition: AudioIO.h:463
floatSample
@ floatSample
Definition: Types.h:714
AudioIOBase::RecordingSchedule::mLatencyCorrection
double mLatencyCorrection
Definition: AudioIOBase.h:313
AudioIoCallback::mUpdateMeters
bool mUpdateMeters
Definition: AudioIO.h:506
AudioIoCallback::mDetectDropouts
bool mDetectDropouts
Definition: AudioIO.h:547
AudioIoCallback::mRecordingException
wxAtomicInt mRecordingException
Definition: AudioIO.h:540
AudioIOBase::GetSupportedPlaybackRates
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1, double rate=0.0)
Get a list of sample rates the output (playback) device supports.
Definition: AudioIOBase.cpp:513
AudioIO::GetNumCaptureChannels
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:688
AudioIOBase::ugAudioIO
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:262
AudioIoCallback::mAudioThreadFillBuffersLoopActive
volatile bool mAudioThreadFillBuffersLoopActive
Definition: AudioIO.h:492
AudioIOBase::RecordingSchedule::ToDiscard
double ToDiscard() const
Definition: AudioIOBase.cpp:1350
AudioIOBase::IsBusy
bool IsBusy() const
Returns true if audio i/o is busy starting, stopping, playing, or recording.
Definition: AudioIOBase.cpp:345
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:698
AudacityException
Base class for exceptions specially processed by the application.
Definition: AudacityException.h:25
AudioIoCallback::mSoftwarePlaythrough
bool mSoftwarePlaythrough
Definition: AudioIO.h:482
samplePtr
char * samplePtr
Definition: Types.h:729
sampleFormat
sampleFormat
Definition: Types.h:709
WaveTrack::SyncLockAdjust
void SyncLockAdjust(double oldT1, double newT1) override
Definition: WaveTrack.cpp:1130
AudioIOBase::PlaybackSchedule::Init
void Init(double t0, double t1, const AudioIOStartStreamOptions &options, const RecordingSchedule *pRecordingSchedule)
Definition: AudioIOBase.cpp:382
AudioIOBase::mPaused
bool mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:269
sampleCount::as_double
double as_double() const
Definition: Types.h:608
AudioIoCallback::mPauseRec
bool mPauseRec
True if Sound Activated Recording is enabled.
Definition: AudioIO.h:484
AudioIOBase::PlaybackSchedule::RealTimeRestart
void RealTimeRestart()
Definition: AudioIOBase.cpp:1335
AudioIOBase::PlaybackSchedule::RealTimeAdvance
void RealTimeAdvance(double increment)
Definition: AudioIOBase.cpp:1322
RealtimeEffectManager::RealtimeFinalize
void RealtimeFinalize()
Definition: RealtimeEffectManager.cpp:197
AudioIoCallback::mCaptureBuffers
ArrayOf< std::unique_ptr< RingBuffer > > mCaptureBuffers
Definition: AudioIO.h:461
AudioIO::GetCommonlyAvailCapture
size_t GetCommonlyAvailCapture()
Get the number of audio samples ready in all of the recording buffers.
Definition: AudioIO.cpp:2676
AudioIOBase::mMixerOutputVol
float mMixerOutputVol
Definition: AudioIOBase.h:301
AudioIOBase::mOutputMeter
wxWeakRef< MeterPanelBase > mOutputMeter
Definition: AudioIOBase.h:285
AudioIoCallback::mLastPlaybackTimeMillis
wxLongLong mLastPlaybackTimeMillis
Definition: AudioIO.h:494
PaError
int PaError
Definition: AudioIO.h:67
FindProjectFrame
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null.
Definition: Project.h:172
AudioIOBase::PlaybackSchedule::PlayingStraight
bool PlayingStraight() const
Definition: AudioIOBase.h:417
AudioIoCallback::mRecordingSchedule
RecordingSchedule mRecordingSchedule
Definition: AudioIO.h:562
AudioIOBase::PlaybackSchedule::Interactive
bool Interactive() const
Definition: AudioIOBase.h:421
AudioIOStartStreamOptions::captureMeter
MeterPanelBase * captureMeter
Definition: AudioIOBase.h:90
format
int format
Definition: ExportPCM.cpp:54
AudioIOBase::PlaybackSchedule::SetTrackTime
void SetTrackTime(double time)
Set current track time value, unadjusted.
Definition: AudioIOBase.h:391
DeviceManager::GetTimeSinceRescan
float GetTimeSinceRescan()
Definition: DeviceManager.cpp:313
AudioIoCallback::mLostSamples
unsigned long long mLostSamples
Definition: AudioIO.h:489
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94
AudioIoCallback::mPlaybackMixers
ArrayOf< std::unique_ptr< Mixer > > mPlaybackMixers
Definition: AudioIO.h:466
AudioIOBase::mRate
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:280
AudioIoCallback::mSuspendAudioThread
wxMutex mSuspendAudioThread
Definition: AudioIO.h:525
AudioIoCallback::mLastRecordingOffset
volatile double mLastRecordingOffset
Definition: AudioIO.h:501
AudioIOBase::mMidiStreamActive
bool mMidiStreamActive
mMidiStreamActive tells when mMidiStream is open for output
Definition: AudioIOBase.h:275
AudioIoCallback::TimeQueue::mTail
struct AudioIoCallback::TimeQueue::Cursor mTail
AudioIOBase::PlaybackSchedule::PLAY_LOOPED
@ PLAY_LOOPED
Definition: AudioIOBase.h:361
AudioIOBase::GetSupportedSampleRates
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.
Definition: AudioIOBase.cpp:648
AudioIOBase::PlaybackSchedule::ResetMode
void ResetMode()
Definition: AudioIOBase.h:415
AudioIOBase::SetMixer
void SetMixer(int inputSource)
Definition: AudioIOBase.cpp:101
AudioIoCallback::mAudioThreadFillBuffersLoopRunning
volatile bool mAudioThreadFillBuffersLoopRunning
Definition: AudioIO.h:491
AudioIOBase::HostName
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:85
AudioIoCallback::mNextStreamToken
static int mNextStreamToken
Definition: AudioIO.h:467
AudioIoCallback::mCachedBestRateCapturing
static bool mCachedBestRateCapturing
Definition: AudioIO.h:521
AudioIOBase::mInputMeter
wxWeakRef< MeterPanelBase > mInputMeter
Definition: AudioIOBase.h:284
ENV_DB_KEY
#define ENV_DB_KEY
Definition: GUISettings.h:15
KeyboardCapture::Release
void Release(wxWindow *handler)
Definition: KeyboardCapture.cpp:74
audacityAudioCallback
int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
Definition: AudioIO.cpp:3633
AudioIO::StartPortAudioStream
bool StartPortAudioStream(const AudioIOStartStreamOptions &options, unsigned int numPlaybackChannels, unsigned int numCaptureChannels, sampleFormat captureFormat)
Opens the portaudio stream(s) used to do playback or recording (or both) through.
Definition: AudioIO.cpp:1221
RealtimeEffectManager::Get
static RealtimeEffectManager & Get()
Definition: RealtimeEffectManager.cpp:43
RealtimeEffectManager::RealtimeSuspend
void RealtimeSuspend()
Definition: RealtimeEffectManager.cpp:217
AudioIoCallback::TimeQueue::Producer
void Producer(const PlaybackSchedule &schedule, double rate, double scrubSpeed, size_t nSamples)
Definition: AudioIO.cpp:4541
AudioIoCallback::ClearRecordingException
void ClearRecordingException()
Definition: AudioIO.h:543
QualityPrefs::SampleFormatChoice
static sampleFormat SampleFormatChoice()
Definition: QualityPrefs.cpp:246
ShowErrorDialog
void ShowErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, const bool Close, const wxString &log)
Displays an error dialog with a button that offers help.
Definition: ErrorDialog.cpp:148
SampleBuffer
Definition: SampleFormat.h:53
AudioIOStartStreamOptions::pStartTime
double * pStartTime
Definition: AudioIOBase.h:97
AudioIOBase::RecordingSchedule::mCrossfadeData
PRCrossfadeData mCrossfadeData
Definition: AudioIOBase.h:315
LAT1CTOWX
#define LAT1CTOWX(X)
Definition: Internat.h:161
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
AudioIoCallback::mCaptureRingBufferSecs
double mCaptureRingBufferSecs
Definition: AudioIO.h:474
AudioIOBase::RecordingSchedule::mDuration
double mDuration
Definition: AudioIOBase.h:314
AudioIoCallback::mTimeQueue
struct AudioIoCallback::TimeQueue mTimeQueue
AudioIoCallback::GetCommonlyReadyPlayback
size_t GetCommonlyReadyPlayback()
Get the number of audio samples ready in all of the playback buffers.
Definition: AudioIO.cpp:2664
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:151
AudioIOBase::GetSupportedCaptureRates
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1, double rate=0.0)
Get a list of sample rates the input (recording) device supports.
Definition: AudioIOBase.cpp:578
AudioIOStartStreamOptions::pCrossfadeData
PRCrossfadeData * pCrossfadeData
Definition: AudioIOBase.h:108
AudioIoCallback::mUsingAlsa
bool mUsingAlsa
Definition: AudioIO.h:516
AudioIO::StartStreamCleanup
void StartStreamCleanup(bool bOnlyBuffers=false)
Clean up after StartStream if it fails.
Definition: AudioIO.cpp:2019
_
#define _(s)
Definition: Internat.h:76
AudioIoCallback::mSilenceLevel
float mSilenceLevel
Definition: AudioIO.h:485
AudioIOBase::PlaybackSchedule::GetTrackTime
double GetTrackTime() const
Get current track time value, unadjusted.
Definition: AudioIOBase.h:386
sampleCount
Definition: Types.h:581
AudioIoCallback::mSeek
double mSeek
Definition: AudioIO.h:472
AudioIOBase::RecordingSchedule::mPosition
double mPosition
Definition: AudioIOBase.h:319
AudioIO::StopStream
void StopStream() override
Stop recording, playback or input monitoring.
Definition: AudioIO.cpp:2163
AudioIO::SetMeters
void SetMeters()
Set the current VU meters - this should be done once after each call to StartStream currently.
Definition: AudioIO.cpp:2153
Verbatim
TranslatableString Verbatim(wxString str)
Definition: Types.h:573
WarningDialogKey
wxString WarningDialogKey(const wxString &internalDialogName)
Definition: Prefs.cpp:368
AudioIoCallback::mLostCaptureIntervals
std::vector< std::pair< double, double > > mLostCaptureIntervals
Definition: AudioIO.h:546
AudioIOBase::PlaybackSchedule::ClampTrackTime
double ClampTrackTime(double trackTime) const
Clamps argument to be between mT0 and mT1.
Definition: AudioIOBase.cpp:457
GuardedCall
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), const F3 &delayedHandler={})
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Definition: AudacityException.h:187
AudioIoCallback::mCachedBestRatePlaying
static bool mCachedBestRatePlaying
Definition: AudioIO.h:520
DeviceManager::Instance
static DeviceManager * Instance()
Gets the singleton instance.
Definition: DeviceManager.cpp:47
AudioIOBase::PlaybackSchedule::RealTimeRemaining
double RealTimeRemaining() const
Definition: AudioIOBase.cpp:1317
AudioIoCallback::mLastPaError
PaError mLastPaError
Definition: AudioIO.h:502
AudioIoCallback::GetListener
std::shared_ptr< AudioIOListener > GetListener() const
Definition: AudioIO.h:288
AudioIOBase::mCachedBestRateIn
static double mCachedBestRateIn
Definition: AudioIOBase.h:309
RealtimeEffectManager::RealtimeResume
void RealtimeResume()
Definition: RealtimeEffectManager.cpp:250
AudioIOBase::PlaybackSchedule::Looping
bool Looping() const
Definition: AudioIOBase.h:418
AudioIOBase::PlaybackSchedule::RealTimeInit
void RealTimeInit(double trackTime)
Definition: AudioIOBase.cpp:1327
AudioIoCallback::TimeQueue::mData
Doubles mData
Definition: AudioIO.h:568
AudioIoCallback::mResample
ArrayOf< std::unique_ptr< Resample > > mResample
Definition: AudioIO.h:460
AudioIOBase::mStreamToken
volatile int mStreamToken
Definition: AudioIOBase.h:277
AudioIoCallback::mListener
std::weak_ptr< AudioIOListener > mListener
Definition: AudioIO.h:509
DefaultDelayedHandlerAction
A default template parameter for GuardedCall.
Definition: AudacityException.h:106
AudioIOStartStreamOptions::pProject
AudacityProject * pProject
Definition: AudioIOBase.h:89
AudioIO::GetCommonlyFreePlayback
size_t GetCommonlyFreePlayback()
Get the number of audio samples free in all of the playback buffers.
Definition: AudioIO.cpp:2653
AudioIOStartStreamOptions::playbackStreamPrimer
std::function< unsigned long() > playbackStreamPrimer
Definition: AudioIOBase.h:113
TransactionScope::Commit
bool Commit()
Definition: DBConnection.cpp:623
AudioIO
AudioIO uses the PortAudio library to play and record sound.
Definition: AudioIO.h:589
TimeQueueGrainSize
constexpr size_t TimeQueueGrainSize
Definition: AudioIO.cpp:527
AudioIOBase::HandleDeviceChange
void HandleDeviceChange()
update state after changing what audio devices are selected
Definition: AudioIOBase.cpp:110
AudioIO::Get
static AudioIO * Get()
Definition: AudioIO.cpp:505
safenew
#define safenew
Definition: MemoryX.h:8
AudioIOBase::RecordingSchedule::mPreRoll
double mPreRoll
Definition: AudioIOBase.h:312
lrint
#define lrint(dbl)
Definition: float_cast.h:148
AudioIoCallback::mMinCaptureSecsToCopy
double mMinCaptureSecsToCopy
Definition: AudioIO.h:481
DEFAULT_LATENCY_DURATION
#define DEFAULT_LATENCY_DURATION
Definition: RecordingPrefs.h:33
TransportTracks::prerollTracks
WaveTrackConstArray prerollTracks
Definition: AudioIO.h:99
AudioIoCallback::TimeQueue::mHead
struct AudioIoCallback::TimeQueue::Cursor mHead
Mixer
Functions for doing the mixdown of the tracks.
Definition: Mix.h:78
AudioIoCallback::SetRecordingException
void SetRecordingException()
Definition: AudioIO.h:541
AudioIoCallback::mCaptureTracks
WaveTrackArray mCaptureTracks
Definition: AudioIO.h:462
AudioIOBase::RecordingSchedule::Consumed
double Consumed() const
Definition: AudioIOBase.cpp:1345
AudioIO::AudioIO
AudioIO()
Definition: AudioIO.cpp:957
AudioIoCallback::mFactor
double mFactor
Definition: AudioIO.h:468
AudioIoCallback::TimeQueue::mLastTime
double mLastTime
Definition: AudioIO.h:570
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:82
AudioIOBase::PlaybackSchedule::mWarpedTime
double mWarpedTime
Definition: AudioIOBase.h:342
AudioIoCallback::mCachedBestRateOut
static double mCachedBestRateOut
Definition: AudioIO.h:519
AudioIoCallback::TimeQueue::mSize
size_t mSize
Definition: AudioIO.h:569
AudioIoCallback::mPlaybackRingBufferSecs
double mPlaybackRingBufferSecs
Definition: AudioIO.h:473
AudioIOBase::mEmulateMixerOutputVol
bool mEmulateMixerOutputVol
Definition: AudioIOBase.h:292