basic work for openal and audio3d

This commit is contained in:
georgemoralis 2026-02-10 16:28:48 +02:00
parent 140f73eadf
commit 25182c4962
5 changed files with 765 additions and 5 deletions

View File

@ -297,6 +297,11 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/openal_manager.h
src/core/libraries/ngs2/ngs2.cpp
src/core/libraries/ngs2/ngs2.h
src/core/libraries/audio3d/audio3d.cpp
src/core/libraries/audio3d/audio3d_openal.cpp
src/core/libraries/audio3d/audio3d_openal.h
src/core/libraries/audio3d/audio3d.h
src/core/libraries/audio3d/audio3d_error.h
)
set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
@ -458,9 +463,6 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/ngs2/ngs2_submixer.cpp
src/core/libraries/ngs2/ngs2_submixer.h
src/core/libraries/ajm/ajm_error.h
src/core/libraries/audio3d/audio3d.cpp
src/core/libraries/audio3d/audio3d.h
src/core/libraries/audio3d/audio3d_error.h
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
src/core/libraries/game_live_streaming/gamelivestreaming.h
src/core/libraries/remote_play/remoteplay.cpp

View File

@ -206,7 +206,7 @@ s32 PS4_SYSV_ABI sceAudioOutInit() {
return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT;
}
audio = std::make_unique<SDLAudioOut>();
audio = std::make_unique<OpenALAudioOut>();
LOG_INFO(Lib_AudioOut, "Audio system initialized");
return ORBIS_OK;

View File

