#include "prx/libScePad/include/PadState.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include extern "C" { int APS5_VABI sceAudioOutOpen(int, int, int, std::uint32_t, std::uint32_t, std::uint32_t); int APS5_VABI sceAudioOutExOpen(int, std::uint32_t); int APS5_VABI sceAudioOutClose(int); int APS5_VABI sceAudioOutOutput(int, const void*); int APS5_VABI sceAudioOutOutputs(AudioOutOutputParam*, std::uint32_t); int APS5_VABI sceAudioOutSetVolume(int, std::uint32_t, int*); } static void Require(bool value, const char* message) { if (!value) throw std::runtime_error(message); } struct Case { std::string_view name; std::uint32_t format = 1; bool batch = false; std::array volume{32768, 32768}; std::array expected{128, 255}; bool silence = false; bool drain = false; bool ex = false; }; int main(int argc, char** argv) { try { const std::string_view mode = argc == 2 ? argv[1] : "--s16"; const Case cases[] = { {"--s16"}, {"--f32", 4}, {"--batch_s16", 1, true}, {"--batch_f32", 4, true}, {"--mono_s16", 0, false, {32768, 32768}, {128, 128}}, {"--mono_f32", 3, false, {32768, 32768}, {128, 128}}, {"--gain_s16", 1, false, {16384, 8192}, {64, 64}}, {"--gain_f32", 4, true, {16384, 8192}, {64, 64}}, {"--muted", 4, false, {0, 0}, {0, 0}}, {"--silence", 1, true, {32768, 32768}, {0, 0}, true}, {"--drain", 4, false, {32768, 32768}, {0, 0}, false, true}, {"--ex", 1, true, {32768, 32768}, {0, 0}, false, false, true}, {"--pad_control"} }; const Case* selected = nullptr; for (const auto& entry : cases) if (entry.name == mode) selected = &entry; Require(argc <= 2 && selected != nullptr, "invalid test arguments"); const auto& test = *selected; Pad::Initialize(); Pad::SetVibrationMode(1); std::uint32_t seen = 0; PadOutputState output{}; static_cast(PadFetchOutput_nid_postfix(&seen, &output)); struct Port { int handle = -1; ~Port() { if (handle > 0) sceAudioOutClose(handle); } } port; if (mode == "--pad_control") { PadSubmitAudioHaptics_nid_postfix(128, 255); } else { const std::uint32_t frames = test.ex ? 256u : 48000u; const bool floating = test.format == 3 || test.format == 4; const std::uint32_t channels = test.format == 0 || test.format == 3 ? 1u : 2u; std::vector integers(frames * 2); std::vector floats(frames * 2); for (std::uint32_t frame = 0; frame < frames && !test.silence; ++frame) { integers[frame * channels] = 8192; floats[frame * channels] = 0.25f; if (channels == 2) { integers[frame * channels + 1] = -16384; floats[frame * channels + 1] = -0.5f; } } port.handle = test.ex ? sceAudioOutExOpen(0x10000000, 0) : sceAudioOutOpen(0x10000000, 10, 0, frames, 48000, test.format); Require(port.handle > 0, "legacy vibration port did not open"); auto volume = test.volume; Require(sceAudioOutSetVolume(port.handle, 3, volume.data()) == 0, "legacy vibration volume failed"); const void* samples = test.drain ? nullptr : floating ? static_cast(floats.data()) : static_cast(integers.data()); AudioOutOutputParam param{}; param.handle = port.handle; param.ptr = samples; const int result = test.batch ? sceAudioOutOutputs(¶m, 1) : sceAudioOutOutput(port.handle, samples); Require(result == static_cast(frames), "legacy vibration output returned the wrong frame count"); } const bool changed = PadFetchOutput_nid_postfix(&seen, &output); Require(changed == (test.expected[0] != 0 || test.expected[1] != 0), "legacy vibration output published the wrong controller state"); Require(output.audioVibrationLarge == test.expected[0] && output.audioVibrationSmall == test.expected[1], "legacy vibration PCM did not reach the existing motor bridge"); Require(output.vibrationLarge == 0 && output.vibrationSmall == 0, "audio vibration replaced regular motor state"); std::puts("legacy vibration bridge test passed"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "%s\n", error.what()); return 1; } }