mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-16 16:47:41 +02:00
Separate AudioIOBase from AudioIO
This commit is contained in:
parent
42a4f55ffe
commit
51051ee933
@ -1478,7 +1478,7 @@ bool AudacityApp::OnInit()
|
|||||||
// More initialization
|
// More initialization
|
||||||
|
|
||||||
InitDitherers();
|
InitDitherers();
|
||||||
InitAudioIO();
|
AudioIO::Init();
|
||||||
|
|
||||||
#ifdef __WXMAC__
|
#ifdef __WXMAC__
|
||||||
|
|
||||||
@ -2041,7 +2041,7 @@ int AudacityApp::OnExit()
|
|||||||
|
|
||||||
DeinitFFT();
|
DeinitFFT();
|
||||||
|
|
||||||
DeinitAudioIO();
|
AudioIO::Deinit();
|
||||||
|
|
||||||
// Terminate the PluginManager (must be done before deleting the locale)
|
// Terminate the PluginManager (must be done before deleting the locale)
|
||||||
PluginManager::Get().Terminate();
|
PluginManager::Get().Terminate();
|
||||||
|
1362
src/AudioIO.cpp
1362
src/AudioIO.cpp
File diff suppressed because it is too large
Load Diff
438
src/AudioIO.h
438
src/AudioIO.h
@ -15,16 +15,12 @@
|
|||||||
|
|
||||||
#include "Audacity.h" // for USE_* macros
|
#include "Audacity.h" // for USE_* macros
|
||||||
|
|
||||||
|
#include "AudioIOBase.h" // to inherit
|
||||||
|
|
||||||
#include "Experimental.h"
|
#include "Experimental.h"
|
||||||
|
|
||||||
#include "portaudio.h"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
#include <wx/atomic.h> // member variable
|
#include <wx/atomic.h> // member variable
|
||||||
#include <wx/weakref.h> // member variable
|
|
||||||
|
|
||||||
#ifdef USE_MIDI
|
#ifdef USE_MIDI
|
||||||
|
|
||||||
@ -44,22 +40,17 @@ using NoteTrackConstArray = std::vector < std::shared_ptr< const NoteTrack > >;
|
|||||||
|
|
||||||
#endif // USE_MIDI
|
#endif // USE_MIDI
|
||||||
|
|
||||||
#if USE_PORTMIXER
|
|
||||||
#include "../lib-src/portmixer/include/portmixer.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <wx/event.h> // to declare custom event types
|
#include <wx/event.h> // to declare custom event types
|
||||||
|
|
||||||
#include "SampleFormat.h"
|
#include "SampleFormat.h"
|
||||||
|
|
||||||
class wxArrayString;
|
class wxArrayString;
|
||||||
|
class AudioIOBase;
|
||||||
class AudioIO;
|
class AudioIO;
|
||||||
class RingBuffer;
|
class RingBuffer;
|
||||||
class Mixer;
|
class Mixer;
|
||||||
class Resample;
|
class Resample;
|
||||||
class BoundedEnvelope;
|
|
||||||
class AudioThread;
|
class AudioThread;
|
||||||
class MeterPanel;
|
|
||||||
class SelectedRegion;
|
class SelectedRegion;
|
||||||
|
|
||||||
class AudacityProject;
|
class AudacityProject;
|
||||||
@ -68,19 +59,8 @@ class WaveTrack;
|
|||||||
using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >;
|
using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >;
|
||||||
using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >;
|
using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >;
|
||||||
|
|
||||||
extern AUDACITY_DLL_API AudioIO *gAudioIO;
|
|
||||||
|
|
||||||
void InitAudioIO();
|
|
||||||
void DeinitAudioIO();
|
|
||||||
wxString DeviceName(const PaDeviceInfo* info);
|
|
||||||
wxString HostName(const PaDeviceInfo* info);
|
|
||||||
bool ValidateDeviceNames();
|
bool ValidateDeviceNames();
|
||||||
|
|
||||||
class AudioIOListener;
|
|
||||||
|
|
||||||
// #include <cfloat> if you need this constant
|
|
||||||
#define BAD_STREAM_TIME (-DBL_MAX)
|
|
||||||
|
|
||||||
#define MAX_MIDI_BUFFER_SIZE 5000
|
#define MAX_MIDI_BUFFER_SIZE 5000
|
||||||
#define DEFAULT_SYNTH_LATENCY 5
|
#define DEFAULT_SYNTH_LATENCY 5
|
||||||
|
|
||||||
@ -108,48 +88,6 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
|||||||
// So leave the separate thread ENABLED.
|
// So leave the separate thread ENABLED.
|
||||||
#define USE_MIDI_THREAD
|
#define USE_MIDI_THREAD
|
||||||
|
|
||||||
struct ScrubbingOptions;
|
|
||||||
|
|
||||||
using PRCrossfadeData = std::vector< std::vector < float > >;
|
|
||||||
|
|
||||||
// To avoid growing the argument list of StartStream, add fields here
|
|
||||||
struct AudioIOStartStreamOptions
|
|
||||||
{
|
|
||||||
explicit
|
|
||||||
AudioIOStartStreamOptions(AudacityProject *pProject_, double rate_)
|
|
||||||
: pProject{ pProject_ }
|
|
||||||
, envelope(nullptr)
|
|
||||||
, listener(NULL)
|
|
||||||
, rate(rate_)
|
|
||||||
, playLooped(false)
|
|
||||||
, cutPreviewGapStart(0.0)
|
|
||||||
, cutPreviewGapLen(0.0)
|
|
||||||
, pStartTime(NULL)
|
|
||||||
, preRoll(0.0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
AudacityProject *pProject{};
|
|
||||||
MeterPanel *captureMeter{}, *playbackMeter{};
|
|
||||||
BoundedEnvelope *envelope; // for time warping
|
|
||||||
AudioIOListener* listener;
|
|
||||||
double rate;
|
|
||||||
bool playLooped;
|
|
||||||
double cutPreviewGapStart;
|
|
||||||
double cutPreviewGapLen;
|
|
||||||
double * pStartTime;
|
|
||||||
double preRoll;
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
|
||||||
// Non-null value indicates that scrubbing will happen
|
|
||||||
// (do not specify a time track, looping, or recording, which
|
|
||||||
// are all incompatible with scrubbing):
|
|
||||||
ScrubbingOptions *pScrubbingOptions {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// contents may get swapped with empty vector
|
|
||||||
PRCrossfadeData *pCrossfadeData{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TransportTracks {
|
struct TransportTracks {
|
||||||
WaveTrackArray playbackTracks;
|
WaveTrackArray playbackTracks;
|
||||||
WaveTrackArray captureTracks;
|
WaveTrackArray captureTracks;
|
||||||
@ -296,7 +234,9 @@ void MessageBuffer<Data>::Write( Data &&data )
|
|||||||
mSlots[idx].mBusy.store( false, std::memory_order_release );
|
mSlots[idx].mBusy.store( false, std::memory_order_release );
|
||||||
}
|
}
|
||||||
|
|
||||||
class AUDACITY_DLL_API AudioIoCallback {
|
class AUDACITY_DLL_API AudioIoCallback /* not final */
|
||||||
|
: public AudioIOBase
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
AudioIoCallback();
|
AudioIoCallback();
|
||||||
~AudioIoCallback();
|
~AudioIoCallback();
|
||||||
@ -374,9 +314,6 @@ public:
|
|||||||
double AudioTime() { return mPlaybackSchedule.mT0 + mNumFrames / mRate; }
|
double AudioTime() { return mPlaybackSchedule.mT0 + mNumFrames / mRate; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** \brief Find out if playback / recording is currently paused */
|
|
||||||
bool IsPaused() const;
|
|
||||||
|
|
||||||
|
|
||||||
/** \brief Get the number of audio samples ready in all of the playback
|
/** \brief Get the number of audio samples ready in all of the playback
|
||||||
* buffers.
|
* buffers.
|
||||||
@ -452,12 +389,8 @@ public:
|
|||||||
double mNextEventTime;
|
double mNextEventTime;
|
||||||
/// Track of next event
|
/// Track of next event
|
||||||
NoteTrack *mNextEventTrack;
|
NoteTrack *mNextEventTrack;
|
||||||
/// True when output reaches mT1
|
|
||||||
bool mMidiOutputComplete{ true };
|
|
||||||
/// Is the next event a note-on?
|
/// Is the next event a note-on?
|
||||||
bool mNextIsNoteOn;
|
bool mNextIsNoteOn;
|
||||||
/// mMidiStreamActive tells when mMidiStream is open for output
|
|
||||||
bool mMidiStreamActive;
|
|
||||||
/// when true, mSendMidiState means send only updates, not note-on's,
|
/// when true, mSendMidiState means send only updates, not note-on's,
|
||||||
/// used to send state changes that precede the selected notes
|
/// used to send state changes that precede the selected notes
|
||||||
bool mSendMidiState;
|
bool mSendMidiState;
|
||||||
@ -494,11 +427,8 @@ public:
|
|||||||
WaveTrackArray mPlaybackTracks;
|
WaveTrackArray mPlaybackTracks;
|
||||||
|
|
||||||
ArrayOf<std::unique_ptr<Mixer>> mPlaybackMixers;
|
ArrayOf<std::unique_ptr<Mixer>> mPlaybackMixers;
|
||||||
volatile int mStreamToken;
|
|
||||||
static int mNextStreamToken;
|
static int mNextStreamToken;
|
||||||
double mFactor;
|
double mFactor;
|
||||||
/// Audio playback rate in samples per second
|
|
||||||
double mRate;
|
|
||||||
unsigned long mMaxFramesOutput; // The actual number of frames output.
|
unsigned long mMaxFramesOutput; // The actual number of frames output.
|
||||||
bool mbMicroFades;
|
bool mbMicroFades;
|
||||||
|
|
||||||
@ -512,9 +442,6 @@ public:
|
|||||||
size_t mPlaybackQueueMinimum;
|
size_t mPlaybackQueueMinimum;
|
||||||
|
|
||||||
double mMinCaptureSecsToCopy;
|
double mMinCaptureSecsToCopy;
|
||||||
/// True if audio playback is paused
|
|
||||||
bool mPaused;
|
|
||||||
PaStream *mPortStreamV19;
|
|
||||||
bool mSoftwarePlaythrough;
|
bool mSoftwarePlaythrough;
|
||||||
/// True if Sound Activated Recording is enabled
|
/// True if Sound Activated Recording is enabled
|
||||||
bool mPauseRec;
|
bool mPauseRec;
|
||||||
@ -539,28 +466,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
AudacityProject *mOwningProject;
|
|
||||||
wxWeakRef<MeterPanel> mInputMeter{};
|
|
||||||
wxWeakRef<MeterPanel> mOutputMeter{};
|
|
||||||
bool mUpdateMeters;
|
bool mUpdateMeters;
|
||||||
volatile bool mUpdatingMeters;
|
volatile bool mUpdatingMeters;
|
||||||
|
|
||||||
#if USE_PORTMIXER
|
|
||||||
PxMixer *mPortMixer;
|
|
||||||
float mPreviousHWPlaythrough;
|
|
||||||
#endif /* USE_PORTMIXER */
|
|
||||||
|
|
||||||
bool mEmulateMixerOutputVol;
|
|
||||||
/** @brief Can we control the hardware input level?
|
|
||||||
*
|
|
||||||
* This flag is set to true if using portmixer to control the
|
|
||||||
* input volume seems to be working (and so we offer the user the control),
|
|
||||||
* and to false (locking the control out) otherwise. This avoids stupid
|
|
||||||
* scaled clipping problems when trying to do software emulated input volume
|
|
||||||
* control */
|
|
||||||
bool mInputMixerWorks;
|
|
||||||
float mMixerOutputVol;
|
|
||||||
|
|
||||||
AudioIOListener* mListener;
|
AudioIOListener* mListener;
|
||||||
|
|
||||||
friend class AudioThread;
|
friend class AudioThread;
|
||||||
@ -568,17 +476,9 @@ protected:
|
|||||||
friend class MidiThread;
|
friend class MidiThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
friend void InitAudioIO();
|
|
||||||
|
|
||||||
bool mUsingAlsa { false };
|
bool mUsingAlsa { false };
|
||||||
|
|
||||||
// For cacheing supported sample rates
|
// For cacheing supported sample rates
|
||||||
static int mCachedPlaybackIndex;
|
|
||||||
static std::vector<long> mCachedPlaybackRates;
|
|
||||||
static int mCachedCaptureIndex;
|
|
||||||
static std::vector<long> mCachedCaptureRates;
|
|
||||||
static std::vector<long> mCachedSampleRates;
|
|
||||||
static double mCachedBestRateIn;
|
|
||||||
static double mCachedBestRateOut;
|
static double mCachedBestRateOut;
|
||||||
static bool mCachedBestRatePlaying;
|
static bool mCachedBestRatePlaying;
|
||||||
static bool mCachedBestRateCapturing;
|
static bool mCachedBestRateCapturing;
|
||||||
@ -622,154 +522,7 @@ public:
|
|||||||
bool mDetectUpstreamDropouts{ true };
|
bool mDetectUpstreamDropouts{ true };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct RecordingSchedule {
|
RecordingSchedule mRecordingSchedule{};
|
||||||
double mPreRoll{};
|
|
||||||
double mLatencyCorrection{}; // negative value usually
|
|
||||||
double mDuration{};
|
|
||||||
PRCrossfadeData mCrossfadeData;
|
|
||||||
|
|
||||||
// These are initialized by the main thread, then updated
|
|
||||||
// only by the thread calling FillBuffers:
|
|
||||||
double mPosition{};
|
|
||||||
bool mLatencyCorrected{};
|
|
||||||
|
|
||||||
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
|
|
||||||
double ToConsume() const;
|
|
||||||
double Consumed() const;
|
|
||||||
double ToDiscard() const;
|
|
||||||
} mRecordingSchedule{};
|
|
||||||
|
|
||||||
struct PlaybackSchedule {
|
|
||||||
/// Playback starts at offset of mT0, which is measured in seconds.
|
|
||||||
double mT0;
|
|
||||||
/// Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 during scrubbing.
|
|
||||||
double mT1;
|
|
||||||
/// Current track time position during playback, in seconds.
|
|
||||||
/// Initialized by the main thread but updated by worker threads during
|
|
||||||
/// playback or recording, and periodically reread by the main thread for
|
|
||||||
/// purposes such as display update.
|
|
||||||
std::atomic<double> mTime;
|
|
||||||
|
|
||||||
/// Accumulated real time (not track position), starting at zero (unlike
|
|
||||||
/// mTime), and wrapping back to zero each time around looping play.
|
|
||||||
/// Thus, it is the length in real seconds between mT0 and mTime.
|
|
||||||
double mWarpedTime;
|
|
||||||
|
|
||||||
/// Real length to be played (if looping, for each pass) after warping via a
|
|
||||||
/// time track, computed just once when starting the stream.
|
|
||||||
/// Length in real seconds between mT0 and mT1. Always positive.
|
|
||||||
double mWarpedLength;
|
|
||||||
|
|
||||||
// mWarpedTime and mWarpedLength are irrelevant when scrubbing,
|
|
||||||
// else they are used in updating mTime,
|
|
||||||
// and when not scrubbing or playing looped, mTime is also used
|
|
||||||
// in the test for termination of playback.
|
|
||||||
|
|
||||||
// with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
|
|
||||||
// (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
|
|
||||||
|
|
||||||
const BoundedEnvelope *mEnvelope;
|
|
||||||
|
|
||||||
volatile enum {
|
|
||||||
PLAY_STRAIGHT,
|
|
||||||
PLAY_LOOPED,
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
|
||||||
PLAY_SCRUB,
|
|
||||||
PLAY_AT_SPEED, // a version of PLAY_SCRUB.
|
|
||||||
#endif
|
|
||||||
} mPlayMode { PLAY_STRAIGHT };
|
|
||||||
double mCutPreviewGapStart;
|
|
||||||
double mCutPreviewGapLen;
|
|
||||||
|
|
||||||
void Init(
|
|
||||||
double t0, double t1,
|
|
||||||
const AudioIOStartStreamOptions &options,
|
|
||||||
const RecordingSchedule *pRecordingSchedule );
|
|
||||||
|
|
||||||
/** \brief True if the end time is before the start time */
|
|
||||||
bool ReversedTime() const
|
|
||||||
{
|
|
||||||
return mT1 < mT0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Get current track time value, unadjusted
|
|
||||||
*
|
|
||||||
* Returns a time in seconds.
|
|
||||||
*/
|
|
||||||
double GetTrackTime() const
|
|
||||||
{ return mTime.load(std::memory_order_relaxed); }
|
|
||||||
|
|
||||||
/** \brief Set current track time value, unadjusted
|
|
||||||
*/
|
|
||||||
void SetTrackTime( double time )
|
|
||||||
{ mTime.store(time, std::memory_order_relaxed); }
|
|
||||||
|
|
||||||
/** \brief Clamps argument to be between mT0 and mT1
|
|
||||||
*
|
|
||||||
* Returns the bound if the value is out of bounds; does not wrap.
|
|
||||||
* Returns a time in seconds.
|
|
||||||
*/
|
|
||||||
double ClampTrackTime( double trackTime ) const;
|
|
||||||
|
|
||||||
/** \brief Clamps mTime to be between mT0 and mT1
|
|
||||||
*
|
|
||||||
* Returns the bound if the value is out of bounds; does not wrap.
|
|
||||||
* Returns a time in seconds.
|
|
||||||
*/
|
|
||||||
double LimitTrackTime() const;
|
|
||||||
|
|
||||||
/** \brief Normalizes mTime, clamping it and handling gaps from cut preview.
|
|
||||||
*
|
|
||||||
* Clamps the time (unless scrubbing), and skips over the cut section.
|
|
||||||
* Returns a time in seconds.
|
|
||||||
*/
|
|
||||||
double NormalizeTrackTime() const;
|
|
||||||
|
|
||||||
void ResetMode() { mPlayMode = PLAY_STRAIGHT; }
|
|
||||||
|
|
||||||
bool PlayingStraight() const { return mPlayMode == PLAY_STRAIGHT; }
|
|
||||||
bool Looping() const { return mPlayMode == PLAY_LOOPED; }
|
|
||||||
bool Scrubbing() const { return mPlayMode == PLAY_SCRUB; }
|
|
||||||
bool PlayingAtSpeed() const { return mPlayMode == PLAY_AT_SPEED; }
|
|
||||||
bool Interactive() const { return Scrubbing() || PlayingAtSpeed(); }
|
|
||||||
|
|
||||||
// Returns true if a loop pass, or the sole pass of straight play,
|
|
||||||
// is completed at the current value of mTime
|
|
||||||
bool PassIsComplete() const;
|
|
||||||
|
|
||||||
// Returns true if time equals t1 or is on opposite side of t1, to t0
|
|
||||||
bool Overruns( double trackTime ) const;
|
|
||||||
|
|
||||||
// Compute the NEW track time for the given one and a real duration,
|
|
||||||
// taking into account whether the schedule is for looping
|
|
||||||
double AdvancedTrackTime(
|
|
||||||
double trackTime, double realElapsed, double speed) const;
|
|
||||||
|
|
||||||
// Use the function above in the callback after consuming samples from the
|
|
||||||
// playback ring buffers, during usual straight or looping play
|
|
||||||
void TrackTimeUpdate(double realElapsed);
|
|
||||||
|
|
||||||
// Convert a nonnegative real duration to an increment of track time
|
|
||||||
// relative to mT0.
|
|
||||||
double TrackDuration(double realElapsed) const;
|
|
||||||
|
|
||||||
// Convert time between mT0 and argument to real duration, according to
|
|
||||||
// time track if one is given; result is always nonnegative
|
|
||||||
double RealDuration(double trackTime1) const;
|
|
||||||
|
|
||||||
// How much real time left?
|
|
||||||
double RealTimeRemaining() const;
|
|
||||||
|
|
||||||
// Advance the real time position
|
|
||||||
void RealTimeAdvance( double increment );
|
|
||||||
|
|
||||||
// Determine starting duration within the first pass -- sometimes not
|
|
||||||
// zero
|
|
||||||
void RealTimeInit( double trackTime );
|
|
||||||
|
|
||||||
void RealTimeRestart();
|
|
||||||
|
|
||||||
} mPlaybackSchedule;
|
|
||||||
|
|
||||||
// Another circular buffer
|
// Another circular buffer
|
||||||
// Holds track time values corresponding to every nth sample in the playback
|
// Holds track time values corresponding to every nth sample in the playback
|
||||||
@ -794,17 +547,17 @@ protected:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AUDACITY_DLL_API AudioIO final : public AudioIoCallback {
|
class AUDACITY_DLL_API AudioIO final
|
||||||
|
: public AudioIoCallback
|
||||||
|
{
|
||||||
|
|
||||||
public:
|
|
||||||
AudioIO();
|
AudioIO();
|
||||||
~AudioIO();
|
~AudioIO();
|
||||||
|
|
||||||
|
public:
|
||||||
// This might return null during application startup or shutdown
|
// This might return null during application startup or shutdown
|
||||||
static AudioIO *Get();
|
static AudioIO *Get();
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
AudioIOListener* GetListener() { return mListener; }
|
AudioIOListener* GetListener() { return mListener; }
|
||||||
void SetListener(AudioIOListener* listener);
|
void SetListener(AudioIOListener* listener);
|
||||||
|
|
||||||
@ -822,7 +575,7 @@ public:
|
|||||||
* Allocates buffers for recording and playback, gets the Audio thread to
|
* Allocates buffers for recording and playback, gets the Audio thread to
|
||||||
* fill them, and sets the stream rolling.
|
* fill them, and sets the stream rolling.
|
||||||
* If successful, returns a token identifying this particular stream
|
* If successful, returns a token identifying this particular stream
|
||||||
* instance. For use with IsStreamActive() below */
|
* instance. For use with IsStreamActive() */
|
||||||
|
|
||||||
int StartStream(const TransportTracks &tracks,
|
int StartStream(const TransportTracks &tracks,
|
||||||
double t0, double t1,
|
double t0, double t1,
|
||||||
@ -855,21 +608,6 @@ public:
|
|||||||
double GetLastScrubTime() const;
|
double GetLastScrubTime() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** \brief Returns true if audio i/o is busy starting, stopping, playing,
|
|
||||||
* or recording.
|
|
||||||
*
|
|
||||||
* When this is false, it's safe to start playing or recording */
|
|
||||||
bool IsBusy() const;
|
|
||||||
|
|
||||||
/** \brief Returns true if the audio i/o is running at all, but not during
|
|
||||||
* cleanup
|
|
||||||
*
|
|
||||||
* Doesn't return true if the device has been closed but some disk i/o or
|
|
||||||
* cleanup is still going on. If you want to know if it's safe to start a
|
|
||||||
* NEW stream, use IsBusy() */
|
|
||||||
bool IsStreamActive() const;
|
|
||||||
bool IsStreamActive(int token) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wxString LastPaErrorString();
|
wxString LastPaErrorString();
|
||||||
|
|
||||||
@ -899,17 +637,6 @@ public:
|
|||||||
bool GetHasSolo() { return mHasSolo; }
|
bool GetHasSolo() { return mHasSolo; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** \brief Returns true if the stream is active, or even if audio I/O is
|
|
||||||
* busy cleaning up its data or writing to disk.
|
|
||||||
*
|
|
||||||
* This is used by TrackPanel to determine when a track has been completely
|
|
||||||
* recorded, and it's safe to flush to disk. */
|
|
||||||
bool IsAudioTokenActive(int token) const;
|
|
||||||
|
|
||||||
/** \brief Returns true if we're monitoring input (but not recording or
|
|
||||||
* playing actual audio) */
|
|
||||||
bool IsMonitoring() const;
|
|
||||||
|
|
||||||
/** \brief Pause and un-pause playback and recording */
|
/** \brief Pause and un-pause playback and recording */
|
||||||
void SetPaused(bool state);
|
void SetPaused(bool state);
|
||||||
|
|
||||||
@ -919,7 +646,6 @@ public:
|
|||||||
* with that stream. If no mixer is available, output is emulated and
|
* with that stream. If no mixer is available, output is emulated and
|
||||||
* input is stuck at 1.0f (a gain is applied to output samples).
|
* input is stuck at 1.0f (a gain is applied to output samples).
|
||||||
*/
|
*/
|
||||||
void SetMixer(int inputSource);
|
|
||||||
void SetMixer(int inputSource, float inputVolume,
|
void SetMixer(int inputSource, float inputVolume,
|
||||||
float playbackVolume);
|
float playbackVolume);
|
||||||
void GetMixer(int *inputSource, float *inputVolume,
|
void GetMixer(int *inputSource, float *inputVolume,
|
||||||
@ -927,7 +653,7 @@ public:
|
|||||||
/** @brief Find out if the input hardware level control is available
|
/** @brief Find out if the input hardware level control is available
|
||||||
*
|
*
|
||||||
* Checks the mInputMixerWorks variable, which is set up in
|
* Checks the mInputMixerWorks variable, which is set up in
|
||||||
* AudioIO::HandleDeviceChange(). External people care, because we want to
|
* AudioIOBase::HandleDeviceChange(). External people care, because we want to
|
||||||
* disable the UI if it doesn't work.
|
* disable the UI if it doesn't work.
|
||||||
*/
|
*/
|
||||||
bool InputMixerWorks();
|
bool InputMixerWorks();
|
||||||
@ -935,7 +661,7 @@ public:
|
|||||||
/** @brief Find out if the output level control is being emulated via software attenuation
|
/** @brief Find out if the output level control is being emulated via software attenuation
|
||||||
*
|
*
|
||||||
* Checks the mEmulateMixerOutputVol variable, which is set up in
|
* Checks the mEmulateMixerOutputVol variable, which is set up in
|
||||||
* AudioIO::HandleDeviceChange(). External classes care, because we want to
|
* AudioIOBase::HandleDeviceChange(). External classes care, because we want to
|
||||||
* modify the UI if it doesn't work.
|
* modify the UI if it doesn't work.
|
||||||
*/
|
*/
|
||||||
bool OutputMixerEmulated();
|
bool OutputMixerEmulated();
|
||||||
@ -946,82 +672,6 @@ public:
|
|||||||
* soundcard mixer (driven by PortMixer) */
|
* soundcard mixer (driven by PortMixer) */
|
||||||
wxArrayString GetInputSourceNames();
|
wxArrayString GetInputSourceNames();
|
||||||
|
|
||||||
/** \brief update state after changing what audio devices are selected
|
|
||||||
*
|
|
||||||
* Called when the devices stored in the preferences are changed to update
|
|
||||||
* the audio mixer capabilities
|
|
||||||
*
|
|
||||||
* \todo: Make this do a sample rate query and store the result in the
|
|
||||||
* AudioIO object to avoid doing it later? Would simplify the
|
|
||||||
* GetSupported*Rate functions considerably */
|
|
||||||
void HandleDeviceChange();
|
|
||||||
|
|
||||||
/** \brief Get a list of sample rates the output (playback) device
|
|
||||||
* supports.
|
|
||||||
*
|
|
||||||
* If no information about available sample rates can be fetched,
|
|
||||||
* an empty list is returned.
|
|
||||||
*
|
|
||||||
* You can explicitely give the index of the device. If you don't
|
|
||||||
* give it, the currently selected device from the preferences will be used.
|
|
||||||
*
|
|
||||||
* You may also specify a rate for which to check in addition to the
|
|
||||||
* standard rates.
|
|
||||||
*/
|
|
||||||
static std::vector<long> GetSupportedPlaybackRates(int DevIndex = -1,
|
|
||||||
double rate = 0.0);
|
|
||||||
|
|
||||||
/** \brief Get a list of sample rates the input (recording) device
|
|
||||||
* supports.
|
|
||||||
*
|
|
||||||
* If no information about available sample rates can be fetched,
|
|
||||||
* an empty list is returned.
|
|
||||||
*
|
|
||||||
* You can explicitely give the index of the device. If you don't
|
|
||||||
* give it, the currently selected device from the preferences will be used.
|
|
||||||
*
|
|
||||||
* You may also specify a rate for which to check in addition to the
|
|
||||||
* standard rates.
|
|
||||||
*/
|
|
||||||
static std::vector<long> GetSupportedCaptureRates(int devIndex = -1,
|
|
||||||
double rate = 0.0);
|
|
||||||
|
|
||||||
/** \brief Get a list of sample rates the current input/output device
|
|
||||||
* combination supports.
|
|
||||||
*
|
|
||||||
* Since there is no concept (yet) for different input/output
|
|
||||||
* sample rates, this currently returns only sample rates that are
|
|
||||||
* supported on both the output and input device. If no information
|
|
||||||
* about available sample rates can be fetched, it returns a default
|
|
||||||
* list.
|
|
||||||
* You can explicitely give the indexes of the playDevice/recDevice.
|
|
||||||
* If you don't give them, the selected devices from the preferences
|
|
||||||
* will be used.
|
|
||||||
* You may also specify a rate for which to check in addition to the
|
|
||||||
* standard rates.
|
|
||||||
*/
|
|
||||||
static std::vector<long> GetSupportedSampleRates(int playDevice = -1,
|
|
||||||
int recDevice = -1,
|
|
||||||
double rate = 0.0);
|
|
||||||
|
|
||||||
/** \brief Get a supported sample rate which can be used a an optimal
|
|
||||||
* default.
|
|
||||||
*
|
|
||||||
* Currently, this uses the first supported rate in the list
|
|
||||||
* [44100, 48000, highest sample rate]. Used in Project as a default value
|
|
||||||
* for project rates if one cannot be retrieved from the preferences.
|
|
||||||
* So all in all not that useful or important really
|
|
||||||
*/
|
|
||||||
static int GetOptimalSupportedSampleRate();
|
|
||||||
|
|
||||||
/** \brief During playback, the track time most recently played
|
|
||||||
*
|
|
||||||
* When playing looped, this will start from t0 again,
|
|
||||||
* too. So the returned time should be always between
|
|
||||||
* t0 and t1
|
|
||||||
*/
|
|
||||||
double GetStreamTime();
|
|
||||||
|
|
||||||
sampleFormat GetCaptureFormat() { return mCaptureFormat; }
|
sampleFormat GetCaptureFormat() { return mCaptureFormat; }
|
||||||
unsigned GetNumPlaybackChannels() const { return mNumPlaybackChannels; }
|
unsigned GetNumPlaybackChannels() const { return mNumPlaybackChannels; }
|
||||||
unsigned GetNumCaptureChannels() const { return mNumCaptureChannels; }
|
unsigned GetNumCaptureChannels() const { return mNumCaptureChannels; }
|
||||||
@ -1029,24 +679,6 @@ public:
|
|||||||
// Meaning really capturing, not just pre-rolling
|
// Meaning really capturing, not just pre-rolling
|
||||||
bool IsCapturing() const;
|
bool IsCapturing() const;
|
||||||
|
|
||||||
/** \brief Array of common audio sample rates
|
|
||||||
*
|
|
||||||
* These are the rates we will always support, regardless of hardware support
|
|
||||||
* for them (by resampling in audacity if needed) */
|
|
||||||
static const int StandardRates[];
|
|
||||||
/** \brief How many standard sample rates there are */
|
|
||||||
static const int NumStandardRates;
|
|
||||||
|
|
||||||
/** \brief Get diagnostic information on all the available audio I/O devices
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
wxString GetDeviceInfo();
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
||||||
/** \brief Get diagnostic information on all the available MIDI I/O devices */
|
|
||||||
wxString GetMidiDeviceInfo();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** \brief Ensure selected device names are valid
|
/** \brief Ensure selected device names are valid
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -1065,8 +697,6 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool IsAvailable(AudacityProject *projecT) const;
|
bool IsAvailable(AudacityProject *projecT) const;
|
||||||
void SetCaptureMeter(AudacityProject *project, MeterPanel *meter);
|
|
||||||
void SetPlaybackMeter(AudacityProject *project, MeterPanel *meter);
|
|
||||||
|
|
||||||
/** \brief Return a valid sample rate that is supported by the current I/O
|
/** \brief Return a valid sample rate that is supported by the current I/O
|
||||||
* device(s).
|
* device(s).
|
||||||
@ -1085,12 +715,13 @@ public:
|
|||||||
friend class MidiThread;
|
friend class MidiThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
friend void InitAudioIO();
|
static void Init();
|
||||||
|
static void Deinit();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** \brief Set the current VU meters - this should be done once after
|
/** \brief Set the current VU meters - this should be done once after
|
||||||
* each call to StartStream currently */
|
* each call to StartStream currently */
|
||||||
void SetMeters();
|
void SetMeters();
|
||||||
@ -1142,39 +773,6 @@ private:
|
|||||||
* all record buffers without underflow). */
|
* all record buffers without underflow). */
|
||||||
size_t GetCommonlyAvailCapture();
|
size_t GetCommonlyAvailCapture();
|
||||||
|
|
||||||
/** \brief get the index of the supplied (named) recording device, or the
|
|
||||||
* device selected in the preferences if none given.
|
|
||||||
*
|
|
||||||
* Pure utility function, but it comes round a number of times in the code
|
|
||||||
* and would be neater done once. If the device isn't found, return the
|
|
||||||
* default device index.
|
|
||||||
*/
|
|
||||||
static int getRecordDevIndex(const wxString &devName = {});
|
|
||||||
/** \brief get the index of the device selected in the preferences.
|
|
||||||
*
|
|
||||||
* If the device isn't found, returns -1
|
|
||||||
*/
|
|
||||||
#if USE_PORTMIXER
|
|
||||||
static int getRecordSourceIndex(PxMixer *portMixer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** \brief get the index of the supplied (named) playback device, or the
|
|
||||||
* device selected in the preferences if none given.
|
|
||||||
*
|
|
||||||
* Pure utility function, but it comes round a number of times in the code
|
|
||||||
* and would be neater done once. If the device isn't found, return the
|
|
||||||
* default device index.
|
|
||||||
*/
|
|
||||||
static int getPlayDevIndex(const wxString &devName = {});
|
|
||||||
|
|
||||||
/** \brief Array of audio sample rates to try to use
|
|
||||||
*
|
|
||||||
* These are the rates we will check if a device supports, and is as long
|
|
||||||
* as I can think of (to try and work out what the card can do) */
|
|
||||||
static const int RatesToTry[];
|
|
||||||
/** \brief How many sample rates to try */
|
|
||||||
static const int NumRatesToTry;
|
|
||||||
|
|
||||||
/** \brief Allocate RingBuffer structures, and others, needed for playback
|
/** \brief Allocate RingBuffer structures, and others, needed for playback
|
||||||
* and recording.
|
* and recording.
|
||||||
*
|
*
|
||||||
@ -1191,6 +789,4 @@ private:
|
|||||||
void StartStreamCleanup(bool bOnlyBuffers = false);
|
void StartStreamCleanup(bool bOnlyBuffers = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
using AudioIOBase = AudioIO;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
1351
src/AudioIOBase.cpp
1351
src/AudioIOBase.cpp
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,472 @@ Paul Licameli split from AudioIO.h
|
|||||||
#ifndef __AUDACITY_AUDIO_IO_BASE__
|
#ifndef __AUDACITY_AUDIO_IO_BASE__
|
||||||
#define __AUDACITY_AUDIO_IO_BASE__
|
#define __AUDACITY_AUDIO_IO_BASE__
|
||||||
|
|
||||||
#include "AudioIO.h"
|
#include "Audacity.h" // for USE_* macros
|
||||||
|
#include "Experimental.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cfloat>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <wx/string.h>
|
||||||
|
#include <wx/weakref.h> // member variable
|
||||||
|
#include "portaudio.h"
|
||||||
|
|
||||||
|
#if USE_PORTMIXER
|
||||||
|
#include "../lib-src/portmixer/include/portmixer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AudioIOBase;
|
||||||
|
|
||||||
|
class AudacityProject;
|
||||||
|
class AudioIOListener;
|
||||||
|
class BoundedEnvelope;
|
||||||
|
class MeterPanel;
|
||||||
|
using PRCrossfadeData = std::vector< std::vector < float > >;
|
||||||
|
|
||||||
|
#define BAD_STREAM_TIME (-DBL_MAX)
|
||||||
|
|
||||||
|
// For putting an increment of work in the scrubbing queue
|
||||||
|
struct ScrubbingOptions {
|
||||||
|
ScrubbingOptions() {}
|
||||||
|
|
||||||
|
bool adjustStart {};
|
||||||
|
|
||||||
|
// usually from TrackList::GetEndTime()
|
||||||
|
double maxTime {};
|
||||||
|
double minTime {};
|
||||||
|
|
||||||
|
bool bySpeed {};
|
||||||
|
bool isPlayingAtSpeed{};
|
||||||
|
|
||||||
|
double delay {};
|
||||||
|
|
||||||
|
// Limiting values for the speed of a scrub interval:
|
||||||
|
double minSpeed { 0.0 };
|
||||||
|
double maxSpeed { 1.0 };
|
||||||
|
|
||||||
|
|
||||||
|
// When maximum speed scrubbing skips to follow the mouse,
|
||||||
|
// this is the minimum amount of playback allowed at the maximum speed:
|
||||||
|
double minStutterTime {};
|
||||||
|
|
||||||
|
static double MaxAllowedScrubSpeed()
|
||||||
|
{ return 32.0; } // Is five octaves enough for your amusement?
|
||||||
|
static double MinAllowedScrubSpeed()
|
||||||
|
{ return 0.01; } // Mixer needs a lower bound speed. Scrub no slower than this.
|
||||||
|
};
|
||||||
|
|
||||||
|
// To avoid growing the argument list of StartStream, add fields here
|
||||||
|
struct AudioIOStartStreamOptions
|
||||||
|
{
|
||||||
|
explicit
|
||||||
|
AudioIOStartStreamOptions(AudacityProject *pProject_, double rate_)
|
||||||
|
: pProject{ pProject_ }
|
||||||
|
, envelope(nullptr)
|
||||||
|
, listener(NULL)
|
||||||
|
, rate(rate_)
|
||||||
|
, playLooped(false)
|
||||||
|
, cutPreviewGapStart(0.0)
|
||||||
|
, cutPreviewGapLen(0.0)
|
||||||
|
, pStartTime(NULL)
|
||||||
|
, preRoll(0.0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AudacityProject *pProject{};
|
||||||
|
MeterPanel *captureMeter{}, *playbackMeter{};
|
||||||
|
BoundedEnvelope *envelope; // for time warping
|
||||||
|
AudioIOListener* listener;
|
||||||
|
double rate;
|
||||||
|
bool playLooped;
|
||||||
|
double cutPreviewGapStart;
|
||||||
|
double cutPreviewGapLen;
|
||||||
|
double * pStartTime;
|
||||||
|
double preRoll;
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
|
// Non-null value indicates that scrubbing will happen
|
||||||
|
// (do not specify a time track, looping, or recording, which
|
||||||
|
// are all incompatible with scrubbing):
|
||||||
|
ScrubbingOptions *pScrubbingOptions {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// contents may get swapped with empty vector
|
||||||
|
PRCrossfadeData *pCrossfadeData{};
|
||||||
|
};
|
||||||
|
|
||||||
|
///\brief A singleton object supporting queries of the state of any active
|
||||||
|
/// audio streams, and audio device capabilities
|
||||||
|
class AudioIOBase /* not final */
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static AudioIOBase *Get();
|
||||||
|
|
||||||
|
void SetCaptureMeter(AudacityProject *project, MeterPanel *meter);
|
||||||
|
void SetPlaybackMeter(AudacityProject *project, MeterPanel *meter);
|
||||||
|
|
||||||
|
/** \brief update state after changing what audio devices are selected
|
||||||
|
*
|
||||||
|
* Called when the devices stored in the preferences are changed to update
|
||||||
|
* the audio mixer capabilities
|
||||||
|
*
|
||||||
|
* \todo: Make this do a sample rate query and store the result in the
|
||||||
|
* AudioIO object to avoid doing it later? Would simplify the
|
||||||
|
* GetSupported*Rate functions considerably */
|
||||||
|
void HandleDeviceChange();
|
||||||
|
|
||||||
|
/** \brief Get a list of sample rates the output (playback) device
|
||||||
|
* supports.
|
||||||
|
*
|
||||||
|
* If no information about available sample rates can be fetched,
|
||||||
|
* an empty list is returned.
|
||||||
|
*
|
||||||
|
* You can explicitely give the index of the device. If you don't
|
||||||
|
* give it, the currently selected device from the preferences will be used.
|
||||||
|
*
|
||||||
|
* You may also specify a rate for which to check in addition to the
|
||||||
|
* standard rates.
|
||||||
|
*/
|
||||||
|
static std::vector<long> GetSupportedPlaybackRates(int DevIndex = -1,
|
||||||
|
double rate = 0.0);
|
||||||
|
|
||||||
|
/** \brief Get a list of sample rates the input (recording) device
|
||||||
|
* supports.
|
||||||
|
*
|
||||||
|
* If no information about available sample rates can be fetched,
|
||||||
|
* an empty list is returned.
|
||||||
|
*
|
||||||
|
* You can explicitely give the index of the device. If you don't
|
||||||
|
* give it, the currently selected device from the preferences will be used.
|
||||||
|
*
|
||||||
|
* You may also specify a rate for which to check in addition to the
|
||||||
|
* standard rates.
|
||||||
|
*/
|
||||||
|
static std::vector<long> GetSupportedCaptureRates(int devIndex = -1,
|
||||||
|
double rate = 0.0);
|
||||||
|
|
||||||
|
/** \brief Get a list of sample rates the current input/output device
|
||||||
|
* combination supports.
|
||||||
|
*
|
||||||
|
* Since there is no concept (yet) for different input/output
|
||||||
|
* sample rates, this currently returns only sample rates that are
|
||||||
|
* supported on both the output and input device. If no information
|
||||||
|
* about available sample rates can be fetched, it returns a default
|
||||||
|
* list.
|
||||||
|
* You can explicitely give the indexes of the playDevice/recDevice.
|
||||||
|
* If you don't give them, the selected devices from the preferences
|
||||||
|
* will be used.
|
||||||
|
* You may also specify a rate for which to check in addition to the
|
||||||
|
* standard rates.
|
||||||
|
*/
|
||||||
|
static std::vector<long> GetSupportedSampleRates(int playDevice = -1,
|
||||||
|
int recDevice = -1,
|
||||||
|
double rate = 0.0);
|
||||||
|
|
||||||
|
/** \brief Get a supported sample rate which can be used a an optimal
|
||||||
|
* default.
|
||||||
|
*
|
||||||
|
* Currently, this uses the first supported rate in the list
|
||||||
|
* [44100, 48000, highest sample rate]. Used in Project as a default value
|
||||||
|
* for project rates if one cannot be retrieved from the preferences.
|
||||||
|
* So all in all not that useful or important really
|
||||||
|
*/
|
||||||
|
static int GetOptimalSupportedSampleRate();
|
||||||
|
|
||||||
|
/** \brief During playback, the track time most recently played
|
||||||
|
*
|
||||||
|
* When playing looped, this will start from t0 again,
|
||||||
|
* too. So the returned time should be always between
|
||||||
|
* t0 and t1
|
||||||
|
*/
|
||||||
|
double GetStreamTime();
|
||||||
|
|
||||||
|
/** \brief Array of common audio sample rates
|
||||||
|
*
|
||||||
|
* These are the rates we will always support, regardless of hardware support
|
||||||
|
* for them (by resampling in audacity if needed) */
|
||||||
|
static const int StandardRates[];
|
||||||
|
/** \brief How many standard sample rates there are */
|
||||||
|
static const int NumStandardRates;
|
||||||
|
|
||||||
|
/** \brief Get diagnostic information on all the available audio I/O devices
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
wxString GetDeviceInfo();
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
|
/** \brief Get diagnostic information on all the available MIDI I/O devices */
|
||||||
|
wxString GetMidiDeviceInfo();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \brief Find out if playback / recording is currently paused */
|
||||||
|
bool IsPaused() const;
|
||||||
|
|
||||||
|
/** \brief Returns true if audio i/o is busy starting, stopping, playing,
|
||||||
|
* or recording.
|
||||||
|
*
|
||||||
|
* When this is false, it's safe to start playing or recording */
|
||||||
|
bool IsBusy() const;
|
||||||
|
|
||||||
|
/** \brief Returns true if the audio i/o is running at all, but not during
|
||||||
|
* cleanup
|
||||||
|
*
|
||||||
|
* Doesn't return true if the device has been closed but some disk i/o or
|
||||||
|
* cleanup is still going on. If you want to know if it's safe to start a
|
||||||
|
* NEW stream, use IsBusy() */
|
||||||
|
bool IsStreamActive() const;
|
||||||
|
bool IsStreamActive(int token) const;
|
||||||
|
|
||||||
|
/** \brief Returns true if the stream is active, or even if audio I/O is
|
||||||
|
* busy cleaning up its data or writing to disk.
|
||||||
|
*
|
||||||
|
* This is used by TrackPanel to determine when a track has been completely
|
||||||
|
* recorded, and it's safe to flush to disk. */
|
||||||
|
bool IsAudioTokenActive(int token) const;
|
||||||
|
|
||||||
|
/** \brief Returns true if we're monitoring input (but not recording or
|
||||||
|
* playing actual audio) */
|
||||||
|
bool IsMonitoring() const;
|
||||||
|
|
||||||
|
/* Mixer services are always available. If no stream is running, these
|
||||||
|
* methods use whatever device is specified by the preferences. If a
|
||||||
|
* stream *is* running, naturally they manipulate the mixer associated
|
||||||
|
* with that stream. If no mixer is available, output is emulated and
|
||||||
|
* input is stuck at 1.0f (a gain is applied to output samples).
|
||||||
|
*/
|
||||||
|
void SetMixer(int inputSource);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static std::unique_ptr<AudioIOBase> ugAudioIO;
|
||||||
|
static wxString DeviceName(const PaDeviceInfo* info);
|
||||||
|
static wxString HostName(const PaDeviceInfo* info);
|
||||||
|
|
||||||
|
AudacityProject *mOwningProject;
|
||||||
|
|
||||||
|
/// True if audio playback is paused
|
||||||
|
bool mPaused;
|
||||||
|
|
||||||
|
/// True when output reaches mT1
|
||||||
|
bool mMidiOutputComplete{ true };
|
||||||
|
|
||||||
|
/// mMidiStreamActive tells when mMidiStream is open for output
|
||||||
|
bool mMidiStreamActive;
|
||||||
|
|
||||||
|
volatile int mStreamToken;
|
||||||
|
|
||||||
|
/// Audio playback rate in samples per second
|
||||||
|
double mRate;
|
||||||
|
|
||||||
|
PaStream *mPortStreamV19;
|
||||||
|
|
||||||
|
wxWeakRef<MeterPanel> mInputMeter{};
|
||||||
|
wxWeakRef<MeterPanel> mOutputMeter{};
|
||||||
|
|
||||||
|
#if USE_PORTMIXER
|
||||||
|
PxMixer *mPortMixer;
|
||||||
|
float mPreviousHWPlaythrough;
|
||||||
|
#endif /* USE_PORTMIXER */
|
||||||
|
|
||||||
|
bool mEmulateMixerOutputVol;
|
||||||
|
/** @brief Can we control the hardware input level?
|
||||||
|
*
|
||||||
|
* This flag is set to true if using portmixer to control the
|
||||||
|
* input volume seems to be working (and so we offer the user the control),
|
||||||
|
* and to false (locking the control out) otherwise. This avoids stupid
|
||||||
|
* scaled clipping problems when trying to do software emulated input volume
|
||||||
|
* control */
|
||||||
|
bool mInputMixerWorks;
|
||||||
|
float mMixerOutputVol;
|
||||||
|
|
||||||
|
// For cacheing supported sample rates
|
||||||
|
static int mCachedPlaybackIndex;
|
||||||
|
static std::vector<long> mCachedPlaybackRates;
|
||||||
|
static int mCachedCaptureIndex;
|
||||||
|
static std::vector<long> mCachedCaptureRates;
|
||||||
|
static std::vector<long> mCachedSampleRates;
|
||||||
|
static double mCachedBestRateIn;
|
||||||
|
|
||||||
|
struct RecordingSchedule {
|
||||||
|
double mPreRoll{};
|
||||||
|
double mLatencyCorrection{}; // negative value usually
|
||||||
|
double mDuration{};
|
||||||
|
PRCrossfadeData mCrossfadeData;
|
||||||
|
|
||||||
|
// These are initialized by the main thread, then updated
|
||||||
|
// only by the thread calling FillBuffers:
|
||||||
|
double mPosition{};
|
||||||
|
bool mLatencyCorrected{};
|
||||||
|
|
||||||
|
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
|
||||||
|
double ToConsume() const;
|
||||||
|
double Consumed() const;
|
||||||
|
double ToDiscard() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlaybackSchedule {
|
||||||
|
/// Playback starts at offset of mT0, which is measured in seconds.
|
||||||
|
double mT0;
|
||||||
|
/// Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 during scrubbing.
|
||||||
|
double mT1;
|
||||||
|
/// Current track time position during playback, in seconds.
|
||||||
|
/// Initialized by the main thread but updated by worker threads during
|
||||||
|
/// playback or recording, and periodically reread by the main thread for
|
||||||
|
/// purposes such as display update.
|
||||||
|
std::atomic<double> mTime;
|
||||||
|
|
||||||
|
/// Accumulated real time (not track position), starting at zero (unlike
|
||||||
|
/// mTime), and wrapping back to zero each time around looping play.
|
||||||
|
/// Thus, it is the length in real seconds between mT0 and mTime.
|
||||||
|
double mWarpedTime;
|
||||||
|
|
||||||
|
/// Real length to be played (if looping, for each pass) after warping via a
|
||||||
|
/// time track, computed just once when starting the stream.
|
||||||
|
/// Length in real seconds between mT0 and mT1. Always positive.
|
||||||
|
double mWarpedLength;
|
||||||
|
|
||||||
|
// mWarpedTime and mWarpedLength are irrelevant when scrubbing,
|
||||||
|
// else they are used in updating mTime,
|
||||||
|
// and when not scrubbing or playing looped, mTime is also used
|
||||||
|
// in the test for termination of playback.
|
||||||
|
|
||||||
|
// with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
|
||||||
|
// (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
|
||||||
|
|
||||||
|
const BoundedEnvelope *mEnvelope;
|
||||||
|
|
||||||
|
volatile enum {
|
||||||
|
PLAY_STRAIGHT,
|
||||||
|
PLAY_LOOPED,
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
|
PLAY_SCRUB,
|
||||||
|
PLAY_AT_SPEED, // a version of PLAY_SCRUB.
|
||||||
|
#endif
|
||||||
|
} mPlayMode { PLAY_STRAIGHT };
|
||||||
|
double mCutPreviewGapStart;
|
||||||
|
double mCutPreviewGapLen;
|
||||||
|
|
||||||
|
void Init(
|
||||||
|
double t0, double t1,
|
||||||
|
const AudioIOStartStreamOptions &options,
|
||||||
|
const RecordingSchedule *pRecordingSchedule );
|
||||||
|
|
||||||
|
/** \brief True if the end time is before the start time */
|
||||||
|
bool ReversedTime() const
|
||||||
|
{
|
||||||
|
return mT1 < mT0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Get current track time value, unadjusted
|
||||||
|
*
|
||||||
|
* Returns a time in seconds.
|
||||||
|
*/
|
||||||
|
double GetTrackTime() const
|
||||||
|
{ return mTime.load(std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
/** \brief Set current track time value, unadjusted
|
||||||
|
*/
|
||||||
|
void SetTrackTime( double time )
|
||||||
|
{ mTime.store(time, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
/** \brief Clamps argument to be between mT0 and mT1
|
||||||
|
*
|
||||||
|
* Returns the bound if the value is out of bounds; does not wrap.
|
||||||
|
* Returns a time in seconds.
|
||||||
|
*/
|
||||||
|
double ClampTrackTime( double trackTime ) const;
|
||||||
|
|
||||||
|
/** \brief Clamps mTime to be between mT0 and mT1
|
||||||
|
*
|
||||||
|
* Returns the bound if the value is out of bounds; does not wrap.
|
||||||
|
* Returns a time in seconds.
|
||||||
|
*/
|
||||||
|
double LimitTrackTime() const;
|
||||||
|
|
||||||
|
/** \brief Normalizes mTime, clamping it and handling gaps from cut preview.
|
||||||
|
*
|
||||||
|
* Clamps the time (unless scrubbing), and skips over the cut section.
|
||||||
|
* Returns a time in seconds.
|
||||||
|
*/
|
||||||
|
double NormalizeTrackTime() const;
|
||||||
|
|
||||||
|
void ResetMode() { mPlayMode = PLAY_STRAIGHT; }
|
||||||
|
|
||||||
|
bool PlayingStraight() const { return mPlayMode == PLAY_STRAIGHT; }
|
||||||
|
bool Looping() const { return mPlayMode == PLAY_LOOPED; }
|
||||||
|
bool Scrubbing() const { return mPlayMode == PLAY_SCRUB; }
|
||||||
|
bool PlayingAtSpeed() const { return mPlayMode == PLAY_AT_SPEED; }
|
||||||
|
bool Interactive() const { return Scrubbing() || PlayingAtSpeed(); }
|
||||||
|
|
||||||
|
// Returns true if a loop pass, or the sole pass of straight play,
|
||||||
|
// is completed at the current value of mTime
|
||||||
|
bool PassIsComplete() const;
|
||||||
|
|
||||||
|
// Returns true if time equals t1 or is on opposite side of t1, to t0
|
||||||
|
bool Overruns( double trackTime ) const;
|
||||||
|
|
||||||
|
// Compute the NEW track time for the given one and a real duration,
|
||||||
|
// taking into account whether the schedule is for looping
|
||||||
|
double AdvancedTrackTime(
|
||||||
|
double trackTime, double realElapsed, double speed) const;
|
||||||
|
|
||||||
|
// Use the function above in the callback after consuming samples from the
|
||||||
|
// playback ring buffers, during usual straight or looping play
|
||||||
|
void TrackTimeUpdate(double realElapsed);
|
||||||
|
|
||||||
|
// Convert a nonnegative real duration to an increment of track time
|
||||||
|
// relative to mT0.
|
||||||
|
double TrackDuration(double realElapsed) const;
|
||||||
|
|
||||||
|
// Convert time between mT0 and argument to real duration, according to
|
||||||
|
// time track if one is given; result is always nonnegative
|
||||||
|
double RealDuration(double trackTime1) const;
|
||||||
|
|
||||||
|
// How much real time left?
|
||||||
|
double RealTimeRemaining() const;
|
||||||
|
|
||||||
|
// Advance the real time position
|
||||||
|
void RealTimeAdvance( double increment );
|
||||||
|
|
||||||
|
// Determine starting duration within the first pass -- sometimes not
|
||||||
|
// zero
|
||||||
|
void RealTimeInit( double trackTime );
|
||||||
|
|
||||||
|
void RealTimeRestart();
|
||||||
|
|
||||||
|
} mPlaybackSchedule;
|
||||||
|
|
||||||
|
/** \brief get the index of the supplied (named) recording device, or the
|
||||||
|
* device selected in the preferences if none given.
|
||||||
|
*
|
||||||
|
* Pure utility function, but it comes round a number of times in the code
|
||||||
|
* and would be neater done once. If the device isn't found, return the
|
||||||
|
* default device index.
|
||||||
|
*/
|
||||||
|
static int getRecordDevIndex(const wxString &devName = {});
|
||||||
|
|
||||||
|
/** \brief get the index of the device selected in the preferences.
|
||||||
|
*
|
||||||
|
* If the device isn't found, returns -1
|
||||||
|
*/
|
||||||
|
#if USE_PORTMIXER
|
||||||
|
static int getRecordSourceIndex(PxMixer *portMixer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \brief get the index of the supplied (named) playback device, or the
|
||||||
|
* device selected in the preferences if none given.
|
||||||
|
*
|
||||||
|
* Pure utility function, but it comes round a number of times in the code
|
||||||
|
* and would be neater done once. If the device isn't found, return the
|
||||||
|
* default device index.
|
||||||
|
*/
|
||||||
|
static int getPlayDevIndex(const wxString &devName = {});
|
||||||
|
|
||||||
|
/** \brief Array of audio sample rates to try to use
|
||||||
|
*
|
||||||
|
* These are the rates we will check if a device supports, and is as long
|
||||||
|
* as I can think of (to try and work out what the card can do) */
|
||||||
|
static const int RatesToTry[];
|
||||||
|
/** \brief How many sample rates to try */
|
||||||
|
static const int NumRatesToTry;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,6 +16,7 @@ Paul Licameli split from TrackPanel.cpp
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wx/longlong.h>
|
#include <wx/longlong.h>
|
||||||
|
|
||||||
|
#include "../../AudioIOBase.h" // for ScrubbingOptions
|
||||||
#include "../../ClientData.h"
|
#include "../../ClientData.h"
|
||||||
#include "../../widgets/Overlay.h" // to inherit
|
#include "../../widgets/Overlay.h" // to inherit
|
||||||
#include "../../commands/CommandContext.h"
|
#include "../../commands/CommandContext.h"
|
||||||
@ -34,36 +35,6 @@ extern AudacityProject *GetActiveProject();
|
|||||||
#define USE_SCRUB_THREAD
|
#define USE_SCRUB_THREAD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For putting an increment of work in the scrubbing queue
|
|
||||||
struct ScrubbingOptions {
|
|
||||||
ScrubbingOptions() {}
|
|
||||||
|
|
||||||
bool adjustStart {};
|
|
||||||
|
|
||||||
// usually from TrackList::GetEndTime()
|
|
||||||
double maxTime {};
|
|
||||||
double minTime {};
|
|
||||||
|
|
||||||
bool bySpeed {};
|
|
||||||
bool isPlayingAtSpeed{};
|
|
||||||
|
|
||||||
double delay {};
|
|
||||||
|
|
||||||
// Limiting values for the speed of a scrub interval:
|
|
||||||
double minSpeed { 0.0 };
|
|
||||||
double maxSpeed { 1.0 };
|
|
||||||
|
|
||||||
|
|
||||||
// When maximum speed scrubbing skips to follow the mouse,
|
|
||||||
// this is the minimum amount of playback allowed at the maximum speed:
|
|
||||||
double minStutterTime {};
|
|
||||||
|
|
||||||
static double MaxAllowedScrubSpeed()
|
|
||||||
{ return 32.0; } // Is five octaves enough for your amusement?
|
|
||||||
static double MinAllowedScrubSpeed()
|
|
||||||
{ return 0.01; } // Mixer needs a lower bound speed. Scrub no slower than this.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Scrub state object
|
// Scrub state object
|
||||||
class Scrubber final
|
class Scrubber final
|
||||||
: public wxEvtHandler
|
: public wxEvtHandler
|
||||||
|
Loading…
x
Reference in New Issue
Block a user