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