AudioStream: Add surround expansion via FreeSurround

This commit is contained in:
Stenzek
2024-04-16 16:45:15 +10:00
parent 7932d284f1
commit 0fbc1a3a8a
31 changed files with 1977 additions and 554 deletions

View File

@@ -3628,14 +3628,12 @@ void FullscreenUI::DrawControllerSettingsPage()
MenuHeading(FSUI_CSTR("Input Sources"));
#ifdef ENABLE_SDL2
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_COG, "Enable SDL Input Source"),
FSUI_CSTR("The SDL input source supports most controllers."), "InputSources", "SDL", true, true,
false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_WIFI, "SDL DualShock 4 / DualSense Enhanced Mode"),
FSUI_CSTR("Provides vibration and LED control support over Bluetooth."), "InputSources",
"SDLControllerEnhancedMode", false, bsi->GetBoolValue("InputSources", "SDL", true), false);
#endif
#ifdef _WIN32
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_COG, "Enable XInput Input Source"),
FSUI_CSTR("The XInput source provides support for XBox 360/XBox One/XBox Series controllers."),
@@ -4800,25 +4798,30 @@ void FullscreenUI::DrawAudioSettingsPage()
DrawEnumSetting(
bsi, FSUI_CSTR("Audio Backend"),
FSUI_CSTR("The audio backend determines how frames produced by the emulator are submitted to the host."), "Audio",
"Backend", Settings::DEFAULT_AUDIO_BACKEND, &Settings::ParseAudioBackend, &Settings::GetAudioBackendName,
&Settings::GetAudioBackendDisplayName, AudioBackend::Count);
"Backend", AudioStream::DEFAULT_BACKEND, &AudioStream::ParseBackendName, &AudioStream::GetBackendName,
&AudioStream::GetBackendDisplayName, AudioBackend::Count);
DrawEnumSetting(bsi, FSUI_CSTR("Expansion Mode"),
FSUI_CSTR("Determines how audio is expanded from stereo to surround for supported games."), "Audio",
"ExpansionMode", AudioStreamParameters::DEFAULT_EXPANSION_MODE, &AudioStream::ParseExpansionMode,
&AudioStream::GetExpansionModeName, &AudioStream::GetExpansionModeDisplayName,
AudioExpansionMode::Count);
DrawEnumSetting(bsi, FSUI_CSTR("Stretch Mode"),
FSUI_CSTR("Determines quality of audio when not running at 100% speed."), "Audio", "StretchMode",
Settings::DEFAULT_AUDIO_STRETCH_MODE, &AudioStream::ParseStretchMode,
AudioStreamParameters::DEFAULT_STRETCH_MODE, &AudioStream::ParseStretchMode,
&AudioStream::GetStretchModeName, &AudioStream::GetStretchModeDisplayName, AudioStretchMode::Count);
DrawIntRangeSetting(bsi, FSUI_CSTR("Buffer Size"),
FSUI_CSTR("Determines the amount of audio buffered before being pulled by the host API."),
"Audio", "BufferMS", Settings::DEFAULT_AUDIO_BUFFER_MS, 10, 500, "%d ms");
"Audio", "BufferMS", AudioStreamParameters::DEFAULT_BUFFER_MS, 10, 500, "%d ms");
const u32 output_latency =
GetEffectiveUIntSetting(bsi, "Audio", "OutputLatencyMS", Settings::DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
GetEffectiveUIntSetting(bsi, "Audio", "OutputLatencyMS", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);
bool output_latency_minimal = (output_latency == 0);
if (ToggleButton(FSUI_CSTR("Minimal Output Latency"),
FSUI_CSTR("When enabled, the minimum supported output latency will be used for the host API."),
&output_latency_minimal))
{
bsi->SetUIntValue("Audio", "OutputLatencyMS",
output_latency_minimal ? 0 : Settings::DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
output_latency_minimal ? 0 : AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);
SetSettingsChanged(bsi);
}
if (!output_latency_minimal)
@@ -4827,7 +4830,7 @@ void FullscreenUI::DrawAudioSettingsPage()
bsi, FSUI_CSTR("Output Latency"),
FSUI_CSTR("Determines how much latency there is between the audio being picked up by the host API, and "
"played through speakers."),
"Audio", "OutputLatencyMS", Settings::DEFAULT_AUDIO_OUTPUT_LATENCY_MS, 1, 500, "%d ms");
"Audio", "OutputLatencyMS", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS, 1, 500, "%d ms");
}
EndMenuButtons();
@@ -7109,6 +7112,7 @@ TRANSLATE_NOOP("FullscreenUI", "Depth Buffer");
TRANSLATE_NOOP("FullscreenUI", "Desktop Mode");
TRANSLATE_NOOP("FullscreenUI", "Details");
TRANSLATE_NOOP("FullscreenUI", "Details unavailable for game not scanned in game list.");
TRANSLATE_NOOP("FullscreenUI", "Determines how audio is expanded from stereo to surround for supported games.");
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitor are.");
TRANSLATE_NOOP("FullscreenUI", "Determines how much latency there is between the audio being picked up by the host API, and played through speakers.");
TRANSLATE_NOOP("FullscreenUI", "Determines how much of the area typically not visible on a consumer TV set to crop/hide.");
@@ -7182,6 +7186,7 @@ TRANSLATE_NOOP("FullscreenUI", "Exit And Save State");
TRANSLATE_NOOP("FullscreenUI", "Exit DuckStation");
TRANSLATE_NOOP("FullscreenUI", "Exit Without Saving");
TRANSLATE_NOOP("FullscreenUI", "Exits Big Picture mode, returning to the desktop interface.");
TRANSLATE_NOOP("FullscreenUI", "Expansion Mode");
TRANSLATE_NOOP("FullscreenUI", "Failed to copy text to clipboard.");
TRANSLATE_NOOP("FullscreenUI", "Failed to delete save state.");
TRANSLATE_NOOP("FullscreenUI", "Failed to delete {}.");

