15#include <wx/sstream.h>
16#include <wx/txtstrm.h>
75 wxString infoName = wxSafeConvertMB2WX(info->name);
82 wxString hostapiName = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
102#if defined(USE_PORTMIXER)
103 int oldRecordSource = Px_GetCurrentInputSource(mPortMixer);
104 if ( inputSource != oldRecordSource )
105 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 int highestSampleRate;
162 highestSampleRate = 44100;
176 PaStreamParameters playbackParameters;
178 playbackParameters.device = playDeviceNum;
179 playbackParameters.sampleFormat = paFloat32;
180 playbackParameters.hostApiSpecificStreamInfo = NULL;
181 playbackParameters.channelCount = 1;
182 if (Pa_GetDeviceInfo(playDeviceNum))
183 playbackParameters.suggestedLatency =
184 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
186 playbackParameters.suggestedLatency =
189 PaStreamParameters captureParameters;
191 captureParameters.device = recDeviceNum;
192 captureParameters.sampleFormat = paFloat32;;
193 captureParameters.hostApiSpecificStreamInfo = NULL;
194 captureParameters.channelCount = 1;
195 if (Pa_GetDeviceInfo(recDeviceNum))
196 captureParameters.suggestedLatency =
197 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
199 captureParameters.suggestedLatency =
204 error = Pa_OpenStream(&stream,
205 &captureParameters, &playbackParameters,
206 highestSampleRate, paFramesPerBufferUnspecified,
207 paClipOff | paDitherOff,
212 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
214 Pa_CloseStream(stream);
221 error = Pa_OpenStream(&stream,
222 &captureParameters, NULL,
223 highestSampleRate, paFramesPerBufferUnspecified,
224 paClipOff | paDitherOff,
228 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
230 Pa_CloseStream(stream);
238 error = Pa_OpenStream(&stream,
239 NULL, &playbackParameters,
240 highestSampleRate, paFramesPerBufferUnspecified,
241 paClipOff | paDitherOff,
245 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
247 Pa_CloseStream(stream);
261 if (sourceIndex >= 0) {
264 sourceIndex = getRecordSourceIndex(mPortMixer);
265 if (sourceIndex >= 0)
274 float inputVol = Px_GetInputVolume(mPortMixer);
276 Px_SetInputVolume(mPortMixer, 0.0);
277 if (Px_GetInputVolume(mPortMixer) > 0.1)
279 Px_SetInputVolume(mPortMixer, 0.2f);
280 if (Px_GetInputVolume(mPortMixer) < 0.1 ||
281 Px_GetInputVolume(mPortMixer) > 0.3)
283 Px_SetInputVolume(mPortMixer, inputVol);
285 Pa_CloseStream(stream);
289 wxPrintf(
"PortMixer: Recording: %s\n"
296 const std::shared_ptr<AudacityProject> &project,
const std::weak_ptr<Meter> &wMeter)
299 ( pOwningProject ) && ( pOwningProject != project))
302 auto meter = wMeter.lock();
306 meter->Reset(
mRate,
true);
313 const std::shared_ptr<AudacityProject> &project,
const std::weak_ptr<Meter> &wMeter)
316 ( pOwningProject ) && ( pOwningProject != project))
319 auto meter = wMeter.lock();
323 meter->Reset(
mRate,
true);
331 return mPaused.load(std::memory_order_relaxed);
344 bool isActive =
false;
349 isActive = isActive ||
351 [](
auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
384 std::vector<long> supported;
385 int irate = (int)rate;
386 const PaDeviceInfo* devInfo = NULL;
389 devInfo = Pa_GetDeviceInfo(devIndex);
393 wxLogDebug(
wxT(
"GetSupportedPlaybackRates() Could not get device info!"));
399 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
400 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
402 PaStreamParameters pars;
404 pars.device = devIndex;
405 pars.channelCount = 1;
406 pars.sampleFormat = paFloat32;
407 pars.suggestedLatency = devInfo->defaultHighOutputLatency;
408 pars.hostApiSpecificStreamInfo = NULL;
415 if (!(isDirectSound &&
RatesToTry[i] > 200000)){
416 if (Pa_IsFormatSupported(NULL, &pars,
RatesToTry[i]) == 0)
427 if (!(isDirectSound &&
RatesToTry[i] > 200000))
428 if (Pa_IsFormatSupported(NULL, &pars, irate) == 0)
429 supported.push_back(irate);
449 std::vector<long> supported;
450 int irate = (int)rate;
451 const PaDeviceInfo* devInfo = NULL;
454 devInfo = Pa_GetDeviceInfo(devIndex);
458 wxLogDebug(
wxT(
"GetSupportedCaptureRates() Could not get device info!"));
468 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
469 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
471 PaStreamParameters pars;
473 pars.device = devIndex;
474 pars.channelCount = recordChannels;
475 pars.sampleFormat = paFloat32;
476 pars.suggestedLatency = latencyDuration / 1000.0;
477 pars.hostApiSpecificStreamInfo = NULL;
483 if (!(isDirectSound &&
RatesToTry[i] > 200000))
485 if (Pa_IsFormatSupported(&pars, NULL,
RatesToTry[i]) == 0)
496 if (!(isDirectSound &&
RatesToTry[i] > 200000))
497 if (Pa_IsFormatSupported(&pars, NULL, irate) == 0)
498 supported.push_back(irate);
505 int playDevice,
int recDevice,
double rate)
508 if (playDevice == -1) {
511 if (recDevice == -1) {
529 std::vector<long> result;
531 for (i = 0; i < (int)playback.size(); i++)
533 result.push_back(playback[i]);
564 if (rates.empty())
return 44100;
570int AudioIOBase::getRecordSourceIndex(PxMixer *portMixer)
574 int numSources = Px_GetNumInputSources(portMixer);
575 for (i = 0; i < numSources; i++) {
576 if (sourceName == wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i))))
585 wxString devName(devNameArg);
591 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
592 PaHostApiIndex hostNum;
593 for (hostNum = 0; hostNum < hostCnt; hostNum++)
595 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
596 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
598 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
600 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
602 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
603 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxOutputChannels > 0 )
613 return hinfo->defaultOutputDevice;
621 PaDeviceIndex deviceNum = Pa_GetDefaultOutputDevice();
640 wxString devName(devNameArg);
646 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
647 PaHostApiIndex hostNum;
648 for (hostNum = 0; hostNum < hostCnt; hostNum++)
650 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
651 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
653 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
655 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
657 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
658 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxInputChannels > 0 )
668 return hinfo->defaultInputDevice;
674 PaDeviceIndex deviceNum = Pa_GetDefaultInputDevice();
697 wxStringOutputStream o;
698 wxTextOutputStream s(o, wxEOL_UNIX);
701 return XO(
"Stream is active ... unable to gather information.\n")
707 int recDeviceNum = Pa_GetDefaultInputDevice();
708 int playDeviceNum = Pa_GetDefaultOutputDevice();
709 int cnt = Pa_GetDeviceCount();
712 wxLogDebug(
wxT(
"Portaudio reports %d audio devices"),cnt);
714 s <<
wxT(
"==============================\n");
715 s <<
XO(
"Default recording device number: %d\n").Format( recDeviceNum );
716 s <<
XO(
"Default playback device number: %d\n").Format( playDeviceNum);
724 s <<
XO(
"No devices found\n");
725 return o.GetString();
728 const PaDeviceInfo* info;
730 for (j = 0; j < cnt; j++) {
731 s <<
wxT(
"==============================\n");
733 info = Pa_GetDeviceInfo(j);
735 s <<
XO(
"Device info unavailable for: %d\n").Format( j );
740 s <<
XO(
"Device ID: %d\n").Format( j );
741 s <<
XO(
"Device name: %s\n").Format(
name );
742 s <<
XO(
"Host name: %s\n").Format(
HostName(info) );
743 s <<
XO(
"Recording channels: %d\n").Format( info->maxInputChannels );
744 s <<
XO(
"Playback channels: %d\n").Format( info->maxOutputChannels );
745 s <<
XO(
"Low Recording Latency: %g\n").Format( info->defaultLowInputLatency );
746 s <<
XO(
"Low Playback Latency: %g\n").Format( info->defaultLowOutputLatency );
747 s <<
XO(
"High Recording Latency: %g\n").Format( info->defaultHighInputLatency );
748 s <<
XO(
"High Playback Latency: %g\n").Format( info->defaultHighOutputLatency );
753 s <<
XO(
"Supported Rates:\n");
754 for (
int k = 0; k < (int) rates.size(); k++) {
755 s <<
wxT(
" ") << (int)rates[k] <<
wxT(
"\n");
758 if (
name == playDevice && info->maxOutputChannels > 0)
761 if (
name == recDevice && info->maxInputChannels > 0)
766 if (recDeviceNum < 0 && info->maxInputChannels > 0){
769 if (playDeviceNum < 0 && info->maxOutputChannels > 0){
774 bool haveRecDevice = (recDeviceNum >= 0);
775 bool havePlayDevice = (playDeviceNum >= 0);
777 s <<
wxT(
"==============================\n");
779 s <<
XO(
"Selected recording device: %d - %s\n").Format( recDeviceNum, recDevice );
781 s <<
XO(
"No recording device found for '%s'.\n").Format( recDevice );
784 s <<
XO(
"Selected playback device: %d - %s\n").Format( playDeviceNum, playDevice );
786 s <<
XO(
"No playback device found for '%s'.\n").Format( playDevice );
788 std::vector<long> supportedSampleRates;
790 if (havePlayDevice && haveRecDevice) {
793 s <<
XO(
"Supported Rates:\n");
794 for (
int k = 0; k < (int) supportedSampleRates.size(); k++) {
795 s <<
wxT(
" ") << (int)supportedSampleRates[k] <<
wxT(
"\n");
799 s <<
XO(
"Cannot check mutual sample rates without both devices.\n");
800 return o.GetString();
803#if defined(USE_PORTMIXER)
804 if (supportedSampleRates.size() > 0)
806 int highestSampleRate = supportedSampleRates.back();
807 bool EmulateMixerInputVol =
true;
808 float MixerInputVol = 1.0;
809 float MixerOutputVol = 1.0;
815 PaStreamParameters playbackParameters;
817 playbackParameters.device = playDeviceNum;
818 playbackParameters.sampleFormat = paFloat32;
819 playbackParameters.hostApiSpecificStreamInfo = NULL;
820 playbackParameters.channelCount = 1;
821 if (Pa_GetDeviceInfo(playDeviceNum)){
822 playbackParameters.suggestedLatency =
823 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
826 playbackParameters.suggestedLatency =
829 PaStreamParameters captureParameters;
831 captureParameters.device = recDeviceNum;
832 captureParameters.sampleFormat = paFloat32;;
833 captureParameters.hostApiSpecificStreamInfo = NULL;
834 captureParameters.channelCount = 1;
835 if (Pa_GetDeviceInfo(recDeviceNum)){
836 captureParameters.suggestedLatency =
837 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
840 captureParameters.suggestedLatency =
844 error = Pa_OpenStream(&stream,
845 &captureParameters, &playbackParameters,
846 highestSampleRate, paFramesPerBufferUnspecified,
847 paClipOff | paDitherOff,
851 error = Pa_OpenStream(&stream,
852 &captureParameters, NULL,
853 highestSampleRate, paFramesPerBufferUnspecified,
854 paClipOff | paDitherOff,
859 s <<
XO(
"Received %d while opening devices\n").Format( error );
860 return o.GetString();
863 PxMixer *PortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
866 s <<
XO(
"Unable to open Portmixer\n");
867 Pa_CloseStream(stream);
868 return o.GetString();
871 s <<
wxT(
"==============================\n");
872 s <<
XO(
"Available mixers:\n");
875 cnt = Px_GetNumMixers(stream);
876 for (
int i = 0; i < cnt; i++) {
877 wxString
name = wxSafeConvertMB2WX(Px_GetMixerName(stream, i));
878 s <<
XO(
"%d - %s\n").Format( i,
name );
881 s <<
wxT(
"==============================\n");
882 s <<
XO(
"Available recording sources:\n");
883 cnt = Px_GetNumInputSources(PortMixer);
884 for (
int i = 0; i < cnt; i++) {
885 wxString
name = wxSafeConvertMB2WX(Px_GetInputSourceName(PortMixer, i));
886 s <<
XO(
"%d - %s\n").Format( i,
name );
889 s <<
wxT(
"==============================\n");
890 s <<
XO(
"Available playback volumes:\n");
891 cnt = Px_GetNumOutputVolumes(PortMixer);
892 for (
int i = 0; i < cnt; i++) {
893 wxString
name = wxSafeConvertMB2WX(Px_GetOutputVolumeName(PortMixer, i));
894 s <<
XO(
"%d - %s\n").Format( i,
name );
899 MixerInputVol = Px_GetInputVolume(PortMixer);
900 EmulateMixerInputVol =
false;
901 Px_SetInputVolume(PortMixer, 0.0);
902 if (Px_GetInputVolume(PortMixer) > 0.1)
903 EmulateMixerInputVol =
true;
904 Px_SetInputVolume(PortMixer, 0.2f);
905 if (Px_GetInputVolume(PortMixer) < 0.1 ||
906 Px_GetInputVolume(PortMixer) > 0.3)
907 EmulateMixerInputVol =
true;
908 Px_SetInputVolume(PortMixer, MixerInputVol);
910 Pa_CloseStream(stream);
912 s <<
wxT(
"==============================\n");
913 s << ( EmulateMixerInputVol
914 ?
XO(
"Recording volume is emulated\n")
915 :
XO(
"Recording volume is native\n") );
917 Px_CloseMixer(PortMixer);
921 return o.GetString();
926 std::vector<AudioIODiagnostics> result;
928 wxT(
"audiodev.txt"), GetDeviceInfo(),
wxT(
"Audio Device Info") });
929 for(
auto &pExt : mAudioIOExt )
931 result.emplace_back(pExt->Dump());
936 L
"/AudioIO/Host", L
"" };
938 L
"/AudioIO/LatencyCorrection", -130.0 };
940 L
"/AudioIO/LatencyDuration", 100.0 };
942 L
"/AudioIO/PlaybackDevice", L
"" };
944 L
"/AudioIO/PlaybackSource", L
"" };
946 L
"/AudioIO/PlaybackVolume", 1.0 };
948 L
"/AudioIO/RecordChannels", 2 };
950 L
"/AudioIO/RecordingDevice", L
"" };
952 L
"/AudioIO/RecordingSource", L
"" };
954 L
"/AudioIO/RecordingSourceIndex", -1 };
DoubleSetting AudioIOLatencyCorrection
StringSetting AudioIORecordingSource
DoubleSetting AudioIOPlaybackVolume
StringSetting AudioIOPlaybackSource
StringSetting AudioIOPlaybackDevice
DoubleSetting AudioIOLatencyDuration
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
IntSetting AudioIORecordingSourceIndex
IntSetting AudioIORecordChannels
const TranslatableString name
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)
std::atomic< bool > mPaused
True if audio playback is paused.
static std::vector< long > mCachedSampleRates
double mRate
Audio playback rate in samples per second.
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1, double rate=0.0)
Get a list of sample rates the input (recording) device supports.
bool IsMonitoring() const
Returns true if we're monitoring input (but not recording or playing actual audio)
static int mCachedCaptureIndex
wxString GetDeviceInfo() const
Get diagnostic information on all the available audio I/O devices.
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1, double rate=0.0)
Get a list of sample rates the output (playback) device supports.
static const int StandardRates[]
Array of common audio sample rates.
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
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 > mCachedCaptureRates
static int GetOptimalSupportedSampleRate()
Get a supported sample rate which can be used a an optimal default.
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.
std::weak_ptr< AudacityProject > mOwningProject
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...
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 const int NumStandardRates
How many standard sample rates there are.
static std::vector< long > mCachedPlaybackRates
static int mCachedPlaybackIndex
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.
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.