MythTV master
audiogeneralsettings.cpp
Go to the documentation of this file.
1
2// -*- Mode: c++ -*-
3
4// Standard UNIX C headers
5#include <chrono> // for milliseconds
6#include <fcntl.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <thread> // for sleep_for
10
11// Qt headers
12#include <QtGlobal>
13#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
14#include <QtSystemDetection>
15#endif
16#include <QCoreApplication>
17#include <QDir>
18#include <QEvent>
19#include <utility>
20
21// MythTV headers
24#include "libmythbase/mythconfig.h"
31
32// MythFrontend
34
35extern "C" {
36#include "libavformat/avformat.h"
37}
38
40 HostComboBoxSetting("AudioOutputDevice", true), m_parent(parent)
41{
42 setLabel(tr("Audio output device"));
43#ifdef Q_OS_ANDROID
44 QString dflt = "OpenSLES:";
45#elif CONFIG_AUDIO_ALSA
46 QString dflt = "ALSA:default";
47#elif CONFIG_AUDIO_PULSEOUTPUT
48 QString dflt = "PulseAudio:default";
49#elif defined(Q_OS_DARWIN)
50 QString dflt = "CoreAudio:";
51#elif defined(Q_OS_WINDOWS)
52 QString dflt = "Windows:";
53#else
54 QString dflt = "NULL";
55#endif
56 QString current = gCoreContext->GetSetting(QString("AudioOutputDevice"),
57 dflt);
59
60 connect(this, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
62}
63
65{
68}
69
71{
73
74 if (vect.empty())
75 return;
76
77 QString value = getValue();
79
80 // Adding the current value first avoids marking the setting as changed
81 addSelection(value, value, true);
82 for (const auto & it : std::as_const(vect))
83 {
84 if (value != it.m_name)
85 addSelection(it.m_name, it.m_name);
86 }
87}
88
90{
91 QString desc = m_parent->AudioDeviceMap().value(setting->getValue()).m_desc;
92 setHelpText(desc);
93}
94
96 GroupSetting *groupSetting)
97 : StandardSettingDialog(parent, name, groupSetting)
98{
99}
100
102{
103 SetBusyPopupMessage(tr("Scanning for available audio devices"));
105}
106
108{
110
111 auto *settings = qobject_cast<AudioConfigSettings*>(GetGroupSettings());
112 if (settings == nullptr)
113 return;
114 settings->CheckConfiguration();
115}
116
118 : m_triggerDigital(new GroupSetting()),
119 m_passThroughOverride(PassThroughOverride()),
120 m_passThroughDeviceOverride(PassThroughOutputDevice())
121{
122 setLabel(tr("Audio System"));
123
125 // Rescan button
126
127 auto *rescan = new ButtonStandardSetting("rescan");
128 rescan->setLabel(tr("Rescan"));
129 rescan->setHelpText(tr("Rescan for available audio devices. "
130 "Current entry will be checked and "
131 "capability entries populated."));
132 addChild(rescan);
134
135 // digital settings
136 m_triggerDigital->setLabel(tr("Digital Audio Capabilities"));
143
148
149 //Advanced Settings
150 auto *advancedSettings = new GroupSetting();
151 advancedSettings->setLabel(tr("Advanced Audio Settings"));
152 advancedSettings->setHelpText(tr("Enable extra audio settings. Under most "
153 "usage all options should be left alone"));
154 addChild(advancedSettings);
155 advancedSettings->addChild(m_passThroughOverride);
156 advancedSettings->addChild(m_passThroughDeviceOverride);
160
161 StandardSetting *srcqualityoverride = SRCQualityOverride();
162 srcqualityoverride->addTargetedChild("1", SRCQuality());
163 addChild(srcqualityoverride);
164
165 advancedSettings->addChild(Audio48kOverride());
166#if CONFIG_AUDIO_ALSA
167 advancedSettings->addChild(SPDIFRateOverride());
168#endif
169
170 advancedSettings->addChild(HBRPassthrough());
171
172 advancedSettings->addChild(m_mpcm = MPCM());
173
176
177 // Set slots
178 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
179 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateVisibility));
180 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
181 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
182 connect(m_ac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
183 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilitiesAC3));
184
185 connect(m_dtsPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
186 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
187 connect(m_eac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
188 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
189 connect(m_trueHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
190 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
191 connect(m_dtsHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
192 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
193 //Slot for audio test
194 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
196 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
198}
199
201{
202 QString name = m_outputDevice->getValue();
204 AudioOutput::GetAudioDeviceConfig(name, name, true);
205 if (adc)
206 {
207 if (adc->m_settings.IsInvalid())
208 {
209 QString msg = tr("%1 is invalid or not useable.").arg(name);
210
211 ShowOkPopup(msg);
212
213 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
214 .arg(name));
215 }
216 delete adc;
217 }
218
219 if (!CheckPassthrough())
220 {
221 QString pt_name = m_passThroughDeviceOverride->getValue();
222 QString pt_msg = tr("Passthrough device is invalid or not useable. Check "
223 "configuration in Advanced Settings:") +
224 pt_name;
225
226 ShowOkPopup(pt_msg);
227
228 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
229 .arg(pt_name));
230 }
231}
232
234{
236 AudioRescan();
237 // If this is the initial setup where there was nothing on the DB,
238 // set changed so that user can save.
239 if (gCoreContext->GetSetting(QString("AudioOutputDevice"),"").isEmpty())
240 setChanged(true);
241}
242
244{
245 if (!m_slotLock.tryLock())
246 return;
247
249
250 m_audioDevs.clear();
251 for (const auto & dev : std::as_const(*list))
252 m_audioDevs.insert(dev.m_name, dev);
253
254 m_devices = *list;
255 delete list;
256
257 QString name = m_outputDevice->getValue();
258 if (!m_audioDevs.contains(name))
259 {
260 // Scan for possible custom entry that isn't in the list
262 AudioOutput::GetAudioDeviceConfig(name, name, true);
263 m_audioDevs.insert(name, *adc);
264 m_devices.append(*adc);
265 delete adc;
266 }
268 m_slotLock.unlock();
270}
271
273{
275 return;
276
277 int cur_speakers = m_maxAudioChannels->getValue().toInt();
278 m_audioUpmix->setEnabled(cur_speakers > 2);
279 m_audioUpmixType->setEnabled(cur_speakers > 2);
280}
281
283 bool restore, bool AC3)
284{
285 int max_speakers = 8;
286 int realmax_speakers = 8;
287
288 bool invalid = false;
289 QString out;
290
291 if (m_outputDevice)
292 out = m_outputDevice->getValue();
293
294 if (!out.isEmpty())
295 {
296 restore = out != m_lastAudioDevice;
297 m_lastAudioDevice = out;
298 }
299
300 AudioOutputSettings settings;
301 AudioOutputSettings settingsdigital;
302
303 // Test if everything is set yet
307 return settings;
308
309 if (!m_slotLock.tryLock()) // Doing a rescan of channels
310 return settings;
311
312 bool bAC3 = true;
313 //bool bDTS = true;
314 bool bLPCM = true;
315 bool bEAC3 = true;
316 bool bTRUEHD = true;
317 bool bDTSHD = true;
318
319 if (!m_audioDevs.contains(out))
320 {
321 LOG(VB_AUDIO, LOG_ERR, QString("Update not found (%1)").arg(out));
322 invalid = true;
323 }
324 else
325 {
326 bool bForceDigital = m_passThroughOverride->boolValue();
327
328 settings = m_audioDevs.value(out).m_settings;
329 settingsdigital = bForceDigital ?
331 .m_settings : settings;
332
333 realmax_speakers = max_speakers = settings.BestSupportedChannels();
334
335 bAC3 = settingsdigital.canFeature(FEATURE_AC3) &&
337 //bDTS = settingsdigital.canFeature(FEATURE_DTS) &&
338 // m_dtsPassThrough->boolValue();
339 bLPCM = settings.canFeature(FEATURE_LPCM) &&
340 !gCoreContext->GetBoolSetting("StereoPCM", false);
341 bEAC3 = settingsdigital.canFeature(FEATURE_EAC3) &&
342 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
343 bTRUEHD = settingsdigital.canFeature(FEATURE_TRUEHD) &&
344 !gCoreContext->GetBoolSetting("Audio48kOverride", false) &&
345 gCoreContext->GetBoolSetting("HBRPassthru", true);
346 bDTSHD = settingsdigital.canFeature(FEATURE_DTSHD) &&
347 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
348
349 if (max_speakers > 2 && !bLPCM)
350 max_speakers = 2;
351 if (max_speakers == 2 && bAC3)
352 {
353 max_speakers = 6;
354 if (AC3)
355 {
356 restore = true;
357 }
358 }
359 }
360
361 m_triggerDigital->setEnabled(invalid || settingsdigital.canFeature(
367
368 int cur_speakers = m_maxAudioChannels->getValue().toInt();
369 if (cur_speakers > m_maxSpeakers)
370 {
372 }
373 if (restore)
374 {
375 cur_speakers = m_maxSpeakers;
376 }
377
378 if (cur_speakers > max_speakers)
379 {
380 LOG(VB_AUDIO, LOG_INFO, QString("Reset device %1").arg(out));
381 cur_speakers = max_speakers;
382 }
383
384 // Remove everything and re-add available channels
385 bool chansChanged = m_maxAudioChannels->haveChanged();
387 for (int i = 1; i <= max_speakers; i++)
388 {
389 if (invalid || settings.IsSupportedChannels(i) ||
390 settingsdigital.IsSupportedChannels(i))
391 {
392 QString txt;
393
394 switch (i)
395 {
396 case 2:
397 txt = QCoreApplication::translate("(Common)", "Stereo");
398 break;
399 case 6:
400 txt = QCoreApplication::translate("(Common)", "5.1");
401 break;
402 case 8:
403 txt = QCoreApplication::translate("(Common)", "7.1");
404 break;
405 default:
406 continue;
407 }
408 m_maxAudioChannels->addSelection(txt, QString::number(i),
409 i == cur_speakers);
410 }
411 }
412 m_maxAudioChannels->setChanged(chansChanged);
413
414 setMPCMEnabled(settings.canPassthrough() >= 0);
415
416 // Return values is used by audio test
417 // where we mainly are interested by the number of channels
418 // if we support AC3 and/or LPCM
419 settings.SetBestSupportedChannels(cur_speakers);
420 settings.setFeature(bAC3, FEATURE_AC3);
421 settings.setFeature(bLPCM && realmax_speakers > 2, FEATURE_LPCM);
422
423 m_slotLock.unlock();
424 return settings;
425}
426
428{
430}
431
433{
434 return UpdateCapabilities(false, true);
435}
436
438{
440}
441
443{
444 QString name = "MaxChannels";
445
446 auto *gc = new HostComboBoxSetting(name, false);
447
448 gc->setLabel(tr("Speaker configuration"));
449
450 gc->addSelection(QCoreApplication::translate("(Common)", "Stereo"),
451 "2", true); // default
452
453 gc->setHelpText(tr("Select the maximum number of audio "
454 "channels supported by your receiver "
455 "and speakers."));
456 return gc;
457}
458
460{
461 auto *gc = new HostCheckBoxSetting("AudioDefaultUpmix");
462
463 gc->setLabel(tr("Upconvert stereo to 5.1 surround"));
464
465 gc->setValue(true);
466
467 gc->setHelpText(tr("If enabled, MythTV will upconvert stereo "
468 "to 5.1 audio. You can enable or disable "
469 "the upconversion during playback at any time."));
470 return gc;
471}
472
474{
475 auto *gc = new HostComboBoxSetting("AudioUpmixType", false);
476
477 gc->setLabel(tr("Upmix Quality"));
478
479 gc->addSelection(tr("Passive"), "0");
480 gc->addSelection(tr("Hall", "Upmix Quality"), "3");
481 gc->addSelection(tr("Good", "Upmix Quality"), "1");
482 gc->addSelection(tr("Best", "Upmix Quality"), "2", true); // default
483
484 gc->setHelpText(tr("Set the audio surround-upconversion quality."));
485
486 return gc;
487}
488
490{
491 auto *gc = new HostCheckBoxSetting("AC3PassThru");
492
493 gc->setLabel(tr("Dolby Digital"));
494
495 gc->setValue(false);
496
497 gc->setHelpText(tr("Enable if your amplifier or sound decoder "
498 "supports AC-3/Dolby Digital. You must use a digital "
499 "connection. Uncheck if using an analog connection."));
500 return gc;
501}
502
504{
505 auto *gc = new HostCheckBoxSetting("DTSPassThru");
506
507 gc->setLabel(tr("DTS"));
508
509 gc->setValue(false);
510
511 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
512 "DTS. You must use a digital connection. Uncheck "
513 "if using an analog connection"));
514 return gc;
515}
516
518{
519 auto *gc = new HostCheckBoxSetting("EAC3PassThru");
520
521 gc->setLabel(tr("E-AC-3"));
522
523 gc->setValue(false);
524
525 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
526 "E-AC-3 (DD+). You must use a HDMI connection."));
527 return gc;
528}
529
531{
532 auto *gc = new HostCheckBoxSetting("TrueHDPassThru");
533
534 gc->setLabel(tr("TrueHD"));
535
536 gc->setValue(false);
537
538 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
539 "Dolby TrueHD. You must use a HDMI connection."));
540 return gc;
541}
542
544{
545 auto *gc = new HostCheckBoxSetting("DTSHDPassThru");
546
547 gc->setLabel(tr("DTS-HD"));
548
549 gc->setValue(false);
550
551 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
552 "DTS-HD. You must use a HDMI connection."));
553 return gc;
554}
555
557{
558 bool ok = true;
559
561 {
562 QString name = m_passThroughDeviceOverride->getValue();
564 AudioOutput::GetAudioDeviceConfig(name, name, true);
565 if (adc->m_settings.IsInvalid())
566 {
567 LOG(VB_GENERAL, LOG_ERR,
568 QString("Passthru device %1 isn't usable "
569 "Check audio configuration").arg(name));
570 ok = false;
571 }
572 // add it to list of known devices
573 m_audioDevs.insert(name, *adc);
574 m_devices.append(*adc);
575 delete adc;
576 }
577 return ok;
578}
579
580#if CONFIG_AUDIO_OSS
581static void fillSelectionsFromDir(HostComboBoxSetting *comboBox,
582 const QDir& dir, bool absPath = true)
583
584{
585 QFileInfoList entries = dir.entryInfoList();
586 for (const auto & fi : std::as_const(entries))
587 {
588 if (absPath)
589 comboBox->addSelection( fi.absoluteFilePath() );
590 else
591 comboBox->addSelection( fi.fileName() );
592 }
593}
594#endif
595
597{
598 AudioOutputSettings settings = UpdateCapabilities(false);
599 QString out = m_outputDevice->getValue();
600 QString passthrough =
603 int channels = m_maxAudioChannels->getValue().toInt();
604
605 m_audioTest->UpdateCapabilities(out, passthrough, channels, settings);
606}
607
608ChannelChangedEvent::ChannelChangedEvent(QString channame, bool fulltest)
609 : QEvent(kEventType),
610 m_channel(std::move(channame)),
611 m_fulltest(fulltest)
612{
613}
614
616 QString main, QString passthrough,
617 int channels,
618 AudioOutputSettings &settings,
619 bool hd) :
620 MThread("AudioTest"),
621 m_parent(parent), m_channels(channels), m_device(std::move(main)),
622 m_passthrough(std::move(passthrough)), m_hd(hd),
623 m_samplerate(hd ? settings.BestSupportedRate() : 48000),
624 m_format(hd ? settings.BestSupportedFormat() : FORMAT_S16)
625{
628 AV_CODEC_ID_NONE, m_samplerate,
630 true, false, 0, &settings);
631 if (isOutputOpen())
632 {
633 m_audioOutput->Pause(true);
634 }
635}
636
637const QEvent::Type ChannelChangedEvent::kEventType =
638 static_cast<QEvent::Type>(QEvent::registerEventType());
639
641{
642 cancel();
643 wait();
644 delete m_audioOutput;
645}
646
648{
649 m_interrupted = true;
650}
651
653{
654 m_channel = channel;
655}
656
658{
659 RunProlog();
660 m_interrupted = false;
661 std::array<std::array<int,8>,7> smptelayout {{
662 { 0, 1, 1 }, //stereo
663 { }, //not used
664 { }, //not used
665 { 0, 2, 1, 4, 3 }, //5.0
666 { 0, 2, 1, 5, 4, 3 }, //5.1
667 { 0, 2, 1, 6, 4, 5, 3 }, //6.1
668 { 0, 2, 1, 7, 5, 4, 6, 3 }, //7.1
669 }};
670
671 if (isOutputOpen())
672 {
673 char *frames = new (std::align_val_t(16)) char[m_channels * 1024_UZ * sizeof(int32_t)];
674
675 m_audioOutput->Pause(false);
676
677 int begin = 0;
678 int end = m_channels;
679 if (m_channel >= 0)
680 {
681 begin = m_channel;
682 end = m_channel + 1;
683 }
684 while (!m_interrupted)
685 {
686 for (int i = begin; i < end && !m_interrupted; i++)
687 {
688 int current = smptelayout[m_channels - 2][i];
689
690 if (m_parent)
691 {
692 QString channel;
693
694 switch(current)
695 {
696 case 0:
697 channel = "frontleft";
698 break;
699 case 1:
700 channel = "frontright";
701 break;
702 case 2:
703 channel = "center";
704 break;
705 case 3:
706 channel = "lfe";
707 break;
708 case 4:
709 if (m_channels == 6)
710 channel = "surroundleft";
711 else if (m_channels == 7)
712 channel = "rearright";
713 else
714 channel = "rearleft";
715 break;
716 case 5:
717 if (m_channels == 6)
718 channel = "surroundright";
719 else if (m_channels == 7)
720 channel = "surroundleft";
721 else
722 channel = "rearright";
723 break;
724 case 6:
725 if (m_channels == 7)
726 channel = "surroundright";
727 else
728 channel = "surroundleft";
729 break;
730 case 7:
731 channel = "surroundright";
732 break;
733 }
734 QCoreApplication::postEvent(
735 m_parent, new ChannelChangedEvent(channel,
736 m_channel < 0));
737 LOG(VB_AUDIO, LOG_INFO, QString("AudioTest: %1 (%2->%3)")
738 .arg(channel).arg(i).arg(current));
739 }
740
741 // play sample sound for about 3s
742 int top = m_samplerate / 1000 * 3;
743 for (int j = 0; j < top && !m_interrupted; j++)
744 {
746 current, 1000,
747 m_hd ? 32 : 16))
748 {
749 LOG(VB_AUDIO, LOG_ERR, "AddData() Audio buffer "
750 "overflow, audio data lost!");
751 }
752 std::this_thread::sleep_for(m_audioOutput->LengthLastData());
753 }
755 m_audioOutput->Pause(true);
756 std::this_thread::sleep_for(500ms); // .5s pause
757 m_audioOutput->Pause(false);
758 }
759 if (m_channel >= 0)
760 break;
761 }
762 m_audioOutput->Pause(true);
763
764 ::operator delete[] (frames, std::align_val_t(16));
765 }
766 RunEpilog();
767}
768
770{
771 int channels = 2;
772
773 m_channels = gCoreContext->GetNumSetting("TestingChannels", channels);
774 setLabel(tr("Audio Configuration Testing"));
775 setHelpText(tr("Will play a test pattern on all configured "
776 "speakers"));
777
779 m_frontleft->setLabel(tr("Front Left"));
780 m_frontleft->setHelpText(tr("Start front left channel test"));
782 connect(m_frontleft,
784
785 m_frontright = new ButtonStandardSetting(m_channels == 2 ? "1" : "2");
786 m_frontright->setLabel(tr("Front Right"));
787 m_frontright->setHelpText(tr("Start front right channel test"));
789 connect(m_frontright,
791
793 m_rearleft->setLabel(tr("Rear Left"));
794 m_rearleft->setHelpText(tr("Start rear left channel test"));
796 connect(m_rearleft,
798
800 m_rearright->setLabel(tr("Rear Right"));
801 m_rearright->setHelpText(tr("Start rear right channel test"));
803 connect(m_rearright,
805
806 QString lfe;
807 QString surroundleft;
808 if (m_channels == 6)
809 {
810 lfe = "5";
811 surroundleft = "4";
812 }
813 else if (m_channels == 7)
814 {
815 lfe = "6";
816 surroundleft = "5";
817 }
818 else
819 {
820 lfe = "7";
821 surroundleft = "6";
822 }
823
824 m_lfe = new ButtonStandardSetting(lfe);
825 m_lfe->setLabel(tr("LFE"));
826 m_lfe->setHelpText(tr("Start LFE channel test"));
828 connect(m_lfe,
830
831 m_surroundleft = new ButtonStandardSetting(surroundleft);
832 m_surroundleft->setLabel(tr("Surround Left"));
833 m_surroundleft->setHelpText(tr("Start surround left channel test"));
835 connect(m_surroundleft,
837
839 m_surroundright->setLabel(tr("Surround Right"));
840 m_surroundright->setHelpText(tr("Start surround right channel test"));
842 connect(m_surroundright,
844
846 m_center->setLabel(tr("Center"));
847 m_center->setHelpText(tr("Start center channel test"));
849 connect(m_center,
851
853 m_startButton->setLabel(tr("Test All"));
854 m_startButton->setHelpText(tr("Start all channels test"));
857
859 m_hd->setLabel(tr("Use Highest Quality Mode"));
860 m_hd->setHelpText(tr("Use the highest audio quality settings "
861 "supported by your audio card. This will be "
862 "a good place to start troubleshooting "
863 "potential errors"));
864 addChild(m_hd);
865 connect(m_hd, qOverload<const QString&>(&StandardSetting::valueChanged),
867}
868
870{
871 if (m_at)
872 {
873 m_at->cancel();
874 m_at->wait();
875 delete m_at;
876 }
877}
878
879
881 const QString &passthrough,
882 int channels,
883 const AudioOutputSettings &settings)
884{
885 m_main = main;
886 m_passthrough = passthrough;
887 m_channels = channels;
888 m_settings = settings;
895}
896
898{
899 if (this->sender() == m_startButton)
900 {
901 if (m_at && m_at->isRunning())
902 cancelTest();
903 else
904 {
905 prepareTest();
906 m_at->setChannel(-1);
907 m_at->start();
908 m_startButton->setLabel(QCoreApplication::translate("(Common)",
909 "Stop"));
910 }
911 return;
912 }
913 if (m_at && m_at->isRunning())
914 {
915 m_at->cancel();
916 m_at->wait();
917 }
918
919 prepareTest();
920
921 int channel = 1;
922
923 if (this->sender() == m_frontleft)
924 channel = 0;
925 else if (this->sender() == m_frontright)
926 channel = (m_channels == 2) ? 1 : 2;
927 else if (this->sender() == m_rearleft)
928 channel = 5;
929 else if (this->sender() == m_rearright)
930 channel = 4;
931 else if (this->sender() == m_lfe)
932 {
933 if (m_channels == 6)
934 channel = 5;
935 else if (m_channels == 7)
936 channel = 6;
937 else
938 channel = 7;
939 }
940 else if (this->sender() == m_surroundleft)
941 {
942 if (m_channels == 6)
943 channel = 4;
944 else if (m_channels == 7)
945 channel = 5;
946 else
947 channel = 6;
948 }
949 else if (this->sender() == m_surroundright)
950 {
951 channel = 3;
952 }
953 else if (this->sender() == m_center)
954 {
955 channel = 1;
956 }
957
958 m_at->setChannel(channel);
959
960 m_at->start();
961}
962
963void AudioTest::togglequality(const QString &/*value*/)
964{
965 cancelTest();
967}
968
970{
971 if (m_at && m_at->isRunning())
972 {
973 m_at->cancel();
974 m_startButton->setLabel(tr("Test All"));
975 }
976}
977
979{
980 cancelTest();
981 if (m_at)
982 {
983 m_at->cancel();
984 m_at->wait();
985 delete m_at;
986 }
987
990 if (!m_at->isOutputOpen())
991 {
992 QString msg = tr("Audio device is invalid or not useable.");
994
995 auto *mcd = new MythConfirmationDialog(mainStack,
996 msg, false);
997
998 if (mcd->Create())
999 mainStack->AddScreen(mcd);
1000 else
1001 delete mcd;
1002 }
1003}
1004
1005bool AudioTest::event(QEvent *event)
1006{
1008 return QObject::event(event); //not handled
1009
1010 auto *cce = dynamic_cast<ChannelChangedEvent*>(event);
1011 if (cce == nullptr)
1012 return GroupSetting::event(event);
1013
1014 QString channel = cce->m_channel;
1015
1016 if (!cce->m_fulltest)
1017 return GroupSetting::event(event);
1018
1019 bool fl = false;
1020 bool fr = false;
1021 bool c = false;
1022 bool lfe = false;
1023 bool sl = false;
1024 bool sr = false;
1025 bool rl = false;
1026 bool rr = false;
1027
1028 if (channel == "frontleft")
1029 {
1030 fl = true;
1031 }
1032 else if (channel == "frontright")
1033 {
1034 fr = true;
1035 }
1036 else if (channel == "center")
1037 {
1038 c = true;
1039 }
1040 else if (channel == "lfe")
1041 {
1042 lfe = true;
1043 }
1044 else if (channel == "surroundleft")
1045 {
1046 sl = true;
1047 }
1048 else if (channel == "surroundright")
1049 {
1050 sr = true;
1051 }
1052 else if (channel == "rearleft")
1053 {
1054 rl = true;
1055 }
1056 else if (channel == "rearright")
1057 {
1058 rr = true;
1059 }
1060 if (m_frontleft)
1062 if (m_frontright)
1064 if (m_center)
1065 m_center->setEnabled(c);
1066 if (m_surroundleft)
1068 if (m_surroundright)
1070 if (m_rearleft)
1072 if (m_rearright)
1074 if (m_lfe)
1075 m_lfe->setEnabled(lfe);
1076 return GroupSetting::event(event);
1077}
1078
1079
1081{
1082 auto *gc = new HostCheckBoxSetting("MythControlsVolume");
1083
1084 gc->setLabel(tr("Use internal volume controls"));
1085
1086 gc->setValue(true);
1087
1088 gc->setHelpText(tr("If enabled, MythTV will control the PCM and "
1089 "master mixer volume. Disable this option if you "
1090 "prefer to control the volume externally (for "
1091 "example, using your amplifier) or if you use an "
1092 "external mixer program."));
1093
1094 gc->addTargetedChild("1", MixerDevice());
1095 gc->addTargetedChild("1", MixerControl());
1096 gc->addTargetedChild("1", MixerVolume());
1097 gc->addTargetedChild("1", PCMVolume());
1098 return gc;
1099}
1100
1102{
1103 auto *gc = new HostComboBoxSetting("MixerDevice", true);
1104 gc->setLabel(tr("Mixer device"));
1105
1106#if CONFIG_AUDIO_OSS
1107 QDir dev("/dev", "mixer*", QDir::Name, QDir::System);
1108 fillSelectionsFromDir(gc, dev);
1109
1110 dev.setPath("/dev/sound");
1111 if (dev.exists())
1112 {
1113 fillSelectionsFromDir(gc, dev);
1114 }
1115#endif
1116#if CONFIG_AUDIO_ALSA
1117 gc->addSelection("ALSA:default", "ALSA:default");
1118#endif
1119#ifdef Q_OS_WINDOWS
1120 gc->addSelection("DirectX:", "DirectX:");
1121 gc->addSelection("Windows:", "Windows:");
1122#endif
1123#ifdef Q_OS_ANDROID
1124 gc->addSelection("OpenSLES:", "OpenSLES:");
1125#endif
1126#ifndef Q_OS_WINDOWS
1127 gc->addSelection(tr("software"), "software");
1128#endif
1129
1130 gc->setHelpText(tr("Setting the mixer device to \"%1\" lets MythTV control "
1131 "the volume of all audio at the expense of a slight "
1132 "quality loss.")
1133 .arg(tr("software")));
1134
1135 return gc;
1136}
1137
1138const std::array<const char *,2> AudioConfigSettings::kMixerControlControls
1139 {QT_TR_NOOP("PCM"),
1140 QT_TR_NOOP("Master")};
1141
1143{
1144 auto *gc = new HostComboBoxSetting("MixerControl", true);
1145
1146 gc->setLabel(tr("Mixer controls"));
1147
1148 for (const auto & control : kMixerControlControls)
1149 gc->addSelection(tr(control), control);
1150
1151 gc->setHelpText(tr("Changing the volume adjusts the selected mixer."));
1152
1153 return gc;
1154}
1155
1157{
1158 auto *gs = new HostSpinBoxSetting("MasterMixerVolume", 0, 100, 1);
1159
1160 gs->setLabel(tr("Master mixer volume"));
1161
1162 gs->setValue(70);
1163
1164 gs->setHelpText(tr("Initial volume for the Master mixer. This affects "
1165 "all sound created by the audio device. Note: Do not "
1166 "set this too low."));
1167 return gs;
1168}
1169
1171{
1172 auto *gs = new HostSpinBoxSetting("PCMMixerVolume", 0, 100, 1);
1173
1174 gs->setLabel(tr("PCM mixer volume"));
1175
1176 gs->setValue(70);
1177
1178 gs->setHelpText(tr("Initial volume for PCM output. Using the volume "
1179 "keys in MythTV will adjust this parameter."));
1180 return gs;
1181}
1182
1184{
1185 auto *gc = new HostCheckBoxSetting("StereoPCM");
1186
1187 gc->setLabel(tr("Stereo PCM Only"));
1188
1189 gc->setValue(false);
1190
1191 gc->setHelpText(tr("Enable if your amplifier or sound decoder only "
1192 "supports 2 channel PCM (typically an old HDMI 1.0 "
1193 "device). Multichannel audio will be re-encoded to "
1194 "AC-3 when required"));
1195 return gc;
1196}
1197
1199{
1200 auto *gc = new HostCheckBoxSetting("SRCQualityOverride");
1201
1202 gc->setLabel(tr("Override SRC quality"));
1203
1204 gc->setValue(false);
1205
1206 gc->setHelpText(tr("Enable to override audio sample rate "
1207 "conversion quality."));
1208 return gc;
1209}
1210
1212{
1213 auto *gc = new HostComboBoxSetting("SRCQuality", false);
1214
1215 gc->setLabel(tr("Sample rate conversion"));
1216
1217 gc->addSelection(tr("Disabled", "Sample rate conversion"), "-1");
1218 gc->addSelection(tr("Fastest", "Sample rate conversion"), "0");
1219 gc->addSelection(tr("Good", "Sample rate conversion"), "1", true); // default
1220 gc->addSelection(tr("Best", "Sample rate conversion"), "2");
1221
1222 gc->setHelpText(tr("Set the quality of audio sample-rate "
1223 "conversion. \"%1\" (default) provides the best "
1224 "compromise between CPU usage and quality. \"%2\" "
1225 "lets the audio device handle sample-rate conversion.")
1226 .arg(tr("Good", "Sample rate conversion"),
1227 tr("Disabled", "Sample rate conversion")));
1228
1229 return gc;
1230}
1231
1233{
1234 auto *gc = new HostCheckBoxSetting("Audio48kOverride");
1235
1236 gc->setLabel(tr("Force audio device output to 48kHz"));
1237 gc->setValue(false);
1238
1239 gc->setHelpText(tr("Force audio sample rate to 48kHz. Some audio devices "
1240 "will report various rates, but they ultimately "
1241 "crash."));
1242 return gc;
1243}
1244
1246{
1247 auto *gc = new HostCheckBoxSetting("PassThruDeviceOverride");
1248
1249 gc->setLabel(tr("Separate digital output device"));
1250
1251 gc->setValue(false);
1252
1253 gc->setHelpText(tr("Use a distinct digital output device from default. "
1254 "(default is not checked)"));
1255 return gc;
1256}
1257
1259{
1260 auto *gc = new HostComboBoxSetting("PassThruOutputDevice",
1261 true);
1262
1263 gc->setLabel(tr("Digital output device"));
1264 //TODO Is default not equivalent to PassThroughOverride off? if so
1265 //PassThruDeviceOverridedsetting could be removed
1266 gc->addSelection(QCoreApplication::translate("(Common)", "Default"),
1267 "Default");
1268#ifdef Q_OS_WINDOWS
1269 gc->addSelection("DirectX:Primary Sound Driver");
1270#else
1271 gc->addSelection("ALSA:iec958:{ AES0 0x02 }",
1272 "ALSA:iec958:{ AES0 0x02 }");
1273 gc->addSelection("ALSA:hdmi", "ALSA:hdmi");
1274 gc->addSelection("ALSA:plughw:0,3", "ALSA:plughw:0,3");
1275#endif
1276
1277 gc->setHelpText(tr("Audio output device to use for digital audio. This "
1278 "value is currently only used with ALSA and DirectX "
1279 "sound output."));
1280 return gc;
1281}
1282
1284{
1285 auto *gc = new HostCheckBoxSetting("SPDIFRateOverride");
1286
1287 gc->setLabel(tr("SPDIF 48kHz rate override"));
1288
1289 gc->setValue(false);
1290
1291 gc->setHelpText(tr("ALSA only. By default, let ALSA determine the "
1292 "passthrough sampling rate. If checked set the sampling "
1293 "rate to 48kHz for passthrough. (default is not "
1294 "checked)"));
1295 return gc;
1296}
1297
1299{
1300 auto *gc = new HostCheckBoxSetting("HBRPassthru");
1301
1302 gc->setLabel(tr("HBR passthrough support"));
1303
1304 gc->setValue(true);
1305
1306 gc->setHelpText(tr("HBR support is required for TrueHD and DTS-HD "
1307 "passthrough. If unchecked, Myth will limit the "
1308 "passthrough bitrate to 6.144Mbit/s. This will "
1309 "disable True-HD passthrough, however will allow "
1310 "DTS-HD content to be sent as DTS-HD Hi-Res. (default "
1311 "is checked)"));
1312 return gc;
1313}
1314
1316{
1317 m_mpcm->setEnabled(flag);
1318}
1319// vim:set sw=4 ts=4 expandtab:
@ FORMAT_S16
@ FEATURE_DTS
@ FEATURE_AC3
@ FEATURE_DTSHD
@ FEATURE_EAC3
@ FEATURE_LPCM
@ FEATURE_TRUEHD
@ AUDIOOUTPUT_VIDEO
Definition: audiosettings.h:23
void Load(void) override
Load data which will ultimately be displayed on-screen or used to determine what appears on-screen (S...
AudioConfigScreen(MythScreenStack *parent, const char *name, GroupSetting *groupSetting)
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
void UpdateVisibility(StandardSetting *)
AudioDeviceComboBox * m_outputDevice
HostCheckBoxSetting * m_passThroughOverride
ADCMap & AudioDeviceMap(void)
AudioOutputSettings UpdateCapabilitiesAC3(void)
HostComboBoxSetting * m_audioUpmixType
static const std::array< const char *, 2 > kMixerControlControls
static HostComboBoxSetting * PassThroughOutputDevice()
HostCheckBoxSetting * m_trueHDPassThrough
static HostComboBoxSetting * MixerDevice()
static HostCheckBoxSetting * AudioUpmix()
static HostCheckBoxSetting * DTSPassThrough()
static HostCheckBoxSetting * SRCQualityOverride()
static HostCheckBoxSetting * TrueHDPassThrough()
HostCheckBoxSetting * m_dtsPassThrough
static HostCheckBoxSetting * EAC3PassThrough()
static HostComboBoxSetting * AudioUpmixType()
static HostCheckBoxSetting * AC3PassThrough()
static HostCheckBoxSetting * HBRPassthrough()
HostComboBoxSetting * m_passThroughDeviceOverride
HostCheckBoxSetting * m_dtsHDPassThrough
static HostComboBoxSetting * MixerControl()
static HostCheckBoxSetting * Audio48kOverride()
HostCheckBoxSetting * m_ac3PassThrough
static HostComboBoxSetting * MaxAudioChannels()
static HostCheckBoxSetting * MPCM()
HostCheckBoxSetting * m_mpcm
GroupSetting * m_triggerDigital
HostCheckBoxSetting * m_eac3PassThrough
static HostCheckBoxSetting * DTSHDPassThrough()
HostComboBoxSetting * m_maxAudioChannels
static HostCheckBoxSetting * MythControlsVolume()
AudioOutput::ADCVect m_devices
AudioOutputSettings UpdateCapabilities(bool restore=true, bool AC3=false)
static HostCheckBoxSetting * SPDIFRateOverride()
static HostSpinBoxSetting * PCMVolume()
AudioOutput::ADCVect & AudioDeviceVect(void)
static HostComboBoxSetting * SRCQuality()
static HostSpinBoxSetting * MixerVolume()
HostCheckBoxSetting * m_audioUpmix
static HostCheckBoxSetting * PassThroughOverride()
void AudioDescriptionHelp(StandardSetting *setting)
void edit(MythScreenType *screen) override
AudioConfigSettings * m_parent
AudioDeviceComboBox(AudioConfigSettings *parent)
bool IsSupportedChannels(int channels)
bool canFeature(DigitalFeature arg) const
return DigitalFeature mask.
void SetBestSupportedChannels(int channels)
Force set the greatest number of channels supported by the audio device.
void setFeature(DigitalFeature arg)
set the provided digital feature possible values are:
bool IsInvalid() const
return true if class instance is marked invalid.
AudioOutputSettings m_settings
Definition: audiooutput.h:40
virtual void Drain(void)=0
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
Definition: audiooutput.cpp:67
virtual std::chrono::milliseconds LengthLastData(void) const
Definition: audiooutput.h:138
static ADCVect * GetOutputList(void)
QVector< AudioDeviceConfig > ADCVect
Definition: audiooutput.h:55
static AudioDeviceConfig * GetAudioDeviceConfig(QString &name, const QString &desc, bool willsuspendpa=false)
virtual void Pause(bool paused)=0
bool playPinkNoise(char *frames, int channels, int channel, int count, int bits)
Generates and plays pink noise from a speaker for testing.
void setChannel(int channel)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
AudioTestThread(QObject *parent, QString main, QString passthrough, int channels, AudioOutputSettings &settings, bool hd)
AudioOutput * m_audioOutput
ButtonStandardSetting * m_startButton
ButtonStandardSetting * m_frontright
ButtonStandardSetting * m_lfe
ButtonStandardSetting * m_surroundleft
ButtonStandardSetting * m_frontleft
TransMythUICheckBoxSetting * m_hd
ButtonStandardSetting * m_center
ButtonStandardSetting * m_surroundright
void togglequality(const QString &)
ButtonStandardSetting * m_rearright
bool event(QEvent *event) override
ButtonStandardSetting * m_rearleft
AudioTestThread * m_at
~AudioTest() override
AudioOutputSettings m_settings
void UpdateCapabilities(const QString &main, const QString &passthrough, int channels, const AudioOutputSettings &settings)
ChannelChangedEvent(QString channame, bool fulltest)
static const Type kEventType
GroupSetting()=default
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
bool isRunning(void) const
Definition: mthread.cpp:261
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:194
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:281
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:207
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:298
Dialog asking for user confirmation.
QString GetSetting(const QString &key, const QString &defaultval="")
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
MythScreenStack * GetMainStack()
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void SetBusyPopupMessage(const QString &message)
void addSelection(const QString &label, QString value=QString(), bool select=false)
void edit(MythScreenType *screen) override
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
void Load(void) override
Load data which will ultimately be displayed on-screen or used to determine what appears on-screen (S...
GroupSetting * GetGroupSettings(void) const
virtual void addChild(StandardSetting *child)
virtual void Load(void)
void addTargetedChild(const QString &value, StandardSetting *setting)
virtual void setHelpText(const QString &str)
bool haveChanged()
Return true if the setting have changed or any of its children.
void valueChanged(const QString &newValue)
virtual QString getValue(void) const
virtual void setEnabled(bool enabled)
virtual void setLabel(QString str)
void setChanged(bool changed)
@ AC3
Definition: element.h:84
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
STL namespace.
uint fillSelectionsFromDir(const QDir &dir, uint minor_min, uint minor_max, const QString &card, const QRegularExpression &driver, bool allow_duplicates, V2CaptureDeviceList *pList, const QString &cardType)
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:86