View File

@@ -380,34 +380,3 @@ void Host::ReleaseGPUDevice()
g_gpu_device.reset();
}
#ifndef __ANDROID__
std::unique_ptr<AudioStream> Host::CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
u32 latency_ms, AudioStretchMode stretch)
{
switch (backend)
{
#ifdef ENABLE_CUBEB
case AudioBackend::Cubeb:
return AudioStream::CreateCubebAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
#ifdef ENABLE_SDL2
case AudioBackend::SDL:
return AudioStream::CreateSDLAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
#ifdef _WIN32
case AudioBackend::XAudio2:
return AudioStream::CreateXAudio2Stream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
case AudioBackend::Null:
return AudioStream::CreateNullStream(sample_rate, channels, buffer_ms);
default:
return nullptr;
}
}
#endif

View File

@@ -21,6 +21,7 @@
class SettingsInterface;
struct WindowInfo;
enum class AudioBackend : u8;
enum class AudioExpansionMode : u8;
enum class AudioStretchMode : u8;
enum class RenderAPI : u32;
class AudioStream;
@@ -72,11 +73,6 @@ SettingsInterface* GetSettingsInterface();
/// If an input profile is being used, this will be the input layer, otherwise the layered interface.
SettingsInterface* GetSettingsInterfaceForBindings();
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
u32 latency_ms, AudioStretchMode stretch);
/// Debugger feedback.
void ReportDebuggerMessage(const std::string_view& message);
void ReportFormattedDebuggerMessage(const char* format, ...);

View File

