Skip to content

Commit

Permalink
SDL driver's input and output device configuration via combo box (#7421)
Browse files Browse the repository at this point in the history
* Enable configuration of input device for SDL

Up to now the SDL audio driver attempted to use the default recording
device. This might not be what users want or expect, especially since the
actually used device is not visible anywhere. So if recording does not
work for the users they have no way to find out what's wrong.

Extend the settings screen of the SDL driver with a combo box that allows
to select the input device to be used. Store the selected device name in
a new attribute called "inputdevice" in the "audiosdl" section of the
configuration file.

Use the information from the configuration when attempting to inialize
the input device. Fall back to the default device if that does not work.

(cherry picked from commit 33139b9)

* Provide a setting for system default input

Provide the setting "[System Default]" which instructs the SDL driver to
use the default device of the system as the input device. In the
configuration file this option is represented as an empty string. This
should play well with the current existing configuration of the users.

(cherry picked from commit 29c43c2)

* Configuration of output device for SDL

Let users configure the output device that's used by the SDL driver.
Code-wise the implementation is very similar to the input device
configuration.

Use a `QComboBox` instead of a `QLineEdit` for `m_device` and rename it
to `m_playbackDeviceComboBox`.

Rename `s_defaultInputDevice` to `s_systemDefaultDevice` because it is
used in the context of playback and input devices.

(cherry picked from commit 1ab45e4)

* Ensure label visibility

Make sure that labels are always shown by setting the row wrap policy of
the form layout to wrap long rows.

(cherry picked from commit a123d0e)

* Rename "Device"

Rename "Device" to "Playback device" to make clear what the setting
refers to.

(cherry picked from commit 1f0cda4)

* Remove repeated strings

Introduce const expressions to get rid of repeated strings with a risk
of typos.

(cherry picked from commit f9ea970)

* Apply some more changes

Apply some more changes that have been made to `AudioSdl` in the
recording branch.

* Conditional ternary operator

Also use a conditional ternary operator for the input device setup.

* Methods for population of combo boxes

Move the population of the input and playback device combo boxes into
the methods `populatePlaybackDeviceComboBox` and
`populateInputDeviceComboBox`.

* Sort devices in combo box

Sort the devices names alphabetically in the input and playback combo
boxes. The default devices is always shown as the first entry.

* Code review fixes

Use `AudioDeviceSetupWidget` instead of `QObject` to translate "[System
Default]".

Fix copy/paste error in comment.

* Simplify some constexpr statements
  • Loading branch information
michaelgregorius committed Aug 10, 2024
1 parent 44a8b03 commit 74c73e5
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 17 deletions.
10 changes: 8 additions & 2 deletions include/AudioSdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"

class QLineEdit;
class QComboBox;

namespace lmms
{
Expand All @@ -64,10 +64,16 @@ class AudioSdl : public AudioDevice
~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;
} ;


Expand Down
125 changes: 110 additions & 15 deletions src/core/audio/AudioSdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <QFormLayout>
#include <QLineEdit>
#include <QComboBox>
#include <SDL.h>

#include "AudioEngine.h"
Expand All @@ -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()])
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -283,24 +304,98 @@ 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);
}




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
}


Expand Down

0 comments on commit 74c73e5

Please sign in to comment.