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
20#include "libmythbase/mythconfig.h"
27
28// MythFrontend
30
31extern "C" {
32#include "libavformat/avformat.h"
33}
34
36 HostComboBoxSetting("AudioOutputDevice", true), m_parent(parent)
37{
38 setLabel(tr("Audio output device"));
39#ifdef Q_OS_ANDROID
40 QString dflt = "OpenSLES:";
41#elif CONFIG_AUDIO_ALSA
42 QString dflt = "ALSA:default";
43#elif CONFIG_AUDIO_PULSEOUTPUT
44 QString dflt = "PulseAudio:default";
45#elif defined(Q_OS_DARWIN)
46 QString dflt = "CoreAudio:";
47#elif defined(_WIN32)
48 QString dflt = "Windows:";
49#else
50 QString dflt = "NULL";
51#endif
52 QString current = gCoreContext->GetSetting(QString("AudioOutputDevice"),
53 dflt);
55
56 connect(this, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
58}
59
61{
64}
65
67{
69
70 if (vect.empty())
71 return;
72
73 QString value = getValue();
75
76 // Adding the current value first avoids marking the setting as changed
77 addSelection(value, value, true);
78 for (const auto & it : std::as_const(vect))
79 {
80 if (value != it.m_name)
81 addSelection(it.m_name, it.m_name);
82 }
83}
84
86{
87 QString desc = m_parent->AudioDeviceMap().value(setting->getValue()).m_desc;
88 setHelpText(desc);
89}
90
92 GroupSetting *groupSetting)
93 : StandardSettingDialog(parent, name, groupSetting)
94{
95}
96
98{
99 SetBusyPopupMessage(tr("Scanning for available audio devices"));
101}
102
104{
106
107 auto *settings = qobject_cast<AudioConfigSettings*>(GetGroupSettings());
108 if (settings == nullptr)
109 return;
110 settings->CheckConfiguration();
111}
112
114 : m_triggerDigital(new GroupSetting()),
115 m_passThroughOverride(PassThroughOverride()),
116 m_passThroughDeviceOverride(PassThroughOutputDevice())
117{
118 setLabel(tr("Audio System"));
119
121 // Rescan button
122
123 auto *rescan = new ButtonStandardSetting("rescan");
124 rescan->setLabel(tr("Rescan"));
125 rescan->setHelpText(tr("Rescan for available audio devices. "
126 "Current entry will be checked and "
127 "capability entries populated."));
128 addChild(rescan);
130
131 // digital settings
132 m_triggerDigital->setLabel(tr("Digital Audio Capabilities"));
139
144
145 //Advanced Settings
146 auto *advancedSettings = new GroupSetting();
147 advancedSettings->setLabel(tr("Advanced Audio Settings"));
148 advancedSettings->setHelpText(tr("Enable extra audio settings. Under most "
149 "usage all options should be left alone"));
150 addChild(advancedSettings);
151 advancedSettings->addChild(m_passThroughOverride);
152 advancedSettings->addChild(m_passThroughDeviceOverride);
156
157 StandardSetting *srcqualityoverride = SRCQualityOverride();
158 srcqualityoverride->addTargetedChild("1", SRCQuality());
159 addChild(srcqualityoverride);
160
161 advancedSettings->addChild(Audio48kOverride());
162#if CONFIG_AUDIO_ALSA
163 advancedSettings->addChild(SPDIFRateOverride());
164#endif
165
166 advancedSettings->addChild(HBRPassthrough());
167
168 advancedSettings->addChild(m_mpcm = MPCM());
169
171
172 // Set slots
173 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
174 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateVisibility));
175 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
176 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
177 connect(m_ac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
178 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilitiesAC3));
179
180 connect(m_dtsPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
181 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
182 connect(m_eac3PassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
183 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
184 connect(m_trueHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
185 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
186 connect(m_dtsHDPassThrough, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
187 this, qOverload<StandardSetting *>(&AudioConfigSettings::UpdateCapabilities));
188 //Slot for audio test
189 connect(m_outputDevice, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
191 connect(m_maxAudioChannels, qOverload<StandardSetting *>(&StandardSetting::valueChanged),
193}
194
196{
197 QString name = m_outputDevice->getValue();
199 AudioOutput::GetAudioDeviceConfig(name, name, true);
200 if (adc)
201 {
202 if (adc->m_settings.IsInvalid())
203 {
204 QString msg = tr("%1 is invalid or not useable.").arg(name);
205
206 ShowOkPopup(msg);
207
208 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
209 .arg(name));
210 }
211 delete adc;
212 }
213
214 if (!CheckPassthrough())
215 {
216 QString pt_name = m_passThroughDeviceOverride->getValue();
217 QString pt_msg = tr("Passthrough device is invalid or not useable. Check "
218 "configuration in Advanced Settings:") +
219 pt_name;
220
221 ShowOkPopup(pt_msg);
222
223 LOG(VB_GENERAL, LOG_ERR, QString("Audio device %1 isn't usable")
224 .arg(pt_name));
225 }
226}
227
229{
231 AudioRescan();
232 // If this is the initial setup where there was nothing on the DB,
233 // set changed so that user can save.
234 if (gCoreContext->GetSetting(QString("AudioOutputDevice"),"").isEmpty())
235 setChanged(true);
236}
237
239{
240 if (!m_slotLock.tryLock())
241 return;
242
244
245 m_audioDevs.clear();
246 for (const auto & dev : std::as_const(*list))
247 m_audioDevs.insert(dev.m_name, dev);
248
249 m_devices = *list;
250 delete list;
251
252 QString name = m_outputDevice->getValue();
253 if (!m_audioDevs.contains(name))
254 {
255 // Scan for possible custom entry that isn't in the list
257 AudioOutput::GetAudioDeviceConfig(name, name, true);
258 m_audioDevs.insert(name, *adc);
259 m_devices.append(*adc);
260 delete adc;
261 }
263 m_slotLock.unlock();
265}
266
268{
270 return;
271
272 int cur_speakers = m_maxAudioChannels->getValue().toInt();
273 m_audioUpmix->setEnabled(cur_speakers > 2);
274 m_audioUpmixType->setEnabled(cur_speakers > 2);
275}
276
278 bool restore, bool AC3)
279{
280 int max_speakers = 8;
281 int realmax_speakers = 8;
282
283 bool invalid = false;
284 QString out;
285
286 if (m_outputDevice)
287 out = m_outputDevice->getValue();
288
289 if (!out.isEmpty())
290 {
291 restore = out != m_lastAudioDevice;
292 m_lastAudioDevice = out;
293 }
294
295 AudioOutputSettings settings;
296 AudioOutputSettings settingsdigital;
297
298 // Test if everything is set yet
302 return settings;
303
304 if (!m_slotLock.tryLock()) // Doing a rescan of channels
305 return settings;
306
307 bool bAC3 = true;
308 //bool bDTS = true;
309 bool bLPCM = true;
310 bool bEAC3 = true;
311 bool bTRUEHD = true;
312 bool bDTSHD = true;
313
314 if (!m_audioDevs.contains(out))
315 {
316 LOG(VB_AUDIO, LOG_ERR, QString("Update not found (%1)").arg(out));
317 invalid = true;
318 }
319 else
320 {
321 bool bForceDigital = m_passThroughOverride->boolValue();
322
323 settings = m_audioDevs.value(out).m_settings;
324 settingsdigital = bForceDigital ?
326 .m_settings : settings;
327
328 realmax_speakers = max_speakers = settings.BestSupportedChannels();
329
330 bAC3 = settingsdigital.canFeature(FEATURE_AC3) &&
332 //bDTS = settingsdigital.canFeature(FEATURE_DTS) &&
333 // m_dtsPassThrough->boolValue();
334 bLPCM = settings.canFeature(FEATURE_LPCM) &&
335 !gCoreContext->GetBoolSetting("StereoPCM", false);
336 bEAC3 = settingsdigital.canFeature(FEATURE_EAC3) &&
337 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
338 bTRUEHD = settingsdigital.canFeature(FEATURE_TRUEHD) &&
339 !gCoreContext->GetBoolSetting("Audio48kOverride", false) &&
340 gCoreContext->GetBoolSetting("HBRPassthru", true);
341 bDTSHD = settingsdigital.canFeature(FEATURE_DTSHD) &&
342 !gCoreContext->GetBoolSetting("Audio48kOverride", false);
343
344 if (max_speakers > 2 && !bLPCM)
345 max_speakers = 2;
346 if (max_speakers == 2 && bAC3)
347 {
348 max_speakers = 6;
349 if (AC3)
350 {
351 restore = true;
352 }
353 }
354 }
355
356 m_triggerDigital->setEnabled(invalid || settingsdigital.canFeature(
362
363 int cur_speakers = m_maxAudioChannels->getValue().toInt();
364 if (cur_speakers > m_maxSpeakers)
365 {
367 }
368 if (restore)
369 {
370 cur_speakers = m_maxSpeakers;
371 }
372
373 if (cur_speakers > max_speakers)
374 {
375 LOG(VB_AUDIO, LOG_INFO, QString("Reset device %1").arg(out));
376 cur_speakers = max_speakers;
377 }
378
379 // Remove everything and re-add available channels
380 bool chansChanged = m_maxAudioChannels->haveChanged();
382 for (int i = 1; i <= max_speakers; i++)
383 {
384 if (invalid || settings.IsSupportedChannels(i) ||
385 settingsdigital.IsSupportedChannels(i))
386 {
387 QString txt;
388
389 switch (i)
390 {
391 case 2:
392 txt = QCoreApplication::translate("(Common)", "Stereo");
393 break;
394 case 6:
395 txt = QCoreApplication::translate("(Common)", "5.1");
396 break;
397 case 8:
398 txt = QCoreApplication::translate("(Common)", "7.1");
399 break;
400 default:
401 continue;
402 }
403 m_maxAudioChannels->addSelection(txt, QString::number(i),
404 i == cur_speakers);
405 }
406 }
407 m_maxAudioChannels->setChanged(chansChanged);
408
409 setMPCMEnabled(settings.canPassthrough() >= 0);
410
411 // Return values is used by audio test
412 // where we mainly are interested by the number of channels
413 // if we support AC3 and/or LPCM
414 settings.SetBestSupportedChannels(cur_speakers);
415 settings.setFeature(bAC3, FEATURE_AC3);
416 settings.setFeature(bLPCM && realmax_speakers > 2, FEATURE_LPCM);
417
418 m_slotLock.unlock();
419 return settings;
420}
421
423{
425}
426
428{
429 return UpdateCapabilities(false, true);
430}
431
433{
435}
436
438{
439 QString name = "MaxChannels";
440
441 auto *gc = new HostComboBoxSetting(name, false);
442
443 gc->setLabel(tr("Speaker configuration"));
444
445 gc->addSelection(QCoreApplication::translate("(Common)", "Stereo"),
446 "2", true); // default
447
448 gc->setHelpText(tr("Select the maximum number of audio "
449 "channels supported by your receiver "
450 "and speakers."));
451 return gc;
452}
453
455{
456 auto *gc = new HostCheckBoxSetting("AudioDefaultUpmix");
457
458 gc->setLabel(tr("Upconvert stereo to 5.1 surround"));
459
460 gc->setValue(true);
461
462 gc->setHelpText(tr("If enabled, MythTV will upconvert stereo "
463 "to 5.1 audio. You can enable or disable "
464 "the upconversion during playback at any time."));
465 return gc;
466}
467
469{
470 auto *gc = new HostComboBoxSetting("AudioUpmixType", false);
471
472 gc->setLabel(tr("Upmix Quality"));
473
474 gc->addSelection(tr("Passive"), "0");
475 gc->addSelection(tr("Hall", "Upmix Quality"), "3");
476 gc->addSelection(tr("Good", "Upmix Quality"), "1");
477 gc->addSelection(tr("Best", "Upmix Quality"), "2", true); // default
478
479 gc->setHelpText(tr("Set the audio surround-upconversion quality."));
480
481 return gc;
482}
483
485{
486 auto *gc = new HostCheckBoxSetting("AC3PassThru");
487
488 gc->setLabel(tr("Dolby Digital"));
489
490 gc->setValue(false);
491
492 gc->setHelpText(tr("Enable if your amplifier or sound decoder "
493 "supports AC-3/Dolby Digital. You must use a digital "
494 "connection. Uncheck if using an analog connection."));
495 return gc;
496}
497
499{
500 auto *gc = new HostCheckBoxSetting("DTSPassThru");
501
502 gc->setLabel(tr("DTS"));
503
504 gc->setValue(false);
505
506 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
507 "DTS. You must use a digital connection. Uncheck "
508 "if using an analog connection"));
509 return gc;
510}
511
513{
514 auto *gc = new HostCheckBoxSetting("EAC3PassThru");
515
516 gc->setLabel(tr("E-AC-3"));
517
518 gc->setValue(false);
519
520 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
521 "E-AC-3 (DD+). You must use a HDMI connection."));
522 return gc;
523}
524
526{
527 auto *gc = new HostCheckBoxSetting("TrueHDPassThru");
528
529 gc->setLabel(tr("TrueHD"));
530
531 gc->setValue(false);
532
533 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
534 "Dolby TrueHD. You must use a HDMI connection."));
535 return gc;
536}
537
539{
540 auto *gc = new HostCheckBoxSetting("DTSHDPassThru");
541
542 gc->setLabel(tr("DTS-HD"));
543
544 gc->setValue(false);
545
546 gc->setHelpText(tr("Enable if your amplifier or sound decoder supports "
547 "DTS-HD. You must use a HDMI connection."));
548 return gc;
549}
550
552{
553 bool ok = true;
554
556 {
557 QString name = m_passThroughDeviceOverride->getValue();
559 AudioOutput::GetAudioDeviceConfig(name, name, true);
560 if (adc->m_settings.IsInvalid())
561 {
562 LOG(VB_GENERAL, LOG_ERR,
563 QString("Passthru device %1 isn't usable "
564 "Check audio configuration").arg(name));
565 ok = false;
566 }
567 // add it to list of known devices
568 m_audioDevs.insert(name, *adc);
569 m_devices.append(*adc);
570 delete adc;
571 }
572 return ok;
573}
574
575#if CONFIG_AUDIO_OSS
576static void fillSelectionsFromDir(HostComboBoxSetting *comboBox,
577 const QDir& dir, bool absPath = true)
578
579{
580 QFileInfoList entries = dir.entryInfoList();
581 for (const auto & fi : std::as_const(entries))
582 {
583 if (absPath)
584 comboBox->addSelection( fi.absoluteFilePath() );
585 else
586 comboBox->addSelection( fi.fileName() );
587 }
588}
589#endif
590
592{
593 AudioOutputSettings settings = UpdateCapabilities(false);
594 QString out = m_outputDevice->getValue();
595 QString passthrough =
598 int channels = m_maxAudioChannels->getValue().toInt();
599
600 m_audioTest->UpdateCapabilities(out, passthrough, channels, settings);
601}
602
603ChannelChangedEvent::ChannelChangedEvent(QString channame, bool fulltest)
604 : QEvent(kEventType),
605 m_channel(std::move(channame)),
606 m_fulltest(fulltest)
607{
608}
609
611 QString main, QString passthrough,
612 int channels,
613 AudioOutputSettings &settings,
614 bool hd) :
615 MThread("AudioTest"),
616 m_parent(parent), m_channels(channels), m_device(std::move(main)),
617 m_passthrough(std::move(passthrough)), m_hd(hd),
618 m_samplerate(hd ? settings.BestSupportedRate() : 48000),
619 m_format(hd ? settings.BestSupportedFormat() : FORMAT_S16)
620{
623 AV_CODEC_ID_NONE, m_samplerate,
625 true, false, 0, &settings);
626 if (result().isEmpty())
627 {
628 m_audioOutput->Pause(true);
629 }
630}
631
632const QEvent::Type ChannelChangedEvent::kEventType =
633 static_cast<QEvent::Type>(QEvent::registerEventType());
634
636{
637 cancel();
638 wait();
639 delete m_audioOutput;
640}
641
643{
644 m_interrupted = true;
645}
646
648{
649 QString errMsg;
650 if (!m_audioOutput)
651 errMsg = tr("Unable to create AudioOutput.");
652 else
653 errMsg = m_audioOutput->GetError();
654 return errMsg;
655}
656
658{
659 m_channel = channel;
660}
661
663{
664 RunProlog();
665 m_interrupted = false;
666 std::array<std::array<int,8>,7> smptelayout {{
667 { 0, 1, 1 }, //stereo
668 { }, //not used
669 { }, //not used
670 { 0, 2, 1, 4, 3 }, //5.0
671 { 0, 2, 1, 5, 4, 3 }, //5.1
672 { 0, 2, 1, 6, 4, 5, 3 }, //6.1
673 { 0, 2, 1, 7, 5, 4, 6, 3 }, //7.1
674 }};
675
676 if (m_audioOutput && (m_audioOutput->GetError().isEmpty()))
677 {
678 char *frames = new (std::align_val_t(16)) char[m_channels * 1024_UZ * sizeof(int32_t)];
679
680 m_audioOutput->Pause(false);
681
682 int begin = 0;
683 int end = m_channels;
684 if (m_channel >= 0)
685 {
686 begin = m_channel;
687 end = m_channel + 1;
688 }
689 while (!m_interrupted)
690 {
691 for (int i = begin; i < end && !m_interrupted; i++)
692 {
693 int current = smptelayout[m_channels - 2][i];
694
695 if (m_parent)
696 {
697 QString channel;
698
699 switch(current)
700 {
701 case 0:
702 channel = "frontleft";
703 break;
704 case 1:
705 channel = "frontright";
706 break;
707 case 2:
708 channel = "center";
709 break;
710 case 3:
711 channel = "lfe";
712 break;
713 case 4:
714 if (m_channels == 6)
715 channel = "surroundleft";
716 else if (m_channels == 7)
717 channel = "rearright";
718 else
719 channel = "rearleft";
720 break;
721 case 5:
722 if (m_channels == 6)
723 channel = "surroundright";
724 else if (m_channels == 7)
725 channel = "surroundleft";
726 else
727 channel = "rearright";
728 break;
729 case 6:
730 if (m_channels == 7)
731 channel = "surroundright";
732 else
733 channel = "surroundleft";
734 break;
735 case 7:
736 channel = "surroundright";
737 break;
738 }
739 QCoreApplication::postEvent(
740 m_parent, new ChannelChangedEvent(channel,
741 m_channel < 0));
742 LOG(VB_AUDIO, LOG_INFO, QString("AudioTest: %1 (%2->%3)")
743 .arg(channel).arg(i).arg(current));
744 }
745
746 // play sample sound for about 3s
747 int top = m_samplerate / 1000 * 3;
748 for (int j = 0; j < top && !m_interrupted; j++)
749 {
751 current, 1000,
752 m_hd ? 32 : 16);
753 if (!m_audioOutput->AddFrames(frames, 1000 , -1ms))
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->result().isEmpty())
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 _WIN32
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#if !defined(_WIN32)
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 _WIN32
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.
static char * GeneratePinkFrames(char *frames, int channels, int channel, int count, int bits=16)
AudioOutputSettings m_settings
Definition: audiooutput.h:34
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:60
virtual std::chrono::milliseconds LengthLastData(void) const
Definition: audiooutput.h:129
static ADCVect * GetOutputList(void)
QVector< AudioDeviceConfig > ADCVect
Definition: audiooutput.h:47
static AudioDeviceConfig * GetAudioDeviceConfig(QString &name, const QString &desc, bool willsuspendpa=false)
virtual bool AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode)=0
Add frames to the audiobuffer for playback.
virtual void Pause(bool paused)=0
QString GetError(void) const
Definition: audiooutput.h:146
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