mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 04:02:42 +01:00
Implement support for emulating Rock Band 3's MIDI Pro Adapter
Co-authored-by: Megamouse <studienricky89@googlemail.com>
This commit is contained in:
parent
8e15afb2c4
commit
24dde5d42b
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -84,3 +84,6 @@
|
||||
path = 3rdparty/miniupnp/miniupnp
|
||||
url = ../../miniupnp/miniupnp.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/rtmidi/rtmidi"]
|
||||
path = 3rdparty/rtmidi/rtmidi
|
||||
url = ../../thestk/rtmidi
|
||||
|
4
3rdparty/CMakeLists.txt
vendored
4
3rdparty/CMakeLists.txt
vendored
@ -363,6 +363,9 @@ endif()
|
||||
# MINIUPNP
|
||||
add_subdirectory(miniupnp EXCLUDE_FROM_ALL)
|
||||
|
||||
# RTMIDI
|
||||
add_subdirectory(rtmidi EXCLUDE_FROM_ALL)
|
||||
|
||||
# add nice ALIAS targets for ease of use
|
||||
if(USE_SYSTEM_LIBUSB)
|
||||
add_library(3rdparty::libusb ALIAS usb-1.0-shared)
|
||||
@ -391,3 +394,4 @@ add_library(3rdparty::libcurl ALIAS libcurl)
|
||||
add_library(3rdparty::soundtouch ALIAS soundtouch)
|
||||
add_library(3rdparty::sdl2 ALIAS ${SDL2_TARGET})
|
||||
add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static)
|
||||
add_library(3rdparty::rtmidi ALIAS rtmidi)
|
||||
|
1
3rdparty/rtmidi/CMakeLists.txt
vendored
Normal file
1
3rdparty/rtmidi/CMakeLists.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(rtmidi EXCLUDE_FROM_ALL)
|
1
3rdparty/rtmidi/rtmidi
vendored
Submodule
1
3rdparty/rtmidi/rtmidi
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 84a99422a3faf1ab417fe71c0903a48debb9376a
|
61
3rdparty/rtmidi/rtmidi.vcxproj
vendored
Normal file
61
3rdparty/rtmidi/rtmidi.vcxproj
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="rtmidi\RtMidi.h" />
|
||||
<ClInclude Include="rtmidi\rtmidi_c.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="rtmidi\RtMidi.cpp" />
|
||||
<ClCompile Include="rtmidi\rtmidi_c.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}</ProjectGuid>
|
||||
<RootNamespace>rtmidi</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>__WINDOWS_MM__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./rtmidi/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -23,9 +23,9 @@ namespace cfg
|
||||
{
|
||||
for (const auto& node : owner->m_nodes)
|
||||
{
|
||||
if (node->get_name() == name)
|
||||
if (node->get_name() == m_name)
|
||||
{
|
||||
cfg_log.fatal("Node already exists: %s", name);
|
||||
cfg_log.fatal("Node already exists: %s", m_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asmjit", "3rdparty\asmjit\a
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emucore", "rpcs3\emucore.vcxproj", "{C4A10229-4712-4BD2-B63E-50D93C67A038}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3} = {2C902C67-985C-4BE0-94A3-E0FE2EB929A3}
|
||||
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404} = {939FE206-1182-ABC3-1234-FEAB88E98404}
|
||||
EndProjectSection
|
||||
@ -41,6 +42,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml-cpp", "3rdparty\yaml-c
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxproj", "{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3} = {2C902C67-985C-4BE0-94A3-E0FE2EB929A3}
|
||||
{3384223A-6D97-4799-9862-359F85312892} = {3384223A-6D97-4799-9862-359F85312892}
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {349EE8F9-7D25-4909-AAF5-FF3FADE72187}
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D} = {3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}
|
||||
@ -96,6 +98,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pine", "pine", "{A55DA1B5-C
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_static", "3rdparty\miniupnp\miniupnpc_static.vcxproj", "{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtmidi", "3rdparty\rtmidi\rtmidi.vcxproj", "{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@ -194,6 +198,10 @@ Global
|
||||
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Debug|x64.Build.0 = Debug|x64
|
||||
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.ActiveCfg = Release|x64
|
||||
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.Build.0 = Release|x64
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}.Debug|x64.Build.0 = Debug|x64
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}.Release|x64.ActiveCfg = Release|x64
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -224,6 +232,7 @@ Global
|
||||
{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{A55DA1B5-CC17-4525-BE7F-1659CE17BB56} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{2C902C67-985C-4BE0-94A3-E0FE2EB929A3} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
|
||||
|
@ -398,11 +398,18 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Io/Infinity.cpp
|
||||
Io/Skylander.cpp
|
||||
Io/GHLtar.cpp
|
||||
Io/midi_config_types.cpp
|
||||
Io/RB3MidiKeyboard.cpp
|
||||
Io/RB3MidiGuitar.cpp
|
||||
Io/Buzz.cpp
|
||||
Io/Turntable.cpp
|
||||
Io/usio.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(rpcs3_emu PRIVATE
|
||||
3rdparty::rtmidi
|
||||
)
|
||||
|
||||
# Np
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
NP/fb_helpers.cpp
|
||||
|
@ -20,7 +20,10 @@
|
||||
#include "Emu/Io/GHLtar.h"
|
||||
#include "Emu/Io/Buzz.h"
|
||||
#include "Emu/Io/Turntable.h"
|
||||
#include "Emu/Io/RB3MidiKeyboard.h"
|
||||
#include "Emu/Io/RB3MidiGuitar.h"
|
||||
#include "Emu/Io/usio.h"
|
||||
#include "Emu/Io/midi_config_types.h"
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
@ -315,6 +318,28 @@ usb_handler_thread::usb_handler_thread()
|
||||
usb_devices.push_back(std::make_shared<usb_device_usio>(get_new_location()));
|
||||
}
|
||||
|
||||
const std::vector<std::string> devices_list = fmt::split(g_cfg.io.midi_devices.to_string(), { "@@@" });
|
||||
for (usz index = 0; index < std::min(max_midi_devices, devices_list.size()); index++)
|
||||
{
|
||||
const midi_device device = midi_device::from_string(::at32(devices_list, index));
|
||||
if (device.name.empty()) continue;
|
||||
|
||||
sys_usbd.notice("Adding Emulated Midi Pro Adapter (type=%s, name=%s)", device.type, device.name);
|
||||
|
||||
switch (device.type)
|
||||
{
|
||||
case midi_device_type::guitar:
|
||||
usb_devices.push_back(std::make_shared<usb_device_rb3_midi_guitar>(get_new_location(), device.name, false));
|
||||
break;
|
||||
case midi_device_type::guitar_22fret:
|
||||
usb_devices.push_back(std::make_shared<usb_device_rb3_midi_guitar>(get_new_location(), device.name, true));
|
||||
break;
|
||||
case midi_device_type::keyboard:
|
||||
usb_devices.push_back(std::make_shared<usb_device_rb3_midi_keyboard>(get_new_location(), device.name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_cfg.io.ghltar == ghltar_handler::one_controller || g_cfg.io.ghltar == ghltar_handler::two_controllers)
|
||||
{
|
||||
sys_usbd.notice("Adding emulated GHLtar (1 player)");
|
||||
|
308
rpcs3/Emu/Io/RB3MidiGuitar.cpp
Normal file
308
rpcs3/Emu/Io/RB3MidiGuitar.cpp
Normal file
@ -0,0 +1,308 @@
|
||||
// Rock Band 3 MIDI Pro Adapter Emulator (Guitar Mode)
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "RB3MidiGuitar.h"
|
||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||
|
||||
LOG_CHANNEL(rb3_midi_guitar_log);
|
||||
|
||||
usb_device_rb3_midi_guitar::usb_device_rb3_midi_guitar(const std::array<u8, 7>& location, const std::string& device_name, bool twentytwo_fret)
|
||||
: usb_device_emulated(location)
|
||||
{
|
||||
// For the 22-fret guitar (Fender Squier), the only thing that's different
|
||||
// is the device ID reported by the MIDI Pro Adapter.
|
||||
//
|
||||
// Everything else is *exactly* the same as the 17-fret guitar (Fender Mustang).
|
||||
if (twentytwo_fret)
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 64, 0x12ba, 0x2538, 0x01, 0x01, 0x02, 0x00, 0x01});
|
||||
}
|
||||
else
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 64, 0x12ba, 0x2438, 0x01, 0x01, 0x02, 0x00, 0x01});
|
||||
}
|
||||
|
||||
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{41, 1, 1, 0, 0x80, 32}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0, 0, 2, 3, 0, 0, 0}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{0x0111, 0x00, 0x01, 0x22, 137}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x03, 0x0040, 10}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x02, 0x03, 0x0040, 10}));
|
||||
|
||||
usb_device_emulated::add_string("Licensed by Sony Computer Entertainment America");
|
||||
usb_device_emulated::add_string("Harmonix RB3 MIDI Guitar Interface for PlayStation®3");
|
||||
|
||||
// connect to midi device
|
||||
midi_in = rtmidi_in_create_default();
|
||||
ensure(midi_in);
|
||||
|
||||
rb3_midi_guitar_log.notice("Using %s API", rtmidi_api_name(rtmidi_in_get_current_api(midi_in)));
|
||||
|
||||
if (!midi_in->ok)
|
||||
{
|
||||
rb3_midi_guitar_log.error("Could not get MIDI in ptr: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
rtmidi_in_ignore_types(midi_in, false, true, true);
|
||||
|
||||
const s32 port_count = rtmidi_get_port_count(midi_in);
|
||||
|
||||
if (port_count == -1)
|
||||
{
|
||||
rb3_midi_guitar_log.error("Could not get MIDI port count: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
for (s32 port_number = 0; port_number < port_count; port_number++)
|
||||
{
|
||||
char buf[128]{};
|
||||
s32 size = sizeof(buf);
|
||||
if (rtmidi_get_port_name(midi_in, port_number, buf, &size) == -1)
|
||||
{
|
||||
rb3_midi_guitar_log.error("Error getting port name for port %d: %s", port_number, midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
rb3_midi_guitar_log.notice("Found device with name: %s", buf);
|
||||
|
||||
if (device_name == buf)
|
||||
{
|
||||
rtmidi_open_port(midi_in, port_number, "RPCS3 MIDI Guitar Input");
|
||||
rb3_midi_guitar_log.success("Connected to device: %s", device_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb3_midi_guitar_log.error("Could not find device with name: %s", device_name);
|
||||
}
|
||||
|
||||
usb_device_rb3_midi_guitar::~usb_device_rb3_midi_guitar()
|
||||
{
|
||||
rtmidi_in_free(midi_in);
|
||||
}
|
||||
|
||||
static const std::array<u8, 40> disabled_response = {
|
||||
0xe9, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0f, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x82,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x26, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static const std::array<u8, 40> enabled_response = {
|
||||
0xe9, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x8a,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x26, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
void usb_device_rb3_midi_guitar::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||
{
|
||||
transfer->fake = true;
|
||||
|
||||
// configuration packets sent by rock band 3
|
||||
// we only really need to check 1 byte here to figure out if the game
|
||||
// wants to enable midi data or disable it
|
||||
if (bmRequestType == 0x21 && bRequest == 0x9 && wLength == 40)
|
||||
{
|
||||
if (buf_size < 3)
|
||||
{
|
||||
rb3_midi_guitar_log.warning("buffer size < 3, bailing out early (buf_size=0x%x)", buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (buf[2])
|
||||
{
|
||||
case 0x89:
|
||||
rb3_midi_guitar_log.notice("MIDI data enabled.");
|
||||
buttons_enabled = true;
|
||||
response_pos = 0;
|
||||
break;
|
||||
case 0x81:
|
||||
rb3_midi_guitar_log.notice("MIDI data disabled.");
|
||||
buttons_enabled = false;
|
||||
response_pos = 0;
|
||||
break;
|
||||
default:
|
||||
rb3_midi_guitar_log.warning("Unhandled SET_REPORT request: 0x%02X");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// the game expects some sort of response to the configuration packet
|
||||
else if (bmRequestType == 0xa1 && bRequest == 0x1)
|
||||
{
|
||||
transfer->expected_count = buf_size;
|
||||
if (buttons_enabled)
|
||||
{
|
||||
const usz remaining_bytes = enabled_response.size() - response_pos;
|
||||
const usz copied_bytes = std::min<usz>(remaining_bytes, buf_size);
|
||||
memcpy(buf, &enabled_response[response_pos], copied_bytes);
|
||||
response_pos += copied_bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
const usz remaining_bytes = disabled_response.size() - response_pos;
|
||||
const usz copied_bytes = std::min<usz>(remaining_bytes, buf_size);
|
||||
memcpy(buf, &disabled_response[response_pos], copied_bytes);
|
||||
response_pos += copied_bytes;
|
||||
}
|
||||
}
|
||||
else if (bmRequestType == 0x21 && bRequest == 0x9 && wLength == 8)
|
||||
{
|
||||
// the game uses this request to do things like set the LEDs
|
||||
// we don't have any LEDs, so do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_guitar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/, UsbTransfer* transfer)
|
||||
{
|
||||
transfer->fake = true;
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
// the real device takes 8ms to send a response, but there is
|
||||
// no reason we can't make it faster
|
||||
transfer->expected_time = get_timestamp() + 1'000;
|
||||
|
||||
|
||||
// default input state
|
||||
const std::array<u8, 27> bytes = {
|
||||
0x00, 0x00, 0x08, 0x80, 0x80, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||
0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00};
|
||||
|
||||
if (buf_size < bytes.size())
|
||||
{
|
||||
rb3_midi_guitar_log.warning("buffer size < %x, bailing out early (buf_size=0x%x)", bytes.size(), buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buf, bytes.data(), bytes.size());
|
||||
|
||||
while (true)
|
||||
{
|
||||
u8 midi_msg[32];
|
||||
usz size = sizeof(midi_msg);
|
||||
|
||||
// this returns a double as some sort of delta time, with -1.0
|
||||
// being used to signal an error
|
||||
if (rtmidi_in_get_message(midi_in, midi_msg, &size) == -1.0)
|
||||
{
|
||||
rb3_midi_guitar_log.error("Error getting MIDI message: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
parse_midi_message(midi_msg, size);
|
||||
}
|
||||
|
||||
write_state(buf);
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_guitar::parse_midi_message(u8* msg, usz size)
|
||||
{
|
||||
// this is not emulated correctly but the game doesn't seem to care
|
||||
button_state.count++;
|
||||
|
||||
// read frets
|
||||
if (size == 8 && msg[0] == 0xF0 && msg[4] == 0x01)
|
||||
{
|
||||
switch (msg[5])
|
||||
{
|
||||
case 1:
|
||||
button_state.frets[0] = msg[6] - 0x40;
|
||||
break;
|
||||
case 2:
|
||||
button_state.frets[1] = msg[6] - 0x3B;
|
||||
break;
|
||||
case 3:
|
||||
button_state.frets[2] = msg[6] - 0x37;
|
||||
break;
|
||||
case 4:
|
||||
button_state.frets[3] = msg[6] - 0x32;
|
||||
break;
|
||||
case 5:
|
||||
button_state.frets[4] = msg[6] - 0x2D;
|
||||
break;
|
||||
case 6:
|
||||
button_state.frets[5] = msg[6] - 0x28;
|
||||
break;
|
||||
default:
|
||||
rb3_midi_guitar_log.warning("Invalid string for fret event: %d", msg[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// read strings
|
||||
if (size == 8 && msg[0] == 0xF0 && msg[4] == 0x05)
|
||||
{
|
||||
button_state.string_velocities[msg[5] - 1] = msg[6];
|
||||
}
|
||||
|
||||
// read buttons
|
||||
if (size == 10 && msg[0] == 0xF0 && msg[4] == 0x08)
|
||||
{
|
||||
button_state.dpad = msg[7] & 0x0f;
|
||||
|
||||
button_state.square = (msg[5] & 0b0000'0001) == 0b0000'0001;
|
||||
button_state.cross = (msg[5] & 0b0000'0010) == 0b0000'0010;
|
||||
button_state.circle = (msg[5] & 0b0000'0100) == 0b0000'0100;
|
||||
button_state.triangle = (msg[5] & 0b0000'1000) == 0b0000'1000;
|
||||
|
||||
button_state.select = (msg[6] & 0b0000'0001) == 0b0000'0001;
|
||||
button_state.start = (msg[6] & 0b0000'0010) == 0b0000'0010;
|
||||
button_state.tilt_sensor = (msg[7] & 0b0100'0000) == 0b0100'0000;
|
||||
}
|
||||
|
||||
// sustain pedal
|
||||
if (size == 3 && msg[0] == 0xB0 && msg[1] == 0x40)
|
||||
{
|
||||
button_state.sustain_pedal = msg[2] >= 40;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_guitar::write_state(u8* buf)
|
||||
{
|
||||
// encode frets
|
||||
buf[8] |= (button_state.frets[0] & 0b11111) << 2;
|
||||
buf[8] |= (button_state.frets[1] & 0b11000) >> 3;
|
||||
buf[7] |= (button_state.frets[1] & 0b00111) << 5;
|
||||
buf[7] |= (button_state.frets[2] & 0b11111) >> 0;
|
||||
buf[6] |= (button_state.frets[3] & 0b11111) << 2;
|
||||
buf[6] |= (button_state.frets[4] & 0b11000) >> 3;
|
||||
buf[5] |= (button_state.frets[4] & 0b00111) << 5;
|
||||
buf[5] |= (button_state.frets[5] & 0b11111) >> 0;
|
||||
|
||||
// encode strings
|
||||
buf[14] = button_state.string_velocities[0];
|
||||
buf[13] = button_state.string_velocities[1];
|
||||
buf[12] = button_state.string_velocities[2];
|
||||
buf[11] = button_state.string_velocities[3];
|
||||
buf[10] = button_state.string_velocities[4];
|
||||
buf[9] = button_state.string_velocities[5];
|
||||
|
||||
// encode tilt sensor/sustain_pedal
|
||||
if (button_state.tilt_sensor || button_state.sustain_pedal)
|
||||
{
|
||||
buf[15] = 0x7f;
|
||||
buf[16] = 0x7f;
|
||||
buf[17] = 0x7f;
|
||||
}
|
||||
|
||||
buf[1] |= 0b0000'0001 * button_state.select;
|
||||
buf[1] |= 0b0000'0010 * button_state.start;
|
||||
|
||||
buf[0] |= 0b0000'0010 * button_state.cross;
|
||||
buf[0] |= 0b0000'0100 * button_state.circle;
|
||||
buf[0] |= 0b0000'1000 * button_state.triangle;
|
||||
buf[0] |= 0b0000'0001 * button_state.square;
|
||||
|
||||
buf[2] = button_state.dpad;
|
||||
}
|
44
rpcs3/Emu/Io/RB3MidiGuitar.h
Normal file
44
rpcs3/Emu/Io/RB3MidiGuitar.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/usb_device.h"
|
||||
|
||||
#include <rtmidi_c.h>
|
||||
|
||||
class usb_device_rb3_midi_guitar : public usb_device_emulated
|
||||
{
|
||||
private:
|
||||
u32 response_pos = 0;
|
||||
bool buttons_enabled = false;
|
||||
RtMidiInPtr midi_in{};
|
||||
|
||||
// button states
|
||||
struct
|
||||
{
|
||||
u8 count = 0;
|
||||
|
||||
bool cross = false;
|
||||
bool circle = false;
|
||||
bool square = false;
|
||||
bool triangle = false;
|
||||
|
||||
bool start = false;
|
||||
bool select = false;
|
||||
bool tilt_sensor = false;
|
||||
bool sustain_pedal = false; // used for overdrive
|
||||
|
||||
u8 dpad = 8;
|
||||
|
||||
std::array<u8, 6> frets{};
|
||||
std::array<u8, 6> string_velocities{};
|
||||
} button_state;
|
||||
|
||||
void parse_midi_message(u8* msg, usz size);
|
||||
void write_state(u8* buf);
|
||||
|
||||
public:
|
||||
usb_device_rb3_midi_guitar(const std::array<u8, 7>& location, const std::string& device_name, bool twentytwo_fret);
|
||||
~usb_device_rb3_midi_guitar();
|
||||
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
};
|
333
rpcs3/Emu/Io/RB3MidiKeyboard.cpp
Normal file
333
rpcs3/Emu/Io/RB3MidiKeyboard.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
// Rock Band 3 MIDI Pro Adapter Emulator (Keyboard Mode)
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "RB3MidiKeyboard.h"
|
||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||
|
||||
LOG_CHANNEL(rb3_midi_keyboard_log);
|
||||
|
||||
usb_device_rb3_midi_keyboard::usb_device_rb3_midi_keyboard(const std::array<u8, 7>& location, const std::string& device_name)
|
||||
: usb_device_emulated(location)
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 64, 0x12ba, 0x2338, 0x01, 0x01, 0x02, 0x00, 0x01});
|
||||
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{41, 1, 1, 0, 0x80, 32}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0, 0, 2, 3, 0, 0, 0}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{0x0111, 0x00, 0x01, 0x22, 137}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x03, 0x0040, 10}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x02, 0x03, 0x0040, 10}));
|
||||
|
||||
usb_device_emulated::add_string("Licensed by Sony Computer Entertainment America");
|
||||
usb_device_emulated::add_string("Harmonix RB3 MIDI Keyboard Interface for PlayStation®3");
|
||||
|
||||
// connect to midi device
|
||||
midi_in = rtmidi_in_create_default();
|
||||
ensure(midi_in);
|
||||
|
||||
rb3_midi_keyboard_log.notice("Using %s API", rtmidi_api_name(rtmidi_in_get_current_api(midi_in)));
|
||||
|
||||
if (!midi_in->ok)
|
||||
{
|
||||
rb3_midi_keyboard_log.error("Could not get MIDI in ptr: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const s32 port_count = rtmidi_get_port_count(midi_in);
|
||||
|
||||
if (port_count == -1)
|
||||
{
|
||||
rb3_midi_keyboard_log.error("Could not get MIDI port count: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
for (s32 port_number = 0; port_number < port_count; port_number++)
|
||||
{
|
||||
char buf[128]{};
|
||||
s32 size = sizeof(buf);
|
||||
if (rtmidi_get_port_name(midi_in, port_number, buf, &size) == -1)
|
||||
{
|
||||
rb3_midi_keyboard_log.error("Error getting port name for port %d: %s", port_number, midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
rb3_midi_keyboard_log.notice("Found device with name: %s", buf);
|
||||
|
||||
if (device_name == buf)
|
||||
{
|
||||
rtmidi_open_port(midi_in, port_number, "RPCS3 MIDI Keyboard Input");
|
||||
rb3_midi_keyboard_log.success("Connected to device: %s", device_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb3_midi_keyboard_log.error("Could not find device with name: %s", device_name);
|
||||
}
|
||||
|
||||
usb_device_rb3_midi_keyboard::~usb_device_rb3_midi_keyboard()
|
||||
{
|
||||
rtmidi_in_free(midi_in);
|
||||
}
|
||||
|
||||
static const std::array<u8, 40> disabled_response = {
|
||||
0xe9, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x82,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x26, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static const std::array<u8, 40> enabled_response = {
|
||||
0xe9, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x8a,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x21, 0x26, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
void usb_device_rb3_midi_keyboard::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||
{
|
||||
transfer->fake = true;
|
||||
|
||||
// configuration packets sent by rock band 3
|
||||
// we only really need to check 1 byte here to figure out if the game
|
||||
// wants to enable midi data or disable it
|
||||
if (bmRequestType == 0x21 && bRequest == 0x9 && wLength == 40)
|
||||
{
|
||||
if (buf_size < 3)
|
||||
{
|
||||
rb3_midi_keyboard_log.warning("buffer size < 3, bailing out early (buf_size=0x%x)", buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (buf[2])
|
||||
{
|
||||
case 0x89:
|
||||
rb3_midi_keyboard_log.notice("MIDI data enabled.");
|
||||
buttons_enabled = true;
|
||||
response_pos = 0;
|
||||
break;
|
||||
case 0x81:
|
||||
rb3_midi_keyboard_log.notice("MIDI data disabled.");
|
||||
buttons_enabled = false;
|
||||
response_pos = 0;
|
||||
break;
|
||||
default:
|
||||
rb3_midi_keyboard_log.warning("Unhandled SET_REPORT request: 0x%02X");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// the game expects some sort of response to the configuration packet
|
||||
else if (bmRequestType == 0xa1 && bRequest == 0x1)
|
||||
{
|
||||
transfer->expected_count = buf_size;
|
||||
if (buttons_enabled)
|
||||
{
|
||||
const usz remaining_bytes = enabled_response.size() - response_pos;
|
||||
const usz copied_bytes = std::min<usz>(remaining_bytes, buf_size);
|
||||
memcpy(buf, &enabled_response[response_pos], copied_bytes);
|
||||
response_pos += copied_bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
const usz remaining_bytes = disabled_response.size() - response_pos;
|
||||
const usz copied_bytes = std::min<usz>(remaining_bytes, buf_size);
|
||||
memcpy(buf, &disabled_response[response_pos], copied_bytes);
|
||||
response_pos += copied_bytes;
|
||||
}
|
||||
}
|
||||
else if (bmRequestType == 0x21 && bRequest == 0x9 && wLength == 8)
|
||||
{
|
||||
// the game uses this request to do things like set the LEDs
|
||||
// we don't have any LEDs, so do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_keyboard::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/, UsbTransfer* transfer)
|
||||
{
|
||||
transfer->fake = true;
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
// the real device takes 8ms to send a response, but there is
|
||||
// no reason we can't make it faster
|
||||
transfer->expected_time = get_timestamp() + 1'000;
|
||||
|
||||
// default input state
|
||||
static const std::array<u8, 27> bytes = {
|
||||
0x00, 0x00, 0x08, 0x80, 0x80, 0x80, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||
0x02, 0x00, 0x02};
|
||||
|
||||
if (buf_size < bytes.size())
|
||||
{
|
||||
rb3_midi_keyboard_log.warning("buffer size < %x, bailing out early (buf_size=0x%x)", bytes.size(), buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buf, bytes.data(), bytes.size());
|
||||
|
||||
while (true)
|
||||
{
|
||||
u8 midi_msg[32];
|
||||
usz size = sizeof(midi_msg);
|
||||
|
||||
// this returns a double as some sort of delta time, with -1.0
|
||||
// being used to signal an error
|
||||
if (rtmidi_in_get_message(midi_in, midi_msg, &size) == -1.0)
|
||||
{
|
||||
rb3_midi_keyboard_log.error("Error getting MIDI message: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
parse_midi_message(midi_msg, size);
|
||||
}
|
||||
|
||||
write_state(buf);
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size)
|
||||
{
|
||||
// this is not emulated correctly but the game doesn't seem to care
|
||||
button_state.count++;
|
||||
|
||||
// handle note on/off messages
|
||||
if (size == 3 && (msg[0] == 0x80 || msg[0] == 0x90))
|
||||
{
|
||||
// handle navigation buttons
|
||||
switch (msg[1])
|
||||
{
|
||||
case 44: // G#2
|
||||
button_state.cross = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 42: // F#2
|
||||
button_state.circle = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 39: // D#2
|
||||
button_state.square = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 37: // C#2
|
||||
button_state.triangle = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 46: // A#2
|
||||
button_state.start = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 36: // C2
|
||||
button_state.select = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 45: // A2
|
||||
button_state.overdrive = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 41: // F2
|
||||
button_state.dpad_up = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 43: // G2
|
||||
button_state.dpad_down = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 38: // D2
|
||||
button_state.dpad_left = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
case 40: // E2
|
||||
button_state.dpad_right = ((0x10 & msg[0]) == 0x10);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// handle keyboard keys
|
||||
if (msg[1] >= 48 && msg[1] <= (48 + button_state.keys.size()))
|
||||
{
|
||||
const u32 key = msg[1] - 48;
|
||||
button_state.keys[key] = ((0x10 & msg[0]) == 0x10);
|
||||
button_state.velocities[key] = msg[2];
|
||||
}
|
||||
}
|
||||
|
||||
// control channel for overdrive
|
||||
else if (size == 3 && msg[0] == 0xB0)
|
||||
{
|
||||
switch (msg[1])
|
||||
{
|
||||
case 0x1:
|
||||
case 0x40:
|
||||
button_state.overdrive = msg[2] > 40;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pitch wheel
|
||||
else if (size == 3 && msg[0] == 0xE0)
|
||||
{
|
||||
const u16 msb = msg[2];
|
||||
const u16 lsb = msg[1];
|
||||
button_state.pitch_wheel = (msb << 7) | lsb;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_rb3_midi_keyboard::write_state(u8* buf)
|
||||
{
|
||||
// buttons
|
||||
buf[0] |= 0b0000'0010 * button_state.cross;
|
||||
buf[0] |= 0b0000'0100 * button_state.circle;
|
||||
buf[0] |= 0b0000'0001 * button_state.square;
|
||||
buf[0] |= 0b0000'1000 * button_state.triangle;
|
||||
buf[1] |= 0b0000'0010 * button_state.start;
|
||||
buf[1] |= 0b0000'0001 * button_state.select;
|
||||
|
||||
// dpad
|
||||
if (button_state.dpad_up)
|
||||
{
|
||||
buf[2] = 0;
|
||||
}
|
||||
else if (button_state.dpad_down)
|
||||
{
|
||||
buf[2] = 4;
|
||||
}
|
||||
else if (button_state.dpad_left)
|
||||
{
|
||||
buf[2] = 6;
|
||||
}
|
||||
else if (button_state.dpad_right)
|
||||
{
|
||||
buf[2] = 2;
|
||||
}
|
||||
|
||||
// build key bitfield and write velocities
|
||||
u32 key_mask = 0;
|
||||
u8 vel_idx = 0;
|
||||
|
||||
for (usz i = 0; i < button_state.keys.size(); i++)
|
||||
{
|
||||
key_mask <<= 1;
|
||||
key_mask |= 0x1 * button_state.keys[i];
|
||||
|
||||
// the keyboard can only report 5 velocities from left to right
|
||||
if (button_state.keys[i] && vel_idx < 5)
|
||||
{
|
||||
buf[8 + vel_idx++] = button_state.velocities[i];
|
||||
}
|
||||
}
|
||||
|
||||
// write keys
|
||||
buf[5] = (key_mask >> 17) & 0xff;
|
||||
buf[6] = (key_mask >> 9) & 0xff;
|
||||
buf[7] = (key_mask >> 1) & 0xff;
|
||||
buf[8] |= 0b1000'0000 * (key_mask & 0x1);
|
||||
|
||||
// overdrive
|
||||
buf[13] |= 0b1000'0000 * button_state.overdrive;
|
||||
|
||||
// pitch wheel
|
||||
const u8 wheel_pos = std::abs((button_state.pitch_wheel >> 6) - 0x80);
|
||||
if (wheel_pos >= 5)
|
||||
{
|
||||
buf[15] = std::min<u8>(std::max<u8>(0x5, wheel_pos), 0x75);
|
||||
}
|
||||
}
|
49
rpcs3/Emu/Io/RB3MidiKeyboard.h
Normal file
49
rpcs3/Emu/Io/RB3MidiKeyboard.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/usb_device.h"
|
||||
|
||||
#include <rtmidi_c.h>
|
||||
|
||||
class usb_device_rb3_midi_keyboard : public usb_device_emulated
|
||||
{
|
||||
private:
|
||||
u32 response_pos = 0;
|
||||
bool buttons_enabled = false;
|
||||
RtMidiInPtr midi_in{};
|
||||
|
||||
// button states
|
||||
// TODO: emulate velocity
|
||||
struct
|
||||
{
|
||||
u8 count = 0;
|
||||
|
||||
bool cross = false;
|
||||
bool circle = false;
|
||||
bool square = false;
|
||||
bool triangle = false;
|
||||
|
||||
bool start = false;
|
||||
bool select = false;
|
||||
bool overdrive = false;
|
||||
|
||||
bool dpad_up = false;
|
||||
bool dpad_down = false;
|
||||
bool dpad_left = false;
|
||||
bool dpad_right = false;
|
||||
|
||||
std::array<bool, 25> keys{};
|
||||
std::array<u8, 25> velocities{};
|
||||
|
||||
s16 pitch_wheel = 0;
|
||||
} button_state;
|
||||
|
||||
void parse_midi_message(u8* msg, usz size);
|
||||
void write_state(u8* buf);
|
||||
|
||||
public:
|
||||
usb_device_rb3_midi_keyboard(const std::array<u8, 7>& location, const std::string& device_name);
|
||||
~usb_device_rb3_midi_keyboard();
|
||||
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
};
|
49
rpcs3/Emu/Io/midi_config_types.cpp
Normal file
49
rpcs3/Emu/Io/midi_config_types.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "stdafx.h"
|
||||
#include "midi_config_types.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/Config.h"
|
||||
|
||||
template <>
|
||||
void fmt_class_string<midi_device_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](midi_device_type value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case midi_device_type::guitar: return "Guitar (17 frets)";
|
||||
case midi_device_type::guitar_22fret: return "Guitar (22 frets)";
|
||||
case midi_device_type::keyboard: return "Keyboard";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<midi_device>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const midi_device& obj = get_object(arg);
|
||||
fmt::append(out, "%sßßß%s", obj.type, obj.name);
|
||||
}
|
||||
|
||||
midi_device midi_device::from_string(const std::string& str)
|
||||
{
|
||||
midi_device res{};
|
||||
|
||||
if (const std::vector<std::string> parts = fmt::split(str, {"ßßß"}); !parts.empty())
|
||||
{
|
||||
u64 result;
|
||||
|
||||
if (cfg::try_to_enum_value(&result, &fmt_class_string<midi_device_type>::format, ::at32(parts, 0)))
|
||||
{
|
||||
res.type = static_cast<midi_device_type>(static_cast<std::underlying_type_t<midi_device_type>>(result));
|
||||
}
|
||||
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
res.name = ::at32(parts, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
20
rpcs3/Emu/Io/midi_config_types.h
Normal file
20
rpcs3/Emu/Io/midi_config_types.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
static constexpr usz max_midi_devices = 3;
|
||||
|
||||
enum class midi_device_type
|
||||
{
|
||||
keyboard,
|
||||
guitar,
|
||||
guitar_22fret,
|
||||
};
|
||||
|
||||
struct midi_device
|
||||
{
|
||||
midi_device_type type{};
|
||||
std::string name;
|
||||
|
||||
static midi_device from_string(const std::string& str);
|
||||
};
|
@ -247,7 +247,7 @@ u32 usb_device_emulated::get_descriptor(u8 type, u8 index, u8* buf, u32 buf_size
|
||||
const std::u16string u16str = utf8_to_utf16(strings[index - 1]);
|
||||
const u8 len = static_cast<u8>(std::min(u16str.size() * sizeof(u16) + 2, static_cast<usz>(0xFF)));
|
||||
buf[0] = len;
|
||||
expected_count = std::min(len, ::narrow<u8>(buf_size));
|
||||
expected_count = std::min(len, ::narrow<u8>(std::min<u32>(255, buf_size)));
|
||||
memcpy(buf + 2, u16str.data(), expected_count - 2);
|
||||
}
|
||||
}
|
||||
@ -316,7 +316,7 @@ void usb_device_emulated::isochronous_transfer(UsbTransfer* transfer)
|
||||
{
|
||||
}
|
||||
|
||||
void usb_device_emulated::add_string(char* str)
|
||||
void usb_device_emulated::add_string(std::string str)
|
||||
{
|
||||
strings.emplace_back(str);
|
||||
strings.emplace_back(std::move(str));
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ public:
|
||||
void isochronous_transfer(UsbTransfer* transfer) override;
|
||||
|
||||
// Emulated specific functions
|
||||
void add_string(char* str);
|
||||
void add_string(std::string str);
|
||||
u32 get_descriptor(u8 type, u8 index, u8* buf, u32 buf_size);
|
||||
u32 get_status(bool self_powered, bool remote_wakeup, u8* buf, u32 buf_size);
|
||||
|
||||
|
@ -276,6 +276,7 @@ struct cfg_root : cfg::node
|
||||
cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true};
|
||||
cfg::_bool background_input_enabled{this, "Background input enabled", true, true};
|
||||
cfg::_bool show_move_cursor{this, "Show move cursor", false, true};
|
||||
cfg::string midi_devices{ this, "Emulated Midi devices", "ßßß@@@ßßß@@@ßßß@@@" };
|
||||
} io{ this };
|
||||
|
||||
struct node_sys : cfg::node
|
||||
|
@ -40,7 +40,7 @@
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\zlib\zlib;..\3rdparty\llvm\llvm\include;..\3rdparty\llvm\llvm\llvm\include;..\3rdparty\llvm\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;..\3rdparty\llvm\llvm\include;..\3rdparty\llvm\llvm\llvm\include;..\3rdparty\llvm\llvm_build\include;$(VULKAN_SDK)\Include</AdditionalIncludeDirectories>
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -69,6 +69,9 @@
|
||||
<ClCompile Include="Emu\Cell\Modules\HLE_PATCHES.cpp" />
|
||||
<ClCompile Include="Emu\games_config.cpp" />
|
||||
<ClCompile Include="Emu\Io\camera_config.cpp" />
|
||||
<ClCompile Include="Emu\Io\midi_config_types.cpp" />
|
||||
<ClCompile Include="Emu\Io\RB3MidiGuitar.cpp" />
|
||||
<ClCompile Include="Emu\Io\RB3MidiKeyboard.cpp" />
|
||||
<ClCompile Include="Emu\Io\recording_config.cpp" />
|
||||
<ClCompile Include="Emu\Io\Turntable.cpp" />
|
||||
<ClCompile Include="Emu\Io\GHLtar.cpp" />
|
||||
@ -503,9 +506,12 @@
|
||||
<ClInclude Include="Emu\games_config.h" />
|
||||
<ClInclude Include="Emu\Io\camera_config.h" />
|
||||
<ClInclude Include="Emu\Io\camera_handler_base.h" />
|
||||
<ClInclude Include="Emu\Io\midi_config_types.h" />
|
||||
<ClInclude Include="Emu\Io\music_handler_base.h" />
|
||||
<ClInclude Include="Emu\Io\Null\null_camera_handler.h" />
|
||||
<ClInclude Include="Emu\Io\Null\null_music_handler.h" />
|
||||
<ClInclude Include="Emu\Io\RB3MidiGuitar.h" />
|
||||
<ClInclude Include="Emu\Io\RB3MidiKeyboard.h" />
|
||||
<ClInclude Include="Emu\Io\recording_config.h" />
|
||||
<ClInclude Include="Emu\Io\Turntable.h" />
|
||||
<ClInclude Include="Emu\Io\GHLtar.h" />
|
||||
|
@ -1153,6 +1153,15 @@
|
||||
<ClCompile Include="Emu\games_config.cpp">
|
||||
<Filter>Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\RB3MidiGuitar.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\RB3MidiKeyboard.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\midi_config_types.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -2326,6 +2335,15 @@
|
||||
<ClInclude Include="Emu\config_mode.h">
|
||||
<Filter>Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\RB3MidiGuitar.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\RB3MidiKeyboard.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\midi_config_types.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||
|
@ -71,7 +71,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>release\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -89,7 +89,7 @@
|
||||
<ExternalWarningLevel>TurnOffAllWarnings</ExternalWarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -123,7 +123,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>debug\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -140,7 +140,7 @@
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -723,6 +723,7 @@
|
||||
<ClCompile Include="rpcs3qt\localized.cpp" />
|
||||
<ClCompile Include="rpcs3qt\log_viewer.cpp" />
|
||||
<ClCompile Include="rpcs3qt\microphone_creator.cpp" />
|
||||
<ClCompile Include="rpcs3qt\midi_creator.cpp" />
|
||||
<ClCompile Include="rpcs3qt\movie_item.cpp" />
|
||||
<ClCompile Include="rpcs3qt\movie_item_base.cpp" />
|
||||
<ClCompile Include="rpcs3qt\osk_dialog_frame.cpp" />
|
||||
@ -1312,6 +1313,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\midi_creator.h" />
|
||||
<ClInclude Include="rpcs3qt\movie_item.h" />
|
||||
<ClInclude Include="rpcs3qt\movie_item_base.h" />
|
||||
<ClInclude Include="rpcs3qt\numbered_widget_item.h" />
|
||||
|
@ -1023,6 +1023,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_elf_memory_dumping_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\midi_creator.cpp">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -1199,6 +1202,9 @@
|
||||
<ClInclude Include="rpcs3qt\movie_item_base.h">
|
||||
<Filter>Gui\custom items</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\midi_creator.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -51,6 +51,7 @@ set(SRC_FILES
|
||||
memory_string_searcher.cpp
|
||||
memory_viewer_panel.cpp
|
||||
microphone_creator.cpp
|
||||
midi_creator.cpp
|
||||
movie_item.cpp
|
||||
movie_item_base.cpp
|
||||
msg_dialog_frame.cpp
|
||||
@ -156,4 +157,5 @@ target_link_libraries(rpcs3_ui
|
||||
3rdparty::libpng
|
||||
3rdparty::7z
|
||||
3rdparty::wolfssl
|
||||
3rdparty::libcurl)
|
||||
3rdparty::libcurl
|
||||
3rdparty::rtmidi)
|
||||
|
@ -1277,6 +1277,14 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
|
||||
case stereo_render_mode_options::over_under: return tr("Over-under", "3D Display Mode");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::MidiDevices:
|
||||
switch (static_cast<midi_device_type>(index))
|
||||
{
|
||||
case midi_device_type::guitar: return tr("Guitar (17 frets)", "Midi Device Type");
|
||||
case midi_device_type::guitar_22fret: return tr("Guitar (22 frets)", "Midi Device Type");
|
||||
case midi_device_type::keyboard: return tr("Keyboard", "Midi Device Type");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include "microphone_creator.h"
|
||||
#include "midi_creator.h"
|
||||
#include "render_creator.h"
|
||||
#include "emu_settings_type.h"
|
||||
|
||||
@ -80,6 +81,9 @@ public:
|
||||
/** Gets a list of all the microphones available.*/
|
||||
microphone_creator m_microphone_creator;
|
||||
|
||||
/** Gets a list of all the midi devices available.*/
|
||||
midi_creator m_midi_creator;
|
||||
|
||||
/** Loads the settings from path.*/
|
||||
void LoadSettings(const std::string& title_id = "");
|
||||
|
||||
|
@ -155,6 +155,7 @@ enum class emu_settings_type
|
||||
Buzz,
|
||||
Turntable,
|
||||
GHLtar,
|
||||
MidiDevices,
|
||||
|
||||
// Misc
|
||||
ExitRPCS3OnFinish,
|
||||
@ -335,6 +336,7 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
|
||||
{ emu_settings_type::Buzz, { "Input/Output", "Buzz emulated controller" }},
|
||||
{ emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }},
|
||||
{ emu_settings_type::GHLtar, { "Input/Output", "GHLtar emulated controller" }},
|
||||
{ emu_settings_type::MidiDevices, { "Input/Output", "Emulated Midi devices" }},
|
||||
|
||||
// Misc
|
||||
{ emu_settings_type::ExitRPCS3OnFinish, { "Miscellaneous", "Exit RPCS3 when process finishes" }},
|
||||
|
@ -69,13 +69,10 @@ std::string microphone_creator::set_device(u32 num, const QString& text)
|
||||
|
||||
void microphone_creator::parse_devices(const std::string& list)
|
||||
{
|
||||
for (u32 index = 0; index < 4; index++)
|
||||
{
|
||||
m_sel_list[index] = "";
|
||||
}
|
||||
m_sel_list = {};
|
||||
|
||||
const auto devices_list = fmt::split(list, { "@@@" });
|
||||
for (u32 index = 0; index < std::min<u32>(4, ::size32(devices_list)); index++)
|
||||
for (u32 index = 0; index < std::min<u32>(m_sel_list.size(), ::size32(devices_list)); index++)
|
||||
{
|
||||
m_sel_list[index] = devices_list[index];
|
||||
}
|
||||
|
103
rpcs3/rpcs3qt/midi_creator.cpp
Normal file
103
rpcs3/rpcs3qt/midi_creator.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include "stdafx.h"
|
||||
#include "midi_creator.h"
|
||||
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include <rtmidi_c.h>
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
midi_creator::midi_creator()
|
||||
{
|
||||
refresh_list();
|
||||
}
|
||||
|
||||
// We need to recreate the localized string because the midi creator is currently only created once.
|
||||
QString midi_creator::get_none()
|
||||
{
|
||||
return tr("None", "Midi device");
|
||||
}
|
||||
|
||||
void midi_creator::refresh_list()
|
||||
{
|
||||
m_midi_list.clear();
|
||||
m_midi_list.append(get_none());
|
||||
|
||||
RtMidiInPtr midi_in = rtmidi_in_create_default();
|
||||
ensure(midi_in);
|
||||
|
||||
cfg_log.notice("MIDI: Using %s api", rtmidi_api_name(rtmidi_in_get_current_api(midi_in)));
|
||||
|
||||
if (!midi_in->ok)
|
||||
{
|
||||
cfg_log.error("Could not get MIDI in ptr: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const s32 port_count = rtmidi_get_port_count(midi_in);
|
||||
|
||||
if (port_count == -1)
|
||||
{
|
||||
cfg_log.error("Could not get MIDI port count: %s", midi_in->msg);
|
||||
return;
|
||||
}
|
||||
|
||||
for (s32 port_number = 0; port_number < port_count; port_number++)
|
||||
{
|
||||
char buf[128]{};
|
||||
s32 size = sizeof(buf);
|
||||
if (rtmidi_get_port_name(midi_in, port_number, buf, &size) == -1)
|
||||
{
|
||||
cfg_log.error("Error getting midi port name for port %d: %s", port_number, midi_in->msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
cfg_log.notice("Found midi device with name: %s", buf);
|
||||
m_midi_list.append(QString::fromUtf8(buf));
|
||||
}
|
||||
|
||||
rtmidi_in_free(midi_in);
|
||||
}
|
||||
|
||||
QStringList midi_creator::get_midi_list() const
|
||||
{
|
||||
return m_midi_list;
|
||||
}
|
||||
|
||||
std::array<midi_device, max_midi_devices> midi_creator::get_selection_list() const
|
||||
{
|
||||
return m_sel_list;
|
||||
}
|
||||
|
||||
std::string midi_creator::set_device(u32 num, const midi_device& device)
|
||||
{
|
||||
ensure(num < m_sel_list.size());
|
||||
|
||||
m_sel_list[num] = device;
|
||||
|
||||
if (device.name == get_none().toStdString())
|
||||
{
|
||||
m_sel_list[num].name.clear();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
for (const midi_device& device : m_sel_list)
|
||||
{
|
||||
fmt::append(result, "%s@@@", device);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void midi_creator::parse_devices(const std::string& list)
|
||||
{
|
||||
m_sel_list = {};
|
||||
|
||||
const std::vector<std::string> devices_list = fmt::split(list, { "@@@" });
|
||||
for (usz index = 0; index < std::min(m_sel_list.size(), devices_list.size()); index++)
|
||||
{
|
||||
m_sel_list[index] = midi_device::from_string(devices_list[index]);
|
||||
}
|
||||
}
|
23
rpcs3/rpcs3qt/midi_creator.h
Normal file
23
rpcs3/rpcs3qt/midi_creator.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/midi_config_types.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class midi_creator : public QObject
|
||||
{
|
||||
public:
|
||||
midi_creator();
|
||||
QString get_none();
|
||||
std::string set_device(u32 num, const midi_device& device);
|
||||
void parse_devices(const std::string& list);
|
||||
void refresh_list();
|
||||
QStringList get_midi_list() const;
|
||||
std::array<midi_device, max_midi_devices> get_selection_list() const;
|
||||
|
||||
private:
|
||||
QStringList m_midi_list;
|
||||
std::array<midi_device, max_midi_devices> m_sel_list;
|
||||
};
|
@ -980,7 +980,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
const auto change_microphone_device = [mic_none, propagate_used_devices, this](u32 index, const QString& text)
|
||||
{
|
||||
m_emu_settings->SetSetting(emu_settings_type::MicrophoneDevices, m_emu_settings->m_microphone_creator.set_device(index, text));
|
||||
if (const u32 next_index = index + 1; next_index < 4 && text == mic_none)
|
||||
if (const u32 next_index = index + 1; next_index < m_mics_combo.size() && text == mic_none)
|
||||
m_mics_combo[next_index]->setCurrentText(mic_none);
|
||||
propagate_used_devices();
|
||||
};
|
||||
@ -1133,7 +1133,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
|
||||
const std::array<std::string, 4> mic_sel_list = m_emu_settings->m_microphone_creator.get_selection_list();
|
||||
|
||||
for (s32 index = 3; index >= 0; index--)
|
||||
for (s32 index = static_cast<int>(mic_sel_list.size()) - 1; index >= 0; index--)
|
||||
{
|
||||
const QString qmic = qstr(mic_sel_list[index]);
|
||||
|
||||
@ -1257,6 +1257,102 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
m_emu_settings->EnhanceCheckBox(ui->showMoveCursorBox, emu_settings_type::ShowMoveCursor);
|
||||
SubscribeTooltip(ui->showMoveCursorBox, tooltips.settings.show_move_cursor);
|
||||
|
||||
// Midi
|
||||
const QString midi_none = m_emu_settings->m_midi_creator.get_none();
|
||||
const midi_device def_midi_device{ .type = midi_device_type::keyboard, .name = midi_none.toStdString() };
|
||||
const std::vector<std::string> midi_device_types = cfg::try_to_enum_list(&fmt_class_string<midi_device_type>::format);
|
||||
|
||||
m_midi_type_combo[0] = ui->midiTypeBox1;
|
||||
m_midi_type_combo[1] = ui->midiTypeBox2;
|
||||
m_midi_type_combo[2] = ui->midiTypeBox3;
|
||||
m_midi_device_combo[0] = ui->midiDeviceBox1;
|
||||
m_midi_device_combo[1] = ui->midiDeviceBox2;
|
||||
m_midi_device_combo[2] = ui->midiDeviceBox3;
|
||||
|
||||
SubscribeTooltip(ui->gb_midi_1, tooltips.settings.midi_devices);
|
||||
SubscribeTooltip(ui->gb_midi_2, tooltips.settings.midi_devices);
|
||||
SubscribeTooltip(ui->gb_midi_3, tooltips.settings.midi_devices);
|
||||
|
||||
const auto propagate_midi_devices = [midi_none, this]()
|
||||
{
|
||||
for (u32 index = 0; index < m_midi_device_combo.size(); index++)
|
||||
{
|
||||
const QString cur_item = m_midi_device_combo[index]->currentText();
|
||||
QStringList cur_list = m_emu_settings->m_midi_creator.get_midi_list();
|
||||
for (u32 subindex = 0; subindex < m_midi_device_combo.size(); subindex++)
|
||||
{
|
||||
if (subindex != index && m_midi_device_combo[subindex]->currentText() != midi_none)
|
||||
cur_list.removeOne(m_midi_device_combo[subindex]->currentText());
|
||||
}
|
||||
m_midi_device_combo[index]->blockSignals(true);
|
||||
m_midi_device_combo[index]->clear();
|
||||
m_midi_device_combo[index]->addItems(cur_list);
|
||||
m_midi_device_combo[index]->setCurrentText(cur_item);
|
||||
m_midi_device_combo[index]->blockSignals(false);
|
||||
}
|
||||
};
|
||||
|
||||
const auto change_midi_device = [propagate_midi_devices, this](u32 index, const midi_device& device)
|
||||
{
|
||||
m_emu_settings->SetSetting(emu_settings_type::MidiDevices, m_emu_settings->m_midi_creator.set_device(index, device));
|
||||
propagate_midi_devices();
|
||||
};
|
||||
|
||||
for (u32 i = 0; i < m_midi_type_combo.size(); i++)
|
||||
{
|
||||
m_midi_type_combo[i]->blockSignals(true);
|
||||
for (const std::string& type_str : midi_device_types)
|
||||
{
|
||||
midi_device_type type{};
|
||||
if (u64 result; cfg::try_to_enum_value(&result, &fmt_class_string<midi_device_type>::format, type_str))
|
||||
{
|
||||
type = static_cast<midi_device_type>(static_cast<std::underlying_type_t<midi_device_type>>(result));
|
||||
}
|
||||
const QString type_name = m_emu_settings->GetLocalizedSetting(QString::fromStdString(fmt::format("%s", type)), emu_settings_type::MidiDevices, static_cast<int>(type), false);
|
||||
m_midi_type_combo[i]->addItem(type_name, static_cast<int>(type));
|
||||
}
|
||||
m_midi_type_combo[i]->blockSignals(false);
|
||||
|
||||
connect(m_midi_type_combo[i], &QComboBox::currentTextChanged, this, [this, change_midi_device, i](const QString& /*text*/)
|
||||
{
|
||||
const midi_device device{ .type = static_cast<midi_device_type>(m_midi_type_combo[i]->currentData().toInt()), .name = m_midi_device_combo[i]->currentText().toStdString() };
|
||||
change_midi_device(i, device);
|
||||
});
|
||||
connect(m_midi_device_combo[i], &QComboBox::currentTextChanged, this, [this, change_midi_device, i](const QString& text)
|
||||
{
|
||||
const midi_device device{ .type = static_cast<midi_device_type>(m_midi_type_combo[i]->currentData().toInt()), .name = text.toStdString() };
|
||||
change_midi_device(i, device);
|
||||
});
|
||||
connect(this, &settings_dialog::signal_restore_dependant_defaults, this, [change_midi_device, i, def_midi_device]() { change_midi_device(i, def_midi_device); });
|
||||
}
|
||||
|
||||
m_emu_settings->m_midi_creator.refresh_list();
|
||||
propagate_midi_devices(); // Fills comboboxes list
|
||||
|
||||
m_emu_settings->m_midi_creator.parse_devices(m_emu_settings->GetSetting(emu_settings_type::MidiDevices));
|
||||
|
||||
const std::array<midi_device, max_midi_devices> midi_sel_list = m_emu_settings->m_midi_creator.get_selection_list();
|
||||
|
||||
for (s32 index = static_cast<int>(midi_sel_list.size()) - 1; index >= 0; index--)
|
||||
{
|
||||
const midi_device& device = midi_sel_list[index];
|
||||
const QString qmidi = QString::fromStdString(device.name);
|
||||
|
||||
m_midi_type_combo[index]->setCurrentIndex(m_midi_type_combo[index]->findData(static_cast<int>(device.type)));
|
||||
|
||||
if (qmidi.isEmpty() || m_midi_device_combo[index]->findText(qmidi) == -1)
|
||||
{
|
||||
m_midi_device_combo[index]->setCurrentText(midi_none);
|
||||
change_midi_device(index, def_midi_device); // Ensures the value is set in config
|
||||
}
|
||||
else
|
||||
{
|
||||
m_midi_device_combo[index]->setCurrentText(qmidi);
|
||||
}
|
||||
}
|
||||
|
||||
propagate_midi_devices(); // Enables/Disables comboboxes and checks values from config for sanity
|
||||
|
||||
// _____ _ _______ _
|
||||
// / ____| | | |__ __| | |
|
||||
// | (___ _ _ ___| |_ ___ _ __ ___ | | __ _| |__
|
||||
|
@ -45,6 +45,9 @@ private:
|
||||
QString m_old_renderer;
|
||||
// Audio tab
|
||||
std::array<QComboBox*, 4> m_mics_combo;
|
||||
// IO tab
|
||||
std::array<QComboBox*, 3> m_midi_type_combo;
|
||||
std::array<QComboBox*, 3> m_midi_device_combo;
|
||||
|
||||
int m_tab_index;
|
||||
std::unique_ptr<Ui::settings_dialog> ui;
|
||||
|
@ -43,8 +43,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>838</width>
|
||||
<height>641</height>
|
||||
<width>821</width>
|
||||
<height>716</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -940,12 +940,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_additional_settings">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Additional Settings</string>
|
||||
</property>
|
||||
@ -1613,19 +1607,7 @@
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="inputTab_layout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="ioGridLayout">
|
||||
<item row="0" column="2">
|
||||
<widget class="QGroupBox" name="gb_buzz_emulated">
|
||||
<property name="title">
|
||||
<string>Buzz! emulated controller</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_buzz_emulated_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="buzzBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="ioGridLayout" columnstretch="1,1,1">
|
||||
<item row="2" column="2">
|
||||
<widget class="QGroupBox" name="gb_ghltar_emulated">
|
||||
<property name="title">
|
||||
@ -1638,14 +1620,14 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QGroupBox" name="gb_camera_flip">
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="gb_move_handler">
|
||||
<property name="title">
|
||||
<string>Camera Flip</string>
|
||||
<string>Move Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_camera_flip_layout">
|
||||
<layout class="QVBoxLayout" name="gb_move_handler_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cameraFlipBox"/>
|
||||
<widget class="QComboBox" name="moveBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -1662,6 +1644,30 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="gb_mouse_handler">
|
||||
<property name="title">
|
||||
<string>Mouse Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_mouse_handler_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="mouseHandlerBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QGroupBox" name="gb_buzz_emulated">
|
||||
<property name="title">
|
||||
<string>Buzz! emulated controller</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_buzz_emulated_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="buzzBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="gb_keyboard_handler">
|
||||
<property name="title">
|
||||
@ -1686,14 +1692,26 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="gb_mouse_handler">
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="gb_camera_type">
|
||||
<property name="title">
|
||||
<string>Mouse Handler</string>
|
||||
<string>Camera Input</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_mouse_handler_layout">
|
||||
<layout class="QVBoxLayout" name="gb_camera_type_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="mouseHandlerBox"/>
|
||||
<widget class="QComboBox" name="cameraTypeBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QGroupBox" name="gb_camera_flip">
|
||||
<property name="title">
|
||||
<string>Camera Flip</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_camera_flip_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cameraFlipBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -1710,30 +1728,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="gb_camera_type">
|
||||
<property name="title">
|
||||
<string>Camera Input</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_camera_type_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cameraTypeBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="gb_move_handler">
|
||||
<property name="title">
|
||||
<string>Move Handler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_move_handler_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="moveBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="gb_camera_setting">
|
||||
<property name="title">
|
||||
@ -1746,7 +1740,64 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="3" column="2">
|
||||
<widget class="QGroupBox" name="gb_midi_1">
|
||||
<property name="title">
|
||||
<string>Emulated Midi device 1</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="midLayout1">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="midiHLayout1" stretch="1,2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiTypeBox1"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiDeviceBox1"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QGroupBox" name="gb_midi_3">
|
||||
<property name="title">
|
||||
<string>Emulated Midi device 3</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="midLayout3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="midiHLayout3" stretch="1,2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiTypeBox3"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiDeviceBox3"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QGroupBox" name="gb_midi_2">
|
||||
<property name="title">
|
||||
<string>Emulated Midi device 2</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="midLayout2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="midiHLayout2" stretch="1,2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiTypeBox2"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="midiDeviceBox2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" rowspan="2">
|
||||
<widget class="QGroupBox" name="gb_additional_io_settings">
|
||||
<property name="title">
|
||||
<string>Additional Settings</string>
|
||||
@ -1766,6 +1817,22 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacerIoAdditionalSettings">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -223,6 +223,7 @@ public:
|
||||
const QString ghltar = tr("Guitar Hero Live (GHL) Guitar controller support.\nSelect 1 or 2 controllers if the game requires GHL Guitar controllers and you don't have real guitar controllers.\nSelect Null if the game has support for DualShock or if you have real guitar controllers.\nA real guitar controller can be used at the same time as an emulated guitar controller.");
|
||||
const QString background_input = tr("Allows pad and keyboard input while the game window is unfocused.");
|
||||
const QString show_move_cursor = tr("Shows the raw position of the PS Move input.\nThis can be very helpful during calibration screens.");
|
||||
const QString midi_devices = tr("Select up to 3 emulated midi devices and their type.");
|
||||
|
||||
// network
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user