#include "prx/libSceAudioPropagation/AudioPropagation.hpp" #include #include #include #include #include namespace { void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "AudioPropagation: %s\n", message); std::abort(); } } template void RequireThrows(TCall call, const char* message) { try { call(); } catch (const TException&) { return; } catch (...) { Require(false, message); } Require(false, message); } AudioPropagationSystemMemory Memory() { AudioPropagationSystemMemory memory{}; memory.desc = {AudioPropagation::SystemMemoryId, AudioPropagation::SystemMemorySize}; return memory; } AudioPropagation::Attribute Attribute(const void* value, std::size_t size) { AudioPropagation::Attribute attribute{}; attribute.id = 0x20000; attribute.value = value; attribute.valueSize = size; return attribute; } } int main() { const std::uint64_t options[7]{}; alignas(16) std::uint8_t arena[64]{}; auto memory = Memory(); Require(sceAudioPropagationSystemQueryMemory(options, &memory) == 0, "query memory failed"); Require(memory.size_cpu_mem != 0 && memory.size_cpu_mem <= sizeof(arena), "cpu size outside the arena"); Require(memory.size_gpu_mem == 0 && memory.p_cpu_mem == nullptr && memory.p_gpu_mem == nullptr, "query wrote more than the sizes"); RequireThrows([&] { sceAudioPropagationSystemQueryMemory(nullptr, &memory); }, "null options"); RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, nullptr); }, "null memory"); auto wrong = Memory(); wrong.desc.size = 0x28; RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, &wrong); }, "wrong descriptor size"); wrong = Memory(); wrong.desc.id = AudioPropagation::RayId; RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, &wrong); }, "wrong descriptor id"); AudioPropagationHandle system = 0; RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "create without cpu memory"); memory.p_cpu_mem = arena + 1; RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "misaligned cpu memory"); memory.p_cpu_mem = arena; memory.size_gpu_mem = 16; RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "unrequested gpu memory"); memory.size_gpu_mem = 0; const auto required = memory.size_cpu_mem; memory.size_cpu_mem = required - 1; RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "cpu memory too small"); memory.size_cpu_mem = required; Require(sceAudioPropagationSystemCreate(options, &memory, &system) == 0, "create failed"); Require(system == reinterpret_cast(arena), "system handle is not its memory"); AudioPropagationHandle twice = 0; RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &twice); }, "second system in the same memory"); const std::uint64_t value = 1; const auto attribute = Attribute(&value, sizeof(value)); const auto empty = Attribute(nullptr, sizeof(value)); Require(sceAudioPropagationSystemSetAttributes(system, &attribute, 1) == 0, "system attributes failed"); Require(sceAudioPropagationSystemSetAttributes(system, nullptr, 0) == 0, "empty system attributes failed"); RequireThrows([&] { sceAudioPropagationSystemSetAttributes(system, &empty, 1); }, "attribute without value"); RequireThrows([&] { sceAudioPropagationSystemSetAttributes(system + 16, &attribute, 1); }, "unknown system"); std::uint8_t materialBytes[AudioPropagation::MaterialSize]{}; auto* material = reinterpret_cast(materialBytes); AudioPropagationHandle materialHandle = 0; RequireThrows([&] { sceAudioPropagationSystemRegisterMaterial(system, material, &materialHandle); }, "material without descriptor"); *material = {AudioPropagation::MaterialId, AudioPropagation::MaterialSize}; Require(sceAudioPropagationSystemRegisterMaterial(system, material, &materialHandle) == 0 && materialHandle != 0, "register material failed"); Require(sceAudioPropagationSystemUnregisterMaterial(materialHandle) == 0, "unregister material failed"); RequireThrows([&] { sceAudioPropagationSystemUnregisterMaterial(materialHandle); }, "unregister twice"); AudioPropagationHandle rooms[2]{}; Require(sceAudioPropagationRoomCreate(system, &rooms[0]) == 0 && sceAudioPropagationRoomCreate(system, &rooms[1]) == 0, "room create failed"); Require(rooms[0] != 0 && rooms[0] != rooms[1], "room handles are not distinct"); AudioPropagation::PortalParams portalParams{}; portalParams.desc = {AudioPropagation::PortalParamsId, AudioPropagation::PortalParamsSize}; portalParams.rooms[0] = rooms[0]; portalParams.rooms[1] = materialHandle; AudioPropagationHandle portal = 0; RequireThrows([&] { sceAudioPropagationPortalCreate(system, &portalParams, &portal); }, "portal to a non-room"); portalParams.rooms[1] = rooms[1]; Require(sceAudioPropagationPortalCreate(system, &portalParams, &portal) == 0, "portal create failed"); const AudioPropagation::Attribute portalAttributes[4]{attribute, attribute, attribute, attribute}; Require(sceAudioPropagationPortalSetAttributes(portal, portalAttributes, 4) == 0, "portal attributes failed"); RequireThrows([&] { sceAudioPropagationPortalSetAttributes(rooms[0], portalAttributes, 4); }, "portal attributes on a room"); AudioPropagationHandle source = 0; Require(sceAudioPropagationSourceCreate(system, &source) == 0, "source create failed"); RequireThrows([&] { sceAudioPropagationSourceCreate(system, nullptr); }, "source without output"); Require(sceAudioPropagationSourceSetAttributes(source, portalAttributes, 3) == 0, "source attributes failed"); std::uint32_t count = 7; Require(sceAudioPropagationSourceGetAudioPathCount(source, &count) == 0 && count == 0, "source reports audio paths"); AudioPropagationHandle path = 0; RequireThrows([&] { sceAudioPropagationSourceGetAudioPath(source, 0, &path); }, "audio path without paths"); std::uint8_t rays[AudioPropagation::RaySize * 2]{}; count = 7; Require(sceAudioPropagationSystemGetRays(system, rays, &count) == 0 && count == 0, "system requested rays"); count = 7; Require(sceAudioPropagationSourceGetRays(source, rays, &count) == 0 && count == 0, "source requested rays"); RequireThrows([&] { sceAudioPropagationSourceGetRays(source, rays, nullptr); }, "rays without count"); Require(sceAudioPropagationSystemSetRays(system, rays, 0) == 0, "empty ray results failed"); RequireThrows([&] { sceAudioPropagationSystemSetRays(system, rays, 1); }, "unrequested ray results"); Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 0, 1, nullptr, 0) == 0, "empty path calculation failed"); RequireThrows([&] { sceAudioPropagationSourceCalculateAudioPaths(source, rays, 1, 1, nullptr, 0); }, "path calculation with rays"); Require(sceAudioPropagationSourceSetAudioPaths(source, nullptr, 0) == 0, "empty audio paths failed"); RequireThrows([&] { sceAudioPropagationSourceSetAudioPaths(source, rays, 1); }, "set audio paths"); alignas(16) std::uint8_t otherArena[64]{}; auto otherMemory = Memory(); Require(sceAudioPropagationSystemQueryMemory(options, &otherMemory) == 0, "second query memory failed"); otherMemory.p_cpu_mem = otherArena; AudioPropagationHandle otherSystem = 0; Require(sceAudioPropagationSystemCreate(options, &otherMemory, &otherSystem) == 0, "second system create failed"); AudioPropagationHandle otherSource = 0; Require(sceAudioPropagationSourceCreate(otherSystem, &otherSource) == 0, "second system source create failed"); AudioPropagationHandle secondSource = 0; Require(sceAudioPropagationSourceCreate(system, &secondSource) == 0, "second source create failed"); float outputs[2][64]; const auto fill = [&] { std::memset(outputs, 0xab, sizeof(outputs)); }; const auto zeroed = [&](int index) { for (const float sample : outputs[index]) { if (sample != 0.0f) return false; } return true; }; const auto untouched = [&] { const auto* bytes = reinterpret_cast(outputs); for (std::size_t index = 0; index < sizeof(outputs); ++index) { if (bytes[index] != 0xab) return false; } return true; }; AudioPropagation::RenderInfo infos[2]{}; const auto reset = [&] { infos[0] = {{AudioPropagation::RenderInfoId, AudioPropagation::RenderInfoSize}, source, outputs[0], sizeof(outputs[0]), AudioPropagation::RenderFormat, 0}; infos[1] = {{AudioPropagation::RenderInfoId, AudioPropagation::RenderInfoSize}, secondSource, outputs[1], sizeof(outputs[1]), AudioPropagation::RenderFormat, 0}; fill(); }; reset(); Require(sceAudioPropagationSourceRender(system, infos, 1) == 0, "render failed"); Require(zeroed(0), "render left the output unwritten"); const auto* tail = reinterpret_cast(outputs[1]); Require(tail[0] == 0xab, "render wrote past the output size"); reset(); infos[0].outputSize = sizeof(float) * 16; Require(sceAudioPropagationSourceRender(system, infos, 2) == 0, "render of two sources failed"); Require(outputs[0][15] == 0.0f && outputs[0][16] != 0.0f && zeroed(1), "render did not write each output for its size"); reset(); RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 0); }, "render without infos"); RequireThrows([&] { sceAudioPropagationSourceRender(system, nullptr, 1); }, "render with null infos"); RequireThrows([&] { sceAudioPropagationSourceRender(system + 16, infos, 1); }, "render on an unknown system"); infos[1].desc.id = AudioPropagation::RayId; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 2); }, "render info with a wrong descriptor id"); Require(untouched(), "rejected render wrote an output"); reset(); infos[0].desc.size = 0x28; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 1); }, "render info with a wrong descriptor size"); reset(); infos[0].source = rooms[0]; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 1); }, "render of a room"); reset(); infos[0].source = otherSource; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 1); }, "render of another system's source"); reset(); infos[0].output = nullptr; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 1); }, "render without output"); reset(); infos[0].outputSize = 0; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 1); }, "render with an empty output"); reset(); infos[1].format = 1; RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 2); }, "render with an unknown format"); Require(untouched(), "render with an unknown format wrote an output"); Require(sceAudioPropagationSourceDestroy(system, secondSource) == 0, "second source destroy failed"); reset(); RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 2); }, "render of a destroyed source"); Require(sceAudioPropagationSystemDestroy(otherSystem) == 0, "second system destroy failed"); RequireThrows([&] { sceAudioPropagationRoomDestroy(system, source); }, "room destroy of a source"); RequireThrows([&] { sceAudioPropagationSourceDestroy(system + 16, source); }, "source destroy with another system"); Require(sceAudioPropagationPortalDestroy(system, portal) == 0, "portal destroy failed"); Require(sceAudioPropagationSourceDestroy(system, source) == 0, "source destroy failed"); Require(sceAudioPropagationRoomDestroy(system, rooms[0]) == 0, "room destroy failed"); AudioPropagationHandle lateSource = 0; AudioPropagationHandle lateMaterial = 0; Require(sceAudioPropagationSourceCreate(system, &lateSource) == 0, "late source create failed"); Require(sceAudioPropagationSystemRegisterMaterial(system, material, &lateMaterial) == 0, "late material register failed"); Require(sceAudioPropagationSystemDestroy(system) == 0, "system destroy failed"); RequireThrows([&] { sceAudioPropagationSourceSetAttributes(lateSource, &attribute, 1); }, "source used after its system"); RequireThrows([&] { sceAudioPropagationRoomDestroy(system, lateSource); }, "orphan destroyed as the wrong kind"); RequireThrows([&] { sceAudioPropagationSourceDestroy(system + 16, lateSource); }, "orphan destroyed with another system"); Require(sceAudioPropagationRoomDestroy(system, rooms[1]) == 0, "orphaned room destroy is not a no-op"); Require(sceAudioPropagationSourceDestroy(system, lateSource) == 0, "orphaned source destroy is not a no-op"); Require(sceAudioPropagationSystemUnregisterMaterial(lateMaterial) == 0, "orphaned material unregister is not a no-op"); RequireThrows([&] { sceAudioPropagationSourceDestroy(system, lateSource); }, "orphan destroyed twice"); RequireThrows([&] { sceAudioPropagationSystemDestroy(system); }, "destroy twice"); Require(sceAudioPropagationSystemCreate(options, &memory, &system) == 0, "memory reuse after destroy failed"); Require(sceAudioPropagationSystemDestroy(system) == 0, "second destroy failed"); std::puts("AudioPropagation tests passed"); return 0; }