Audacity 3.2.0
AudioIOBase.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5AudioIOBase.cpp
6
7Paul Licameli split from AudioIO.cpp
8
9**********************************************************************/
10
11
12#include "AudioIOBase.h"
13
14#include <cassert>
15
16#include <wx/log.h>
17#include <wx/sstream.h>
18#include <wx/txtstrm.h>
19
20#include "IteratorX.h"
21#include "Meter.h"
22#include "Prefs.h"
23
24#include "portaudio.h"
25
26#if USE_PORTMIXER
27#include "portmixer.h"
28#endif
29
31std::vector<long> AudioIOBase::mCachedPlaybackRates;
33std::vector<long> AudioIOBase::mCachedCaptureRates;
34std::vector<long> AudioIOBase::mCachedSampleRates;
36
37const int AudioIOBase::StandardRates[] = {
38 8000,
39 11025,
40 16000,
41 22050,
42 32000,
43 44100,
44 48000,
45 88200,
46 96000,
47 176400,
48 192000,
49 352800,
50 384000
51};
52
54
55const int AudioIOBase::RatesToTry[] = {
56 8000,
57 9600,
58 11025,
59 12000,
60 15000,
61 16000,
62 22050,
63 24000,
64 32000,
65 44100,
66 48000,
67 88200,
68 96000,
69 176400,
70 192000,
71 352800,
72 384000
73};
75
76wxString AudioIOBase::DeviceName(const PaDeviceInfo* info)
77{
78 wxString infoName = wxSafeConvertMB2WX(info->name);
79
80 return infoName;
81}
82
83wxString AudioIOBase::HostName(const PaDeviceInfo* info)
84{
85 wxString hostapiName = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
86
87 return hostapiName;
88}
89
90std::unique_ptr<AudioIOBase> AudioIOBase::ugAudioIO;
91
93
95{
96 return ugAudioIO.get();
97}
98
99AudioIOBase::AudioIOBase() = default;
100
101AudioIOBase::~AudioIOBase() = default;
102
103void AudioIOBase::SetMixer(int inputSource)
104{
105#if defined(USE_PORTMIXER)
106 int oldRecordSource = Px_GetCurrentInputSource(mPortMixer);
107 if ( inputSource != oldRecordSource )
108 Px_SetCurrentInputSource(mPortMixer, inputSource);
109#endif
110}
111
113{
114 // This should not happen, but it would screw things up if it did.
115 // Vaughan, 2010-10-08: But it *did* happen, due to a bug, and nobody
116 // caught it because this method just returned. Added wxASSERT().
117 wxASSERT(!IsStreamActive());
118 if (IsStreamActive())
119 return;
120
121 // get the selected record and playback devices
122 const int playDeviceNum = getPlayDevIndex();
123 const int recDeviceNum = getRecordDevIndex();
124
125 // If no change needed, return
126 if (mCachedPlaybackIndex == playDeviceNum &&
127 mCachedCaptureIndex == recDeviceNum)
128 return;
129
130 // cache playback/capture rates
133 mCachedSampleRates = GetSupportedSampleRates(playDeviceNum, recDeviceNum);
134 mCachedPlaybackIndex = playDeviceNum;
135 mCachedCaptureIndex = recDeviceNum;
136 mCachedBestRateIn = 0.0;
137
138#if defined(USE_PORTMIXER)
139
140 // if we have a PortMixer object, close it down
141 if (mPortMixer) {
142 #if __WXMAC__
143 // on the Mac we must make sure that we restore the hardware playthrough
144 // state of the sound device to what it was before, because there isn't
145 // a UI for this (!)
146 if (Px_SupportsPlaythrough(mPortMixer) && mPreviousHWPlaythrough >= 0.0)
147 Px_SetPlaythrough(mPortMixer, mPreviousHWPlaythrough);
148 mPreviousHWPlaythrough = -1.0;
149 #endif
150 Px_CloseMixer(mPortMixer);
151 mPortMixer = NULL;
152 }
153
154 // that might have given us no rates whatsoever, so we have to guess an
155 // answer to do the next bit
156 int numrates = mCachedSampleRates.size();
157 int highestSampleRate;
158 if (numrates > 0)
159 {
160 highestSampleRate = mCachedSampleRates[numrates - 1];
161 }
162 else
163 { // we don't actually have any rates that work for Rec and Play. Guess one
164 // to use for messing with the mixer, which doesn't actually do either
165 highestSampleRate = 44100;
166 // mCachedSampleRates is still empty, but it's not used again, so
167 // can ignore
168 }
169
170 mInputMixerWorks = false;
171
172 int error;
173 // This tries to open the device with the samplerate worked out above, which
174 // will be the highest available for play and record on the device, or
175 // 44.1kHz if the info cannot be fetched.
176
177 PaStream *stream;
178
179 PaStreamParameters playbackParameters;
180
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;
188 else
189 playbackParameters.suggestedLatency =
191
192 PaStreamParameters captureParameters;
193
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;
201 else
202 captureParameters.suggestedLatency =
204
205 // try opening for record and playback
206 // Not really doing I/O so pass nullptr for the callback function
207 error = Pa_OpenStream(&stream,
208 &captureParameters, &playbackParameters,
209 highestSampleRate, paFramesPerBufferUnspecified,
210 paClipOff | paDitherOff,
211 nullptr, NULL);
212
213 if (!error) {
214 // Try portmixer for this stream
215 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
216 if (!mPortMixer) {
217 Pa_CloseStream(stream);
218 error = true;
219 }
220 }
221
222 // if that failed, try just for record
223 if( error ) {
224 error = Pa_OpenStream(&stream,
225 &captureParameters, NULL,
226 highestSampleRate, paFramesPerBufferUnspecified,
227 paClipOff | paDitherOff,
228 nullptr, NULL);
229
230 if (!error) {
231 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
232 if (!mPortMixer) {
233 Pa_CloseStream(stream);
234 error = true;
235 }
236 }
237 }
238
239 // finally, try just for playback
240 if ( error ) {
241 error = Pa_OpenStream(&stream,
242 NULL, &playbackParameters,
243 highestSampleRate, paFramesPerBufferUnspecified,
244 paClipOff | paDitherOff,
245 nullptr, NULL);
246
247 if (!error) {
248 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
249 if (!mPortMixer) {
250 Pa_CloseStream(stream);
251 error = true;
252 }
253 }
254 }
255
256 // FIXME: TRAP_ERR errors in HandleDeviceChange not reported.
257 // if it's still not working, give up
258 if( error )
259 return;
260
261 // Set input source
262#if USE_PORTMIXER
263 auto sourceIndex = AudioIORecordingSourceIndex.Read(); // defaults to -1
264 if (sourceIndex >= 0) {
265 //the current index of our source may be different because the stream
266 //is a combination of two devices, so update it.
267 sourceIndex = getRecordSourceIndex(mPortMixer);
268 if (sourceIndex >= 0)
269 SetMixer(sourceIndex);
270 }
271#endif
272
273 // Determine mixer capabilities - if it doesn't support control of output
274 // signal level, we emulate it (by multiplying this value by all outgoing
275 // samples)
276
277 float inputVol = Px_GetInputVolume(mPortMixer);
278 mInputMixerWorks = true; // assume it works unless proved wrong
279 Px_SetInputVolume(mPortMixer, 0.0);
280 if (Px_GetInputVolume(mPortMixer) > 0.1)
281 mInputMixerWorks = false; // can't set to zero
282 Px_SetInputVolume(mPortMixer, 0.2f);
283 if (Px_GetInputVolume(mPortMixer) < 0.1 ||
284 Px_GetInputVolume(mPortMixer) > 0.3)
285 mInputMixerWorks = false; // can't set level accurately
286 Px_SetInputVolume(mPortMixer, inputVol);
287
288 Pa_CloseStream(stream);
289
290
291 #if 0
292 wxPrintf("PortMixer: Recording: %s\n"
293 mInputMixerWorks? "hardware": "no control");
294 #endif
295#endif // USE_PORTMIXER
296}
297
299 const std::shared_ptr<AudacityProject> &project, const std::weak_ptr<Meter> &wMeter)
300{
301 if (auto pOwningProject = mOwningProject.lock();
302 ( pOwningProject ) && ( pOwningProject != project))
303 return;
304
305 auto meter = wMeter.lock();
306 if (meter)
307 {
308 mInputMeter = meter;
309 meter->Reset(mRate, true);
310 }
311 else
312 mInputMeter.reset();
313}
314
316 const std::shared_ptr<AudacityProject> &project, const std::weak_ptr<Meter> &wMeter)
317{
318 if (auto pOwningProject = mOwningProject.lock();
319 ( pOwningProject ) && ( pOwningProject != project))
320 return;
321
322 auto meter = wMeter.lock();
323 if (meter)
324 {
325 mOutputMeter = meter;
326 meter->Reset(mRate, true);
327 }
328 else
329 mOutputMeter.reset();
330}
331
333{
334 return mPaused.load(std::memory_order_relaxed);
335}
336
338{
339 if (mStreamToken != 0)
340 return true;
341
342 return false;
343}
344
346{
347 bool isActive = false;
348 // JKC: Not reporting any Pa error, but that looks OK.
349 if( mPortStreamV19 )
350 isActive = (Pa_IsStreamActive( mPortStreamV19 ) > 0);
351
352 isActive = isActive ||
353 std::any_of(mAudioIOExt.begin(), mAudioIOExt.end(),
354 [](auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
355 return isActive;
356}
357
358bool AudioIOBase::IsStreamActive(int token) const
359{
360 return (this->IsStreamActive() && this->IsAudioTokenActive(token));
361}
362
364{
365 return ( token > 0 && token == mStreamToken );
366}
367
369{
370 return ( mPortStreamV19 && mStreamToken==0 );
371}
372
373std::vector<long> AudioIOBase::GetSupportedPlaybackRates(int devIndex, double rate)
374{
375 if (devIndex == -1)
376 { // weren't given a device index, get the prefs / default one
377 devIndex = getPlayDevIndex();
378 }
379
380 // Check if we can use the cached rates
381 if (mCachedPlaybackIndex != -1 && devIndex == mCachedPlaybackIndex
382 && (rate == 0.0 || make_iterator_range(mCachedPlaybackRates).contains(rate)))
383 {
385 }
386
387 std::vector<long> supported;
388 int irate = (int)rate;
389 const PaDeviceInfo* devInfo = NULL;
390 int i;
391
392 devInfo = Pa_GetDeviceInfo(devIndex);
393
394 if (!devInfo)
395 {
396 wxLogDebug(wxT("GetSupportedPlaybackRates() Could not get device info!"));
397 return supported;
398 }
399
400 // LLL: Remove when a proper method of determining actual supported
401 // DirectSound rate is devised.
402 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
403 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
404
405 PaStreamParameters pars;
406
407 pars.device = devIndex;
408 pars.channelCount = 1;
409 pars.sampleFormat = paFloat32;
410 pars.suggestedLatency = devInfo->defaultHighOutputLatency;
411 pars.hostApiSpecificStreamInfo = NULL;
412
413 // JKC: PortAudio Errors handled OK here. No need to report them
414 for (i = 0; i < NumRatesToTry; i++)
415 {
416 // LLL: Remove when a proper method of determining actual supported
417 // DirectSound rate is devised.
418 if (!(isDirectSound && RatesToTry[i] > 200000)){
419 if (Pa_IsFormatSupported(NULL, &pars, RatesToTry[i]) == 0)
420 supported.push_back(RatesToTry[i]);
421 Pa_Sleep( 10 );// There are ALSA drivers that don't like being probed
422 // too quickly.
423 }
424 }
425
426 if (irate != 0 && !make_iterator_range(supported).contains(irate))
427 {
428 // LLL: Remove when a proper method of determining actual supported
429 // DirectSound rate is devised.
430 if (!(isDirectSound && irate > 200000))
431 if (Pa_IsFormatSupported(NULL, &pars, irate) == 0)
432 supported.push_back(irate);
433 }
434
435 return supported;
436}
437
438std::vector<long> AudioIOBase::GetSupportedCaptureRates(int devIndex, double rate)
439{
440 if (devIndex == -1)
441 { // not given a device, look up in prefs / default
442 devIndex = getRecordDevIndex();
443 }
444
445 // Check if we can use the cached rates
446 if (mCachedCaptureIndex != -1 && devIndex == mCachedCaptureIndex
447 && (rate == 0.0 || make_iterator_range(mCachedCaptureRates).contains(rate)))
448 {
449 return mCachedCaptureRates;
450 }
451
452 std::vector<long> supported;
453 int irate = (int)rate;
454 const PaDeviceInfo* devInfo = NULL;
455 int i;
456
457 devInfo = Pa_GetDeviceInfo(devIndex);
458
459 if (!devInfo)
460 {
461 wxLogDebug(wxT("GetSupportedCaptureRates() Could not get device info!"));
462 return supported;
463 }
464
465 auto latencyDuration = AudioIOLatencyDuration.Read();
466 // Why not defaulting to 2 as elsewhere?
467 auto recordChannels = AudioIORecordChannels.ReadWithDefault(1);
468
469 // LLL: Remove when a proper method of determining actual supported
470 // DirectSound rate is devised.
471 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
472 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
473
474 PaStreamParameters pars;
475
476 pars.device = devIndex;
477 pars.channelCount = recordChannels;
478 pars.sampleFormat = paFloat32;
479 pars.suggestedLatency = latencyDuration / 1000.0;
480 pars.hostApiSpecificStreamInfo = NULL;
481
482 for (i = 0; i < NumRatesToTry; i++)
483 {
484 // LLL: Remove when a proper method of determining actual supported
485 // DirectSound rate is devised.
486 if (!(isDirectSound && RatesToTry[i] > 200000))
487 {
488 if (Pa_IsFormatSupported(&pars, NULL, RatesToTry[i]) == 0)
489 supported.push_back(RatesToTry[i]);
490 Pa_Sleep( 10 );// There are ALSA drivers that don't like being probed
491 // too quickly.
492 }
493 }
494
495 if (irate != 0 && !make_iterator_range(supported).contains(irate))
496 {
497 // LLL: Remove when a proper method of determining actual supported
498 // DirectSound rate is devised.
499 if (!(isDirectSound && irate > 200000))
500 if (Pa_IsFormatSupported(&pars, NULL, irate) == 0)
501 supported.push_back(irate);
502 }
503
504 return supported;
505}
506
508 int playDevice, int recDevice, double rate)
509{
510 // Not given device indices, look up prefs
511 if (playDevice == -1) {
512 playDevice = getPlayDevIndex();
513 }
514 if (recDevice == -1) {
515 recDevice = getRecordDevIndex();
516 }
517
518 // Check if we can use the cached rates
519 if (mCachedPlaybackIndex != -1 && mCachedCaptureIndex != -1 &&
520 playDevice == mCachedPlaybackIndex &&
521 recDevice == mCachedCaptureIndex &&
522 (rate == 0.0 || make_iterator_range(mCachedSampleRates).contains(rate)))
523 {
524 return mCachedSampleRates;
525 }
526
527 auto playback = GetSupportedPlaybackRates(playDevice, rate);
528 auto capture = GetSupportedCaptureRates(recDevice, rate);
529 int i;
530
531 // Return only sample rates which are in both arrays
532 std::vector<long> result;
533
534 for (i = 0; i < (int)playback.size(); i++)
535 if (make_iterator_range(capture).contains(playback[i]))
536 result.push_back(playback[i]);
537
538 // If this yields no results, use the default sample rates nevertheless
539/* if (result.empty())
540 {
541 for (i = 0; i < NumStandardRates; i++)
542 result.push_back(StandardRates[i]);
543 }*/
544
545 return result;
546}
547
553{
554 auto rates = GetSupportedSampleRates();
555
556 if (make_iterator_range(rates).contains(44100))
557 return 44100;
558
559 if (make_iterator_range(rates).contains(48000))
560 return 48000;
561
562 // if there are no supported rates, the next bit crashes. So check first,
563 // and give them a "sensible" value if there are no valid values. They
564 // will still get an error later, but with any luck may have changed
565 // something by then. It's no worse than having an invalid default rate
566 // stored in the preferences, which we don't check for
567 if (rates.empty()) return 44100;
568
569 return rates.back();
570}
571
572#if USE_PORTMIXER
573int AudioIOBase::getRecordSourceIndex(PxMixer *portMixer)
574{
575 int i;
576 auto sourceName = AudioIORecordingSource.Read();
577 int numSources = Px_GetNumInputSources(portMixer);
578 for (i = 0; i < numSources; i++) {
579 if (sourceName == wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i))))
580 return i;
581 }
582 return -1;
583}
584#endif
585
586int AudioIOBase::getPlayDevIndex(const wxString &devNameArg)
587{
588 wxString devName(devNameArg);
589 // if we don't get given a device, look up the preferences
590 if (devName.empty())
591 devName = AudioIOPlaybackDevice.Read();
592
593 auto hostName = AudioIOHost.Read();
594 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
595 PaHostApiIndex hostNum;
596 for (hostNum = 0; hostNum < hostCnt; hostNum++)
597 {
598 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
599 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
600 {
601 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
602 {
603 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
604
605 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
606 if (dinfo && DeviceName(dinfo) == devName && dinfo->maxOutputChannels > 0 )
607 {
608 // this device name matches the stored one, and works.
609 // So we say this is the answer and return it
610 return deviceNum;
611 }
612 }
613
614 // The device wasn't found so use the default for this host.
615 // LL: At this point, preferences and active no longer match.
616 return hinfo->defaultOutputDevice;
617 }
618 }
619
620 // The host wasn't found, so use the default output device.
621 // FIXME: TRAP_ERR PaErrorCode not handled well (this code is similar to input code
622 // and the input side has more comments.)
623
624 PaDeviceIndex deviceNum = Pa_GetDefaultOutputDevice();
625
626 // Sometimes PortAudio returns -1 if it cannot find a suitable default
627 // device, so we just use the first one available
628 //
629 // LL: At this point, preferences and active no longer match
630 //
631 // And I can't imagine how far we'll get specifying an "invalid" index later
632 // on...are we certain "0" even exists?
633 if (deviceNum < 0) {
634 assert(false);
635 deviceNum = 0;
636 }
637
638 return deviceNum;
639}
640
641int AudioIOBase::getRecordDevIndex(const wxString &devNameArg)
642{
643 wxString devName(devNameArg);
644 // if we don't get given a device, look up the preferences
645 if (devName.empty())
646 devName = AudioIORecordingDevice.Read();
647
648 auto hostName = AudioIOHost.Read();
649 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
650 PaHostApiIndex hostNum;
651 for (hostNum = 0; hostNum < hostCnt; hostNum++)
652 {
653 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
654 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
655 {
656 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
657 {
658 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
659
660 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
661 if (dinfo && DeviceName(dinfo) == devName && dinfo->maxInputChannels > 0 )
662 {
663 // this device name matches the stored one, and works.
664 // So we say this is the answer and return it
665 return deviceNum;
666 }
667 }
668
669 // The device wasn't found so use the default for this host.
670 // LL: At this point, preferences and active no longer match.
671 return hinfo->defaultInputDevice;
672 }
673 }
674
675 // The host wasn't found, so use the default input device.
676 // FIXME: TRAP_ERR PaErrorCode not handled well in getRecordDevIndex()
677 PaDeviceIndex deviceNum = Pa_GetDefaultInputDevice();
678
679 // Sometimes PortAudio returns -1 if it cannot find a suitable default
680 // device, so we just use the first one available
681 // PortAudio has an error reporting function. We should log/report the error?
682 //
683 // LL: At this point, preferences and active no longer match
684 //
685 // And I can't imagine how far we'll get specifying an "invalid" index later
686 // on...are we certain "0" even exists?
687 if (deviceNum < 0) {
688 // JKC: This ASSERT will happen if you run with no config file
689 // This happens once. Config file will exist on the next run.
690 // TODO: Look into this a bit more. Could be relevant to blank Device Toolbar.
691 assert(false);
692 deviceNum = 0;
693 }
694
695 return deviceNum;
696}
697
699{
700 wxStringOutputStream o;
701 wxTextOutputStream s(o, wxEOL_UNIX);
702
703 if (IsStreamActive()) {
704 return XO("Stream is active ... unable to gather information.\n")
705 .Translation();
706 }
707
708
709 // FIXME: TRAP_ERR PaErrorCode not handled. 3 instances in GetDeviceInfo().
710 int recDeviceNum = Pa_GetDefaultInputDevice();
711 int playDeviceNum = Pa_GetDefaultOutputDevice();
712 int cnt = Pa_GetDeviceCount();
713
714 // PRL: why only into the log?
715 wxLogDebug(wxT("Portaudio reports %d audio devices"),cnt);
716
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);
720
721 auto recDevice = AudioIORecordingDevice.Read();
722 auto playDevice = AudioIOPlaybackDevice.Read();
723 int j;
724
725 // This gets info on all available audio devices (input and output)
726 if (cnt <= 0) {
727 s << XO("No devices found\n");
728 return o.GetString();
729 }
730
731 const PaDeviceInfo* info;
732
733 for (j = 0; j < cnt; j++) {
734 s << wxT("==============================\n");
735
736 info = Pa_GetDeviceInfo(j);
737 if (!info) {
738 s << XO("Device info unavailable for: %d\n").Format( j );
739 continue;
740 }
741
742 wxString name = DeviceName(info);
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 );
752
753 auto rates = GetSupportedPlaybackRates(j, 0.0);
754
755 /* i18n-hint: Supported, meaning made available by the system */
756 s << XO("Supported Rates:\n");
757 for (int k = 0; k < (int) rates.size(); k++) {
758 s << wxT(" ") << (int)rates[k] << wxT("\n");
759 }
760
761 if (name == playDevice && info->maxOutputChannels > 0)
762 playDeviceNum = j;
763
764 if (name == recDevice && info->maxInputChannels > 0)
765 recDeviceNum = j;
766
767 // Sometimes PortAudio returns -1 if it cannot find a suitable default
768 // device, so we just use the first one available
769 if (recDeviceNum < 0 && info->maxInputChannels > 0){
770 recDeviceNum = j;
771 }
772 if (playDeviceNum < 0 && info->maxOutputChannels > 0){
773 playDeviceNum = j;
774 }
775 }
776
777 bool haveRecDevice = (recDeviceNum >= 0);
778 bool havePlayDevice = (playDeviceNum >= 0);
779
780 s << wxT("==============================\n");
781 if (haveRecDevice)
782 s << XO("Selected recording device: %d - %s\n").Format( recDeviceNum, recDevice );
783 else
784 s << XO("No recording device found for '%s'.\n").Format( recDevice );
785
786 if (havePlayDevice)
787 s << XO("Selected playback device: %d - %s\n").Format( playDeviceNum, playDevice );
788 else
789 s << XO("No playback device found for '%s'.\n").Format( playDevice );
790
791 std::vector<long> supportedSampleRates;
792
793 if (havePlayDevice && haveRecDevice) {
794 supportedSampleRates = GetSupportedSampleRates(playDeviceNum, recDeviceNum);
795
796 s << XO("Supported Rates:\n");
797 for (int k = 0; k < (int) supportedSampleRates.size(); k++) {
798 s << wxT(" ") << (int)supportedSampleRates[k] << wxT("\n");
799 }
800 }
801 else {
802 s << XO("Cannot check mutual sample rates without both devices.\n");
803 return o.GetString();
804 }
805
806#if defined(USE_PORTMIXER)
807 if (supportedSampleRates.size() > 0)
808 {
809 int highestSampleRate = supportedSampleRates.back();
810 bool EmulateMixerInputVol = true;
811 float MixerInputVol = 1.0;
812 float MixerOutputVol = 1.0;
813
814 int error;
815
816 PaStream *stream;
817
818 PaStreamParameters playbackParameters;
819
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;
827 }
828 else
829 playbackParameters.suggestedLatency =
831
832 PaStreamParameters captureParameters;
833
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;
841 }
842 else
843 captureParameters.suggestedLatency =
845
846 // Not really doing I/O so pass nullptr for the callback function
847 error = Pa_OpenStream(&stream,
848 &captureParameters, &playbackParameters,
849 highestSampleRate, paFramesPerBufferUnspecified,
850 paClipOff | paDitherOff,
851 nullptr, NULL);
852
853 if (error) {
854 error = Pa_OpenStream(&stream,
855 &captureParameters, NULL,
856 highestSampleRate, paFramesPerBufferUnspecified,
857 paClipOff | paDitherOff,
858 nullptr, NULL);
859 }
860
861 if (error) {
862 s << XO("Received %d while opening devices\n").Format( error );
863 return o.GetString();
864 }
865
866 PxMixer *PortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
867
868 if (!PortMixer) {
869 s << XO("Unable to open Portmixer\n");
870 Pa_CloseStream(stream);
871 return o.GetString();
872 }
873
874 s << wxT("==============================\n");
875 s << XO("Available mixers:\n");
876
877 // FIXME: ? PortMixer errors on query not reported in GetDeviceInfo
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 );
882 }
883
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 );
890 }
891
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 );
898 }
899
900 // Check, if PortMixer supports adjusting input levels on the interface
901
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);
912
913 Pa_CloseStream(stream);
914
915 s << wxT("==============================\n");
916 s << ( EmulateMixerInputVol
917 ? XO("Recording volume is emulated\n")
918 : XO("Recording volume is native\n") );
919
920 Px_CloseMixer(PortMixer);
921
922 } //end of massive if statement if a valid sample rate has been found
923#endif
924 return o.GetString();
925}
926
927auto AudioIOBase::GetAllDeviceInfo() -> std::vector<AudioIODiagnostics>
928{
929 std::vector<AudioIODiagnostics> result;
930 result.push_back({
931 wxT("audiodev.txt"), GetDeviceInfo(), wxT("Audio Device Info") });
932 for( auto &pExt : mAudioIOExt )
933 if ( pExt )
934 result.emplace_back(pExt->Dump());
935 return result;
936}
937
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 };
wxT("CloseDown"))
DoubleSetting AudioIOLatencyCorrection
StringSetting AudioIORecordingSource
DoubleSetting AudioIOPlaybackVolume
StringSetting AudioIOPlaybackSource
StringSetting AudioIOPlaybackDevice
DoubleSetting AudioIOLatencyDuration
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
IntSetting AudioIORecordingSourceIndex
IntSetting AudioIORecordChannels
void PaStream
Definition: AudioIOBase.h:23
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
const auto project
A singleton object supporting queries of the state of any active audio streams, and audio device capa...
Definition: AudioIOBase.h:102
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:260
PaStream * mPortStreamV19
Definition: AudioIOBase.h:257
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:83
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:248
static std::vector< long > mCachedSampleRates
Definition: AudioIOBase.h:281
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:255
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
Definition: AudioIOBase.h:279
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.
Definition: AudioIOBase.h:189
virtual ~AudioIOBase()
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:241
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
Definition: AudioIOBase.h:322
static double mCachedBestRateIn
Definition: AudioIOBase.h:282
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()
Definition: AudioIOBase.cpp:94
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)
Definition: AudioIOBase.cpp:76
int mStreamToken
Definition: AudioIOBase.h:251
static std::vector< long > mCachedCaptureRates
Definition: AudioIOBase.h:280
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
Definition: AudioIOBase.h:245
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?
Definition: AudioIOBase.h:274
void SetPlaybackMeter(const std::shared_ptr< AudacityProject > &project, const std::weak_ptr< Meter > &meter)
static const int NumRatesToTry
How many sample rates to try.
Definition: AudioIOBase.h:317
bool IsPaused() const
Find out if playback / recording is currently paused.
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:259
static const int NumStandardRates
How many standard sample rates there are.
Definition: AudioIOBase.h:191
static std::vector< long > mCachedPlaybackRates
Definition: AudioIOBase.h:278
static int mCachedPlaybackIndex
Definition: AudioIOBase.h:277
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.
Definition: AudioIOBase.h:315
virtual ~AudioIOExtBase()
Specialization of Setting for double.
Definition: Prefs.h:363
Specialization of Setting for int.
Definition: Prefs.h:356
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:213
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
const T & GetDefault() const
Definition: Prefs.h:199
Specialization of Setting for strings.
Definition: Prefs.h:370