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