Files
AnyPS5/core/libs/prx/libSceAudioOut/src/AudioOut2SpeakerArray.cpp
T
primesoul-web 3f03bc0e24 feat(libs): finish the CesCs conversions and the AudioOut, AudioIn and GameUpdate exports
libSceCesCs goes to 17 of 17: three-byte EUC-JP JIS X 0212 (0x8F)
characters convert through a table read from the Unicode consortium's
JIS0212.TXT, both directions, so sceCesEucJpToUcs2, the MbcsStrTo*
and *Get*Len forms and the context conversions accept them, and a
character with cells in both JIS X 0208 and JIS X 0212 still encodes
to the two-byte form. sceCesRefersUcsProfileCp1250 returns a profile
with the Windows-1250 table of Microsoft's bestfit1250.txt, accepted
by the SBC and context conversions.

The AudioOut HDMI bitstream Sys family follows the Ex ports: SysOpen
opens a port only for mode 5, the TrueHD MAT route EVO-PLAYER-PS5
reads from the firmware 12.70 mode tables, paced at 192 kHz with no
host device; SysConfigureOutput accepts the observed HDMI MAT switch;
SysClose closes a Sys port. sceAudioOut2MasteringGetState fills the
states by states id with the descriptors KytyPS5 declares, SetParam
answers a null parameter with the mastering invalid-API-parameter
error, Set3DLatency follows KytyPS5's output validation (0x80268001
above 2, any user), SetSystemDebugState records the debug state id,
and GetSpeakerArrayCoefficients returns the front pair KytyPS5 and
prosper both return.

sceAudioInOpen accepts param 0x10 as float mono like KytyPS5; HqOpen
answers a longer grain or a lower rate with the invalid-size and
invalid-frequency errors; AsyncOpen also accepts the 384-sample grain
SharpProspero documents. sceGameUpdateGetAddcontLatestVersion answers
a null entitlement label with 0x80412803, as prosper does.
2026-10-18 18:52:36 +02:00

