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
30std::map<int, std::vector<long>> AudioIOBase::mCachedPlaybackRates;
31std::map<int, std::vector<long>> AudioIOBase::mCachedCaptureRates;
32std::map<std::pair<int, int>, std::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 (mCurrentPlaybackIndex == playDeviceNum &&
127 mCurrentCaptureIndex == recDeviceNum)
128 return;
129
130 // Update playback/capture device indices
131 mCurrentPlaybackIndex = playDeviceNum;
132 mCurrentCaptureIndex = 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 // Looking for highest supported sample rate for a given
152 // play/rec device pair
153 long highestSampleRate = GetClosestSupportedSampleRate(playDeviceNum, recDeviceNum, INT_MAX);
154 if (highestSampleRate == 0)
155 {
156 // we don't actually have any rates that work for Rec and Play. Guess one
157 // to use for messing with the mixer, which doesn't actually do either
158 highestSampleRate = 44100;
159 }
160
161 mInputMixerWorks = false;
162
163 int error;
164 // This tries to open the device with the samplerate worked out above, which
165 // will be the highest available for play and record on the device, or
166 // 44.1kHz if the info cannot be fetched.
167
168 PaStream *stream;
169
170 PaStreamParameters playbackParameters;
171
172 playbackParameters.device = playDeviceNum;
173 playbackParameters.sampleFormat = paFloat32;
174 playbackParameters.hostApiSpecificStreamInfo = NULL;
175 playbackParameters.channelCount = 1;
176 if (Pa_GetDeviceInfo(playDeviceNum))
177 playbackParameters.suggestedLatency =
178 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
179 else
180 playbackParameters.suggestedLatency =
182
183 PaStreamParameters captureParameters;
184
185 captureParameters.device = recDeviceNum;
186 captureParameters.sampleFormat = paFloat32;;
187 captureParameters.hostApiSpecificStreamInfo = NULL;
188 captureParameters.channelCount = 1;
189 if (Pa_GetDeviceInfo(recDeviceNum))
190 captureParameters.suggestedLatency =
191 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
192 else
193 captureParameters.suggestedLatency =
195
196 // try opening for record and playback
197 // Not really doing I/O so pass nullptr for the callback function
198 error = Pa_OpenStream(&stream,
199 &captureParameters, &playbackParameters,
200 highestSampleRate, paFramesPerBufferUnspecified,
201 paClipOff | paDitherOff,
202 nullptr, NULL);
203
204 if (!error) {
205 // Try portmixer for this stream
206 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
207 if (!mPortMixer) {
208 Pa_CloseStream(stream);
209 error = true;
210 }
211 }
212
213 // if that failed, try just for record
214 if( error ) {
215 error = Pa_OpenStream(&stream,
216 &captureParameters, NULL,
217 highestSampleRate, paFramesPerBufferUnspecified,
218 paClipOff | paDitherOff,
219 nullptr, NULL);
220
221 if (!error) {
222 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
223 if (!mPortMixer) {
224 Pa_CloseStream(stream);
225 error = true;
226 }
227 }
228 }
229
230 // finally, try just for playback
231 if ( error ) {
232 error = Pa_OpenStream(&stream,
233 NULL, &playbackParameters,
234 highestSampleRate, paFramesPerBufferUnspecified,
235 paClipOff | paDitherOff,
236 nullptr, NULL);
237
238 if (!error) {
239 mPortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
240 if (!mPortMixer) {
241 Pa_CloseStream(stream);
242 error = true;
243 }
244 }
245 }
246
247 // FIXME: TRAP_ERR errors in HandleDeviceChange not reported.
248 // if it's still not working, give up
249 if( error )
250 return;
251
252 // Set input source
253#if USE_PORTMIXER
254 auto sourceIndex = AudioIORecordingSourceIndex.Read(); // defaults to -1
255 if (sourceIndex >= 0) {
256 //the current index of our source may be different because the stream
257 //is a combination of two devices, so update it.
258 sourceIndex = getRecordSourceIndex(mPortMixer);
259 if (sourceIndex >= 0)
260 SetMixer(sourceIndex);
261 }
262#endif
263
264 // Determine mixer capabilities - if it doesn't support control of output
265 // signal level, we emulate it (by multiplying this value by all outgoing
266 // samples)
267
268 float inputVol = Px_GetInputVolume(mPortMixer);
269 mInputMixerWorks = true; // assume it works unless proved wrong
270 Px_SetInputVolume(mPortMixer, 0.0);
271 if (Px_GetInputVolume(mPortMixer) > 0.1)
272 mInputMixerWorks = false; // can't set to zero
273 Px_SetInputVolume(mPortMixer, 0.2f);
274 if (Px_GetInputVolume(mPortMixer) < 0.1 ||
275 Px_GetInputVolume(mPortMixer) > 0.3)
276 mInputMixerWorks = false; // can't set level accurately
277 Px_SetInputVolume(mPortMixer, inputVol);
278
279 Pa_CloseStream(stream);
280
281
282 #if 0
283 wxPrintf("PortMixer: Recording: %s\n"
284 mInputMixerWorks? "hardware": "no control");
285 #endif
286#endif // USE_PORTMIXER
287}
288
290 const std::shared_ptr<AudacityProject> &project, const std::weak_ptr<Meter> &wMeter)
291{
292 if (auto pOwningProject = mOwningProject.lock();
293 ( pOwningProject ) && ( pOwningProject != project))
294 return;
295
296 auto meter = wMeter.lock();
297 if (meter)
298 {
299 mInputMeter = meter;
300 meter->Reset(mRate, true);
301 }
302 else
303 mInputMeter.reset();
304}
305
307 const std::shared_ptr<AudacityProject> &project, const std::weak_ptr<Meter> &wMeter)
308{
309 if (auto pOwningProject = mOwningProject.lock();
310 ( pOwningProject ) && ( pOwningProject != project))
311 return;
312
313 auto meter = wMeter.lock();
314 if (meter)
315 {
316 mOutputMeter = meter;
317 meter->Reset(mRate, true);
318 }
319 else
320 mOutputMeter.reset();
321}
322
324{
325 return mPaused.load(std::memory_order_relaxed);
326}
327
329{
330 if (mStreamToken != 0)
331 return true;
332
333 return false;
334}
335
337{
338 bool isActive = false;
339 // JKC: Not reporting any Pa error, but that looks OK.
340 if( mPortStreamV19 )
341 isActive = (Pa_IsStreamActive( mPortStreamV19 ) > 0);
342
343 isActive = isActive ||
344 std::any_of(mAudioIOExt.begin(), mAudioIOExt.end(),
345 [](auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
346 return isActive;
347}
348
349bool AudioIOBase::IsStreamActive(int token) const
350{
351 return (this->IsStreamActive() && this->IsAudioTokenActive(token));
352}
353
355{
356 return ( token > 0 && token == mStreamToken );
357}
358
360{
361 return ( mPortStreamV19 && mStreamToken==0 );
362}
363
364bool AudioIOBase::IsPlaybackRateSupported(int devIndex, long rate)
365{
366 if (devIndex == -1)
367 { // weren't given a device index, get the prefs / default one
368 devIndex = getPlayDevIndex();
369 }
370
371 // Check if we can use the cached rate
372 if (mCachedPlaybackRates.count(devIndex) &&
373 (make_iterator_range(mCachedPlaybackRates.at(devIndex)).contains(rate)))
374 {
375 return true;
376 }
377
378 auto devInfo = Pa_GetDeviceInfo(devIndex);
379
380 if (!devInfo)
381 {
382 wxLogDebug(wxT("IsPlaybackRateSupported() Could not get device info!"));
383 return false;
384 }
385
386 // LLL: Remove when a proper method of determining actual supported
387 // DirectSound rate is devised.
388 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
389 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
390
391 PaStreamParameters pars;
392
393 pars.device = devIndex;
394 pars.channelCount = 1;
395 pars.sampleFormat = paFloat32;
396 pars.suggestedLatency = devInfo->defaultHighOutputLatency;
397 pars.hostApiSpecificStreamInfo = NULL;
398
399 // JKC: PortAudio Errors handled OK here. No need to report them
400
401 // LLL: Remove when a proper method of determining actual supported
402 // DirectSound rate is devised.
403 if (!(isDirectSound && rate > 200000)){
404 if (Pa_IsFormatSupported(NULL, &pars, rate) == 0)
405 {
406 mCachedPlaybackRates[devIndex].push_back(rate);
407 return true;
408 }
409 }
410 return false;
411}
412
413bool AudioIOBase::IsCaptureRateSupported(int devIndex, long rate)
414{
415 if (devIndex == -1)
416 { // not given a device, look up in prefs / default
417 devIndex = getRecordDevIndex();
418 }
419
420 // Check if we can use the cached rate
421 if (mCachedCaptureRates.count(devIndex) &&
422 (make_iterator_range(mCachedCaptureRates.at(devIndex)).contains(rate)))
423 {
424 return true;
425 }
426
427 auto devInfo = Pa_GetDeviceInfo(devIndex);
428
429 if (!devInfo)
430 {
431 wxLogDebug(wxT("IsCaptureRateSupported() Could not get device info!"));
432 return false;
433 }
434
435 auto latencyDuration = AudioIOLatencyDuration.Read();
436 // Why not defaulting to 2 as elsewhere?
437 auto recordChannels = AudioIORecordChannels.ReadWithDefault(1);
438
439 // LLL: Remove when a proper method of determining actual supported
440 // DirectSound rate is devised.
441 const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo(devInfo->hostApi);
442 bool isDirectSound = (hostInfo && hostInfo->type == paDirectSound);
443
444 PaStreamParameters pars;
445
446 pars.device = devIndex;
447 pars.channelCount = recordChannels;
448 pars.sampleFormat = paFloat32;
449 pars.suggestedLatency = latencyDuration / 1000.0;
450 pars.hostApiSpecificStreamInfo = NULL;
451
452 // LLL: Remove when a proper method of determining actual supported
453 // DirectSound rate is devised.
454 if (!(isDirectSound && rate > 200000))
455 {
456 if (Pa_IsFormatSupported(&pars, NULL, rate) == 0)
457 {
458 mCachedCaptureRates[devIndex].push_back(rate);
459 return true;
460 }
461 }
462 return false;
463}
464
465std::vector<long> AudioIOBase::GetSupportedPlaybackRates(int devIndex)
466{
467 if (devIndex == -1)
468 { // weren't given a device index, get the prefs / default one
469 devIndex = getPlayDevIndex();
470 }
471
472 std::vector<long> supportedRates;
473
474 for(const long rate : RatesToTry) {
475 if (IsPlaybackRateSupported(devIndex, rate)) {
476 supportedRates.push_back(rate);
477 }
478 Pa_Sleep( 10 ); // There are ALSA drivers that don't like being probed
479 // too quickly.
480 }
481
482 return supportedRates;
483}
484
485std::vector<long> AudioIOBase::GetSupportedCaptureRates(int devIndex)
486{
487 if (devIndex == -1)
488 { // weren't given a device index, get the prefs / default one
489 devIndex = getRecordDevIndex();
490 }
491
492 std::vector<long> supportedRates;
493
494 for(const long rate : RatesToTry) {
495 if (IsCaptureRateSupported(devIndex, rate)) {
496 supportedRates.push_back(rate);
497 }
498 Pa_Sleep( 10 ); // There are ALSA drivers that don't like being probed
499 // too quickly.
500 }
501
502 return supportedRates;
503}
504
506{
507 long supportedRate = 0;
508 if (devIndex == -1)
509 { // weren't given a device index, get the prefs / default one
510 devIndex = getPlayDevIndex();
511 }
512
513 if (rate == 0.0)
514 { // not given a correct rate
515 return 0;
516 }
517
518 // First we will probe the requested state
519 std::vector<long> rates = { rate };
520 // Next default rates higher than requested
521 auto higherRatesIt = std::upper_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
522 std::copy(higherRatesIt, RatesToTry + NumRatesToTry, std::back_inserter(rates));
523 // Last default rates lower than requested in reverse order
524 auto lowerRatesIt = std::lower_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
525 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(RatesToTry),
526 std::back_inserter(rates));
527
528 for (const long rateToTry : rates)
529 {
530 if (IsPlaybackRateSupported(devIndex, rateToTry))
531 {
532 supportedRate = rateToTry;
533 break;
534 }
535 Pa_Sleep( 10 ); // There are ALSA drivers that don't like being probed
536 // too quickly.
537 }
538
539 return supportedRate;
540}
541
543{
544 long supportedRate = 0;
545 if (devIndex == -1)
546 { // not given a device, look up in prefs / default
547 devIndex = getRecordDevIndex();
548 }
549
550 if (rate == 0)
551 { // not given a correct rate
552 return supportedRate;
553 }
554
555 // Check if we can use the cached rate
556 if (mCachedCaptureRates.count(devIndex)
557 && (make_iterator_range(mCachedCaptureRates[devIndex]).contains(rate)))
558 {
559 supportedRate = rate;
560 return supportedRate;
561 }
562
563
564 // First we will probe the requested state
565 std::vector<long> rates = { rate };
566
567 // Next default rates higher than requested
568 auto higherRatesIt = std::upper_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
569 std::copy(higherRatesIt, RatesToTry + NumRatesToTry, std::back_inserter(rates));
570
571 // Last default rates lower than requested in reverse order
572 auto lowerRatesIt = std::lower_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
573 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(RatesToTry),
574 std::back_inserter(rates));
575
576 for (const long rateToTry : rates)
577 {
578 if (IsCaptureRateSupported(devIndex, rateToTry))
579 {
580 supportedRate = rateToTry;
581 break;
582 }
583 Pa_Sleep( 10 ); // There are ALSA drivers that don't like being probed
584 // too quickly.
585 }
586
587 return supportedRate;
588}
589
591 int playDevice, int recDevice, long rate)
592{
593 long supportedRate = 0;
594
595 // Not given device indices, look up prefs
596 if (playDevice == -1) {
597 playDevice = getPlayDevIndex();
598 }
599 if (recDevice == -1) {
600 recDevice = getRecordDevIndex();
601 }
602
603 // Check if we can use the cached rates
604 std::pair<int, int> devicePair { playDevice, recDevice };
605 if (mCachedSampleRates.count(devicePair) &&
606 make_iterator_range(mCachedSampleRates.at(devicePair)).contains(rate))
607 {
608 return rate;
609 }
610
611 // First we will probe the requested state
612 std::vector<long> rates { rate };
613
614 // Next default rates higher than requested
615 auto higherRatesIt = std::upper_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
616 std::copy(higherRatesIt, RatesToTry + NumRatesToTry, std::back_inserter(rates));
617
618 // Last default rates lower than requested in reverse order
619 auto lowerRatesIt = std::lower_bound(RatesToTry, RatesToTry + NumRatesToTry, rate);
620 std::copy(std::make_reverse_iterator(lowerRatesIt), std::make_reverse_iterator(RatesToTry),
621 std::back_inserter(rates));
622
623 for (const long rateToTry : rates)
624 {
625 if (IsPlaybackRateSupported(playDevice, rateToTry) &&
626 IsCaptureRateSupported(recDevice, rateToTry))
627 {
628 supportedRate = rateToTry;
629 break;
630 }
631 Pa_Sleep( 10 ); // There are ALSA drivers that don't like being probed
632 // too quickly.
633 }
634
635 mCachedSampleRates[devicePair].push_back(supportedRate);
636
637 return supportedRate;
638}
639
640std::vector<long> AudioIOBase::GetSupportedSampleRates(int playDevice, int recDevice)
641{
642 // Not given device indices, look up prefs
643 if (playDevice == -1)
644 {
645 playDevice = getPlayDevIndex();
646 }
647 if (recDevice == -1)
648 {
649 recDevice = getRecordDevIndex();
650 }
651
652 auto playback = GetSupportedPlaybackRates(playDevice);
653 auto capture = GetSupportedCaptureRates(recDevice);
654
655 // Return only sample rates which are in both arrays
656 std::vector<long> result;
657
658 std::set_intersection(playback.begin(), playback.end(),
659 capture.begin(), capture.end(),
660 std::back_inserter(result));
661
662 return result;
663}
664
670{
671 auto rate = GetClosestSupportedSampleRate(-1, -1, 44100);
672
673 // if there are no supported rates, the next bit crashes. So check first,
674 // and give them a "sensible" value if there are no valid values. They
675 // will still get an error later, but with any luck may have changed
676 // something by then. It's no worse than having an invalid default rate
677 // stored in the preferences, which we don't check for
678 if (rate == 0)
679 {
680 return 44100;
681 }
682
683 return rate;
684}
685
686#if USE_PORTMIXER
687int AudioIOBase::getRecordSourceIndex(PxMixer *portMixer)
688{
689 int i;
690 auto sourceName = AudioIORecordingSource.Read();
691 int numSources = Px_GetNumInputSources(portMixer);
692 for (i = 0; i < numSources; i++) {
693 if (sourceName == wxString(wxSafeConvertMB2WX(Px_GetInputSourceName(portMixer, i))))
694 return i;
695 }
696 return -1;
697}
698#endif
699
700int AudioIOBase::getPlayDevIndex(const wxString &devNameArg)
701{
702 wxString devName(devNameArg);
703 // if we don't get given a device, look up the preferences
704 if (devName.empty())
705 devName = AudioIOPlaybackDevice.Read();
706
707 auto hostName = AudioIOHost.Read();
708 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
709 PaHostApiIndex hostNum;
710 for (hostNum = 0; hostNum < hostCnt; hostNum++)
711 {
712 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
713 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
714 {
715 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
716 {
717 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
718
719 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
720 if (dinfo && DeviceName(dinfo) == devName && dinfo->maxOutputChannels > 0 )
721 {
722 // this device name matches the stored one, and works.
723 // So we say this is the answer and return it
724 return deviceNum;
725 }
726 }
727
728 // The device wasn't found so use the default for this host.
729 // LL: At this point, preferences and active no longer match.
730 return hinfo->defaultOutputDevice;
731 }
732 }
733
734 // The host wasn't found, so use the default output device.
735 // FIXME: TRAP_ERR PaErrorCode not handled well (this code is similar to input code
736 // and the input side has more comments.)
737
738 PaDeviceIndex deviceNum = Pa_GetDefaultOutputDevice();
739
740 // Sometimes PortAudio returns -1 if it cannot find a suitable default
741 // device, so we just use the first one available
742 //
743 // LL: At this point, preferences and active no longer match
744 //
745 // And I can't imagine how far we'll get specifying an "invalid" index later
746 // on...are we certain "0" even exists?
747 if (deviceNum < 0) {
748 assert(false);
749 deviceNum = 0;
750 }
751
752 return deviceNum;
753}
754
755int AudioIOBase::getRecordDevIndex(const wxString &devNameArg)
756{
757 wxString devName(devNameArg);
758 // if we don't get given a device, look up the preferences
759 if (devName.empty())
760 devName = AudioIORecordingDevice.Read();
761
762 auto hostName = AudioIOHost.Read();
763 PaHostApiIndex hostCnt = Pa_GetHostApiCount();
764 PaHostApiIndex hostNum;
765 for (hostNum = 0; hostNum < hostCnt; hostNum++)
766 {
767 const PaHostApiInfo *hinfo = Pa_GetHostApiInfo(hostNum);
768 if (hinfo && wxString(wxSafeConvertMB2WX(hinfo->name)) == hostName)
769 {
770 for (PaDeviceIndex hostDevice = 0; hostDevice < hinfo->deviceCount; hostDevice++)
771 {
772 PaDeviceIndex deviceNum = Pa_HostApiDeviceIndexToDeviceIndex(hostNum, hostDevice);
773
774 const PaDeviceInfo *dinfo = Pa_GetDeviceInfo(deviceNum);
775 if (dinfo && DeviceName(dinfo) == devName && dinfo->maxInputChannels > 0 )
776 {
777 // this device name matches the stored one, and works.
778 // So we say this is the answer and return it
779 return deviceNum;
780 }
781 }
782
783 // The device wasn't found so use the default for this host.
784 // LL: At this point, preferences and active no longer match.
785 return hinfo->defaultInputDevice;
786 }
787 }
788
789 // The host wasn't found, so use the default input device.
790 // FIXME: TRAP_ERR PaErrorCode not handled well in getRecordDevIndex()
791 PaDeviceIndex deviceNum = Pa_GetDefaultInputDevice();
792
793 // Sometimes PortAudio returns -1 if it cannot find a suitable default
794 // device, so we just use the first one available
795 // PortAudio has an error reporting function. We should log/report the error?
796 //
797 // LL: At this point, preferences and active no longer match
798 //
799 // And I can't imagine how far we'll get specifying an "invalid" index later
800 // on...are we certain "0" even exists?
801 if (deviceNum < 0) {
802 // JKC: This will happen if you run with no config file
803 // This happens once. Config file will exist on the next run.
804 // TODO: Look into this a bit more. Could be relevant to blank Device Toolbar.
805 wxLogDebug("PortAudio returns -1, cannot find a suitable default device, so we just use the first one available");
806 deviceNum = 0;
807 }
808
809 return deviceNum;
810}
811
813{
814 wxStringOutputStream o;
815 wxTextOutputStream s(o, wxEOL_UNIX);
816
817 if (IsStreamActive()) {
818 return XO("Stream is active ... unable to gather information.\n")
819 .Translation();
820 }
821
822
823 // FIXME: TRAP_ERR PaErrorCode not handled. 3 instances in GetDeviceInfo().
824 int recDeviceNum = Pa_GetDefaultInputDevice();
825 int playDeviceNum = Pa_GetDefaultOutputDevice();
826 int cnt = Pa_GetDeviceCount();
827
828 // PRL: why only into the log?
829 wxLogDebug(wxT("Portaudio reports %d audio devices"),cnt);
830
831 s << wxT("==============================\n");
832 s << XO("Default recording device number: %d\n").Format( recDeviceNum );
833 s << XO("Default playback device number: %d\n").Format( playDeviceNum);
834
835 auto recDevice = AudioIORecordingDevice.Read();
836 auto playDevice = AudioIOPlaybackDevice.Read();
837 int j;
838
839 // This gets info on all available audio devices (input and output)
840 if (cnt <= 0) {
841 s << XO("No devices found\n");
842 return o.GetString();
843 }
844
845 const PaDeviceInfo* info;
846
847 for (j = 0; j < cnt; j++) {
848 s << wxT("==============================\n");
849
850 info = Pa_GetDeviceInfo(j);
851 if (!info) {
852 s << XO("Device info unavailable for: %d\n").Format( j );
853 continue;
854 }
855
856 wxString name = DeviceName(info);
857 s << XO("Device ID: %d\n").Format( j );
858 s << XO("Device name: %s\n").Format( name );
859 s << XO("Host name: %s\n").Format( HostName(info) );
860 s << XO("Recording channels: %d\n").Format( info->maxInputChannels );
861 s << XO("Playback channels: %d\n").Format( info->maxOutputChannels );
862 s << XO("Low Recording Latency: %g\n").Format( info->defaultLowInputLatency );
863 s << XO("Low Playback Latency: %g\n").Format( info->defaultLowOutputLatency );
864 s << XO("High Recording Latency: %g\n").Format( info->defaultHighInputLatency );
865 s << XO("High Playback Latency: %g\n").Format( info->defaultHighOutputLatency );
866
867 if (info->maxOutputChannels)
868 {
869 auto rates = GetSupportedPlaybackRates(j);
870
871 /* i18n-hint: Supported, meaning made available by the system */
872 s << XO("Supported Playback Rates:\n");
873 for (int k = 0; k < (int) rates.size(); k++) {
874 s << wxT(" ") << (int) rates[k] << wxT("\n");
875 }
876 }
877
878 if (info->maxInputChannels)
879 {
880 auto rates = GetSupportedCaptureRates(j);
881
882 /* i18n-hint: Supported, meaning made available by the system */
883 s << XO("Supported Capture Rates:\n");
884 for (int k = 0; k < (int) rates.size(); k++) {
885 s << wxT(" ") << (int) rates[k] << wxT("\n");
886 }
887 }
888
889 if (name == playDevice && info->maxOutputChannels > 0)
890 playDeviceNum = j;
891
892 if (name == recDevice && info->maxInputChannels > 0)
893 recDeviceNum = j;
894
895 // Sometimes PortAudio returns -1 if it cannot find a suitable default
896 // device, so we just use the first one available
897 if (recDeviceNum < 0 && info->maxInputChannels > 0){
898 recDeviceNum = j;
899 }
900 if (playDeviceNum < 0 && info->maxOutputChannels > 0){
901 playDeviceNum = j;
902 }
903 }
904
905 bool haveRecDevice = (recDeviceNum >= 0);
906 bool havePlayDevice = (playDeviceNum >= 0);
907
908 s << wxT("==============================\n");
909 if (haveRecDevice)
910 s << XO("Selected recording device: %d - %s\n").Format( recDeviceNum, recDevice );
911 else
912 s << XO("No recording device found for '%s'.\n").Format( recDevice );
913
914 if (havePlayDevice)
915 s << XO("Selected playback device: %d - %s\n").Format( playDeviceNum, playDevice );
916 else
917 s << XO("No playback device found for '%s'.\n").Format( playDevice );
918
919 std::vector<long> supportedSampleRates;
920
921 if (havePlayDevice && haveRecDevice) {
922 supportedSampleRates = GetSupportedSampleRates(playDeviceNum, recDeviceNum);
923
924 s << XO("Supported Rates:\n");
925 for (int k = 0; k < (int) supportedSampleRates.size(); k++) {
926 s << wxT(" ") << (int)supportedSampleRates[k] << wxT("\n");
927 }
928 }
929 else {
930 s << XO("Cannot check mutual sample rates without both devices.\n");
931 return o.GetString();
932 }
933
934#if defined(USE_PORTMIXER)
935 if (supportedSampleRates.size() > 0)
936 {
937 int highestSampleRate = supportedSampleRates.back();
938 bool EmulateMixerInputVol = true;
939 float MixerInputVol = 1.0;
940 float MixerOutputVol = 1.0;
941
942 int error;
943
944 PaStream *stream;
945
946 PaStreamParameters playbackParameters;
947
948 playbackParameters.device = playDeviceNum;
949 playbackParameters.sampleFormat = paFloat32;
950 playbackParameters.hostApiSpecificStreamInfo = NULL;
951 playbackParameters.channelCount = 1;
952 if (Pa_GetDeviceInfo(playDeviceNum)){
953 playbackParameters.suggestedLatency =
954 Pa_GetDeviceInfo(playDeviceNum)->defaultLowOutputLatency;
955 }
956 else
957 playbackParameters.suggestedLatency =
959
960 PaStreamParameters captureParameters;
961
962 captureParameters.device = recDeviceNum;
963 captureParameters.sampleFormat = paFloat32;;
964 captureParameters.hostApiSpecificStreamInfo = NULL;
965 captureParameters.channelCount = 1;
966 if (Pa_GetDeviceInfo(recDeviceNum)){
967 captureParameters.suggestedLatency =
968 Pa_GetDeviceInfo(recDeviceNum)->defaultLowInputLatency;
969 }
970 else
971 captureParameters.suggestedLatency =
973
974 // Not really doing I/O so pass nullptr for the callback function
975 error = Pa_OpenStream(&stream,
976 &captureParameters, &playbackParameters,
977 highestSampleRate, paFramesPerBufferUnspecified,
978 paClipOff | paDitherOff,
979 nullptr, NULL);
980
981 if (error) {
982 error = Pa_OpenStream(&stream,
983 &captureParameters, NULL,
984 highestSampleRate, paFramesPerBufferUnspecified,
985 paClipOff | paDitherOff,
986 nullptr, NULL);
987 }
988
989 if (error) {
990 s << XO("Received %d while opening devices\n").Format( error );
991 return o.GetString();
992 }
993
994 PxMixer *PortMixer = Px_OpenMixer(stream, recDeviceNum, playDeviceNum, 0);
995
996 if (!PortMixer) {
997 s << XO("Unable to open Portmixer\n");
998 Pa_CloseStream(stream);
999 return o.GetString();
1000 }
1001
1002 s << wxT("==============================\n");
1003 s << XO("Available mixers:\n");
1004
1005 // FIXME: ? PortMixer errors on query not reported in GetDeviceInfo
1006 cnt = Px_GetNumMixers(stream);
1007 for (int i = 0; i < cnt; i++) {
1008 wxString name = wxSafeConvertMB2WX(Px_GetMixerName(stream, i));
1009 s << XO("%d - %s\n").Format( i, name );
1010 }
1011
1012 s << wxT("==============================\n");
1013 s << XO("Available recording sources:\n");
1014 cnt = Px_GetNumInputSources(PortMixer);
1015 for (int i = 0; i < cnt; i++) {
1016 wxString name = wxSafeConvertMB2WX(Px_GetInputSourceName(PortMixer, i));
1017 s << XO("%d - %s\n").Format( i, name );
1018 }
1019
1020 s << wxT("==============================\n");
1021 s << XO("Available playback volumes:\n");
1022 cnt = Px_GetNumOutputVolumes(PortMixer);
1023 for (int i = 0; i < cnt; i++) {
1024 wxString name = wxSafeConvertMB2WX(Px_GetOutputVolumeName(PortMixer, i));
1025 s << XO("%d - %s\n").Format( i, name );
1026 }
1027
1028 // Check, if PortMixer supports adjusting input levels on the interface
1029
1030 MixerInputVol = Px_GetInputVolume(PortMixer);
1031 EmulateMixerInputVol = false;
1032 Px_SetInputVolume(PortMixer, 0.0);
1033 if (Px_GetInputVolume(PortMixer) > 0.1)
1034 EmulateMixerInputVol = true;
1035 Px_SetInputVolume(PortMixer, 0.2f);
1036 if (Px_GetInputVolume(PortMixer) < 0.1 ||
1037 Px_GetInputVolume(PortMixer) > 0.3)
1038 EmulateMixerInputVol = true;
1039 Px_SetInputVolume(PortMixer, MixerInputVol);
1040
1041 Pa_CloseStream(stream);
1042
1043 s << wxT("==============================\n");
1044 s << ( EmulateMixerInputVol
1045 ? XO("Recording volume is emulated\n")
1046 : XO("Recording volume is native\n") );
1047
1048 Px_CloseMixer(PortMixer);
1049
1050 } //end of massive if statement if a valid sample rate has been found
1051#endif
1052 return o.GetString();
1053}
1054
1055auto AudioIOBase::GetAllDeviceInfo() -> std::vector<AudioIODiagnostics>
1056{
1057 std::vector<AudioIODiagnostics> result;
1058 result.push_back({
1059 wxT("audiodev.txt"), GetDeviceInfo(), wxT("Audio Device Info") });
1060 for( auto &pExt : mAudioIOExt )
1061 if ( pExt )
1062 result.emplace_back(pExt->Dump());
1063 return result;
1064}
1065
1067 L"/AudioIO/Host", L"" };
1069 L"/AudioIO/LatencyCorrection", -130.0 };
1071 L"/AudioIO/LatencyDuration", 100.0 };
1073 L"/AudioIO/PlaybackDevice", L"" };
1075 L"/AudioIO/PlaybackSource", L"" };
1077 L"/AudioIO/PlaybackVolume", 1.0 };
1079 L"/AudioIO/RecordChannels", 2 };
1081 L"/AudioIO/RecordingDevice", L"" };
1083 L"/AudioIO/RecordingSource", L"" };
1085 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:25
XO("Cut/Copy/Paste")
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
wxString name
Definition: TagsEditor.cpp:166
const auto project
A singleton object supporting queries of the state of any active audio streams, and audio device capa...
Definition: AudioIOBase.h:104
std::weak_ptr< Meter > mOutputMeter
Definition: AudioIOBase.h:319
PaStream * mPortStreamV19
Definition: AudioIOBase.h:316
static wxString HostName(const PaDeviceInfo *info)
Definition: AudioIOBase.cpp:83
static std::map< std::pair< int, int >, std::vector< long > > mCachedSampleRates
Definition: AudioIOBase.h:338
std::atomic< bool > mPaused
True if audio playback is paused.
Definition: AudioIOBase.h:307
static bool IsCaptureRateSupported(int devIndex, long rate)
Check if the specified sample rate is supported by a device.
static long GetClosestSupportedPlaybackRate(int devIndex, long rate)
Find the closest supported sample rate for given playback device.
double mRate
Audio playback rate in samples per second.
Definition: AudioIOBase.h:314
bool IsMonitoring() const
Returns true if we're monitoring input (but not recording or playing actual audio)
wxString GetDeviceInfo() const
Get diagnostic information on all the available audio I/O devices.
static bool IsPlaybackRateSupported(int devIndex, long rate)
Check if the specified playback rate is supported by a device.
static const int StandardRates[]
Array of common audio sample rates.
Definition: AudioIOBase.h:248
static int mCurrentPlaybackIndex
Definition: AudioIOBase.h:339
virtual ~AudioIOBase()
static std::unique_ptr< AudioIOBase > ugAudioIO
Definition: AudioIOBase.h:300
std::vector< std::unique_ptr< AudioIOExtBase > > mAudioIOExt
Definition: AudioIOBase.h:381
static double mCachedBestRateIn
Definition: AudioIOBase.h:341
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
static int mCurrentCaptureIndex
Definition: AudioIOBase.h:340
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
static std::vector< long > GetSupportedSampleRates(int playDevice=-1, int recDevice=-1)
Get a list of sample rates the current input/output device combination supports.
int mStreamToken
Definition: AudioIOBase.h:310
static std::vector< long > GetSupportedPlaybackRates(int DevIndex=-1)
Get a list of sample rates the output (playback) device supports.
static int GetOptimalSupportedSampleRate()
Get a supported sample rate which can be used a an optimal default.
static std::map< int, std::vector< long > > mCachedCaptureRates
Definition: AudioIOBase.h:337
std::weak_ptr< AudacityProject > mOwningProject
Definition: AudioIOBase.h:304
static std::map< int, std::vector< long > > mCachedPlaybackRates
Definition: AudioIOBase.h:336
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...
static long GetClosestSupportedSampleRate(int playDevice, int recDevice, long rate)
Find the closest supported sample rate for given playback and recording devices.
bool mInputMixerWorks
Can we control the hardware input level?
Definition: AudioIOBase.h:333
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:376
bool IsPaused() const
Find out if playback / recording is currently paused.
std::weak_ptr< Meter > mInputMeter
Definition: AudioIOBase.h:318
static long GetClosestSupportedCaptureRate(int devIndex, long rate)
Find the closest supported sample rate for given recording device.
static const int NumStandardRates
How many standard sample rates there are.
Definition: AudioIOBase.h:250
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:374
static std::vector< long > GetSupportedCaptureRates(int devIndex=-1)
Get a list of sample rates the input (recording) device supports.
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
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40