USB: Add DJ hero turntable emulation

This commit is contained in:
Sanjay Govind 2023-07-01 21:46:41 +12:00 committed by Connor McLaughlin
parent 28a197b8cd
commit f91f257a7d
7 changed files with 598 additions and 1 deletions

View File

@ -437,6 +437,7 @@ set(pcsx2USBSources
USB/usb-pad/usb-pad-ff.cpp
USB/usb-pad/usb-pad.cpp
USB/usb-pad/usb-seamic.cpp
USB/usb-pad/usb-turntable.cpp
USB/usb-printer/usb-printer.cpp
)

View File

@ -21,6 +21,7 @@
#include "usb-mic/usb-mic-singstar.h"
#include "usb-msd/usb-msd.h"
#include "usb-pad/usb-pad.h"
#include "usb-pad/usb-turntable.h"
#include "usb-printer/usb-printer.h"
#include "usb-lightgun/guncon2.h"
@ -82,6 +83,7 @@ void RegisterDevice::Register()
inst.Add(DEVTYPE_HIDKBD, new usb_hid::HIDKbdDevice());
inst.Add(DEVTYPE_HIDMOUSE, new usb_hid::HIDMouseDevice());
inst.Add(DEVTYPE_RBKIT, new usb_pad::RBDrumKitDevice());
inst.Add(DEVTYPE_DJ, new usb_pad::DJTurntableDevice());
inst.Add(DEVTYPE_BUZZ, new usb_pad::BuzzDevice());
inst.Add(DEVTYPE_EYETOY, new usb_eyetoy::EyeToyWebCamDevice());
inst.Add(DEVTYPE_BEATMANIA_DADADA, new usb_hid::BeatManiaDevice());

View File

@ -50,7 +50,8 @@ enum DeviceType : s32
DEVTYPE_SEGA_SEAMIC,
DEVTYPE_PRINTER,
DEVTYPE_KEYBOARDMANIA,
DEVTYPE_GUNCON2
DEVTYPE_GUNCON2,
DEVTYPE_DJ
};
class DeviceProxy

View File