@ -0,0 +1,612 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/audio/audioout.h"
#include "core/libraries/audio/audioout_error.h"
#include "core/libraries/audio3d/audio3d_openal.h"
#include "core/libraries/audio3d/audio3d_error.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
namespace Libraries::Audio3dOpenAL {
static constexpr u32 AUDIO3D_SAMPLE_RATE = 48000;
static constexpr AudioOut::OrbisAudioOutParamFormat AUDIO3D_OUTPUT_FORMAT =
AudioOut::OrbisAudioOutParamFormat::S16Stereo;
static constexpr u32 AUDIO3D_OUTPUT_NUM_CHANNELS = 2;
static constexpr u32 AUDIO3D_OUTPUT_BUFFER_FRAMES = 0x100;
static std::unique_ptr<Audio3dState> state;
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) {
LOG_INFO(Lib_Audio3d, "called, handle = {}", handle);
return AudioOut::sceAudioOutClose(handle);
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(
const OrbisAudio3dPortId port_id, const Libraries::UserService::OrbisUserServiceUserId user_id,
s32 type, const s32 index, const u32 len, const u32 freq,
const AudioOut::OrbisAudioOutParamExtendedInformation param) {
LOG_INFO(Lib_Audio3d,
"called, port_id = {}, user_id = {}, type = {}, index = {}, len = {}, freq = {}",
port_id, user_id, type, index, len, freq);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
if (len != state->ports[port_id].parameters.granularity) {
LOG_ERROR(Lib_Audio3d, "len != state->ports[port_id].parameters.granularity");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return sceAudioOutOpen(user_id, static_cast<AudioOut::OrbisAudioOutPort>(type), index, len,
freq, param);
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(const s32 handle, void* ptr) {
LOG_DEBUG(Lib_Audio3d, "called, handle = {}, ptr = {}", handle, ptr);
if (!ptr) {
LOG_ERROR(Lib_Audio3d, "!ptr");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (handle < 0 || (handle & 0xFFFF) > 25) {
LOG_ERROR(Lib_Audio3d, "handle < 0 || (handle & 0xFFFF) > 25");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
return AudioOut::sceAudioOutOutput(handle, ptr);
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* param,
const u32 num) {
LOG_DEBUG(Lib_Audio3d, "called, param = {}, num = {}", static_cast<void*>(param), num);
if (!param || !num) {
LOG_ERROR(Lib_Audio3d, "!param || !num");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return AudioOut::sceAudioOutOutputs(param, num);
}
// Simple audio conversion to stereo S16
static void ConvertAudioToStereoS16(const OrbisAudio3dPcm& pcm, const u32 num_channels,
u8** out_data, u32* out_size) {
const u32 num_samples = pcm.num_samples;
const u32 output_samples = num_samples * 2; // Stereo
*out_size = output_samples * sizeof(s16);
*out_data = static_cast<u8*>(malloc(*out_size));
s16* dst = reinterpret_cast<s16*>(*out_data);
if (pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
const s16* src = static_cast<const s16*>(pcm.sample_buffer);
if (num_channels == 2) {
// Already stereo S16, just copy
std::memcpy(dst, src, num_samples * 2 * sizeof(s16));
} else if (num_channels == 8) {
// 8 channel to stereo - downmix (just take FL and FR)
for (u32 i = 0; i < num_samples; i++) {
dst[i * 2 + 0] = src[i * 8 + 0]; // FL
dst[i * 2 + 1] = src[i * 8 + 1]; // FR
}
} else {
// Mono to stereo
for (u32 i = 0; i < num_samples; i++) {
dst[i * 2 + 0] = src[i];
dst[i * 2 + 1] = src[i];
}
}
} else {
// Float to S16
const float* src = static_cast<const float*>(pcm.sample_buffer);
if (num_channels == 2) {
// Stereo float to stereo S16
for (u32 i = 0; i < output_samples; i++) {
float sample = src[i] * 32768.0f;
dst[i] = static_cast<s16>(std::clamp(sample, -32768.0f, 32767.0f));
}
} else if (num_channels == 8) {
// 8 channel float to stereo S16 - downmix
for (u32 i = 0; i < num_samples; i++) {
float left = src[i * 8 + 0] * 32768.0f; // FL
float right = src[i * 8 + 1] * 32768.0f; // FR
dst[i * 2 + 0] = static_cast<s16>(std::clamp(left, -32768.0f, 32767.0f));
dst[i * 2 + 1] = static_cast<s16>(std::clamp(right, -32768.0f, 32767.0f));
}
} else {
// Mono float to stereo S16
for (u32 i = 0; i < num_samples; i++) {
float sample = src[i] * 32768.0f;
s16 converted = static_cast<s16>(std::clamp(sample, -32768.0f, 32767.0f));
dst[i * 2 + 0] = converted;
dst[i * 2 + 1] = converted;
}
}
}
}
static s32 PortQueueAudio(Port& port, const OrbisAudio3dPcm& pcm, const u32 num_channels) {
u8* dst_data = nullptr;
u32 dst_size = 0;
// Convert audio to stereo S16
ConvertAudioToStereoS16(pcm, num_channels, &dst_data, &dst_size);
if (!dst_data) {
LOG_ERROR(Lib_Audio3d, "Failed to convert audio samples");
return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY;
}
port.queue.emplace_back(AudioData{
.sample_buffer = dst_data,
.num_samples = pcm.num_samples,
});
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dBedWrite(const OrbisAudio3dPortId port_id, const u32 num_channels,
const OrbisAudio3dFormat format, void* buffer,
const u32 num_samples) {
return sceAudio3dBedWrite2(port_id, num_channels, format, buffer, num_samples,
OrbisAudio3dOutputRoute::ORBIS_AUDIO3D_OUTPUT_BOTH, false);
}
s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32 num_channels,
const OrbisAudio3dFormat format, void* buffer,
const u32 num_samples,
const OrbisAudio3dOutputRoute output_route,
const bool restricted) {
LOG_DEBUG(
Lib_Audio3d,
"called, port_id = {}, num_channels = {}, format = {}, num_samples = {}, output_route "
"= {}, restricted = {}",
port_id, num_channels, magic_enum::enum_name(format), num_samples,
magic_enum::enum_name(output_route), restricted);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
if (output_route > OrbisAudio3dOutputRoute::ORBIS_AUDIO3D_OUTPUT_BOTH) {
LOG_ERROR(Lib_Audio3d, "output_route > ORBIS_AUDIO3D_OUTPUT_BOTH");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (format > OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) {
LOG_ERROR(Lib_Audio3d, "format > ORBIS_AUDIO3D_FORMAT_FLOAT");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (num_channels != 2 && num_channels != 8) {
LOG_ERROR(Lib_Audio3d, "num_channels != 2 && num_channels != 8");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (!buffer || !num_samples) {
LOG_ERROR(Lib_Audio3d, "!buffer || !num_samples");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) {
if ((reinterpret_cast<uintptr_t>(buffer) & 3) != 0) {
LOG_ERROR(Lib_Audio3d, "buffer & 3 != 0");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
} else if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) {
if ((reinterpret_cast<uintptr_t>(buffer) & 1) != 0) {
LOG_ERROR(Lib_Audio3d, "buffer & 1 != 0");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
}
return PortQueueAudio(state->ports[port_id],
OrbisAudio3dPcm{
.format = format,
.sample_buffer = buffer,
.num_samples = num_samples,
},
num_channels);
}
s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* params) {
LOG_DEBUG(Lib_Audio3d, "called");
if (params) {
auto default_params = OrbisAudio3dOpenParameters{
.size_this = 0x20,
.granularity = 0x100,
.rate = OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000,
.max_objects = 512,
.queue_depth = 2,
.buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH,
};
memcpy(params, &default_params, 0x20);
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dInitialize(const s64 reserved) {
LOG_INFO(Lib_Audio3d, "called, reserved = {}", reserved);
if (reserved != 0) {
LOG_ERROR(Lib_Audio3d, "reserved != 0");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (state) {
LOG_ERROR(Lib_Audio3d, "already initialized");
return ORBIS_AUDIO3D_ERROR_NOT_READY;
}
state = std::make_unique<Audio3dState>();
if (const auto init_ret = AudioOut::sceAudioOutInit();
init_ret < 0 && init_ret != ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT) {
return init_ret;
}
AudioOut::OrbisAudioOutParamExtendedInformation ext_info{};
ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT);
state->audio_out_handle =
AudioOut::sceAudioOutOpen(0xFF, AudioOut::OrbisAudioOutPort::Audio3d, 0,
AUDIO3D_OUTPUT_BUFFER_FRAMES, AUDIO3D_SAMPLE_RATE, ext_info);
if (state->audio_out_handle < 0) {
return state->audio_out_handle;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dObjectReserve(const OrbisAudio3dPortId port_id,
OrbisAudio3dObjectId* object_id) {
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}", port_id);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
if (!object_id) {
LOG_ERROR(Lib_Audio3d, "!object_id");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
*object_id = 1; // Simple stub
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(const OrbisAudio3dPortId port_id,
const OrbisAudio3dObjectId object_id,
const u64 num_attributes,
const OrbisAudio3dAttribute* attribute_array) {
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, object_id = {}, num_attributes = {}", port_id,
object_id, num_attributes);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
if (!num_attributes || !attribute_array) {
LOG_ERROR(Lib_Audio3d, "!num_attributes || !attribute_array");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
for (u64 i = 0; i < num_attributes; i++) {
const auto& attr = attribute_array[i];
if (attr.attribute_id == OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_PCM) {
if (attr.value_size < sizeof(OrbisAudio3dPcm)) {
continue;
}
const auto* pcm = static_cast<const OrbisAudio3dPcm*>(attr.value);
// Queue the PCM data (assumes stereo for simplicity)
PortQueueAudio(state->ports[port_id], *pcm, 2);
}
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortAdvance(const OrbisAudio3dPortId port_id) {
LOG_TRACE(Lib_Audio3d, "called, port_id = {}", port_id);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
auto& port = state->ports[port_id];
if (port.current_buffer) {
free(port.current_buffer->sample_buffer);
port.current_buffer.reset();
}
if (!port.queue.empty()) {
port.current_buffer = port.queue.front();
port.queue.pop_front();
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortClose() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortCreate() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortDestroy() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortFlush() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortFreeState() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetList() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetParameters() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(const OrbisAudio3dPortId port_id, u32* queue_level,
u32* queue_available) {
LOG_DEBUG(Lib_Audio3d, "called, port_id = {}", port_id);
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
const auto& port = state->ports[port_id];
const auto queue_size = port.queue.size();
if (queue_level) {
*queue_level = static_cast<u32>(queue_size);
}
if (queue_available) {
*queue_available = port.parameters.queue_depth - static_cast<u32>(queue_size);
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetState() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServiceUserId user_id,
const OrbisAudio3dOpenParameters* parameters,
OrbisAudio3dPortId* port_id) {
LOG_INFO(Lib_Audio3d, "called, user_id = {}", user_id);
if (!parameters || !port_id) {
LOG_ERROR(Lib_Audio3d, "!parameters || !port_id");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (parameters->granularity != AUDIO3D_OUTPUT_BUFFER_FRAMES) {
LOG_ERROR(Lib_Audio3d, "parameters->granularity != AUDIO3D_OUTPUT_BUFFER_FRAMES");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
if (parameters->rate != OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000) {
LOG_ERROR(Lib_Audio3d, "parameters->rate != ORBIS_AUDIO3D_RATE_48000");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
static OrbisAudio3dPortId next_id = 1;
*port_id = next_id++;
state->ports[*port_id].parameters = *parameters;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortPush(const OrbisAudio3dPortId port_id,
const OrbisAudio3dBlocking blocking) {
LOG_TRACE(Lib_Audio3d, "called, port_id = {}, blocking = {}", port_id,
magic_enum::enum_name(blocking));
if (!state->ports.contains(port_id)) {
LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
const auto& port = state->ports[port_id];
if (!port.current_buffer) {
LOG_ERROR(Lib_Audio3d, "!port.current_buffer");
return ORBIS_AUDIO3D_ERROR_INVALID_PORT;
}
// Output the current buffer
const auto ret =
AudioOut::sceAudioOutOutput(state->audio_out_handle, port.current_buffer->sample_buffer);
if (ret < 0) {
return ret;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(const OrbisAudio3dPortId port_id,
const OrbisAudio3dAttributeId attribute_id,
void* attribute, const u64 attribute_size) {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dReportRegisterHandler() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dReportUnregisterHandler() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dSetGpuRenderer() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dStrError() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dTerminate() {
LOG_INFO(Lib_Audio3d, "called");
if (!state) {
return ORBIS_OK;
}
if (state->audio_out_handle >= 0) {
AudioOut::sceAudioOutClose(state->audio_out_handle);
}
// Clean up queued audio data
for (auto& [port_id, port] : state->ports) {
if (port.current_buffer) {
free(port.current_buffer->sample_buffer);
}
for (auto& data : port.queue) {
free(data.sample_buffer);
}
}
state.reset();
return ORBIS_OK;
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("pZlOm1aF3aA", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dAudioOutClose);
LIB_FUNCTION("ucEsi62soTo", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dAudioOutOpen);
LIB_FUNCTION("7NYEzJ9SJbM", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dAudioOutOutput);
LIB_FUNCTION("HbxYY27lK6E", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dAudioOutOutputs);
LIB_FUNCTION("9tEwE0GV0qo", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dBedWrite);
LIB_FUNCTION("xH4Q9UILL3o", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dBedWrite2);
LIB_FUNCTION("lvWMW6vEqFU", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dCreateSpeakerArray);
LIB_FUNCTION("8hm6YdoQgwg", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dDeleteSpeakerArray);
LIB_FUNCTION("Im+jOoa5WAI", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dGetDefaultOpenParameters);
LIB_FUNCTION("kEqqyDkmgdI", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dGetSpeakerArrayMemorySize);
LIB_FUNCTION("-R1DukFq7Dk", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dGetSpeakerArrayMixCoefficients);
LIB_FUNCTION("-Re+pCWvwjQ", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dGetSpeakerArrayMixCoefficients2);
LIB_FUNCTION("UmCvjSmuZIw", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dInitialize);
LIB_FUNCTION("jO2tec4dJ2M", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectReserve);
LIB_FUNCTION("4uyHN9q4ZeU", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectSetAttributes);
LIB_FUNCTION("1HXxo-+1qCw", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dObjectUnreserve);
LIB_FUNCTION("lw0qrdSjZt8", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortAdvance);
LIB_FUNCTION("OyVqOeVNtSk", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortClose);
LIB_FUNCTION("UHFOgVNz0kk", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortCreate);
LIB_FUNCTION("Mw9mRQtWepY", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortDestroy);
LIB_FUNCTION("ZOGrxWLgQzE", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortFlush);
LIB_FUNCTION("uJ0VhGcxCTQ", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortFreeState);
LIB_FUNCTION("9ZA23Ia46Po", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dPortGetAttributesSupported);
LIB_FUNCTION("SEggctIeTcI", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortGetList);
LIB_FUNCTION("flPcUaXVXcw", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortGetParameters);
LIB_FUNCTION("YaaDbDwKpFM", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortGetQueueLevel);
LIB_FUNCTION("CKHlRW2E9dA", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortGetState);
LIB_FUNCTION("iRX6GJs9tvE", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortGetStatus);
LIB_FUNCTION("XeDDK0xJWQA", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortOpen);
LIB_FUNCTION("VEVhZ9qd4ZY", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortPush);
LIB_FUNCTION("-pzYDZozm+M", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortQueryDebug);
LIB_FUNCTION("Yq9bfUQ0uJg", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dPortSetAttribute);
LIB_FUNCTION("QfNXBrKZeI0", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dReportRegisterHandler);
LIB_FUNCTION("psv2gbihC1A", "libSceAudio3d", 1, "libSceAudio3d",
sceAudio3dReportUnregisterHandler);
LIB_FUNCTION("yEYXcbAGK14", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dSetGpuRenderer);
LIB_FUNCTION("Aacl5qkRU6U", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dStrError);
LIB_FUNCTION("WW1TS2iz5yc", "libSceAudio3d", 1, "libSceAudio3d", sceAudio3dTerminate);
}
} // namespace Libraries::Audio3d

View File

@ -0,0 +1,144 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <queue>
#include "common/types.h"
#include "core/libraries/audio/audioout.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Audio3dOpenAL {
enum class OrbisAudio3dRate : u32 {
ORBIS_AUDIO3D_RATE_48000 = 0,
};
enum class OrbisAudio3dBufferMode : u32 {
ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0,
ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1,
ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2,
};
struct OrbisAudio3dOpenParameters {
u64 size_this;
u32 granularity;
OrbisAudio3dRate rate;
u32 max_objects;
u32 queue_depth;
OrbisAudio3dBufferMode buffer_mode;
int : 32;
u32 num_beds;
};
enum class OrbisAudio3dFormat : u32 {
ORBIS_AUDIO3D_FORMAT_S16 = 0,
ORBIS_AUDIO3D_FORMAT_FLOAT = 1,
};
enum class OrbisAudio3dOutputRoute : u32 {
ORBIS_AUDIO3D_OUTPUT_BOTH = 0,
ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1,
ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2,
};
enum class OrbisAudio3dBlocking : u32 {
ORBIS_AUDIO3D_BLOCKING_ASYNC = 0,
ORBIS_AUDIO3D_BLOCKING_SYNC = 1,
};
struct OrbisAudio3dPcm {
OrbisAudio3dFormat format;
void* sample_buffer;
u32 num_samples;
};
enum class OrbisAudio3dAttributeId : u32 {
ORBIS_AUDIO3D_ATTRIBUTE_PCM = 1,
};
using OrbisAudio3dPortId = u32;
using OrbisAudio3dObjectId = u32;
struct OrbisAudio3dAttribute {
OrbisAudio3dAttributeId attribute_id;
int : 32;
void* value;
u64 value_size;
};
struct AudioData {
u8* sample_buffer;
u32 num_samples;
};
struct Port {
OrbisAudio3dOpenParameters parameters{};
std::deque<AudioData> queue; // Only stores PCM buffers for now
std::optional<AudioData> current_buffer{};
};
struct Audio3dState {
std::unordered_map<OrbisAudio3dPortId, Port> ports;
s32 audio_out_handle;
};
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle);
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id,
Libraries::UserService::OrbisUserServiceUserId user_id,
s32 type, s32 index, u32 len, u32 freq,
AudioOut::OrbisAudioOutParamExtendedInformation param);
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, void* ptr);
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* param, u32 num);
s32 PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId port_id, u32 num_channels,
OrbisAudio3dFormat format, void* buffer, u32 num_samples);
s32 PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId port_id, u32 num_channels,
OrbisAudio3dFormat format, void* buffer, u32 num_samples,
OrbisAudio3dOutputRoute output_route, bool restricted);
s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray();
s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray();
s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* params);
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize();
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients();
s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2();
s32 PS4_SYSV_ABI sceAudio3dInitialize(s64 reserved);
s32 PS4_SYSV_ABI sceAudio3dObjectReserve(OrbisAudio3dPortId port_id,
OrbisAudio3dObjectId* object_id);
s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId port_id,
OrbisAudio3dObjectId object_id, u64 num_attributes,
const OrbisAudio3dAttribute* attribute_array);
s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve();
s32 PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId port_id);
s32 PS4_SYSV_ABI sceAudio3dPortClose();
s32 PS4_SYSV_ABI sceAudio3dPortCreate();
s32 PS4_SYSV_ABI sceAudio3dPortDestroy();
s32 PS4_SYSV_ABI sceAudio3dPortFlush();
s32 PS4_SYSV_ABI sceAudio3dPortFreeState();
s32 PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported();
s32 PS4_SYSV_ABI sceAudio3dPortGetList();
s32 PS4_SYSV_ABI sceAudio3dPortGetParameters();
s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId port_id, u32* queue_level,
u32* queue_available);
s32 PS4_SYSV_ABI sceAudio3dPortGetState();
s32 PS4_SYSV_ABI sceAudio3dPortGetStatus();
s32 PS4_SYSV_ABI sceAudio3dPortOpen(Libraries::UserService::OrbisUserServiceUserId user_id,
const OrbisAudio3dOpenParameters* parameters,
OrbisAudio3dPortId* port_id);
s32 PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId port_id, OrbisAudio3dBlocking blocking);
s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug();
s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(OrbisAudio3dPortId port_id,
OrbisAudio3dAttributeId attribute_id, void* attribute,
u64 attribute_size);
s32 PS4_SYSV_ABI sceAudio3dReportRegisterHandler();
s32 PS4_SYSV_ABI sceAudio3dReportUnregisterHandler();
s32 PS4_SYSV_ABI sceAudio3dSetGpuRenderer();
s32 PS4_SYSV_ABI sceAudio3dStrError();
s32 PS4_SYSV_ABI sceAudio3dTerminate();
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Audio3d

View File

@ -74,6 +74,7 @@
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h"
#include "core/libraries/audio3d/audio3d_openal.h"
namespace Libraries {
@ -126,7 +127,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::AvPlayer::RegisterLib(sym);
Libraries::Videodec::RegisterLib(sym);
Libraries::Videodec2::RegisterLib(sym);
Libraries::Audio3d::RegisterLib(sym);
//Libraries::Audio3d::RegisterLib(sym);
Libraries::Audio3dOpenAL::RegisterLib(sym);
Libraries::Ime::RegisterLib(sym);
Libraries::GameLiveStreaming::RegisterLib(sym);
Libraries::SharePlay::RegisterLib(sym);