Merge pull request #8036 from jordan-woyak/emu-drums-fix
WiimoteEmu: Drum extension accuracy improvements.
This commit is contained in:
commit
37c85b32c0
|
@ -4,9 +4,8 @@
|
||||||
|
|
||||||
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "Common/BitUtils.h"
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
|
@ -21,12 +20,12 @@ namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
constexpr std::array<u8, 6> drums_id{{0x01, 0x00, 0xa4, 0x20, 0x01, 0x03}};
|
constexpr std::array<u8, 6> drums_id{{0x01, 0x00, 0xa4, 0x20, 0x01, 0x03}};
|
||||||
|
|
||||||
constexpr std::array<u16, 6> drum_pad_bitmasks{{
|
constexpr std::array<u8, 6> drum_pad_bitmasks{{
|
||||||
Drums::PAD_RED,
|
Drums::PAD_RED,
|
||||||
Drums::PAD_YELLOW,
|
Drums::PAD_YELLOW,
|
||||||
Drums::PAD_BLUE,
|
Drums::PAD_BLUE,
|
||||||
Drums::PAD_GREEN,
|
|
||||||
Drums::PAD_ORANGE,
|
Drums::PAD_ORANGE,
|
||||||
|
Drums::PAD_GREEN,
|
||||||
Drums::PAD_BASS,
|
Drums::PAD_BASS,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
@ -34,19 +33,28 @@ constexpr std::array<const char*, 6> drum_pad_names{{
|
||||||
_trans("Red"),
|
_trans("Red"),
|
||||||
_trans("Yellow"),
|
_trans("Yellow"),
|
||||||
_trans("Blue"),
|
_trans("Blue"),
|
||||||
_trans("Green"),
|
|
||||||
_trans("Orange"),
|
_trans("Orange"),
|
||||||
|
_trans("Green"),
|
||||||
_trans("Bass"),
|
_trans("Bass"),
|
||||||
}};
|
}};
|
||||||
|
|
||||||
constexpr std::array<u16, 2> drum_button_bitmasks{{
|
constexpr std::array<Drums::VelocityID, 6> drum_pad_velocity_ids{{
|
||||||
|
Drums::VelocityID::Red,
|
||||||
|
Drums::VelocityID::Yellow,
|
||||||
|
Drums::VelocityID::Blue,
|
||||||
|
Drums::VelocityID::Orange,
|
||||||
|
Drums::VelocityID::Green,
|
||||||
|
Drums::VelocityID::Bass,
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr std::array<u8, 2> drum_button_bitmasks{{
|
||||||
Drums::BUTTON_MINUS,
|
Drums::BUTTON_MINUS,
|
||||||
Drums::BUTTON_PLUS,
|
Drums::BUTTON_PLUS,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Drums::Drums() : Extension1stParty(_trans("Drums"))
|
Drums::Drums() : Extension1stParty(_trans("Drums"))
|
||||||
{
|
{
|
||||||
// pads
|
// Pads.
|
||||||
groups.emplace_back(m_pads = new ControllerEmu::Buttons(_trans("Pads")));
|
groups.emplace_back(m_pads = new ControllerEmu::Buttons(_trans("Pads")));
|
||||||
for (auto& drum_pad_name : drum_pad_names)
|
for (auto& drum_pad_name : drum_pad_names)
|
||||||
{
|
{
|
||||||
|
@ -54,12 +62,18 @@ Drums::Drums() : Extension1stParty(_trans("Drums"))
|
||||||
new ControllerEmu::Input(ControllerEmu::Translate, drum_pad_name));
|
new ControllerEmu::Input(ControllerEmu::Translate, drum_pad_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// stick
|
m_pads->AddSetting(&m_hit_strength_setting,
|
||||||
constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / STICK_RADIUS;
|
// i18n: Refers to how hard emulated drum pads are struck.
|
||||||
groups.emplace_back(m_stick =
|
{_trans("Hit Strength"),
|
||||||
new ControllerEmu::OctagonAnalogStick(_trans("Stick"), gate_radius));
|
// i18n: The symbol for percent.
|
||||||
|
_trans("%")},
|
||||||
|
50);
|
||||||
|
|
||||||
// buttons
|
// Stick.
|
||||||
|
groups.emplace_back(m_stick =
|
||||||
|
new ControllerEmu::OctagonAnalogStick(_trans("Stick"), GATE_RADIUS));
|
||||||
|
|
||||||
|
// Buttons.
|
||||||
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
|
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
|
||||||
m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "-"));
|
m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "-"));
|
||||||
m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "+"));
|
m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "+"));
|
||||||
|
@ -69,39 +83,98 @@ void Drums::Update()
|
||||||
{
|
{
|
||||||
DataFormat drum_data = {};
|
DataFormat drum_data = {};
|
||||||
|
|
||||||
// stick
|
// The meaning of these bits are unknown but they are usually set.
|
||||||
|
drum_data.unk1 = 0b11;
|
||||||
|
drum_data.unk2 = 0b11;
|
||||||
|
drum_data.unk3 = 0b1;
|
||||||
|
drum_data.unk4 = 0b1;
|
||||||
|
drum_data.unk5 = 0b11;
|
||||||
|
|
||||||
|
// Send no velocity data by default.
|
||||||
|
drum_data.velocity_id = u8(VelocityID::None);
|
||||||
|
drum_data.no_velocity_data_1 = 1;
|
||||||
|
drum_data.no_velocity_data_2 = 1;
|
||||||
|
drum_data.softness = 7;
|
||||||
|
|
||||||
|
// Stick.
|
||||||
{
|
{
|
||||||
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
|
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
|
||||||
|
|
||||||
drum_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER);
|
drum_data.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||||
drum_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER);
|
drum_data.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement these:
|
// Buttons.
|
||||||
drum_data.which = 0x1F;
|
m_buttons->GetState(&drum_data.buttons, drum_button_bitmasks.data());
|
||||||
drum_data.none = 1;
|
|
||||||
drum_data.hhp = 1;
|
|
||||||
drum_data.velocity = 0xf;
|
|
||||||
drum_data.softness = 7;
|
|
||||||
|
|
||||||
// buttons
|
// Drum pads.
|
||||||
m_buttons->GetState(&drum_data.bt, drum_button_bitmasks.data());
|
u8 current_pad_input = 0;
|
||||||
|
m_pads->GetState(¤t_pad_input, drum_pad_bitmasks.data());
|
||||||
|
m_new_pad_hits |= ~m_prev_pad_input & current_pad_input;
|
||||||
|
m_prev_pad_input = current_pad_input;
|
||||||
|
|
||||||
// pads
|
static_assert(std::tuple_size<decltype(m_pad_remaining_frames)>::value ==
|
||||||
m_pads->GetState(&drum_data.bt, drum_pad_bitmasks.data());
|
drum_pad_bitmasks.size(),
|
||||||
|
"Array sizes do not match.");
|
||||||
|
|
||||||
// flip button bits
|
// Figure out which velocity id to send. (needs to be sent once for each newly hit drum-pad)
|
||||||
drum_data.bt ^= 0xFFFF;
|
for (std::size_t i = 0; i != drum_pad_bitmasks.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto drum_pad = drum_pad_bitmasks[i];
|
||||||
|
|
||||||
|
if (m_new_pad_hits & drum_pad)
|
||||||
|
{
|
||||||
|
// Clear the bit so velocity data is not sent again until the next hit.
|
||||||
|
m_new_pad_hits &= ~drum_pad;
|
||||||
|
|
||||||
|
drum_data.velocity_id = u8(drum_pad_velocity_ids[i]);
|
||||||
|
|
||||||
|
drum_data.no_velocity_data_1 = 0;
|
||||||
|
drum_data.no_velocity_data_2 = 0;
|
||||||
|
|
||||||
|
// Set softness from user-configured hit strength setting.
|
||||||
|
drum_data.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
|
||||||
|
|
||||||
|
// A drum-pad hit causes the relevent bit to be triggered for the next 10 frames.
|
||||||
|
constexpr u8 HIT_FRAME_COUNT = 10;
|
||||||
|
|
||||||
|
m_pad_remaining_frames[i] = HIT_FRAME_COUNT;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out which drum-pad bits to send.
|
||||||
|
// Note: Relevent bits are not set until after velocity data has been sent.
|
||||||
|
// My drums never exposed simultaneous hits. One pad bit was always sent before the other.
|
||||||
|
for (std::size_t i = 0; i != drum_pad_bitmasks.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& remaining_frames = m_pad_remaining_frames[i];
|
||||||
|
|
||||||
|
if (remaining_frames != 0)
|
||||||
|
{
|
||||||
|
drum_data.drum_pads |= drum_pad_bitmasks[i];
|
||||||
|
--remaining_frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip button and drum-pad bits. (0 == pressed)
|
||||||
|
drum_data.buttons ^= 0xff;
|
||||||
|
drum_data.drum_pads ^= 0xff;
|
||||||
|
|
||||||
|
// Copy data to proper region in the "register".
|
||||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = drum_data;
|
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = drum_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Drums::IsButtonPressed() const
|
bool Drums::IsButtonPressed() const
|
||||||
{
|
{
|
||||||
u16 buttons = 0;
|
u8 buttons = 0;
|
||||||
m_buttons->GetState(&buttons, drum_button_bitmasks.data());
|
m_buttons->GetState(&buttons, drum_button_bitmasks.data());
|
||||||
m_pads->GetState(&buttons, drum_pad_bitmasks.data());
|
|
||||||
return buttons != 0;
|
u8 pads = 0;
|
||||||
|
m_pads->GetState(&pads, drum_pad_bitmasks.data());
|
||||||
|
|
||||||
|
return buttons != 0 || pads != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Drums::Reset()
|
void Drums::Reset()
|
||||||
|
@ -110,7 +183,21 @@ void Drums::Reset()
|
||||||
|
|
||||||
m_reg.identifier = drums_id;
|
m_reg.identifier = drums_id;
|
||||||
|
|
||||||
// TODO: Is there calibration data?
|
// Both 16-byte blocks of calibration data seem to be 0xff filled.
|
||||||
|
m_reg.calibration.fill(0xff);
|
||||||
|
|
||||||
|
m_prev_pad_input = 0;
|
||||||
|
m_new_pad_hits = 0;
|
||||||
|
m_pad_remaining_frames = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Drums::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
EncryptedExtension::DoState(p);
|
||||||
|
|
||||||
|
p.Do(m_prev_pad_input);
|
||||||
|
p.Do(m_new_pad_hits);
|
||||||
|
p.Do(m_pad_remaining_frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerEmu::ControlGroup* Drums::GetGroup(DrumsGroup group)
|
ControllerEmu::ControlGroup* Drums::GetGroup(DrumsGroup group)
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||||
|
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
||||||
|
|
||||||
namespace ControllerEmu
|
namespace ControllerEmu
|
||||||
{
|
{
|
||||||
|
@ -28,25 +31,51 @@ class Drums : public Extension1stParty
|
||||||
public:
|
public:
|
||||||
struct DataFormat
|
struct DataFormat
|
||||||
{
|
{
|
||||||
u8 sx : 6;
|
u8 stick_x : 6;
|
||||||
u8 pad1 : 2; // always 0
|
// Seemingly random.
|
||||||
|
u8 unk1 : 2;
|
||||||
|
|
||||||
u8 sy : 6;
|
u8 stick_y : 6;
|
||||||
u8 pad2 : 2; // always 0
|
// Seemingly random.
|
||||||
|
u8 unk2 : 2;
|
||||||
|
|
||||||
u8 pad3 : 1; // unknown
|
// Always 1 with no velocity data and seemingly random otherwise.
|
||||||
u8 which : 5;
|
u8 unk3 : 1;
|
||||||
u8 none : 1;
|
// For which "pad" the velocity data is for.
|
||||||
u8 hhp : 1;
|
u8 velocity_id : 7;
|
||||||
|
|
||||||
u8 pad4 : 1; // unknown
|
// Always 1 with no velocity data and seemingly random otherwise.
|
||||||
u8 velocity : 4; // unknown
|
u8 unk4 : 1;
|
||||||
|
// 1 with no velocity data and 0 when velocity data is present.
|
||||||
|
u8 no_velocity_data_1 : 1;
|
||||||
|
// These two bits seem to always be set. (0b11)
|
||||||
|
u8 unk5 : 2;
|
||||||
|
// 1 with no velocity data and 0 when velocity data is present.
|
||||||
|
u8 no_velocity_data_2 : 1;
|
||||||
|
// How "soft" a drum pad has been hit as a range from 0:very-hard to 7:very-soft.
|
||||||
u8 softness : 3;
|
u8 softness : 3;
|
||||||
|
|
||||||
u16 bt; // buttons
|
// Button bits.
|
||||||
|
u8 buttons;
|
||||||
|
|
||||||
|
// Drum-pad bits.
|
||||||
|
u8 drum_pads;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DataFormat) == 6, "Wrong size");
|
static_assert(sizeof(DataFormat) == 6, "Wrong size");
|
||||||
|
|
||||||
|
enum class VelocityID : u8
|
||||||
|
{
|
||||||
|
None = 0b1111111,
|
||||||
|
Bass = 0b1011011,
|
||||||
|
// TODO: Implement HiHat.
|
||||||
|
// HiHat = 0b0011011,
|
||||||
|
Red = 0b1011001,
|
||||||
|
Yellow = 0b1010001,
|
||||||
|
Blue = 0b1001111,
|
||||||
|
Orange = 0b1001110,
|
||||||
|
Green = 0b1010010,
|
||||||
|
};
|
||||||
|
|
||||||
Drums();
|
Drums();
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
@ -55,28 +84,47 @@ public:
|
||||||
|
|
||||||
ControllerEmu::ControlGroup* GetGroup(DrumsGroup group);
|
ControllerEmu::ControlGroup* GetGroup(DrumsGroup group);
|
||||||
|
|
||||||
enum
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
|
enum : u8
|
||||||
{
|
{
|
||||||
BUTTON_PLUS = 0x04,
|
BUTTON_PLUS = 0x04,
|
||||||
BUTTON_MINUS = 0x10,
|
BUTTON_MINUS = 0x10,
|
||||||
|
|
||||||
PAD_BASS = 0x0400,
|
// FYI: The low/high bits of the button byte are "random" when velocity data is present.
|
||||||
PAD_BLUE = 0x0800,
|
// HAVE_VELOCITY_DATA = 0b10000001,
|
||||||
PAD_GREEN = 0x1000,
|
|
||||||
PAD_YELLOW = 0x2000,
|
|
||||||
PAD_RED = 0x4000,
|
|
||||||
PAD_ORANGE = 0x8000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u8 STICK_CENTER = 0x20;
|
enum : u8
|
||||||
static const u8 STICK_RADIUS = 0x1f;
|
{
|
||||||
|
// FYI: HiHat sets no bits here.
|
||||||
|
PAD_BASS = 0x04,
|
||||||
|
PAD_BLUE = 0x08,
|
||||||
|
PAD_GREEN = 0x10,
|
||||||
|
PAD_YELLOW = 0x20,
|
||||||
|
PAD_RED = 0x40,
|
||||||
|
PAD_ORANGE = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Test real hardware. Is this accurate?
|
// Note: My hardware's octagon stick produced the complete range of values (0 - 0x3f)
|
||||||
static const u8 STICK_GATE_RADIUS = 0x16;
|
// It also had perfect center values of 0x20 with absolutely no "play".
|
||||||
|
static constexpr ControlState GATE_RADIUS = 1.0;
|
||||||
|
static constexpr u8 STICK_MIN = 0x00;
|
||||||
|
static constexpr u8 STICK_CENTER = 0x20;
|
||||||
|
static constexpr u8 STICK_MAX = 0x3f;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ControllerEmu::Buttons* m_buttons;
|
ControllerEmu::Buttons* m_buttons;
|
||||||
ControllerEmu::Buttons* m_pads;
|
ControllerEmu::Buttons* m_pads;
|
||||||
ControllerEmu::AnalogStick* m_stick;
|
ControllerEmu::AnalogStick* m_stick;
|
||||||
|
|
||||||
|
ControllerEmu::SettingValue<double> m_hit_strength_setting;
|
||||||
|
|
||||||
|
// Holds previous user input state to watch for "new" hits.
|
||||||
|
u8 m_prev_pad_input;
|
||||||
|
// Holds new drum pad hits that still need velocity data to be sent.
|
||||||
|
u8 m_new_pad_hits;
|
||||||
|
// Holds how many more frames to send each drum-pad bit.
|
||||||
|
std::array<u8, 6> m_pad_remaining_frames;
|
||||||
};
|
};
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
static const u32 STATE_VERSION = 109; // Last changed in PR 7861
|
static const u32 STATE_VERSION = 110; // Last changed in PR 8036
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
Loading…
Reference in New Issue