Microphone implementation

This commit is contained in:
RipleyTom 2018-12-20 23:36:56 +01:00 committed by Ani
parent 2898309f68
commit 6c6b973342
10 changed files with 1428 additions and 230 deletions

View File

@ -1,8 +1,9 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/Cell/PPUModule.h" #include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "Emu/RSX/rsx_utils.h" #include "Emu/RSX/rsx_utils.h"
#include "Utilities/StrUtil.h"
#include "cellAudioIn.h" #include "cellAudioIn.h"
#include "cellAudioOut.h" #include "cellAudioOut.h"
@ -11,6 +12,115 @@
LOG_CHANNEL(cellAvconfExt); LOG_CHANNEL(cellAvconfExt);
struct avconf_manager
{
std::vector<CellAudioInDeviceInfo> devices;
void copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info);
avconf_manager();
};
avconf_manager::avconf_manager()
{
u32 curindex = 0;
auto mic_list = fmt::split(g_cfg.audio.microphone_devices, {"@@@"});
if (mic_list.size())
{
switch (g_cfg.audio.microphone_type)
{
case microphone_handler::standard:
for (u32 index = 0; index < mic_list.size(); index++)
{
devices.emplace_back();
devices[curindex].portType = CELL_AUDIO_IN_PORT_USB;
devices[curindex].availableModeCount = 1;
devices[curindex].state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
devices[curindex].deviceId = 0xE11CC0DE + curindex;
devices[curindex].type = 0xC0DEE11C;
devices[curindex].availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
devices[curindex].availableModes[0].channel = CELL_AUDIO_IN_CHNUM_2;
devices[curindex].availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
devices[curindex].deviceNumber = curindex;
strcpy(devices[curindex].name, mic_list[index].c_str());
curindex++;
}
break;
case microphone_handler::real_singstar:
case microphone_handler::singstar:
// Only one device for singstar device
devices.emplace_back();
devices[curindex].portType = CELL_AUDIO_IN_PORT_USB;
devices[curindex].availableModeCount = 1;
devices[curindex].state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
devices[curindex].deviceId = 0x57A3C0DE;
devices[curindex].type = 0xC0DE57A3;
devices[curindex].availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
devices[curindex].availableModes[0].channel = CELL_AUDIO_IN_CHNUM_2;
devices[curindex].availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
devices[curindex].deviceNumber = curindex;
strcpy(devices[curindex].name, mic_list[0].c_str());
curindex++;
break;
case microphone_handler::rocksmith:
devices.emplace_back();
devices[curindex].portType = CELL_AUDIO_IN_PORT_USB;
devices[curindex].availableModeCount = 1;
devices[curindex].state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
devices[curindex].deviceId = 0x12BA00FF; // Specific to rocksmith usb input
devices[curindex].type = 0xC0DE73C4;
devices[curindex].availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
devices[curindex].availableModes[0].channel = CELL_AUDIO_IN_CHNUM_1;
devices[curindex].availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
devices[curindex].deviceNumber = curindex;
strcpy(devices[curindex].name, mic_list[0].c_str());
curindex++;
break;
default: break;
}
}
if (g_cfg.io.camera != camera_handler::null)
{
devices.emplace_back();
devices[curindex].portType = CELL_AUDIO_IN_PORT_USB;
devices[curindex].availableModeCount = 1;
devices[curindex].state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
devices[curindex].deviceId = 0xDEADBEEF;
devices[curindex].type = 0xBEEFDEAD;
devices[curindex].availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
devices[curindex].availableModes[0].channel = CELL_AUDIO_IN_CHNUM_NONE;
devices[curindex].availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
devices[curindex].deviceNumber = curindex;
strcpy(devices[curindex].name, "USB Camera");
curindex++;
}
}
void avconf_manager::copy_device_info(u32 num, vm::ptr<CellAudioInDeviceInfo> info)
{
memset(info.get_ptr(), 0, sizeof(CellAudioInDeviceInfo));
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(info->name, devices[num].name);
}
s32 cellAudioOutUnregisterDevice(u32 deviceNumber) s32 cellAudioOutUnregisterDevice(u32 deviceNumber)
{ {
cellAvconfExt.todo("cellAudioOutUnregisterDevice(deviceNumber=0x%x)", deviceNumber); cellAvconfExt.todo("cellAudioOutUnregisterDevice(deviceNumber=0x%x)", deviceNumber);
@ -38,23 +148,21 @@ s32 cellVideoOutSetupDisplay()
s32 cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr<CellAudioInDeviceInfo> info) s32 cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr<CellAudioInDeviceInfo> info)
{ {
cellAvconfExt.todo("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); cellAvconfExt.todo("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info);
info->portType = CELL_AUDIO_IN_PORT_USB;
info->availableModeCount = 1; auto av_manager = fxm::get_always<avconf_manager>();
info->state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
info->deviceNumber = 0; if (deviceNumber >= av_manager->devices.size())
// Some games check if deviceId and type are the same. return CELL_AUDIO_OUT_ERROR_DEVICE_NOT_FOUND;
info->deviceId = 0xDEADBEEF;
info->type = 0xBEEFDEAD; av_manager->copy_device_info(deviceNumber, info);
info->availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
info->availableModes[0].channel = CELL_AUDIO_IN_CHNUM_NONE;
info->availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
strcpy(info->name, "USB Camera");
return CELL_OK; return CELL_OK;
} }
s32 cellVideoOutConvertCursorColor(u32 videoOut, s32 displaybuffer_format, f32 gamma, s32 source_buffer_format, vm::ptr<void> src_addr, vm::ptr<u32> dest_addr, s32 num) s32 cellVideoOutConvertCursorColor(u32 videoOut, s32 displaybuffer_format, f32 gamma, s32 source_buffer_format, vm::ptr<void> src_addr, vm::ptr<u32> dest_addr, s32 num)
{ {
cellAvconfExt.todo("cellVideoOutConvertCursorColor(videoOut=%d, displaybuffer_format=0x%x, gamma=0x%x, source_buffer_format=0x%x, src_addr=*0x%x, dest_addr=*0x%x, num=0x%x)", videoOut, displaybuffer_format, gamma, source_buffer_format, src_addr, dest_addr, num); cellAvconfExt.todo("cellVideoOutConvertCursorColor(videoOut=%d, displaybuffer_format=0x%x, gamma=0x%x, source_buffer_format=0x%x, src_addr=*0x%x, dest_addr=*0x%x, num=0x%x)", videoOut,
displaybuffer_format, gamma, source_buffer_format, src_addr, dest_addr, num);
return CELL_OK; return CELL_OK;
} }
@ -68,26 +176,30 @@ s32 cellVideoOutGetGamma(u32 videoOut, vm::ptr<f32> gamma)
} }
auto conf = fxm::get_always<rsx::avconf>(); auto conf = fxm::get_always<rsx::avconf>();
*gamma = conf->gamma; *gamma = conf->gamma;
return CELL_OK; return CELL_OK;
} }
s32 cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioInDeviceInfo> info) s32 cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioInDeviceInfo> device_info)
{ {
cellAvconfExt.todo("cellAudioInGetAvailableDeviceInfo(count=0x%x, info=*0x%x)", count, info); cellAvconfExt.todo("cellAudioInGetAvailableDeviceInfo(count=0x%x, info=*0x%x)", count, device_info);
info->portType = CELL_AUDIO_IN_PORT_USB;
info->availableModeCount = 1; if (count > 16 || !device_info.addr())
info->state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE; {
info->deviceNumber = 0; return CELL_AUDIO_IN_ERROR_ILLEGAL_PARAMETER;
// Some games check if deviceId and type are the same. }
info->deviceId = 0xDEADBEEF;
info->type = 0xBEEFDEAD; auto av_manager = fxm::get_always<avconf_manager>();
info->availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
info->availableModes[0].channel = CELL_AUDIO_IN_CHNUM_NONE; u32 num_devices_returned = std::min(count, (u32)av_manager->devices.size());
info->availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
strcpy(info->name, "USB Camera"); for (u32 index = 0; index < num_devices_returned; index++)
return 1; // number of available devices {
av_manager->copy_device_info(index, device_info + index);
}
return (s32)num_devices_returned;
} }
s32 cellAudioOutGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioOutDeviceInfo2> info) s32 cellAudioOutGetAvailableDeviceInfo(u32 count, vm::ptr<CellAudioOutDeviceInfo2> info)
@ -110,7 +222,7 @@ s32 cellVideoOutSetGamma(u32 videoOut, f32 gamma)
return CELL_VIDEO_OUT_ERROR_ILLEGAL_PARAMETER; return CELL_VIDEO_OUT_ERROR_ILLEGAL_PARAMETER;
} }
auto conf = fxm::get_always<rsx::avconf>(); auto conf = fxm::get_always<rsx::avconf>();
conf->gamma = gamma; conf->gamma = gamma;
return CELL_OK; return CELL_OK;
@ -137,6 +249,7 @@ s32 cellAudioInSetDeviceMode(u32 deviceMode)
s32 cellAudioInRegisterDevice(u64 deviceType, vm::cptr<char> name, vm::ptr<CellAudioInRegistrationOption> option, vm::ptr<CellAudioInDeviceConfiguration> config) s32 cellAudioInRegisterDevice(u64 deviceType, vm::cptr<char> name, vm::ptr<CellAudioInRegistrationOption> option, vm::ptr<CellAudioInDeviceConfiguration> config)
{ {
cellAvconfExt.todo("cellAudioInRegisterDevice(deviceType=0x%llx, name=%s, option=*0x%x, config=*0x%x)", deviceType, name, option, config); cellAvconfExt.todo("cellAudioInRegisterDevice(deviceType=0x%llx, name=%s, option=*0x%x, config=*0x%x)", deviceType, name, option, config);
return 0; // device number return 0; // device number
} }
@ -155,10 +268,10 @@ s32 cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<f32> screenSize)
return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT;
} }
//TODO: Use virtual screen size // TODO: Use virtual screen size
#ifdef _WIN32 #ifdef _WIN32
//HDC screen = GetDC(NULL); // HDC screen = GetDC(NULL);
//float diagonal = roundf(sqrtf((powf(float(GetDeviceCaps(screen, HORZSIZE)), 2) + powf(float(GetDeviceCaps(screen, VERTSIZE)), 2))) * 0.0393f); // float diagonal = roundf(sqrtf((powf(float(GetDeviceCaps(screen, HORZSIZE)), 2) + powf(float(GetDeviceCaps(screen, VERTSIZE)), 2))) * 0.0393f);
#else #else
// TODO: Linux implementation, without using wx // TODO: Linux implementation, without using wx
// float diagonal = roundf(sqrtf((powf(wxGetDisplaySizeMM().GetWidth(), 2) + powf(wxGetDisplaySizeMM().GetHeight(), 2))) * 0.0393f); // float diagonal = roundf(sqrtf((powf(wxGetDisplaySizeMM().GetWidth(), 2) + powf(wxGetDisplaySizeMM().GetHeight(), 2))) * 0.0393f);
@ -173,9 +286,8 @@ s32 cellVideoOutSetCopyControl(u32 videoOut, u32 control)
return CELL_OK; return CELL_OK;
} }
DECLARE(ppu_module_manager::cellAvconfExt)
DECLARE(ppu_module_manager::cellAvconfExt)("cellSysutilAvconfExt", []() ("cellSysutilAvconfExt", []() {
{
REG_FUNC(cellSysutilAvconfExt, cellAudioOutUnregisterDevice); REG_FUNC(cellSysutilAvconfExt, cellAudioOutUnregisterDevice);
REG_FUNC(cellSysutilAvconfExt, cellAudioOutGetDeviceInfo2); REG_FUNC(cellSysutilAvconfExt, cellAudioOutGetDeviceInfo2);
REG_FUNC(cellSysutilAvconfExt, cellVideoOutSetXVColor); REG_FUNC(cellSysutilAvconfExt, cellVideoOutSetXVColor);

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +1,42 @@
#pragma once #pragma once
#include "Utilities/BEType.h" #include "Utilities/BEType.h"
#include "Utilities/Thread.h" #include "Utilities/Thread.h"
#include "3rdparty/OpenAL/include/alext.h"
// Error Codes // Error Codes
enum enum
{ {
CELL_MIC_ERROR_ALREADY_INIT = 0x80140101, CELL_MIC_ERROR_ALREADY_INIT = 0x80140101,
CELL_MIC_ERROR_SYSTEM = 0x80140102, CELL_MIC_ERROR_SYSTEM = 0x80140102,
CELL_MIC_ERROR_NOT_INIT = 0x80140103, CELL_MIC_ERROR_NOT_INIT = 0x80140103,
CELL_MIC_ERROR_PARAM = 0x80140104, CELL_MIC_ERROR_PARAM = 0x80140104,
CELL_MIC_ERROR_PORT_FULL = 0x80140105, CELL_MIC_ERROR_PORT_FULL = 0x80140105,
CELL_MIC_ERROR_ALREADY_OPEN = 0x80140106, CELL_MIC_ERROR_ALREADY_OPEN = 0x80140106,
CELL_MIC_ERROR_NOT_OPEN = 0x80140107, CELL_MIC_ERROR_NOT_OPEN = 0x80140107,
CELL_MIC_ERROR_NOT_RUN = 0x80140108, CELL_MIC_ERROR_NOT_RUN = 0x80140108,
CELL_MIC_ERROR_TRANS_EVENT = 0x80140109, CELL_MIC_ERROR_TRANS_EVENT = 0x80140109,
CELL_MIC_ERROR_OPEN = 0x8014010a, CELL_MIC_ERROR_OPEN = 0x8014010a,
CELL_MIC_ERROR_SHAREDMEMORY = 0x8014010b, CELL_MIC_ERROR_SHAREDMEMORY = 0x8014010b,
CELL_MIC_ERROR_MUTEX = 0x8014010c, CELL_MIC_ERROR_MUTEX = 0x8014010c,
CELL_MIC_ERROR_EVENT_QUEUE = 0x8014010d, CELL_MIC_ERROR_EVENT_QUEUE = 0x8014010d,
CELL_MIC_ERROR_DEVICE_NOT_FOUND = 0x8014010e, CELL_MIC_ERROR_DEVICE_NOT_FOUND = 0x8014010e,
CELL_MIC_ERROR_SYSTEM_NOT_FOUND = 0x8014010e, CELL_MIC_ERROR_SYSTEM_NOT_FOUND = 0x8014010e,
CELL_MIC_ERROR_FATAL = 0x8014010f, CELL_MIC_ERROR_FATAL = 0x8014010f,
CELL_MIC_ERROR_DEVICE_NOT_SUPPORT = 0x80140110, CELL_MIC_ERROR_DEVICE_NOT_SUPPORT = 0x80140110,
}; };
struct CellMicInputFormat struct CellMicInputFormat
{ {
u8 channelNum; u8 channelNum;
u8 subframeSize; u8 subframeSize;
u8 bitResolution; u8 bitResolution;
u8 dataType; u8 dataType;
be_t<u32> sampleRate; be_t<u32> sampleRate;
}; };
enum CellMicSignalState enum CellMicSignalState : u32
{ {
CELL_MIC_SIGSTATE_LOCTALK = 0, CELL_MIC_SIGSTATE_LOCTALK = 0,
CELL_MIC_SIGSTATE_FARTALK = 1, CELL_MIC_SIGSTATE_FARTALK = 1,
@ -47,32 +49,220 @@ enum CellMicSignalState
enum CellMicCommand enum CellMicCommand
{ {
CELL_MIC_ATTACH = 2, CELL_MIC_ATTACH = 2,
CELL_MIC_DATA = 5, CELL_MIC_DATA = 5,
}; };
// TODO: generate this from input from an actual microphone enum CellMicDeviceAttr : u32
const u32 bufferSize = 1; {
CELLMIC_DEVATTR_LED = 9,
CELLMIC_DEVATTR_GAIN = 10,
CELLMIC_DEVATTR_VOLUME = 201,
CELLMIC_DEVATTR_AGC = 202,
CELLMIC_DEVATTR_CHANVOL = 301,
CELLMIC_DEVATTR_DSPTYPE = 302,
};
enum CellMicSignalType : u8
{
CELLMIC_SIGTYPE_NULL = 0,
CELLMIC_SIGTYPE_DSP = 1,
CELLMIC_SIGTYPE_AUX = 2,
CELLMIC_SIGTYPE_RAW = 4,
};
enum CellMicType : s32
{
CELLMIC_TYPE_UNDEF = -1,
CELLMIC_TYPE_UNKNOWN = 0,
CELLMIC_TYPE_EYETOY1 = 1,
CELLMIC_TYPE_EYETOY2 = 2,
CELLMIC_TYPE_USBAUDIO = 3,
CELLMIC_TYPE_BLUETOOTH = 4,
CELLMIC_TYPE_A2DP = 5,
} CellMicType;
template <std::size_t S>
class simple_ringbuf
{
public:
simple_ringbuf()
{
m_container.resize(S);
}
bool has_data() const
{
return m_used != 0;
}
u32 read_bytes(u8* buf, const u32 size)
{
u32 to_read = size > m_used ? m_used : size;
if (!to_read)
return 0;
u8* data = m_container.data();
u32 new_tail = m_tail + to_read;
if (new_tail >= S)
{
u32 first_chunk_size = S - m_tail;
std::memcpy(buf, data + m_tail, first_chunk_size);
std::memcpy(buf + first_chunk_size, data, to_read - first_chunk_size);
m_tail = (new_tail - S);
}
else
{
std::memcpy(buf, data + m_tail, to_read);
m_tail = new_tail;
}
m_used -= to_read;
return to_read;
}
void write_bytes(const u8* buf, const u32 size)
{
ASSERT(size <= S);
if (u32 over_size = m_used + size; over_size > S)
{
m_tail += (over_size - S);
if (m_tail > S)
m_tail -= S;
m_used = S;
}
else
{
m_used = over_size;
}
u8* data = m_container.data();
u32 new_head = m_head + size;
if (new_head >= S)
{
u32 first_chunk_size = S - m_head;
std::memcpy(data + m_head, buf, first_chunk_size);
std::memcpy(data, buf + first_chunk_size, size - first_chunk_size);
m_head = (new_head - S);
}
else
{
std::memcpy(data + m_head, buf, size);
m_head = new_head;
}
}
protected:
std::vector<u8> m_container;
u32 m_head = 0, m_tail = 0, m_used = 0;
};
class microphone_device
{
public:
microphone_device(microphone_handler type);
void add_device(const std::string& name);
s32 open_microphone(const u8 type, const u32 dsp_r, const u32 raw_r, const u8 channels = 2);
s32 close_microphone();
s32 start_microphone();
s32 stop_microphone();
void update_audio();
bool has_data() const;
bool is_opened() const { return mic_opened; }
bool is_started() const { return mic_started; }
u8 get_signal_types() const { return signal_types; }
u8 get_bit_resolution() const { return bit_resolution; }
u32 get_raw_samplingrate() const { return raw_samplingrate; }
u8 get_num_channels() const { return num_channels; }
u8 get_datatype() const
{
switch(device_type)
{
case microphone_handler::real_singstar:
case microphone_handler::singstar:
return 0; // LE
default:
return 1; // BE
}
}
u32 read_raw(u8* buf, u32 size) { return rbuf_raw.read_bytes(buf, size); }
u32 read_dsp(u8* buf, u32 size) { return rbuf_dsp.read_bytes(buf, size); }
// Microphone attributes
u32 attr_gain = 3;
u32 attr_volume = 145;
u32 attr_agc = 0;
u32 attr_chanvol[2] = {145, 145};
u32 attr_led = 0;
u32 attr_dsptype = 0;
private:
static void variable_byteswap(const void* src, void* dst, const u32 bytesize);
u32 capture_audio();
void get_raw(const u32 num_samples);
void get_dsp(const u32 num_samples);
private:
microphone_handler device_type;
std::vector<std::string> device_name;
bool mic_opened = false;
bool mic_started = false;
std::vector<ALCdevice*> input_devices;
std::vector<std::vector<u8>> internal_bufs;
std::vector<u8> temp_buf;
// Sampling information provided at opening of mic
u32 raw_samplingrate = 48000;
u32 dsp_samplingrate = 48000;
u32 aux_samplingrate = 48000;
u8 bit_resolution = 16;
u8 num_channels = 2;
u8 signal_types = CELLMIC_SIGTYPE_NULL;
u32 sample_size; // Determined at opening for internal use
static constexpr std::size_t inbuf_size = 400000; // Default value unknown
simple_ringbuf<inbuf_size> rbuf_raw;
simple_ringbuf<inbuf_size> rbuf_dsp;
simple_ringbuf<inbuf_size> rbuf_aux;
};
class mic_context class mic_context
{ {
public: public:
void operator()(); void operator()();
void load_config_and_init();
// Default value of 48000 for no particular reason u64 event_queue_key = 0;
u32 DspFrequency = 48000; // DSP is the default type
u32 rawFrequency = 48000;
u32 AuxFrequency = 48000;
u8 bitResolution = 32;
bool micOpened = false;
bool micStarted = false;
u64 eventQueueKey = 0;
u32 signalStateLocalTalk = 9; // value is in range 0-10. 10 indicates talking, 0 indicating none. std::unordered_map<u8, microphone_device> mic_list;
u32 signalStateFarTalk = 0; // value is in range 0-10. 10 indicates talking from far away, 0 indicating none.
f32 signalStateNoiseSupression; // value is in decibels protected:
f32 signalStateGainControl; const u64 start_time = get_system_time();
f32 signalStateMicSignalLevel; // value is in decibels u64 m_counter = 0;
f32 signalStateSpeakerSignalLevel; // value is in decibels
// u32 signalStateLocalTalk = 9; // value is in range 0-10. 10 indicates talking, 0 indicating none.
// u32 signalStateFarTalk = 0; // value is in range 0-10. 10 indicates talking from far away, 0 indicating none.
// f32 signalStateNoiseSupression; // value is in decibels
// f32 signalStateGainControl;
// f32 signalStateMicSignalLevel; // value is in decibels
// f32 signalStateSpeakerSignalLevel; // value is in decibels
}; };
using mic_thread = named_thread<mic_context>; using mic_thread = named_thread<mic_context>;

View File

@ -119,6 +119,15 @@ enum class move_handler
fake, fake,
}; };
enum class microphone_handler
{
null,
standard,
singstar,
real_singstar,
rocksmith,
};
enum class video_resolution enum class video_resolution
{ {
_1080, _1080,
@ -546,7 +555,8 @@ struct cfg_root : cfg::node
cfg::_int<1, 1000> sampling_period_multiplier{this, "Sampling Period Multiplier", 100}; cfg::_int<1, 1000> sampling_period_multiplier{this, "Sampling Period Multiplier", 100};
cfg::_bool enable_time_stretching{this, "Enable Time Stretching", false}; cfg::_bool enable_time_stretching{this, "Enable Time Stretching", false};
cfg::_int<0, 100> time_stretching_threshold{this, "Time Stretching Threshold", 75}; cfg::_int<0, 100> time_stretching_threshold{this, "Time Stretching Threshold", 75};
cfg::_enum<microphone_handler> microphone_type{ this, "Microphone Type", microphone_handler::null };
cfg::string microphone_devices{ this, "Microphone Devices", ";;;;" };
} audio{this}; } audio{this};
struct node_io : cfg::node struct node_io : cfg::node
@ -559,7 +569,6 @@ struct cfg_root : cfg::node
cfg::_enum<camera_handler> camera{this, "Camera", camera_handler::null}; cfg::_enum<camera_handler> camera{this, "Camera", camera_handler::null};
cfg::_enum<fake_camera_type> camera_type{this, "Camera type", fake_camera_type::unknown}; cfg::_enum<fake_camera_type> camera_type{this, "Camera type", fake_camera_type::unknown};
cfg::_enum<move_handler> move{this, "Move", move_handler::null}; cfg::_enum<move_handler> move{this, "Move", move_handler::null};
} io{this}; } io{this};
struct node_sys : cfg::node struct node_sys : cfg::node

View File

@ -9,7 +9,8 @@
"enableBuffering": "Enables audio buffering, which reduces crackle/stutter but increases audio latency (requires XAudio2 or OpenAL).", "enableBuffering": "Enables audio buffering, which reduces crackle/stutter but increases audio latency (requires XAudio2 or OpenAL).",
"audioBufferDuration": "Target buffer duration in milliseconds.\nHigher values make the buffering algorithm's job easier, but may introduce noticeable audio latency.", "audioBufferDuration": "Target buffer duration in milliseconds.\nHigher values make the buffering algorithm's job easier, but may introduce noticeable audio latency.",
"enableTimeStretching": "Enables time stretching - requires buffering to be enabled.\nReduces crackle/stutter further, but may cause a very noticeable reduction in audio quality on slower CPUs.", "enableTimeStretching": "Enables time stretching - requires buffering to be enabled.\nReduces crackle/stutter further, but may cause a very noticeable reduction in audio quality on slower CPUs.",
"timeStretchingThreshold": "Buffer fill level (in percentage) below which time stretching will start." "timeStretchingThreshold": "Buffer fill level (in percentage) below which time stretching will start.",
"microphoneBox": "Standard should be used for most games.\nSingstar emulates a singstar device and should be used with Singstar games.\nReal Singstar should only be used with a REAL Singstar device with Singstar games.\nRocksmith should be used with a Rocksmith dongle."
}, },
"cpu": { "cpu": {
"PPU": { "PPU": {

View File

@ -9,6 +9,7 @@
#include "Emu/System.h" #include "Emu/System.h"
#include "Utilities/Config.h" #include "Utilities/Config.h"
#include "Utilities/Thread.h" #include "Utilities/Thread.h"
#include "Utilities/StrUtil.h"
#include <QMessageBox> #include <QMessageBox>
@ -24,6 +25,8 @@
#include "Emu/RSX/VK/VKHelpers.h" #include "Emu/RSX/VK/VKHelpers.h"
#endif #endif
#include "3rdparty/OpenAL/include/alext.h"
extern std::string g_cfg_defaults; //! Default settings grabbed from Utilities/Config.h extern std::string g_cfg_defaults; //! Default settings grabbed from Utilities/Config.h
inline std::string sstr(const QString& _in) { return _in.toStdString(); } inline std::string sstr(const QString& _in) { return _in.toStdString(); }
@ -228,6 +231,58 @@ emu_settings::Render_Creator::Render_Creator()
renderers = { &D3D12, &Vulkan, &OpenGL, &NullRender }; renderers = { &D3D12, &Vulkan, &OpenGL, &NullRender };
} }
emu_settings::Microphone_Creator::Microphone_Creator()
{
RefreshList();
}
void emu_settings::Microphone_Creator::RefreshList()
{
microphones_list.clear();
microphones_list.append(mic_none);
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_TRUE)
{
const char *devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
while (*devices != 0)
{
microphones_list.append(qstr(devices));
devices += strlen(devices) + 1;
}
}
else
{
// Without enumeration we can only use one device
microphones_list.append(qstr(alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER)));
}
}
std::string emu_settings::Microphone_Creator::SetDevice(u32 num, QString& text)
{
if (text == mic_none)
sel_list[num-1] = "";
else
sel_list[num-1] = text.toStdString();
const std::string final_list = sel_list[0] + "@@@" + sel_list[1] + "@@@" + sel_list[2] + "@@@" + sel_list[3] + "@@@";
return final_list;
}
void emu_settings::Microphone_Creator::ParseDevices(std::string list)
{
for (u32 index = 0; index < 4; index++)
{
sel_list[index] = "";
}
const auto devices_list = fmt::split(list, { "@@@" });
for (u32 index = 0; index < std::min((u32)4, (u32)devices_list.size()); index++)
{
sel_list[index] = devices_list[index];
}
}
emu_settings::emu_settings() : QObject() emu_settings::emu_settings() : QObject()
{ {
} }

