Large diffs are not rendered by default.

@@ -0,0 +1,52 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#ifdef _WIN32
// clang-format off
#include <Windows.h>
#include <mmreg.h>
// clang-format on
#endif

#include <atomic>
#include <string>
#include <thread>
#include <vector>

#include "AudioCommon/SoundStream.h"

struct IAudioClient;
struct IAudioRenderClient;
struct IMMDevice;
struct IMMDeviceEnumerator;

class WASAPIStream final : public SoundStream
{
#ifdef _WIN32
public:
explicit WASAPIStream();
~WASAPIStream();
bool Init() override;
bool SetRunning(bool running) override;
void SoundLoop() override;

static bool isValid();
static std::vector<std::string> GetAvailableDevices();
static IMMDevice* GetDeviceByName(std::string name);

private:
u32 m_frames_in_buffer = 0;
std::atomic<bool> m_running = false;
std::atomic<bool> m_stopped = false;
std::thread m_thread;

IAudioClient* m_audio_client = nullptr;
IAudioRenderClient* m_audio_renderer = nullptr;
IMMDeviceEnumerator* m_enumerator = nullptr;
HANDLE m_need_data_event = nullptr;
WAVEFORMATEXTENSIBLE m_format;
#endif // _WIN32
};
@@ -292,6 +292,10 @@ void SConfig::SaveDSPSettings(IniFile& ini)
dsp->Set("Backend", sBackend);
dsp->Set("Volume", m_Volume);
dsp->Set("CaptureLog", m_DSPCaptureLog);

#ifdef _WIN32
dsp->Set("WASAPIDevice", sWASAPIDevice);
#endif
}

void SConfig::SaveInputSettings(IniFile& ini)
@@ -589,6 +593,10 @@ void SConfig::LoadDSPSettings(IniFile& ini)
dsp->Get("Volume", &m_Volume, 100);
dsp->Get("CaptureLog", &m_DSPCaptureLog, false);

#ifdef _WIN32
dsp->Get("WASAPIDevice", &sWASAPIDevice, "default");
#endif

m_IsMuted = false;
}

@@ -22,14 +22,14 @@ enum class Language;
enum class Region;
struct Partition;
class Volume;
}
} // namespace DiscIO
namespace IOS
{
namespace ES
{
class TMDReader;
}
}
} // namespace IOS

// DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output")
@@ -39,6 +39,7 @@ class TMDReader;
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_XAUDIO2 "XAudio2"
#define BACKEND_OPENSLES "OpenSLES"
#define BACKEND_WASAPI "WASAPI (Exclusive Mode)"

enum GPUDeterminismMode
{
@@ -298,6 +299,11 @@ struct SConfig
int m_Volume;
std::string sBackend;

#ifdef _WIN32
// WSAPI settings
std::string sWASAPIDevice;
#endif

// Input settings
bool m_BackgroundInput;
bool m_AdapterRumble[4];
@@ -17,6 +17,7 @@
#include <QVBoxLayout>

#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/WASAPIStream.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "DolphinQt2/Config/SettingsWindow.h"
@@ -88,6 +89,14 @@ void AudioPane::CreateWidgets()
backend_layout->addRow(m_backend_label, m_backend_combo);
if (m_latency_control_supported)
backend_layout->addRow(m_latency_label, m_latency_spin);

#ifdef _WIN32
m_wasapi_device_label = new QLabel(tr("Device:"));
m_wasapi_device_combo = new QComboBox;

backend_layout->addRow(m_wasapi_device_label, m_wasapi_device_combo);
#endif

backend_layout->addRow(m_dolby_pro_logic);

auto* stretching_box = new QGroupBox(tr("Audio Stretching Settings"));
@@ -140,6 +149,12 @@ void AudioPane::ConnectWidgets()
connect(m_dsp_hle, &QRadioButton::toggled, this, &AudioPane::SaveSettings);
connect(m_dsp_lle, &QRadioButton::toggled, this, &AudioPane::SaveSettings);
connect(m_dsp_interpreter, &QRadioButton::toggled, this, &AudioPane::SaveSettings);

#ifdef _WIN32
connect(m_wasapi_device_combo,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&AudioPane::SaveSettings);
#endif
}

void AudioPane::LoadSettings()
@@ -183,6 +198,18 @@ void AudioPane::LoadSettings()
m_stretching_buffer_slider->setValue(SConfig::GetInstance().m_audio_stretch_max_latency);
m_stretching_buffer_slider->setEnabled(m_stretching_enable->isChecked());
m_stretching_buffer_indicator->setText(tr("%1 ms").arg(m_stretching_buffer_slider->value()));

#ifdef _WIN32
if (SConfig::GetInstance().sWASAPIDevice == "default")
{
m_wasapi_device_combo->setCurrentIndex(0);
}
else
{
m_wasapi_device_combo->setCurrentText(
QString::fromStdString(SConfig::GetInstance().sWASAPIDevice));
}
#endif
}

void AudioPane::SaveSettings()
@@ -227,6 +254,15 @@ void AudioPane::SaveSettings()
m_stretching_buffer_indicator->setText(
tr("%1 ms").arg(SConfig::GetInstance().m_audio_stretch_max_latency));

#ifdef _WIN32
std::string device = "default";

if (m_wasapi_device_combo->currentIndex() != 0)
device = m_wasapi_device_combo->currentText().toStdString();

SConfig::GetInstance().sWASAPIDevice = device;
#endif

AudioCommon::UpdateSoundStream();
}

@@ -240,6 +276,22 @@ void AudioPane::OnBackendChanged()
m_latency_label->setEnabled(AudioCommon::SupportsLatencyControl(backend));
m_latency_spin->setEnabled(AudioCommon::SupportsLatencyControl(backend));
}

#ifdef _WIN32
bool is_wasapi = backend == BACKEND_WASAPI;
m_wasapi_device_label->setHidden(!is_wasapi);
m_wasapi_device_combo->setHidden(!is_wasapi);

if (is_wasapi)
{
m_wasapi_device_combo->clear();
m_wasapi_device_combo->addItem(tr("Default Device"));

for (const auto device : WASAPIStream::GetAvailableDevices())
m_wasapi_device_combo->addItem(QString::fromStdString(device));
}
#endif

m_volume_slider->setEnabled(AudioCommon::SupportsVolumeChanges(backend));
m_volume_indicator->setEnabled(AudioCommon::SupportsVolumeChanges(backend));
}
@@ -257,6 +309,10 @@ void AudioPane::OnEmulationStateChanged(bool running)
m_latency_label->setEnabled(!running);
m_latency_spin->setEnabled(!running);
}

#ifdef _WIN32
m_wasapi_device_combo->setEnabled(!running);
#endif
}

void AudioPane::OnVolumeChanged(int volume)
@@ -52,6 +52,10 @@ class AudioPane final : public QWidget
QCheckBox* m_dolby_pro_logic;
QLabel* m_latency_label;
QSpinBox* m_latency_spin;
#ifdef _WIN32
QLabel* m_wasapi_device_label;
QComboBox* m_wasapi_device_combo;
#endif

// Audio Stretching
QCheckBox* m_stretching_enable;