feat(sceAudioOut): audio out init functions impl
This commit is contained in:
parent
a7521f3a7e
commit
49e200c88a
|
|
@ -6,5 +6,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
|
||||
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_VIDEO OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_RENDER OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_X11 OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_WAYLAND OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_OPENGL OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_OPENGLES OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_VULKAN OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_JOYSTICK OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_HAPTIC OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_HIDAPI OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_SENSOR OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_STATIC_PIC ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(3rdparty/SDL2)
|
||||
|
||||
add_subdirectory(core/libs)
|
||||
add_subdirectory(core/relinker)
|
||||
|
|
|
|||
|
|
@ -8,5 +8,8 @@ add_library(libSceAudioOut SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOut2System.cpp
|
||||
)
|
||||
|
||||
target_include_directories(libSceAudioOut PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(libSceAudioOut PRIVATE libc)
|
||||
target_include_directories(libSceAudioOut PRIVATE
|
||||
${LIBS_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/SDL2/include
|
||||
)
|
||||
target_link_libraries(libSceAudioOut PRIVATE libc libkernel SDL2-static)
|
||||
|
|
|
|||
|
|
@ -1,57 +1,469 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/Time/include/Time.hpp"
|
||||
|
||||
static constexpr int PORT_TYPE_MAIN = 0;
|
||||
static constexpr int PORT_TYPE_BGM = 1;
|
||||
static constexpr int PORT_TYPE_VOICE = 2;
|
||||
static constexpr int PORT_TYPE_PERSONAL = 3;
|
||||
static constexpr int PORT_TYPE_PADSPK = 4;
|
||||
static constexpr int PORT_TYPE_VIBRATION = 10;
|
||||
static constexpr int PORT_TYPE_AUDIO3D = 126;
|
||||
static constexpr int PORT_TYPE_AUX = 127;
|
||||
|
||||
static constexpr int PORTS_MAX = 32;
|
||||
static constexpr int DEFAULT_VOLUME = 32768;
|
||||
static constexpr std::uint32_t FORMAT_MASK = 0xFFu;
|
||||
static constexpr std::uint64_t TARGET_LATENCY_US = 40000;
|
||||
static constexpr std::uint64_t DRAIN_TIMEOUT_US = 200000;
|
||||
static constexpr std::uint64_t DRAIN_SLEEP_US = 1000;
|
||||
|
||||
enum class Format {
|
||||
Unknown,
|
||||
S16Mono,
|
||||
S16Stereo,
|
||||
S16_8Ch,
|
||||
F32Mono,
|
||||
F32Stereo,
|
||||
F32_8Ch,
|
||||
S16_8ChStd,
|
||||
F32_8ChStd,
|
||||
};
|
||||
|
||||
static bool formatIsFloat(Format f) {
|
||||
return f == Format::F32Mono || f == Format::F32Stereo ||
|
||||
f == Format::F32_8Ch || f == Format::F32_8ChStd;
|
||||
}
|
||||
|
||||
static bool formatIsStd(Format f) {
|
||||
return f == Format::S16_8ChStd || f == Format::F32_8ChStd;
|
||||
}
|
||||
|
||||
static int channelsForFormat(Format f) {
|
||||
switch (f) {
|
||||
case Format::S16Mono:
|
||||
case Format::F32Mono:
|
||||
return 1;
|
||||
case Format::S16Stereo:
|
||||
case Format::F32Stereo:
|
||||
return 2;
|
||||
case Format::S16_8Ch:
|
||||
case Format::F32_8Ch:
|
||||
case Format::S16_8ChStd:
|
||||
case Format::F32_8ChStd:
|
||||
return 8;
|
||||
default:
|
||||
throw std::runtime_error("channelsForFormat: unknown format");
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_AudioFormat sdlFormat(Format f) {
|
||||
return formatIsFloat(f) ? AUDIO_F32SYS : AUDIO_S16SYS;
|
||||
}
|
||||
|
||||
static std::uint32_t bytesPerSample(Format f) {
|
||||
return formatIsFloat(f) ? sizeof(float) : sizeof(std::int16_t);
|
||||
}
|
||||
|
||||
struct Port {
|
||||
bool used = false;
|
||||
int type = 0;
|
||||
std::uint32_t samplesNum = 0;
|
||||
std::uint32_t freq = 0;
|
||||
Format format = Format::Unknown;
|
||||
int channels = 0;
|
||||
int volume[8] = {};
|
||||
std::uint64_t lastOutputTime = 0;
|
||||
SDL_AudioDeviceID device = 0;
|
||||
SDL_AudioSpec spec = {};
|
||||
};
|
||||
|
||||
static std::mutex g_mutex;
|
||||
static Port g_ports[PORTS_MAX];
|
||||
static bool g_sdlInitialized = false;
|
||||
|
||||
static bool ensureSdlAudio() {
|
||||
if (g_sdlInitialized) {
|
||||
return true;
|
||||
}
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
return false;
|
||||
}
|
||||
g_sdlInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool openDevice(Port& port) {
|
||||
if (!ensureSdlAudio()) {
|
||||
return false;
|
||||
}
|
||||
SDL_AudioSpec desired{};
|
||||
desired.freq = static_cast<int>(port.freq);
|
||||
desired.format = sdlFormat(port.format);
|
||||
desired.channels = static_cast<Uint8>(port.channels);
|
||||
desired.samples = static_cast<Uint16>(port.samplesNum);
|
||||
desired.callback = nullptr;
|
||||
SDL_AudioSpec obtained{};
|
||||
port.device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
if (port.device == 0) {
|
||||
return false;
|
||||
}
|
||||
port.spec = obtained;
|
||||
SDL_PauseAudioDevice(port.device, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void closeDevice(Port& port) {
|
||||
if (port.device != 0 && SDL_WasInit(SDL_INIT_AUDIO) != 0) {
|
||||
SDL_ClearQueuedAudio(port.device);
|
||||
SDL_CloseAudioDevice(port.device);
|
||||
}
|
||||
port.device = 0;
|
||||
port.spec = {};
|
||||
}
|
||||
|
||||
static constexpr std::uint32_t STD_8CH_MAP[8] = {0, 1, 2, 3, 6, 7, 4, 5};
|
||||
|
||||
static const void* prepareBuffer(const Port& port, const void* data, std::vector<std::uint8_t>& buf) {
|
||||
const auto frames = port.samplesNum;
|
||||
const auto ch = static_cast<std::uint32_t>(port.channels);
|
||||
const auto bps = bytesPerSample(port.format);
|
||||
const auto size = frames * ch * bps;
|
||||
|
||||
bool volumeChanged = false;
|
||||
for (std::uint32_t i = 0; i < ch; i++) {
|
||||
if (port.volume[i] != DEFAULT_VOLUME) {
|
||||
volumeChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!volumeChanged && !formatIsStd(port.format)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
buf.resize(size);
|
||||
const bool isStd = formatIsStd(port.format) && ch == 8;
|
||||
|
||||
if (formatIsFloat(port.format)) {
|
||||
auto* dst = reinterpret_cast<float*>(buf.data());
|
||||
const auto* src = static_cast<const float*>(data);
|
||||
for (std::uint32_t fr = 0; fr < frames; fr++) {
|
||||
for (std::uint32_t c = 0; c < ch; c++) {
|
||||
const auto srcCh = isStd ? STD_8CH_MAP[c] : c;
|
||||
dst[fr * ch + c] = src[fr * ch + srcCh] *
|
||||
(static_cast<float>(port.volume[c]) / static_cast<float>(DEFAULT_VOLUME));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto* dst = reinterpret_cast<std::int16_t*>(buf.data());
|
||||
const auto* src = static_cast<const std::int16_t*>(data);
|
||||
for (std::uint32_t fr = 0; fr < frames; fr++) {
|
||||
for (std::uint32_t c = 0; c < ch; c++) {
|
||||
const auto srcCh = isStd ? STD_8CH_MAP[c] : c;
|
||||
std::int64_t s = static_cast<std::int64_t>(src[fr * ch + srcCh]) *
|
||||
port.volume[c] / DEFAULT_VOLUME;
|
||||
s = std::clamp(s,
|
||||
static_cast<std::int64_t>(std::numeric_limits<std::int16_t>::min()),
|
||||
static_cast<std::int64_t>(std::numeric_limits<std::int16_t>::max()));
|
||||
dst[fr * ch + c] = static_cast<std::int16_t>(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
static void queueAudio(Port& port, const void* data) {
|
||||
if (port.device == 0 || data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> prepareBuf;
|
||||
const void* prepared = prepareBuffer(port, data, prepareBuf);
|
||||
const std::uint32_t preparedSize = port.samplesNum *
|
||||
static_cast<std::uint32_t>(port.channels) * bytesPerSample(port.format);
|
||||
|
||||
SDL_AudioCVT cvt{};
|
||||
const int cvtResult = SDL_BuildAudioCVT(
|
||||
&cvt,
|
||||
sdlFormat(port.format), static_cast<Uint8>(port.channels), static_cast<int>(port.freq),
|
||||
port.spec.format, port.spec.channels, port.spec.freq);
|
||||
|
||||
if (cvtResult < 0) {
|
||||
throw std::runtime_error(std::string("SDL_BuildAudioCVT: ") + SDL_GetError());
|
||||
}
|
||||
|
||||
const void* queueData = prepared;
|
||||
std::uint32_t queueSize = preparedSize;
|
||||
std::vector<std::uint8_t> convertBuf;
|
||||
|
||||
if (cvtResult > 0) {
|
||||
convertBuf.resize(static_cast<std::size_t>(preparedSize) * cvt.len_mult);
|
||||
std::memcpy(convertBuf.data(), prepared, preparedSize);
|
||||
cvt.buf = convertBuf.data();
|
||||
cvt.len = static_cast<int>(preparedSize);
|
||||
if (SDL_ConvertAudio(&cvt) < 0) {
|
||||
throw std::runtime_error(std::string("SDL_ConvertAudio: ") + SDL_GetError());
|
||||
}
|
||||
queueData = cvt.buf;
|
||||
queueSize = static_cast<std::uint32_t>(cvt.len_cvt);
|
||||
}
|
||||
|
||||
const std::uint64_t bufferUs = port.freq != 0
|
||||
? (1000000ULL * port.samplesNum) / port.freq
|
||||
: 0;
|
||||
const std::uint32_t buffers = bufferUs != 0
|
||||
? static_cast<std::uint32_t>((TARGET_LATENCY_US + bufferUs - 1) / bufferUs)
|
||||
: 2u;
|
||||
const std::uint32_t minQueued = queueSize * std::clamp(buffers, 2u, 16u);
|
||||
const std::uint64_t waitStart = sceKernelGetProcessTime();
|
||||
|
||||
while (SDL_GetQueuedAudioSize(port.device) > minQueued) {
|
||||
if (sceKernelGetProcessTime() - waitStart > DRAIN_TIMEOUT_US) {
|
||||
SDL_ClearQueuedAudio(port.device);
|
||||
break;
|
||||
}
|
||||
struct timespec req{};
|
||||
req.tv_sec = 0;
|
||||
req.tv_nsec = static_cast<long>(DRAIN_SLEEP_US * 1000ULL);
|
||||
nanosleep(&req, nullptr);
|
||||
}
|
||||
|
||||
if (SDL_QueueAudio(port.device, queueData, queueSize) < 0) {
|
||||
throw std::runtime_error(std::string("SDL_QueueAudio: ") + SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
static bool portTypeValid(int type) {
|
||||
return (type >= PORT_TYPE_MAIN && type <= PORT_TYPE_PADSPK) ||
|
||||
type == PORT_TYPE_VIBRATION ||
|
||||
type == PORT_TYPE_AUDIO3D ||
|
||||
type == PORT_TYPE_AUX;
|
||||
}
|
||||
|
||||
static Port* getPort(int handle) {
|
||||
const int idx = handle - 1;
|
||||
if (idx < 0 || idx >= PORTS_MAX || !g_ports[idx].used) {
|
||||
return nullptr;
|
||||
}
|
||||
return &g_ports[idx];
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceAudioOutInit(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceAudioOutInit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutOpen(int user_id, int type, int index, uint32_t len, uint32_t freq, uint32_t param) {
|
||||
(void)user_id;
|
||||
(void)type;
|
||||
(void)index;
|
||||
(void)len;
|
||||
(void)freq;
|
||||
(void)param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAudioOutOpen(int userId, int type, int index, std::uint32_t len,
|
||||
std::uint32_t freq, std::uint32_t param) {
|
||||
(void)userId;
|
||||
if (!portTypeValid(type)) {
|
||||
return -2144993270;
|
||||
}
|
||||
if (index != 0) {
|
||||
throw std::runtime_error("sceAudioOutOpen: index != 0 not supported");
|
||||
}
|
||||
|
||||
Format format = Format::Unknown;
|
||||
switch (param & FORMAT_MASK) {
|
||||
case 0: format = Format::S16Mono; break;
|
||||
case 1: format = Format::S16Stereo; break;
|
||||
case 2: format = Format::S16_8Ch; break;
|
||||
case 3: format = Format::F32Mono; break;
|
||||
case 4: format = Format::F32Stereo; break;
|
||||
case 5: format = Format::F32_8Ch; break;
|
||||
case 6: format = Format::S16_8ChStd; break;
|
||||
case 7: format = Format::F32_8ChStd; break;
|
||||
default:
|
||||
throw std::runtime_error("sceAudioOutOpen: unknown format param");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
for (int i = 0; i < PORTS_MAX; i++) {
|
||||
if (!g_ports[i].used) {
|
||||
Port& port = g_ports[i];
|
||||
port.used = true;
|
||||
port.type = type;
|
||||
port.samplesNum = len;
|
||||
port.freq = freq;
|
||||
port.format = format;
|
||||
port.channels = channelsForFormat(format);
|
||||
port.lastOutputTime = 0;
|
||||
for (int c = 0; c < port.channels; c++) {
|
||||
port.volume[c] = DEFAULT_VOLUME;
|
||||
}
|
||||
if (type != PORT_TYPE_VIBRATION) {
|
||||
openDevice(port);
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return -2144993275;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutClose(int handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
Port* port = getPort(handle);
|
||||
if (port == nullptr) {
|
||||
return -2144993277;
|
||||
}
|
||||
closeDevice(*port);
|
||||
*port = Port{};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutOutput(int handle, const void* ptr) {
|
||||
(void)handle;
|
||||
(void)ptr;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
Port* port = getPort(handle);
|
||||
if (port == nullptr) {
|
||||
return -2144993277;
|
||||
}
|
||||
|
||||
const std::uint64_t blockUs = (1000000ULL * port->samplesNum) / port->freq;
|
||||
const std::uint64_t now = sceKernelGetProcessTime();
|
||||
const std::uint64_t next = port->lastOutputTime + blockUs;
|
||||
if (next > now && port->device == 0) {
|
||||
const std::uint64_t waitUs = next - now;
|
||||
struct timespec req{};
|
||||
req.tv_sec = static_cast<time_t>(waitUs / 1000000ULL);
|
||||
req.tv_nsec = static_cast<long>((waitUs % 1000000ULL) * 1000ULL);
|
||||
nanosleep(&req, nullptr);
|
||||
}
|
||||
|
||||
queueAudio(*port, ptr);
|
||||
port->lastOutputTime = sceKernelGetProcessTime();
|
||||
return static_cast<int>(port->samplesNum);
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutOutputs(AudioOutOutputParam* param, uint32_t num) {
|
||||
(void)param;
|
||||
(void)num;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAudioOutOutputs(AudioOutOutputParam* param, std::uint32_t num) {
|
||||
if (param == nullptr || num == 0) {
|
||||
return -2144993276;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
|
||||
for (std::uint32_t i = 0; i < num; i++) {
|
||||
if (getPort(param[i].handle) == nullptr) {
|
||||
return -2144993277;
|
||||
}
|
||||
}
|
||||
|
||||
Port& first = *getPort(param[0].handle);
|
||||
const std::uint64_t blockUs = (1000000ULL * first.samplesNum) / first.freq;
|
||||
const std::uint64_t now = sceKernelGetProcessTime();
|
||||
|
||||
std::uint64_t maxWait = 0;
|
||||
for (std::uint32_t i = 0; i < num; i++) {
|
||||
Port& p = *getPort(param[i].handle);
|
||||
const std::uint64_t next = p.lastOutputTime + blockUs;
|
||||
const std::uint64_t wait = next > now ? next - now : 0;
|
||||
if (wait > maxWait) {
|
||||
maxWait = wait;
|
||||
}
|
||||
}
|
||||
|
||||
bool anyDevice = false;
|
||||
for (std::uint32_t i = 0; i < num; i++) {
|
||||
if (getPort(param[i].handle)->device != 0) {
|
||||
anyDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxWait != 0 && !anyDevice) {
|
||||
struct timespec req{};
|
||||
req.tv_sec = static_cast<time_t>(maxWait / 1000000ULL);
|
||||
req.tv_nsec = static_cast<long>((maxWait % 1000000ULL) * 1000ULL);
|
||||
nanosleep(&req, nullptr);
|
||||
}
|
||||
|
||||
for (std::uint32_t i = 0; i < num; i++) {
|
||||
queueAudio(*getPort(param[i].handle), param[i].ptr);
|
||||
}
|
||||
|
||||
const std::uint64_t done = sceKernelGetProcessTime();
|
||||
for (std::uint32_t i = 0; i < num; i++) {
|
||||
getPort(param[i].handle)->lastOutputTime = done;
|
||||
}
|
||||
|
||||
return static_cast<int>(first.samplesNum);
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutSetVolume(int handle, uint32_t flag, int* vol) {
|
||||
(void)handle;
|
||||
(void)flag;
|
||||
(void)vol;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceAudioOutSetVolume(int handle, std::uint32_t flag, int* vol) {
|
||||
if (vol == nullptr) {
|
||||
return -2144993276;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
Port* port = getPort(handle);
|
||||
if (port == nullptr) {
|
||||
return -2144993277;
|
||||
}
|
||||
const bool isStd = formatIsStd(port->format);
|
||||
for (int i = 0; i < port->channels; i++, flag >>= 1u) {
|
||||
if ((flag & 1u) == 0) {
|
||||
continue;
|
||||
}
|
||||
int srcIdx = i;
|
||||
if (isStd) {
|
||||
if (i == 4) srcIdx = 6;
|
||||
else if (i == 5) srcIdx = 7;
|
||||
else if (i == 6) srcIdx = 4;
|
||||
else if (i == 7) srcIdx = 5;
|
||||
}
|
||||
port->volume[i] = vol[srcIdx];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAudioOutGetPortState(int handle, AudioOutPortState* state) {
|
||||
(void)handle;
|
||||
(void)state;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
if (state == nullptr) {
|
||||
return -2144993276;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
Port* port = getPort(handle);
|
||||
if (port == nullptr) {
|
||||
return -2144993277;
|
||||
}
|
||||
state->rerouteCounter = 0;
|
||||
state->volume = 127;
|
||||
state->flag = 0;
|
||||
state->activeState = 0;
|
||||
state->reserved[0] = 0;
|
||||
switch (port->type) {
|
||||
case PORT_TYPE_MAIN:
|
||||
case PORT_TYPE_BGM:
|
||||
case PORT_TYPE_AUDIO3D:
|
||||
state->output = 1;
|
||||
state->channel = static_cast<std::uint8_t>(port->channels > 2 ? 2 : port->channels);
|
||||
break;
|
||||
case PORT_TYPE_VOICE:
|
||||
case PORT_TYPE_PERSONAL:
|
||||
state->output = 0x40;
|
||||
state->channel = 1;
|
||||
break;
|
||||
case PORT_TYPE_PADSPK:
|
||||
case PORT_TYPE_VIBRATION:
|
||||
state->output = 4;
|
||||
state->channel = 1;
|
||||
break;
|
||||
case PORT_TYPE_AUX:
|
||||
state->output = 0x80;
|
||||
state->channel = 0;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("sceAudioOutGetPortState: unknown port type");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue