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 cancelTest();
909 else
910 {
911 prepareTest();
912 m_at->setChannel(-1);
913 m_at->start();
914 m_startButton->setLabel(QCoreApplication::translate("(Common)",
915 "Stop"));
916 }
917 return;
918 }
919 if (m_at && m_at->isRunning())
920 {
921 m_at->cancel();
922 m_at->wait();
923 }
924
925 prepareTest();
926
927 int channel = 1;
928
929 if (this->sender() == m_frontleft)
930 channel = 0;
931 else if (this->sender() == m_frontright)
932 channel = (m_channels == 2) ? 1 : 2;
933 else if (this->sender() == m_rearleft)
934 channel = 5;
935 else if (this->sender() == m_rearright)
936 channel = 4;
937 else if (this->sender() == m_lfe)
938 {
939 if (m_channels == 6)
940 channel = 5;
941 else if (m_channels == 7)
942 channel = 6;
943 else
944 channel = 7;
945 }
946 else if (this->sender() == m_surroundleft)
947 {
948 if (m_channels == 6)
949 channel = 4;
950 else if (m_channels == 7)
951 channel = 5;
952 else
953 channel = 6;
954 }
955 else if (this->sender() == m_surroundright)
956 {
957 channel = 3;
958 }
959 else if (this->sender() == m_center)
960 {
961 channel = 1;
962 }
963
964 m_at->setChannel(channel);
965
966 m_at->start();
967}
968
969void AudioTest::togglequality(const QString &/*value*/)
970{
971 cancelTest();
973}
974
976{
977 if (m_at && m_at->isRunning())
978 {
979 m_at->cancel();
980 m_startButton->setLabel(tr("Test All"));
981 }
982}
983
985{
986 cancelTest();
987 if (m_at)
988 {
989 m_at->cancel();
990 m_at->wait();
991 delete m_at;
992 }
993
996 if (!m_at->isOutputOpen())
997 {
998 QString msg = tr("Audio device is invalid or not useable.");
1000
1001 auto *mcd = new MythConfirmationDialog(mainStack,
1002 msg, false);
1003
1004 if (mcd->Create())
1005 mainStack->AddScreen(mcd);
1006 else
1007 delete mcd;
1008 }
1009}
1010
1011bool AudioTest::event(QEvent *event)
1012{
1014 return QObject::event(event); //not handled
1015
1016 auto *cce = dynamic_cast<ChannelChangedEvent*>(event);
1017 if (cce == nullptr)
1018 return GroupSetting::event(event);
1019
1020 QString channel = cce->m_channel;
1021
1022 if (!cce->m_fulltest)
1023 return GroupSetting::event(event);
1024
1025 bool fl = false;
1026 bool fr = false;
1027 bool c = false;
1028 bool lfe = false;
1029 bool sl = false;
1030 bool sr = false;
1031 bool rl = false;
1032 bool rr = false;
1033
1034 if (channel == "frontleft")
1035 {
1036 fl = true;
1037 }
1038 else if (channel == "frontright")
1039 {
1040 fr = true;
1041 }
1042 else if (channel == "center")
1043 {
1044 c = true;
1045 }
1046 else if (channel == "lfe")
1047 {
1048 lfe = true;
1049 }
1050 else if (channel == "surroundleft")
1051 {
1052 sl = true;
1053 }
1054 else if (channel == "surroundright")
1055 {
1056 sr = true;
1057 }
1058 else if (channel == "rearleft")
1059 {
1060 rl = true;
1061 }
1062 else if (channel == "rearright")
1063 {
1064 rr = true;
1065 }
1066 if (m_frontleft)
1068 if (m_frontright)
1070 if (m_center)
1071 m_center->setEnabled(c);
1072 if (m_surroundleft)
1074 if (m_surroundright)
1076 if (m_rearleft)
1078 if (m_rearright)
1080 if (m_lfe)
1081 m_lfe->setEnabled(lfe);
1082 return GroupSetting::event(event);
1083}
1084
1085
1087{
1088 auto *gc = new HostCheckBoxSetting("MythControlsVolume");
1089
1090 gc->setLabel(tr("Use internal volume controls"));
1091
1092 gc->setValue(true);
1093
1094 gc->setHelpText(tr("If enabled, MythTV will control the PCM and "
1095 "master mixer volume. Disable this option if you "
1096 "prefer to control the volume externally (for "
1097 "example, using your amplifier) or if you use an "
1098 "external mixer program."));
1099
1100 gc->addTargetedChild("1", MixerDevice());
1101 gc->addTargetedChild("1", MixerControl());
1102 gc->addTargetedChild("1", MixerVolume());
1103 gc->addTargetedChild("1", PCMVolume());
1104 return gc;
1105}
1106
1108{
1109 auto *gc = new HostComboBoxSetting("MixerDevice", true);
1110 gc->setLabel(tr("Mixer device"));
1111
1112#if CONFIG_AUDIO_OSS
1113 QDir dev("/dev", "mixer*", QDir::Name, QDir::System);
1114 fillSelectionsFromDir(gc, dev);
1115
1116 dev.setPath("/dev/sound");
1117 if (dev.exists())
1118 {
1119 fillSelectionsFromDir(gc, dev);
1120 }
1121#endif
1122#if CONFIG_AUDIO_ALSA
1123 gc->addSelection("ALSA:default", "ALSA:default");
1124#endif
1125#ifdef Q_OS_WINDOWS
1126 gc->addSelection("DirectX:", "DirectX:");
1127 gc->addSelection("Windows:", "Windows:");
1128#endif
1129#ifdef Q_OS_ANDROID
1130 gc->addSelection("OpenSLES:", "OpenSLES:");
1131#endif
1132#ifndef Q_OS_WINDOWS
1133 gc->addSelection(tr("software"), "software");
1134#endif
1135
1136 gc->setHelpText(tr("Setting the mixer device to \"%1\" lets MythTV control "
1137 "the volume of all audio at the expense of a slight "
1138 "quality loss.")
1139 .arg(tr("software")));
1140
1141 return gc;
1142}
1143
1144const std::array<const char *,2> AudioConfigSettings::kMixerControlControls
1145 {QT_TR_NOOP("PCM"),
1146 QT_TR_NOOP("Master")};
1147
1149{
1150 auto *gc = new HostComboBoxSetting("MixerControl", true);
1151
1152 gc->setLabel(tr("Mixer controls"));
1153
1154 for (const auto & control : kMixerControlControls)
1155 gc->addSelection(tr(control), control);
1156
1157 gc->setHelpText(tr("Changing the volume adjusts the selected mixer."));
1158
1159 return gc;
1160}
1161
1163{
1164 auto *gs = new HostSpinBoxSetting("MasterMixerVolume", 0, 100, 1);
1165
1166 gs->setLabel(tr("Master mixer volume"));
1167
1168 gs->setValue(70);
1169
1170 gs->setHelpText(tr("Initial volume for the Master mixer. This affects "
1171 "all sound created by the audio device. Note: Do not "
1172 "set this too low."));
1173 return gs;
1174}
1175
1177{
1178 auto *gs = new HostSpinBoxSetting("PCMMixerVolume", 0, 100, 1);
1179
1180 gs->setLabel(tr("PCM mixer volume"));
1181
1182 gs->setValue(70);
1183
1184 gs->setHelpText(tr("Initial volume for PCM output. Using the volume "
1185 "keys in MythTV will adjust this parameter."));
1186 return gs;
1187}
1188
1190{
1191 auto *gc = new HostCheckBoxSetting("StereoPCM");
1192
1193 gc->setLabel(tr("Stereo PCM Only"));
1194
1195 gc->setValue(false);
1196
1197 gc->setHelpText(tr("Enable if your amplifier or sound decoder only "
1198 "supports 2 channel PCM (typically an old HDMI 1.0 "
1199 "device). Multichannel audio will be re-encoded to "
1200 "AC-3 when required"));
1201 return gc;
1202}
1203
1205{
1206 auto *gc = new HostCheckBoxSetting("SRCQualityOverride");
1207
1208 gc->setLabel(tr("Override SRC quality"));
1209
1210 gc->setValue(false);
1211
1212 gc->setHelpText(tr("Enable to override audio sample rate "
1213 "conversion quality."));
1214 return gc;
1215}
1216
1218{
1219 auto *gc = new HostComboBoxSetting("SRCQuality", false);
1220
1221 gc->setLabel(tr("Sample rate conversion"));
1222
1223 gc->addSelection(tr("Disabled", "Sample rate conversion"), "-1");
1224 gc->addSelection(tr("Fastest", "Sample rate conversion"), "0");
1225 gc->addSelection(tr("Good", "Sample rate conversion"), "1", true); // default
1226 gc->addSelection(tr("Best", "Sample rate conversion"), "2");
1227
1228 gc->setHelpText(tr("Set the quality of audio sample-rate "
1229 "conversion. \"%1\" (default) provides the best "
1230 "compromise between CPU usage and quality. \"%2\" "
1231 "lets the audio device handle sample-rate conversion.")
1232 .arg(tr("Good", "Sample rate conversion"),
1233 tr("Disabled", "Sample rate conversion")));
1234
1235 return gc;
1236}
1237
1239{
1240 auto *gc = new HostCheckBoxSetting("Audio48kOverride");
1241
1242 gc->setLabel(tr("Force audio device output to 48kHz"));
1243 gc->setValue(false);
1244
1245 gc->setHelpText(tr("Force audio sample rate to 48kHz. Some audio devices "
1246 "will report various rates, but they ultimately "
1247 "crash."));
1248 return gc;
1249}
1250
1252{
1253 auto *gc = new HostCheckBoxSetting("PassThruDeviceOverride");
1254
1255 gc->setLabel(tr("Separate digital output device"));
1256
1257 gc->setValue(false);
1258
1259 gc->setHelpText(tr("Use a distinct digital output device from default. "
1260 "(default is not checked)"));
1261 return gc;
1262}
1263
1265{
1266 auto *gc = new HostComboBoxSetting("PassThruOutputDevice",
1267 true);
1268
1269 gc->setLabel(tr("Digital output device"));
1270 //TODO Is default not equivalent to PassThroughOverride off? if so
1271 //PassThruDeviceOverridedsetting could be removed
1272 gc->addSelection(QCoreApplication::translate("(Common)", "Default"),
1273 "Default");
1274#ifdef Q_OS_WINDOWS
1275 gc->addSelection("DirectX:Primary Sound Driver");
1276#else
1277 gc->addSelection("ALSA:iec958:{ AES0 0x02 }",
1278 "ALSA:iec958:{ AES0 0x02 }");
1279 gc->addSelection("ALSA:hdmi", "ALSA:hdmi");
1280 gc->addSelection("ALSA:plughw:0,3", "ALSA:plughw:0,3");
1281#endif
1282
1283 gc->setHelpText(tr("Audio output device to use for digital audio. This "
1284 "value is currently only used with ALSA and DirectX "
1285 "sound output."));
1286 return gc;
1287}
1288
1290{
1291 auto *gc = new HostCheckBoxSetting("SPDIFRateOverride");
1292
1293 gc->setLabel(tr("SPDIF 48kHz rate override"));
1294
1295 gc->setValue(false);
1296
1297 gc->setHelpText(tr("ALSA only. By default, let ALSA determine the "
1298 "passthrough sampling rate. If checked set the sampling "
1299 "rate to 48kHz for passthrough. (default is not "
1300 "checked)"));
1301 return gc;
1302}
1303
1305{
1306 auto *gc = new HostCheckBoxSetting("HBRPassthru");
1307
1308 gc->setLabel(tr("HBR passthrough support"));
1309
1310 gc->setValue(true);
1311
1312 gc->setHelpText(tr("HBR support is required for TrueHD and DTS-HD "
1313 "passthrough. If unchecked, Myth will limit the "
1314 "passthrough bitrate to 6.144Mbit/s. This will "
1315 "disable True-HD passthrough, however will allow "
1316 "DTS-HD content to be sent as DTS-HD Hi-Res. (default "
1317 "is checked)"));
1318 return gc;
1319}
1320
1322{
1323 m_mpcm->setEnabled(flag);
1324}
1325// 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
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