diff --git a/include/AudioSdl.h b/include/AudioSdl.h index fcfe97318..c256815f0 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -39,7 +39,7 @@ #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" -class QLineEdit; +class QComboBox; namespace lmms { @@ -64,10 +64,16 @@ public: ~setupWidget() override = default; void saveSettings() override; + + private: + void populatePlaybackDeviceComboBox(); + void populateInputDeviceComboBox(); private: - QLineEdit * m_device; + QComboBox* m_playbackDeviceComboBox = nullptr; + QComboBox* m_inputDeviceComboBox = nullptr; + static QString s_systemDefaultDevice; } ; diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index f58766725..192fb62e3 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "AudioEngine.h" @@ -37,6 +38,10 @@ namespace lmms { +constexpr auto SectionSDL = "audiosdl"; +constexpr auto PlaybackDeviceSDL = "device"; +constexpr auto InputDeviceSDL = "inputdevice"; + AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : AudioDevice( DEFAULT_CHANNELS, _audioEngine ), m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) @@ -78,11 +83,19 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : SDL_AudioSpec actual; #ifdef LMMS_HAVE_SDL2 - m_outputDevice = SDL_OpenAudioDevice (nullptr, - 0, - &m_audioHandle, - &actual, - 0); + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL).toStdString(); + const bool isDefaultPlayback = playbackDevice.empty(); + + // Try with the configured device + const auto playbackDeviceCStr = isDefaultPlayback ? nullptr : playbackDevice.c_str(); + m_outputDevice = SDL_OpenAudioDevice(playbackDeviceCStr, 0, &m_audioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_outputDevice == 0 && !isDefaultPlayback) + { + m_outputDevice = SDL_OpenAudioDevice(nullptr, 0, &m_audioHandle, &actual, 0); + } + if (m_outputDevice == 0) { qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); return; @@ -108,11 +121,19 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : m_inputAudioHandle = m_audioHandle; m_inputAudioHandle.callback = sdlInputAudioCallback; - m_inputDevice = SDL_OpenAudioDevice (nullptr, - 1, - &m_inputAudioHandle, - &actual, - 0); + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL).toStdString(); + const bool isDefaultInput = inputDevice.empty(); + + // Try with the configured device + const auto inputDeviceCStr = isDefaultInput ? nullptr : inputDevice.c_str(); + m_inputDevice = SDL_OpenAudioDevice (inputDeviceCStr, 1, &m_inputAudioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_inputDevice == 0 && !isDefaultInput) + { + m_inputDevice = SDL_OpenAudioDevice(nullptr, 1, &m_inputAudioHandle, &actual, 0); + } + if (m_inputDevice != 0) { m_supportsCapture = true; } else { @@ -283,15 +304,25 @@ void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) { #endif +QString AudioSdl::setupWidget::s_systemDefaultDevice = AudioDeviceSetupWidget::tr("[System Default]"); + AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioSdl::name(), _parent ) { QFormLayout * form = new QFormLayout(this); + form->setRowWrapPolicy(QFormLayout::WrapLongRows); - QString dev = ConfigManager::inst()->value( "audiosdl", "device" ); - m_device = new QLineEdit( dev, this ); + m_playbackDeviceComboBox = new QComboBox(this); - form->addRow(tr("Device"), m_device); + populatePlaybackDeviceComboBox(); + + form->addRow(tr("Playback device"), m_playbackDeviceComboBox); + + m_inputDeviceComboBox = new QComboBox(this); + + populateInputDeviceComboBox(); + + form->addRow(tr("Input device"), m_inputDeviceComboBox); } @@ -299,8 +330,72 @@ AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : void AudioSdl::setupWidget::saveSettings() { - ConfigManager::inst()->setValue( "audiosdl", "device", - m_device->text() ); + const auto currentPlaybackDevice = m_playbackDeviceComboBox->currentText(); + if (currentPlaybackDevice == s_systemDefaultDevice) + { + // Represent the default playback device with an empty string + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, ""); + } + else if (!currentPlaybackDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, currentPlaybackDevice); + } + + const auto currentInputDevice = m_inputDeviceComboBox->currentText(); + if (currentInputDevice == s_systemDefaultDevice) + { + // Represent the default input device with an empty string + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, ""); + } + else if (!currentInputDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, currentInputDevice); + } +} + +void AudioSdl::setupWidget::populatePlaybackDeviceComboBox() +{ +#ifdef LMMS_HAVE_SDL2 + m_playbackDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList playbackDevices; + const int numberOfPlaybackDevices = SDL_GetNumAudioDevices(0); + for (int i = 0; i < numberOfPlaybackDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 0); + playbackDevices.append(deviceName); + } + + playbackDevices.sort(); + + m_playbackDeviceComboBox->addItems(playbackDevices); + + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL); + m_playbackDeviceComboBox->setCurrentText(playbackDevice.isEmpty() ? s_systemDefaultDevice : playbackDevice); +#endif +} + +void AudioSdl::setupWidget::populateInputDeviceComboBox() +{ +#ifdef LMMS_HAVE_SDL2 + m_inputDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList inputDevices; + const int numberOfInputDevices = SDL_GetNumAudioDevices(1); + for (int i = 0; i < numberOfInputDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 1); + inputDevices.append(deviceName); + } + + inputDevices.sort(); + + m_inputDeviceComboBox->addItems(inputDevices); + + // Set the current device to the one in the configuration + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL); + m_inputDeviceComboBox->setCurrentText(inputDevice.isEmpty() ? s_systemDefaultDevice : inputDevice); +#endif }