6 changed files with 224 additions and 3 deletions
-
6src/audio_core/CMakeLists.txt
-
156src/audio_core/sink/ps4_sink.cpp
-
43src/audio_core/sink/ps4_sink.h
-
17src/audio_core/sink/sink_details.cpp
-
3src/common/settings_enums.h
-
2src/core/hle/service/services.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
|
||||
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue