Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TESTERS NEEDED] cellMic: wait for registration before using secondary microphones #13484

Merged
merged 7 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 58 additions & 23 deletions rpcs3/Emu/Cell/Modules/cellAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ void audio_ringbuffer::process_resampled_data()
{
if (!cfg.time_stretching_enabled) return;

const auto samples = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
commit_data(samples.first, samples.second);
const auto [buffer, samples] = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
commit_data(buffer, samples);
}

void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
Expand Down Expand Up @@ -455,7 +455,7 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
u32 untouched = 0;
u32 incomplete = 0;

for (auto& port : ports)
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;
active++;
Expand Down Expand Up @@ -526,7 +526,7 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
void cell_audio_thread::reset_ports(s32 offset)
{
// Memset buffer to 0 and tag
for (auto& port : ports)
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;

Expand All @@ -548,7 +548,7 @@ void cell_audio_thread::advance(u64 timestamp)
// update ports
reset_ports(0);

for (auto& port : ports)
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;

Expand All @@ -572,12 +572,11 @@ void cell_audio_thread::advance(u64 timestamp)

// send aftermix event (normal audio event)
std::array<std::shared_ptr<lv2_event_queue>, MAX_AUDIO_EVENT_QUEUES> queues;
std::array<u64, MAX_AUDIO_EVENT_QUEUES> event_sources;
u32 queue_count = 0;

event_period++;

for (const auto& key_inf : keys)
for (const key_info& key_inf : keys)
{
if (key_inf.flags & CELL_AUDIO_EVENTFLAG_NOMIX)
{
Expand Down Expand Up @@ -607,6 +606,7 @@ void cell_audio_thread::advance(u64 timestamp)
}

event_sources[queue_count] = key_inf.source;
event_data3[queue_count] = (key_inf.flags & CELL_AUDIO_EVENTFLAG_BEFOREMIX) ? key_inf.source : 0;
queue_count++;
}
}
Expand All @@ -616,7 +616,7 @@ void cell_audio_thread::advance(u64 timestamp)
for (u32 i = 0; i < queue_count; i++)
{
lv2_obj::notify_all_t notify;
queues[i]->send(event_sources[i], 0, 0, 0);
queues[i]->send(event_sources[i], CELL_AUDIO_EVENT_MIX, 0, event_data3[i]);
Megamouse marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -648,9 +648,9 @@ namespace audio
if (auto& g_audio = g_fxo->get<cell_audio>(); g_fxo->is_init<cell_audio>())
{
// Only reboot the audio renderer if a relevant setting changed
const auto new_raw = get_raw_config();
const cell_audio_config::raw_config new_raw = get_raw_config();

if (const auto raw = g_audio.cfg.raw;
if (const cell_audio_config::raw_config raw = g_audio.cfg.raw;
force_reset ||
raw.audio_device != new_raw.audio_device ||
raw.desired_buffer_duration != new_raw.desired_buffer_duration ||
Expand Down Expand Up @@ -735,7 +735,7 @@ void cell_audio_thread::operator()()
{
loop_count++;

const auto update_req = m_update_configuration.observe();
const audio_backend_update update_req = m_update_configuration.observe();
if (update_req != audio_backend_update::NONE)
{
cellAudio.warning("Updating cell_audio_thread configuration");
Expand All @@ -758,7 +758,15 @@ void cell_audio_thread::operator()()
continue;
}

// TODO: send beforemix event (in ~2,6 ms before mixing)
// TODO: (no idea how much of this is already implemented)
// The hardware heartbeat interval of libaudio is ~5.3ms.
// As soon as one interval starts, libaudio waits for ~2.6ms (half of the interval) before it mixes the audio.
// There are 2 different types of games:
// - Normal games:
// Once the audio was mixed, we send the CELL_AUDIO_EVENT_MIX event and the game can process audio.
// - Latency sensitive games:
// If CELL_AUDIO_EVENTFLAG_BEFOREMIX is specified, we immediately send the CELL_AUDIO_EVENT_MIX event and the game can process audio.
// We then have to wait for a maximum of ~2.6ms for cellAudioSendAck and then mix immediately.

const u64 time_since_last_period = timestamp - m_last_period_end;

Expand Down Expand Up @@ -1057,7 +1065,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
std::memset(out_buffer, 0, out_buffer_sz * sizeof(float));

// mixing
for (auto& port : ports)
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;

Expand All @@ -1070,7 +1078,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
// spread port volume changes over 13ms
auto step_volume = [master_volume, &m](audio_port& port)
{
const auto param = port.level_set.load();
const audio_port::level_set_t param = port.level_set.load();

if (param.inc != 0.0f)
{
Expand Down Expand Up @@ -1180,11 +1188,11 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
void cell_audio_thread::finish_port_volume_stepping()
{
// part of cellAudioSetPortLevel functionality
for (auto& port : ports)
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;

const auto param = port.level_set.load();
const audio_port::level_set_t param = port.level_set.load();
port.level = param.value;
port.level_set.compare_and_swap(param, { param.value, 0.0f });
}
Expand Down Expand Up @@ -1312,7 +1320,7 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
}

// Open audio port
const auto port = g_audio.open_port();
audio_port* port = g_audio.open_port();

if (!port)
{
Expand Down Expand Up @@ -1373,7 +1381,7 @@ error_code cellAudioGetPortConfig(u32 portNum, vm::ptr<CellAudioPortConfig> port

portConfig->readIndexAddr = port.index;

switch (auto state = port.state.load())
switch (audio_port_state state = port.state.load())
{
case audio_port_state::closed:
portConfig->status = CELL_AUDIO_STATUS_CLOSE;
Expand Down Expand Up @@ -1414,7 +1422,7 @@ error_code cellAudioPortStart(u32 portNum)
return CELL_AUDIO_ERROR_PARAM;
}

switch (auto state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started))
switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started))
{
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
case audio_port_state::started: return CELL_AUDIO_ERROR_PORT_ALREADY_RUN;
Expand All @@ -1441,7 +1449,7 @@ error_code cellAudioPortClose(u32 portNum)
return CELL_AUDIO_ERROR_PARAM;
}

switch (auto state = g_audio.ports[portNum].state.exchange(audio_port_state::closed))
switch (audio_port_state state = g_audio.ports[portNum].state.exchange(audio_port_state::closed))
{
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
case audio_port_state::started: return CELL_OK;
Expand All @@ -1468,7 +1476,7 @@ error_code cellAudioPortStop(u32 portNum)
return CELL_AUDIO_ERROR_PARAM;
}

switch (auto state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::started, audio_port_state::opened))
switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::started, audio_port_state::opened))
{
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_RUN;
case audio_port_state::started: return CELL_OK;
Expand Down Expand Up @@ -1679,7 +1687,13 @@ error_code AudioSetNotifyEventQueue(u64 key, u32 iFlags)
}

// Set unique source associated with the key
g_audio.keys.push_back({g_audio.event_period, iFlags, ((process_getpid() + u64{}) << 32) + lv2_event_port::id_base + (g_audio.key_count++ * lv2_event_port::id_step), std::move(q)});
g_audio.keys.push_back({
.start_period = g_audio.event_period,
.flags = iFlags,
.source = ((process_getpid() + u64{}) << 32) + lv2_event_port::id_base + (g_audio.key_count++ * lv2_event_port::id_step),
.ack_timestamp = 0,
.port = std::move(q)
});
g_audio.key_count %= lv2_event_port::id_count;

return CELL_OK;
Expand Down Expand Up @@ -1911,7 +1925,28 @@ error_code cellAudioMiscSetAccessoryVolume(u32 devNum, float volume)

error_code cellAudioSendAck(u64 data3)
{
cellAudio.todo("cellAudioSendAck(data3=0x%llx)", data3);
cellAudio.trace("cellAudioSendAck(data3=0x%llx)", data3);

auto& g_audio = g_fxo->get<cell_audio>();

std::unique_lock lock(g_audio.mutex);

if (!g_audio.init)
{
return CELL_AUDIO_ERROR_NOT_INIT;
}

// TODO: error checks

for (cell_audio_thread::key_info& k : g_audio.keys)
{
if (k.source == data3)
{
k.ack_timestamp = get_system_time();
break;
}
}

return CELL_OK;
}

Expand Down
3 changes: 3 additions & 0 deletions rpcs3/Emu/Cell/Modules/cellAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,15 @@ class cell_audio_thread

u32 key_count = 0;
u8 event_period = 0;
std::array<u64, MAX_AUDIO_EVENT_QUEUES> event_sources{};
std::array<u64, MAX_AUDIO_EVENT_QUEUES> event_data3{};

struct key_info
{
u8 start_period = 0; // Starting event_period
u32 flags = 0; // iFlags
u64 source = 0; // Event source
u64 ack_timestamp = 0; // timestamp of last call of cellAudioSendAck
std::shared_ptr<lv2_event_queue> port{}; // Underlying event port
};

Expand Down
74 changes: 60 additions & 14 deletions rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
#include "Emu/RSX/rsx_utils.h"
#include "Utilities/StrUtil.h"

#include "cellMic.h"
#include "cellAudioIn.h"
#include "cellAudioOut.h"
#include "cellVideoOut.h"

#include <optional>

LOG_CHANNEL(cellAvconfExt);

template<>
Expand All @@ -34,9 +37,12 @@ void fmt_class_string<CellAudioInError>::format(std::string& out, u64 arg)

struct avconf_manager
{
shared_mutex mutex;
std::vector<CellAudioInDeviceInfo> devices;
CellAudioInDeviceMode inDeviceMode = CELL_AUDIO_IN_SINGLE_DEVICE_MODE; // TODO: use somewhere

void copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info);
void copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info) const;
std::optional<CellAudioInDeviceInfo> get_device_info(vm::cptr<char> name) const;

avconf_manager();

Expand All @@ -49,7 +55,8 @@ avconf_manager::avconf_manager()
{
u32 curindex = 0;

auto mic_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"});
const std::vector<std::string> mic_list = fmt::split(g_cfg.audio.microphone_devices.to_string(), {"@@@"});

if (!mic_list.empty())
{
switch (g_cfg.audio.microphone_type)
Expand Down Expand Up @@ -131,20 +138,24 @@ avconf_manager::avconf_manager()
}
}

void avconf_manager::copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info)
void avconf_manager::copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info) const
{
memset(info.get_ptr(), 0, sizeof(CellAudioInDeviceInfo));
ensure(num < devices.size());
*info = devices[num];
}

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);
std::optional<CellAudioInDeviceInfo> avconf_manager::get_device_info(vm::cptr<char> name) const
{
for (const CellAudioInDeviceInfo& device : devices)
{
if (strncmp(device.name, name.get_ptr(), sizeof(device.name)) == 0)
{
return device;
}
}

return std::nullopt;
}

error_code cellAudioOutUnregisterDevice(u32 deviceNumber)
Expand Down Expand Up @@ -199,6 +210,7 @@ error_code cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr<C
}

auto& av_manager = g_fxo->get<avconf_manager>();
std::lock_guard lock(av_manager.mutex);

if (deviceNumber >= av_manager.devices.size())
return CELL_AUDIO_OUT_ERROR_DEVICE_NOT_FOUND;
Expand Down Expand Up @@ -273,6 +285,7 @@ error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioInDevic
}

auto& av_manager = g_fxo->get<avconf_manager>();
std::lock_guard lock(av_manager.mutex);

u32 num_devices_returned = std::min<u32>(count, ::size32(av_manager.devices));

Expand All @@ -281,6 +294,15 @@ error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioInDevic
av_manager.copy_device_info(index, device_info + index);
}

CellAudioInDeviceInfo disconnected_device{};
disconnected_device.state = CELL_AUDIO_OUT_DEVICE_STATE_UNAVAILABLE;
disconnected_device.deviceNumber = 0xff;

for (u32 index = num_devices_returned; index < count; index++)
{
device_info[index] = disconnected_device;
}

return not_an_error(num_devices_returned);
}

Expand Down Expand Up @@ -355,6 +377,11 @@ error_code cellAudioInSetDeviceMode(u32 deviceMode)
return CELL_AUDIO_IN_ERROR_ILLEGAL_PARAMETER;
}