View File

@ -109,6 +109,8 @@ public:
AudioBufferDuration, AudioBufferDuration,
EnableTimeStretching, EnableTimeStretching,
TimeStretchingThreshold, TimeStretchingThreshold,
MicrophoneType,
MicrophoneDevices,
// Input / Output // Input / Output
PadHandler, PadHandler,
@ -180,6 +182,17 @@ public:
Render_Creator(); Render_Creator();
}; };
struct Microphone_Creator
{
QStringList microphones_list;
QString mic_none = tr("None");
std::array<std::string, 4> sel_list;
std::string SetDevice(u32 num, QString& text);
void ParseDevices(std::string list);
void RefreshList();
Microphone_Creator();
};
std::set<SettingsType> m_broken_types; // list of broken settings std::set<SettingsType> m_broken_types; // list of broken settings
/** Creates a settings object which reads in the config.yml file at rpcs3/bin/%path%/config.yml /** Creates a settings object which reads in the config.yml file at rpcs3/bin/%path%/config.yml
@ -224,6 +237,9 @@ public:
/** Gets all the renderer info for gpu settings.*/ /** Gets all the renderer info for gpu settings.*/
Render_Creator m_render_creator; Render_Creator m_render_creator;
/** Gets a list of all the microphones available.*/
Microphone_Creator m_microphone_creator;
/** Loads the settings from path.*/ /** Loads the settings from path.*/
void LoadSettings(const std::string& title_id = ""); void LoadSettings(const std::string& title_id = "");
@ -320,6 +336,8 @@ private:
{ AudioBufferDuration, { "Audio", "Desired Audio Buffer Duration"}}, { AudioBufferDuration, { "Audio", "Desired Audio Buffer Duration"}},
{ EnableTimeStretching, { "Audio", "Enable Time Stretching"}}, { EnableTimeStretching, { "Audio", "Enable Time Stretching"}},
{ TimeStretchingThreshold, { "Audio", "Time Stretching Threshold"}}, { TimeStretchingThreshold, { "Audio", "Time Stretching Threshold"}},
{ MicrophoneType, { "Audio", "Microphone Type" }},
{ MicrophoneDevices, { "Audio", "Microphone Devices" }},
// Input / Output // Input / Output
{ PadHandler, { "Input/Output", "Pad"}}, { PadHandler, { "Input/Output", "Pad"}},

