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