Files
AnyPS5/core/libs/prx/libSceVoice/Export.cpp
T

393 lines
12 KiB
C++

#include <cstdint>
#include <cstddef>
#include <cstring>
#include <map>
#include <mutex>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr std::int32_t PortInDevice = 0;
constexpr std::int32_t PortInPcm = 1;
constexpr std::int32_t PortInVoice = 2;
constexpr std::int32_t PortOutPcm = 3;
constexpr std::int32_t PortOutVoice = 4;
constexpr std::int32_t PortOutDevice = 5;
constexpr std::int32_t PortStateReady = 1;
constexpr std::uint32_t VoiceFrameMs = 20;
constexpr std::int32_t PortAttrVoiceActivity = 1001;
constexpr int VoiceErrorNotInitialized = static_cast<int>(0x804E0801u);
constexpr int VoiceErrorAlreadyInitialized = static_cast<int>(0x804E0802u);
constexpr auto NotInitialized = static_cast<std::int32_t>(0x804e0801u);
constexpr auto InvalidPort = static_cast<std::int32_t>(0x804e0804u);
constexpr auto InvalidArgument = static_cast<std::int32_t>(0x804e0805u);
constexpr auto PortNotFound = static_cast<std::int32_t>(0x804e0807u);
struct Port {
std::int32_t type;
float volume;
std::uint32_t bitrate;
bool muted = false;
bool paused = false;
};
struct Voice {
std::mutex mutex;
bool initialized = false;
bool started = false;
std::int32_t appType = 0;
std::uint32_t nextId = 0;
std::map<std::uint32_t, Port> ports;
std::set<std::pair<std::uint32_t, std::uint32_t>> connections;
};
Voice& State() {
static Voice voice;
return voice;
}
[[noreturn]] void Fail(const char* function, const std::string& why) {
throw std::logic_error(std::string(function) + ": " + why);
}
Port& RequirePort(Voice& voice, std::uint32_t id, const char* function) {
if (!voice.initialized) Fail(function, "library not initialized");
const auto found = voice.ports.find(id);
if (found == voice.ports.end()) Fail(function, "unknown port " + std::to_string(id));
return found->second;
}
bool IsInput(std::int32_t type) { return type == PortInDevice || type == PortInPcm || type == PortInVoice; }
}
extern "C" {
int APS5_VABI sceVoiceInit(VoiceInitParam* param, int32_t version) {
(void)version;
if (param == nullptr) APS5_INVALID_ARG_EX;
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (voice.initialized) return VoiceErrorAlreadyInitialized;
voice.appType = param->app_type;
voice.initialized = true;
return 0;
}
int APS5_VABI sceVoiceSetThreadsParams(void* params) {
if (params == nullptr) APS5_INVALID_ARG_EX;
return 0;
}
int APS5_VABI sceVoiceStart(const VoiceStartParam* param) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return VoiceErrorNotInitialized;
if (param == nullptr) APS5_INVALID_ARG_EX;
voice.started = true;
return 0;
}
int APS5_VABI sceVoiceStop(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) Fail(__func__, "library not initialized");
voice.started = false;
return 0;
}
int APS5_VABI sceVoiceEnd_nid_postfix(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) Fail(__func__, "library not initialized");
voice.ports.clear();
voice.connections.clear();
voice.started = false;
voice.appType = 0;
voice.initialized = false;
return 0;
}
int APS5_VABI sceVoiceCreatePort(uint32_t* port_id, const VoicePortParam* param) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return VoiceErrorNotInitialized;
if (port_id == nullptr || param == nullptr) APS5_INVALID_ARG_EX;
if (param->port_type < PortInDevice || param->port_type > PortOutDevice) Fail(__func__, "unknown port type " + std::to_string(param->port_type));
const bool voicePort = param->port_type == PortInVoice || param->port_type == PortOutVoice;
const auto id = voice.nextId++;
voice.ports.emplace(id, Port{param->port_type, param->volume, voicePort ? static_cast<std::uint32_t>(param->voice.bitrate) : 0u});
*port_id = id;
return 0;
}
int APS5_VABI sceVoiceDeletePort(uint32_t port_id) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
RequirePort(voice, port_id, __func__);
std::erase_if(voice.connections, [port_id](const auto& link) { return link.first == port_id || link.second == port_id; });
voice.ports.erase(port_id);
return 0;
}
int APS5_VABI sceVoiceConnectIPortToOPort(uint32_t input_port_id, uint32_t output_port_id) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!IsInput(RequirePort(voice, input_port_id, __func__).type)) Fail(__func__, "first port is not an input port");
if (IsInput(RequirePort(voice, output_port_id, __func__).type)) Fail(__func__, "second port is not an output port");
voice.connections.emplace(input_port_id, output_port_id);
return 0;
}
int APS5_VABI sceVoiceDisconnectIPortFromOPort(uint32_t input_port_id, uint32_t output_port_id) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
RequirePort(voice, input_port_id, __func__);
RequirePort(voice, output_port_id, __func__);
if (voice.connections.erase({input_port_id, output_port_id}) == 0) Fail(__func__, "ports are not connected");
return 0;
}
int APS5_VABI sceVoiceGetBitRate(uint32_t port_id, uint32_t* bitrate) {
if (bitrate == nullptr) APS5_INVALID_ARG_EX;
auto& voice = State();
std::lock_guard lock(voice.mutex);
const auto& port = RequirePort(voice, port_id, __func__);
if (port.type != PortInVoice && port.type != PortOutVoice) Fail(__func__, "port does not carry encoded voice");
*bitrate = port.bitrate;
return 0;
}
int APS5_VABI sceVoiceSetVolume(uint32_t port_id, float volume) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
RequirePort(voice, port_id, __func__).volume = volume;
return 0;
}
int APS5_VABI sceVoiceGetVolume(uint32_t port_id, float* volume) {
if (volume == nullptr) APS5_INVALID_ARG_EX;
auto& voice = State();
std::lock_guard lock(voice.mutex);
*volume = RequirePort(voice, port_id, __func__).volume;
return 0;
}
int APS5_VABI sceVoiceGetPortInfo(uint32_t port_id, VoicePortInfo* info) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return VoiceErrorNotInitialized;
if (info == nullptr) APS5_INVALID_ARG_EX;
const auto& port = RequirePort(voice, port_id, __func__);
*info = VoicePortInfo{};
info->port_type = port.type;
info->state = PortStateReady;
if (port.bitrate != 0) info->frame_size = (port.bitrate * VoiceFrameMs + 7999u) / 8000u;
return 0;
}
int APS5_VABI sceVoiceGetPortAttr(std::uint32_t portId, std::int32_t attr, void* value, std::int32_t size) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
if (portId > 127) return InvalidPort;
if (value == nullptr) return InvalidArgument;
std::int32_t expectedSize;
switch (attr) {
case 1000:
case 1001:
case 2000:
case 2002:
expectedSize = 1;
break;
case 1002:
case 2001:
expectedSize = 4;
break;
case 1003:
expectedSize = 2;
break;
default:
return InvalidArgument;
}
if (size != expectedSize) return InvalidArgument;
const auto found = voice.ports.find(portId);
if (found == voice.ports.end()) return InvalidPort;
const auto requiredType = attr == 1003 ? PortOutDevice : PortInDevice;
if (found->second.type != requiredType) return InvalidArgument;
Fail(__func__, "device attribute backend is not implemented");
}
int APS5_VABI sceVoiceReadFromOPort(uint32_t output_port_id, void* data, uint32_t* size) {
if (data == nullptr || size == nullptr) APS5_INVALID_ARG_EX;
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (IsInput(RequirePort(voice, output_port_id, __func__).type)) Fail(__func__, "port is not an output port");
*size = 0;
return 0;
}
int APS5_VABI sceVoiceWriteToIPort(uint32_t input_port_id, const void* data, uint32_t* size, int16_t frame_gaps) {
(void)input_port_id;
(void)data;
(void)size;
(void)frame_gaps;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceSetMuteFlag(uint32_t port_id, bool muted) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
RequirePort(voice, port_id, __func__).muted = muted;
return 0;
}
int APS5_VABI sceVoiceGetMuteFlag(uint32_t port_id, bool* muted) {
if (muted == nullptr) APS5_INVALID_ARG_EX;
auto& voice = State();
std::lock_guard lock(voice.mutex);
*muted = RequirePort(voice, port_id, __func__).muted;
return 0;
}
int APS5_VABI sceVoiceGetResourceInfo(VoiceResourceInfo* info) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
if (info == nullptr) return InvalidArgument;
switch (voice.appType) {
case 4:
*info = {0, 0, 4, 5};
break;
case 0x08000000:
case 0x10000000:
*info = {15, 4, 4, 4};
break;
case 0x20000000:
*info = {7, 4, 4, 4};
break;
default:
Fail(__func__, "unsupported initialization mode");
}
return 0;
}
int APS5_VABI sceVoiceEnableChat(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
Fail(__func__, "audio chat backend is not implemented");
}
int APS5_VABI sceVoiceResetPort(std::uint32_t portId) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
if (portId > 127) return InvalidPort;
const auto found = voice.ports.find(portId);
if (found == voice.ports.end()) return PortNotFound;
if (voice.started && (found->second.type == PortInDevice || found->second.type == PortOutDevice)) Fail(__func__, "device reset backend is not implemented");
return 0;
}
int APS5_VABI sceVoicePausePort(std::uint32_t portId) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
if (portId > 127) return InvalidPort;
const auto found = voice.ports.find(portId);
if (found == voice.ports.end()) return PortNotFound;
found->second.paused = true;
return 0;
}
int APS5_VABI sceVoiceResumePort(std::uint32_t portId) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
if (portId > 127) return InvalidPort;
const auto found = voice.ports.find(portId);
if (found == voice.ports.end()) return PortNotFound;
found->second.paused = false;
return 0;
}
int APS5_VABI sceVoiceDisableChat(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return NotInitialized;
Fail(__func__, "audio chat backend is not implemented");
}
int APS5_VABI sceVoiceInitHQ(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceGetMemorySize(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceDeviceInCustomerData(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceQAEnableRecording(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceVADAdjustment(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceVADPVTAdjustment(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceVADSetVersion(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceSetMuteFlagAll(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceSetBitRate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoiceUpdatePort(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceVoicePausePortAll(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return VoiceErrorNotInitialized;
return 0;
}
int APS5_VABI sceVoiceResumePortAll(void) {
auto& voice = State();
std::lock_guard lock(voice.mutex);
if (!voice.initialized) return VoiceErrorNotInitialized;
return 0;
}
}