View File

@ -774,6 +774,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
ui->timeStretchingThresholdLabel->setEnabled(enabled); ui->timeStretchingThresholdLabel->setEnabled(enabled);
ui->timeStretchingThreshold->setEnabled(enabled); ui->timeStretchingThreshold->setEnabled(enabled);
}; };
auto EnableBufferingOptions = [this, EnableTimeStretchingOptions](bool enabled) auto EnableBufferingOptions = [this, EnableTimeStretchingOptions](bool enabled)
{ {
ui->audioBufferDuration->setEnabled(enabled); ui->audioBufferDuration->setEnabled(enabled);
@ -781,6 +782,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
ui->enableTimeStretching->setEnabled(enabled); ui->enableTimeStretching->setEnabled(enabled);
EnableTimeStretchingOptions(enabled && ui->enableTimeStretching->isChecked()); EnableTimeStretchingOptions(enabled && ui->enableTimeStretching->isChecked());
}; };
auto EnableBuffering = [this, EnableBufferingOptions](const QString& text) auto EnableBuffering = [this, EnableBufferingOptions](const QString& text)
{ {
const bool enabled = text == "XAudio2" || text == "OpenAL"; const bool enabled = text == "XAudio2" || text == "OpenAL";
@ -788,6 +790,84 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
EnableBufferingOptions(enabled && ui->enableBuffering->isChecked()); EnableBufferingOptions(enabled && ui->enableBuffering->isChecked());
}; };
auto ChangeMicrophoneType = [=](QString text)
{
std::string s_standard, s_singstar, s_realsingstar, s_rocksmith;
auto enableMicsCombo = [=](u32 max)
{
ui->microphone1Box->setEnabled(true);
if (max == 1 || ui->microphone1Box->currentText() == xemu_settings->m_microphone_creator.mic_none)
return;
ui->microphone2Box->setEnabled(true);
if (max > 2 && ui->microphone2Box->currentText() != xemu_settings->m_microphone_creator.mic_none)
{
ui->microphone3Box->setEnabled(true);
if (ui->microphone3Box->currentText() != xemu_settings->m_microphone_creator.mic_none)
{
ui->microphone4Box->setEnabled(true);
}
}
};
ui->microphone1Box->setEnabled(false);
ui->microphone2Box->setEnabled(false);
ui->microphone3Box->setEnabled(false);
ui->microphone4Box->setEnabled(false);
fmt_class_string<microphone_handler>::format(s_standard, static_cast<u64>(microphone_handler::standard));
fmt_class_string<microphone_handler>::format(s_singstar, static_cast<u64>(microphone_handler::singstar));
fmt_class_string<microphone_handler>::format(s_realsingstar, static_cast<u64>(microphone_handler::real_singstar));
fmt_class_string<microphone_handler>::format(s_rocksmith, static_cast<u64>(microphone_handler::rocksmith));
if (text == s_standard.c_str())
{
enableMicsCombo(4);
return;
}
if (text == s_singstar.c_str())
{
enableMicsCombo(2);
return;
}
if (text == s_realsingstar.c_str() || text == s_rocksmith.c_str())
{
enableMicsCombo(1);
return;
}
};
auto PropagateUsedDevices = [=]()
{
for (u32 index = 0; index < 4; index++)
{
const QString cur_item = mics_combo[index]->currentText();
QStringList cur_list = xemu_settings->m_microphone_creator.microphones_list;
for (u32 subindex = 0; subindex < 4; subindex++)
{
if (subindex != index && mics_combo[subindex]->currentText() != xemu_settings->m_microphone_creator.mic_none)
cur_list.removeOne(mics_combo[subindex]->currentText());
}
mics_combo[index]->blockSignals(true);
mics_combo[index]->clear();
mics_combo[index]->addItems(cur_list);
mics_combo[index]->setCurrentText(cur_item);
mics_combo[index]->blockSignals(false);
}
ChangeMicrophoneType(ui->microphoneBox->currentText());
};
auto ChangeMicrophoneDevice = [=](u32 next_index, QString text)
{
xemu_settings->SetSetting(emu_settings::MicrophoneDevices, xemu_settings->m_microphone_creator.SetDevice(next_index, text));
if (next_index < 4 && text == xemu_settings->m_microphone_creator.mic_none)
mics_combo[next_index]->setCurrentText(xemu_settings->m_microphone_creator.mic_none);
PropagateUsedDevices();
};
// Comboboxes // Comboboxes
xemu_settings->EnhanceComboBox(ui->audioOutBox, emu_settings::AudioRenderer); xemu_settings->EnhanceComboBox(ui->audioOutBox, emu_settings::AudioRenderer);
@ -800,6 +880,36 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
ui->audioOutBox->setItemText(ui->renderBox->findData("Null"), tr("Disable Audio Output")); ui->audioOutBox->setItemText(ui->renderBox->findData("Null"), tr("Disable Audio Output"));
connect(ui->audioOutBox, &QComboBox::currentTextChanged, EnableBuffering); connect(ui->audioOutBox, &QComboBox::currentTextChanged, EnableBuffering);
// Microphone Comboboxes
mics_combo[0] = ui->microphone1Box;
mics_combo[1] = ui->microphone2Box;
mics_combo[2] = ui->microphone3Box;
mics_combo[3] = ui->microphone4Box;
connect(mics_combo[0], &QComboBox::currentTextChanged, [=](const QString& text) { ChangeMicrophoneDevice(1, text); });
connect(mics_combo[1], &QComboBox::currentTextChanged, [=](const QString& text) { ChangeMicrophoneDevice(2, text); });
connect(mics_combo[2], &QComboBox::currentTextChanged, [=](const QString& text) { ChangeMicrophoneDevice(3, text); });
connect(mics_combo[3], &QComboBox::currentTextChanged, [=](const QString& text) { ChangeMicrophoneDevice(4, text); });
xemu_settings->m_microphone_creator.RefreshList();
PropagateUsedDevices(); // Fills comboboxes list
xemu_settings->m_microphone_creator.ParseDevices(xemu_settings->GetSetting(emu_settings::MicrophoneDevices));
for (s32 index = 3; index >= 0; index--)
{
if (xemu_settings->m_microphone_creator.sel_list[index] == "" || mics_combo[index]->findText(qstr(xemu_settings->m_microphone_creator.sel_list[index])) == -1)
{
mics_combo[index]->setCurrentText(xemu_settings->m_microphone_creator.mic_none);
ChangeMicrophoneDevice(index+1, xemu_settings->m_microphone_creator.mic_none); // Ensures the value is set in config
}
else
mics_combo[index]->setCurrentText(qstr(xemu_settings->m_microphone_creator.sel_list[index]));
}
xemu_settings->EnhanceComboBox(ui->microphoneBox, emu_settings::MicrophoneType);
SubscribeTooltip(ui->microphoneBox, json_audio["microphoneBox"].toString());
connect(ui->microphoneBox, &QComboBox::currentTextChanged, ChangeMicrophoneType);
PropagateUsedDevices(); // Enables/Disables comboboxes and checks values from config for sanity
// Checkboxes // Checkboxes
xemu_settings->EnhanceCheckBox(ui->audioDump, emu_settings::DumpToFile); xemu_settings->EnhanceCheckBox(ui->audioDump, emu_settings::DumpToFile);

