diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 97de6a15c9..c755a20e63 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -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 ) diff --git a/pcsx2/USB/deviceproxy.cpp b/pcsx2/USB/deviceproxy.cpp index 79a66025c6..d285e3c26e 100644 --- a/pcsx2/USB/deviceproxy.cpp +++ b/pcsx2/USB/deviceproxy.cpp @@ -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()); diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index 27942dfc3d..b393d5e2d1 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -50,7 +50,8 @@ enum DeviceType : s32 DEVTYPE_SEGA_SEAMIC, DEVTYPE_PRINTER, DEVTYPE_KEYBOARDMANIA, - DEVTYPE_GUNCON2 + DEVTYPE_GUNCON2, + DEVTYPE_DJ }; class DeviceProxy diff --git a/pcsx2/USB/usb-pad/usb-turntable.cpp b/pcsx2/USB/usb-pad/usb-turntable.cpp new file mode 100644 index 0000000000..660c6b55eb --- /dev/null +++ b/pcsx2/USB/usb-pad/usb-turntable.cpp @@ -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 . + */ + +#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(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(std::min(data.left_turntable_up * turntable_multiplier, 0x7F)); + } + else + { + left_turntable += static_cast(std::min(data.left_turntable_down * turntable_multiplier, 0x7F)); + } + if (data.right_turntable_up > 0) + { + right_turntable -= static_cast(std::min(data.right_turntable_up * turntable_multiplier, 0x7F)); + } + else + { + right_turntable += static_cast(std::min(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(static_cast(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(s->data.crossfader_left) / 512.0f; + + case CID_DJ_CROSSFADER_RIGHT: + return static_cast(s->data.crossfader_right) / 512.0f; + + case CID_DJ_EFFECTSKNOB_LEFT: + return static_cast(s->data.effectsknob_left) / 512.0f; + + case CID_DJ_EFFECTSKNOB_RIGHT: + return static_cast(s->data.effectsknob_right) / 512.0f; + + case CID_DJ_LEFT_TURNTABLE_UP: + return static_cast(s->data.left_turntable_up) / 255.0f; + + case CID_DJ_LEFT_TURNTABLE_DOWN: + return static_cast(s->data.left_turntable_down) / 255.0f; + + case CID_DJ_RIGHT_TURNTABLE_UP: + return static_cast(s->data.right_turntable_up) / 255.0f; + + case CID_DJ_RIGHT_TURNTABLE_DOWN: + return static_cast(s->data.right_turntable_down) / 255.0f; + + case CID_DJ_DPAD_UP: + return static_cast(s->data.hat_up); + + case CID_DJ_DPAD_DOWN: + return static_cast(s->data.hat_down); + + case CID_DJ_DPAD_LEFT: + return static_cast(s->data.hat_left); + + case CID_DJ_DPAD_RIGHT: + return static_cast(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(std::clamp(std::lroundf(value * 512.0f), 0, 512)); + break; + + case CID_DJ_CROSSFADER_RIGHT: + s->data.crossfader_right = static_cast(std::clamp(std::lroundf(value * 512.0f), 0, 512)); + break; + + case CID_DJ_EFFECTSKNOB_LEFT: + s->data.effectsknob_left = static_cast(std::clamp(std::lroundf(value * 512.0f), 0, 512)); + break; + + case CID_DJ_EFFECTSKNOB_RIGHT: + s->data.effectsknob_right = static_cast(std::clamp(std::lroundf(value * 512.0f), 0, 512)); + break; + + case CID_DJ_LEFT_TURNTABLE_UP: + s->data.left_turntable_up = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_DJ_LEFT_TURNTABLE_DOWN: + s->data.left_turntable_down = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_DJ_RIGHT_TURNTABLE_UP: + s->data.right_turntable_up = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_DJ_RIGHT_TURNTABLE_DOWN: + s->data.right_turntable_down = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_DJ_DPAD_UP: + s->data.hat_up = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + s->UpdateHatSwitch(); + break; + case CID_DJ_DPAD_DOWN: + s->data.hat_down = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + s->UpdateHatSwitch(); + break; + case CID_DJ_DPAD_LEFT: + s->data.hat_left = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + s->UpdateHatSwitch(); + break; + case CID_DJ_DPAD_RIGHT: + s->data.hat_right = static_cast(std::clamp(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 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 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 diff --git a/pcsx2/USB/usb-pad/usb-turntable.h b/pcsx2/USB/usb-pad/usb-turntable.h new file mode 100644 index 0000000000..57dba1ceeb --- /dev/null +++ b/pcsx2/USB/usb-pad/usb-turntable.h @@ -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 . + */ + +#pragma once +#include "SaveState.h" +#include +#include + +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 Bindings(u32 subtype) const override; + gsl::span Settings(u32 subtype) const override; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + }; + +} // namespace usb_pad diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 455cfd6925..c8f2c93589 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -341,6 +341,7 @@ + @@ -698,6 +699,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 06e5aacba5..8b5c36a834 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1244,6 +1244,9 @@ System\Ps2\USB\usb-pad + + System\Ps2\USB\usb-pad + System\Ps2\USB\usb-pad @@ -2210,6 +2213,9 @@ System\Ps2\USB\usb-pad + + System\Ps2\USB\usb-pad + System\Ps2\USB\usb-pad\lg