136 lines
5.5 KiB
C++

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <new>
#include <stdexcept>
#include <string>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
struct GuestVbapParams {
const AudioOut2Position* positions;
std::uint32_t numSpeakers;
std::uint8_t is3d;
void* memory;
std::size_t memorySize;
std::uint32_t reserved;
};
constexpr std::uint32_t MaxSpeakers = 32;
constexpr std::uint32_t AmbisonicsChannelFlag = 0x40;
constexpr std::uint32_t MaxAmbisonicsOrder = 5;
struct SpeakerArray {
std::uint32_t magic;
std::uint32_t numSpeakers;
AudioOut2Position directions[MaxSpeakers];
};
constexpr std::uint32_t SpeakerArrayMagic = 0x53504b41;
SpeakerArray& Array(AudioOut2SpeakerArrayHandle handle) {
auto* array = static_cast<SpeakerArray*>(handle);
if (array == nullptr || array->magic != SpeakerArrayMagic) throw std::invalid_argument("invalid speaker array handle");
return *array;
}
float Sn3d(std::uint32_t acn, const AudioOut2Position& direction) {
const int n = static_cast<int>(std::sqrt(static_cast<double>(acn)));
const int m = static_cast<int>(acn) - n * n - n;
const int am = std::abs(m);
const double length = std::sqrt(double(direction.x) * direction.x + double(direction.y) * direction.y + double(direction.z) * direction.z);
const double sinEl = length > 0 ? direction.z / length : 0;
const double cosEl = std::sqrt(std::max(0.0, 1 - sinEl * sinEl));
const double azimuth = std::atan2(direction.y, direction.x);
double legendre = 1;
for (int i = 1; i <= am; ++i) legendre *= (2 * i - 1) * cosEl;
if (n > am) {
double previous = legendre;
double current = sinEl * (2 * am + 1) * legendre;
for (int l = am + 2; l <= n; ++l) {
const double next = (sinEl * (2 * l - 1) * current - (l + am - 1) * previous) / (l - am);
previous = current;
current = next;
}
legendre = current;
}
double ratio = 1;
for (int i = n - am + 1; i <= n + am; ++i) ratio /= i;
const double norm = std::sqrt((m == 0 ? 1.0 : 2.0) * ratio);
return static_cast<float>(norm * legendre * (m < 0 ? std::sin(am * azimuth) : std::cos(am * azimuth)));
}
}
extern "C" {
int APS5_VABI sceAudioOut2SpeakerArrayCreate(AudioOut2SpeakerArrayHandle* handle, const void* vbap_params, const void* ambi_params) {
const auto* params = static_cast<const GuestVbapParams*>(vbap_params);
if (handle == nullptr || params == nullptr || ambi_params == nullptr || params->positions == nullptr ||
params->numSpeakers == 0 || params->numSpeakers > MaxSpeakers || params->memory == nullptr ||
params->memorySize < sizeof(SpeakerArray) || reinterpret_cast<std::uintptr_t>(params->memory) % alignof(SpeakerArray) != 0) {
APS5_INVALID_ARG_EX;
}
auto* array = new (params->memory) SpeakerArray{SpeakerArrayMagic, params->numSpeakers, {}};
std::copy_n(params->positions, params->numSpeakers, array->directions);
*handle = array;
return 0;
}
int APS5_VABI sceAudioOut2SpeakerArrayDestroy(AudioOut2SpeakerArrayHandle handle) {
Array(handle).magic = 0;
return 0;
}
int APS5_VABI sceAudioOut2GetSpeakerArrayAmbisonicsCoefficients(AudioOut2SpeakerArrayHandle handle, uint32_t ambisonics_channel, float* coefficients, uint32_t num_coefficients) {
const auto& array = Array(handle);
if (coefficients == nullptr || num_coefficients != array.numSpeakers) APS5_INVALID_ARG_EX;
const std::uint32_t acn = ambisonics_channel & ~AmbisonicsChannelFlag;
if ((ambisonics_channel & AmbisonicsChannelFlag) == 0 || acn >= (MaxAmbisonicsOrder + 1) * (MaxAmbisonicsOrder + 1)) {
throw std::invalid_argument("unsupported ambisonics channel " + std::to_string(ambisonics_channel));
}
const auto order = static_cast<std::uint32_t>(std::sqrt(static_cast<double>(acn)));
const std::uint32_t decodable = std::max<std::uint32_t>(1, (array.numSpeakers - 1) / 2);
for (std::uint32_t speaker = 0; speaker < array.numSpeakers; ++speaker) {
coefficients[speaker] = order > decodable ? 0.0f : Sn3d(acn, array.directions[speaker]) / static_cast<float>(array.numSpeakers);
}
return 0;
}
int APS5_VABI sceAudioOut2GetSpeakerArrayCoefficients(AudioOut2SpeakerArrayHandle handle, AudioOut2Position pos, float spread, float* coefficients, uint32_t num_coefficients, uint8_t height_aware, float downmix_spread_radius) {
(void)pos;
(void)spread;
(void)height_aware;
(void)downmix_spread_radius;
const SpeakerArray& array = Array(handle);
if (coefficients == nullptr || num_coefficients != array.numSpeakers) APS5_INVALID_ARG_EX;
std::fill_n(coefficients, array.numSpeakers, 0.0f);
if (array.numSpeakers > 0) coefficients[0] = 1.0f;
if (array.numSpeakers > 1) coefficients[1] = 1.0f;
return 0;
}
size_t APS5_VABI sceAudioOut2GetSpeakerArrayMemorySize(uint32_t num_speakers, uint8_t is_3d, uint8_t is_ambisonics) {
(void)is_3d;
(void)is_ambisonics;
if (num_speakers == 0 || num_speakers > MaxSpeakers) APS5_INVALID_ARG_EX;
return sizeof(SpeakerArray);
}
int APS5_VABI sceAudioOut2GetSpeakerInfo(AudioOut2SpeakerInfo* info, uint32_t flags) {
(void)flags;
if (!info) return static_cast<int>(0x80260502);
constexpr std::uint8_t SpeakerTypeStereo = 0;
constexpr std::uint32_t FrontLeftAndRight = 0x3;
*info = AudioOut2SpeakerInfo{};
info->type = SpeakerTypeStereo;
info->available_bits = FrontLeftAndRight;
info->speaker_angle[0] = {-30, 0};
info->speaker_angle[1] = {30, 0};
return 0;
}
}