@@ -306,16 +306,12 @@ void Settings::Load(SettingsInterface& si)
cdrom_seek_speedup = si.GetIntValue("CDROM", "SeekSpeedup", 1);
audio_backend =
ParseAudioBackend(si.GetStringValue("Audio", "Backend", GetAudioBackendName(DEFAULT_AUDIO_BACKEND)).c_str())
.value_or(DEFAULT_AUDIO_BACKEND);
AudioStream::ParseBackendName(
si.GetStringValue("Audio", "Backend", AudioStream::GetBackendName(AudioStream::DEFAULT_BACKEND)).c_str())
.value_or(AudioStream::DEFAULT_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())
.value_or(DEFAULT_AUDIO_STRETCH_MODE);
audio_output_latency_ms = si.GetUIntValue("Audio", "OutputLatencyMS", DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
audio_buffer_ms = si.GetUIntValue("Audio", "BufferMS", DEFAULT_AUDIO_BUFFER_MS);
audio_stream_parameters.Load(si, "Audio");
audio_output_volume = si.GetUIntValue("Audio", "OutputVolume", 100);
audio_fast_forward_volume = si.GetUIntValue("Audio", "FastForwardVolume", 100);
@@ -422,6 +418,9 @@ void Settings::Load(SettingsInterface& si)
si.GetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold", 128);
#ifdef __ANDROID__
// No expansion due to license incompatibility.
audio_expansion_mode = AudioExpansionMode::Disabled;
// Android users are incredibly silly and don't understand that stretch is in the aspect ratio list...
if (si.GetBoolValue("Display", "Stretch", false))
display_aspect_ratio = DisplayAspectRatio::MatchWindow;
@@ -563,12 +562,10 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetIntValue("CDROM", "ReadSpeedup", cdrom_read_speedup);
si.SetIntValue("CDROM", "SeekSpeedup", cdrom_seek_speedup);
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
si.SetStringValue("Audio", "Backend", AudioStream::GetBackendName(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);
audio_stream_parameters.Save(si, "Audio");
si.SetUIntValue("Audio", "OutputVolume", audio_output_volume);
si.SetUIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
si.SetBoolValue("Audio", "OutputMuted", audio_output_muted);
@@ -1570,64 +1567,9 @@ const char* Settings::GetDisplayScreenshotFormatExtension(DisplayScreenshotForma
return s_display_screenshot_format_extensions[static_cast<size_t>(format)];
}
static constexpr const std::array s_audio_backend_names = {
"Null",
#ifdef ENABLE_CUBEB
"Cubeb",
#endif
#ifdef ENABLE_SDL2
"SDL",
#endif
#ifdef _WIN32
"XAudio2",
#endif
#ifdef __ANDROID__
"AAudio", "OpenSLES",
#endif
};
static constexpr const std::array s_audio_backend_display_names = {
TRANSLATE_NOOP("AudioBackend", "Null (No Output)"),
#ifdef ENABLE_CUBEB
TRANSLATE_NOOP("AudioBackend", "Cubeb"),
#endif
#ifdef ENABLE_SDL2
TRANSLATE_NOOP("AudioBackend", "SDL"),
#endif
#ifdef _WIN32
TRANSLATE_NOOP("AudioBackend", "XAudio2"),
#endif
#ifdef __ANDROID__
"AAudio",
"OpenSL ES",
#endif
};
std::optional<AudioBackend> Settings::ParseAudioBackend(const char* str)
{
int index = 0;
for (const char* name : s_audio_backend_names)
{
if (StringUtil::Strcasecmp(name, str) == 0)
return static_cast<AudioBackend>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetAudioBackendName(AudioBackend backend)
{
return s_audio_backend_names[static_cast<int>(backend)];
}
const char* Settings::GetAudioBackendDisplayName(AudioBackend backend)
{
return Host::TranslateToCString("AudioBackend", s_audio_backend_display_names[static_cast<int>(backend)]);
}
static constexpr const std::array s_controller_type_names = {
"None", "DigitalController", "AnalogController", "AnalogJoystick", "GunCon", "PlayStationMouse", "NeGcon", "NeGconRumble"};
"None", "DigitalController", "AnalogController", "AnalogJoystick",
"GunCon", "PlayStationMouse", "NeGcon", "NeGconRumble"};
static constexpr const std::array s_controller_display_names = {
TRANSLATE_NOOP("ControllerType", "None"),
TRANSLATE_NOOP("ControllerType", "Digital Controller"),
@@ -2031,8 +1973,9 @@ static const char* s_log_filters[] = {
"WAVWriter",
"WindowInfo",
#ifdef ENABLE_CUBEB
#ifndef __ANDROID__
"CubebAudioStream",
"SDLAudioStream",
#endif
#ifdef ENABLE_OPENGL

View File

@@ -178,14 +178,12 @@ struct Settings
u32 cdrom_read_speedup = 1;
u32 cdrom_seek_speedup = 1;
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;
u32 audio_fast_forward_volume = 100;
AudioStreamParameters audio_stream_parameters;
AudioBackend audio_backend = AudioStream::DEFAULT_BACKEND;
bool audio_output_muted : 1 = false;
bool use_old_mdec_routines : 1 = false;
@@ -432,10 +430,6 @@ struct Settings
static const char* GetDisplayScreenshotFormatDisplayName(DisplayScreenshotFormat mode);
static const char* GetDisplayScreenshotFormatExtension(DisplayScreenshotFormat mode);
static std::optional<AudioBackend> ParseAudioBackend(const char* str);
static const char* GetAudioBackendName(AudioBackend backend);
static const char* GetAudioBackendDisplayName(AudioBackend backend);
static std::optional<ControllerType> ParseControllerTypeName(std::string_view str);
static const char* GetControllerTypeName(ControllerType type);
static const char* GetControllerTypeDisplayName(ControllerType type);
@@ -478,18 +472,6 @@ struct Settings
static constexpr CPUFastmemMode DEFAULT_CPU_FASTMEM_MODE = CPUFastmemMode::Disabled;
#endif
#if defined(ENABLE_CUBEB)
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Cubeb;
#elif defined(_WIN32)
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::XAudio2;
#elif defined(__ANDROID__)
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::AAudio;
#elif defined(ENABLE_SDL2)
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::SDL;
#else
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Null;
#endif
static constexpr DisplayDeinterlacingMode DEFAULT_DISPLAY_DEINTERLACING_MODE = DisplayDeinterlacingMode::Adaptive;
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
@@ -517,15 +499,6 @@ struct Settings
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
#ifndef __ANDROID__
static constexpr u32 DEFAULT_AUDIO_BUFFER_MS = 50;
static constexpr u32 DEFAULT_AUDIO_OUTPUT_LATENCY_MS = 20;
#else
static constexpr u32 DEFAULT_AUDIO_BUFFER_MS = 100;
static constexpr u32 DEFAULT_AUDIO_OUTPUT_LATENCY_MS = 20;
#endif
static constexpr AudioStretchMode DEFAULT_AUDIO_STRETCH_MODE = AudioStretchMode::TimeStretch;
static constexpr bool DEFAULT_SAVE_STATE_COMPRESSION = true;
// Enable console logging by default on Linux platforms.

View File

@@ -16,6 +16,7 @@
#include "common/bitfield.h"
#include "common/bitutils.h"
#include "common/error.h"
#include "common/fifo_queue.h"
#include "common/log.h"
#include "common/path.h"
@@ -43,7 +44,6 @@ namespace {
enum : u32
{
SPU_BASE = 0x1F801C00,
NUM_CHANNELS = 2,
NUM_VOICES = 24,
NUM_VOICE_REGISTERS = 8,
VOICE_ADDRESS_SHIFT = 3,
@@ -422,7 +422,7 @@ void SPU::Initialize()
&SPU::Execute, nullptr, false);
s_transfer_event = TimingEvents::CreateTimingEvent(
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, &SPU::ExecuteTransfer, nullptr, false);
s_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
s_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, g_settings.audio_stream_parameters.buffer_ms);
CreateOutputStream();
Reset();
@@ -430,19 +430,24 @@ void SPU::Initialize()
void SPU::CreateOutputStream()
{
Log_InfoFmt("Creating '{}' audio stream, sample rate = {}, channels = {}, buffer = {}, latency = {}, stretching = {}",
Settings::GetAudioBackendName(g_settings.audio_backend), static_cast<u32>(SAMPLE_RATE),
static_cast<u32>(NUM_CHANNELS), g_settings.audio_buffer_ms, g_settings.audio_output_latency_ms,
AudioStream::GetStretchModeName(g_settings.audio_stretch_mode));
Log_InfoFmt(
"Creating '{}' audio stream, sample rate = {}, expansion = {}, buffer = {}, latency = {}, stretching = {}",
AudioStream::GetBackendName(g_settings.audio_backend), static_cast<u32>(SAMPLE_RATE),
AudioStream::GetExpansionModeName(g_settings.audio_stream_parameters.expansion_mode),
g_settings.audio_stream_parameters.buffer_ms, g_settings.audio_stream_parameters.output_latency_ms,
AudioStream::GetStretchModeName(g_settings.audio_stream_parameters.stretch_mode));
Error error;
s_audio_stream =
Host::CreateAudioStream(g_settings.audio_backend, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms,
g_settings.audio_output_latency_ms, g_settings.audio_stretch_mode);
AudioStream::CreateStream(g_settings.audio_backend, SAMPLE_RATE, g_settings.audio_stream_parameters, &error);
if (!s_audio_stream)
{
Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output.");
Host::ReportErrorAsync(
"Error",
fmt::format("Failed to create or configure audio stream, falling back to null output. The error was:\n{}",
error.GetDescription()));
s_audio_stream.reset();
s_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
s_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, g_settings.audio_stream_parameters.buffer_ms);
}
s_audio_stream->SetOutputVolume(System::GetAudioOutputVolume());

View File

@@ -2748,8 +2748,8 @@ void System::UpdateSpeedLimiterState()
s_pre_frame_sleep = s_throttler_enabled && g_settings.display_pre_frame_sleep;
s_syncing_to_host = false;
if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) &&
s_target_speed == 1.0f && IsValid())
if (g_settings.sync_to_host_refresh_rate &&
(g_settings.audio_stream_parameters.stretch_mode != AudioStretchMode::Off) && s_target_speed == 1.0f && IsValid())
{
float host_refresh_rate;
if (g_gpu_device->GetHostRefreshRate(&host_refresh_rate))
@@ -2780,7 +2780,8 @@ void System::UpdateSpeedLimiterState()
// Adjust nominal rate when resampling, or syncing to host.
const bool rate_adjust =
(s_syncing_to_host || g_settings.audio_stretch_mode == AudioStretchMode::Resample) && s_target_speed > 0.0f;
(s_syncing_to_host || g_settings.audio_stream_parameters.stretch_mode == AudioStretchMode::Resample) &&
s_target_speed > 0.0f;
stream->SetNominalRate(rate_adjust ? s_target_speed : 1.0f);
if (old_target_speed < s_target_speed)
@@ -3714,17 +3715,15 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
{
Host::AddIconOSDMessage("AudioBackendSwitch", ICON_FA_HEADPHONES,
fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} audio backend."),
Settings::GetAudioBackendName(g_settings.audio_backend)),
AudioStream::GetBackendDisplayName(g_settings.audio_backend)),
Host::OSD_INFO_DURATION);
}
SPU::RecreateOutputStream();
}
if (g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode);
if (g_settings.audio_buffer_ms != old_settings.audio_buffer_ms ||
g_settings.audio_output_latency_ms != old_settings.audio_output_latency_ms ||
g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
if (g_settings.audio_stream_parameters.stretch_mode != old_settings.audio_stream_parameters.stretch_mode)
SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stream_parameters.stretch_mode);
if (g_settings.audio_stream_parameters != old_settings.audio_stream_parameters)
{
SPU::RecreateOutputStream();
UpdateSpeedLimiterState();

View File

@@ -185,25 +185,6 @@ enum class DisplayScreenshotFormat : u8
Count
};
enum class AudioBackend : u8
{
Null,
#ifdef ENABLE_CUBEB
Cubeb,
#endif
#ifdef ENABLE_SDL2
SDL,
#endif
#ifdef _WIN32
XAudio2,
#endif
#ifdef __ANDROID__
AAudio,
OpenSLES,
#endif
Count
};
enum class ControllerType
{
None,