auto& av_manager = g_fxo->get<avconf_manager>();
std::lock_guard lock(av_manager.mutex);

av_manager.inDeviceMode = static_cast<CellAudioInDeviceMode>(deviceMode);

return CELL_OK;
}

Expand All @@ -368,12 +395,31 @@ error_code cellAudioInRegisterDevice(u64 deviceType, vm::cptr<char> name, vm::pt
return CELL_AUDIO_IN_ERROR_ILLEGAL_PARAMETER;
}

return not_an_error(0); // device number
auto& av_manager = g_fxo->get<avconf_manager>();
const std::lock_guard lock(av_manager.mutex);

std::optional<CellAudioInDeviceInfo> info = av_manager.get_device_info(name);
if (!info || !memchr(info->name, '\0', sizeof(info->name)))
{
// TODO
return CELL_AUDIO_IN_ERROR_DEVICE_NOT_FOUND;
}

auto& mic_thr = g_fxo->get<mic_thread>();
const std::lock_guard mic_lock(mic_thr.mutex);
const u32 device_number = mic_thr.register_device(info->name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From doc I think the game can repeatedly call this function, it'll just return the same device number again.
We're missing a check to make sure not to register the same device twice, either here or in mic_context::register_device.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it return the registered index (see register_device)


return not_an_error(device_number);
}

error_code cellAudioInUnregisterDevice(u32 deviceNumber)
{
cellAvconfExt.todo("cellAudioInUnregisterDevice(deviceNumber=0x%x)", deviceNumber);

auto& mic_thr = g_fxo->get<mic_thread>();
const std::lock_guard lock(mic_thr.mutex);
mic_thr.unregister_device(deviceNumber);

return CELL_OK;
}

Expand Down
Loading