Browse Source

native ps4 audio sink

eden-orbis-ps4
lizzie 3 days ago
parent
commit
ad8d6de8bc
  1. 6
      src/audio_core/CMakeLists.txt
  2. 156
      src/audio_core/sink/ps4_sink.cpp
  3. 43
      src/audio_core/sink/ps4_sink.h
  4. 17
      src/audio_core/sink/sink_details.cpp
  5. 3
      src/common/settings_enums.h
  6. 2
      src/core/hle/service/services.cpp

6
src/audio_core/CMakeLists.txt

@ -256,4 +256,10 @@ else()
target_compile_definitions(audio_core PRIVATE HAVE_SDL2) target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif() endif()
if(PLATFORM_PS4)
target_sources(audio_core PRIVATE
sink/ps4_sink.cpp
sink/ps4_sink.h)
endif()
create_target_directory_groups(audio_core) create_target_directory_groups(audio_core)

156
src/audio_core/sink/ps4_sink.cpp

@ -0,0 +1,156 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <span>
#include <stop_token>
#include <vector>
#include <orbis/AudioOut.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/ps4_sink.h"
#include "audio_core/sink/sink_stream.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
namespace AudioCore::Sink {
/// @brief PS4 sink stream, responsible for sinking samples to hardware.
struct PS4SinkStream final : public SinkStream {
/// @brief Create a new sink stream.
/// @param device_channels_ - Number of channels supported by the hardware.
/// @param system_channels_ - Number of channels the audio systems expect.
/// @param output_device - Name of the output device to use for this stream.
/// @param input_device - Name of the input device to use for this stream.
/// @param type_ - Type of this stream.
/// @param system_ - Core system.
/// @param event - Event used only for audio renderer, signalled on buffer consume.
PS4SinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, const std::string& input_device, StreamType type_, Core::System& system_)
: SinkStream{system_, type_}
{
system_channels = system_channels_;
device_channels = device_channels_;
auto const length = 0x800;
auto const sample_rate = 48000;
auto const num_channels = this->GetDeviceChannels();
output_buffer.resize(length * num_channels * sizeof(s16));
auto const param_type = num_channels == 1 ? ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO : ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO;
audio_dev = sceAudioOutOpen(ORBIS_USER_SERVICE_USER_ID_SYSTEM, ORBIS_AUDIO_OUT_PORT_TYPE_MAIN, 0, length, sample_rate, param_type);
if (audio_dev > 0) {
audio_thread = std::jthread([=, this](std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
if (this->type == StreamType::In) {
// this->ProcessAudioIn(input_buffer, length);
} else {
sceAudioOutOutput(audio_dev, nullptr);
this->ProcessAudioOutAndRender(output_buffer, length);
sceAudioOutOutput(audio_dev, output_buffer.data());
}
}
});
} else {
LOG_ERROR(Service_Audio, "Failed to create audio device! {:#x}", uint32_t(audio_dev));
}
}
~PS4SinkStream() override {
LOG_DEBUG(Service_Audio, "Destroying PS4 stream {}", name);
sceAudioOutClose(audio_dev);
if (audio_thread.joinable()) {
audio_thread.request_stop();
audio_thread.join();
}
}
void Finalize() override {
if (audio_dev > 0) {
Stop();
sceAudioOutClose(audio_dev);
}
}
void Start(bool resume = false) override {
if (audio_dev > 0 && paused) {
paused = false;
}
}
void Stop() override {
if (audio_dev > 0 && !paused) {
}
}
std::vector<s16> output_buffer;
std::jthread audio_thread;
int32_t audio_dev{};
};
PS4Sink::PS4Sink(std::string_view target_device_name) {
int32_t rc = sceAudioOutInit();
if (rc == 0 || unsigned(rc) == ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT) {
if (target_device_name != auto_device_name && !target_device_name.empty()) {
output_device = target_device_name;
} else {
output_device.clear();
}
device_channels = 2;
} else {
LOG_ERROR(Service_Audio, "Unable to open audio out! {:#x}", uint32_t(rc));
}
}
PS4Sink::~PS4Sink() = default;
/// @brief Create a new sink stream.
/// @param system - Core system.
/// @param system_channels - Number of channels the audio system expects. May differ from the device's channel count.
/// @param name - Name of this stream.
/// @param type - Type of this stream, render/in/out.
/// @return A pointer to the created SinkStream
SinkStream* PS4Sink::AcquireSinkStream(Core::System& system, u32 system_channels_, const std::string&, StreamType type) {
system_channels = system_channels_;
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<PS4SinkStream>(device_channels, system_channels, output_device, input_device, type, system));
return stream.get();
}
void PS4Sink::CloseStream(SinkStream* stream) {
for (size_t i = 0; i < sink_streams.size(); i++) {
if (sink_streams[i].get() == stream) {
sink_streams[i].reset();
sink_streams.erase(sink_streams.begin() + i);
break;
}
}
}
void PS4Sink::CloseStreams() {
sink_streams.clear();
}
f32 PS4Sink::GetDeviceVolume() const {
return sink_streams.size() > 0 ? sink_streams[0]->GetDeviceVolume() : 1.f;
}
void PS4Sink::SetDeviceVolume(f32 volume) {
for (auto& stream : sink_streams)
stream->SetDeviceVolume(volume);
}
void PS4Sink::SetSystemVolume(f32 volume) {
for (auto& stream : sink_streams)
stream->SetSystemVolume(volume);
}
std::vector<std::string> ListPS4SinkDevices(bool capture) {
return {{"Default"}};
}
u32 GetPS4Latency() {
return TargetSampleCount * 2;
}
} // namespace AudioCore::Sink

