AnyPS5/core/libs/prx/libSceAudioOut/tests/AudioOutLastOutputTime.cpp

159 lines
6.2 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <thread>
#include <vector>
extern "C" {
int APS5_VABI sceAudioOutOpen(int, int, int, std::uint32_t, std::uint32_t, 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 sceAudioOutGetLastOutputTime(int, std::uint64_t*);
std::uint64_t APS5_VABI sceKernelGetProcessTime();
}
static void Require(bool value, const char* message) {
if (value) return;
std::fprintf(stderr, "%s\n", message);
std::abort();
}
namespace {
constexpr int user = 0x10000000;
constexpr int portTypeMain = 0;
constexpr int portTypeVibration = 10;
constexpr std::uint32_t formatS16Mono = 0;
constexpr std::uint32_t frames = 256;
constexpr std::uint32_t frequency = 48000;
constexpr std::uint64_t untouched = 0xA5A5A5A5A5A5A5A5ull;
constexpr int invalidPort = static_cast<int>(0x80260003);
constexpr int invalidPointer = static_cast<int>(0x80260004);
int Open(int type) {
const int handle = sceAudioOutOpen(user, type, 0, frames, frequency, formatS16Mono);
Require(handle > 0, "port must open");
return handle;
}
std::uint64_t LastOutputTime(int handle) {
std::uint64_t time = untouched;
Require(sceAudioOutGetLastOutputTime(handle, &time) == 0, "an open port must report its last output time");
return time;
}
void Pause() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
std::uint64_t OutputAndCheck(int handle, const std::vector<std::int16_t>& block) {
Pause();
const std::uint64_t before = sceKernelGetProcessTime();
Require(sceAudioOutOutput(handle, block.data()) == static_cast<int>(frames), "output must accept the block");
const std::uint64_t after = sceKernelGetProcessTime();
const std::uint64_t time = LastOutputTime(handle);
Require(time >= before && time <= after, "last output time must be the process time of the output");
return time;
}
void TestOutput(int type) {
const std::vector<std::int16_t> block(frames, 256);
const int handle = Open(type);
Require(LastOutputTime(handle) == 0, "a port that never output must report 0");
const std::uint64_t first = OutputAndCheck(handle, block);
Pause();
Require(LastOutputTime(handle) == first, "the time must not move without an output");
const std::uint64_t second = OutputAndCheck(handle, block);
Require(second > first, "a later output must report a later time");
Pause();
Require(sceAudioOutOutput(handle, nullptr) == static_cast<int>(frames), "waiting for the output must succeed");
Require(LastOutputTime(handle) == second, "waiting without data must not move the time");
Require(sceAudioOutClose(handle) == 0, "port must close");
}
void TestPortsAreIndependent() {
const std::vector<std::int16_t> block(frames, 256);
const int played = Open(portTypeMain);
const int silent = Open(portTypeMain);
Require(played != silent, "two open ports must have different handles");
OutputAndCheck(played, block);
Require(LastOutputTime(silent) == 0, "an output must not move the time of another port");
Require(sceAudioOutClose(played) == 0, "port must close");
Require(sceAudioOutClose(silent) == 0, "port must close");
}
void TestOutputs() {
const std::vector<std::int16_t> block(frames, 256);
const int first = Open(portTypeMain);
const int second = Open(portTypeMain);
const int waiting = Open(portTypeMain);
AudioOutOutputParam params[3] = {{first, block.data()}, {second, block.data()}, {waiting, nullptr}};
Pause();
const std::uint64_t before = sceKernelGetProcessTime();
Require(sceAudioOutOutputs(params, 3) == static_cast<int>(frames), "outputs must accept the blocks");
const std::uint64_t after = sceKernelGetProcessTime();
for (const int handle : {first, second}) {
const std::uint64_t time = LastOutputTime(handle);
Require(time >= before && time <= after, "outputs must set the time of every port that received data");
}
Require(LastOutputTime(waiting) == 0, "outputs must not move the time of a port that received no data");
for (const int handle : {first, second, waiting}) Require(sceAudioOutClose(handle) == 0, "port must close");
}
void TestReopenedPort() {
const std::vector<std::int16_t> block(frames, 256);
const int handle = Open(portTypeMain);
OutputAndCheck(handle, block);
Require(sceAudioOutClose(handle) == 0, "port must close");
const int reopened = Open(portTypeMain);
Require(LastOutputTime(reopened) == 0, "a port opened after a close must not keep the time of the closed one");
Require(sceAudioOutClose(reopened) == 0, "port must close");
}
void TestErrors() {
const std::vector<std::int16_t> block(frames, 256);
const int handle = Open(portTypeMain);
const std::uint64_t time = OutputAndCheck(handle, block);
Require(sceAudioOutGetLastOutputTime(handle, nullptr) == invalidPointer, "a null destination must be rejected");
Require(LastOutputTime(handle) == time, "a rejected call must not move the time");
std::uint64_t destination = untouched;
Require(sceAudioOutGetLastOutputTime(0, &destination) == invalidPort, "handle 0 must be rejected");
Require(sceAudioOutGetLastOutputTime(-1, &destination) == invalidPort, "a negative handle must be rejected");
Require(sceAudioOutGetLastOutputTime(handle + 1, &destination) == invalidPort, "a port that is not open must be rejected");
Require(sceAudioOutGetLastOutputTime(1000, &destination) == invalidPort, "an out of range handle must be rejected");
Require(sceAudioOutClose(handle) == 0, "port must close");
Require(sceAudioOutGetLastOutputTime(handle, &destination) == invalidPort, "a closed port must be rejected");
Require(destination == untouched, "a rejected call must not write the destination");
}
}
int main() {
#ifdef _WIN32
_putenv_s("SDL_AUDIODRIVER", "dummy");
#else
setenv("SDL_AUDIODRIVER", "dummy", 1);
#endif
sceKernelGetProcessTime();
TestOutput(portTypeMain);
TestOutput(portTypeVibration);
TestPortsAreIndependent();
TestOutputs();
TestReopenedPort();
TestErrors();
return 0;
}