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);
138#if defined(USE_PORTMIXER)
146 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
147 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
148 mPreviousHWPlaythrough = -1.0;
150 Px_CloseMixer(mPortMixer);
157 int highestSampleRate;
165 highestSampleRate = 44100;
179 PaStreamParameters playbackParameters;
181 playbackParameters.device = playDeviceNum;
182 playbackParameters.sampleFormat = paFloat32;
183 playbackParameters.hostApiSpecificStreamInfo = NULL;
184 playbackParameters.channelCount = 1;
185 if (Pa_GetDeviceInfo(playDeviceNum))
186 playbackParameters.suggestedLatency =
187 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
189 playbackParameters.suggestedLatency =
192 PaStreamParameters captureParameters;
194 captureParameters.device = recDeviceNum;
195 captureParameters.sampleFormat = paFloat32;;
196 captureParameters.hostApiSpecificStreamInfo = NULL;
197 captureParameters.channelCount = 1;
198 if (Pa_GetDeviceInfo(recDeviceNum))
199 captureParameters.suggestedLatency =
200 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
202 captureParameters.suggestedLatency =
207 error = Pa_OpenStream(&stream,
208 &captureParameters, &playbackParameters,
209 highestSampleRate, paFramesPerBufferUnspecified,
210 paClipOff | paDitherOff,
215 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
217 Pa_CloseStream(stream);
224 error = Pa_OpenStream(&stream,
225 &captureParameters, NULL,
226 highestSampleRate, paFramesPerBufferUnspecified,
227 paClipOff | paDitherOff,
231 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
233 Pa_CloseStream(stream);
241 error = Pa_OpenStream(&stream,
242 NULL, &playbackParameters,
243 highestSampleRate, paFramesPerBufferUnspecified,
244 paClipOff | paDitherOff,
248 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
250 Pa_CloseStream(stream);
264 if (sourceIndex >= 0) {
267 sourceIndex = getRecordSourceIndex(mPortMixer);
268 if (sourceIndex >= 0)
277 float inputVol = Px_GetInputVolume(mPortMixer);
279 Px_SetInputVolume(mPortMixer, 0.0);
280 if (Px_GetInputVolume(mPortMixer) > 0.1)
282 Px_SetInputVolume(mPortMixer, 0.2f);
283 if (Px_GetInputVolume(mPortMixer) < 0.1 ||
284 Px_GetInputVolume(mPortMixer) > 0.3)
286 Px_SetInputVolume(mPortMixer, inputVol);
288 Pa_CloseStream(stream);
292 wxPrintf(
"PortMixer: Recording: %s\n"
299 const std::shared_ptr<AudacityProject> &
project,
const std::weak_ptr<Meter> &wMeter)
302 ( pOwningProject ) && ( pOwningProject !=
project))
305 auto meter = wMeter.lock();
309 meter->Reset(
mRate,
true);
316 const std::shared_ptr<AudacityProject> &
project,
const std::weak_ptr<Meter> &wMeter)
319 ( pOwningProject ) && ( pOwningProject !=
project))
322 auto meter = wMeter.lock();
326 meter->Reset(
mRate,
true);
334 return mPaused.load(std::memory_order_relaxed);
347 bool isActive =
false;
352 isActive = isActive ||
354 [](
auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
387 std::vector<long> supported;
388 int irate = (int)rate;
389 const PaDeviceInfo* devInfo = NULL;
392 devInfo = Pa_GetDeviceInfo(devIndex);
396 wxLogDebug(
wxT(
"GetSupportedPlaybackRates() Could not get device info!"));
402 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
403 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
405 PaStreamParameters pars;
407 pars.device = devIndex;
408 pars.channelCount = 1;
409 pars.sampleFormat = paFloat32;
410 pars.suggestedLatency = devInfo->defaultHighOutputLatency;
411 pars.hostApiSpecificStreamInfo = NULL;
418 if (!(isDirectSound &&
RatesToTry[i] > 200000)){
419 if (Pa_IsFormatSupported(NULL, &pars,
RatesToTry[i]) == 0)
430 if (!(isDirectSound && irate > 200000))
431 if (Pa_IsFormatSupported(NULL, &pars, irate) == 0)
432 supported.push_back(irate);
452 std::vector<long> supported;
453 int irate = (int)rate;
454 const PaDeviceInfo* devInfo = NULL;
457 devInfo = Pa_GetDeviceInfo(devIndex);
461 wxLogDebug(
wxT(
"GetSupportedCaptureRates() Could not get device info!"));
471 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
472 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
474 PaStreamParameters pars;
476 pars.device = devIndex;
477 pars.channelCount = recordChannels;
478 pars.sampleFormat = paFloat32;
479 pars.suggestedLatency = latencyDuration / 1000.0;
480 pars.hostApiSpecificStreamInfo = NULL;
486 if (!(isDirectSound &&
RatesToTry[i] > 200000))
488 if (Pa_IsFormatSupported(&pars, NULL,
RatesToTry[i]) == 0)
499 if (!(isDirectSound && irate > 200000))
500 if (Pa_IsFormatSupported(&pars, NULL, irate) == 0)
501 supported.push_back(irate);
508 int playDevice,
int recDevice,
double rate)
511 if (playDevice == -1) {
514 if (recDevice == -1) {
532 std::vector<long> result;
534 for (i = 0; i < (int)playback.size(); i++)
536 result.push_back(playback[i]);
567 if (rates.empty())
return 44100;
573int AudioIOBase::getRecordSourceIndex(PxMixer *portMixer)
577 int numSources = Px_GetNumInputSources(portMixer);
578 for (i = 0; i < numSources; i++) {
579 if (sourceName == wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i))))
588 wxString devName(devNameArg);
594 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
595 PaHostApiIndex hostNum;
596 for (hostNum = 0; hostNum < hostCnt; hostNum++)
598 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
599 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
601 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
603 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
605 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
606 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxOutputChannels > 0 )
616 return hinfo->defaultOutputDevice;
624 PaDeviceIndex deviceNum = Pa_GetDefaultOutputDevice();
643 wxString devName(devNameArg);
649 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
650 PaHostApiIndex hostNum;
651 for (hostNum = 0; hostNum < hostCnt; hostNum++)
653 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
654 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
656 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
658 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
660 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
661 if (dinfo &&
DeviceName(dinfo) == devName && dinfo->maxInputChannels > 0 )
671 return hinfo->defaultInputDevice;
677 PaDeviceIndex deviceNum = Pa_GetDefaultInputDevice();
700 wxStringOutputStream o;
701 wxTextOutputStream s(o, wxEOL_UNIX);
704 return XO(
"Stream is active ... unable to gather information.\n")
710 int recDeviceNum = Pa_GetDefaultInputDevice();
711 int playDeviceNum = Pa_GetDefaultOutputDevice();
712 int cnt = Pa_GetDeviceCount();
715 wxLogDebug(
wxT(
"Portaudio reports %d audio devices"),cnt);
717 s <<
wxT(
"==============================\n");
718 s <<
XO(
"Default recording device number: %d\n").Format( recDeviceNum );
719 s <<
XO(
"Default playback device number: %d\n").Format( playDeviceNum);
727 s <<
XO(
"No devices found\n");
728 return o.GetString();
731 const PaDeviceInfo* info;
733 for (j = 0; j < cnt; j++) {
734 s <<
wxT(
"==============================\n");
736 info = Pa_GetDeviceInfo(j);
738 s <<
XO(
"Device info unavailable for: %d\n").Format( j );
743 s <<
XO(
"Device ID: %d\n").Format( j );
744 s <<
XO(
"Device name: %s\n").Format(
name );
745 s <<
XO(
"Host name: %s\n").Format(
HostName(info) );
746 s <<
XO(
"Recording channels: %d\n").Format( info->maxInputChannels );
747 s <<
XO(
"Playback channels: %d\n").Format( info->maxOutputChannels );
748 s <<
XO(
"Low Recording Latency: %g\n").Format( info->defaultLowInputLatency );
749 s <<
XO(
"Low Playback Latency: %g\n").Format( info->defaultLowOutputLatency );
750 s <<
XO(
"High Recording Latency: %g\n").Format( info->defaultHighInputLatency );
751 s <<
XO(
"High Playback Latency: %g\n").Format( info->defaultHighOutputLatency );
756 s <<
XO(
"Supported Rates:\n");
757 for (
int k = 0; k < (int) rates.size(); k++) {
758 s <<
wxT(
" ") << (int)rates[k] <<
wxT(
"\n");
761 if (
name == playDevice && info->maxOutputChannels > 0)
764 if (
name == recDevice && info->maxInputChannels > 0)
769 if (recDeviceNum < 0 && info->maxInputChannels > 0){
772 if (playDeviceNum < 0 && info->maxOutputChannels > 0){
777 bool haveRecDevice = (recDeviceNum >= 0);
778 bool havePlayDevice = (playDeviceNum >= 0);
780 s <<
wxT(
"==============================\n");
782 s <<
XO(
"Selected recording device: %d - %s\n").Format( recDeviceNum, recDevice );
784 s <<
XO(
"No recording device found for '%s'.\n").Format( recDevice );
787 s <<
XO(
"Selected playback device: %d - %s\n").Format( playDeviceNum, playDevice );
789 s <<
XO(
"No playback device found for '%s'.\n").Format( playDevice );
793 if (havePlayDevice && haveRecDevice) {
796 s <<
XO(
"Supported Rates:\n");
802 s <<
XO(
"Cannot check mutual sample rates without both devices.\n");
803 return o.GetString();
806#if defined(USE_PORTMIXER)
810 bool EmulateMixerInputVol =
true;
811 float MixerInputVol = 1.0;
812 float MixerOutputVol = 1.0;
818 PaStreamParameters playbackParameters;
820 playbackParameters.device = playDeviceNum;
821 playbackParameters.sampleFormat = paFloat32;
822 playbackParameters.hostApiSpecificStreamInfo = NULL;
823 playbackParameters.channelCount = 1;
824 if (Pa_GetDeviceInfo(playDeviceNum)){
825 playbackParameters.suggestedLatency =
826 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
829 playbackParameters.suggestedLatency =
832 PaStreamParameters captureParameters;
834 captureParameters.device = recDeviceNum;
835 captureParameters.sampleFormat = paFloat32;;
836 captureParameters.hostApiSpecificStreamInfo = NULL;
837 captureParameters.channelCount = 1;
838 if (Pa_GetDeviceInfo(recDeviceNum)){
839 captureParameters.suggestedLatency =
840 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
843 captureParameters.suggestedLatency =
847 error = Pa_OpenStream(&stream,
848 &captureParameters, &playbackParameters,
849 highestSampleRate, paFramesPerBufferUnspecified,
850 paClipOff | paDitherOff,
854 error = Pa_OpenStream(&stream,
855 &captureParameters, NULL,
856 highestSampleRate, paFramesPerBufferUnspecified,
857 paClipOff | paDitherOff,
862 s <<
XO(
"Received %d while opening devices\n").Format( error );
863 return o.GetString();
866 PxMixer *PortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
869 s <<
XO(
"Unable to open Portmixer\n");
870 Pa_CloseStream(stream);
871 return o.GetString();
874 s <<
wxT(
"==============================\n");
875 s <<
XO(
"Available mixers:\n");
878 cnt = Px_GetNumMixers(stream);
879 for (
int i = 0; i < cnt; i++) {
880 wxString
name = wxSafeConvertMB2WX(Px_GetMixerName(stream, i));
881 s <<
XO(
"%d - %s\n").Format( i,
name );
884 s <<
wxT(
"==============================\n");
885 s <<
XO(
"Available recording sources:\n");
886 cnt = Px_GetNumInputSources(PortMixer);
887 for (
int i = 0; i < cnt; i++) {
888 wxString
name = wxSafeConvertMB2WX(Px_GetInputSourceName(PortMixer, i));
889 s <<
XO(
"%d - %s\n").Format( i,
name );
892 s <<
wxT(
"==============================\n");
893 s <<
XO(
"Available playback volumes:\n");
894 cnt = Px_GetNumOutputVolumes(PortMixer);
895 for (
int i = 0; i < cnt; i++) {
896 wxString
name = wxSafeConvertMB2WX(Px_GetOutputVolumeName(PortMixer, i));
897 s <<
XO(
"%d - %s\n").Format( i,
name );
902 MixerInputVol = Px_GetInputVolume(PortMixer);
903 EmulateMixerInputVol =
false;
904 Px_SetInputVolume(PortMixer, 0.0);
905 if (Px_GetInputVolume(PortMixer) > 0.1)
906 EmulateMixerInputVol =
true;
907 Px_SetInputVolume(PortMixer, 0.2f);
908 if (Px_GetInputVolume(PortMixer) < 0.1 ||
909 Px_GetInputVolume(PortMixer) > 0.3)
910 EmulateMixerInputVol =
true;
911 Px_SetInputVolume(PortMixer, MixerInputVol);
913 Pa_CloseStream(stream);
915 s <<
wxT(
"==============================\n");
916 s << ( EmulateMixerInputVol
917 ?
XO(
"Recording volume is emulated\n")
918 :
XO(
"Recording volume is native\n") );
920 Px_CloseMixer(PortMixer);
924 return o.GetString();
929 std::vector<AudioIODiagnostics> result;
931 wxT(
"audiodev.txt"), GetDeviceInfo(),
wxT(
"Audio Device Info") });
932 for(
auto &pExt : mAudioIOExt )
934 result.emplace_back(pExt->Dump());
939 L
"/AudioIO/Host", L
"" };
941 L
"/AudioIO/LatencyCorrection", -130.0 };
943 L
"/AudioIO/LatencyDuration", 100.0 };
945 L
"/AudioIO/PlaybackDevice", L
"" };
947 L
"/AudioIO/PlaybackSource", L
"" };
949 L
"/AudioIO/PlaybackVolume", 1.0 };
951 L
"/AudioIO/RecordChannels", 2 };
953 L
"/AudioIO/RecordingDevice", L
"" };
955 L
"/AudioIO/RecordingSource", L
"" };
957 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.
constexpr int supportedSampleRates[]