mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-25 20:25:38 -06:00
* Logic update, no QT ui * Fixing errors * Gui boxes * fixes * prevent device list refreshing too fast when game not running * Removed duplicate Socket declarations in kernel/file_system.cpp and fs.h * Fixed clang-format and micDevice errors * Ran clang-format and fixed rebase compiler issues * Settings dialog fix * Addressed squidbus' concerns * Update config.cpp to adhere to clang-format * Removed a space causing clang-format to complain * Addressed squidbus' concerns and added fallbacks Concerns: - Changed dev_name construct to remove unnecessary cast - Added an invalid AudioDeviceID macro to replace magic number --------- Co-authored-by: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com>
157 lines
6.2 KiB
C++
157 lines
6.2 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <algorithm>
|
|
#include <thread>
|
|
#include <SDL3/SDL_audio.h>
|
|
#include <SDL3/SDL_hints.h>
|
|
|
|
#include "common/config.h"
|
|
#include "common/logging/log.h"
|
|
#include "core/libraries/audio/audioout.h"
|
|
#include "core/libraries/audio/audioout_backend.h"
|
|
|
|
#define SDL_INVALID_AUDIODEVICEID 0 // Defined in SDL_audio.h but not made a macro
|
|
namespace Libraries::AudioOut {
|
|
|
|
class SDLPortBackend : public PortBackend {
|
|
public:
|
|
explicit SDLPortBackend(const PortOut& port)
|
|
: frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) {
|
|
const SDL_AudioSpec fmt = {
|
|
.format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
|
|
.channels = port.format_info.num_channels,
|
|
.freq = static_cast<int>(port.sample_rate),
|
|
};
|
|
|
|
// Determine port type
|
|
std::string port_name = port.type == OrbisAudioOutPort::PadSpk
|
|
? Config::getPadSpkOutputDevice()
|
|
: Config::getMainOutputDevice();
|
|
SDL_AudioDeviceID dev_id = SDL_INVALID_AUDIODEVICEID;
|
|
if (port_name == "None") {
|
|
stream = nullptr;
|
|
return;
|
|
} else if (port_name == "Default Device") {
|
|
dev_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
|
} else {
|
|
try {
|
|
SDL_AudioDeviceID* dev_array = SDL_GetAudioPlaybackDevices(nullptr);
|
|
for (; dev_array != 0;) {
|
|
std::string dev_name(SDL_GetAudioDeviceName(*dev_array));
|
|
if (dev_name == port_name) {
|
|
dev_id = *dev_array;
|
|
break;
|
|
} else {
|
|
dev_array++;
|
|
}
|
|
}
|
|
if (dev_id == SDL_INVALID_AUDIODEVICEID) {
|
|
LOG_WARNING(Lib_AudioOut, "Audio device not found: {}", port_name);
|
|
dev_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
|
}
|
|
} catch (const std::exception& e) {
|
|
LOG_ERROR(Lib_AudioOut, "Invalid audio output device: {}", port_name);
|
|
stream = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Open the audio stream
|
|
stream = SDL_OpenAudioDeviceStream(dev_id, &fmt, nullptr, nullptr);
|
|
if (stream == nullptr) {
|
|
LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
|
|
return;
|
|
}
|
|
CalculateQueueThreshold();
|
|
if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
|
|
port.format_info.num_channels)) {
|
|
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
|
|
SDL_GetError());
|
|
SDL_DestroyAudioStream(stream);
|
|
stream = nullptr;
|
|
return;
|
|
}
|
|
if (!SDL_ResumeAudioStreamDevice(stream)) {
|
|
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
|
|
SDL_DestroyAudioStream(stream);
|
|
stream = nullptr;
|
|
return;
|
|
}
|
|
SDL_SetAudioStreamGain(stream, Config::getVolumeSlider() / 100.0f);
|
|
}
|
|
|
|
~SDLPortBackend() override {
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
SDL_DestroyAudioStream(stream);
|
|
stream = nullptr;
|
|
}
|
|
|
|
void Output(void* ptr) override {
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
// AudioOut library manages timing, but we still need to guard against the SDL
|
|
// audio queue stalling, which may happen during device changes, for example.
|
|
// Otherwise, latency may grow over time unbounded.
|
|
if (const auto queued = SDL_GetAudioStreamQueued(stream); queued >= queue_threshold) {
|
|
LOG_INFO(Lib_AudioOut, "SDL audio queue backed up ({} queued, {} threshold), clearing.",
|
|
queued, queue_threshold);
|
|
SDL_ClearAudioStream(stream);
|
|
// Recalculate the threshold in case this happened because of a device change.
|
|
CalculateQueueThreshold();
|
|
}
|
|
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(guest_buffer_size))) {
|
|
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
|
|
}
|
|
}
|
|
|
|
void SetVolume(const std::array<int, 8>& ch_volumes) override {
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
// SDL does not have per-channel volumes, for now just take the maximum of the channels.
|
|
const auto vol = *std::ranges::max_element(ch_volumes);
|
|
if (!SDL_SetAudioStreamGain(stream, static_cast<float>(vol) / SCE_AUDIO_OUT_VOLUME_0DB *
|
|
Config::getVolumeSlider() / 100.0f)) {
|
|
LOG_WARNING(Lib_AudioOut, "Failed to change SDL audio stream volume: {}",
|
|
SDL_GetError());
|
|
}
|
|
}
|
|
|
|
private:
|
|
void CalculateQueueThreshold() {
|
|
SDL_AudioSpec discard;
|
|
int sdl_buffer_frames;
|
|
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
|
|
&sdl_buffer_frames)) {
|
|
LOG_WARNING(Lib_AudioOut, "Failed to get SDL audio stream buffer size: {}",
|
|
SDL_GetError());
|
|
sdl_buffer_frames = 0;
|
|
}
|
|
const auto sdl_buffer_size = sdl_buffer_frames * frame_size;
|
|
const auto new_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4;
|
|
if (host_buffer_size != sdl_buffer_size || queue_threshold != new_threshold) {
|
|
host_buffer_size = sdl_buffer_size;
|
|
queue_threshold = new_threshold;
|
|
LOG_INFO(Lib_AudioOut,
|
|
"SDL audio buffers: guest = {} bytes, host = {} bytes, threshold = {} bytes",
|
|
guest_buffer_size, host_buffer_size, queue_threshold);
|
|
}
|
|
}
|
|
|
|
u32 frame_size;
|
|
u32 guest_buffer_size;
|
|
u32 host_buffer_size{};
|
|
u32 queue_threshold{};
|
|
SDL_AudioStream* stream{};
|
|
};
|
|
|
|
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {
|
|
return std::make_unique<SDLPortBackend>(port);
|
|
}
|
|
|
|
} // namespace Libraries::AudioOut
|