diff --git a/mythtv/libs/libmyth/audio/audiooutpututil.cpp b/mythtv/libs/libmyth/audio/audiooutpututil.cpp index d9af347..2d096d2 100644 --- a/mythtv/libs/libmyth/audio/audiooutpututil.cpp +++ b/mythtv/libs/libmyth/audio/audiooutpututil.cpp @@ -3,6 +3,8 @@ using namespace std; #include "mythconfig.h" #include "audiooutpututil.h" +#include +#include #define LOC QString("AO: ") #define LOC_ERR QString("AO, ERROR: ") @@ -629,3 +631,41 @@ void AudioOutputUtil::MuteChannel(int obits, int channels, int ch, else _MuteChannel((int *)buffer, channels, ch, frames); } + +#if HAVE_BIGENDIAN +#define LE_SHORT(v) bswap_16(v) +#else +#define LE_SHORT(v) (v) +#endif + +char *AudioOutputUtil::GeneratePinkSamples(char *frames, int channels, + int channel, + int count) +{ + pink_noise_t pink; + + initialize_pink_noise(&pink, 16); + + double res; + int32_t ires; + int16_t *samp16 = (int16_t*) frames; + + while (count-- > 0) + { + for(int chn = 0 ; chn < channels; chn++) + { + if (chn==channel) + { + res = generate_pink_noise_sample(&pink) * 0x03fffffff; /* Don't use MAX volume */ + ires = res; + *samp16++ = LE_SHORT(ires >> 16); + } + else + { + *samp16++ = 0; + } + } + } + return frames; +} + diff --git a/mythtv/libs/libmyth/audio/audiooutpututil.h b/mythtv/libs/libmyth/audio/audiooutpututil.h index 858fa6e..8262f8f 100644 --- a/mythtv/libs/libmyth/audio/audiooutpututil.h +++ b/mythtv/libs/libmyth/audio/audiooutpututil.h @@ -4,6 +4,9 @@ using namespace std; #include "mythverbose.h" #include "audiooutputsettings.h" +extern "C" { +#include "pink.h" +} class MPUBLIC AudioOutputUtil { @@ -16,6 +19,8 @@ class MPUBLIC AudioOutputUtil bool music, bool upmix); static void MuteChannel(int obits, int channels, int ch, void *buffer, int bytes); + static char *GeneratePinkSamples(char *frames, int channels, + int channel, int count); }; #endif diff --git a/mythtv/libs/libmyth/audio/pink.c b/mythtv/libs/libmyth/audio/pink.c new file mode 100644 index 0000000..ef30755 --- /dev/null +++ b/mythtv/libs/libmyth/audio/pink.c @@ -0,0 +1,96 @@ +/* + patest_pink.c + + generate Pink Noise using Gardner method. + Optimization suggested by James McCartney uses a tree + to select which random value to replace. + + x x x x x x x x x x x x x x x x + x x x x x x x x + x x x x + x x + x + + Tree is generated by counting trailing zeros in an increasing index. + When the index is zero, no random number is selected. + + This program uses the Portable Audio library which is under development. + For more information see: http://www.audiomulch.com/portaudio/ + + Author: Phil Burk, http://www.softsynth.com + + Revision History: + + Copyleft 1999 Phil Burk - No rights reserved. +*/ + +#include +#include +#include "pink.h" + +/************************************************************/ +/* Calculate pseudo-random 32 bit number based on linear congruential method. */ +static unsigned long generate_random_number( void ) +{ + static unsigned long rand_seed = 22222; /* Change this for different random sequences. */ + rand_seed = (rand_seed * 196314165) + 907633515; + return rand_seed; +} + +/* Setup PinkNoise structure for N rows of generators. */ +void initialize_pink_noise( pink_noise_t *pink, int num_rows ) +{ + int i; + long pmax; + pink->pink_index = 0; + pink->pink_index_mask = (1<pink_scalar = 1.0f / pmax; +/* Initialize rows. */ + for( i=0; ipink_rows[i] = 0; + pink->pink_running_sum = 0; +} + +/* generate Pink noise values between -1.0 and +1.0 */ +float generate_pink_noise_sample( pink_noise_t *pink ) +{ + long new_random; + long sum; + float output; + +/* Increment and mask index. */ + pink->pink_index = (pink->pink_index + 1) & pink->pink_index_mask; + +/* If index is zero, don't update any random values. */ + if( pink->pink_index != 0 ) + { + /* Determine how many trailing zeros in PinkIndex. */ + /* This algorithm will hang if n==0 so test first. */ + int num_zeros = 0; + int n = pink->pink_index; + while( (n & 1) == 0 ) + { + n = n >> 1; + num_zeros++; + } + + /* Replace the indexed ROWS random value. + * Subtract and add back to Running_sum instead of adding all the random + * values together. Only one changes each time. + */ + pink->pink_running_sum -= pink->pink_rows[num_zeros]; + new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT; + pink->pink_running_sum += new_random; + pink->pink_rows[num_zeros] = new_random; + } + +/* Add extra white noise value. */ + new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT; + sum = pink->pink_running_sum + new_random; + +/* Scale to range of -1.0 to 0.9999. */ + output = pink->pink_scalar * sum; + + return output; +} diff --git a/mythtv/libs/libmyth/audio/pink.h b/mythtv/libs/libmyth/audio/pink.h new file mode 100644 index 0000000..510bd33 --- /dev/null +++ b/mythtv/libs/libmyth/audio/pink.h @@ -0,0 +1,22 @@ +#ifndef AUDIOPINK +#define AUDIOPINK + +#include "mythexp.h" + +#define PINK_MAX_RANDOM_ROWS (30) +#define PINK_RANDOM_BITS (24) +#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) + +typedef struct +{ + long pink_rows[PINK_MAX_RANDOM_ROWS]; + long pink_running_sum; /* Used to optimize summing of generators. */ + int pink_index; /* Incremented each sample. */ + int pink_index_mask; /* Index wrapped by ANDing with this mask. */ + float pink_scalar; /* Used to scale within range of -1.0 to +1.0 */ +} pink_noise_t; + +void initialize_pink_noise( pink_noise_t *pink, int num_rows ); +float generate_pink_noise_sample( pink_noise_t *pink ); + +#endif diff --git a/mythtv/libs/libmyth/libmyth.pro b/mythtv/libs/libmyth/libmyth.pro index 2178699..afa8971 100644 --- a/mythtv/libs/libmyth/libmyth.pro +++ b/mythtv/libs/libmyth/libmyth.pro @@ -14,7 +14,7 @@ QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2) HEADERS += audio/audiooutput.h audio/audiooutputbase.h audio/audiooutputnull.h HEADERS += audio/audiooutpututil.h audio/audiooutputdownmix.h HEADERS += audio/audiooutputdigitalencoder.h audio/audiosettings.h -HEADERS += audio/audiooutputsettings.h +HEADERS += audio/audiooutputsettings.h audio/pink.h HEADERS += backendselect.h dbsettings.h dialogbox.h HEADERS += generictree.h langsettings.h HEADERS += managedlist.h mythconfigdialogs.h mythconfiggroups.h @@ -39,7 +39,7 @@ HEADERS += rssparse.h netutils.h # remove when everything is switched to mythui HEADERS += virtualkeyboard_qt.h -SOURCES += audio/audiooutput.cpp audio/audiooutputbase.cpp +SOURCES += audio/audiooutput.cpp audio/audiooutputbase.cpp audio/pink.c SOURCES += audio/audiooutputnull.cpp audio/audiooutputdigitalencoder.cpp SOURCES += audio/audiooutpututil.cpp audio/audiooutputdownmix.cpp SOURCES += audio/audiosettings.cpp audio/audiooutputsettings.cpp diff --git a/mythtv/programs/mythfrontend/audiosettings.cpp b/mythtv/programs/mythfrontend/audiosettings.cpp new file mode 100644 index 0000000..0e19f97 --- /dev/null +++ b/mythtv/programs/mythfrontend/audiosettings.cpp @@ -0,0 +1,804 @@ + +// -*- Mode: c++ -*- + +// Standard UNIX C headers +#include +#include +#include +#include + +// Qt headers +#include +#include +#include +#include +#include +#include + +// MythTV headers +#include "mythconfig.h" +#include "mythcorecontext.h" +#include "mythverbose.h" +#include "audiosettings.h" +#include "audiooutpututil.h" +#include "mythscreenstack.h" +#include "mythprogressdialog.h" +#include "mythdialogbox.h" + +class TriggeredItem : public TriggeredConfigurationGroup +{ + public: + TriggeredItem(Setting *checkbox, ConfigurationGroup *group) : + TriggeredConfigurationGroup(false, false, false, false) + { + setTrigger(checkbox); + + addTarget("1", group); + addTarget("0", new VerticalConfigurationGroup(true)); + } + TriggeredItem(Setting *checkbox, Setting *setting) : + TriggeredConfigurationGroup(false, false, false, false) + { + setTrigger(checkbox); + + addTarget("1", setting); + addTarget("0", new VerticalConfigurationGroup(false, false)); + } +}; + +AudioDeviceComboBox::AudioDeviceComboBox(AudioConfigSettings *parent) : + HostComboBox("AudioOutputDevice", true), m_parent(parent) +{ + setLabel(QObject::tr("Audio output device")); +#ifdef USING_ALSA + QString dflt = "ALSA:default"; +#elif USING_PULSEOUTPUT + QString dflt = "PulseAudio:default"; +#elif CONFIG_DARWIN + QString dflt = "CoreAudio:"; +#elif USING_MINGW + QString dflt = "Windows:"; +#else + QString dflt = "NULL"; +#endif + QString current = gCoreContext->GetSetting(QString("AudioOutputDevice"), + dflt); + addSelection(current, current, true); + + connect(this, SIGNAL(valueChanged(const QString&)), + this, SLOT(AudioDescriptionHelp(const QString&))); +} + +void AudioDeviceComboBox::AudioRescan() +{ + AudioOutput::ADCVect &vect = m_parent->AudioDeviceVect(); + AudioOutput::ADCVect::const_iterator it; + + if (vect.empty()) + return; + + QString value = getValue(); + clearSelections(); + resetMaxCount(vect.size()); + + bool found = false; + for (it = vect.begin(); it != vect.end(); it++) + addSelection(it->name, it->name, + value == it->name ? (found = true) : false); + if (!found) + { + resetMaxCount(vect.size()+1); + addSelection(value, value, true); + } + // For some reason, it adds an empty entry, remove it + removeSelection(QString::null); +} + +void AudioDeviceComboBox::AudioDescriptionHelp(const QString &device) +{ + QString desc = m_parent->AudioDeviceMap().value(device).desc; + setHelpText(desc); +} + +AudioConfigSettings::AudioConfigSettings() : + VerticalConfigurationGroup(false, true, false, false), + m_OutputDevice(NULL), m_MaxAudioChannels(NULL), + m_AudioUpmix(NULL), m_AudioUpmixType(NULL), + m_AC3PassThrough(NULL), m_DTSPassThrough(NULL), + m_EAC3PassThrough(NULL),m_TrueHDPassThrough(NULL), + m_passthrough8(false) +{ + setLabel(QObject::tr("Audio System")); + setUseLabel(false); + + ConfigurationGroup *devicegroup = new HorizontalConfigurationGroup(false, + false); + devicegroup->addChild((m_OutputDevice = new AudioDeviceComboBox(this))); + // Rescan button + TransButtonSetting *rescan = new TransButtonSetting("rescan"); + rescan->setLabel(QObject::tr("Rescan")); + rescan->setHelpText(QObject::tr("Rescan for available audio devices. " + "Current entry will be checked and " + "capability entries populated.")); + devicegroup->addChild(rescan); + connect(rescan, SIGNAL(pressed()), this, SLOT(AudioRescan())); + addChild(devicegroup); + + QString name = m_OutputDevice->getValue(); + AudioOutput::AudioDeviceConfig *adc = + AudioOutput::GetAudioDeviceConfig(name, name, true); + if (adc->settings.IsInvalid()) + VERBOSE(VB_IMPORTANT, QString("Audio device %1 isn't usable " + "Check audio configuration").arg(name)); + audiodevs.insert(name, *adc); + devices.append(*adc); + + delete adc; + CheckPassthrough(); + + ConfigurationGroup *maingroup = new VerticalConfigurationGroup(false, + false); + addChild(maingroup); + + m_triggerDigital = new TransCheckBoxSetting(); + m_AC3PassThrough = AC3PassThrough(); + m_DTSPassThrough = DTSPassThrough(); + m_EAC3PassThrough = EAC3PassThrough(); + m_TrueHDPassThrough = TrueHDPassThrough(); + + m_cgsettings = new HorizontalConfigurationGroup(); + m_cgsettings->setLabel(QObject::tr("Digital Audio Capabilities")); + m_cgsettings->addChild(m_AC3PassThrough); + m_cgsettings->addChild(m_DTSPassThrough); + m_cgsettings->addChild(m_EAC3PassThrough); + m_cgsettings->addChild(m_TrueHDPassThrough); + + TriggeredItem *sub1 = new TriggeredItem(m_triggerDigital, m_cgsettings); + + maingroup->addChild(sub1); + + maingroup->addChild((m_MaxAudioChannels = MaxAudioChannels())); + + TransButtonSetting *advanced = new TransButtonSetting("advanced"); + advanced->setLabel(QObject::tr("Advanced Audio Settings")); + advanced->setHelpText(QObject::tr("Enable extra audio settings. Under most " + "usage all options should be unchecked")); + connect(advanced, SIGNAL(pressed()), this, SLOT(AudioAdvanced())); + addChild(advanced); + + TransButtonSetting *test = new TransButtonSetting("test"); + test->setLabel(QObject::tr("Test")); + test->setHelpText(QObject::tr("Will play a test pattern on all configured " + "speakers")); + connect(test, SIGNAL(pressed()), this, SLOT(AudioTest())); + addChild(test); + + maingroup->addChild((m_AudioUpmix = AudioUpmix())); + maingroup->addChild((m_AudioUpmixType = AudioUpmixType())); + + // Set slots + connect(m_MaxAudioChannels, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateVisibility(const QString&))); + connect(m_OutputDevice, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateCapabilities(const QString&))); + connect(m_AC3PassThrough, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateCapabilities(const QString&))); + connect(m_DTSPassThrough, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateCapabilities(const QString&))); + connect(m_EAC3PassThrough, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateCapabilities(const QString&))); + connect(m_TrueHDPassThrough, SIGNAL(valueChanged(const QString&)), + this, SLOT(UpdateCapabilities(const QString&))); + + AudioRescan(); +} + +void AudioConfigSettings::AudioRescan() +{ + if (!slotlock.tryLock()) + return; + + QVector* list = + AudioOutput::GetOutputList(); + QVector::const_iterator it; + + audiodevs.clear(); + for (it = list->begin(); it != list->end(); it++) + audiodevs.insert(it->name, *it); + + devices = *list; + delete list; + + QString name = m_OutputDevice->getValue(); + if (!audiodevs.contains(name)) + { + // Scan for possible custom entry that isn't in the list + AudioOutput::AudioDeviceConfig *adc = + AudioOutput::GetAudioDeviceConfig(name, name, true); + if (adc->settings.IsInvalid()) + { + QString msg = name + QObject::tr(" is invalid or not useable."); + MythPopupBox::showOkPopup( + GetMythMainWindow(), QObject::tr("Warning"), msg); + VERBOSE(VB_IMPORTANT, QString("Audio device %1 isn't usable ") + .arg(name)); + } + audiodevs.insert(name, *adc); + devices.append(*adc); + delete adc; + } + m_OutputDevice->AudioRescan(); + slotlock.unlock(); + UpdateCapabilities(QString::null); +} + +void AudioConfigSettings::AudioAdvanced() +{ + QString out = m_OutputDevice->getValue(); + bool invalid = false; + AudioOutputSettings settings; + + if (!audiodevs.contains(out)) + { + invalid = true; + } + else + { + settings = audiodevs.value(out).settings; + } + + AudioAdvancedSettingsGroup audiosettings(invalid || + (settings.canLPCM() && + settings.canPassthrough() >= 0)); + + if (audiosettings.exec() == kDialogCodeAccepted) + { + CheckPassthrough(); + UpdateCapabilities(QString::null); + } +} + +class AudioTestThread : public QThread +{ +public: + void run() + { + char *frames_in = new char[m_channels * 48000 * sizeof(int16_t) + 15]; + char *frames = (char *)(((long)frames_in + 15) & ~0xf); + for (int i = 0; i < m_channels; i++) + { + AudioOutputUtil::GeneratePinkSamples(frames, m_channels, + i, 48000); + if (!m_audioOutput->AddFrames(frames, 48000, -1)) + { + VERBOSE(VB_AUDIO, "AddAudioData() " + "Audio buffer overflow, audio data lost!"); + } + if (m_dialog) + { + // Send event here + //QApplication::postEvent(m_dialog, NULL); + } + sleep(1); + } + delete[] frames_in; + } + QString result() + { + QString errMsg; + + if (!m_audioOutput) + errMsg = QObject::tr("Unable to create AudioOutput."); + else + errMsg = m_audioOutput->GetError(); + return errMsg; + } + void setUIWindow(MythUIProgressDialog *dialog) + { + m_dialog = dialog; + } + + AudioTestThread(QString main, QString pass, int channels) + : m_channels(channels), m_dialog(NULL) + { + m_audioOutput = AudioOutput::OpenAudio(main, + pass, + FORMAT_S16, channels, + 0, 48000, + AUDIOOUTPUT_VIDEO, + true, false); + } + ~AudioTestThread() + { + if (m_audioOutput) + delete m_audioOutput; + } + +private: + AudioOutput *m_audioOutput; + int m_channels; + MythUIProgressDialog *m_dialog; +}; + +void AudioConfigSettings::AudioTest() +{ + QString out = m_OutputDevice->getValue(); + QString name; + int channels = m_MaxAudioChannels->getValue().toInt(); + QString errMsg; + + if (gCoreContext->GetNumSetting("PassThruDeviceOverride", false)) + { + name = gCoreContext->GetSetting("PassThruOutputDevice"); + } + AudioTestThread *att = new AudioTestThread(out, name, channels); + + QString result = att->result(); + if (!result.isEmpty()) + { + MythPopupBox::showOkPopup( + GetMythMainWindow(), QObject::tr("Error"), errMsg); + delete att; + att = NULL; + } + else + { + // set MythUI recipient here + att->setUIWindow(NULL); + att->start(); + } + if (att && att->wait()) + { + delete att; + } +} + +bool AudioConfigSettings::CheckPassthrough() +{ + m_passthrough8 = false; + if (gCoreContext->GetNumSetting("PassThruDeviceOverride", false)) + { + QString name = gCoreContext->GetSetting("PassThruOutputDevice"); + AudioOutput::AudioDeviceConfig *adc = + AudioOutput::GetAudioDeviceConfig(name, name, true); + if (adc->settings.IsInvalid()) + { + VERBOSE(VB_IMPORTANT, QString("Passthru device %1 isn't usable " + "Check audio configuration").arg(name)); + } + else + { + if (adc->settings.BestSupportedChannels() >= 8) + { + m_passthrough8 = true; + } + } + delete adc; + } + return m_passthrough8; +} + +void AudioConfigSettings::UpdateVisibility(const QString &device) +{ + if (!m_MaxAudioChannels && !m_AudioUpmix && !m_AudioUpmixType) + return; + + int cur_speakers = m_MaxAudioChannels->getValue().toInt(); + m_AudioUpmix->setEnabled(cur_speakers > 2); + m_AudioUpmixType->setEnabled(cur_speakers > 2); +} + +void AudioConfigSettings::UpdateCapabilities(const QString &device) +{ + int max_speakers = 8; + bool invalid = false; + AudioOutputSettings settings; + + // Test if everything is set yet + if (!m_OutputDevice || !m_MaxAudioChannels || + !m_AC3PassThrough || !m_DTSPassThrough || + !m_EAC3PassThrough || !m_TrueHDPassThrough) + return; + + if (!slotlock.tryLock()) // Doing a rescan of channels + return; + + bool bForceDigital = gCoreContext->GetNumSetting("PassThruDeviceOverride", + false); + + QString out = m_OutputDevice->getValue(); + if (!audiodevs.contains(out)) + { + VERBOSE(VB_AUDIO, QString("Update not found (%1)").arg(out)); + invalid = true; + } + else + { + settings = audiodevs.value(out).settings; + + max_speakers = settings.BestSupportedChannels(); + + bool bAC3 = (settings.canAC3() || bForceDigital) && + m_AC3PassThrough->boolValue(); + bool bDTS = (settings.canDTS() || bForceDigital) && + m_DTSPassThrough->boolValue(); + bool bLPCM = settings.canPassthrough() == -1 || + (settings.canLPCM() && + !gCoreContext->GetNumSetting("StereoPCM", false)); + + if (max_speakers > 2 && !bLPCM) + max_speakers = 2; + if (max_speakers == 2 && (bAC3 || bDTS)) + max_speakers = 6; + } + + m_triggerDigital->setValue(invalid || bForceDigital || + settings.canAC3() || settings.canDTS()); + m_EAC3PassThrough->setEnabled(invalid || m_passthrough8 || + (settings.canPassthrough() >= 0 && + max_speakers >= 8)); + m_TrueHDPassThrough->setEnabled(invalid || m_passthrough8 || + (settings.canPassthrough() >= 0 && + max_speakers >= 8)); + if (m_EAC3PassThrough->boolValue() || m_TrueHDPassThrough->boolValue()) + { + max_speakers = 8; + } + + int cur_speakers = m_MaxAudioChannels->getValue().toInt(); + + if (cur_speakers > max_speakers) + { + VERBOSE(VB_AUDIO, QString("Reset device %1").arg(out)); + cur_speakers = max_speakers; + } + + // Remove everything and re-add available channels + m_MaxAudioChannels->clearSelections(); + m_MaxAudioChannels->resetMaxCount(3); + for (int i = 1; i <= max_speakers; i++) + { + if (invalid || settings.IsSupportedChannels(i) || + (bForceDigital && i >= 6 )) + { + QString txt; + + switch (i) + { + case 2: + txt = QObject::tr("Stereo"); + break; + case 6: + txt = QObject::tr("5.1"); + break; + case 8: + txt = QObject::tr("7.1"); + break; + default: + continue; + } + m_MaxAudioChannels->addSelection(txt, QString::number(i), + i == cur_speakers); + } + } + slotlock.unlock(); +} + +HostComboBox *AudioConfigSettings::MaxAudioChannels() +{ + QString name = "MaxChannels"; + HostComboBox *gc = new HostComboBox(name, false); + gc->setLabel(QObject::tr("Speaker configuration")); + gc->addSelection(QObject::tr("Stereo"), "2", true); // default + gc->setHelpText(QObject::tr("Select the maximum number of audio " + "channels supported by your receiver " + "and speakers.")); + return gc; +} + +HostCheckBox *AudioConfigSettings::AudioUpmix() +{ + HostCheckBox *gc = new HostCheckBox("AudioDefaultUpmix"); + gc->setLabel(QObject::tr("Upconvert stereo to 5.1 surround")); + gc->setValue(true); + gc->setHelpText(QObject::tr("If enabled, MythTV will upconvert stereo " + "to 5.1 audio. You can enable or disable " + "the upconversion during playback at any time.")); + return gc; +} + +HostComboBox *AudioConfigSettings::AudioUpmixType() +{ + HostComboBox *gc = new HostComboBox("AudioUpmixType",false); + gc->setLabel(QObject::tr("Upmix Quality")); + gc->addSelection(QObject::tr("Good"), "1"); + gc->addSelection(QObject::tr("Best"), "2", true); // default + gc->setHelpText(QObject::tr("Set the audio surround-upconversion quality.")); + return gc; +} + +HostCheckBox *AudioConfigSettings::AC3PassThrough() +{ + HostCheckBox *gc = new HostCheckBox("AC3PassThru"); + gc->setLabel(QObject::tr("Dolby Digital")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " + "supports AC3/Dolby Digital. You must use a digital " + "connection. Uncheck if using an analog connection.")); + return gc; +} + +HostCheckBox *AudioConfigSettings::DTSPassThrough() +{ + HostCheckBox *gc = new HostCheckBox("DTSPassThru"); + gc->setLabel(QObject::tr("DTS")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " + "supports DTS. You must use a digital connection. Uncheck " + "if using an analog connection")); + return gc; +} + +HostCheckBox *AudioConfigSettings::EAC3PassThrough() +{ + HostCheckBox *gc = new HostCheckBox("EAC3PassThru"); + gc->setLabel(QObject::tr("E-AC3/DTS-HD")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " + "supports E-AC3 (DD+) or DTS-HD. You must use a hdmi " + "connection.")); + return gc; +} + +HostCheckBox *AudioConfigSettings::TrueHDPassThrough() +{ + HostCheckBox *gc = new HostCheckBox("TrueHDPassThru"); + gc->setLabel(QObject::tr("TrueHD")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " + "supports Dolby TrueHD. You must use a hdmi connection.")); + return gc; +} + +static HostCheckBox *MythControlsVolume() +{ + HostCheckBox *gc = new HostCheckBox("MythControlsVolume"); + gc->setLabel(QObject::tr("Use internal volume controls")); + gc->setValue(true); + gc->setHelpText(QObject::tr("If enabled, MythTV will control the PCM and " + "master mixer volume. Disable this option if you prefer " + "to control the volume externally (for example, using " + "your amplifier) or if you use an external mixer program.")); + return gc; +} + +HostComboBox *AudioMixerSettings::MixerDevice() +{ + HostComboBox *gc = new HostComboBox("MixerDevice", true); + gc->setLabel(QObject::tr("Mixer device")); + +#ifdef USING_OSS + QDir dev("/dev", "mixer*", QDir::Name, QDir::System); + gc->fillSelectionsFromDir(dev); + + dev.setPath("/dev/sound"); + if (dev.exists()) + { + gc->fillSelectionsFromDir(dev); + } +#endif +#ifdef USING_ALSA + gc->addSelection("ALSA:default", "ALSA:default"); +#endif +#ifdef USING_MINGW + gc->addSelection("DirectX:", "DirectX:"); + gc->addSelection("Windows:", "Windows:"); +#endif +#if !defined(USING_MINGW) + gc->addSelection("software", "software"); + gc->setHelpText(QObject::tr("Setting the mixer device to \"software\" " + "lets MythTV control the volume of all audio at the " + "expense of a slight quality loss.")); +#endif + + return gc; +} + +const char* AudioMixerSettings::MixerControlControls[] = { "PCM", + "Master" }; + +HostComboBox *AudioMixerSettings::MixerControl() +{ + HostComboBox *gc = new HostComboBox("MixerControl", true); + gc->setLabel(QObject::tr("Mixer controls")); + for (unsigned int i = 0; i < sizeof(MixerControlControls) / sizeof(char*); + ++i) + { + gc->addSelection(QObject::tr(MixerControlControls[i]), + MixerControlControls[i]); + } + + gc->setHelpText(QObject::tr("Changing the volume adjusts the selected " + "mixer.")); + return gc; +} + +HostSlider *AudioMixerSettings::MixerVolume() +{ + HostSlider *gs = new HostSlider("MasterMixerVolume", 0, 100, 1); + gs->setLabel(QObject::tr("Master mixer volume")); + gs->setValue(70); + gs->setHelpText(QObject::tr("Initial volume for the Master mixer. " + "This affects all sound created by the audio device. " + "Note: Do not set this too low.")); + return gs; +} + +HostSlider *AudioMixerSettings::PCMVolume() +{ + HostSlider *gs = new HostSlider("PCMMixerVolume", 0, 100, 1); + gs->setLabel(QObject::tr("PCM mixer volume")); + gs->setValue(70); + gs->setHelpText(QObject::tr("Initial volume for PCM output. Using the " + "volume keys in MythTV will adjust this parameter.")); + return gs; +} + +AudioMixerSettings::AudioMixerSettings() : + TriggeredConfigurationGroup(false, true, false, false) +{ + setLabel(QObject::tr("Audio Mixer")); + setUseLabel(false); + + Setting *volumeControl = MythControlsVolume(); + addChild(volumeControl); + + // Mixer settings + ConfigurationGroup *settings = + new VerticalConfigurationGroup(false, true, false, false); + settings->addChild(MixerDevice()); + settings->addChild(MixerControl()); + settings->addChild(MixerVolume()); + settings->addChild(PCMVolume()); + + ConfigurationGroup *dummy = + new VerticalConfigurationGroup(false, true, false, false); + + // Show Mixer config only if internal volume controls enabled + setTrigger(volumeControl); + addTarget("0", dummy); + addTarget("1", settings); +} + +AudioGeneralSettings::AudioGeneralSettings() +{ + addChild(new AudioConfigSettings()); + addChild(new AudioMixerSettings()); +} + +HostCheckBox *AudioAdvancedSettings::MPCM() +{ + HostCheckBox *gc = new HostCheckBox("StereoPCM"); + gc->setLabel(QObject::tr("Stereo PCM Only")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " + "only supports 2 channels PCM (typically an old HDMI 1.0 " + "device). Multi-channels audio will be re-encoded to AC3 " + "when required")); + return gc; +} + +HostCheckBox *AudioAdvancedSettings::SRCQualityOverride() +{ + HostCheckBox *gc = new HostCheckBox("SRCQualityOverride"); + gc->setLabel(QObject::tr("Override SRC quality")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Enable to override audio sample rate " + "conversion quality.")); + return gc; +} + +HostComboBox *AudioAdvancedSettings::SRCQuality() +{ + HostComboBox *gc = new HostComboBox("SRCQuality", false); + gc->setLabel(QObject::tr("Sample rate conversion")); + gc->addSelection(QObject::tr("Disabled"), "-1"); + gc->addSelection(QObject::tr("Fastest"), "0"); + gc->addSelection(QObject::tr("Good"), "1", true); // default + gc->addSelection(QObject::tr("Best"), "2"); + gc->setHelpText(QObject::tr("Set the quality of audio sample-rate " + "conversion. \"Good\" (default) provides the best " + "compromise between CPU usage and quality. \"Disabled\" " + "lets the audio device handle sample-rate conversion.")); + return gc; +} + +HostCheckBox *AudioAdvancedSettings::Audio48kOverride() +{ + HostCheckBox *gc = new HostCheckBox("Audio48kOverride"); + gc->setLabel(QObject::tr("Force audio device output to 48kHz")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Force audio sample rate to 48kHz. " + "Some audio devices will report various rates, " + "but they ultimately crash.")); + return gc; +} + +HostCheckBox *AudioAdvancedSettings::PassThroughOverride() +{ + HostCheckBox *gc = new HostCheckBox("PassThruDeviceOverride"); + gc->setLabel(QObject::tr("Separate digital output device")); + gc->setValue(false); + gc->setHelpText(QObject::tr("Use a distinct digital output device from " + "default.")); + return gc; +} + +HostComboBox *AudioAdvancedSettings::PassThroughOutputDevice() +{ + HostComboBox *gc = new HostComboBox("PassThruOutputDevice", true); + + gc->setLabel(QObject::tr("Digital output device")); + gc->addSelection(QObject::tr("Default"), "Default"); +#ifdef USING_MINGW + gc->addSelection("DirectX:Primary Sound Driver"); +#else + gc->addSelection("ALSA:iec958:{ AES0 0x02 }", + "ALSA:iec958:{ AES0 0x02 }"); + gc->addSelection("ALSA:hdmi", "ALSA:hdmi"); + gc->addSelection("ALSA:plughw:0,3", "ALSA:plughw:0,3"); +#endif + + gc->setHelpText(QObject::tr("Deprecated. Audio output device to use for " + "digital audio. This value is currently only used " + "with ALSA and DirectX sound output.")); + return gc; +} + +AudioAdvancedSettings::AudioAdvancedSettings(bool mpcm) +{ + ConfigurationGroup *settings3 = + new HorizontalConfigurationGroup(false, false); + + m_PassThroughOverride = PassThroughOverride(); + TriggeredItem *sub3 = + new TriggeredItem(m_PassThroughOverride, PassThroughOutputDevice()); + settings3->addChild(m_PassThroughOverride); + settings3->addChild(sub3); + + ConfigurationGroup *settings4 = + new HorizontalConfigurationGroup(false, false); + Setting *srcqualityoverride = SRCQualityOverride(); + TriggeredItem *sub4 = + new TriggeredItem(srcqualityoverride, SRCQuality()); + settings4->addChild(srcqualityoverride); + settings4->addChild(sub4); + + ConfigurationGroup *settings5 = + new HorizontalConfigurationGroup(false, false); + settings5->addChild(Audio48kOverride()); + + ConfigurationGroup *settings6; + + if (mpcm) + { + settings6 = new HorizontalConfigurationGroup(false, false); + settings6->addChild(MPCM()); + } + + addChild(settings4); + addChild(settings5); + addChild(settings3); + if (mpcm) + { + addChild(settings6); + } +} + +AudioAdvancedSettingsGroup::AudioAdvancedSettingsGroup(bool mpcm) +{ + addChild(new AudioAdvancedSettings(mpcm)); +} + +// vim:set sw=4 ts=4 expandtab: + diff --git a/mythtv/programs/mythfrontend/audiosettings.h b/mythtv/programs/mythfrontend/audiosettings.h new file mode 100644 index 0000000..037a8be --- /dev/null +++ b/mythtv/programs/mythfrontend/audiosettings.h @@ -0,0 +1,117 @@ +#ifndef MYTHAUDIOSETTINGS_H +#define MYTHAUDIOSETTINGS_H + +#include +#include + +#include "settings.h" +#include "mythcontext.h" + +#include +#include "audiooutput.h" + +class AudioDeviceComboBox; + +class AudioConfigSettings : public VerticalConfigurationGroup +{ + Q_OBJECT + + public: + AudioConfigSettings(); + + typedef QMap ADCMap; + + ADCMap &AudioDeviceMap(void) { return audiodevs; }; + AudioOutput::ADCVect &AudioDeviceVect(void) { return devices; }; + + private slots: + void UpdateVisibility(const QString&); + void UpdateCapabilities(const QString&); + void AudioRescan(); + void AudioAdvanced(); + void AudioTest(); + + private: + AudioDeviceComboBox *OutputDevice(); + HostComboBox *MaxAudioChannels(); + HostCheckBox *AudioUpmix(); + HostComboBox *AudioUpmixType(); + HostCheckBox *AC3PassThrough(); + HostCheckBox *DTSPassThrough(); + HostCheckBox *EAC3PassThrough(); + HostCheckBox *TrueHDPassThrough(); + bool CheckPassthrough(); + + ConfigurationGroup *m_cgsettings; + AudioDeviceComboBox *m_OutputDevice; + HostComboBox *m_MaxAudioChannels; + HostCheckBox *m_AudioUpmix; + HostComboBox *m_AudioUpmixType; + TransCheckBoxSetting *m_triggerDigital; + HostCheckBox *m_AC3PassThrough; + HostCheckBox *m_DTSPassThrough; + HostCheckBox *m_EAC3PassThrough; + HostCheckBox *m_TrueHDPassThrough; + ADCMap audiodevs; + AudioOutput::ADCVect devices; + QMutex slotlock; + bool m_passthrough8; +}; + +class AudioDeviceComboBox : public HostComboBox +{ + Q_OBJECT + public: + AudioDeviceComboBox(AudioConfigSettings*); + void AudioRescan(); + + private slots: + void AudioDescriptionHelp(const QString&); + + private: + AudioConfigSettings *m_parent; +}; + +class AudioMixerSettings : public TriggeredConfigurationGroup +{ + public: + AudioMixerSettings(); + private: + HostComboBox *MixerDevice(); + HostComboBox *MixerControl(); + HostSlider *MixerVolume(); + HostSlider *PCMVolume(); + static const char *MixerControlControls[]; +}; + +class AudioGeneralSettings : public ConfigurationWizard +{ + public: + AudioGeneralSettings(); +}; + +class AudioAdvancedSettings : public VerticalConfigurationGroup +{ + Q_OBJECT + + public: + AudioAdvancedSettings(bool mpcm); + HostCheckBox *MPCM(); + HostCheckBox *SRCQualityOverride(); + HostComboBox *SRCQuality(); + HostCheckBox *Audio48kOverride(); + HostCheckBox *PassThroughOverride(); + HostComboBox *PassThroughOutputDevice(); + + TransCheckBoxSetting *m_triggerMPCM; + HostCheckBox *m_MPCM; + HostCheckBox *m_PassThroughOverride; +}; + +class AudioAdvancedSettingsGroup : public ConfigurationWizard +{ + public: + AudioAdvancedSettingsGroup(bool mpcm); +}; + +#endif diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp index 3e17a46..57f91a5 100644 --- a/mythtv/programs/mythfrontend/globalsettings.cpp +++ b/mythtv/programs/mythfrontend/globalsettings.cpp @@ -29,6 +29,7 @@ #include "iso639.h" #include "playbackbox.h" #include "globalsettings.h" +#include "audiosettings.h" #include "recordingprofile.h" #include "mythxdisplay.h" #include "DisplayRes.h" @@ -39,556 +40,6 @@ #include "mythdirs.h" #include "mythuihelper.h" -class TriggeredItem : public TriggeredConfigurationGroup -{ - public: - TriggeredItem(Setting *checkbox, ConfigurationGroup *group) : - TriggeredConfigurationGroup(false, false, false, false) - { - setTrigger(checkbox); - - addTarget("1", group); - addTarget("0", new VerticalConfigurationGroup(true)); - } - TriggeredItem(Setting *checkbox, Setting *setting) : - TriggeredConfigurationGroup(false, false, false, false) - { - setTrigger(checkbox); - - addTarget("1", setting); - addTarget("0", new VerticalConfigurationGroup(false, false)); - } -}; - -AudioDeviceComboBox::AudioDeviceComboBox(AudioConfigSettings *parent) : - HostComboBox("AudioOutputDevice", true), m_parent(parent) -{ - setLabel(QObject::tr("Audio output device")); -#ifdef USING_ALSA - QString dflt = "ALSA:default"; -#elif USING_PULSEOUTPUT - QString dflt = "PulseAudio:default"; -#elif CONFIG_DARWIN - QString dflt = "CoreAudio:"; -#elif USING_MINGW - QString dflt = "Windows:"; -#else - QString dflt = "NULL"; -#endif - QString current = gCoreContext->GetSetting(QString("AudioOutputDevice"), - dflt); - addSelection(current, current, true); - - connect(this, SIGNAL(valueChanged(const QString&)), - this, SLOT(AudioDescriptionHelp(const QString&))); -} - -void AudioDeviceComboBox::AudioRescan() -{ - AudioOutput::ADCVect &vect = m_parent->AudioDeviceVect(); - AudioOutput::ADCVect::const_iterator it; - - if (vect.empty()) - return; - - QString value = getValue(); - clearSelections(); - resetMaxCount(vect.size()); - - bool found = false; - for (it = vect.begin(); it != vect.end(); it++) - addSelection(it->name, it->name, - value == it->name ? (found = true) : false); - if (!found) - { - resetMaxCount(vect.size()+1); - addSelection(value, value, true); - } - // For some reason, it adds an empty entry, remove it - removeSelection(QString::null); -} - -void AudioDeviceComboBox::AudioDescriptionHelp(const QString &device) -{ - QString desc = m_parent->AudioDeviceMap().value(device).desc; - setHelpText(desc); -} - -AudioConfigSettings::AudioConfigSettings() : - VerticalConfigurationGroup(false, true, false, false), - m_OutputDevice(NULL), m_MaxAudioChannels(NULL), - m_AudioUpmix(NULL), m_AudioUpmixType(NULL), - m_AC3PassThrough(NULL), m_DTSPassThrough(NULL), m_MPCM(NULL), - m_AdvancedAudioSettings(NULL), m_PassThroughOverride(NULL) -{ - setLabel(QObject::tr("Audio System")); - setUseLabel(false); - - // Rescan button - TransButtonSetting *rescan = new TransButtonSetting("rescan"); - rescan->setLabel(QObject::tr("Scan for audio devices")); - rescan->setHelpText(QObject::tr("Scan for available audio devices. " - "Custom entry will be scanned and " - "capability entries populated.")); - addChild(rescan); - connect(rescan, SIGNAL(pressed()), this, SLOT(AudioRescan())); - - addChild((m_OutputDevice = new AudioDeviceComboBox(this))); - - QString name = m_OutputDevice->getValue(); - AudioOutput::AudioDeviceConfig *adc = - AudioOutput::GetAudioDeviceConfig(name, name, true); - if (adc->settings.IsInvalid()) - VERBOSE(VB_IMPORTANT, QString("Audio device %1 isn't usable " - "Check audio configuration").arg(name)); - audiodevs.insert(name, *adc); - devices.append(*adc); - delete adc; - - ConfigurationGroup *maingroup = new VerticalConfigurationGroup(false, - false); - addChild(maingroup); - - m_triggerDigital = new TransCheckBoxSetting(); - m_AC3PassThrough = AC3PassThrough(); - m_DTSPassThrough = DTSPassThrough(); - - m_cgsettings = new HorizontalConfigurationGroup(); - m_cgsettings->setLabel(QObject::tr("Digital Audio Capabilities")); - m_cgsettings->addChild(m_AC3PassThrough); - m_cgsettings->addChild(m_DTSPassThrough); - - TriggeredItem *sub1 = new TriggeredItem(m_triggerDigital, m_cgsettings); - - maingroup->addChild(sub1); - - maingroup->addChild((m_MaxAudioChannels = MaxAudioChannels())); - maingroup->addChild((m_AudioUpmix = AudioUpmix())); - maingroup->addChild((m_AudioUpmixType = AudioUpmixType())); - - m_AdvancedAudioSettings = AdvancedAudioSettings(); - addChild(m_AdvancedAudioSettings); - - ConfigurationGroup *group2 = - new VerticalConfigurationGroup(false); - - TriggeredItem *sub2 = new TriggeredItem(m_AdvancedAudioSettings, group2); - addChild(sub2); - - ConfigurationGroup *settings3 = - new HorizontalConfigurationGroup(false, false); - - m_PassThroughOverride = PassThroughOverride(); - TriggeredItem *sub3 = - new TriggeredItem(m_PassThroughOverride, PassThroughOutputDevice()); - settings3->addChild(m_PassThroughOverride); - settings3->addChild(sub3); - - ConfigurationGroup *settings4 = - new HorizontalConfigurationGroup(false, false); - Setting *srcqualityoverride = SRCQualityOverride(); - TriggeredItem *sub4 = - new TriggeredItem(srcqualityoverride, SRCQuality()); - settings4->addChild(srcqualityoverride); - settings4->addChild(sub4); - - ConfigurationGroup *settings5 = - new HorizontalConfigurationGroup(false, false); - settings5->addChild(Audio48kOverride()); - - m_triggerMPCM = new TransCheckBoxSetting(); - m_MPCM = MPCM(); - TriggeredItem *subMPCM = new TriggeredItem(m_triggerMPCM, m_MPCM); - - ConfigurationGroup *settings6 = - new HorizontalConfigurationGroup(false, false); - settings6->addChild(subMPCM); - - group2->addChild(settings4); - group2->addChild(settings5); - group2->addChild(settings3); - group2->addChild(settings6); - - // Set slots - connect(m_MaxAudioChannels, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateVisibility(const QString&))); - connect(m_OutputDevice, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); - connect(m_AC3PassThrough, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); - connect(m_DTSPassThrough, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); - connect(m_MPCM, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); - connect(m_PassThroughOverride, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); - connect(m_AdvancedAudioSettings, SIGNAL(valueChanged(const QString&)), - this, SLOT(UpdateCapabilities(const QString&))); -} - -void AudioConfigSettings::AudioRescan() -{ - if (!slotlock.tryLock()) - return; - - QVector* list = - AudioOutput::GetOutputList(); - QVector::const_iterator it; - - audiodevs.clear(); - for (it = list->begin(); it != list->end(); it++) - audiodevs.insert(it->name, *it); - - devices = *list; - delete list; - - QString name = m_OutputDevice->getValue(); - if (!audiodevs.contains(name)) - { - // Scan for possible custom entry that isn't in the list - AudioOutput::AudioDeviceConfig *adc = - AudioOutput::GetAudioDeviceConfig(name, name, true); - if (adc->settings.IsInvalid()) - { - QString msg = name + QObject::tr(" is invalid or not useable."); - MythPopupBox::showOkPopup( - GetMythMainWindow(), QObject::tr("Warning"), msg); - VERBOSE(VB_IMPORTANT, QString("Audio device %1 isn't usable ") - .arg(name)); - } - audiodevs.insert(name, *adc); - devices.append(*adc); - delete adc; - } - m_OutputDevice->AudioRescan(); - slotlock.unlock(); - UpdateCapabilities(QString::null); -} - -void AudioConfigSettings::UpdateVisibility(const QString &device) -{ - if (!m_MaxAudioChannels && !m_AudioUpmix && !m_AudioUpmixType) - return; - - int cur_speakers = m_MaxAudioChannels->getValue().toInt(); - m_AudioUpmix->setEnabled(cur_speakers > 2); - m_AudioUpmixType->setEnabled(cur_speakers > 2); -} - -void AudioConfigSettings::UpdateCapabilities(const QString &device) -{ - int max_speakers = 8; - bool invalid = false; - AudioOutputSettings settings; - - // Test if everything is set yet - if (!m_OutputDevice || !m_MaxAudioChannels || !m_AdvancedAudioSettings || - !m_AC3PassThrough || !m_DTSPassThrough || !m_MPCM || - !m_PassThroughOverride) - return; - - if (!slotlock.tryLock()) // Doing a rescan of channels - return; - - bool bForceDigital = (m_AdvancedAudioSettings->boolValue() && - m_PassThroughOverride->boolValue()); - - QString out = m_OutputDevice->getValue(); - if (!audiodevs.contains(out)) - { - VERBOSE(VB_AUDIO, QString("Update not found (%1)").arg(out)); - invalid = true; - } - else - { - settings = audiodevs.value(out).settings; - - max_speakers = settings.BestSupportedChannels(); - - bool bAC3 = (settings.canAC3() || bForceDigital) && - m_AC3PassThrough->boolValue(); - bool bDTS = (settings.canDTS() || bForceDigital) && - m_DTSPassThrough->boolValue(); - bool bLPCM = settings.canPassthrough() == -1 || - (settings.canLPCM() && - m_AdvancedAudioSettings->boolValue() && !m_MPCM->boolValue()); - - if (max_speakers > 2 && !bLPCM) - max_speakers = 2; - if (max_speakers == 2 && (bAC3 || bDTS)) - max_speakers = 6; - } - - m_triggerDigital->setValue(invalid || bForceDigital || - settings.canAC3() || settings.canDTS()); - m_triggerMPCM->setValue(invalid || (settings.canLPCM() && - settings.canPassthrough() >= 0)); - - int cur_speakers = m_MaxAudioChannels->getValue().toInt(); - - if (cur_speakers > max_speakers) - { - VERBOSE(VB_AUDIO, QString("Reset device %1").arg(out)); - cur_speakers = max_speakers; - } - - // Remove everything and re-add available channels - m_MaxAudioChannels->clearSelections(); - m_MaxAudioChannels->resetMaxCount(3); - for (int i = 1; i <= max_speakers; i++) - { - if (invalid || settings.IsSupportedChannels(i) || - (bForceDigital && i == 6)) - { - QString txt; - - switch (i) - { - case 2: - txt = QObject::tr("Stereo"); - break; - case 6: - txt = QObject::tr("5.1"); - break; - case 8: - txt = QObject::tr("7.1"); - break; - default: - continue; - } - m_MaxAudioChannels->addSelection(txt, QString::number(i), - i == cur_speakers); - } - } - slotlock.unlock(); -} - -HostComboBox *AudioConfigSettings::MaxAudioChannels() -{ - QString name = "MaxChannels"; - HostComboBox *gc = new HostComboBox(name, false); - gc->setLabel(QObject::tr("Speaker configuration")); - gc->addSelection(QObject::tr("Stereo"), "2", true); // default - gc->setHelpText(QObject::tr("Select the maximum number of audio " - "channels supported by your receiver " - "and speakers.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::AudioUpmix() -{ - HostCheckBox *gc = new HostCheckBox("AudioDefaultUpmix"); - gc->setLabel(QObject::tr("Upconvert stereo to 5.1 surround")); - gc->setValue(true); - gc->setHelpText(QObject::tr("If enabled, MythTV will upconvert stereo " - "to 5.1 audio. You can enable or disable " - "the upconversion during playback at any time.")); - return gc; -} - -HostComboBox *AudioConfigSettings::AudioUpmixType() -{ - HostComboBox *gc = new HostComboBox("AudioUpmixType",false); - gc->setLabel(QObject::tr("Upmix Quality")); - gc->addSelection(QObject::tr("Good"), "1"); - gc->addSelection(QObject::tr("Best"), "2", true); // default - gc->setHelpText(QObject::tr("Set the audio surround-upconversion quality.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::AC3PassThrough() -{ - HostCheckBox *gc = new HostCheckBox("AC3PassThru"); - gc->setLabel(QObject::tr("Dolby Digital")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " - "supports AC3/Dolby Digital. You must use a digital " - "connection. Uncheck if using an analog connection.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::DTSPassThrough() -{ - HostCheckBox *gc = new HostCheckBox("DTSPassThru"); - gc->setLabel(QObject::tr("DTS")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " - "supports DTS. You must use a digital connection. Uncheck " - "if using an analog connection")); - return gc; -} - -HostCheckBox *AudioConfigSettings::MPCM() -{ - HostCheckBox *gc = new HostCheckBox("StereoPCM"); - gc->setLabel(QObject::tr("Stereo PCM Only")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Enable if your amplifier or sound decoder " - "only supports 2 channels PCM (typically an old HDMI 1.0 " - "device). Multi-channels audio will be re-encoded to AC3 " - "when required")); - return gc; -} - -HostCheckBox *AudioConfigSettings::AdvancedAudioSettings() -{ - HostCheckBox *gc = new HostCheckBox("AdvancedAudioSettings"); - gc->setLabel(QObject::tr("Advanced audio configuration")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Enable extra audio settings.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::SRCQualityOverride() -{ - HostCheckBox *gc = new HostCheckBox("SRCQualityOverride"); - gc->setLabel(QObject::tr("Override SRC quality")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Enable to override audio sample rate " - "conversion quality.")); - return gc; -} - -HostComboBox *AudioConfigSettings::SRCQuality() -{ - HostComboBox *gc = new HostComboBox("SRCQuality", false); - gc->setLabel(QObject::tr("Sample rate conversion")); - gc->addSelection(QObject::tr("Disabled"), "-1"); - gc->addSelection(QObject::tr("Fastest"), "0"); - gc->addSelection(QObject::tr("Good"), "1", true); // default - gc->addSelection(QObject::tr("Best"), "2"); - gc->setHelpText(QObject::tr("Set the quality of audio sample-rate " - "conversion. \"Good\" (default) provides the best " - "compromise between CPU usage and quality. \"Disabled\" " - "lets the audio device handle sample-rate conversion.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::Audio48kOverride() -{ - HostCheckBox *gc = new HostCheckBox("Audio48kOverride"); - gc->setLabel(QObject::tr("Force audio device output to 48kHz")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Force audio sample rate to 48kHz. " - "Some audio devices will report various rates, " - "but they ultimately crash.")); - return gc; -} - -HostCheckBox *AudioConfigSettings::PassThroughOverride() -{ - HostCheckBox *gc = new HostCheckBox("PassThruDeviceOverride"); - gc->setLabel(QObject::tr("Separate digital output device")); - gc->setValue(false); - gc->setHelpText(QObject::tr("Use a distinct digital output device from " - "default.")); - return gc; -} - -HostComboBox *AudioConfigSettings::PassThroughOutputDevice() -{ - HostComboBox *gc = new HostComboBox("PassThruOutputDevice", true); - - gc->setLabel(QObject::tr("Digital output device")); - gc->addSelection(QObject::tr("Default"), "Default"); -#ifdef USING_MINGW - gc->addSelection("DirectX:Primary Sound Driver"); -#else - gc->addSelection("ALSA:iec958:{ AES0 0x02 }", - "ALSA:iec958:{ AES0 0x02 }"); - gc->addSelection("ALSA:hdmi", "ALSA:hdmi"); - gc->addSelection("ALSA:plughw:0,3", "ALSA:plughw:0,3"); -#endif - - gc->setHelpText(QObject::tr("Deprecated. Audio output device to use for " - "digital audio. This value is currently only used " - "with ALSA and DirectX sound output.")); - return gc; -} - -static HostCheckBox *MythControlsVolume() -{ - HostCheckBox *gc = new HostCheckBox("MythControlsVolume"); - gc->setLabel(QObject::tr("Use internal volume controls")); - gc->setValue(true); - gc->setHelpText(QObject::tr("If enabled, MythTV will control the PCM and " - "master mixer volume. Disable this option if you prefer " - "to control the volume externally (for example, using " - "your amplifier) or if you use an external mixer program.")); - return gc; -} - -static HostComboBox *MixerDevice() -{ - HostComboBox *gc = new HostComboBox("MixerDevice", true); - gc->setLabel(QObject::tr("Mixer device")); - -#ifdef USING_OSS - QDir dev("/dev", "mixer*", QDir::Name, QDir::System); - gc->fillSelectionsFromDir(dev); - - dev.setPath("/dev/sound"); - if (dev.exists()) - { - gc->fillSelectionsFromDir(dev); - } -#endif -#ifdef USING_ALSA - gc->addSelection("ALSA:default", "ALSA:default"); -#endif -#ifdef USING_MINGW - gc->addSelection("DirectX:", "DirectX:"); - gc->addSelection("Windows:", "Windows:"); -#endif -#if !defined(USING_MINGW) - gc->addSelection("software", "software"); - gc->setHelpText(QObject::tr("Setting the mixer device to \"software\" " - "lets MythTV control the volume of all audio at the " - "expense of a slight quality loss.")); -#endif - - return gc; -} - -static const char* MixerControlControls[] = { "PCM", - "Master" }; - -static HostComboBox *MixerControl() -{ - HostComboBox *gc = new HostComboBox("MixerControl", true); - gc->setLabel(QObject::tr("Mixer controls")); - for (unsigned int i = 0; i < sizeof(MixerControlControls) / sizeof(char*); - ++i) - { - gc->addSelection(QObject::tr(MixerControlControls[i]), - MixerControlControls[i]); - } - - gc->setHelpText(QObject::tr("Changing the volume adjusts the selected mixer.")); - return gc; -} - -static HostSlider *MixerVolume() -{ - HostSlider *gs = new HostSlider("MasterMixerVolume", 0, 100, 1); - gs->setLabel(QObject::tr("Master mixer volume")); - gs->setValue(70); - gs->setHelpText(QObject::tr("Initial volume for the Master mixer. " - "This affects all sound created by the audio device. " - "Note: Do not set this too low.")); - return gs; -} - -static HostSlider *PCMVolume() -{ - HostSlider *gs = new HostSlider("PCMMixerVolume", 0, 100, 1); - gs->setLabel(QObject::tr("PCM mixer volume")); - gs->setValue(70); - gs->setHelpText(QObject::tr("Initial volume for PCM output. Using the " - "volume keys in MythTV will adjust this parameter.")); - return gs; -} - static HostCheckBox *DecodeExtraAudio() { HostCheckBox *gc = new HostCheckBox("DecodeExtraAudio"); @@ -3196,36 +2647,6 @@ static HostSpinBox *EPGRecThreshold() return gs; } -class AudioMixerSettingsGroup : public TriggeredConfigurationGroup -{ - public: - AudioMixerSettingsGroup() : - TriggeredConfigurationGroup(false, true, false, false) - { - setLabel(QObject::tr("Audio Mixer")); - setUseLabel(false); - - Setting *volumeControl = MythControlsVolume(); - addChild(volumeControl); - - // Mixer settings - ConfigurationGroup *settings = - new VerticalConfigurationGroup(false, true, false, false); - settings->addChild(MixerDevice()); - settings->addChild(MixerControl()); - settings->addChild(MixerVolume()); - settings->addChild(PCMVolume()); - - ConfigurationGroup *dummy = - new VerticalConfigurationGroup(false, true, false, false); - - // Show Mixer config only if internal volume controls enabled - setTrigger(volumeControl); - addTarget("0", dummy); - addTarget("1", settings); - } -}; - static HostComboBox *MythLanguage() { HostComboBox *gc = new HostComboBox("Language"); @@ -3923,10 +3344,6 @@ MainGeneralSettings::MainGeneralSettings() pin->addChild(SetupPinCode()); addChild(pin); - addChild(new AudioConfigSettings()); - - addChild(new AudioMixerSettingsGroup()); - VerticalConfigurationGroup *general = new VerticalConfigurationGroup(false, true, false, false); general->setLabel(QObject::tr("General")); diff --git a/mythtv/programs/mythfrontend/globalsettings.h b/mythtv/programs/mythfrontend/globalsettings.h index 7c9b4d5..90cb98a 100644 --- a/mythtv/programs/mythfrontend/globalsettings.h +++ b/mythtv/programs/mythfrontend/globalsettings.h @@ -9,73 +9,8 @@ #include "videodisplayprofile.h" #include -#include "audiooutput.h" class QFileInfo; -class AudioDeviceComboBox; - -class AudioConfigSettings : public VerticalConfigurationGroup -{ - Q_OBJECT - - public: - AudioConfigSettings(); - - typedef QMap ADCMap; - - ADCMap &AudioDeviceMap(void) { return audiodevs; }; - AudioOutput::ADCVect &AudioDeviceVect(void) { return devices; }; - - private slots: - void UpdateVisibility(const QString&); - void UpdateCapabilities(const QString&); - void AudioRescan(); - - private: - AudioDeviceComboBox *OutputDevice(); - HostComboBox *MaxAudioChannels(); - HostCheckBox *AudioUpmix(); - HostComboBox *AudioUpmixType(); - HostCheckBox *AC3PassThrough(); - HostCheckBox *DTSPassThrough(); - HostCheckBox *MPCM(); - HostCheckBox *AdvancedAudioSettings(); - HostCheckBox *SRCQualityOverride(); - HostComboBox *SRCQuality(); - HostCheckBox *Audio48kOverride(); - HostCheckBox *PassThroughOverride(); - HostComboBox *PassThroughOutputDevice(); - - ConfigurationGroup *m_cgsettings; - AudioDeviceComboBox *m_OutputDevice; - HostComboBox *m_MaxAudioChannels; - HostCheckBox *m_AudioUpmix; - HostComboBox *m_AudioUpmixType; - TransCheckBoxSetting *m_triggerDigital; - TransCheckBoxSetting *m_triggerMPCM; - HostCheckBox *m_AC3PassThrough; - HostCheckBox *m_DTSPassThrough; - HostCheckBox *m_MPCM; - HostCheckBox *m_AdvancedAudioSettings; - HostCheckBox *m_PassThroughOverride; - ADCMap audiodevs; - AudioOutput::ADCVect devices; - QMutex slotlock; -}; - -class AudioDeviceComboBox : public HostComboBox -{ - Q_OBJECT - public: - AudioDeviceComboBox(AudioConfigSettings*); - void AudioRescan(); - - private slots: - void AudioDescriptionHelp(const QString&); - - private: - AudioConfigSettings *m_parent; -}; class ThemeSelector : public HostImageSelect { diff --git a/mythtv/programs/mythfrontend/main.cpp b/mythtv/programs/mythfrontend/main.cpp index 7b42569..feec308 100644 --- a/mythtv/programs/mythfrontend/main.cpp +++ b/mythtv/programs/mythfrontend/main.cpp @@ -33,7 +33,9 @@ using namespace std; #include "programrecpriority.h" #include "channelrecpriority.h" #include "custompriority.h" +#include "audiooutput.h" #include "globalsettings.h" +#include "audiosettings.h" #include "profilegroup.h" #include "playgroup.h" #include "networkcontrol.h" @@ -593,6 +595,11 @@ static void TVMenuCallback(void *data, QString &selection) QStringList strlist( QString("REFRESH_BACKEND") ); gCoreContext->SendReceiveStringList(strlist); } + else if (sel == "settings audiogeneral") + { + AudioGeneralSettings audiosettings; + audiosettings.exec(); + } else if (sel == "settings playback") { PlaybackSettings settings; diff --git a/mythtv/programs/mythfrontend/mythfrontend.pro b/mythtv/programs/mythfrontend/mythfrontend.pro index 6531079..2b161ff 100644 --- a/mythtv/programs/mythfrontend/mythfrontend.pro +++ b/mythtv/programs/mythfrontend/mythfrontend.pro @@ -20,7 +20,7 @@ INSTALLS += setting QMAKE_CLEAN += $(TARGET) # Input -HEADERS += playbackbox.h viewscheduled.h globalsettings.h +HEADERS += playbackbox.h viewscheduled.h globalsettings.h audiosettings.h HEADERS += manualschedule.h programrecpriority.h channelrecpriority.h HEADERS += statusbox.h networkcontrol.h custompriority.h HEADERS += mediarenderer.h mythfexml.h playbackboxlistitem.h @@ -33,7 +33,7 @@ HEADERS += proglist.h proglist_helpers.h HEADERS += playbackboxhelper.h viewschedulediff.h HEADERS += themechooser.h -SOURCES += main.cpp playbackbox.cpp viewscheduled.cpp +SOURCES += main.cpp playbackbox.cpp viewscheduled.cpp audiosettings.cpp SOURCES += globalsettings.cpp manualschedule.cpp programrecpriority.cpp SOURCES += channelrecpriority.cpp statusbox.cpp networkcontrol.cpp SOURCES += mediarenderer.cpp mythfexml.cpp playbackboxlistitem.cpp diff --git a/mythtv/themes/DVR/main_settings.xml b/mythtv/themes/DVR/main_settings.xml index 2472b9a..8d1b5f8 100644 --- a/mythtv/themes/DVR/main_settings.xml +++ b/mythtv/themes/DVR/main_settings.xml @@ -23,6 +23,13 @@ + + + + + + + +