@ -0,0 +1,476 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Host.h"
#include "Input/InputManager.h"
#include "StateWrapper.h"
#include "USB/USB.h"
#include "USB/deviceproxy.h"
#include "USB/qemu-usb/USBinternal.h"
#include "USB/qemu-usb/desc.h"
#include "USB/usb-pad/usb-turntable.h"
#include "USB/usb-pad/usb-pad.h"
namespace usb_pad
{
static const USBDescStrings desc_strings = {
"",
"RedOctane DJ",
"Guitar Hero5 for PlayStation (R) 3"};
// Should be usb 2.0, but seems to make no difference with DJ Hero games
static const uint8_t dev_descriptor[] = {
/* bLength */ 0x12, //(18)
/* bDescriptorType */ 0x01, //(1)
/* bcdUSB */ WBVAL(0x0110), //USB 1.1
/* bDeviceClass */ 0x00, //(0)
/* bDeviceSubClass */ 0x00, //(0)
/* bDeviceProtocol */ 0x00, //(0)
/* bMaxPacketSize0 */ 0x40, //(64)
/* idVendor */ WBVAL(0x12ba),
/* idProduct */ WBVAL(0x0140),
/* bcdDevice */ WBVAL(0x1000), //(10.00)
/* iManufacturer */ 0x01, //(1)
/* iProduct */ 0x02, //(2)
/* iSerialNumber */ 0x00, //(0)
/* bNumConfigurations */ 0x01, //(1)
};
TurntableState::TurntableState(u32 port_)
: port(port_)
{
turntable_multiplier = 1;
}
TurntableState::~TurntableState() = default;
int TurntableState::TokenIn(u8* buf, int len)
{
std::memset(buf, 0, 8);
UpdateHatSwitch();
u32* buttons = reinterpret_cast<u32*>(buf);
*buttons = (data.buttons & ((1 << (CID_DJ_START + 1)) - 1));
*buttons |= (data.hatswitch & 0xF) << 16;
// Map platter buttons to standard buttons for menus
if (data.buttons & (1 << CID_DJ_LEFT_GREEN) || data.buttons & (1 << CID_DJ_RIGHT_GREEN))
{
*buttons |= (1 << CID_DJ_CROSS);
}
if (data.buttons & (1 << CID_DJ_LEFT_RED) || data.buttons & (1 << CID_DJ_RIGHT_RED))
{
*buttons |= (1 << CID_DJ_CIRCLE);
}
if (data.buttons & (1 << CID_DJ_LEFT_BLUE) || data.buttons & (1 << CID_DJ_RIGHT_BLUE))
{
*buttons |= (1 << CID_DJ_SQUARE);
}
// Crossfader and Effects Knob are put into "accelerometer" bits in the PS3 reports
// So they need to be scaled to 1024
u16 crossfader = 0x0200;
u16 effectsknob = 0x0200;
if (data.crossfader_left > 0)
{
crossfader -= data.crossfader_left;
}
else
{
crossfader += data.crossfader_right;
}
if (data.effectsknob_left > 0)
{
effectsknob -= data.effectsknob_left;
}
else
{
effectsknob += data.effectsknob_right;
}
u8 left_turntable = 0x80;
u8 right_turntable = 0x80;
if (data.left_turntable_up > 0)
{
left_turntable -= static_cast<u8>(std::min<int>(data.left_turntable_up * turntable_multiplier, 0x7F));
}
else
{
left_turntable += static_cast<u8>(std::min<int>(data.left_turntable_down * turntable_multiplier, 0x7F));
}
if (data.right_turntable_up > 0)
{
right_turntable -= static_cast<u8>(std::min<int>(data.right_turntable_up * turntable_multiplier, 0x7F));
}
else
{
right_turntable += static_cast<u8>(std::min<int>(data.right_turntable_down * turntable_multiplier, 0x7F));
}
buf[3] = 0x80;
buf[4] = 0x80;
buf[5] = left_turntable;
buf[6] = right_turntable;
buf[19] = effectsknob & 0xFF;
buf[20] = effectsknob >> 8;
buf[21] = crossfader & 0xFF;
buf[22] = crossfader >> 8;
// Platter buttons
buf[23] = (data.buttons >> CID_DJ_RIGHT_GREEN) & 0xFF;
buf[24] = 0x02;
buf[26] = 0x02;
return len;
}
void TurntableState::UpdateSettings(SettingsInterface& si, const char* devname)
{
turntable_multiplier = USB::GetConfigInt(si, port, devname, "TurntableMultiplier", 1);
}
void TurntableState::UpdateHatSwitch()
{
if (data.hat_up && data.hat_right)
data.hatswitch = 1;
else if (data.hat_right && data.hat_down)
data.hatswitch = 3;
else if (data.hat_down && data.hat_left)
data.hatswitch = 5;
else if (data.hat_left && data.hat_up)
data.hatswitch = 7;
else if (data.hat_up)
data.hatswitch = 0;
else if (data.hat_right)
data.hatswitch = 2;
else if (data.hat_down)
data.hatswitch = 4;
else if (data.hat_left)
data.hatswitch = 6;
else
data.hatswitch = 8;
}
void TurntableState::SetEuphoriaLedState(bool state)
{
data.euphoria_led_state = state;
}
static void turntable_handle_control(USBDevice* dev, USBPacket* p,
int request, int value, int index, int length, uint8_t* data)
{
TurntableState* s = USB_CONTAINER_OF(dev, TurntableState, dev);
int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0)
{
return;
}
switch (request)
{
case SET_REPORT:
if (data[0] == 0x91) {
s->SetEuphoriaLedState(data[2]);
}
break;
case SET_IDLE:
break;
default:
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0)
{
return;
}
p->status = USB_RET_STALL;
break;
}
}
static void turntable_handle_data(USBDevice* dev, USBPacket* p)
{
TurntableState* s = USB_CONTAINER_OF(dev, TurntableState, dev);
switch (p->pid)
{
case USB_TOKEN_IN:
if (p->ep->nr == 1)
{
int ret = s->TokenIn(p->buffer_ptr, p->buffer_size);
if (ret > 0)
p->actual_length += std::min<u32>(static_cast<u32>(ret), p->buffer_size);
else
p->status = ret;
}
else
{
goto fail;
}
break;
case USB_TOKEN_OUT:
break;
default:
fail:
p->status = USB_RET_STALL;
break;
}
}
static void turntable_unrealize(USBDevice* dev)
{
TurntableState* s = USB_CONTAINER_OF(dev, TurntableState, dev);
delete s;
}
const char* DJTurntableDevice::Name() const
{
return TRANSLATE_NOOP("USB", "DJ Hero Turntable");
}
const char* DJTurntableDevice::TypeName() const
{
return "DJTurntable";
}
USBDevice* DJTurntableDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const
{
TurntableState* s = new TurntableState(port);
s->desc.full = &s->desc_dev;
s->desc.str = desc_strings;
if (usb_desc_parse_dev(dev_descriptor, sizeof(dev_descriptor), s->desc, s->desc_dev) < 0)
goto fail;
// PS3 / wii instruments all share the same config descriptors and hid reports
if (usb_desc_parse_config(rb1_config_descriptor, sizeof(rb1_config_descriptor), s->desc_dev) < 0)
goto fail;
s->dev.speed = USB_SPEED_FULL;
s->dev.klass.handle_attach = usb_desc_attach;
s->dev.klass.handle_reset = nullptr;
s->dev.klass.handle_control = turntable_handle_control;
s->dev.klass.handle_data = turntable_handle_data;
s->dev.klass.unrealize = turntable_unrealize;
s->dev.klass.usb_desc = &s->desc;
s->dev.klass.product_desc = nullptr;
usb_desc_init(&s->dev);
usb_ep_init(&s->dev);
return &s->dev;
fail:
turntable_unrealize(&s->dev);
return nullptr;
}
void DJTurntableDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const
{
USB_CONTAINER_OF(dev, TurntableState, dev)->UpdateSettings(si, TypeName());
}
float DJTurntableDevice::GetBindingValue(const USBDevice* dev, u32 bind_index) const
{
const TurntableState* s = USB_CONTAINER_OF(dev, const TurntableState, dev);
switch (bind_index)
{
case CID_DJ_CROSSFADER_LEFT:
return static_cast<float>(s->data.crossfader_left) / 512.0f;
case CID_DJ_CROSSFADER_RIGHT:
return static_cast<float>(s->data.crossfader_right) / 512.0f;
case CID_DJ_EFFECTSKNOB_LEFT:
return static_cast<float>(s->data.effectsknob_left) / 512.0f;
case CID_DJ_EFFECTSKNOB_RIGHT:
return static_cast<float>(s->data.effectsknob_right) / 512.0f;
case CID_DJ_LEFT_TURNTABLE_UP:
return static_cast<float>(s->data.left_turntable_up) / 255.0f;
case CID_DJ_LEFT_TURNTABLE_DOWN:
return static_cast<float>(s->data.left_turntable_down) / 255.0f;
case CID_DJ_RIGHT_TURNTABLE_UP:
return static_cast<float>(s->data.right_turntable_up) / 255.0f;
case CID_DJ_RIGHT_TURNTABLE_DOWN:
return static_cast<float>(s->data.right_turntable_down) / 255.0f;
case CID_DJ_DPAD_UP:
return static_cast<float>(s->data.hat_up);
case CID_DJ_DPAD_DOWN:
return static_cast<float>(s->data.hat_down);
case CID_DJ_DPAD_LEFT:
return static_cast<float>(s->data.hat_left);
case CID_DJ_DPAD_RIGHT:
return static_cast<float>(s->data.hat_right);
case CID_DJ_SQUARE:
case CID_DJ_CROSS:
case CID_DJ_CIRCLE:
case CID_DJ_TRIANGLE:
case CID_DJ_SELECT:
case CID_DJ_START:
case CID_DJ_RIGHT_GREEN:
case CID_DJ_RIGHT_RED:
case CID_DJ_RIGHT_BLUE:
case CID_DJ_LEFT_GREEN:
case CID_DJ_LEFT_RED:
case CID_DJ_LEFT_BLUE:
{
const u32 mask = (1u << bind_index);
return ((s->data.buttons & mask) != 0u) ? 1.0f : 0.0f;
}
default:
return 0.0f;
}
}
void DJTurntableDevice::SetBindingValue(USBDevice* dev, u32 bind_index, float value) const
{
TurntableState* s = USB_CONTAINER_OF(dev, TurntableState, dev);
switch (bind_index)
{
case CID_DJ_CROSSFADER_LEFT:
s->data.crossfader_left = static_cast<u32>(std::clamp<long>(std::lroundf(value * 512.0f), 0, 512));
break;
case CID_DJ_CROSSFADER_RIGHT:
s->data.crossfader_right = static_cast<u32>(std::clamp<long>(std::lroundf(value * 512.0f), 0, 512));
break;
case CID_DJ_EFFECTSKNOB_LEFT:
s->data.effectsknob_left = static_cast<u32>(std::clamp<long>(std::lroundf(value * 512.0f), 0, 512));
break;
case CID_DJ_EFFECTSKNOB_RIGHT:
s->data.effectsknob_right = static_cast<u32>(std::clamp<long>(std::lroundf(value * 512.0f), 0, 512));
break;
case CID_DJ_LEFT_TURNTABLE_UP:
s->data.left_turntable_up = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
break;
case CID_DJ_LEFT_TURNTABLE_DOWN:
s->data.left_turntable_down = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
break;
case CID_DJ_RIGHT_TURNTABLE_UP:
s->data.right_turntable_up = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
break;
case CID_DJ_RIGHT_TURNTABLE_DOWN:
s->data.right_turntable_down = static_cast<u32>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
break;
case CID_DJ_DPAD_UP:
s->data.hat_up = static_cast<u8>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
s->UpdateHatSwitch();
break;
case CID_DJ_DPAD_DOWN:
s->data.hat_down = static_cast<u8>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
s->UpdateHatSwitch();
break;
case CID_DJ_DPAD_LEFT:
s->data.hat_left = static_cast<u8>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
s->UpdateHatSwitch();
break;
case CID_DJ_DPAD_RIGHT:
s->data.hat_right = static_cast<u8>(std::clamp<long>(std::lroundf(value * 255.0f), 0, 255));
s->UpdateHatSwitch();
break;
case CID_DJ_SQUARE:
case CID_DJ_CROSS:
case CID_DJ_CIRCLE:
case CID_DJ_TRIANGLE:
case CID_DJ_SELECT:
case CID_DJ_START:
case CID_DJ_RIGHT_GREEN:
case CID_DJ_RIGHT_RED:
case CID_DJ_RIGHT_BLUE:
case CID_DJ_LEFT_GREEN:
case CID_DJ_LEFT_RED:
case CID_DJ_LEFT_BLUE:
{
const u32 mask = (1u << bind_index);
if (value >= 0.5f)
s->data.buttons |= mask;
else
s->data.buttons &= ~mask;
}
break;
default:
break;
}
}
gsl::span<const InputBindingInfo> DJTurntableDevice::Bindings(u32 subtype) const
{
static constexpr const InputBindingInfo bindings[] = {
{"DPadUp", TRANSLATE_NOOP("USB", "D-Pad Up"), InputBindingInfo::Type::Button, CID_DJ_DPAD_UP, GenericInputBinding::DPadUp},
{"DPadDown", TRANSLATE_NOOP("USB", "D-Pad Down"), InputBindingInfo::Type::Button, CID_DJ_DPAD_DOWN, GenericInputBinding::DPadDown},
{"DPadLeft", TRANSLATE_NOOP("USB", "D-Pad Left"), InputBindingInfo::Type::Button, CID_DJ_DPAD_LEFT, GenericInputBinding::DPadLeft},
{"DPadRight", TRANSLATE_NOOP("USB", "D-Pad Right"), InputBindingInfo::Type::Button, CID_DJ_DPAD_RIGHT, GenericInputBinding::DPadRight},
{"Square", TRANSLATE_NOOP("USB", "Square"), InputBindingInfo::Type::Button, CID_DJ_SQUARE, GenericInputBinding::Unknown},
{"Cross", TRANSLATE_NOOP("USB", "Cross"), InputBindingInfo::Type::Button, CID_DJ_CROSS, GenericInputBinding::Unknown},
{"Circle", TRANSLATE_NOOP("USB", "Circle"), InputBindingInfo::Type::Button, CID_DJ_CIRCLE, GenericInputBinding::Unknown},
{"Triangle", TRANSLATE_NOOP("USB", "Triangle / Euphoria"), InputBindingInfo::Type::Button, CID_DJ_TRIANGLE, GenericInputBinding::Triangle},
{"Select", TRANSLATE_NOOP("USB", "Select"), InputBindingInfo::Type::Button, CID_DJ_SELECT, GenericInputBinding::Select},
{"Start", TRANSLATE_NOOP("USB", "Start"), InputBindingInfo::Type::Button, CID_DJ_START, GenericInputBinding::Start},
{"CrossFaderLeft", TRANSLATE_NOOP("USB", "Cross Fader Left"), InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_LEFT, GenericInputBinding::RightStickDown},
{"CrossFaderRight", TRANSLATE_NOOP("USB", "Cross Fader Right"), InputBindingInfo::Type::HalfAxis, CID_DJ_CROSSFADER_RIGHT, GenericInputBinding::RightStickUp},
{"EffectsKnobLeft", TRANSLATE_NOOP("USB", "Effects Knob Left"), InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_LEFT, GenericInputBinding::RightStickLeft},
{"EffectsKnobRight", TRANSLATE_NOOP("USB", "Effects Knob Right"), InputBindingInfo::Type::HalfAxis, CID_DJ_EFFECTSKNOB_RIGHT, GenericInputBinding::RightStickRight},
{"LeftTurntableUp", TRANSLATE_NOOP("USB", "Left Turntable Up"), InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_UP, GenericInputBinding::LeftStickLeft},
{"LeftTurntableDown", TRANSLATE_NOOP("USB", "Left Turntable Down"), InputBindingInfo::Type::HalfAxis, CID_DJ_LEFT_TURNTABLE_DOWN, GenericInputBinding::LeftStickRight},
{"RightTurntableUp", TRANSLATE_NOOP("USB", "Right Turntable Up"), InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_UP, GenericInputBinding::LeftStickUp},
{"RightTurntableDown", TRANSLATE_NOOP("USB", "Right Turntable Down"), InputBindingInfo::Type::HalfAxis, CID_DJ_RIGHT_TURNTABLE_DOWN, GenericInputBinding::LeftStickDown},
{"Right Green", TRANSLATE_NOOP("USB", "Right Green"), InputBindingInfo::Type::Button, CID_DJ_RIGHT_GREEN, GenericInputBinding::Cross},
{"Right Red", TRANSLATE_NOOP("USB", "Right Red"), InputBindingInfo::Type::Button, CID_DJ_RIGHT_RED, GenericInputBinding::Circle},
{"Right Blue", TRANSLATE_NOOP("USB", "Right Blue"), InputBindingInfo::Type::Button, CID_DJ_RIGHT_BLUE, GenericInputBinding::Square},
{"Left Green", TRANSLATE_NOOP("USB", "Left Green"), InputBindingInfo::Type::Button, CID_DJ_LEFT_GREEN, GenericInputBinding::Unknown},
{"Left Red", TRANSLATE_NOOP("USB", "Left Red"), InputBindingInfo::Type::Button, CID_DJ_LEFT_RED, GenericInputBinding::Unknown},
{"Left Blue", TRANSLATE_NOOP("USB", "Left Blue"), InputBindingInfo::Type::Button, CID_DJ_LEFT_BLUE, GenericInputBinding::Unknown}
};
return bindings;
}
gsl::span<const SettingInfo> DJTurntableDevice::Settings(u32 subtype) const
{
static constexpr const SettingInfo info[] = {
{SettingInfo::Type::Integer, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"),
TRANSLATE_NOOP("USB", "Multiplies the turntable rotation speed by a constant. Useful for using Xbox 360 turntables."),
"1", "1", "100", "1", "%d", nullptr, nullptr, 1.0f}};
return info;
}
} // namespace usb_pad

