diff --git a/rpcs3/Emu/Cell/Modules/cellVoice.cpp b/rpcs3/Emu/Cell/Modules/cellVoice.cpp index eb55297194..002cf4c44c 100644 --- a/rpcs3/Emu/Cell/Modules/cellVoice.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVoice.cpp @@ -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::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()->is_init) + const auto manager = g_fxo->get(); + + 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 id, vm::ptr 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()->is_init) + auto manager = g_fxo->get(); + + std::scoped_lock lock(manager->mtx); + + if (!manager->is_init) return CELL_VOICE_ERROR_LIBVOICE_NOT_INIT; - return CELL_OK; + vm::var 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 portId, vm::cptr 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()->is_init) + auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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(); + 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 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()->is_init) + const auto manager = g_fxo->get(); + + 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 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()->is_init) + const auto manager = g_fxo->get(); + + 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 attrValue) { cellVoice.todo("cellVoiceGetPortAttr(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 pInfo) { cellVoice.todo("cellVoiceGetPortInfo(portId=%d, pInfo=*0x%x)", portId, pInfo); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 attrValue { cellVoice.todo("cellVoiceGetSignalState(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 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()->is_init) + const auto manager = g_fxo->get(); + + 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(0x4f323285); + + if (!port) + return CELL_VOICE_ERROR_TOPOLOGY; + + *volume = port->info.volume; return CELL_OK; } @@ -171,6 +398,8 @@ error_code cellVoiceInit(vm::ptr pArg) const auto manager = g_fxo->get(); + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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 attrValue) { cellVoice.todo("cellVoiceSetPortAttr(portId=%d, attr=%d, attrValue=*0x%x)", portId, attr, attrValue); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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 pArg) { cellVoice.todo("cellVoiceStartEx(pArg=*0x%x)", pArg); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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()->is_init) + const auto manager = g_fxo->get(); + + 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 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()->is_init) + const auto manager = g_fxo->get(); + + 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 data, vm::ptr size { cellVoice.todo("cellVoiceWriteToIPort(ips=%d, data=*0x%x, size=*0x%x)", ips, data, size); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 data, vm::ptr si { cellVoice.todo("cellVoiceWriteToIPortEx(ips=%d, data=*0x%x, size=*0x%x, numFrameLost=%d)", ips, data, size, numFrameLost); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 data, vm::ptr s { cellVoice.todo("cellVoiceWriteToIPortEx2(ips=%d, data=*0x%x, size=*0x%x, frameGaps=%d)", ips, data, size, frameGaps); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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 data, vm::ptr size { cellVoice.todo("cellVoiceReadFromOPort(ops=%d, data=*0x%x, size=*0x%x)", ops, data, size); - if (!g_fxo->get()->is_init) + const auto manager = g_fxo->get(); + + 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; } diff --git a/rpcs3/Emu/Cell/Modules/cellVoice.h b/rpcs3/Emu/Cell/Modules/cellVoice.h index 78c82f218f..47ac3b52cc 100644 --- a/rpcs3/Emu/Cell/Modules/cellVoice.h +++ b/rpcs3/Emu/Cell/Modules/cellVoice.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + // 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 ports; + std::unordered_map> 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 is_init{ false }; };