17#include <wx/sstream.h>
18#include <wx/txtstrm.h>
78 wxString infoName = wxSafeConvertMB2WX(info->name);
85 wxString hostapiName = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
105#if defined(USE_PORTMIXER)
106 int oldRecordSource = Px_GetCurrentInputSource(mPortMixer);
107 if ( inputSource != oldRecordSource )
108 Px_SetCurrentInputSource(mPortMixer, inputSource);
135#if defined(USE_PORTMIXER)
143 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
144 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
145 mPreviousHWPlaythrough = -1.0;
147 Px_CloseMixer(mPortMixer);
154 if (highestSampleRate == 0)
158 highestSampleRate = 44100;
170 PaStreamParameters playbackParameters;
172 playbackParameters.device = playDeviceNum;
173 playbackParameters.sampleFormat = paFloat32;
174 playbackParameters.hostApiSpecificStreamInfo = NULL;
175 playbackParameters.channelCount = 1;
176 if (Pa_GetDeviceInfo(playDeviceNum))
177 playbackParameters.suggestedLatency =
178 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
180 playbackParameters.suggestedLatency =
183 PaStreamParameters captureParameters;
185 captureParameters.device = recDeviceNum;
186 captureParameters.sampleFormat = paFloat32;;
187 captureParameters.hostApiSpecificStreamInfo = NULL;
188 captureParameters.channelCount = 1;
189 if (Pa_GetDeviceInfo(recDeviceNum))
190 captureParameters.suggestedLatency =
191 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
193 captureParameters.suggestedLatency =
198 error = Pa_OpenStream(&stream,
199 &captureParameters, &playbackParameters,
200 highestSampleRate, paFramesPerBufferUnspecified,
201 paClipOff | paDitherOff,
206 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
208 Pa_CloseStream(stream);
215 error = Pa_OpenStream(&stream,
216 &captureParameters, NULL,
217 highestSampleRate, paFramesPerBufferUnspecified,
218 paClipOff | paDitherOff,
222 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
224 Pa_CloseStream(stream);
232 error = Pa_OpenStream(&stream,
233 NULL, &playbackParameters,
234 highestSampleRate, paFramesPerBufferUnspecified,
235 paClipOff | paDitherOff,
239 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
241 Pa_CloseStream(stream);
255 if (sourceIndex >= 0) {
258 sourceIndex = getRecordSourceIndex(mPortMixer);
259 if (sourceIndex >= 0)
268 float inputVol = Px_GetInputVolume(mPortMixer);
270 Px_SetInputVolume(mPortMixer, 0.0);
271 if (Px_GetInputVolume(mPortMixer) > 0.1)
273 Px_SetInputVolume(mPortMixer, 0.2f);
274 if (Px_GetInputVolume(mPortMixer) < 0.1 ||
275 Px_GetInputVolume(mPortMixer) > 0.3)
277 Px_SetInputVolume(mPortMixer, inputVol);
279 Pa_CloseStream(stream);
283 wxPrintf(
"PortMixer: Recording: %s\n"
290 const std::shared_ptr<AudacityProject> &
project,
const std::weak_ptr<Meter> &wMeter)
293 ( pOwningProject ) && ( pOwningProject !=
project))
296 auto meter = wMeter.lock();
300 meter->Reset(
mRate,
true);
307 const std::shared_ptr<AudacityProject> &
project,
const std::weak_ptr<Meter> &wMeter)
310 ( pOwningProject ) && ( pOwningProject !=
project))
313 auto meter = wMeter.lock();
317 meter->Reset(
mRate,
true);
325 return mPaused.load(std::memory_order_relaxed);
338 bool isActive =
false;
343 isActive = isActive ||
345 [](
auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
378 auto devInfo = Pa_GetDeviceInfo(devIndex);
382 wxLogDebug(
wxT(
"IsPlaybackRateSupported() Could not get device info!"));
388 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
389 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
391 PaStreamParameters pars;
393 pars.device = devIndex;
394 pars.channelCount = 1;
395 pars.sampleFormat = paFloat32;
396 pars.suggestedLatency = devInfo->defaultHighOutputLatency;
397 pars.hostApiSpecificStreamInfo = NULL;
403 if (!(isDirectSound && rate > 200000)){
404 if (Pa_IsFormatSupported(NULL, &pars, rate) == 0)
427 auto devInfo = Pa_GetDeviceInfo(devIndex);
431 wxLogDebug(
wxT(
"IsCaptureRateSupported() Could not get device info!"));
441 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
442 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
444 PaStreamParameters pars;
446 pars.device = devIndex;
447 pars.channelCount = recordChannels;
448 pars.sampleFormat = paFloat32;
449 pars.suggestedLatency = latencyDuration / 1000.0;
450 pars.hostApiSpecificStreamInfo = NULL;
454 if (!(isDirectSound && rate > 200000))
456 if (Pa_IsFormatSupported(&pars, NULL, rate) == 0)
472 std::vector<long> supportedRates;
476 supportedRates.push_back(rate);
482 return supportedRates;
492 std::vector<long> supportedRates;
496 supportedRates.push_back(rate);
502 return supportedRates;
507 long supportedRate = 0;
519 std::vector<long> rates = { rate };
525 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(
RatesToTry),
526 std::back_inserter(rates));
528 for (
const long rateToTry : rates)
532 supportedRate = rateToTry;
539 return supportedRate;
544 long supportedRate = 0;
552 return supportedRate;
559 supportedRate = rate;
560 return supportedRate;
565 std::vector<long> rates = { rate };
573 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(
RatesToTry),
574 std::back_inserter(rates));
576 for (
const long rateToTry : rates)
580 supportedRate = rateToTry;
587 return supportedRate;
591 int playDevice,
int recDevice,
long rate)
593 long supportedRate = 0;
596 if (playDevice == -1) {
599 if (recDevice == -1) {
604 std::pair<int, int> devicePair { playDevice, recDevice };
612 std::vector<long> rates { rate };
620 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(
RatesToTry),
621 std::back_inserter(rates));
623 for (
const long rateToTry : rates)
628 supportedRate = rateToTry;
637 return supportedRate;
643 if (playDevice == -1)
656 std::vector<long> result;
658 std::set_intersection(playback.begin(), playback.end(),
659 capture.begin(), capture.end(),
660 std::back_inserter(result));
687int AudioIOBase::getRecordSourceIndex(PxMixer *portMixer)
691 int numSources = Px_GetNumInputSources(portMixer);
692 for (i = 0; i < numSources; i++) {
693 if (sourceName == wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i))))
702 wxString devName(devNameArg);
708 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
709 PaHostApiIndex hostNum;
710 for (hostNum = 0; hostNum < hostCnt; hostNum++)
712 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
713 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
715 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
717 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
719 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
720 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxOutputChannels > 0 )
730 return hinfo->defaultOutputDevice;
738 PaDeviceIndex deviceNum = Pa_GetDefaultOutputDevice();
757 wxString devName(devNameArg);
763 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
764 PaHostApiIndex hostNum;
765 for (hostNum = 0; hostNum < hostCnt; hostNum++)
767 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
768 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
770 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
772 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
774 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
775 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxInputChannels > 0 )
785 return hinfo->defaultInputDevice;
791 PaDeviceIndex deviceNum = Pa_GetDefaultInputDevice();
805 wxLogDebug(
"PortAudio returns -1, cannot find a suitable default device, so we just use the first one available");
814 wxStringOutputStream o;
815 wxTextOutputStream s(o, wxEOL_UNIX);
818 return XO(
"Stream is active ... unable to gather information.\n")
824 int recDeviceNum = Pa_GetDefaultInputDevice();
825 int playDeviceNum = Pa_GetDefaultOutputDevice();
826 int cnt = Pa_GetDeviceCount();
829 wxLogDebug(
wxT(
"Portaudio reports %d audio devices"),cnt);
831 s <<
wxT(
"==============================\n");
832 s <<
XO(
"Default recording device number: %d\n").Format( recDeviceNum );
833 s <<
XO(
"Default playback device number: %d\n").Format( playDeviceNum);
841 s <<
XO(
"No devices found\n");
842 return o.GetString();
845 const PaDeviceInfo* info;
847 for (j = 0; j < cnt; j++) {
848 s <<
wxT(
"==============================\n");
850 info = Pa_GetDeviceInfo(j);
852 s <<
XO(
"Device info unavailable for: %d\n").Format( j );
857 s <<
XO(
"Device ID: %d\n").Format( j );
858 s <<
XO(
"Device name: %s\n").Format(
name );
859 s <<
XO(
"Host name: %s\n").Format(
HostName(info) );
860 s <<
XO(
"Recording channels: %d\n").Format( info->maxInputChannels );
861 s <<
XO(
"Playback channels: %d\n").Format( info->maxOutputChannels );
862 s <<
XO(
"Low Recording Latency: %g\n").Format( info->defaultLowInputLatency );
863 s <<
XO(
"Low Playback Latency: %g\n").Format( info->defaultLowOutputLatency );
864 s <<
XO(
"High Recording Latency: %g\n").Format( info->defaultHighInputLatency );
865 s <<
XO(
"High Playback Latency: %g\n").Format( info->defaultHighOutputLatency );
867 if (info->maxOutputChannels)
872 s <<
XO(
"Supported Playback Rates:\n");
873 for (
int k = 0; k < (int) rates.size(); k++) {
874 s <<
wxT(
" ") << (int) rates[k] <<
wxT(
"\n");
878 if (info->maxInputChannels)
883 s <<
XO(
"Supported Capture Rates:\n");
884 for (
int k = 0; k < (int) rates.size(); k++) {
885 s <<
wxT(
" ") << (int) rates[k] <<
wxT(
"\n");
889 if (
name == playDevice && info->maxOutputChannels > 0)
892 if (
name == recDevice && info->maxInputChannels > 0)
897 if (recDeviceNum < 0 && info->maxInputChannels > 0){
900 if (playDeviceNum < 0 && info->maxOutputChannels > 0){
905 bool haveRecDevice = (recDeviceNum >= 0);
906 bool havePlayDevice = (playDeviceNum >= 0);
908 s <<
wxT(
"==============================\n");
910 s <<
XO(
"Selected recording device: %d - %s\n").Format( recDeviceNum, recDevice );
912 s <<
XO(
"No recording device found for '%s'.\n").Format( recDevice );
915 s <<
XO(
"Selected playback device: %d - %s\n").Format( playDeviceNum, playDevice );
917 s <<
XO(
"No playback device found for '%s'.\n").Format( playDevice );
921 if (havePlayDevice && haveRecDevice) {
924 s <<
XO(
"Supported Rates:\n");
930 s <<
XO(
"Cannot check mutual sample rates without both devices.\n");
931 return o.GetString();
934#if defined(USE_PORTMIXER)
938 bool EmulateMixerInputVol =
true;
939 float MixerInputVol = 1.0;
940 float MixerOutputVol = 1.0;
946 PaStreamParameters playbackParameters;
948 playbackParameters.device = playDeviceNum;
949 playbackParameters.sampleFormat = paFloat32;
950 playbackParameters.hostApiSpecificStreamInfo = NULL;
951 playbackParameters.channelCount = 1;
952 if (Pa_GetDeviceInfo(playDeviceNum)){
953 playbackParameters.suggestedLatency =
954 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
957 playbackParameters.suggestedLatency =
960 PaStreamParameters captureParameters;
962 captureParameters.device = recDeviceNum;
963 captureParameters.sampleFormat = paFloat32;;
964 captureParameters.hostApiSpecificStreamInfo = NULL;
965 captureParameters.channelCount = 1;
966 if (Pa_GetDeviceInfo(recDeviceNum)){
967 captureParameters.suggestedLatency =
968 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
971 captureParameters.suggestedLatency =
975 error = Pa_OpenStream(&stream,
976 &captureParameters, &playbackParameters,
977 highestSampleRate, paFramesPerBufferUnspecified,
978 paClipOff | paDitherOff,
982 error = Pa_OpenStream(&stream,
983 &captureParameters, NULL,
984 highestSampleRate, paFramesPerBufferUnspecified,
985 paClipOff | paDitherOff,
990 s <<
XO(
"Received %d while opening devices\n").Format( error );
991 return o.GetString();
994 PxMixer *PortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
997 s <<
XO(
"Unable to open Portmixer\n");
998 Pa_CloseStream(stream);
999 return o.GetString();
1002 s <<
wxT(
"==============================\n");
1003 s <<
XO(
"Available mixers:\n");
1006 cnt = Px_GetNumMixers(stream);
1007 for (
int i = 0; i < cnt; i++) {
1008 wxString
name = wxSafeConvertMB2WX(Px_GetMixerName(stream, i));
1009 s <<
XO(
"%d - %s\n").Format( i,
name );
1012 s <<
wxT(
"==============================\n");
1013 s <<
XO(
"Available recording sources:\n");
1014 cnt = Px_GetNumInputSources(PortMixer);
1015 for (
int i = 0; i < cnt; i++) {
1016 wxString
name = wxSafeConvertMB2WX(Px_GetInputSourceName(PortMixer, i));
1017 s <<
XO(
"%d - %s\n").Format( i,
name );
1020 s <<
wxT(
"==============================\n");
1021 s <<
XO(
"Available playback volumes:\n");
1022 cnt = Px_GetNumOutputVolumes(PortMixer);
1023 for (
int i = 0; i < cnt; i++) {
1024 wxString
name = wxSafeConvertMB2WX(Px_GetOutputVolumeName(PortMixer, i));
1025 s <<
XO(
"%d - %s\n").Format( i,
name );
1030 MixerInputVol = Px_GetInputVolume(PortMixer);
1031 EmulateMixerInputVol =
false;
1032 Px_SetInputVolume(PortMixer, 0.0);
1033 if (Px_GetInputVolume(PortMixer) > 0.1)
1034 EmulateMixerInputVol =
true;
1035 Px_SetInputVolume(PortMixer, 0.2f);
1036 if (Px_GetInputVolume(PortMixer) < 0.1 ||
1037 Px_GetInputVolume(PortMixer) > 0.3)
1038 EmulateMixerInputVol =
true;
1039 Px_SetInputVolume(PortMixer, MixerInputVol);
1041 Pa_CloseStream(stream);
1043 s <<
wxT(
"==============================\n");
1044 s << ( EmulateMixerInputVol
1045 ?
XO(
"Recording volume is emulated\n")
1046 :
XO(
"Recording volume is native\n") );
1048 Px_CloseMixer(PortMixer);
1052 return o.GetString();
1057 std::vector<AudioIODiagnostics> result;
1059 wxT(
"audiodev.txt"), GetDeviceInfo(),
wxT(
"Audio Device Info") });
1060 for(
auto &pExt : mAudioIOExt )
1062 result.emplace_back(pExt->Dump());
1067 L
"/AudioIO/Host", L
"" };
1069 L
"/AudioIO/LatencyCorrection", -130.0 };
1071 L
"/AudioIO/LatencyDuration", 100.0 };
1073 L
"/AudioIO/PlaybackDevice", L
"" };
1075 L
"/AudioIO/PlaybackSource", L
"" };
1077 L
"/AudioIO/PlaybackVolume", 1.0 };
1079 L
"/AudioIO/RecordChannels", 2 };
1081 L
"/AudioIO/RecordingDevice", L
"" };
1083 L
"/AudioIO/RecordingSource", L
"" };
1085 L
"/AudioIO/RecordingSourceIndex", -1 };
DoubleSetting AudioIOLatencyCorrection
StringSetting AudioIORecordingSource
DoubleSetting AudioIOPlaybackVolume
StringSetting AudioIOPlaybackSource
StringSetting AudioIOPlaybackDevice
DoubleSetting AudioIOLatencyDuration
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
IntSetting AudioIORecordingSourceIndex
IntSetting AudioIORecordChannels
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
A singleton object supporting queries of the state of any active audio streams, and audio device capa...
std::weak_ptr< Meter > mOutputMeter
PaStream * mPortStreamV19
static wxString HostName(const PaDeviceInfo *info)
static std::map< std::pair< int, int >, std::vector< long > > mCachedSampleRates
std::atomic< bool > mPaused
True if audio playback is paused.
static bool IsCaptureRateSupported(int devIndex, long rate)
Check if the specified sample rate is supported by a device.
static long GetClosestSupportedPlaybackRate(int devIndex, long rate)
Find the closest supported sample rate for given playback device.
double mRate
Audio playback rate in samples per second.
bool IsMonitoring() const
Returns true if we're monitoring input (but not recording or playing actual audio)
wxString GetDeviceInfo() const
Get diagnostic information on all the available audio I/O devices.
static bool IsPlaybackRateSupported(int devIndex, long rate)
Check if the specified playback rate is supported by a device.
static const int StandardRates[]
Array of common audio sample rates.
static int mCurrentPlaybackIndex
static std::unique_ptr< AudioIOBase > ugAudioIO
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
static double mCachedBestRateIn
void SetCaptureMeter(const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
static int getPlayDevIndex(const wxString &devName={})
get the index of the device selected in the preferences.
static AudioIOBase * Get()
bool IsStreamActive() const
Returns true if the audio i/o is running at all, but not during cleanup.
bool IsBusy() const
Returns true if audio i/o is busy starting, stopping, playing, or recording.
void HandleDeviceChange()
update state after changing what audio devices are selected
static int mCurrentCaptureIndex
void SetMixer(int inputSource)
std::vector< AudioIODiagnostics > GetAllDeviceInfo()
Get diagnostic information for audio devices and also for extensions.
static wxString DeviceName(const PaDeviceInfo *info)
static std::vector< long > GetSupportedSampleRates(int playDevice=-1, int recDevice=-1)
Get a list of sample rates the current input/output device combination supports.
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1)
Get a list of sample rates the output (playback) device supports.
static int GetOptimalSupportedSampleRate()
Get a supported sample rate which can be used a an optimal default.
static std::map< int, std::vector< long > > mCachedCaptureRates
std::weak_ptr< AudacityProject > mOwningProject
static std::map< int, std::vector< long > > mCachedPlaybackRates
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...
static long GetClosestSupportedSampleRate(int playDevice, int recDevice, long rate)
Find the closest supported sample rate for given playback and recording devices.
bool mInputMixerWorks
Can we control the hardware input level?
void SetPlaybackMeter(const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
static const int NumRatesToTry
How many sample rates to try.
bool IsPaused() const
Find out if playback / recording is currently paused.
std::weak_ptr< Meter > mInputMeter
static long GetClosestSupportedCaptureRate(int devIndex, long rate)
Find the closest supported sample rate for given recording device.
static const int NumStandardRates
How many standard sample rates there are.
static int getRecordDevIndex(const wxString &devName={})
get the index of the supplied (named) recording device, or the device selected in the preferences if ...
static const int RatesToTry[]
Array of audio sample rates to try to use.
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1)
Get a list of sample rates the input (recording) device supports.
virtual ~AudioIOExtBase()
Specialization of Setting for double.
Specialization of Setting for int.
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined */
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
const T & GetDefault() const
Specialization of Setting for strings.
constexpr int supportedSampleRates[]
void copy(const T *src, T *dst, int32_t n)