View File

@ -0,0 +1,109 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "SaveState.h"
#include <list>
#include <string>
namespace usb_pad
{
enum TurntableControlID
{
CID_DJ_SQUARE,
CID_DJ_CROSS,
CID_DJ_CIRCLE,
CID_DJ_TRIANGLE,
CID_DJ_SELECT = 8,
CID_DJ_START,
CID_DJ_RIGHT_GREEN,
CID_DJ_RIGHT_RED,
CID_DJ_RIGHT_BLUE,
CID_DJ_LEFT_GREEN = 14,
CID_DJ_LEFT_RED,
CID_DJ_LEFT_BLUE,
CID_DJ_CROSSFADER_LEFT,
CID_DJ_CROSSFADER_RIGHT,
CID_DJ_EFFECTSKNOB_LEFT,
CID_DJ_EFFECTSKNOB_RIGHT,
CID_DJ_LEFT_TURNTABLE_UP,
CID_DJ_LEFT_TURNTABLE_DOWN,
CID_DJ_RIGHT_TURNTABLE_UP,
CID_DJ_RIGHT_TURNTABLE_DOWN,
CID_DJ_DPAD_UP,
CID_DJ_DPAD_DOWN,
CID_DJ_DPAD_LEFT,
CID_DJ_DPAD_RIGHT,
CID_DJ_COUNT,
};
struct TurntableState
{
TurntableState(u32 port_);
~TurntableState();
void UpdateSettings(SettingsInterface& si, const char* devname);
float GetBindValue(u32 bind) const;
void SetBindValue(u32 bind, float value);
void SetEuphoriaLedState(bool state);
int TokenIn(u8* buf, int len);
int TokenOut(const u8* buf, int len);
void UpdateHatSwitch();
u32 port = 0;
USBDevice dev{};
USBDesc desc{};
USBDescDevice desc_dev{};
u16 turntable_multiplier = 1;
struct
{
// intermediate state, resolved at query time
s16 crossfader_left;
s16 crossfader_right;
s16 effectsknob_left;
s16 effectsknob_right;
s16 left_turntable_up;
s16 left_turntable_down;
s16 right_turntable_up;
s16 right_turntable_down;
bool hat_left : 1;
bool hat_right : 1;
bool hat_up : 1;
bool hat_down : 1;
u8 hatswitch; // direction
u32 buttons; // active high
bool euphoria_led_state; // 1 = on, 0 = off
} data = {};
};
class DJTurntableDevice final : public DeviceProxy
{
public:
const char* Name() const override;
const char* TypeName() const override;
float GetBindingValue(const USBDevice* dev, u32 bind_index) const override;
void SetBindingValue(USBDevice* dev, u32 bind_index, float value) const override;
void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override;
gsl::span<const InputBindingInfo> Bindings(u32 subtype) const override;
gsl::span<const SettingInfo> Settings(u32 subtype) const override;
USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override;
};
} // namespace usb_pad