View File

@ -1,4 +1,4 @@
#pragma once #pragma once
#include "gui_settings.h" #include "gui_settings.h"
#include "emu_settings.h" #include "emu_settings.h"
@ -36,13 +36,15 @@ private Q_SLOTS:
private: private:
void EnhanceSlider(emu_settings::SettingsType settings_type, QSlider* slider, QLabel* label, const QString& label_text); void EnhanceSlider(emu_settings::SettingsType settings_type, QSlider* slider, QLabel* label, const QString& label_text);
//emulator tab // Emulator tab
void AddConfigs(); void AddConfigs();
void AddStylesheets(); void AddStylesheets();
QString m_currentStylesheet; QString m_currentStylesheet;
QString m_currentConfig; QString m_currentConfig;
//gpu tab // Gpu tab
QString m_oldRender = ""; QString m_oldRender = "";
// Audio tab
QComboBox *mics_combo[4];
int m_tab_Index; int m_tab_Index;
Ui::settings_dialog *ui; Ui::settings_dialog *ui;
@ -53,7 +55,7 @@ private:
bool m_use_discord; bool m_use_discord;
QString m_discord_state; QString m_discord_state;
// descriptions // Descriptions
QList<QPair<QLabel*, QString>> m_description_labels; QList<QPair<QLabel*, QString>> m_description_labels;
QHash<QObject*, QString> m_descriptions; QHash<QObject*, QString> m_descriptions;
void SubscribeDescription(QLabel* description); void SubscribeDescription(QLabel* description);

View File

@ -780,7 +780,7 @@
<attribute name="title"> <attribute name="title">
<string>Audio</string> <string>Audio</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_26" stretch="0,1,0"> <layout class="QVBoxLayout" name="verticalLayout_26" stretch="0,0,1,0">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_9" stretch="1,1,1"> <layout class="QHBoxLayout" name="horizontalLayout_9" stretch="1,1,1">
<item> <item>
@ -966,6 +966,157 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>Microphone Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_76">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Microphone Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="microphoneBox"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<layout class="QVBoxLayout" name="verticalLayout_77">
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mic1:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mic3:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_78">
<item>
<widget class="QComboBox" name="microphone1Box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="microphone3Box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_79">
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mic2:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mic4:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_80">
<item>
<widget class="QComboBox" name="microphone2Box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="microphone4Box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer_19"> <spacer name="verticalSpacer_19">
<property name="orientation"> <property name="orientation">