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

117 lines
6.3 KiB
C++

#include "prx/libSceAudioOut/src/AudioOut2PadMix.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
static void Require(bool value, const char*) {
if (!value) std::abort();
}
template<typename TAction>
static bool Throws(TAction action) {
try {
action();
} catch (const std::invalid_argument&) {
return true;
}
return false;
}
template<std::uint32_t Frames>
static std::array<float, Frames * AUDIO_OUT2_PAD_CHANNELS> Silence() {
return {};
}
static void Accumulate(AudioOut2Route route, const float* data, std::uint32_t channels, const float* volume, float* out, std::uint32_t frames) {
for (std::uint32_t frame = 0; frame < frames; frame++) AudioOut2AccumulatePadFrame(route, data + static_cast<std::size_t>(frame) * channels, channels, volume, out + static_cast<std::size_t>(frame) * AUDIO_OUT2_PAD_CHANNELS);
}
int main() {
Require(AudioOut2RouteForPort(0x3, 1) == AudioOut2Route::PadSpeaker, "mono type 0x3 is the pad speaker");
Require(AudioOut2RouteForPort(0x3, 2) == AudioOut2Route::PadSpeaker, "stereo type 0x3 is the pad speaker");
Require(AudioOut2RouteForPort(0x6, 2) == AudioOut2Route::PadVibration, "stereo type 0x6 is the vibration");
Require(AudioOut2RouteForPort(0x6, 1) == AudioOut2Route::PadVibration, "mono type 0x6 is the vibration");
Require(AudioOut2RouteForPort(0x6, 8) == AudioOut2Route::Main, "an 8-channel type 0x6 stays in the main mix");
Require(AudioOut2RouteForPort(0x3, 0) == AudioOut2Route::Main, "an undecoded format stays in the main mix");
for (std::uint16_t type : {0x0, 0x1, 0x2, 0x4, 0x5, 0x7, 0x100}) {
Require(AudioOut2RouteForPort(type, 1) == AudioOut2Route::Main, "other port types stay in the main mix");
Require(AudioOut2RouteForPort(type, 2) == AudioOut2Route::Main, "other port types stay in the main mix");
}
Require(AudioOut2IsPadAudioDevice("DualSense wireless controller (PS5) Direct DualSense Wireless Controller"), "pulse name");
Require(AudioOut2IsPadAudioDevice("DualSense Edge Wireless Controller, USB Audio"), "edge alsa name");
Require(!AudioOut2IsPadAudioDevice("AD102 High Definition Audio Controller Digital Stereo (HDMI)"), "hdmi name");
Require(!AudioOut2IsPadAudioDevice(nullptr), "null name");
constexpr std::uint32_t frames = 3;
const float unity[2] = {1.0f, 1.0f};
{
auto out = Silence<frames>();
const float speaker[frames] = {0.5f, -0.25f, 0.125f};
const float half[1] = {0.5f};
Accumulate(AudioOut2Route::PadSpeaker, speaker, 1, half, out.data(), frames);
for (std::uint32_t frame = 0; frame < frames; frame++) {
const float* pad = &out[frame * AUDIO_OUT2_PAD_CHANNELS];
Require(pad[0] == speaker[frame] * 0.5f && pad[1] == speaker[frame] * 0.5f, "mono speaker on channels 1-2");
Require(pad[2] == 0.0f && pad[3] == 0.0f, "speaker leaves the actuators alone");
}
}
{
auto out = Silence<frames>();
const float vibration[frames * 2] = {0.1f, 0.2f, 0.3f, 0.4f, -0.5f, -0.6f};
const float gains[2] = {1.0f, 0.5f};
Accumulate(AudioOut2Route::PadVibration, vibration, 2, gains, out.data(), frames);
for (std::uint32_t frame = 0; frame < frames; frame++) {
const float* pad = &out[frame * AUDIO_OUT2_PAD_CHANNELS];
Require(pad[0] == 0.0f && pad[1] == 0.0f, "vibration leaves the speaker alone");
Require(pad[2] == vibration[frame * 2] && pad[3] == vibration[frame * 2 + 1] * 0.5f, "vibration on channels 3-4");
}
}
{
auto out = Silence<frames>();
const float speaker[frames] = {0.75f, 0.75f, 0.0f};
const float vibration[frames * 2] = {0.9f, -0.9f, 0.0f, 0.0f, 0.0f, 0.0f};
Accumulate(AudioOut2Route::PadSpeaker, speaker, 1, unity, out.data(), frames);
Accumulate(AudioOut2Route::PadSpeaker, speaker, 1, unity, out.data(), frames);
Accumulate(AudioOut2Route::PadVibration, vibration, 2, unity, out.data(), frames);
Accumulate(AudioOut2Route::PadVibration, vibration, 2, unity, out.data(), frames);
AudioOut2FinishPadMix(out.data(), frames);
Require(out[0] == 1.0f && out[1] == 1.0f, "speaker sum clamps at full scale");
Require(out[2] == 1.0f && out[3] == -1.0f, "vibration sum clamps at full scale");
Require(out[4] == 1.0f && out[6] == 0.0f, "second frame");
for (std::uint32_t index = 8; index < frames * AUDIO_OUT2_PAD_CHANNELS; index++) Require(out[index] == 0.0f, "silent frame stays silent");
}
{
auto out = Silence<1>();
const float data[8] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
Require(Throws([&] { AudioOut2AccumulatePadFrame(AudioOut2Route::Main, data, 2, unity, out.data()); }), "a main-mix port is refused");
Require(Throws([&] { AudioOut2AccumulatePadFrame(AudioOut2Route::PadVibration, data, 8, unity, out.data()); }), "an 8-channel pad port is refused");
for (float sample : out) Require(sample == 0.0f, "a refused port mixes nothing");
}
{
const auto quad = AudioOut2PadLayoutForDriver("pipewire");
Require(quad.channels == 4 && quad.position[0] == 0 && quad.position[1] == 1 && quad.position[2] == 2 && quad.position[3] == 3, "quad layout");
Require(AudioOut2PadLayoutForDriver("alsa").channels == 4 && AudioOut2PadLayoutForDriver(nullptr).channels == 4, "alsa and unknown use quad");
const auto pulse = AudioOut2PadLayoutForDriver("pulseaudio");
Require(pulse.channels == 6 && pulse.position[0] == 0 && pulse.position[1] == 1 && pulse.position[2] == 4 && pulse.position[3] == 5, "pulse layout");
const float pad[2 * AUDIO_OUT2_PAD_CHANNELS] = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f};
float out[2 * AUDIO_OUT2_PAD_DEVICE_CHANNELS_MAX];
for (float& sample : out) sample = 9.0f;
AudioOut2WritePadFrames(pad, pulse, out, 2);
const float expected[2 * 6] = {0.1f, 0.2f, 0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 0.6f, 0.0f, 0.0f, 0.7f, 0.8f};
for (int index = 0; index < 12; index++) Require(out[index] == expected[index], "pulse frames: speaker front, centre and LFE silent, actuators back");
AudioOut2WritePadFrames(pad, quad, out, 2);
for (int index = 0; index < 8; index++) Require(out[index] == pad[index], "quad frames are the pad mix");
}
return 0;
}