#include "prx/libSceOpusCeltDec/libSceOpusCeltDec.hpp" #include "prx/libSceOpusDec/libSceOpusDec.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include namespace { void Require(bool value) { if (!value) std::abort(); } template void RequireThrows(TFunction function) { try { function(); } catch (const std::runtime_error&) { return; } std::abort(); } const std::uint8_t CELT_PACKET[] = { 0xFC, 0xB5, 0x1F, 0xBC, 0xC8, 0xCA, 0x0B, 0xC2, 0x18, 0x1A, 0xFB, 0xDB, 0x3E, 0x2B, 0x6B, 0x0A, 0x67, 0x71, 0x25, 0x2F, 0x51, 0x57, 0x4F, 0x78, 0xB3, 0x1F, 0xE9, 0xF3, 0x99, 0x51, 0xE9, 0x70, 0x72, 0x23, 0x99, 0x3E, 0x26, 0xA3, 0xF1, 0x8F, 0x02, 0xB3, 0xAA, 0xBA, 0xAB, 0x7E, 0xA5, 0x6A, 0xBA, 0x33, 0xCA, 0xE9, 0x43, 0x4D, 0x41, 0xC9, 0x90, 0x3C, 0xD0, 0x5F, 0x55, 0xC1, 0x54, 0xA8, 0xD0, 0x6F, 0x71, 0x4F, 0xCB, 0x95, 0xDF, 0x09, 0xC9, 0xB6, 0xAA, 0xA1, 0x8D, 0xD4, 0x2B, 0xC0, 0xF7, 0x23, 0xDD, 0xF5, 0x54, 0xEF, 0x90, 0xC4, 0xD3, 0x47, 0xC1, 0x91, 0x2D, 0xA8, 0x3D, 0x53, 0xF4, 0x7D, 0xFF, 0xBD, 0x47, 0x0E, 0xD2, 0x95, 0x25, 0x42, 0x66, 0x02, 0xD7, 0x80, 0x00, 0x67, 0x59, 0x13, 0x60, 0x2F, 0x6C, }; struct Api { std::uint32_t errorBase; int (APS5_VABI* initialize)(std::uint32_t*); int (APS5_VABI* terminate)(const std::uint32_t*); int (APS5_VABI* createEx)(const std::uint32_t*, void*, int, int); int (APS5_VABI* decode)(void*, const std::uint8_t*, int, std::int16_t*, int); int (APS5_VABI* decodeWithPriority)(void*, const std::uint8_t*, int, std::int16_t*, int, int); int (APS5_VABI* decodeFloat)(void*, const std::uint8_t*, int, float*, int); int (APS5_VABI* decodeFloatWithPriority)(void*, const std::uint8_t*, int, float*, int, int); int (APS5_VABI* destroy)(void*); }; float Converted(std::int16_t sample) { return sample > 0 ? sample / 32767.0f : sample / 32768.0f; } void RequireValidation(const Api& api) { std::array invalidState{}; std::int16_t output = 12345; float floatOutput = 2.0f; const std::uint8_t packet = 0; const auto error = [&](std::uint32_t offset) { return static_cast(api.errorBase + offset); }; Require(api.decodeWithPriority(nullptr, nullptr, -1, nullptr, -1, 0) == error(0)); Require(api.decodeWithPriority(invalidState.data(), nullptr, -1, nullptr, -1, 0) == error(6)); Require(api.decodeWithPriority(invalidState.data(), &packet, -1, nullptr, -1, 0) == error(7)); Require(api.decodeWithPriority(invalidState.data(), &packet, 1, nullptr, -1, 0) == error(8)); Require(api.decodeWithPriority(invalidState.data(), &packet, 1, &output, -1, 0) == error(9)); for (int priority : {-1, 0, 19, 101, 255}) Require(api.decodeWithPriority(invalidState.data(), &packet, 1, &output, 2, priority) == error(10)); for (int priority : {20, 40, 100}) Require(api.decodeWithPriority(invalidState.data(), &packet, 1, &output, 2, priority) == error(1)); Require(api.decodeFloatWithPriority(nullptr, nullptr, -1, nullptr, -1, 0) == error(0)); Require(api.decodeFloatWithPriority(invalidState.data(), &packet, 1, &floatOutput, -1, 0) == error(9)); Require(api.decodeFloatWithPriority(invalidState.data(), &packet, 1, &floatOutput, 4, 19) == error(10)); Require(api.decodeFloatWithPriority(invalidState.data(), &packet, 1, &floatOutput, 4, 100) == error(1)); Require(api.decodeFloat(invalidState.data(), &packet, 1, nullptr, 4) == error(8)); Require(api.decodeFloat(invalidState.data(), &packet, 1, &floatOutput, 4) == error(1)); Require(output == 12345 && floatOutput == 2.0f); } void RequireDecoding(const Api& api) { std::uint32_t context = 0; Require(api.initialize(&context) == 0); std::array, 4> states{}; for (auto& state : states) Require(api.createEx(&context, state.data(), 48000, 2) == 0); std::array reference{}, prioritized{}; std::array floats{}, prioritizedFloats{}; for (int frame = 0; frame < 3; ++frame) { reference.fill(12345); prioritized.fill(12345); floats.fill(2.0f); prioritizedFloats.fill(2.0f); Require(api.decode(states[0].data(), CELT_PACKET, sizeof(CELT_PACKET), reference.data() + 1, 3840) == 3840); Require(api.decodeWithPriority(states[1].data(), CELT_PACKET, sizeof(CELT_PACKET), prioritized.data() + 1, 3841, 100) == 3840); Require(api.decodeFloat(states[2].data(), CELT_PACKET, sizeof(CELT_PACKET), floats.data() + 1, 7683) == 7680); Require(api.decodeFloatWithPriority(states[3].data(), CELT_PACKET, sizeof(CELT_PACKET), prioritizedFloats.data() + 1, 7680, 20) == 7680); Require(prioritized == reference); Require(std::any_of(reference.begin() + 1, reference.end() - 1, [](auto value) { return value != 0; })); Require(floats.front() == 2.0f && floats.back() == 2.0f && floats == prioritizedFloats); for (std::size_t index = 1; index <= 1920; ++index) Require(floats[index] == Converted(reference[index])); } floats.fill(2.0f); RequireThrows([&] { api.decodeFloat(states[2].data(), CELT_PACKET, sizeof(CELT_PACKET), floats.data() + 1, 7679); }); Require(std::all_of(floats.begin(), floats.end(), [](auto value) { return value == 2.0f; })); for (auto& state : states) Require(api.destroy(state.data()) == 0); Require(api.terminate(&context) == 0); } void RequireTimeoutVariants() { std::uint32_t context = 0; Require(sceOpusDecInitialize(&context) == 0); std::array, 2> states{}; for (auto& state : states) Require(sceOpusDecCreateEx(&context, state.data(), 48000, 2) == 0); std::array invalidState{}; std::int16_t output = 12345; const std::uint8_t packet = 0; std::uint32_t batch = 0xA5A5A5A5u; Require(sceOpusDecDecodeWithPriorityAndTimeout(invalidState.data(), &packet, 1, &output, 2, 19, 0, &batch) == static_cast(0x8174000Au)); Require(sceOpusDecDecodeWithPriorityAndTimeout(invalidState.data(), &packet, 1, &output, 2, 20, 0xFFFFFFFFu, &batch) == static_cast(0x81740001u)); Require(sceOpusDecDecodeFloatWithPriorityAndTimeout(nullptr, &packet, 1, nullptr, 2, 20, 0, &batch) == static_cast(0x81740000u)); Require(sceOpusDecDecodeFloatWithPriorityAndTimeout(invalidState.data(), &packet, -1, nullptr, 2, 20, 1000, &batch) == static_cast(0x81740007u)); std::array reference{}, pcm{}; std::array floats{}; for (int frame = 0; frame < 2; ++frame) { Require(sceOpusDecDecode(states[0].data(), CELT_PACKET, sizeof(CELT_PACKET), reference.data(), 3840) == 3840); Require(sceOpusDecDecodeWithPriorityAndTimeout(states[1].data(), CELT_PACKET, sizeof(CELT_PACKET), pcm.data(), 3840, 40, 0xFFFFFFFFu, &batch) == 3840); Require(pcm == reference); } pcm.fill(12345); floats.fill(2.0f); for (std::uint32_t timeout : {0u, 1u, 1000u, 0xFFFFFFFEu}) { RequireThrows([&] { sceOpusDecDecodeWithPriorityAndTimeout(states[1].data(), CELT_PACKET, sizeof(CELT_PACKET), pcm.data(), 3840, 40, timeout, &batch); }); RequireThrows([&] { sceOpusDecDecodeFloatWithPriorityAndTimeout(states[1].data(), CELT_PACKET, sizeof(CELT_PACKET), floats.data(), 7680, 40, timeout, &batch); }); } Require(batch == 0xA5A5A5A5u); Require(std::all_of(pcm.begin(), pcm.end(), [](auto value) { return value == 12345; })); Require(std::all_of(floats.begin(), floats.end(), [](auto value) { return value == 2.0f; })); Require(sceOpusDecDecode(states[0].data(), CELT_PACKET, sizeof(CELT_PACKET), reference.data(), 3840) == 3840); Require(sceOpusDecDecodeFloatWithPriorityAndTimeout(states[1].data(), CELT_PACKET, sizeof(CELT_PACKET), floats.data(), 7680, 40, 0xFFFFFFFFu, nullptr) == 7680); for (std::size_t index = 0; index < floats.size(); ++index) Require(floats[index] == Converted(reference[index])); for (auto& state : states) Require(sceOpusDecDestroy(state.data()) == 0); Require(sceOpusDecTerminate(&context) == 0); } } // namespace int main() { const Api opus{0x81740000u, sceOpusDecInitialize, sceOpusDecTerminate, sceOpusDecCreateEx, sceOpusDecDecode, sceOpusDecDecodeWithPriority, sceOpusDecDecodeFloat, sceOpusDecDecodeFloatWithPriority, sceOpusDecDestroy}; const Api celt{0x81400000u, sceOpusCeltDecInitialize, sceOpusCeltDecTerminate, sceOpusCeltDecCreateEx, sceOpusCeltDecDecode, sceOpusCeltDecDecodeWithPriority, sceOpusCeltDecDecodeFloat, sceOpusCeltDecDecodeFloatWithPriority, sceOpusCeltDecDestroy}; for (const auto* api : {&opus, &celt}) { RequireValidation(*api); RequireDecoding(*api); } RequireTimeoutVariants(); }