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"
28#ifndef __cpp_size_t_suffix
30#endif
33
34// MythFrontend
36
37extern "C" {
38#include "libavformat/avformat.h"
39}
40
42 HostComboBoxSetting("AudioOutputDevice", true), m_parent(parent)
43{
44 setLabel(tr("Audio output device"));
45#ifdef Q_OS_ANDROID
46 QString dflt = "OpenSLES:";
47#elif CONFIG_AUDIO_ALSA
48 QString dflt = "ALSA:default";
49#elif CONFIG_AUDIO_PULSEOUTPUT
50 QString dflt = "PulseAudio:default";
51#elif defined(Q_OS_DARWIN)
52 QString dflt = "CoreAudio:";
53#elif defined(Q_OS_WINDOWS)
54 QString dflt = "Windows:";
55#else
56 QString dflt = "NULL";
57#endif
58 QString current = gCoreContext->GetSetting(QString("AudioOutputDevice"),
59 dflt);
61
62 connect(this, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
64}
65
67{
70}
71
73{
75
76 if (vect.empty())
77 return;
78
79 QString value = getValue();
81
82 // Adding the current value first avoids marking the setting as changed
83 addSelection(value, value, true);
84 for (const auto & it : std::as_const(vect))
85 {
86 if (value != it.m_name)
87 addSelection(it.m_name, it.m_name);
88 }
89}
90
92{
93 QString desc = m_parent->AudioDeviceMap().value(setting->getValue()).m_desc;
94 setHelpText(desc);
95}
96
98 GroupSetting *groupSetting)
99 : StandardSettingDialog(parent, name, groupSetting)
100{
101}
102
104{
105 SetBusyPopupMessage(tr("Scanning for available audio devices"));
107}
108
110{
112
113 auto *settings = qobject_cast<AudioConfigSettings*>(GetGroupSettings());
114 if (settings == nullptr)
115 return;
116 settings->CheckConfiguration();
117}
118
120 : m_triggerDigital(new GroupSetting()),
121 m_passThroughOverride(PassThroughOverride()),
122 m_passThroughDeviceOverride(PassThroughOutputDevice())
123{
124 setLabel(tr("Audio System"));
125
127 // Rescan button
128
129 auto *rescan = new ButtonStandardSetting("rescan");
130 rescan->setLabel(tr("Rescan"));
131 rescan->setHelpText(tr("Rescan for available audio devices. "
132 "Current entry will be checked and "
133 "capability entries populated."));
134 addChild(rescan);
136
137 // digital settings
138 m_triggerDigital->setLabel(tr("Digital Audio Capabilities"));
145
150
151 //Advanced Settings
152 auto *advancedSettings = new GroupSetting();
153 advancedSettings->setLabel(tr("Advanced Audio Settings"));
154 advancedSettings->setHelpText(tr("Enable extra audio settings. Under most "
155 "usage all options should be left alone"));
156 addChild(advancedSettings);
157 advancedSettings->addChild(m_passThroughOverride);
158 advancedSettings->addChild(m_passThroughDeviceOverride);
162
163 StandardSetting *srcqualityoverride = SRCQualityOverride();
164 srcqualityoverride->addTargetedChild("1", SRCQuality());
165 addChild(srcqualityoverride);
166
167 advancedSettings->addChild(Audio48kOverride());
168#if CONFIG_AUDIO_ALSA
169 advancedSettings->addChild(SPDIFRateOverride());
170#endif
171
172 advancedSettings->addChild(HBRPassthrough());
173
174 advancedSettings->addChild(m_mpcm = MPCM());
175
178
179 // Set slots
180 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
181 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateVisibility));
182 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
183 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
184 connect(m_ac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
185 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilitiesAC3));
186
187 connect(m_dtsPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
188 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
189 connect(m_eac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
190 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
191 connect(m_trueHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
192 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
193 connect(m_dtsHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
194 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
195 //Slot for audio test
196 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
198 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
200}
201
203{
204 QString name = m_outputDevice->getValue();
206 AudioOutput::GetAudioDeviceConfig(name, name, true);
207 if (adc)
208 {
209 if (adc->m_settings.IsInvalid())
210 {
211 QString msg = tr("%1 is invalid or not useable.").arg(name);
212
213 ShowOkPopup(msg);
214
215 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
216 .arg(name));
217 }
218 delete adc;
219 }
220
221 if (!CheckPassthrough())
222 {
223 QString pt_name = m_passThroughDeviceOverride->getValue();
224 QString pt_msg = tr("Passthrough device is invalid or not useable. Check "
225 "configuration in Advanced Settings:") +
226 pt_name;
227
228 ShowOkPopup(pt_msg);
229
230 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
231 .arg(pt_name));
232 }
233}
234
236{
238 AudioRescan();
239 // If this is the initial setup where there was nothing on the DB,
240 // set changed so that user can save.
241 if (gCoreContext->GetSetting(QString("AudioOutputDevice"),"").isEmpty())
242 setChanged(true);
243}
244
246{
247 if (!m_slotLock.tryLock())
248 return;
249
251
252 m_audioDevs.clear();
253 for (const auto & dev : std::as_const(*list))
254 m_audioDevs.insert(dev.m_name, dev);
255
256 m_devices = *list;
257 delete list;
258
259 QString name = m_outputDevice->getValue();
260 if (!m_audioDevs.contains(name))
261 {
262 // Scan for possible custom entry that isn't in the list
264 AudioOutput::GetAudioDeviceConfig(name, name, true);
265 m_audioDevs.insert(name, *adc);
266 m_devices.append(*adc);
267 delete adc;
268 }
270 m_slotLock.unlock();
272}
273
275{
277 return;
278
279 int cur_speakers = m_maxAudioChannels->getValue().toInt();
280 m_audioUpmix->setEnabled(cur_speakers > 2);
281 m_audioUpmixType->setEnabled(cur_speakers > 2);
282}
283
285 bool restore, bool AC3)
286{
287 int max_speakers = 8;
288 int realmax_speakers = 8;
289
290 bool invalid = false;
291 QString out;
292
293 if (m_outputDevice)
294 out = m_outputDevice->getValue();
295
296 if (!out.isEmpty())
297 {
298 restore = out != m_lastAudioDevice;
299 m_lastAudioDevice = out;
300 }
301
302 AudioOutputSettings settings;
303 AudioOutputSettings settingsdigital;
304
305 // Test if everything is set yet
309 return settings;
310
311 if (!m_slotLock.tryLock()) // Doing a rescan of channels
312 return settings;
313
314 bool bAC3 = true;
315 //bool bDTS = true;
316 bool bLPCM = true;
317 bool bEAC3 = true;
318 bool bTRUEHD = true;
319 bool bDTSHD = true;
320
321 if (!m_audioDevs.contains(out))
322 {
323 LOG(VB_AUDIO, LOG_ERR, QString("Update not found (%1)").arg(out));
324 invalid = true;
325 }
326 else
327 {
328 bool bForceDigital = m_passThroughOverride->boolValue();
329
330 settings = m_audioDevs.value(out).m_settings;
331 settingsdigital = bForceDigital ?
333 .m_settings : settings;
334
335 realmax_speakers = max_speakers = settings.BestSupportedChannels();
336
337 bAC3 = settingsdigital.canFeature(FEATURE_AC3) &&
339 //bDTS = settingsdigital.canFeature(FEATURE_DTS) &&
340 // m_dtsPassThrough->boolValue();
341 bLPCM = settings.canFeature(FEATURE_LPCM) &&
342 !gCoreContext->GetBoolSetting("StereoPCM", false);
343 bEAC3 = settingsdigital.canFeature(FEATURE_EAC3) &&
344 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
345 bTRUEHD = settingsdigital.canFeature(FEATURE_TRUEHD) &&
346 !gCoreContext->GetBoolSetting("Audio48kOverride", false) &&
347 gCoreContext->GetBoolSetting("HBRPassthru", true);
348 bDTSHD = settingsdigital.canFeature(FEATURE_DTSHD) &&
349 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
350
351 if (max_speakers > 2 && !bLPCM)
352 max_speakers = 2;
353 if (max_speakers == 2 && bAC3)
354 {
355 max_speakers = 6;
356 if (AC3)
357 {
358 restore = true;
359 }
360 }
361 }
362
363 m_triggerDigital->setEnabled(invalid || settingsdigital.canFeature(
369
370 int cur_speakers = m_maxAudioChannels->getValue().toInt();
371 if (cur_speakers > m_maxSpeakers)
372 {
374 }
375 if (restore)
376 {
377 cur_speakers = m_maxSpeakers;
378 }
379
380 if (cur_speakers > max_speakers)
381 {
382 LOG(VB_AUDIO, LOG_INFO, QString("Reset device %1").arg(out));
383 cur_speakers = max_speakers;
384 }
385
386 // Remove everything and re-add available channels
387 bool chansChanged = m_maxAudioChannels->haveChanged();
389 for (int i = 1; i <= max_speakers; i++)
390 {
391 if (invalid || settings.IsSupportedChannels(i) ||
392 settingsdigital.IsSupportedChannels(i))
393 {
394 QString txt;
395
396 switch (i)
397 {
398 case 2:
399 txt = QCoreApplication::translate("(Common)", "Stereo");
400 break;
401 case 6:
402 txt = QCoreApplication::translate("(Common)", "5.1");
403 break;
404 case 8:
405 txt = QCoreApplication::translate("(Common)", "7.1");
406 break;
407 default:
408 continue;
409 }
410 m_maxAudioChannels->addSelection(txt, QString::number(i),
411 i == cur_speakers);
412 }
413 }
414 m_maxAudioChannels->setChanged(chansChanged);
415
416 setMPCMEnabled(settings.canPassthrough() >= 0);
417
418 // Return values is used by audio test
419 // where we mainly are interested by the number of channels
420 // if we support AC3 and/or LPCM
421 settings.SetBestSupportedChannels(cur_speakers);
422 settings.setFeature(bAC3, FEATURE_AC3);
423 settings.setFeature(bLPCM && realmax_speakers > 2, FEATURE_LPCM);
424
425 m_slotLock.unlock();
426 return settings;
427}
428
430{
432}
433
435{
436 return UpdateCapabilities(false, true);
437}
438
440{
442}
443
445{
446 QString name = "MaxChannels";
447
448 auto *gc = new HostComboBoxSetting(name, false);
449
450 gc->setLabel(tr("Speaker configuration"));
451
452 gc->addSelection(QCoreApplication::translate("(Common)", "Stereo"),
453 "2", true); // default
454
455 gc->setHelpText(tr("Select the maximum number of audio "
456 "channels supported by your receiver "
457 "and speakers."));
458 return gc;
459}
460
462{
463 auto *gc = new HostCheckBoxSetting("AudioDefaultUpmix");
464
465 gc->setLabel(tr("Upconvert stereo to 5.1 surround"));
466
467 gc->setValue(true);
468
469 gc->setHelpText(tr("If enabled, MythTV will upconvert stereo "
470 "to 5.1 audio. You can enable or disable "
471 "the upconversion during playback at any time."));
472 return gc;
473}
474
476{
477 auto *gc = new HostComboBoxSetting("AudioUpmixType", false);
478
479 gc->setLabel(tr("Upmix Quality"));
480
481 gc->addSelection(tr("Passive"), "0");
482 gc->addSelection(tr("Hall", "Upmix Quality"), "3");
483 gc->addSelection(tr("Good", "Upmix Quality"), "1");
484 gc->addSelection(tr("Best", "Upmix Quality"), "2", true); // default
485
486 gc->setHelpText(tr("Set the audio surround-upconversion quality."));
487
488 return gc;
489}
490
492{
493 auto *gc = new HostCheckBoxSetting("AC3PassThru");
494
495 gc->setLabel(tr("Dolby Digital"));
496
497 gc->setValue(false);
498
499 gc->setHelpText(tr("Enable if your amplifier or sound decoder "
500 "supports AC-3/Dolby Digital. You must use a digital "
501 "connection. Uncheck if using an analog connection."));
502 return gc;
503}
504
506{
507 auto *gc = new HostCheckBoxSetting("DTSPassThru");
508
509 gc->setLabel(tr("DTS"));
510
511 gc->setValue(false);
512
513 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
514 "DTS. You must use a digital connection. Uncheck "
515 "if using an analog connection"));
516 return gc;
517}
518
520{
521 auto *gc = new HostCheckBoxSetting("EAC3PassThru");
522
523 gc->setLabel(tr("E-AC-3"));
524
525 gc->setValue(false);
526
527 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
528 "E-AC-3 (DD+). You must use a HDMI connection."));
529 return gc;
530}
531
533{
534 auto *gc = new HostCheckBoxSetting("TrueHDPassThru");
535
536 gc->setLabel(tr("TrueHD"));
537
538 gc->setValue(false);
539
540 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
541 "Dolby TrueHD. You must use a HDMI connection."));
542 return gc;
543}
544
546{
547 auto *gc = new HostCheckBoxSetting("DTSHDPassThru");
548
549 gc->setLabel(tr("DTS-HD"));
550
551 gc->setValue(false);
552
553 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
554 "DTS-HD. You must use a HDMI connection."));
555 return gc;
556}
557
559{
560 bool ok = true;
561
563 {
564 QString name = m_passThroughDeviceOverride->getValue();
566 AudioOutput::GetAudioDeviceConfig(name, name, true);
567 if (adc->m_settings.IsInvalid())
568 {
569 LOG(VB_GENERAL, LOG_ERR,
570 QString("Passthru device %1 isn't usable "
571 "Check audio configuration").arg(name));
572 ok = false;
573 }
574 // add it to list of known devices
575 m_audioDevs.insert(name, *adc);
576 m_devices.append(*adc);
577 delete adc;
578 }
579 return ok;
580}
581
582#if CONFIG_AUDIO_OSS
583static void fillSelectionsFromDir(HostComboBoxSetting *comboBox,
584 const QDir& dir, bool absPath = true)
585
586{
587 QFileInfoList entries = dir.entryInfoList();
588 for (const auto & fi : std::as_const(entries))
589 {
590 if (absPath)
591 comboBox->addSelection( fi.absoluteFilePath() );
592 else
593 comboBox->addSelection( fi.fileName() );
594 }
595}
596#endif
597
599{
600 AudioOutputSettings settings = UpdateCapabilities(false);
601 QString out = m_outputDevice->getValue();
602 QString passthrough =
605 int channels = m_maxAudioChannels->getValue().toInt();
606
607 m_audioTest->UpdateCapabilities(out, passthrough, channels, settings);
608}
609
610ChannelChangedEvent::ChannelChangedEvent(QString channame, bool fulltest)
611 : QEvent(kEventType),
612 m_channel(std::move(channame)),
613 m_fulltest(fulltest)
614{
615}
616
618 QString main, QString passthrough,
619 int channels,
620 AudioOutputSettings &settings,
621 bool hd) :
622 MThread("AudioTest"),
623 m_parent(parent), m_channels(channels), m_device(std::move(main)),
624 m_passthrough(std::move(passthrough)), m_hd(hd),
625 m_samplerate(hd ? settings.BestSupportedRate() : 48000),
626 m_format(hd ? settings.BestSupportedFormat() : FORMAT_S16)
627{
630 AV_CODEC_ID_NONE, m_samplerate,
632 true, false, 0, &settings);
633 if (isOutputOpen())
634 {
635 m_audioOutput->Pause(true);
636 }
637}
638
639const QEvent::Type ChannelChangedEvent::kEventType =
640 static_cast<QEvent::Type>(QEvent::registerEventType());
641
643{
644 cancel();
645 wait();
646 delete m_audioOutput;
647}
648
650{
651 m_interrupted = true;
652}
653
655{
656 m_channel = channel;
657}
658
660{
661 RunProlog();
662 m_interrupted = false;
663 std::array<std::array<int,8>,7> smptelayout {{
664 { 0, 1, 1 }, //stereo
665 { }, //not used
666 { }, //not used
667 { 0, 2, 1, 4, 3 }, //5.0
668 { 0, 2, 1, 5, 4, 3 }, //5.1
669 { 0, 2, 1, 6, 4, 5, 3 }, //6.1
670 { 0, 2, 1, 7, 5, 4, 6, 3 }, //7.1
671 }};
672
673 if (isOutputOpen())
674 {
675#ifdef __cpp_size_t_suffix
676 char *frames = new (std::align_val_t(16)) char[m_channels * 1024UZ * sizeof(int32_t)];
677#else
678 char *frames = new (std::align_val_t(16)) char[m_channels * 1024_UZ * sizeof(int32_t)];
679#endif
680
681 m_audioOutput->Pause(false);
682
683 int begin = 0;
684 int end = m_channels;
685 if (m_channel >= 0)
686 {
687 begin = m_channel;
688 end = m_channel + 1;
689 }
690 while (!m_interrupted)
691 {
692 for (int i = begin; i < end && !m_interrupted; i++)
693 {
694 int current = smptelayout[m_channels - 2][i];
695
696 if (m_parent)
697 {
698 QString channel;
699
700 switch(current)
701 {
702 case 0:
703 channel = "frontleft";
704 break;
705 case 1:
706 channel = "frontright";
707 break;
708 case 2:
709 channel = "center";
710 break;
711 case 3:
712 channel = "lfe";
713 break;
714 case 4:
715 if (m_channels == 6)
716 channel = "surroundleft";
717 else if (m_channels == 7)
718 channel = "rearright";
719 else
720 channel = "rearleft";
721 break;
722 case 5:
723 if (m_channels == 6)
724 channel = "surroundright";
725 else if (m_channels == 7)
726 channel = "surroundleft";
727 else
728 channel = "rearright";
729 break;
730 case 6:
731 if (m_channels == 7)
732 channel = "surroundright";
733 else
734 channel = "surroundleft";
735 break;
736 case 7:
737 channel = "surroundright";
738 break;
739 }
740 QCoreApplication::postEvent(
741 m_parent, new ChannelChangedEvent(channel,
742 m_channel < 0));
743 LOG(VB_AUDIO, LOG_INFO, QString("AudioTest: %1 (%2->%3)")
744 .arg(channel).arg(i).arg(current));
745 }
746
747 // play sample sound for about 3s
748 int top = m_samplerate / 1000 * 3;
749 for (int j = 0; j < top && !m_interrupted; j++)
750 {
752 current, 1000,
753 m_hd ? 32 : 16))
754 {
755 LOG(VB_AUDIO, LOG_ERR, "AddData() Audio buffer "
756 "overflow, audio data lost!");
757 }
758 std::this_thread::sleep_for(m_audioOutput->LengthLastData());
759 }
761 m_audioOutput->Pause(true);
762 std::this_thread::sleep_for(500ms); // .5s pause
763 m_audioOutput->Pause(false);
764 }
765 if (m_channel >= 0)
766 break;
767 }
768 m_audioOutput->Pause(true);
769
770 ::operator delete[] (frames, std::align_val_t(16));
771 }
772 RunEpilog();
773}
774
776{
777 int channels = 2;
778
779 m_channels = gCoreContext->GetNumSetting("TestingChannels", channels);
780 setLabel(tr("Audio Configuration Testing"));
781 setHelpText(tr("Will play a test pattern on all configured "
782 "speakers"));
783
785 m_frontleft->setLabel(tr("Front Left"));
786 m_frontleft->setHelpText(tr("Start front left channel test"));
788 connect(m_frontleft,
790
791 m_frontright = new ButtonStandardSetting(m_channels == 2 ? "1" : "2");
792 m_frontright->setLabel(tr("Front Right"));
793 m_frontright->setHelpText(tr("Start front right channel test"));
795 connect(m_frontright,
797
799 m_rearleft->setLabel(tr("Rear Left"));
800 m_rearleft->setHelpText(tr("Start rear left channel test"));
802 connect(m_rearleft,
804
806 m_rearright->setLabel(tr("Rear Right"));
807 m_rearright->setHelpText(tr("Start rear right channel test"));
809 connect(m_rearright,
811
812 QString lfe;
813 QString surroundleft;
814 if (m_channels == 6)
815 {
816 lfe = "5";
817 surroundleft = "4";
818 }
819 else if (m_channels == 7)
820 {
821 lfe = "6";
822 surroundleft = "5";
823 }
824 else
825 {
826 lfe = "7";
827 surroundleft = "6";
828 }
829
830 m_lfe = new ButtonStandardSetting(lfe);
831 m_lfe->setLabel(tr("LFE"));
832 m_lfe->setHelpText(tr("Start LFE channel test"));
834 connect(m_lfe,
836
837 m_surroundleft = new ButtonStandardSetting(surroundleft);
838 m_surroundleft->setLabel(tr("Surround Left"));
839 m_surroundleft->setHelpText(tr("Start surround left channel test"));
841 connect(m_surroundleft,
843
845 m_surroundright->setLabel(tr("Surround Right"));
846 m_surroundright->setHelpText(tr("Start surround right channel test"));
848 connect(m_surroundright,
850
852 m_center->setLabel(tr("Center"));
853 m_center->setHelpText(tr("Start center channel test"));
855 connect(m_center,
857
859 m_startButton->setLabel(tr("Test All"));
860 m_startButton->setHelpText(tr("Start all channels test"));
863
865 m_hd->setLabel(tr("Use Highest Quality Mode"));
866 m_hd->setHelpText(tr("Use the highest audio quality settings "
867 "supported by your audio card. This will be "
868 "a good place to start troubleshooting "
869 "potential errors"));
870 addChild(m_hd);
871 connect(m_hd, qOverload<const QString&>(&StandardSetting::valueChanged),
873}
874
876{
877 if (m_at)
878 {
879 m_at->cancel();
880 m_at->wait();
881 delete m_at;
882 }
883}
884
885
887 const QString &passthrough,
888 int channels,
889 const AudioOutputSettings &settings)
890{
891 m_main = main;
892 m_passthrough = passthrough;
893 m_channels = channels;
894 m_settings = settings;
901}
902
904{
905 if (this->sender() == m_startButton)
906 {
907 if (m_at && m_at->isRunning())
908 {
909 cancelTest();
910 }
911 else
912 {
913 prepareTest();
914 m_at->setChannel(-1);
915 m_at->start();
916 m_startButton->setLabel(QCoreApplication::translate("(Common)",
917 "Stop"));
918 }
919 return;
920 }
921 if (m_at && m_at->isRunning())
922 {
923 m_at->cancel();
924 m_at->wait();
925 }
926
927 prepareTest();
928
929 int channel = 1;
930
931 if (this->sender() == m_frontleft)
932 {
933 channel = 0;
934 }
935 else if (this->sender() == m_frontright)
936 {
937 channel = (m_channels == 2) ? 1 : 2;
938 }
939 else if (this->sender() == m_rearleft)
940 {
941 channel = 5;
942 }
943 else if (this->sender() == m_rearright)
944 {
945 channel = 4;
946 }
947 else if (this->sender() == m_lfe)
948 {
949 if (m_channels == 6)
950 channel = 5;
951 else if (m_channels == 7)
952 channel = 6;
953 else
954 channel = 7;
955 }
956 else if (this->sender() == m_surroundleft)
957 {
958 if (m_channels == 6)
959 channel = 4;
960 else if (m_channels == 7)
961 channel = 5;
962 else
963 channel = 6;
964 }
965 else if (this->sender() == m_surroundright)
966 {
967 channel = 3;
968 }
969 else if (this->sender() == m_center)
970 {
971 channel = 1;
972 }
973
974 m_at->setChannel(channel);
975
976 m_at->start();
977}
978
979void AudioTest::togglequality(const QString &/*value*/)
980{
981 cancelTest();
983}
984
986{
987 if (m_at && m_at->isRunning())
988 {
989 m_at->cancel();
990 m_startButton->setLabel(tr("Test All"));
991 }
992}
993
995{
996 cancelTest();
997 if (m_at)
998 {
999 m_at->cancel();
1000 m_at->wait();
1001 delete m_at;
1002 }
1003
1006 if (!m_at->isOutputOpen())
1007 {
1008 QString msg = tr("Audio device is invalid or not useable.");
1010
1011 auto *mcd = new MythConfirmationDialog(mainStack,
1012 msg, false);
1013
1014 if (mcd->Create())
1015 mainStack->AddScreen(mcd);
1016 else
1017 delete mcd;
1018 }
1019}
1020
1021bool AudioTest::event(QEvent *event)
1022{
1024 return QObject::event(event); //not handled
1025
1026 auto *cce = dynamic_cast<ChannelChangedEvent*>(event);
1027 if (cce == nullptr)
1028 return GroupSetting::event(event);
1029
1030 QString channel = cce->m_channel;
1031
1032 if (!cce->m_fulltest)
1033 return GroupSetting::event(event);
1034
1035 bool fl = false;
1036 bool fr = false;
1037 bool c = false;
1038 bool lfe = false;
1039 bool sl = false;
1040 bool sr = false;
1041 bool rl = false;
1042 bool rr = false;
1043
1044 if (channel == "frontleft")
1045 {
1046 fl = true;
1047 }
1048 else if (channel == "frontright")
1049 {
1050 fr = true;
1051 }
1052 else if (channel == "center")
1053 {
1054 c = true;
1055 }
1056 else if (channel == "lfe")
1057 {
1058 lfe = true;
1059 }
1060 else if (channel == "surroundleft")
1061 {
1062 sl = true;
1063 }
1064 else if (channel == "surroundright")
1065 {
1066 sr = true;
1067 }
1068 else if (channel == "rearleft")
1069 {
1070 rl = true;
1071 }
1072 else if (channel == "rearright")
1073 {
1074 rr = true;
1075 }
1076 if (m_frontleft)
1078 if (m_frontright)
1080 if (m_center)
1081 m_center->setEnabled(c);
1082 if (m_surroundleft)
1084 if (m_surroundright)
1086 if (m_rearleft)
1088 if (m_rearright)
1090 if (m_lfe)
1091 m_lfe->setEnabled(lfe);
1092 return GroupSetting::event(event);
1093}
1094
1095
1097{
1098 auto *gc = new HostCheckBoxSetting("MythControlsVolume");
1099
1100 gc->setLabel(tr("Use internal volume controls"));
1101
1102 gc->setValue(true);
1103
1104 gc->setHelpText(tr("If enabled, MythTV will control the PCM and "
1105 "master mixer volume. Disable this option if you "
1106 "prefer to control the volume externally (for "
1107 "example, using your amplifier) or if you use an "
1108 "external mixer program."));
1109
1110 gc->addTargetedChild("1", MixerDevice());
1111 gc->addTargetedChild("1", MixerControl());
1112 gc->addTargetedChild("1", MixerVolume());
1113 gc->addTargetedChild("1", PCMVolume());
1114 return gc;
1115}
1116
1118{
1119 auto *gc = new HostComboBoxSetting("MixerDevice", true);
1120 gc->setLabel(tr("Mixer device"));
1121
1122#if CONFIG_AUDIO_OSS
1123 QDir dev("/dev", "mixer*", QDir::Name, QDir::System);
1124 fillSelectionsFromDir(gc, dev);
1125
1126 dev.setPath("/dev/sound");
1127 if (dev.exists())
1128 {
1129 fillSelectionsFromDir(gc, dev);
1130 }
1131#endif
1132#if CONFIG_AUDIO_ALSA
1133 gc->addSelection("ALSA:default", "ALSA:default");
1134#endif
1135#ifdef Q_OS_WINDOWS
1136 gc->addSelection("DirectX:", "DirectX:");
1137 gc->addSelection("Windows:", "Windows:");
1138#endif
1139#ifdef Q_OS_ANDROID
1140 gc->addSelection("OpenSLES:", "OpenSLES:");
1141#endif
1142#ifndef Q_OS_WINDOWS
1143 gc->addSelection(tr("software"), "software");
1144#endif
1145
1146 gc->setHelpText(tr("Setting the mixer device to \"%1\" lets MythTV control "
1147 "the volume of all audio at the expense of a slight "
1148 "quality loss.")
1149 .arg(tr("software")));
1150
1151 return gc;
1152}
1153
1154const std::array<const char *,2> AudioConfigSettings::kMixerControlControls
1155 {QT_TR_NOOP("PCM"),
1156 QT_TR_NOOP("Master")};
1157
1159{
1160 auto *gc = new HostComboBoxSetting("MixerControl", true);
1161
1162 gc->setLabel(tr("Mixer controls"));
1163
1164 for (const auto & control : kMixerControlControls)
1165 gc->addSelection(tr(control), control);
1166
1167 gc->setHelpText(tr("Changing the volume adjusts the selected mixer."));
1168
1169 return gc;
1170}
1171
1173{
1174 auto *gs = new HostSpinBoxSetting("MasterMixerVolume", 0, 100, 1);
1175
1176 gs->setLabel(tr("Master mixer volume"));
1177
1178 gs->setValue(70);
1179
1180 gs->setHelpText(tr("Initial volume for the Master mixer. This affects "
1181 "all sound created by the audio device. Note: Do not "
1182 "set this too low."));
1183 return gs;
1184}
1185
1187{
1188 auto *gs = new HostSpinBoxSetting("PCMMixerVolume", 0, 100, 1);
1189
1190 gs->setLabel(tr("PCM mixer volume"));
1191
1192 gs->setValue(70);
1193
1194 gs->setHelpText(tr("Initial volume for PCM output. Using the volume "
1195 "keys in MythTV will adjust this parameter."));
1196 return gs;
1197}
1198
1200{
1201 auto *gc = new HostCheckBoxSetting("StereoPCM");
1202
1203 gc->setLabel(tr("Stereo PCM Only"));
1204
1205 gc->setValue(false);
1206
1207 gc->setHelpText(tr("Enable if your amplifier or sound decoder only "
1208 "supports 2 channel PCM (typically an old HDMI 1.0 "
1209 "device). Multichannel audio will be re-encoded to "
1210 "AC-3 when required"));
1211 return gc;
1212}
1213
1215{
1216 auto *gc = new HostCheckBoxSetting("SRCQualityOverride");
1217
1218 gc->setLabel(tr("Override SRC quality"));
1219
1220 gc->setValue(false);
1221
1222 gc->setHelpText(tr("Enable to override audio sample rate "
1223 "conversion quality."));
1224 return gc;
1225}
1226
1228{
1229 auto *gc = new HostComboBoxSetting("SRCQuality", false);
1230
1231 gc->setLabel(tr("Sample rate conversion"));
1232
1233 gc->addSelection(tr("Disabled", "Sample rate conversion"), "-1");
1234 gc->addSelection(tr("Fastest", "Sample rate conversion"), "0");
1235 gc->addSelection(tr("Good", "Sample rate conversion"), "1", true); // default
1236 gc->addSelection(tr("Best", "Sample rate conversion"), "2");
1237
1238 gc->setHelpText(tr("Set the quality of audio sample-rate "
1239 "conversion. \"%1\" (default) provides the best "
1240 "compromise between CPU usage and quality. \"%2\" "
1241 "lets the audio device handle sample-rate conversion.")
1242 .arg(tr("Good", "Sample rate conversion"),
1243 tr("Disabled", "Sample rate conversion")));
1244
1245 return gc;
1246}
1247
1249{
1250 auto *gc = new HostCheckBoxSetting("Audio48kOverride");
1251
1252 gc->setLabel(tr("Force audio device output to 48kHz"));
1253 gc->setValue(false);
1254
1255 gc->setHelpText(tr("Force audio sample rate to 48kHz. Some audio devices "
1256 "will report various rates, but they ultimately "
1257 "crash."));
1258 return gc;
1259}
1260
1262{
1263 auto *gc = new HostCheckBoxSetting("PassThruDeviceOverride");
1264
1265 gc->setLabel(tr("Separate digital output device"));
1266
1267 gc->setValue(false);
1268
1269 gc->setHelpText(tr("Use a distinct digital output device from default. "
1270 "(default is not checked)"));
1271 return gc;
1272}
1273
1275{
1276 auto *gc = new HostComboBoxSetting("PassThruOutputDevice",
1277 true);
1278
1279 gc->setLabel(tr("Digital output device"));
1280 //TODO Is default not equivalent to PassThroughOverride off? if so
1281 //PassThruDeviceOverridedsetting could be removed
1282 gc->addSelection(QCoreApplication::translate("(Common)", "Default"),
1283 "Default");
1284#ifdef Q_OS_WINDOWS
1285 gc->addSelection("DirectX:Primary Sound Driver");
1286#else
1287 gc->addSelection("ALSA:iec958:{ AES0 0x02 }",
1288 "ALSA:iec958:{ AES0 0x02 }");
1289 gc->addSelection("ALSA:hdmi", "ALSA:hdmi");
1290 gc->addSelection("ALSA:plughw:0,3", "ALSA:plughw:0,3");
1291#endif
1292
1293 gc->setHelpText(tr("Audio output device to use for digital audio. This "
1294 "value is currently only used with ALSA and DirectX "
1295 "sound output."));
1296 return gc;
1297}
1298
1300{
1301 auto *gc = new HostCheckBoxSetting("SPDIFRateOverride");
1302
1303 gc->setLabel(tr("SPDIF 48kHz rate override"));
1304
1305 gc->setValue(false);
1306
1307 gc->setHelpText(tr("ALSA only. By default, let ALSA determine the "
1308 "passthrough sampling rate. If checked set the sampling "
1309 "rate to 48kHz for passthrough. (default is not "
1310 "checked)"));
1311 return gc;
1312}
1313
1315{
1316 auto *gc = new HostCheckBoxSetting("HBRPassthru");
1317
1318 gc->setLabel(tr("HBR passthrough support"));
1319
1320 gc->setValue(true);
1321
1322 gc->setHelpText(tr("HBR support is required for TrueHD and DTS-HD "
1323 "passthrough. If unchecked, Myth will limit the "
1324 "passthrough bitrate to 6.144Mbit/s. This will "
1325 "disable True-HD passthrough, however will allow "
1326 "DTS-HD content to be sent as DTS-HD Hi-Res. (default "
1327 "is checked)"));
1328 return gc;
1329}
1330
1332{
1333 m_mpcm->setEnabled(flag);
1334}
1335// 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:247
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
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)
virtual 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
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