From ac109e3976e3c92f4d1a28a781a559349954941b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Mar 2023 14:59:43 +0100 Subject: [PATCH] cellMic: wait for registration of multiple input devices --- rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp | 72 +++++++++++--- rpcs3/Emu/Cell/Modules/cellMic.cpp | 119 ++++++++++++++--------- rpcs3/Emu/Cell/Modules/cellMic.h | 7 +- 3 files changed, 139 insertions(+), 59 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp index ac793e0e5ab2..033ee035999f 100644 --- a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp @@ -5,6 +5,7 @@ #include "Emu/RSX/rsx_utils.h" #include "Utilities/StrUtil.h" +#include "cellMic.h" #include "cellAudioIn.h" #include "cellAudioOut.h" #include "cellVideoOut.h" @@ -34,9 +35,12 @@ void fmt_class_string::format(std::string& out, u64 arg) struct avconf_manager { + shared_mutex mutex; std::vector devices; + CellAudioInDeviceMode inDeviceMode = CELL_AUDIO_IN_SINGLE_DEVICE_MODE; // TODO: use somewhere - void copy_device_info(u32 num, vm::ptr info); + void copy_device_info(u32 num, vm::ptr info) const; + std::optional get_device_info(vm::cptr name) const; avconf_manager(); @@ -49,7 +53,8 @@ avconf_manager::avconf_manager() { u32 curindex = 0; - auto mic_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); + const std::vector mic_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); + if (!mic_list.empty()) { switch (g_cfg.audio.microphone_type) @@ -131,20 +136,24 @@ avconf_manager::avconf_manager() } } -void avconf_manager::copy_device_info(u32 num, vm::ptr info) +void avconf_manager::copy_device_info(u32 num, vm::ptr info) const { memset(info.get_ptr(), 0, sizeof(CellAudioInDeviceInfo)); + ensure(num >= devices.size()); + *info = devices[num]; +} + +std::optional avconf_manager::get_device_info(vm::cptr name) const +{ + for (const CellAudioInDeviceInfo& device : devices) + { + if (strncmp(device.name, name.get_ptr(), 64) == 0) + { + return device; + } + } - info->portType = devices[num].portType; - info->availableModeCount = devices[num].availableModeCount; - info->state = devices[num].state; - info->deviceId = devices[num].deviceId; - info->type = devices[num].type; - info->availableModes[0].type = devices[num].availableModes[0].type; - info->availableModes[0].channel = devices[num].availableModes[0].channel; - info->availableModes[0].fs = devices[num].availableModes[0].fs; - info->deviceNumber = devices[num].deviceNumber; - strcpy_trunc(info->name, devices[num].name); + return {}; } error_code cellAudioOutUnregisterDevice(u32 deviceNumber) @@ -199,6 +208,7 @@ error_code cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptrget(); + std::lock_guard lock(av_manager.mutex); if (deviceNumber >= av_manager.devices.size()) return CELL_AUDIO_OUT_ERROR_DEVICE_NOT_FOUND; @@ -273,6 +283,7 @@ error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptrget(); + std::lock_guard lock(av_manager.mutex); u32 num_devices_returned = std::min(count, ::size32(av_manager.devices)); @@ -281,6 +292,15 @@ error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptrget(); + std::lock_guard lock(av_manager.mutex); + + av_manager.inDeviceMode = static_cast(deviceMode); + return CELL_OK; } @@ -368,12 +393,31 @@ error_code cellAudioInRegisterDevice(u64 deviceType, vm::cptr name, vm::pt return CELL_AUDIO_IN_ERROR_ILLEGAL_PARAMETER; } - return not_an_error(0); // device number + auto& av_manager = g_fxo->get(); + const std::lock_guard lock(av_manager.mutex); + + std::optional info = av_manager.get_device_info(name); + if (!info || !memchr(info->name, '\0', 64)) + { + // TODO + return CELL_AUDIO_IN_ERROR_DEVICE_NOT_FOUND; + } + + auto& mic_thr = g_fxo->get(); + const std::lock_guard mic_lock(mic_thr.mutex); + const u32 device_number = mic_thr.register_device(info->name); + + return device_number; } error_code cellAudioInUnregisterDevice(u32 deviceNumber) { cellAvconfExt.todo("cellAudioInUnregisterDevice(deviceNumber=0x%x)", deviceNumber); + + auto& mic_thr = g_fxo->get(); + const std::lock_guard lock(mic_thr.mutex); + mic_thr.unregister_device(deviceNumber); + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 736da325473a..9b9211885cba 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -103,8 +103,7 @@ void mic_context::operator()() for (auto& mic_entry : mic_list) { - auto& mic = mic_entry.second; - mic.update_audio(); + mic_entry.update_audio(); } auto mic_queue = lv2_event_queue::find(event_queue_key); @@ -113,9 +112,9 @@ void mic_context::operator()() return true; } - for (const auto& [dev_num, mic] : mic_list) + for (usz dev_num = 0; dev_num < mic_list.size(); dev_num) { - if (mic.has_data()) + if (mic_list[dev_num].has_data()) { mic_queue->send(event_queue_source, CELLMIC_DATA, dev_num, 0); } @@ -134,45 +133,77 @@ void mic_context::operator()() // Cleanup for (auto& mic_entry : mic_list) { - mic_entry.second.close_microphone(); + mic_entry.close_microphone(); } } void mic_context::load_config_and_init() { - auto device_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); + const std::vector device_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"}); if (!device_list.empty() && mic_list.empty()) { - switch (g_cfg.audio.microphone_type) + // We only register the first device. The rest is registered with cellAudioInRegisterDevice. + if (g_cfg.audio.microphone_type == microphone_handler::singstar) { - case microphone_handler::standard: - { - for (s32 index = 0; index < static_cast(device_list.size()); index++) - { - mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(index), std::forward_as_tuple(microphone_handler::standard)); - ::at32(mic_list, index).add_device(device_list[index]); - } - break; - } - case microphone_handler::singstar: - { - mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(microphone_handler::singstar)); + mic_list.emplace_back(microphone_handler::singstar); ::at32(mic_list, 0).add_device(device_list[0]); + + // Singstar uses the same device for 2 players if (device_list.size() >= 2) + { ::at32(mic_list, 0).add_device(device_list[1]); - break; + } } - case microphone_handler::real_singstar: - case microphone_handler::rocksmith: + else { - mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(g_cfg.audio.microphone_type)); - ::at32(mic_list, 0).add_device(device_list[0]); - break; + [[maybe_unused]] const u32 index = register_device(device_list[0]); } - case microphone_handler::null: - default: break; + } +} + +u32 mic_context::register_device(const std::string& device_name, const std::string& name_2_for_singstar) +{ + const usz index = mic_list.size(); + + switch (g_cfg.audio.microphone_type) + { + case microphone_handler::standard: + case microphone_handler::real_singstar: + case microphone_handler::rocksmith: + { + mic_list.emplace_back(g_cfg.audio.microphone_type.get()); + ::at32(mic_list, index).add_device(device_name); + + if (auto mic_queue = lv2_event_queue::find(event_queue_key)) + { + mic_queue->send(event_queue_source, CELLMIC_ATTACH, index, 0); } + + break; + } + case microphone_handler::singstar: + case microphone_handler::null: + default: + break; + } + + return index; +} + +void mic_context::unregister_device(u32 dev_num) +{ + // Don't allow to unregister the first device for now. + if (dev_num == 0 || dev_num >= mic_list.size()) + { + return; + } + + mic_list.erase(mic_list.begin() + dev_num); + + if (auto mic_queue = lv2_event_queue::find(event_queue_key)) + { + mic_queue->send(event_queue_source, CELLMIC_DETACH, dev_num, 0); } } @@ -563,7 +594,7 @@ error_code cellMicOpenEx(s32 dev_num, s32 rawSampleRate, s32 rawChannel, s32 DSP if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -599,7 +630,7 @@ u8 cellMicIsOpen(s32 dev_num) if (!mic_thr.init) return false; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return false; return ::at32(mic_thr.mic_list, dev_num).is_opened(); @@ -620,7 +651,7 @@ error_code cellMicClose(s32 dev_num) if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -645,7 +676,7 @@ error_code cellMicStartEx(s32 dev_num, u32 iflags) if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -675,7 +706,7 @@ error_code cellMicStop(s32 dev_num) if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -705,7 +736,7 @@ error_code cellMicGetDeviceAttr(s32 dev_num, CellMicDeviceAttr deviceAttributes, if (dev_num < 0) return CELL_MICIN_ERROR_PARAM; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -741,7 +772,7 @@ error_code cellMicSetDeviceAttr(s32 dev_num, CellMicDeviceAttr deviceAttributes, if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -777,7 +808,7 @@ error_code cellMicGetSignalAttr(s32 dev_num, CellMicSignalAttr sig_attrib, vm::p if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& mic = ::at32(mic_thr.mic_list, dev_num); @@ -801,7 +832,7 @@ error_code cellMicSetSignalAttr(s32 dev_num, CellMicSignalAttr sig_attrib, vm::p if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& mic = ::at32(mic_thr.mic_list, dev_num); @@ -825,7 +856,7 @@ error_code cellMicGetSignalState(s32 dev_num, CellMicSignalState sig_state, vm:: if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& mic = ::at32(mic_thr.mic_list, dev_num); @@ -876,7 +907,7 @@ error_code cellMicGetFormatEx(s32 dev_num, vm::ptr format, if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& device = ::at32(mic_thr.mic_list, dev_num); @@ -937,9 +968,9 @@ error_code cellMicSetNotifyEventQueue(u64 key) mic_thr.event_queue_key = key; // TODO: Properly generate/handle mic events - for (const auto& mic_entry : mic_thr.mic_list) + for (usz i = 0; i < mic_thr.mic_list.size(); i) { - mic_queue->send(0, CELLMIC_ATTACH, mic_entry.first, 0); + mic_queue->send(0, CELLMIC_ATTACH, i, 0); } return CELL_OK; @@ -963,9 +994,9 @@ error_code cellMicSetNotifyEventQueue2(u64 key, u64 source, u64 flag) mic_thr.event_queue_source = source; // TODO: Properly generate/handle mic events - for (const auto& mic_entry : mic_thr.mic_list) + for (usz i = 0; i < mic_thr.mic_list.size(); i) { - mic_queue->send(source, CELLMIC_ATTACH, mic_entry.first, 0); + mic_queue->send(source, CELLMIC_ATTACH, i, 0); } return CELL_OK; @@ -997,7 +1028,7 @@ error_code cell_mic_read(s32 dev_num, vm::ptr data, s32 max_bytes, /*CellM if (!mic_thr.init) return CELL_MICIN_ERROR_NOT_INIT; - if (!mic_thr.mic_list.contains(dev_num)) + if (dev_num >= static_cast(mic_thr.mic_list.size())) return CELL_MICIN_ERROR_DEVICE_NOT_FOUND; auto& mic = ::at32(mic_thr.mic_list, dev_num); @@ -1117,7 +1148,7 @@ error_code cellMicGetStatus(s32 dev_num, vm::ptr status) // TODO - if (mic_thr.mic_list.contains(dev_num)) + if (dev_num < mic_thr.mic_list.size()) { const auto& mic = ::at32(mic_thr.mic_list, dev_num); status->raw_samprate = mic.get_raw_samplingrate(); diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index c25b1dcbaec5..0c105651b65e 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -1,6 +1,7 @@ #pragma once #include "Utilities/Thread.h" +#include "Emu/Cell/timers.hpp" #include "3rdparty/OpenAL/include/alext.h" @@ -351,10 +352,14 @@ class mic_context void operator()(); void load_config_and_init(); + // Returns index of registered device + u32 register_device(const std::string& name, const std::string& name_2_for_singstar = {}); + void unregister_device(u32 dev_num); + u64 event_queue_key = 0; u64 event_queue_source = 0; - std::unordered_map mic_list; + std::vector mic_list; shared_mutex mutex; atomic_t init = 0;