mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-21 03:55:32 +00:00
cellVoice: Initial Implementation
Implement system service events, error checking and some functions.
This commit is contained in:
parent
fa73175754
commit
6c2307fb96
2 changed files with 549 additions and 68 deletions
|
@ -3,6 +3,7 @@
|
|||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "cellVoice.h"
|
||||
|
||||
LOG_CHANNEL(cellVoice);
|
||||
|
@ -38,46 +39,164 @@ void fmt_class_string<CellVoiceError>::format(std::string& out, u64 arg)
|
|||
});
|
||||
}
|
||||
|
||||
void voice_manager::reset()
|
||||
{
|
||||
id_ctr = 0;
|
||||
ports.clear();
|
||||
queue_keys.clear();
|
||||
}
|
||||
|
||||
error_code cellVoiceConnectIPortToOPort(u32 ips, u32 ops)
|
||||
{
|
||||
cellVoice.todo("cellVoiceConnectIPortToOPort(ips=%d, ops=%d)", ips, ops);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto iport = manager->access_port(ips);
|
||||
|
||||
if (!iport || iport->info.portType >= CELLVOICE_PORTTYPE_OUT_PCMAUDIO)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
auto oport = manager->access_port(ops);
|
||||
|
||||
if (!oport || oport->info.portType <= CELLVOICE_PORTTYPE_IN_VOICE)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceCreateNotifyEventQueue(vm::ptr<u32> id, vm::ptr<u64> key)
|
||||
{
|
||||
cellVoice.todo("cellVoiceCreateNotifyEventQueue(id=*0x%x, key=*0x%x)", id, key);
|
||||
cellVoice.warning("cellVoiceCreateNotifyEventQueue(id=*0x%x, key=*0x%x)", id, key);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
vm::var<sys_event_queue_attribute_t> attr;
|
||||
attr->protocol = SYS_SYNC_FIFO;
|
||||
attr->type = SYS_PPU_QUEUE;
|
||||
attr->name_u64 = 0;
|
||||
|
||||
for (u64 i = 0; i < 10; i++)
|
||||
{
|
||||
// Create an event queue "bruteforcing" an available key
|
||||
const u64 key_value = 0x80004d494f323285ull + i;
|
||||
if (const s32 res = sys_event_queue_create(id, attr, key_value, 0x40))
|
||||
{
|
||||
if (res != CELL_EEXIST)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*key = key_value;
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_VOICE_ERROR_EVENT_QUEUE;
|
||||
}
|
||||
|
||||
error_code cellVoiceCreatePort(vm::ptr<u32> portId, vm::cptr<CellVoicePortParam> pArg)
|
||||
{
|
||||
cellVoice.todo("cellVoiceCreatePort(portId=*0x%x, pArg=*0x%x)", portId, pArg);
|
||||
cellVoice.warning("cellVoiceCreatePort(portId=*0x%x, pArg=*0x%x)", portId, pArg);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (!pArg)
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
|
||||
switch (pArg->portType)
|
||||
{
|
||||
case CELLVOICE_PORTTYPE_IN_PCMAUDIO:
|
||||
case CELLVOICE_PORTTYPE_OUT_PCMAUDIO:
|
||||
{
|
||||
if (pArg->pcmaudio.format.dataType > CELLVOICE_PCM_INTEGER_LITTLE_ENDIAN)
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
|
||||
break;
|
||||
}
|
||||
case CELLVOICE_PORTTYPE_IN_VOICE:
|
||||
case CELLVOICE_PORTTYPE_OUT_VOICE:
|
||||
{
|
||||
// Must be an exact value
|
||||
switch (auto bitrate = pArg->voice.bitrate)
|
||||
{
|
||||
case CELLVOICE_BITRATE_3850:
|
||||
case CELLVOICE_BITRATE_4650:
|
||||
case CELLVOICE_BITRATE_5700:
|
||||
case CELLVOICE_BITRATE_7300:
|
||||
case CELLVOICE_BITRATE_14400:
|
||||
case CELLVOICE_BITRATE_16000:
|
||||
case CELLVOICE_BITRATE_22533:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
case CELLVOICE_PORTTYPE_IN_MIC:
|
||||
case CELLVOICE_PORTTYPE_OUT_SECONDARY:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (manager->ports.size() > CELLVOICE_MAX_PORT)
|
||||
return CELL_VOICE_ERROR_RESOURCE_INSUFFICIENT;
|
||||
|
||||
// Id: bits [8,15] seem to contain a "random" value
|
||||
// bits [0,7] are based on creation counter modulo 0xa0
|
||||
// The rest are set to zero and ignored.
|
||||
manager->id_ctr = (manager->id_ctr + 1) % 0xa0;
|
||||
|
||||
auto port = manager->ports.begin();
|
||||
bool success = false;
|
||||
|
||||
// It isn't known whether bits[8,15] are guaranteed to be non-zero
|
||||
for (u8 ctr2 = 1; !success; ctr2++)
|
||||
{
|
||||
verify(HERE), ctr2 < CELLVOICE_MAX_PORT + 1;
|
||||
|
||||
std::tie(port, success) = manager->ports.try_emplace((ctr2 << 8) | manager->id_ctr);
|
||||
}
|
||||
|
||||
port->second.info = *pArg;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceDeletePort(u32 portId)
|
||||
{
|
||||
cellVoice.todo("cellVoiceDeletePort(portId=%d)", portId);
|
||||
cellVoice.warning("cellVoiceDeletePort(portId=%d)", portId);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (!manager->ports.erase((u16)portId) == 0)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -85,21 +204,52 @@ error_code cellVoiceDisconnectIPortFromOPort(u32 ips, u32 ops)
|
|||
{
|
||||
cellVoice.todo("cellVoiceDisconnectIPortFromOPort(ips=%d, ops=%d)", ips, ops);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto iport = manager->access_port(ips);
|
||||
|
||||
if (!iport || iport->info.portType >= CELLVOICE_PORTTYPE_OUT_PCMAUDIO)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
auto oport = manager->access_port(ops);
|
||||
|
||||
if (!oport || oport->info.portType <= CELLVOICE_PORTTYPE_IN_VOICE)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceEnd()
|
||||
{
|
||||
cellVoice.todo("cellVoiceEnd()");
|
||||
cellVoice.warning("cellVoiceEnd()");
|
||||
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (std::exchange(manager->voice_service_started, false))
|
||||
{
|
||||
for (auto& key_pair : manager->queue_keys)
|
||||
{
|
||||
if (auto queue = lv2_event_queue::find(key_pair.first))
|
||||
{
|
||||
for (const auto& source : key_pair.second)
|
||||
{
|
||||
queue->send(source, CELLVOICE_EVENT_SERVICE_DETACHED, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manager->reset();
|
||||
manager->is_init = false;
|
||||
|
||||
return CELL_OK;
|
||||
|
@ -107,21 +257,46 @@ error_code cellVoiceEnd()
|
|||
|
||||
error_code cellVoiceGetBitRate(u32 portId, vm::ptr<u32> bitrate)
|
||||
{
|
||||
cellVoice.todo("cellVoiceGetBitRate(portId=%d, bitrate=*0x%x)", portId, bitrate);
|
||||
cellVoice.warning("cellVoiceGetBitRate(portId=%d, bitrate=*0x%x)", portId, bitrate);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (!manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
// No nullptr check!
|
||||
|
||||
// Constant value for errors (meaning unknown)
|
||||
*bitrate = 0x4f323285;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port || (port->info.portType != CELLVOICE_PORTTYPE_IN_VOICE && port->info.portType != CELLVOICE_PORTTYPE_OUT_VOICE))
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
*bitrate = port->info.voice.bitrate;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceGetMuteFlag(u32 portId, vm::ptr<u16> bMuted)
|
||||
{
|
||||
cellVoice.todo("cellVoiceGetMuteFlag(portId=%d, bMuted=*0x%x)", portId, bMuted);
|
||||
cellVoice.warning("cellVoiceGetMuteFlag(portId=%d, bMuted=*0x%x)", portId, bMuted);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
*bMuted = port->info.bMute;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -129,19 +304,46 @@ error_code cellVoiceGetPortAttr(u32 portId, u32 attr, vm::ptr<void> attrValue)
|
|||
{
|
||||
cellVoice.todo("cellVoiceGetPortAttr(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
// Report detached microphone
|
||||
return not_an_error(CELL_VOICE_ERROR_DEVICE_NOT_PRESENT);
|
||||
}
|
||||
|
||||
error_code cellVoiceGetPortInfo(u32 portId, vm::ptr<CellVoiceBasePortInfo> pInfo)
|
||||
{
|
||||
cellVoice.todo("cellVoiceGetPortInfo(portId=%d, pInfo=*0x%x)", portId, pInfo);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
if (!manager->voice_service_started)
|
||||
return CELL_VOICE_ERROR_SERVICE_DETACHED;
|
||||
|
||||
// No nullptr check!
|
||||
pInfo->portType = port->info.portType;
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -149,19 +351,44 @@ error_code cellVoiceGetSignalState(u32 portId, u32 attr, vm::ptr<void> attrValue
|
|||
{
|
||||
cellVoice.todo("cellVoiceGetSignalState(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
// Report detached microphone
|
||||
return not_an_error(CELL_VOICE_ERROR_DEVICE_NOT_PRESENT);
|
||||
}
|
||||
|
||||
error_code cellVoiceGetVolume(u32 portId, vm::ptr<f32> volume)
|
||||
{
|
||||
cellVoice.todo("cellVoiceGetVolume(portId=%d, volume=*0x%x)", portId, volume);
|
||||
cellVoice.warning("cellVoiceGetVolume(portId=%d, volume=*0x%x)", portId, volume);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
// No nullptr check!
|
||||
|
||||
// Constant value for errors (meaning unknown)
|
||||
*volume = std::bit_cast<f32, s32>(0x4f323285);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
*volume = port->info.volume;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -171,6 +398,8 @@ error_code cellVoiceInit(vm::ptr<CellVoiceInitParam> pArg)
|
|||
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_INITIALIZED;
|
||||
|
||||
|
@ -203,9 +432,18 @@ error_code cellVoicePausePort(u32 portId)
|
|||
{
|
||||
cellVoice.todo("cellVoicePausePort(portId=%d)", portId);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -213,7 +451,11 @@ error_code cellVoicePausePortAll()
|
|||
{
|
||||
cellVoice.todo("cellVoicePausePortAll()");
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
|
@ -221,11 +463,18 @@ error_code cellVoicePausePortAll()
|
|||
|
||||
error_code cellVoiceRemoveNotifyEventQueue(u64 key)
|
||||
{
|
||||
cellVoice.todo("cellVoiceRemoveNotifyEventQueue(key=0x%llx)", key);
|
||||
cellVoice.warning("cellVoiceRemoveNotifyEventQueue(key=0x%llx)", key);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (manager->queue_keys.erase(key) == 0)
|
||||
return CELL_VOICE_ERROR_EVENT_QUEUE;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -233,9 +482,18 @@ error_code cellVoiceResetPort(u32 portId)
|
|||
{
|
||||
cellVoice.todo("cellVoiceResetPort(portId=%d)", portId);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -243,9 +501,18 @@ error_code cellVoiceResumePort(u32 portId)
|
|||
{
|
||||
cellVoice.todo("cellVoiceResumePort(portId=%d)", portId);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -253,49 +520,111 @@ error_code cellVoiceResumePortAll()
|
|||
{
|
||||
cellVoice.todo("cellVoiceResumePortAll()");
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceSetBitRate(u32 portId, s32 bitrate)
|
||||
error_code cellVoiceSetBitRate(u32 portId, CellVoiceBitRate bitrate)
|
||||
{
|
||||
cellVoice.todo("cellVoiceSetBitRate(portId=%d, bitrate=%d)", portId, bitrate);
|
||||
cellVoice.warning("cellVoiceSetBitRate(portId=%d, bitrate=%d)", portId, +bitrate);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port || (port->info.portType != CELLVOICE_PORTTYPE_IN_VOICE && port->info.portType != CELLVOICE_PORTTYPE_OUT_VOICE))
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
// TODO: Check ordering of checks.
|
||||
switch (bitrate)
|
||||
{
|
||||
case CELLVOICE_BITRATE_3850:
|
||||
case CELLVOICE_BITRATE_4650:
|
||||
case CELLVOICE_BITRATE_5700:
|
||||
case CELLVOICE_BITRATE_7300:
|
||||
case CELLVOICE_BITRATE_14400:
|
||||
case CELLVOICE_BITRATE_16000:
|
||||
case CELLVOICE_BITRATE_22533:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
port->info.voice.bitrate = bitrate;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceSetMuteFlag(u32 portId, u16 bMuted)
|
||||
{
|
||||
cellVoice.todo("cellVoiceSetMuteFlag(portId=%d, bMuted=%d)", portId, bMuted);
|
||||
cellVoice.warning("cellVoiceSetMuteFlag(portId=%d, bMuted=%d)", portId, bMuted);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
port->info.bMute = bMuted;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceSetMuteFlagAll(u16 bMuted)
|
||||
{
|
||||
cellVoice.todo("cellVoiceSetMuteFlagAll(bMuted=%d)", bMuted);
|
||||
cellVoice.warning("cellVoiceSetMuteFlagAll(bMuted=%d)", bMuted);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
// Doesn't change port->bMute value
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceSetNotifyEventQueue(u64 key, u64 source)
|
||||
{
|
||||
cellVoice.todo("cellVoiceSetNotifyEventQueue(key=0x%llx, source=%d)", key, source);
|
||||
cellVoice.warning("cellVoiceSetNotifyEventQueue(key=0x%llx, source=0x%llx)", key, source);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
// Note: it is allowed to enqueue the key twice (another source is enqueued with FIFO ordering)
|
||||
// It is not allowed to enqueue an invalid key
|
||||
|
||||
if (!lv2_event_queue::find(key))
|
||||
return CELL_VOICE_ERROR_EVENT_QUEUE;
|
||||
|
||||
if (!source)
|
||||
{
|
||||
// TODO: same thing as sys_event_port_send with port.name == 0
|
||||
}
|
||||
|
||||
manager->queue_keys[key].push_back(source);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -303,65 +632,150 @@ error_code cellVoiceSetPortAttr(u32 portId, u32 attr, vm::ptr<void> attrValue)
|
|||
{
|
||||
cellVoice.todo("cellVoiceSetPortAttr(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
// Report detached microphone
|
||||
return not_an_error(CELL_VOICE_ERROR_DEVICE_NOT_PRESENT);
|
||||
}
|
||||
|
||||
error_code cellVoiceSetVolume(u32 portId, f32 volume)
|
||||
{
|
||||
cellVoice.todo("cellVoiceSetVolume(portId=%d, volume=%f)", portId, volume);
|
||||
cellVoice.warning("cellVoiceSetVolume(portId=%d, volume=%f)", portId, volume);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code VoiceStart(voice_manager* manager)
|
||||
{
|
||||
if (std::exchange(manager->voice_service_started, true))
|
||||
return CELL_OK;
|
||||
|
||||
for (auto& key_pair : manager->queue_keys)
|
||||
{
|
||||
if (auto queue = lv2_event_queue::find(key_pair.first))
|
||||
{
|
||||
for (const auto& source : key_pair.second)
|
||||
{
|
||||
queue->send(source, CELLVOICE_EVENT_SERVICE_ATTACHED, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceStart()
|
||||
{
|
||||
cellVoice.todo("cellVoiceStart()");
|
||||
cellVoice.warning("cellVoiceStart()");
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
return CELL_OK;
|
||||
return VoiceStart(manager);
|
||||
}
|
||||
|
||||
error_code cellVoiceStartEx(vm::ptr<CellVoiceStartParam> pArg)
|
||||
{
|
||||
cellVoice.todo("cellVoiceStartEx(pArg=*0x%x)", pArg);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (!pArg)
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
|
||||
return CELL_OK;
|
||||
// TODO: Check provided memory container
|
||||
|
||||
return VoiceStart(manager);
|
||||
}
|
||||
|
||||
error_code cellVoiceStop()
|
||||
{
|
||||
cellVoice.todo("cellVoiceStop()");
|
||||
cellVoice.warning("cellVoiceStop()");
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (!std::exchange(manager->voice_service_started, false))
|
||||
return CELL_OK;
|
||||
|
||||
for (auto& key_pair : manager->queue_keys)
|
||||
{
|
||||
if (auto queue = lv2_event_queue::find(key_pair.first))
|
||||
{
|
||||
for (const auto& source : key_pair.second)
|
||||
{
|
||||
queue->send(source, CELLVOICE_EVENT_SERVICE_DETACHED, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVoiceUpdatePort(u32 portId, vm::cptr<CellVoicePortParam> pArg)
|
||||
{
|
||||
cellVoice.todo("cellVoiceUpdatePort(portId=%d, pArg=*0x%x)", portId, pArg);
|
||||
cellVoice.warning("cellVoiceUpdatePort(portId=%d, pArg=*0x%x)", portId, pArg);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
if (!pArg)
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
|
||||
auto port = manager->access_port(portId);
|
||||
|
||||
if (!port)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
// Not all info is updated
|
||||
port->info.bMute = pArg->bMute;
|
||||
port->info.volume = pArg->volume;
|
||||
port->info.threshold = pArg->threshold;
|
||||
|
||||
if (port->info.portType == CELLVOICE_PORTTYPE_IN_VOICE || port->info.portType == CELLVOICE_PORTTYPE_OUT_VOICE)
|
||||
{
|
||||
port->info.voice.bitrate = pArg->voice.bitrate;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -369,8 +783,17 @@ error_code cellVoiceWriteToIPort(u32 ips, vm::cptr<void> data, vm::ptr<u32> size
|
|||
{
|
||||
cellVoice.todo("cellVoiceWriteToIPort(ips=%d, data=*0x%x, size=*0x%x)", ips, data, size);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto iport = manager->access_port(ips);
|
||||
|
||||
if (!iport || iport->info.portType >= CELLVOICE_PORTTYPE_OUT_PCMAUDIO)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
@ -379,9 +802,18 @@ error_code cellVoiceWriteToIPortEx(u32 ips, vm::cptr<void> data, vm::ptr<u32> si
|
|||
{
|
||||
cellVoice.todo("cellVoiceWriteToIPortEx(ips=%d, data=*0x%x, size=*0x%x, numFrameLost=%d)", ips, data, size, numFrameLost);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto iport = manager->access_port(ips);
|
||||
|
||||
if (!iport || iport->info.portType >= CELLVOICE_PORTTYPE_OUT_PCMAUDIO)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -389,9 +821,18 @@ error_code cellVoiceWriteToIPortEx2(u32 ips, vm::cptr<void> data, vm::ptr<u32> s
|
|||
{
|
||||
cellVoice.todo("cellVoiceWriteToIPortEx2(ips=%d, data=*0x%x, size=*0x%x, frameGaps=%d)", ips, data, size, frameGaps);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto iport = manager->access_port(ips);
|
||||
|
||||
if (!iport || iport->info.portType >= CELLVOICE_PORTTYPE_OUT_PCMAUDIO)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -399,9 +840,18 @@ error_code cellVoiceReadFromOPort(u32 ops, vm::ptr<void> data, vm::ptr<u32> size
|
|||
{
|
||||
cellVoice.todo("cellVoiceReadFromOPort(ops=%d, data=*0x%x, size=*0x%x)", ops, data, size);
|
||||
|
||||
if (!g_fxo->get<voice_manager>()->is_init)
|
||||
const auto manager = g_fxo->get<voice_manager>();
|
||||
|
||||
std::scoped_lock lock(manager->mtx);
|
||||
|
||||
if (manager->is_init)
|
||||
return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT;
|
||||
|
||||
auto oport = manager->access_port(ops);
|
||||
|
||||
if (!oport || oport->info.portType <= CELLVOICE_PORTTYPE_IN_VOICE)
|
||||
return CELL_VOICE_ERROR_TOPOLOGY;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <deque>
|
||||
|
||||
// libvoice = 0x80310801 - 0x803108ff
|
||||
// libvoice version 100
|
||||
|
||||
|
@ -26,14 +29,14 @@ enum CellVoiceError : u32
|
|||
CELL_VOICE_ERROR_TOPOLOGY = 0x80310807,
|
||||
};
|
||||
|
||||
enum CellVoiceAppType
|
||||
enum CellVoiceAppType : u32
|
||||
{
|
||||
CELLVOICE_APPTYPE_GAME_1MB = 1 << 29
|
||||
};
|
||||
|
||||
enum CellVoiceBitRate
|
||||
enum CellVoiceBitRate : u32
|
||||
{
|
||||
CELLVOICE_BITRATE_NULL = -1,
|
||||
CELLVOICE_BITRATE_NULL = ~0u,
|
||||
CELLVOICE_BITRATE_3850 = 3850,
|
||||
CELLVOICE_BITRATE_4650 = 4650,
|
||||
CELLVOICE_BITRATE_5700 = 5700,
|
||||
|
@ -43,7 +46,7 @@ enum CellVoiceBitRate
|
|||
CELLVOICE_BITRATE_22533 = 22533,
|
||||
};
|
||||
|
||||
enum CellVoiceEventType
|
||||
enum CellVoiceEventType : u32
|
||||
{
|
||||
CELLVOICE_EVENT_DATA_ERROR = 1 << 0,
|
||||
CELLVOICE_EVENT_PORT_ATTACHED = 1 << 1,
|
||||
|
@ -54,9 +57,9 @@ enum CellVoiceEventType
|
|||
CELLVOICE_EVENT_PORT_WEAK_DETACHED = 1 << 6,
|
||||
};
|
||||
|
||||
enum CellVoicePcmDataType
|
||||
enum CellVoicePcmDataType : u32
|
||||
{
|
||||
CELLVOICE_PCM_NULL = -1,
|
||||
CELLVOICE_PCM_NULL = ~0u,
|
||||
CELLVOICE_PCM_FLOAT = 0,
|
||||
CELLVOICE_PCM_FLOAT_LITTLE_ENDIAN = 1,
|
||||
CELLVOICE_PCM_SHORT = 2,
|
||||
|
@ -65,7 +68,7 @@ enum CellVoicePcmDataType
|
|||
CELLVOICE_PCM_INTEGER_LITTLE_ENDIAN = 5,
|
||||
};
|
||||
|
||||
enum CellVoicePortAttr
|
||||
enum CellVoicePortAttr : u32
|
||||
{
|
||||
CELLVOICE_ATTR_ENERGY_LEVEL = 1000,
|
||||
CELLVOICE_ATTR_VAD = 1001,
|
||||
|
@ -75,18 +78,18 @@ enum CellVoicePortAttr
|
|||
CELLVOICE_ATTR_SILENCE_THRESHOLD = 1005,
|
||||
};
|
||||
|
||||
enum CellVoicePortState
|
||||
enum CellVoicePortState : u32
|
||||
{
|
||||
CELLVOICE_PORTSTATE_NULL = -1,
|
||||
CELLVOICE_PORTSTATE_NULL = ~0u,
|
||||
CELLVOICE_PORTSTATE_IDLE = 0,
|
||||
CELLVOICE_PORTSTATE_READY = 1,
|
||||
CELLVOICE_PORTSTATE_BUFFERING = 2,
|
||||
CELLVOICE_PORTSTATE_RUNNING = 3,
|
||||
};
|
||||
|
||||
enum CellVoicePortType
|
||||
enum CellVoicePortType : u32
|
||||
{
|
||||
CELLVOICE_PORTTYPE_NULL = -1,
|
||||
CELLVOICE_PORTTYPE_NULL = ~0u,
|
||||
CELLVOICE_PORTTYPE_IN_MIC = 0,
|
||||
CELLVOICE_PORTTYPE_IN_PCMAUDIO = 1,
|
||||
CELLVOICE_PORTTYPE_IN_VOICE = 2,
|
||||
|
@ -95,13 +98,13 @@ enum CellVoicePortType
|
|||
CELLVOICE_PORTTYPE_OUT_SECONDARY = 5,
|
||||
};
|
||||
|
||||
enum CellVoiceSamplingRate
|
||||
enum CellVoiceSamplingRate : u32
|
||||
{
|
||||
CELLVOICE_SAMPLINGRATE_NULL = -1,
|
||||
CELLVOICE_SAMPLINGRATE_NULL = ~0u,
|
||||
CELLVOICE_SAMPLINGRATE_16000 = 16000,
|
||||
};
|
||||
|
||||
enum CellVoiceVersionCheck
|
||||
enum CellVoiceVersionCheck : u32
|
||||
{
|
||||
CELLVOICE_VERSION_100 = 100
|
||||
};
|
||||
|
@ -175,5 +178,33 @@ struct CellVoiceStartParam // aligned(32)
|
|||
|
||||
struct voice_manager
|
||||
{
|
||||
struct port_t
|
||||
{
|
||||
s32 state = CELLVOICE_PORTSTATE_NULL;
|
||||
CellVoicePortParam info;
|
||||
};
|
||||
|
||||
// See cellVoiceCreatePort
|
||||
u32 id_ctr = 0;
|
||||
|
||||
std::unordered_map<u16, port_t> ports;
|
||||
std::unordered_map<u64, std::deque<u64>> queue_keys;
|
||||
bool voice_service_started = false;
|
||||
|
||||
port_t* access_port(u32 id)
|
||||
{
|
||||
// Upper 16 bits are ignored
|
||||
auto pos = ports.find((u16)id);
|
||||
|
||||
if (pos == ports.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &pos->second;
|
||||
}
|
||||
|
||||
void reset();
|
||||
shared_mutex mtx;
|
||||
atomic_t<bool> is_init{ false };
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue