mirror of
https://github.com/ndeadly/MissionControl
synced 2025-02-16 13:18:27 +00:00
mc.mitm: implement proper decoding of nintendo's hd rumble encoding scheme
This commit is contained in:
parent
8cddf8fbeb
commit
43f405e3e6
17 changed files with 554 additions and 111 deletions
|
@ -87,9 +87,9 @@ namespace ams::controller {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DualsenseController::SetVibration(const SwitchRumbleData *rumble_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(rumble_data[0].low_band_amp, rumble_data[1].low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(rumble_data[0].high_band_amp, rumble_data[1].high_band_amp));
|
||||
Result DualsenseController::SetVibration(const SwitchMotorData *motor_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(motor_data->left_motor.low_band_amp, motor_data->right_motor.low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(motor_data->left_motor.high_band_amp, motor_data->right_motor.high_band_amp));
|
||||
return this->PushRumbleLedState();
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ namespace ams::controller {
|
|||
, m_rumble_state({0, 0}) { }
|
||||
|
||||
Result Initialize();
|
||||
Result SetVibration(const SwitchRumbleData *rumble_data);
|
||||
Result SetVibration(const SwitchMotorData *motor_data);
|
||||
Result CancelVibration();
|
||||
Result SetPlayerLed(u8 led_mask);
|
||||
Result SetLightbarColour(RGBColour colour);
|
||||
|
|
|
@ -191,9 +191,9 @@ namespace ams::controller {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Dualshock3Controller::SetVibration(const SwitchRumbleData *rumble_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(rumble_data[0].low_band_amp, rumble_data[1].low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(rumble_data[0].high_band_amp, rumble_data[1].high_band_amp));
|
||||
Result Dualshock3Controller::SetVibration(const SwitchMotorData *motor_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(motor_data->left_motor.low_band_amp, motor_data->right_motor.low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(motor_data->left_motor.high_band_amp, motor_data->right_motor.high_band_amp));
|
||||
R_RETURN(this->PushRumbleLedState());
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace ams::controller {
|
|||
: EmulatedSwitchController(address, id) { }
|
||||
|
||||
Result Initialize(void);
|
||||
Result SetVibration(const SwitchRumbleData *rumble_data);
|
||||
Result SetVibration(const SwitchMotorData *motor_data);
|
||||
Result CancelVibration();
|
||||
Result SetPlayerLed(u8 led_mask);
|
||||
|
||||
|
|
|
@ -65,9 +65,9 @@ namespace ams::controller {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Dualshock4Controller::SetVibration(const SwitchRumbleData *rumble_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(rumble_data[0].low_band_amp, rumble_data[1].low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(rumble_data[0].high_band_amp, rumble_data[1].high_band_amp));
|
||||
Result Dualshock4Controller::SetVibration(const SwitchMotorData *motor_data) {
|
||||
m_rumble_state.amp_motor_left = static_cast<u8>(255 * std::max(motor_data->left_motor.low_band_amp, motor_data->right_motor.low_band_amp));
|
||||
m_rumble_state.amp_motor_right = static_cast<u8>(255 * std::max(motor_data->left_motor.high_band_amp, motor_data->right_motor.high_band_amp));
|
||||
R_RETURN(this->PushRumbleLedState());
|
||||
}
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ namespace ams::controller {
|
|||
, m_rumble_state({0, 0}) { }
|
||||
|
||||
Result Initialize();
|
||||
Result SetVibration(const SwitchRumbleData *rumble_data);
|
||||
Result SetVibration(const SwitchMotorData *motor_data);
|
||||
Result CancelVibration();
|
||||
Result SetPlayerLed(u8 led_mask);
|
||||
Result SetLightbarColour(RGBColour colour);
|
||||
|
|
|
@ -21,72 +21,6 @@ namespace ams::controller {
|
|||
|
||||
namespace {
|
||||
|
||||
// Frequency in Hz rounded to nearest int
|
||||
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md#frequency-table
|
||||
constinit const u16 RumbleFreqLookup[] = {
|
||||
0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031,
|
||||
0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0039, 0x003a, 0x003b,
|
||||
0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, 0x0045, 0x0046, 0x0048,
|
||||
0x0049, 0x004b, 0x004d, 0x004e, 0x0050, 0x0052, 0x0054, 0x0055, 0x0057,
|
||||
0x0059, 0x005b, 0x005d, 0x005f, 0x0061, 0x0063, 0x0066, 0x0068, 0x006a,
|
||||
0x006c, 0x006f, 0x0071, 0x0074, 0x0076, 0x0079, 0x007b, 0x007e, 0x0081,
|
||||
0x0084, 0x0087, 0x0089, 0x008d, 0x0090, 0x0093, 0x0096, 0x0099, 0x009d,
|
||||
0x00a0, 0x00a4, 0x00a7, 0x00ab, 0x00ae, 0x00b2, 0x00b6, 0x00ba, 0x00be,
|
||||
0x00c2, 0x00c7, 0x00cb, 0x00cf, 0x00d4, 0x00d9, 0x00dd, 0x00e2, 0x00e7,
|
||||
0x00ec, 0x00f1, 0x00f7, 0x00fc, 0x0102, 0x0107, 0x010d, 0x0113, 0x0119,
|
||||
0x011f, 0x0125, 0x012c, 0x0132, 0x0139, 0x0140, 0x0147, 0x014e, 0x0155,
|
||||
0x015d, 0x0165, 0x016c, 0x0174, 0x017d, 0x0185, 0x018d, 0x0196, 0x019f,
|
||||
0x01a8, 0x01b1, 0x01bb, 0x01c5, 0x01ce, 0x01d9, 0x01e3, 0x01ee, 0x01f8,
|
||||
0x0203, 0x020f, 0x021a, 0x0226, 0x0232, 0x023e, 0x024b, 0x0258, 0x0265,
|
||||
0x0272, 0x0280, 0x028e, 0x029c, 0x02ab, 0x02ba, 0x02c9, 0x02d9, 0x02e9,
|
||||
0x02f9, 0x030a, 0x031b, 0x032c, 0x033e, 0x0350, 0x0363, 0x0376, 0x0389,
|
||||
0x039d, 0x03b1, 0x03c6, 0x03db, 0x03f1, 0x0407, 0x041d, 0x0434, 0x044c,
|
||||
0x0464, 0x047d, 0x0496, 0x04af, 0x04ca, 0x04e5
|
||||
};
|
||||
constexpr size_t RumbleFreqLookupSize = sizeof(RumbleFreqLookup) / sizeof(u16);
|
||||
|
||||
// Floats from dekunukem repo normalised and scaled by function used by yuzu
|
||||
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md#amplitude-table
|
||||
// https://github.com/yuzu-emu/yuzu/blob/d3a4a192fe26e251f521f0311b2d712f5db9918e/src/input_common/sdl/sdl_impl.cpp#L429
|
||||
constinit const float RumbleAmpLookup[] = {
|
||||
0.000000, 0.120576, 0.137846, 0.146006, 0.154745, 0.164139, 0.174246,
|
||||
0.185147, 0.196927, 0.209703, 0.223587, 0.238723, 0.255268, 0.273420,
|
||||
0.293398, 0.315462, 0.321338, 0.327367, 0.333557, 0.339913, 0.346441,
|
||||
0.353145, 0.360034, 0.367112, 0.374389, 0.381870, 0.389564, 0.397476,
|
||||
0.405618, 0.413996, 0.422620, 0.431501, 0.436038, 0.440644, 0.445318,
|
||||
0.450062, 0.454875, 0.459764, 0.464726, 0.469763, 0.474876, 0.480068,
|
||||
0.485342, 0.490694, 0.496130, 0.501649, 0.507256, 0.512950, 0.518734,
|
||||
0.524609, 0.530577, 0.536639, 0.542797, 0.549055, 0.555413, 0.561872,
|
||||
0.568436, 0.575106, 0.581886, 0.588775, 0.595776, 0.602892, 0.610127,
|
||||
0.617482, 0.624957, 0.632556, 0.640283, 0.648139, 0.656126, 0.664248,
|
||||
0.672507, 0.680906, 0.689447, 0.698135, 0.706971, 0.715957, 0.725098,
|
||||
0.734398, 0.743857, 0.753481, 0.763273, 0.773235, 0.783370, 0.793684,
|
||||
0.804178, 0.814858, 0.825726, 0.836787, 0.848044, 0.859502, 0.871165,
|
||||
0.883035, 0.895119, 0.907420, 0.919943, 0.932693, 0.945673, 0.958889,
|
||||
0.972345, 0.986048, 1.000000
|
||||
};
|
||||
constexpr size_t RumbleAmpLookupSize = sizeof(RumbleAmpLookup) / sizeof(float);
|
||||
|
||||
void DecodeRumbleValues(const u8 enc[], SwitchRumbleData *dec) {
|
||||
u8 hi_freq_ind = 0x20 + (enc[0] >> 2) + ((enc[1] & 0x01) * 0x40) - 1;
|
||||
u8 hi_amp_ind = (enc[1] & 0xfe) >> 1;
|
||||
u8 lo_freq_ind = (enc[2] & 0x7f) - 1;
|
||||
u8 lo_amp_ind = ((enc[3] - 0x40) << 1) + ((enc[2] & 0x80) >> 7);
|
||||
|
||||
if (!((hi_freq_ind < RumbleFreqLookupSize) &&
|
||||
(hi_amp_ind < RumbleAmpLookupSize) &&
|
||||
(lo_freq_ind < RumbleFreqLookupSize) &&
|
||||
(lo_amp_ind < RumbleAmpLookupSize))) {
|
||||
std::memset(dec, 0, sizeof(SwitchRumbleData));
|
||||
return;
|
||||
}
|
||||
|
||||
dec->high_band_freq = float(RumbleFreqLookup[hi_freq_ind]);
|
||||
dec->high_band_amp = RumbleAmpLookup[hi_amp_ind];
|
||||
dec->low_band_freq = float(RumbleFreqLookup[lo_freq_ind]);
|
||||
dec->low_band_amp = RumbleAmpLookup[lo_amp_ind];
|
||||
}
|
||||
|
||||
// CRC-8 with polynomial 0x7 for NFC/IR packets
|
||||
constexpr u8 ComputeCrc8(const void *data, size_t size) {
|
||||
return utils::Crc8<7>::Calculate(data, size);
|
||||
|
@ -151,14 +85,14 @@ namespace ams::controller {
|
|||
|
||||
switch (output_report->id) {
|
||||
case 0x01:
|
||||
R_TRY(this->HandleRumbleData(&output_report->rumble_data));
|
||||
R_TRY(this->HandleRumbleData(&output_report->enc_motor_data));
|
||||
R_TRY(this->HandleHidCommand(&output_report->type0x01.hid_command));
|
||||
break;
|
||||
case 0x10:
|
||||
R_TRY(this->HandleRumbleData(&output_report->rumble_data));
|
||||
R_TRY(this->HandleRumbleData(&output_report->enc_motor_data));
|
||||
break;
|
||||
case 0x11:
|
||||
R_TRY(this->HandleRumbleData(&output_report->rumble_data));
|
||||
R_TRY(this->HandleRumbleData(&output_report->enc_motor_data));
|
||||
//R_TRY(this->HandleNfcIrData(output_report->type0x11.nfc_ir_data));
|
||||
break;
|
||||
default:
|
||||
|
@ -168,12 +102,12 @@ namespace ams::controller {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result EmulatedSwitchController::HandleRumbleData(const SwitchRumbleDataEncoded *encoded) {
|
||||
Result EmulatedSwitchController::HandleRumbleData(const SwitchEncodedMotorData *encoded_motor_data) {
|
||||
if (m_enable_rumble) {
|
||||
SwitchRumbleData rumble_data[2];
|
||||
DecodeRumbleValues(encoded->left_motor, &rumble_data[0]);
|
||||
DecodeRumbleValues(encoded->right_motor, &rumble_data[1]);
|
||||
R_TRY(this->SetVibration(rumble_data));
|
||||
SwitchMotorData motor_data;
|
||||
if (m_rumble_handler.GetDecodedValues(encoded_motor_data, &motor_data)) {
|
||||
R_TRY(this->SetVibration(&motor_data));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
|
|
|
@ -32,14 +32,14 @@ namespace ams::controller {
|
|||
|
||||
protected:
|
||||
void ClearControllerState();
|
||||
virtual Result SetVibration(const SwitchRumbleData *rumble_data) { AMS_UNUSED(rumble_data); R_SUCCEED(); }
|
||||
virtual Result SetVibration(const SwitchMotorData *motor_data) { AMS_UNUSED(motor_data); R_SUCCEED(); }
|
||||
virtual Result CancelVibration() { R_SUCCEED(); }
|
||||
virtual Result SetPlayerLed(u8 led_mask) { AMS_UNUSED(led_mask); R_SUCCEED(); }
|
||||
|
||||
void UpdateControllerState(const bluetooth::HidReport *report) override;
|
||||
virtual void ProcessInputData(const bluetooth::HidReport *report) { AMS_UNUSED(report); }
|
||||
|
||||
Result HandleRumbleData(const SwitchRumbleDataEncoded *encoded);
|
||||
Result HandleRumbleData(const SwitchEncodedMotorData *enc_motor_data);
|
||||
Result HandleHidCommand(const SwitchHidCommand *command);
|
||||
Result HandleNfcIrData(const u8 *nfc_ir);
|
||||
|
||||
|
@ -80,6 +80,8 @@ namespace ams::controller {
|
|||
|
||||
u8 m_input_report_mode;
|
||||
|
||||
SwitchRumbleHandler m_rumble_handler;
|
||||
|
||||
bool m_enable_rumble;
|
||||
bool m_enable_motion;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "../bluetooth_mitm/bluetooth/bluetooth_types.hpp"
|
||||
#include "../bluetooth_mitm/bluetooth/bluetooth_hid_report.hpp"
|
||||
#include "../async/future_response.hpp"
|
||||
#include "switch_rumble_handler.hpp"
|
||||
#include <queue>
|
||||
|
||||
namespace ams::controller {
|
||||
|
@ -123,18 +124,6 @@ namespace ams::controller {
|
|||
s16 z;
|
||||
} PACKED;
|
||||
|
||||
struct SwitchRumbleDataEncoded {
|
||||
u8 left_motor[4];
|
||||
u8 right_motor[4];
|
||||
} PACKED;
|
||||
|
||||
struct SwitchRumbleData {
|
||||
float high_band_freq;
|
||||
float high_band_amp;
|
||||
float low_band_freq;
|
||||
float low_band_amp;
|
||||
} PACKED;
|
||||
|
||||
enum HidCommandType : u8 {
|
||||
HidCommand_PairingOut = 0x01,
|
||||
HidCommand_GetDeviceInfo = 0x02,
|
||||
|
@ -307,7 +296,7 @@ namespace ams::controller {
|
|||
struct SwitchOutputReport {
|
||||
u8 id;
|
||||
u8 counter;
|
||||
SwitchRumbleDataEncoded rumble_data;
|
||||
SwitchEncodedMotorData enc_motor_data;
|
||||
|
||||
union {
|
||||
struct{
|
||||
|
|
313
mc_mitm/source/controllers/switch_rumble_decoder.cpp
Normal file
313
mc_mitm/source/controllers/switch_rumble_decoder.cpp
Normal file
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024 ndeadly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "switch_rumble_decoder.hpp"
|
||||
|
||||
namespace ams::controller {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float MinAmplitude = -8.0f;
|
||||
constexpr float MaxAmplitude = 0.0f;
|
||||
constexpr float DefaultAmplitude = MinAmplitude;
|
||||
|
||||
constexpr float MinFrequency = -2.0f;
|
||||
constexpr float MaxFrequency = 2.0f;
|
||||
constexpr float DefaultFrequency = 0.0f;
|
||||
|
||||
constexpr float CenterFreqHigh = 320.0f;
|
||||
constexpr float CenterFreqLow = 160.0f;
|
||||
|
||||
constexpr float ExpBase2LookupResolution = 1.0f / 32;
|
||||
constexpr float ExpBase2RangeStart = std::min(MinAmplitude, MinFrequency);
|
||||
constexpr float ExpBase2RangeEnd = std::max(MaxAmplitude, MaxFrequency);
|
||||
constexpr size_t ExpBase2LookupLength = (std::fabs(ExpBase2RangeEnd - ExpBase2RangeStart) + ExpBase2LookupResolution) / ExpBase2LookupResolution;
|
||||
|
||||
constexpr std::array<float, ExpBase2LookupLength> ExpBase2Lookup = []() {
|
||||
std::array<float, ExpBase2LookupLength> table = {};
|
||||
|
||||
constexpr float AmplitudeThreshold = -7.9375f;
|
||||
|
||||
for (size_t i = 0; i < table.size(); ++i) {
|
||||
float f = ExpBase2RangeStart + i * ExpBase2LookupResolution;
|
||||
if (f >= AmplitudeThreshold) {
|
||||
table[i] = std::exp2f(f);
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}();
|
||||
|
||||
constexpr u32 GetLookupIndex(float input) {
|
||||
return (input - ExpBase2RangeStart) / ExpBase2LookupResolution;
|
||||
}
|
||||
|
||||
constexpr std::array<float, 128> Am7BitLookup = []() {
|
||||
std::array<float, 128> table = {};
|
||||
|
||||
for (size_t i = 0; i < table.size(); ++i) {
|
||||
if (i == 0) {
|
||||
table[i] = -8.0f;
|
||||
} else if (i < 16) {
|
||||
table[i] = 0.25f * i - 7.75f;
|
||||
} else if (i < 32) {
|
||||
table[i] = 0.0625f * i - 4.9375f;
|
||||
} else {
|
||||
table[i] = 0.03125f * i - 3.96875f;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}();
|
||||
|
||||
constexpr std::array<float, 128> Fm7BitLookup = []() {
|
||||
std::array<float, 128> table = {};
|
||||
|
||||
for (size_t i = 0; i < table.size(); ++i) {
|
||||
table[i] = 0.03125f * i - 2.0f;
|
||||
}
|
||||
|
||||
return table;
|
||||
}();
|
||||
|
||||
enum Switch5BitAction : u8 {
|
||||
Switch5BitAction_Ignore = 0x0,
|
||||
Switch5BitAction_Default = 0x1,
|
||||
Switch5BitAction_Substitute = 0x2,
|
||||
Switch5BitAction_Sum = 0x3,
|
||||
};
|
||||
|
||||
struct Switch5BitCommand {
|
||||
Switch5BitAction am_action;
|
||||
Switch5BitAction fm_action;
|
||||
float am_offset;
|
||||
float fm_offset;
|
||||
};
|
||||
|
||||
constexpr Switch5BitCommand CommandTable[] = {
|
||||
{ .am_action = Switch5BitAction_Default, .fm_action = Switch5BitAction_Default, .am_offset = 0.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = 0.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -0.5f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -1.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -1.5f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -2.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -2.5f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -3.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -3.5f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -4.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -4.5f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Substitute, .fm_action = Switch5BitAction_Ignore, .am_offset = -5.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Substitute, .am_offset = 0.0f, .fm_offset = -0.375f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Substitute, .am_offset = 0.0f, .fm_offset = -0.1875f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Substitute, .am_offset = 0.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Substitute, .am_offset = 0.0f, .fm_offset = 0.1875f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Substitute, .am_offset = 0.0f, .fm_offset = 0.375f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = 0.125f, .fm_offset = 0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Ignore, .am_offset = 0.125f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = 0.125f, .fm_offset = -0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = 0.03125f, .fm_offset = 0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Ignore, .am_offset = 0.03125f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = 0.03125f, .fm_offset = -0.03125f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Sum, .am_offset = 0.0f, .fm_offset = 0.03125f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Ignore, .am_offset = 0.0f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Ignore, .fm_action = Switch5BitAction_Sum, .am_offset = 0.0f, .fm_offset = -0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = -0.03125f, .fm_offset = 0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Ignore, .am_offset = -0.03125f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = -0.03125f, .fm_offset = -0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = -0.125f, .fm_offset = 0.03125f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Ignore, .am_offset = -0.125f, .fm_offset = 0.0f },
|
||||
{ .am_action = Switch5BitAction_Sum, .fm_action = Switch5BitAction_Sum, .am_offset = -0.125f, .fm_offset = -0.03125f }
|
||||
};
|
||||
|
||||
float ApplyCommand(Switch5BitAction action, float offset, float current_val, float default_val, float min, float max) {
|
||||
switch (action) {
|
||||
case Switch5BitAction_Ignore: return current_val;
|
||||
case Switch5BitAction_Substitute: return offset;
|
||||
case Switch5BitAction_Sum: return std::clamp(current_val + offset, min, max);
|
||||
default: return default_val;
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE float ApplyAmCommand(u8 amfm_code, float current_val) {
|
||||
return ApplyCommand(CommandTable[amfm_code].am_action, CommandTable[amfm_code].am_offset, current_val, DefaultAmplitude, MinAmplitude, MaxAmplitude);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE float ApplyFmCommand(u8 amfm_code, float current_val) {
|
||||
return ApplyCommand(CommandTable[amfm_code].fm_action, CommandTable[amfm_code].fm_offset, current_val, DefaultFrequency, MinFrequency, MaxFrequency);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SwitchRumbleDecoder::SwitchRumbleDecoder() {
|
||||
m_state = {
|
||||
.lo_amp_linear = DefaultAmplitude,
|
||||
.lo_freq_linear = DefaultFrequency,
|
||||
.hi_amp_linear = DefaultAmplitude,
|
||||
.hi_freq_linear = DefaultFrequency
|
||||
};
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeSamples(const SwitchEncodedVibrationSamples* encoded, SwitchVibrationSamples* decoded) {
|
||||
switch (encoded->packet_type) {
|
||||
case 0:
|
||||
decoded->count = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (encoded->one5bit.reserved == 0) {
|
||||
this->DecodeOne5Bit(encoded, decoded);
|
||||
} else if (encoded->one7bit.reserved == 0) {
|
||||
this->DecodeOne7Bit(encoded, decoded);
|
||||
} else {
|
||||
this->DecodeThree7Bit(encoded, decoded);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (encoded->two5bit.reserved == 0) {
|
||||
this->DecodeTwo5Bit(encoded, decoded);
|
||||
} else {
|
||||
this->DecodeTwo7Bit(encoded, decoded);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this->DecodeThree5Bit(encoded, decoded);
|
||||
break;
|
||||
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
};
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeOne5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->one5bit.amfm_5bit_lo, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->one5bit.amfm_5bit_lo, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->one5bit.amfm_5bit_hi, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->one5bit.amfm_5bit_hi, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
decoded->count = 1;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeOne7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
m_state.lo_amp_linear = Am7BitLookup[encoded->one7bit.am_7bit_lo];
|
||||
m_state.lo_freq_linear = Fm7BitLookup[encoded->one7bit.fm_7bit_lo];
|
||||
m_state.hi_amp_linear = Am7BitLookup[encoded->one7bit.am_7bit_hi];
|
||||
m_state.hi_freq_linear = Fm7BitLookup[encoded->one7bit.fm_7bit_hi];
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
decoded->count = 1;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeTwo5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->two5bit.amfm_5bit_lo_0, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->two5bit.amfm_5bit_lo_0, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->two5bit.amfm_5bit_hi_0, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->two5bit.amfm_5bit_hi_0, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->two5bit.amfm_5bit_lo_1, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->two5bit.amfm_5bit_lo_1, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->two5bit.amfm_5bit_hi_1, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->two5bit.amfm_5bit_hi_1, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[1]);
|
||||
|
||||
decoded->count = 2;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeTwo7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
if (encoded->two7bit.high_select) {
|
||||
m_state.hi_amp_linear = Am7BitLookup[encoded->two7bit.am_7bit_xx];
|
||||
m_state.hi_freq_linear = Fm7BitLookup[encoded->two7bit.fm_7bit_xx];
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->two7bit.amfm_5bit_xx_0, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->two7bit.amfm_5bit_xx_0, m_state.lo_freq_linear);
|
||||
} else {
|
||||
m_state.lo_amp_linear = Am7BitLookup[encoded->two7bit.am_7bit_xx];
|
||||
m_state.lo_freq_linear = Fm7BitLookup[encoded->two7bit.fm_7bit_xx];
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->two7bit.amfm_5bit_xx_0, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->two7bit.amfm_5bit_xx_0, m_state.hi_freq_linear);
|
||||
}
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->two7bit.amfm_5bit_lo_1, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->two7bit.amfm_5bit_lo_1, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->two7bit.amfm_5bit_hi_1, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->two7bit.amfm_5bit_hi_1, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[1]);
|
||||
|
||||
decoded->count = 2;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeThree5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_lo_0, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_lo_0, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_hi_0, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_hi_0, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_lo_1, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_lo_1, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_hi_1, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_hi_1, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[1]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_lo_2, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_lo_2, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->three5bit.amfm_5bit_hi_2, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->three5bit.amfm_5bit_hi_2, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[2]);
|
||||
|
||||
decoded->count = 3;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::DecodeThree7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded) {
|
||||
if (encoded->three7bit.high_select) {
|
||||
if (encoded->three7bit.freq_select) {
|
||||
m_state.hi_freq_linear = Fm7BitLookup[encoded->three7bit.xx_7bit_xx];
|
||||
} else {
|
||||
m_state.hi_amp_linear = Am7BitLookup[encoded->three7bit.xx_7bit_xx];
|
||||
}
|
||||
} else {
|
||||
if (encoded->three7bit.freq_select) {
|
||||
m_state.lo_freq_linear = Fm7BitLookup[encoded->three7bit.xx_7bit_xx];
|
||||
} else {
|
||||
m_state.lo_amp_linear = Am7BitLookup[encoded->three7bit.xx_7bit_xx];
|
||||
}
|
||||
}
|
||||
this->GetCurrentOutputValue(&decoded->samples[0]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->three7bit.amfm_5bit_lo_1, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->three7bit.amfm_5bit_lo_1, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->three7bit.amfm_5bit_hi_1, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->three7bit.amfm_5bit_hi_1, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[1]);
|
||||
|
||||
m_state.lo_amp_linear = ApplyAmCommand(encoded->three7bit.amfm_5bit_lo_2, m_state.lo_amp_linear);
|
||||
m_state.lo_freq_linear = ApplyFmCommand(encoded->three7bit.amfm_5bit_lo_2, m_state.lo_freq_linear);
|
||||
m_state.hi_amp_linear = ApplyAmCommand(encoded->three7bit.amfm_5bit_hi_2, m_state.hi_amp_linear);
|
||||
m_state.hi_freq_linear = ApplyFmCommand(encoded->three7bit.amfm_5bit_hi_2, m_state.hi_freq_linear);
|
||||
this->GetCurrentOutputValue(&decoded->samples[2]);
|
||||
|
||||
decoded->count = 3;
|
||||
}
|
||||
|
||||
void SwitchRumbleDecoder::GetCurrentOutputValue(SwitchVibrationValues* output) {
|
||||
output->low_band_amp = ExpBase2Lookup[GetLookupIndex(m_state.lo_amp_linear)];
|
||||
output->low_band_freq = ExpBase2Lookup[GetLookupIndex(m_state.lo_freq_linear)] * CenterFreqLow;
|
||||
output->high_band_amp = ExpBase2Lookup[GetLookupIndex(m_state.hi_amp_linear)];
|
||||
output->high_band_freq = ExpBase2Lookup[GetLookupIndex(m_state.hi_freq_linear)] * CenterFreqHigh;
|
||||
}
|
||||
|
||||
}
|
123
mc_mitm/source/controllers/switch_rumble_decoder.hpp
Normal file
123
mc_mitm/source/controllers/switch_rumble_decoder.hpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024 ndeadly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::controller {
|
||||
|
||||
struct SwitchVibrationValues {
|
||||
float low_band_amp;
|
||||
float low_band_freq;
|
||||
float high_band_amp;
|
||||
float high_band_freq;
|
||||
};
|
||||
|
||||
struct SwitchVibrationSamples {
|
||||
u8 count;
|
||||
SwitchVibrationValues samples[3];
|
||||
};
|
||||
|
||||
struct SwitchEncodedVibrationSamples {
|
||||
union {
|
||||
struct {
|
||||
u32 data : 30;
|
||||
u32 packet_type : 2;
|
||||
};
|
||||
|
||||
struct {
|
||||
u32 reserved : 20; // Zero padding
|
||||
u32 amfm_5bit_hi : 5; // 5-bit amfm hi [0]
|
||||
u32 amfm_5bit_lo : 5; // 5-bit amfm lo [0]
|
||||
u32 packet_type : 2; // 1
|
||||
} one5bit;
|
||||
|
||||
struct {
|
||||
u32 reserved : 2; // Zero padding
|
||||
u32 fm_7bit_hi : 7; // 7-bit fm hi [0]
|
||||
u32 am_7bit_hi : 7; // 7-bit am hi [0]
|
||||
u32 fm_7bit_lo : 7; // 7-bit fm lo [0]
|
||||
u32 am_7bit_lo : 7; // 7-bit am lo [0]
|
||||
u32 packet_type : 2; // 1
|
||||
} one7bit;
|
||||
|
||||
struct {
|
||||
u32 reserved : 10; // Zero padding
|
||||
u32 amfm_5bit_hi_1 : 5; // 5-bit amfm hi [1]
|
||||
u32 amfm_5bit_lo_1 : 5; // 5-bit amfm lo [1]
|
||||
u32 amfm_5bit_hi_0 : 5; // 5-bit amfm hi [0]
|
||||
u32 amfm_5bit_lo_0 : 5; // 5-bit amfm lo [0]
|
||||
u32 packet_type : 2; // 2
|
||||
} two5bit;
|
||||
|
||||
struct {
|
||||
u32 high_select : 1; // Whether 7-bit values are high or low
|
||||
u32 fm_7bit_xx : 7; // 7-bit fm hi/lo [0], hi or lo denoted by high_select bit
|
||||
u32 amfm_5bit_hi_1 : 5; // 5-bit amfm hi [1]
|
||||
u32 amfm_5bit_lo_1 : 5; // 5-bit amfm lo [1]
|
||||
u32 amfm_5bit_xx_0 : 5; // 5-bit amfm lo/hi [0], denoted by ~high_select
|
||||
u32 am_7bit_xx : 7; // 7-bit am hi/lo [0], hi or lo denoted by high_select bit
|
||||
u32 packet_type : 2; // 2
|
||||
} two7bit;
|
||||
|
||||
struct {
|
||||
u32 amfm_5bit_hi_2 : 5; // 5-bit amfm hi [2]
|
||||
u32 amfm_5bit_lo_2 : 5; // 5-bit amfm lo [2]
|
||||
u32 amfm_5bit_hi_1 : 5; // 5-bit amfm hi [1]
|
||||
u32 amfm_5bit_lo_1 : 5; // 5-bit amfm lo [1]
|
||||
u32 amfm_5bit_hi_0 : 5; // 5-bit amfm hi [0]
|
||||
u32 amfm_5bit_lo_0 : 5; // 5-bit amfm lo [0]
|
||||
u32 packet_type : 2; // 3
|
||||
} three5bit;
|
||||
|
||||
struct {
|
||||
u32 high_select : 1; // Whether 7-bit value is high or low
|
||||
u32 : 1; // Always 1
|
||||
u32 freq_select : 1; // Whether 7-bit value is freq or amp
|
||||
u32 amfm_5bit_hi_2 : 5; // 5-bit amfm hi [2]
|
||||
u32 amfm_5bit_lo_2 : 5; // 5-bit amfm lo [2]
|
||||
u32 amfm_5bit_hi_1 : 5; // 5-bit amfm hi [1]
|
||||
u32 amfm_5bit_lo_1 : 5; // 5-bit amfm lo [1]
|
||||
u32 xx_7bit_xx : 7; // 7-bit am/fm lo/hi [0], denoted by freq_select and high_select bits
|
||||
u32 packet_type : 2; // 1
|
||||
} three7bit;
|
||||
};
|
||||
} PACKED;
|
||||
|
||||
class SwitchRumbleDecoder {
|
||||
public:
|
||||
SwitchRumbleDecoder();
|
||||
|
||||
void DecodeSamples(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void GetCurrentOutputValue(SwitchVibrationValues *output);
|
||||
|
||||
private:
|
||||
void DecodeOne5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void DecodeOne7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void DecodeTwo5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void DecodeTwo7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void DecodeThree5Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
void DecodeThree7Bit(const SwitchEncodedVibrationSamples *encoded, SwitchVibrationSamples *decoded);
|
||||
|
||||
private:
|
||||
struct {
|
||||
float lo_amp_linear;
|
||||
float lo_freq_linear;
|
||||
float hi_amp_linear;
|
||||
float hi_freq_linear;
|
||||
} m_state;
|
||||
};
|
||||
|
||||
}
|
38
mc_mitm/source/controllers/switch_rumble_handler.cpp
Normal file
38
mc_mitm/source/controllers/switch_rumble_handler.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024 ndeadly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "switch_rumble_handler.hpp"
|
||||
|
||||
namespace ams::controller {
|
||||
|
||||
bool SwitchRumbleHandler::GetDecodedValues(const SwitchEncodedMotorData *encoded, SwitchMotorData *decoded) {
|
||||
return this->GetNextDecodedValue(&m_decoder_left, &encoded->left_motor, &decoded->left_motor) | this->GetNextDecodedValue(&m_decoder_right, &encoded->right_motor, &decoded->right_motor);
|
||||
}
|
||||
|
||||
bool SwitchRumbleHandler::GetNextDecodedValue(SwitchRumbleDecoder *decoder, const SwitchEncodedVibrationSamples *encoded_samples, SwitchVibrationValues *out_sample) {
|
||||
SwitchVibrationSamples decoded_samples;
|
||||
decoder->DecodeSamples(encoded_samples, &decoded_samples);
|
||||
if (decoded_samples.count > 0) {
|
||||
// We will just take the first decoded sample and ignore the others
|
||||
*out_sample = decoded_samples.samples[0];
|
||||
} else {
|
||||
// Repeat the current sample if no new samples were decoded
|
||||
decoder->GetCurrentOutputValue(out_sample);
|
||||
}
|
||||
|
||||
return decoded_samples.count > 0;
|
||||
}
|
||||
|
||||
}
|
44
mc_mitm/source/controllers/switch_rumble_handler.hpp
Normal file
44
mc_mitm/source/controllers/switch_rumble_handler.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2024 ndeadly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "switch_rumble_decoder.hpp"
|
||||
|
||||
namespace ams::controller {
|
||||
|
||||
struct SwitchEncodedMotorData {
|
||||
SwitchEncodedVibrationSamples left_motor;
|
||||
SwitchEncodedVibrationSamples right_motor;
|
||||
} PACKED;
|
||||
|
||||
struct SwitchMotorData {
|
||||
SwitchVibrationValues left_motor;
|
||||
SwitchVibrationValues right_motor;
|
||||
};
|
||||
|
||||
class SwitchRumbleHandler {
|
||||
public:
|
||||
bool GetDecodedValues(const SwitchEncodedMotorData *encoded, SwitchMotorData *decoded);
|
||||
|
||||
private:
|
||||
bool GetNextDecodedValue(SwitchRumbleDecoder *decoder, const SwitchEncodedVibrationSamples *encoded_samples, SwitchVibrationValues *out_sample);
|
||||
|
||||
private:
|
||||
SwitchRumbleDecoder m_decoder_left;
|
||||
SwitchRumbleDecoder m_decoder_right;
|
||||
};
|
||||
|
||||
}
|
|
@ -914,11 +914,11 @@ namespace ams::controller {
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result WiiController::SetVibration(const SwitchRumbleData *rumble_data) {
|
||||
m_rumble_state = rumble_data[0].low_band_amp > 0 ||
|
||||
rumble_data[0].high_band_amp > 0 ||
|
||||
rumble_data[1].low_band_amp > 0 ||
|
||||
rumble_data[1].high_band_amp > 0;
|
||||
Result WiiController::SetVibration(const SwitchMotorData *motor_data) {
|
||||
m_rumble_state = motor_data->left_motor.low_band_amp > 0 ||
|
||||
motor_data->left_motor.high_band_amp > 0 ||
|
||||
motor_data->right_motor.low_band_amp > 0 ||
|
||||
motor_data->right_motor.high_band_amp > 0;
|
||||
|
||||
std::scoped_lock lk(m_output_mutex);
|
||||
|
||||
|
|
|
@ -500,7 +500,7 @@ namespace ams::controller {
|
|||
, m_mp_state_changing(false) { }
|
||||
|
||||
Result Initialize();
|
||||
Result SetVibration(const SwitchRumbleData *rumble_data);
|
||||
Result SetVibration(const SwitchMotorData *motor_data);
|
||||
Result CancelVibration();
|
||||
Result SetPlayerLed(u8 led_mask);
|
||||
void ProcessInputData(const bluetooth::HidReport *report) override;
|
||||
|
|
|
@ -24,13 +24,13 @@ namespace ams::controller {
|
|||
|
||||
}
|
||||
|
||||
Result XboxOneController::SetVibration(const SwitchRumbleData *rumble_data) {
|
||||
Result XboxOneController::SetVibration(const SwitchMotorData *motor_data) {
|
||||
auto report = reinterpret_cast<XboxOneReportData *>(m_output_report.data);
|
||||
m_output_report.size = sizeof(XboxOneOutputReport0x03) + 1;
|
||||
report->id = 0x03;
|
||||
report->output0x03.enable = 0x3;
|
||||
report->output0x03.magnitude_strong = static_cast<u8>(100 * std::max(rumble_data[0].low_band_amp, rumble_data[1].low_band_amp));
|
||||
report->output0x03.magnitude_weak = static_cast<u8>(100 * std::max(rumble_data[0].high_band_amp, rumble_data[1].high_band_amp));
|
||||
report->output0x03.magnitude_strong = static_cast<u8>(100 * std::max(motor_data->left_motor.low_band_amp, motor_data->right_motor.low_band_amp));
|
||||
report->output0x03.magnitude_weak = static_cast<u8>(100 * std::max(motor_data->left_motor.high_band_amp, motor_data->right_motor.high_band_amp));
|
||||
report->output0x03.pulse_sustain_10ms = 1;
|
||||
report->output0x03.pulse_release_10ms = 0;
|
||||
report->output0x03.loop_count = 0;
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace ams::controller {
|
|||
XboxOneController(const bluetooth::Address *address, HardwareID id)
|
||||
: EmulatedSwitchController(address, id) { }
|
||||
|
||||
Result SetVibration(const SwitchRumbleData *rumble_data);
|
||||
Result SetVibration(const SwitchMotorData *motor_data);
|
||||
void ProcessInputData(const bluetooth::HidReport *report) override;
|
||||
|
||||
private:
|
||||
|
|
Loading…
Add table
Reference in a new issue