mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
Qt: Add output audio device selection
This commit is contained in:
parent
8ab46d0713
commit
3082fd55d7
|
@ -287,6 +287,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
ParseAudioBackend(si.GetStringValue("Audio", "Backend", GetAudioBackendName(DEFAULT_AUDIO_BACKEND)).c_str())
|
||||
.value_or(DEFAULT_AUDIO_BACKEND);
|
||||
audio_driver = si.GetStringValue("Audio", "Driver");
|
||||
audio_output_device = si.GetStringValue("Audio", "OutputDevice");
|
||||
audio_stretch_mode =
|
||||
AudioStream::ParseStretchMode(
|
||||
si.GetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(DEFAULT_AUDIO_STRETCH_MODE)).c_str())
|
||||
|
@ -500,6 +501,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
|
||||
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
|
||||
si.SetStringValue("Audio", "Driver", audio_driver.c_str());
|
||||
si.SetStringValue("Audio", "OutputDevice", audio_output_device.c_str());
|
||||
si.SetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(audio_stretch_mode));
|
||||
si.SetUIntValue("Audio", "BufferMS", audio_buffer_ms);
|
||||
si.SetUIntValue("Audio", "OutputLatencyMS", audio_output_latency_ms);
|
||||
|
|
|
@ -159,6 +159,7 @@ struct Settings
|
|||
AudioBackend audio_backend = DEFAULT_AUDIO_BACKEND;
|
||||
AudioStretchMode audio_stretch_mode = DEFAULT_AUDIO_STRETCH_MODE;
|
||||
std::string audio_driver;
|
||||
std::string audio_output_device;
|
||||
u32 audio_output_latency_ms = DEFAULT_AUDIO_OUTPUT_LATENCY_MS;
|
||||
u32 audio_buffer_ms = DEFAULT_AUDIO_BUFFER_MS;
|
||||
u32 audio_output_volume = 100;
|
||||
|
|
|
@ -3128,7 +3128,9 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
UpdateOverclock();
|
||||
}
|
||||
|
||||
if (g_settings.audio_backend != old_settings.audio_backend || g_settings.audio_driver != old_settings.audio_driver)
|
||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||
g_settings.audio_driver != old_settings.audio_driver ||
|
||||
g_settings.audio_output_device != old_settings.audio_output_device)
|
||||
{
|
||||
if (g_settings.audio_backend != old_settings.audio_backend)
|
||||
{
|
||||
|
|
|
@ -102,26 +102,50 @@ void AudioSettingsWidget::updateDriverNames()
|
|||
.value_or(Settings::DEFAULT_AUDIO_BACKEND);
|
||||
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::pair<std::string, std::string>> devices;
|
||||
|
||||
#ifdef WITH_CUBEB
|
||||
if (backend == AudioBackend::Cubeb)
|
||||
{
|
||||
names = CommonHost::GetCubebDriverNames();
|
||||
devices = CommonHost::GetCubebOutputDevices(m_dialog->getEffectiveStringValue("Audio", "Driver", "").c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
m_ui.driver->disconnect();
|
||||
m_ui.driver->clear();
|
||||
if (names.empty())
|
||||
{
|
||||
m_ui.driver->addItem(tr("Default"));
|
||||
m_ui.driver->setEnabled(false);
|
||||
m_ui.driver->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
m_ui.driver->setEnabled(true);
|
||||
for (const std::string& name : names)
|
||||
m_ui.driver->addItem(QString::fromStdString(name));
|
||||
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(m_dialog->getSettingsInterface(), m_ui.driver, "Audio", "Driver",
|
||||
std::move(names.front()));
|
||||
connect(m_ui.driver, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::updateDriverNames);
|
||||
}
|
||||
|
||||
m_ui.outputDevice->disconnect();
|
||||
m_ui.outputDevice->clear();
|
||||
if (names.empty())
|
||||
{
|
||||
m_ui.outputDevice->addItem(tr("Default"));
|
||||
m_ui.outputDevice->setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.outputDevice->setEnabled(true);
|
||||
for (const auto& [id, name] : devices)
|
||||
m_ui.outputDevice->addItem(QString::fromStdString(name), QString::fromStdString(id));
|
||||
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(m_dialog->getSettingsInterface(), m_ui.outputDevice, "Audio",
|
||||
"OutputDevice", std::move(devices.front().first));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSettingsWidget::updateLatencyLabel()
|
||||
|
|
|
@ -32,111 +32,7 @@
|
|||
<string>Configuration</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Buffer Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="startDumpingOnBoot">
|
||||
<property name="text">
|
||||
<string>Start Dumping On Boot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Backend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QSlider" name="outputLatencyMS">
|
||||
<property name="maximum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBothSides</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="outputLatencyMinimal">
|
||||
<property name="text">
|
||||
<string>Minimal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="driver"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="stretchMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off (Noisy)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Resampling (Pitch Shift)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Time Stretch (Tempo Change, Best Sound)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Output Latency:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="audioBackend"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Driver:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Stretch Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QLabel" name="bufferingLabel">
|
||||
<property name="text">
|
||||
<string>Maximum latency: 0 frames (0.00ms)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSlider" name="bufferMS">
|
||||
<property name="minimum">
|
||||
<number>15</number>
|
||||
|
@ -164,6 +60,120 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="driver"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Stretch Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Buffer Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="stretchMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off (Noisy)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Resampling (Pitch Shift)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Time Stretch (Tempo Change, Best Sound)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QLabel" name="bufferingLabel">
|
||||
<property name="text">
|
||||
<string>Maximum latency: 0 frames (0.00ms)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Backend:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="audioBackend"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Output Latency:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Driver:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="startDumpingOnBoot">
|
||||
<property name="text">
|
||||
<string>Start Dumping On Boot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QSlider" name="outputLatencyMS">
|
||||
<property name="maximum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBothSides</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="outputLatencyMinimal">
|
||||
<property name="text">
|
||||
<string>Minimal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Output Device:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="outputDevice"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class SettingsInterface;
|
||||
|
@ -42,6 +43,7 @@ u64 GetSessionPlayedTime();
|
|||
std::unique_ptr<AudioStream> CreateCubebAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms,
|
||||
AudioStretchMode stretch);
|
||||
std::vector<std::string> GetCubebDriverNames();
|
||||
std::vector<std::pair<std::string, std::string>> GetCubebOutputDevices(const char* driver);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
std::unique_ptr<AudioStream> CreateXAudio2Stream(u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms,
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
#include "cubeb_audio_stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/scoped_guard.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common_host.h"
|
||||
#include "core/host.h"
|
||||
#include "core/settings.h"
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "fmt/format.h"
|
||||
Log_SetChannel(CubebAudioStream);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -123,6 +125,40 @@ bool CubebAudioStream::Initialize(u32 latency_ms)
|
|||
}
|
||||
}
|
||||
|
||||
cubeb_devid selected_device = nullptr;
|
||||
const std::string& selected_device_name = g_settings.audio_output_device;
|
||||
cubeb_device_collection devices;
|
||||
bool devices_valid = false;
|
||||
if (!selected_device_name.empty())
|
||||
{
|
||||
rv = cubeb_enumerate_devices(m_context, CUBEB_DEVICE_TYPE_OUTPUT, &devices);
|
||||
devices_valid = (rv == CUBEB_OK);
|
||||
if (rv == CUBEB_OK)
|
||||
{
|
||||
for (size_t i = 0; i < devices.count; i++)
|
||||
{
|
||||
const cubeb_device_info& di = devices.device[i];
|
||||
if (di.device_id && selected_device_name == di.device_id)
|
||||
{
|
||||
Log_InfoPrintf("Using output device '%s' (%s).", di.device_id,
|
||||
di.friendly_name ? di.friendly_name : di.device_id);
|
||||
selected_device = di.devid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected_device)
|
||||
{
|
||||
Host::AddOSDMessage(
|
||||
fmt::format("Requested audio output device '{}' not found, using default.", selected_device_name), 10.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_WarningPrintf("cubeb_enumerate_devices() returned %d, using default device.", rv);
|
||||
}
|
||||
}
|
||||
|
||||
BaseInitialize();
|
||||
m_volume = 100;
|
||||
m_paused = false;
|
||||
|
@ -130,8 +166,12 @@ bool CubebAudioStream::Initialize(u32 latency_ms)
|
|||
char stream_name[32];
|
||||
std::snprintf(stream_name, sizeof(stream_name), "%p", this);
|
||||
|
||||
rv = cubeb_stream_init(m_context, &stream, stream_name, nullptr, nullptr, nullptr, ¶ms, latency_frames,
|
||||
rv = cubeb_stream_init(m_context, &stream, stream_name, nullptr, nullptr, selected_device, ¶ms, latency_frames,
|
||||
&CubebAudioStream::DataCallback, StateCallback, this);
|
||||
|
||||
if (devices_valid)
|
||||
cubeb_device_collection_destroy(m_context, &devices);
|
||||
|
||||
if (rv != CUBEB_OK)
|
||||
{
|
||||
Log_ErrorPrintf("(Cubeb) Could not create stream: %d", rv);
|
||||
|
@ -210,3 +250,40 @@ std::vector<std::string> CommonHost::GetCubebDriverNames()
|
|||
names.emplace_back(cubeb_names[i]);
|
||||
return names;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> CommonHost::GetCubebOutputDevices(const char* driver)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> ret;
|
||||
ret.emplace_back(std::string(), Host::TranslateStdString("CommonHost", "Default Output Device"));
|
||||
|
||||
cubeb* context;
|
||||
int rv = cubeb_init(&context, "DuckStation", (driver && *driver) ? driver : nullptr);
|
||||
if (rv != CUBEB_OK)
|
||||
{
|
||||
Log_ErrorPrintf("cubeb_init() failed: %d", rv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ScopedGuard context_cleanup([context]() { cubeb_destroy(context); });
|
||||
|
||||
cubeb_device_collection devices;
|
||||
rv = cubeb_enumerate_devices(context, CUBEB_DEVICE_TYPE_OUTPUT, &devices);
|
||||
if (rv != CUBEB_OK)
|
||||
{
|
||||
Log_ErrorPrintf("cubeb_enumerate_devices() failed: %d", rv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ScopedGuard devices_cleanup([context, &devices]() { cubeb_device_collection_destroy(context, &devices); });
|
||||
|
||||
for (size_t i = 0; i < devices.count; i++)
|
||||
{
|
||||
const cubeb_device_info& di = devices.device[i];
|
||||
if (!di.device_id)
|
||||
continue;
|
||||
|
||||
ret.emplace_back(di.device_id, di.friendly_name ? di.friendly_name : di.device_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue