diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 0edada2c5..c6a444515 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -617,9 +617,23 @@ s32 PS4_SYSV_ABI sceAudio3dPortClose(const OrbisAudio3dPortId port_id) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAudio3dPortCreate() { - LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceAudio3dPortCreate(u32 granularity, u32 rate, s64 reserved, + OrbisAudio3dPortId* port_id) { + + LOG_INFO(Lib_Audio3d, "called, granularity = {}, rate = {}, reserved = {}, port_id = {}", + granularity, rate, reserved, static_cast(port_id)); + + if (!port_id || reserved) { + LOG_INFO(Lib_Audio3d, "!port_id || reserved"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + OrbisAudio3dOpenParameters local_params{}; + local_params.size_this = 0x10; + local_params.granularity = granularity; + local_params.rate = static_cast(rate); + return sceAudio3dPortOpen(static_cast(0xFF), + &local_params, port_id); } s32 PS4_SYSV_ABI sceAudio3dPortDestroy() { @@ -782,31 +796,143 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() { s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServiceUserId user_id, const OrbisAudio3dOpenParameters* parameters, OrbisAudio3dPortId* port_id) { - LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, id = {}", user_id, + LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, port_id = {}", user_id, static_cast(parameters), static_cast(port_id)); + if (user_id != 0xFF || !parameters || !port_id) { + LOG_ERROR(Lib_Audio3d, "user_id != 0xFF || !parameters || !port_id"); + if (port_id) + *port_id = ORBIS_AUDIO3D_PORT_INVALID; + + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + *port_id = ORBIS_AUDIO3D_PORT_INVALID; + if (!state) { LOG_ERROR(Lib_Audio3d, "!initialized"); return ORBIS_AUDIO3D_ERROR_NOT_READY; } - if (!parameters || !port_id) { - LOG_ERROR(Lib_Audio3d, "!parameters || !id"); + OrbisAudio3dOpenParameters effective{ + .size_this = 0x28, + .granularity = parameters->granularity, + .rate = parameters->rate, + .max_objects = 512, + .queue_depth = 2, + .buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE, + ._pad = 0, + .num_beds = 2, + }; + + switch (parameters->size_this & ~0x7ull) { + case 0x10: + break; + + case 0x18: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH; + break; + + case 0x20: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = parameters->buffer_mode; + break; + + case 0x28: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = parameters->buffer_mode; + effective.num_beds = parameters->num_beds; + break; + + default: + LOG_ERROR(Lib_Audio3d, "invalid size_this"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; } - const int id = static_cast(state->ports.size()) + 1; + if (effective.rate != OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000) { + LOG_ERROR(Lib_Audio3d, "unsupported rate"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } - if (id > 3) { - LOG_ERROR(Lib_Audio3d, "id > 3"); + if (effective.granularity < 0x100) { + LOG_ERROR(Lib_Audio3d, "granularity < 0x100"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if ((effective.granularity & 0xFF) != 0) { + LOG_ERROR(Lib_Audio3d, "granularity not aligned to 0x100"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.max_objects == 0) { + LOG_ERROR(Lib_Audio3d, "max_objects == 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.queue_depth == 0) { + LOG_ERROR(Lib_Audio3d, "queue_depth == 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x100 && effective.queue_depth > 0x40) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x100 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x200 && effective.queue_depth > 0x1F) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x200 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x300 && effective.queue_depth > 0x14) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x300 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.queue_depth > 0xF && effective.granularity > 0x3FF) { + LOG_ERROR(Lib_Audio3d, "queue_depth invalid for large granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (static_cast(effective.buffer_mode) > 2) { + LOG_ERROR(Lib_Audio3d, "invalid buffer_mode"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if ((effective.num_beds & 0xfffffffe) != 2) { + LOG_ERROR(Lib_Audio3d, "invalid num_beds"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.max_objects > 0x200) { + LOG_WARNING(Lib_Audio3d, "max_objects {} exceeds limit, clamping to 512", + effective.max_objects); + effective.max_objects = 0x200; + } + + std::scoped_lock lock{state->ports_mutex}; + + OrbisAudio3dPortId id = ORBIS_AUDIO3D_PORT_INVALID; + for (OrbisAudio3dPortId i = 0; i < MaxPorts; i++) { + if (!state->ports.contains(i)) { + id = i; + break; + } + } + + if (id == ORBIS_AUDIO3D_PORT_INVALID) { + LOG_ERROR(Lib_Audio3d, "no free ports"); return ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES; } + auto& port = state->ports.try_emplace(id).first->second; + port.parameters = effective; + *port_id = id; - auto& port = state->ports[id]; - std::memcpy( - &port.parameters, parameters, - std::min(parameters->size_this, static_cast(sizeof(OrbisAudio3dOpenParameters)))); return ORBIS_OK; } diff --git a/src/core/libraries/audio3d/audio3d.h b/src/core/libraries/audio3d/audio3d.h index 50d2435ba..7e298696d 100644 --- a/src/core/libraries/audio3d/audio3d.h +++ b/src/core/libraries/audio3d/audio3d.h @@ -18,7 +18,13 @@ class SymbolsResolver; namespace Libraries::Audio3d { +using OrbisAudio3dPortId = u32; +using OrbisAudio3dObjectId = u32; +using OrbisAudio3dAmbisonics = u32; + constexpr int ORBIS_AUDIO3D_OBJECT_INVALID = 0xFFFFFFFF; +constexpr OrbisAudio3dPortId ORBIS_AUDIO3D_PORT_INVALID = 0xFFFFFFFFu; +constexpr OrbisAudio3dPortId MaxPorts = 4; enum class OrbisAudio3dRate : u32 { ORBIS_AUDIO3D_RATE_48000 = 0, @@ -37,7 +43,7 @@ struct OrbisAudio3dOpenParameters { u32 max_objects; u32 queue_depth; OrbisAudio3dBufferMode buffer_mode; - int : 32; + u32 _pad; u32 num_beds; }; @@ -77,10 +83,6 @@ enum class OrbisAudio3dAttributeId : u32 { ORBIS_AUDIO3D_ATTRIBUTE_OUTPUT_ROUTE = 11, }; -using OrbisAudio3dPortId = u32; -using OrbisAudio3dObjectId = u32; -using OrbisAudio3dAmbisonics = u32; - struct OrbisAudio3dAttribute { OrbisAudio3dAttributeId attribute_id; int : 32; @@ -119,6 +121,7 @@ struct Port { }; struct Audio3dState { + std::mutex ports_mutex; std::unordered_map ports; }; @@ -154,7 +157,8 @@ s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(OrbisAudio3dPortId port_id, OrbisAudio3dObjectId object_id); s32 PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId port_id); s32 PS4_SYSV_ABI sceAudio3dPortClose(OrbisAudio3dPortId port_id); -s32 PS4_SYSV_ABI sceAudio3dPortCreate(); +s32 PS4_SYSV_ABI sceAudio3dPortCreate(u32 granularity, u32 rate, s64 reserved, + OrbisAudio3dPortId* port_id); s32 PS4_SYSV_ABI sceAudio3dPortDestroy(); s32 PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId port_id); s32 PS4_SYSV_ABI sceAudio3dPortFreeState(); diff --git a/src/core/libraries/audio3d/audio3d_openal.cpp b/src/core/libraries/audio3d/audio3d_openal.cpp index d054df324..43d7d02c7 100644 --- a/src/core/libraries/audio3d/audio3d_openal.cpp +++ b/src/core/libraries/audio3d/audio3d_openal.cpp @@ -617,9 +617,23 @@ s32 PS4_SYSV_ABI sceAudio3dPortClose(const OrbisAudio3dPortId port_id) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAudio3dPortCreate() { - LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceAudio3dPortCreate(u32 granularity, u32 rate, s64 reserved, + OrbisAudio3dPortId* port_id) { + + LOG_INFO(Lib_Audio3d, "called, granularity = {}, rate = {}, reserved = {}, port_id = {}", + granularity, rate, reserved, static_cast(port_id)); + + if (!port_id || reserved) { + LOG_INFO(Lib_Audio3d, "!port_id || reserved"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + OrbisAudio3dOpenParameters local_params{}; + local_params.size_this = 0x10; + local_params.granularity = granularity; + local_params.rate = static_cast(rate); + return sceAudio3dPortOpen(static_cast(0xFF), + &local_params, port_id); } s32 PS4_SYSV_ABI sceAudio3dPortDestroy() { @@ -782,31 +796,143 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() { s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServiceUserId user_id, const OrbisAudio3dOpenParameters* parameters, OrbisAudio3dPortId* port_id) { - LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, id = {}", user_id, + LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, port_id = {}", user_id, static_cast(parameters), static_cast(port_id)); + if (user_id != 0xFF || !parameters || !port_id) { + LOG_ERROR(Lib_Audio3d, "user_id != 0xFF || !parameters || !port_id"); + if (port_id) + *port_id = ORBIS_AUDIO3D_PORT_INVALID; + + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + *port_id = ORBIS_AUDIO3D_PORT_INVALID; + if (!state) { LOG_ERROR(Lib_Audio3d, "!initialized"); return ORBIS_AUDIO3D_ERROR_NOT_READY; } - if (!parameters || !port_id) { - LOG_ERROR(Lib_Audio3d, "!parameters || !id"); + OrbisAudio3dOpenParameters effective{ + .size_this = 0x28, + .granularity = parameters->granularity, + .rate = parameters->rate, + .max_objects = 512, + .queue_depth = 2, + .buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE, + ._pad = 0, + .num_beds = 2, + }; + + switch (parameters->size_this & ~0x7ull) { + case 0x10: + break; + + case 0x18: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH; + break; + + case 0x20: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = parameters->buffer_mode; + break; + + case 0x28: + effective.max_objects = parameters->max_objects; + effective.queue_depth = parameters->queue_depth; + effective.buffer_mode = parameters->buffer_mode; + effective.num_beds = parameters->num_beds; + break; + + default: + LOG_ERROR(Lib_Audio3d, "invalid size_this"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; } - const int id = static_cast(state->ports.size()) + 1; + if (effective.rate != OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000) { + LOG_ERROR(Lib_Audio3d, "unsupported rate"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } - if (id > 3) { - LOG_ERROR(Lib_Audio3d, "id > 3"); + if (effective.granularity < 0x100) { + LOG_ERROR(Lib_Audio3d, "granularity < 0x100"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if ((effective.granularity & 0xFF) != 0) { + LOG_ERROR(Lib_Audio3d, "granularity not aligned to 0x100"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.max_objects == 0) { + LOG_ERROR(Lib_Audio3d, "max_objects == 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.queue_depth == 0) { + LOG_ERROR(Lib_Audio3d, "queue_depth == 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x100 && effective.queue_depth > 0x40) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x100 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x200 && effective.queue_depth > 0x1F) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x200 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.granularity == 0x300 && effective.queue_depth > 0x14) { + LOG_ERROR(Lib_Audio3d, "queue_depth too large for 0x300 granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.queue_depth > 0xF && effective.granularity > 0x3FF) { + LOG_ERROR(Lib_Audio3d, "queue_depth invalid for large granularity"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (static_cast(effective.buffer_mode) > 2) { + LOG_ERROR(Lib_Audio3d, "invalid buffer_mode"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if ((effective.num_beds & 0xfffffffe) != 2) { + LOG_ERROR(Lib_Audio3d, "invalid num_beds"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (effective.max_objects > 0x200) { + LOG_WARNING(Lib_Audio3d, "max_objects {} exceeds limit, clamping to 512", + effective.max_objects); + effective.max_objects = 0x200; + } + + std::scoped_lock lock{state->ports_mutex}; + + OrbisAudio3dPortId id = ORBIS_AUDIO3D_PORT_INVALID; + for (OrbisAudio3dPortId i = 0; i < MaxPorts; i++) { + if (!state->ports.contains(i)) { + id = i; + break; + } + } + + if (id == ORBIS_AUDIO3D_PORT_INVALID) { + LOG_ERROR(Lib_Audio3d, "no free ports"); return ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES; } + auto& port = state->ports.try_emplace(id).first->second; + port.parameters = effective; + *port_id = id; - auto& port = state->ports[id]; - std::memcpy( - &port.parameters, parameters, - std::min(parameters->size_this, static_cast(sizeof(OrbisAudio3dOpenParameters)))); return ORBIS_OK; } diff --git a/src/core/libraries/audio3d/audio3d_openal.h b/src/core/libraries/audio3d/audio3d_openal.h index f611ff5ef..4dac49d65 100644 --- a/src/core/libraries/audio3d/audio3d_openal.h +++ b/src/core/libraries/audio3d/audio3d_openal.h @@ -17,7 +17,13 @@ class SymbolsResolver; namespace Libraries::Audio3dOpenAL { +using OrbisAudio3dPortId = u32; +using OrbisAudio3dObjectId = u32; +using OrbisAudio3dAmbisonics = u32; + constexpr int ORBIS_AUDIO3D_OBJECT_INVALID = 0xFFFFFFFF; +constexpr OrbisAudio3dPortId ORBIS_AUDIO3D_PORT_INVALID = 0xFFFFFFFFu; +constexpr OrbisAudio3dPortId MaxPorts = 4; enum class OrbisAudio3dRate : u32 { ORBIS_AUDIO3D_RATE_48000 = 0, @@ -36,7 +42,7 @@ struct OrbisAudio3dOpenParameters { u32 max_objects; u32 queue_depth; OrbisAudio3dBufferMode buffer_mode; - int : 32; + u32 _pad; u32 num_beds; }; @@ -76,10 +82,6 @@ enum class OrbisAudio3dAttributeId : u32 { ORBIS_AUDIO3D_ATTRIBUTE_OUTPUT_ROUTE = 11, }; -using OrbisAudio3dPortId = u32; -using OrbisAudio3dObjectId = u32; -using OrbisAudio3dAmbisonics = u32; - struct OrbisAudio3dAttribute { OrbisAudio3dAttributeId attribute_id; int : 32; @@ -117,6 +119,7 @@ struct Port { }; struct Audio3dState { + std::mutex ports_mutex; std::unordered_map ports; }; @@ -152,7 +155,8 @@ s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(OrbisAudio3dPortId port_id, OrbisAudio3dObjectId object_id); s32 PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId port_id); s32 PS4_SYSV_ABI sceAudio3dPortClose(OrbisAudio3dPortId port_id); -s32 PS4_SYSV_ABI sceAudio3dPortCreate(); +s32 PS4_SYSV_ABI sceAudio3dPortCreate(u32 granularity, u32 rate, s64 reserved, + OrbisAudio3dPortId* port_id); s32 PS4_SYSV_ABI sceAudio3dPortDestroy(); s32 PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId port_id); s32 PS4_SYSV_ABI sceAudio3dPortFreeState();