43
src/audio_core/sink/ps4_sink.h

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <vector>
#include "audio_core/sink/sink.h"
namespace Core {
class System;
}
namespace AudioCore::Sink {
class SinkStream;
/// @brief PS4 backend sink, holds multiple output streams and is responsible for sinking samples to
/// hardware. Used by Audio Render, Audio In and Audio Out.
struct PS4Sink final : public Sink {
explicit PS4Sink(std::string_view device_id);
~PS4Sink() override;
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, const std::string& name, StreamType type) override;
void CloseStream(SinkStream* stream) override;
void CloseStreams() override;
f32 GetDeviceVolume() const override;
void SetDeviceVolume(f32 volume) override;
void SetSystemVolume(f32 volume) override;
/// Name of the output device used by streams
std::string output_device;
/// Name of the input device used by streams
std::string input_device;
/// Vector of streams managed by this sink
std::vector<SinkStreamPtr> sink_streams;
};
std::vector<std::string> ListPS4SinkDevices(bool capture);
u32 GetPS4Latency();
} // namespace AudioCore::Sink

17
src/audio_core/sink/sink_details.cpp

@ -19,6 +19,9 @@
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
#include "audio_core/sink/sdl2_sink.h" #include "audio_core/sink/sdl2_sink.h"
#endif #endif
#ifdef __OPENORBIS__
#include "audio_core/sink/ps4_sink.h"
#endif
#include "audio_core/sink/null_sink.h" #include "audio_core/sink/null_sink.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
@ -51,6 +54,16 @@ struct SinkDetails {
// sink_details is ordered in terms of desirability, with the best choice at the top. // sink_details is ordered in terms of desirability, with the best choice at the top.
constexpr SinkDetails sink_details[] = { constexpr SinkDetails sink_details[] = {
#ifdef __OPENORBIS__
SinkDetails{
Settings::AudioEngine::Ps4,
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<PS4Sink>(device_id);
},
&ListPS4SinkDevices,
&GetPS4Latency,
},
#endif
#ifdef HAVE_OBOE #ifdef HAVE_OBOE
SinkDetails{ SinkDetails{
Settings::AudioEngine::Oboe, Settings::AudioEngine::Oboe,
@ -115,7 +128,9 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX // BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
// causes audio issues, in that case go with SDL. // causes audio issues, in that case go with SDL.
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
#if defined(__OPENORBIS__)
iter = find_backend(Settings::AudioEngine::Ps4);
#elif defined(HAVE_CUBEB) && defined(HAVE_SDL2)
iter = find_backend(Settings::AudioEngine::Cubeb); iter = find_backend(Settings::AudioEngine::Cubeb);
if (iter->latency() > TargetSampleCount * 3) { if (iter->latency() > TargetSampleCount * 3) {
iter = find_backend(Settings::AudioEngine::Sdl2); iter = find_backend(Settings::AudioEngine::Sdl2);

3
src/common/settings_enums.h

@ -92,12 +92,13 @@ struct EnumMetadata {
// AudioEngine must be specified discretely due to having existing but slightly different // AudioEngine must be specified discretely due to having existing but slightly different
// canonicalizations // canonicalizations
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, };
enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, Ps4 };
template<> template<>
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() { inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
return { return {
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
{"ps4", AudioEngine::Ps4},
}; };
} }
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here /// @brief This is just a sufficiently large number that is more than the number of other enums declared here

2
src/core/hle/service/services.cpp

@ -100,7 +100,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"nvservices", &Nvidia::LoopProcess}, {"nvservices", &Nvidia::LoopProcess},
{"bsdsocket", &Sockets::LoopProcess}, {"bsdsocket", &Sockets::LoopProcess},
}) { }) {
if (run_on_host) kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
if (run_on_host) kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach();
else kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); else kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
} }
if (run_on_host) kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); if (run_on_host) kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();

Loading…
Cancel
Save