View File

@ -341,6 +341,7 @@
<ClCompile Include="USB\usb-pad\usb-pad-sdl-ff.cpp" />
<ClCompile Include="USB\usb-pad\usb-pad.cpp" />
<ClCompile Include="USB\usb-pad\usb-seamic.cpp" />
<ClCompile Include="USB\usb-pad\usb-turntable.cpp" />
<ClCompile Include="USB\usb-printer\usb-printer.cpp" />
<ClCompile Include="USB\USB.cpp" />
<ClCompile Include="VirtualMemory.cpp" />
@ -698,6 +699,7 @@
<ClInclude Include="USB\usb-pad\lg\lg_ff.h" />
<ClInclude Include="USB\usb-pad\usb-pad-sdl-ff.h" />
<ClInclude Include="USB\usb-pad\usb-pad.h" />
<ClInclude Include="USB\usb-pad\usb-turntable.h" />
<ClInclude Include="USB\usb-printer\usb-printer.h" />
<ClInclude Include="USB\USB.h" />
<ClInclude Include="Utilities\AsciiFile.h" />

View File

@ -1244,6 +1244,9 @@
<ClCompile Include="USB\usb-pad\usb-seamic.cpp">
<Filter>System\Ps2\USB\usb-pad</Filter>
</ClCompile>
<ClCompile Include="USB\usb-pad\usb-turntable.cpp">
<Filter>System\Ps2\USB\usb-pad</Filter>
</ClCompile>
<ClCompile Include="USB\usb-pad\usb-pad-ff.cpp">
<Filter>System\Ps2\USB\usb-pad</Filter>
</ClCompile>
@ -2210,6 +2213,9 @@
<ClInclude Include="USB\usb-pad\usb-pad.h">
<Filter>System\Ps2\USB\usb-pad</Filter>
</ClInclude>
<ClInclude Include="USB\usb-pad\usb-turntable.h">
<Filter>System\Ps2\USB\usb-pad</Filter>
</ClInclude>
<ClInclude Include="USB\usb-pad\lg\lg_ff.h">
<Filter>System\Ps2\USB\usb-pad\lg</Filter>
</ClInclude>