mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-23 05:39:18 +01:00
Improve Accuracy of Vibration + Unify Translation + Add Comments
This commit is contained in:
parent
1a58a2e967
commit
21e2c826a1
@ -11,7 +11,7 @@ namespace skyline::input {
|
|||||||
*/
|
*/
|
||||||
struct GuestController {
|
struct GuestController {
|
||||||
NpadControllerType type{};
|
NpadControllerType type{};
|
||||||
i8 partnerIndex{-1}; //!< The index of a Joy-Con partner, if this has one
|
i8 partnerIndex{constant::NullIndex}; //!< The index of a Joy-Con partner, if this has one
|
||||||
NpadDevice *device{nullptr}; //!< A pointer to the NpadDevice that all events from this are redirected to
|
NpadDevice *device{nullptr}; //!< A pointer to the NpadDevice that all events from this are redirected to
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -352,33 +352,75 @@ namespace skyline::input {
|
|||||||
globalTimestamp++;
|
globalTimestamp++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpadDevice::VibrateDevice(i8 vibrateIndex, const NpadVibrationValue &value) {
|
struct VibrationInfo {
|
||||||
std::array<jlong, 3> timings;
|
jlong period;
|
||||||
std::array<jint, 3> amplitudes;
|
jint amplitude;
|
||||||
|
jlong start;
|
||||||
|
jlong end;
|
||||||
|
|
||||||
jlong periodLow = 1000 / value.frequencyLow;
|
VibrationInfo(float frequency, float amplitude) : period(constant::MsInSecond / frequency), amplitude(amplitude), start(0), end(period) {}
|
||||||
jlong periodHigh = 1000 / value.frequencyHigh;
|
};
|
||||||
|
|
||||||
jint amplitudeLow = value.amplitudeLow * 127;
|
template<size_t Size>
|
||||||
jint amplitudeHigh = value.amplitudeHigh * 127;
|
void VibrateDevice(const std::shared_ptr<JvmManager> &jvm, i8 index, std::array<VibrationInfo, Size> vibrations) {
|
||||||
|
jlong totalTime{};
|
||||||
|
std::sort(vibrations.begin(), vibrations.end(), [](const VibrationInfo &a, const VibrationInfo &b) {
|
||||||
|
return a.period < b.period;
|
||||||
|
});
|
||||||
|
|
||||||
if (amplitudeLow + amplitudeHigh == 0 || periodLow + periodHigh == 0) {
|
jint totalAmplitude{};
|
||||||
manager.state.jvm->ClearVibrationDevice(vibrateIndex);
|
for (const auto &vibration : vibrations)
|
||||||
|
totalAmplitude += vibration.amplitude;
|
||||||
|
|
||||||
|
// If this vibration is essentially null then we don't play rather clear any running vibrations
|
||||||
|
if (totalAmplitude == 0 || vibrations[3].period == 0) {
|
||||||
|
jvm->ClearVibrationDevice(index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (periodLow == periodHigh) {
|
// We output an approximation of the combined + linearized vibration data into these arrays, larger arrays would allow for more accurate reproduction of data
|
||||||
timings = {periodLow, periodHigh, 0};
|
std::array<jlong, 50> timings;
|
||||||
amplitudes = {std::min(amplitudeLow + amplitudeHigh, 255), 0, 0};
|
std::array<jint, 50> amplitudes;
|
||||||
} else if (periodLow < periodHigh) {
|
|
||||||
timings = {periodLow, periodHigh - periodLow, periodHigh};
|
// We are essentially unrolling the bands into a linear sequence, due to the data not being always linearizable there will be inaccuracies at the ends unless there's a pattern that's repeatable which will happen when all band's frequencies are factors of each other
|
||||||
amplitudes = {std::min(amplitudeLow + amplitudeHigh, 255), amplitudeHigh, 0};
|
u8 i{};
|
||||||
} else if (periodHigh < periodLow) {
|
for (; i < timings.size(); i++) {
|
||||||
timings = {periodHigh, periodLow - periodHigh, periodLow};
|
jlong time{};
|
||||||
amplitudes = {std::min(amplitudeHigh + amplitudeLow, 255), amplitudeLow, 0};
|
|
||||||
|
u8 startCycleCount{};
|
||||||
|
for (u8 n{}; n < vibrations.size(); n++) {
|
||||||
|
auto &vibration = vibrations[n];
|
||||||
|
if (totalTime <= vibration.start) {
|
||||||
|
vibration.start = vibration.end + vibration.period;
|
||||||
|
totalAmplitude += vibration.amplitude;
|
||||||
|
time = std::max(vibration.period, time);
|
||||||
|
startCycleCount++;
|
||||||
|
} else if (totalTime <= vibration.start) {
|
||||||
|
vibration.end = vibration.start + vibration.period;
|
||||||
|
totalAmplitude -= vibration.amplitude;
|
||||||
|
time = std::max(vibration.period, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all bands start again at this point then we can end the pattern here as a loop to the front will be flawless
|
||||||
|
if (i && startCycleCount == vibrations.size())
|
||||||
|
break;
|
||||||
|
|
||||||
|
timings[i] = time;
|
||||||
|
totalTime += time;
|
||||||
|
|
||||||
|
amplitudes[i] = std::min(totalAmplitude, constant::AmplitudeMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.state.jvm->VibrateDevice(vibrateIndex, timings, amplitudes);
|
jvm->VibrateDevice(index, std::span(timings.begin(), timings.begin() + i), std::span(amplitudes.begin(), amplitudes.begin() + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VibrateDevice(const std::shared_ptr<JvmManager> &jvm, i8 index, const NpadVibrationValue &value) {
|
||||||
|
std::array<VibrationInfo, 2> vibrations{
|
||||||
|
VibrationInfo{value.frequencyLow, value.amplitudeLow * (constant::AmplitudeMax / 2)},
|
||||||
|
{value.frequencyHigh, value.amplitudeHigh * (constant::AmplitudeMax / 2)},
|
||||||
|
};
|
||||||
|
VibrateDevice(jvm, index, vibrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpadDevice::Vibrate(bool isRight, const NpadVibrationValue &value) {
|
void NpadDevice::Vibrate(bool isRight, const NpadVibrationValue &value) {
|
||||||
@ -390,52 +432,21 @@ namespace skyline::input {
|
|||||||
if (vibrationRight)
|
if (vibrationRight)
|
||||||
Vibrate(vibrationLeft, *vibrationRight);
|
Vibrate(vibrationLeft, *vibrationRight);
|
||||||
else
|
else
|
||||||
VibrateDevice(index, value);
|
VibrateDevice(manager.state.jvm, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpadDevice::Vibrate(const NpadVibrationValue &left, const NpadVibrationValue &right) {
|
void NpadDevice::Vibrate(const NpadVibrationValue &left, const NpadVibrationValue &right) {
|
||||||
if (partnerIndex == -1) {
|
if (partnerIndex == constant::NullIndex) {
|
||||||
std::array<jlong, 5> timings;
|
std::array<VibrationInfo, 4> vibrations{
|
||||||
std::array<jint, 5> amplitudes;
|
VibrationInfo{left.frequencyLow, left.amplitudeLow * (constant::AmplitudeMax / 4)},
|
||||||
|
{left.frequencyHigh, left.amplitudeHigh * (constant::AmplitudeMax / 4)},
|
||||||
std::array<std::pair<jlong, jint>, 4> vibrations{std::pair<jlong, jint>{1000 / left.frequencyLow, left.amplitudeLow * 64},
|
{right.frequencyLow, right.amplitudeLow * (constant::AmplitudeMax / 4)},
|
||||||
{1000 / left.frequencyHigh, left.amplitudeHigh * 64},
|
{right.frequencyHigh, right.amplitudeHigh * (constant::AmplitudeMax / 4)},
|
||||||
{1000 / right.frequencyLow, right.amplitudeLow * 64},
|
|
||||||
{1000 / right.frequencyHigh, right.amplitudeHigh * 64},
|
|
||||||
};
|
};
|
||||||
|
VibrateDevice<4>(manager.state.jvm, index, vibrations);
|
||||||
jlong totalTime{};
|
|
||||||
std::sort(vibrations.begin(), vibrations.end(), [](const std::pair<jlong, jint> &a, const std::pair<jlong, jint> &b) {
|
|
||||||
return a.first < b.first;
|
|
||||||
});
|
|
||||||
|
|
||||||
jint totalAmplitude{};
|
|
||||||
for (const auto &vibration : vibrations)
|
|
||||||
totalAmplitude += vibration.second;
|
|
||||||
|
|
||||||
if (totalAmplitude == 0 || vibrations[3].first == 0) {
|
|
||||||
manager.state.jvm->ClearVibrationDevice(index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u8 i{0}; i < vibrations.size(); i++) {
|
|
||||||
const auto &vibration = vibrations[i];
|
|
||||||
|
|
||||||
auto time = vibration.first - totalTime;
|
|
||||||
timings[i] = time;
|
|
||||||
totalTime += time;
|
|
||||||
|
|
||||||
amplitudes[i] = std::min(totalAmplitude, 255);
|
|
||||||
totalAmplitude -= vibration.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
timings[4] = totalTime;
|
|
||||||
amplitudes[4] = 0;
|
|
||||||
|
|
||||||
manager.state.jvm->VibrateDevice(index, timings, amplitudes);
|
|
||||||
} else {
|
} else {
|
||||||
VibrateDevice(index, left);
|
VibrateDevice(manager.state.jvm, index, left);
|
||||||
VibrateDevice(partnerIndex, right);
|
VibrateDevice(manager.state.jvm, partnerIndex, right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
#include <kernel/types/KEvent.h>
|
#include <kernel/types/KEvent.h>
|
||||||
#include "shared_mem.h"
|
#include "shared_mem.h"
|
||||||
|
|
||||||
|
namespace skyline::constant {
|
||||||
|
constexpr jlong MsInSecond = 1000; //!< The amount of milliseconds in a single second of time
|
||||||
|
constexpr jint AmplitudeMax = std::numeric_limits<u8>::max(); //!< The maximum amplitude for Android Vibration APIs
|
||||||
|
constexpr i8 NullIndex = -1; //!< The placeholder index value when there is no device present
|
||||||
|
}
|
||||||
|
|
||||||
namespace skyline::input {
|
namespace skyline::input {
|
||||||
/**
|
/**
|
||||||
* @brief This enumerates the orientations the Joy-Con(s) can be held in
|
* @brief This enumerates the orientations the Joy-Con(s) can be held in
|
||||||
@ -74,7 +80,7 @@ namespace skyline::input {
|
|||||||
bool isSixAxisSingle : 1; //!< If the Six-Axis device is a single unit, either Handheld or Pro-Controller
|
bool isSixAxisSingle : 1; //!< If the Six-Axis device is a single unit, either Handheld or Pro-Controller
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr NpadControllerType GetType() {
|
constexpr NpadControllerType GetType() const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 3:
|
case 3:
|
||||||
return NpadControllerType::ProController;
|
return NpadControllerType::ProController;
|
||||||
@ -129,12 +135,10 @@ namespace skyline::input {
|
|||||||
*/
|
*/
|
||||||
NpadControllerInfo &GetControllerInfo();
|
NpadControllerInfo &GetControllerInfo();
|
||||||
|
|
||||||
void VibrateDevice(i8 index, const NpadVibrationValue &value);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NpadId id;
|
NpadId id;
|
||||||
i8 index{-1}; //!< The index of the device assigned to this player
|
i8 index{constant::NullIndex}; //!< The index of the device assigned to this player
|
||||||
i8 partnerIndex{-1}; //!< The index of a partner device, if present
|
i8 partnerIndex{constant::NullIndex}; //!< The index of a partner device, if present
|
||||||
NpadVibrationValue vibrationLeft; //!< Vibration for the left Joy-Con (Handheld/Pair), left LRA in a Pro-Controller or individual Joy-Cons
|
NpadVibrationValue vibrationLeft; //!< Vibration for the left Joy-Con (Handheld/Pair), left LRA in a Pro-Controller or individual Joy-Cons
|
||||||
std::optional<NpadVibrationValue> vibrationRight; //!< Vibration for the right Joy-Con (Handheld/Pair) or right LRA in a Pro-Controller
|
std::optional<NpadVibrationValue> vibrationRight; //!< Vibration for the right Joy-Con (Handheld/Pair) or right LRA in a Pro-Controller
|
||||||
NpadControllerType type{};
|
NpadControllerType type{};
|
||||||
|
@ -121,18 +121,17 @@ namespace skyline::service::hid {
|
|||||||
auto &valueBuf = request.inputBuf.at(1);
|
auto &valueBuf = request.inputBuf.at(1);
|
||||||
std::span values(reinterpret_cast<NpadVibrationValue *>(valueBuf.address), valueBuf.size / sizeof(NpadVibrationValue));
|
std::span values(reinterpret_cast<NpadVibrationValue *>(valueBuf.address), valueBuf.size / sizeof(NpadVibrationValue));
|
||||||
|
|
||||||
for (int i = 0; i < handles.size(); ++i) {
|
for (size_t i{}; i < handles.size(); ++i) {
|
||||||
auto &handle = handles[i];
|
const auto &handle = handles[i];
|
||||||
|
|
||||||
auto &device = state.input->npad.at(handle.id);
|
auto &device = state.input->npad.at(handle.id);
|
||||||
if (device.type == handle.GetType()) {
|
if (device.type == handle.GetType()) {
|
||||||
if (i + 1 != handles.size() && handles[i + 1].id == handle.id && handles[i + 1].isRight && !handle.isRight) {
|
if (i + 1 != handles.size() && handles[i + 1].id == handle.id && handles[i + 1].isRight && !handle.isRight) {
|
||||||
state.logger->Info("Vibration #{}&{} - Handle: 0x{:02X} (0b{:05b}), Vibration: {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz - {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz", i, i + 1, u8(handle.id), u8(handle.type), values[i].amplitudeLow, values[i].frequencyLow, values[i].amplitudeHigh, values[i].frequencyHigh, values[i + 1].amplitudeLow, values[i + 1].frequencyLow, values[i + 1].amplitudeHigh, values[i + 1].frequencyHigh);
|
state.logger->Debug("Vibration #{}&{} - Handle: 0x{:02X} (0b{:05b}), Vibration: {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz - {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz", i, i + 1, u8(handle.id), u8(handle.type), values[i].amplitudeLow, values[i].frequencyLow, values[i].amplitudeHigh, values[i].frequencyHigh, values[i + 1].amplitudeLow, values[i + 1].frequencyLow, values[i + 1].amplitudeHigh, values[i + 1].frequencyHigh);
|
||||||
device.Vibrate(values[i], values[i + 1]);
|
device.Vibrate(values[i], values[i + 1]);
|
||||||
i++;
|
i++;
|
||||||
} else {
|
} else {
|
||||||
auto &value = values[i];
|
const auto &value = values[i];
|
||||||
state.logger->Info("Vibration #{} - Handle: 0x{:02X} (0b{:05b}), Vibration: {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz", i, u8(handle.id), u8(handle.type), value.amplitudeLow, value.frequencyLow, value.amplitudeHigh, value.frequencyHigh);
|
state.logger->Debug("Vibration #{} - Handle: 0x{:02X} (0b{:05b}), Vibration: {:.2f}@{:.2f}Hz, {:.2f}@{:.2f}Hz", i, u8(handle.id), u8(handle.type), value.amplitudeLow, value.frequencyLow, value.amplitudeHigh, value.frequencyHigh);
|
||||||
device.Vibrate(handle.isRight, value);
|
device.Vibrate(handle.isRight, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user