From 458300b02ec9934948d7c1e9255bde1e0c987456 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Jun 2024 23:28:04 -0700 Subject: [PATCH] Core: Improve rumble emulation by averaging state over entire frame (fixes #3232) --- CHANGES | 1 + include/mgba/core/interface.h | 15 +++++++- include/mgba/internal/gb/memory.h | 1 + include/mgba/internal/gba/gba.h | 1 + src/core/interface.c | 41 ++++++++++++++++++++++ src/core/scripting.c | 15 ++++++-- src/gb/gb.c | 6 ++++ src/gb/mbc.c | 1 + src/gb/mbc/mbc.c | 4 ++- src/gb/mbc/unlicensed.c | 4 ++- src/gba/cart/gpio.c | 4 ++- src/gba/gba.c | 7 ++++ src/gba/sio/gbp.c | 5 +-- src/platform/libretro/libretro.c | 34 +++++------------- src/platform/psp2/psp2-context.c | 27 ++++----------- src/platform/qt/input/SDLInputDriver.cpp | 2 +- src/platform/sdl/main.c | 2 +- src/platform/sdl/sdl-events.c | 44 +++++++----------------- src/platform/sdl/sdl-events.h | 5 +-- src/platform/switch/main.c | 40 +++++++-------------- src/platform/wii/main.c | 5 +-- 21 files changed, 142 insertions(+), 122 deletions(-) diff --git a/CHANGES b/CHANGES index 2ff272fb6..626e9aa41 100644 --- a/CHANGES +++ b/CHANGES @@ -30,6 +30,7 @@ Other fixes: - Updater: Fix updating appimage across filesystems Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) + - Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232) - GB: Prevent incompatible BIOSes from being used on differing models - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index b07f300f3..3e6261e9c 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -110,9 +110,22 @@ struct mRTCGenericState { void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core); struct mRumble { - void (*setRumble)(struct mRumble*, int enable); + void (*reset)(struct mRumble*, bool enable); + void (*setRumble)(struct mRumble*, bool enable, uint32_t sinceLast); + void (*integrate)(struct mRumble*, uint32_t period); }; +struct mRumbleIntegrator { + struct mRumble d; + bool state; + uint32_t timeOn; + uint32_t totalTime; + + void (*setRumble)(struct mRumbleIntegrator*, float value); +}; + +void mRumbleIntegratorInit(struct mRumbleIntegrator*); + struct mCoreChannelInfo { size_t id; const char* internalName; diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 052a9f77e..e4b34088b 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -339,6 +339,7 @@ struct GBMemory { struct mRTCSource* rtc; struct mRotationSource* rotation; struct mRumble* rumble; + int32_t lastRumble; struct mImageSource* cam; }; diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index e66ac47f3..83246ff98 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -91,6 +91,7 @@ struct GBA { struct GBALuminanceSource* luminanceSource; struct mRTCSource* rtcSource; struct mRumble* rumble; + int32_t lastRumble; bool isPristine; size_t pristineRomSize; diff --git a/src/core/interface.c b/src/core/interface.c index d63754895..e5b7f8378 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -109,3 +109,44 @@ void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) { rtc->d.serialize = _rtcGenericSerialize; rtc->d.deserialize = _rtcGenericDeserialize; } + +static void mRumbleIntegratorReset(struct mRumble* rumble, bool enable) { + struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; + integrator->state = enable; + integrator->timeOn = 0; + integrator->totalTime = 0; +} + +static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) { + struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; + + if (integrator->state) { + integrator->timeOn += sinceLast; + } + integrator->totalTime += sinceLast; + integrator->state = enable; +} + +static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) { + if (!period) { + return; + } + + struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; + if (integrator->state) { + integrator->timeOn += period - integrator->totalTime; + } + integrator->setRumble(integrator, fminf(integrator->timeOn / (float) period, 1.0f)); + + integrator->totalTime = 0; + integrator->timeOn = 0; +} + +void mRumbleIntegratorInit(struct mRumbleIntegrator* integrator) { + integrator->d.reset = mRumbleIntegratorReset; + integrator->d.setRumble = mRumbleIntegratorSetRumble; + integrator->d.integrate = mRumbleIntegratorIntegrate; + + integrator->state = false; + integrator->timeOn = 0; +} diff --git a/src/core/scripting.c b/src/core/scripting.c index 13cc403c8..ea0af459d 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -196,6 +196,7 @@ struct mScriptCoreAdapter { struct mScriptDebugger debugger; #endif struct mRumble rumble; + struct mRumbleIntegrator rumbleIntegrator; struct mRumble* oldRumble; struct mRotationSource rotation; struct mScriptValue* rotationCbTable; @@ -1165,16 +1166,22 @@ mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) mSCRIPT_DEFINE_END; -static void _setRumble(struct mRumble* rumble, int enable) { +static void _setRumble(struct mRumble* rumble, bool enable, uint32_t timeSince) { struct mScriptCoreAdapter* adapter = containerof(rumble, struct mScriptCoreAdapter, rumble); if (adapter->oldRumble) { - adapter->oldRumble->setRumble(adapter->oldRumble, enable); + adapter->oldRumble->setRumble(adapter->oldRumble, enable, timeSince); } + adapter->rumbleIntegrator.d.setRumble(&adapter->rumbleIntegrator.d, enable, timeSince); +} + +static void _setRumbleFloat(struct mRumbleIntegrator* integrator, float level) { + struct mScriptCoreAdapter* adapter = containerof(integrator, struct mScriptCoreAdapter, rumbleIntegrator); + struct mScriptList args; mScriptListInit(&args, 1); - *mScriptListAppend(&args) = mSCRIPT_MAKE_BOOL(!!enable); + *mScriptListAppend(&args) = mSCRIPT_MAKE_F32(level); mScriptContextTriggerCallback(adapter->context, "rumble", &args); mScriptListDeinit(&args); } @@ -1293,6 +1300,8 @@ void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core adapter->memory.type = mSCRIPT_TYPE_MS_TABLE; adapter->memory.type->alloc(&adapter->memory); + mRumbleIntegratorInit(&adapter->rumbleIntegrator); + adapter->rumbleIntegrator.setRumble = _setRumbleFloat; adapter->rumble.setRumble = _setRumble; adapter->rotation.sample = _rotationSample; adapter->rotation.readTiltX = _rotationReadTiltX; diff --git a/src/gb/gb.c b/src/gb/gb.c index c0e966c27..c6198ed58 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -1174,6 +1174,12 @@ void GBFrameEnded(struct GB* gb) { } } + struct mRumble* rumble = gb->memory.rumble; + if (rumble && rumble->integrate) { + gb->memory.lastRumble = mTimingCurrentTime(&gb->timing); + rumble->integrate(rumble, GB_VIDEO_TOTAL_LENGTH); + } + // TODO: Move to common code if (gb->stream && gb->stream->postVideoFrame) { const color_t* pixels; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 76c005f96..c9ebed546 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -540,6 +540,7 @@ void GBMBCReset(struct GB* gb) { gb->memory.cartBus = 0xFF; gb->memory.cartBusPc = 0; gb->memory.cartBusDecay = 1; + gb->memory.lastRumble = 0; memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); GBMBCInit(gb); diff --git a/src/gb/mbc/mbc.c b/src/gb/mbc/mbc.c index 864b6e9f8..699bdb209 100644 --- a/src/gb/mbc/mbc.c +++ b/src/gb/mbc/mbc.c @@ -253,7 +253,9 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { case 0x4: case 0x5: if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { - memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); + int32_t currentTime = mTimingCurrentTime(&gb->timing); + memory->rumble->setRumble(memory->rumble, (value >> 3) & 1, currentTime - memory->lastRumble); + memory->lastRumble = currentTime; value &= ~8; } GBMBCSwitchSramBank(gb, value & 0xF); diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c index 1577a15fa..950ddf074 100644 --- a/src/gb/mbc/unlicensed.c +++ b/src/gb/mbc/unlicensed.c @@ -228,7 +228,9 @@ void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { } if (mbcState->rumble && memory->rumble) { - memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); + int32_t currentTime = mTimingCurrentTime(&gb->timing); + memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02), currentTime - memory->lastRumble); + memory->lastRumble = currentTime; } break; } diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 0a396f746..fde4e3714 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -354,7 +354,9 @@ void _rumbleReadPins(struct GBACartridgeHardware* hw) { return; } - rumble->setRumble(rumble, !!(hw->pinState & 8)); + int32_t currentTime = mTimingCurrentTime(&hw->p->timing); + rumble->setRumble(rumble, !!(hw->pinState & 8), currentTime - hw->p->lastRumble); + hw->p->lastRumble = currentTime; } // == Light sensor diff --git a/src/gba/gba.c b/src/gba/gba.c index 0ceb8209a..013dcad4e 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -250,6 +250,7 @@ void GBAReset(struct ARMCore* cpu) { gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->yankedRomSize = 0; } + gba->lastRumble = 0; mTimingClear(&gba->timing); GBAMemoryReset(gba); GBAVideoReset(&gba->video); @@ -993,6 +994,12 @@ void GBAFrameEnded(struct GBA* gba) { GBASIOPlayerUpdate(gba); } + struct mRumble* rumble = gba->rumble; + if (rumble && rumble->integrate) { + gba->lastRumble = mTimingCurrentTime(&gba->timing); + rumble->integrate(rumble, VIDEO_TOTAL_LENGTH); + } + size_t c; for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index b5f987352..d992ebe2e 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -108,12 +108,13 @@ uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uin if (gbp->txPosition < 12 && gbp->txPosition > 0) { // TODO: Check expected } else if (gbp->txPosition >= 12) { - uint32_t mask = 0x33; // 0x00 = Stop // 0x11 = Hard Stop // 0x22 = Start if (gbp->p->rumble) { - gbp->p->rumble->setRumble(gbp->p->rumble, (rx & mask) == 0x22); + int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); + gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); + gbp->p->lastRumble = currentTime; } } mTimingDeschedule(&gbp->p->timing, &gbp->event); diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index b7d7d8243..88743b63e 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -33,7 +33,6 @@ * to calculating the average for the last 180 * frames, or 3 seconds of runtime... */ #define SAMPLES_PER_FRAME_MOVING_AVG_ALPHA (1.0f / 180.0f) -#define RUMBLE_PWM 35 #define EVENT_RATE 60 #define VIDEO_WIDTH_MAX 256 @@ -54,7 +53,7 @@ static void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel lev static void _postAudioBuffer(struct mAVStream*, struct mAudioBuffer*); static void _audioRateChanged(struct mAVStream*, unsigned rate); -static void _setRumble(struct mRumble* rumble, int enable); +static void _setRumble(struct mRumbleIntegrator*, float level); static uint8_t _readLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux); static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch); @@ -77,9 +76,7 @@ static void* savedata; static struct mAVStream stream; static bool sensorsInitDone; static bool rumbleInitDone; -static int rumbleUp; -static int rumbleDown; -static struct mRumble rumble; +static struct mRumbleIntegrator rumble; static struct GBALuminanceSource lux; static struct mRotationSource rotation; static bool tiltEnabled; @@ -467,6 +464,7 @@ void retro_init(void) { // TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported rumbleInitDone = false; + mRumbleIntegratorInit(&rumble); rumble.setRumble = _setRumble; rumbleCallback = 0; @@ -645,18 +643,6 @@ void retro_run(void) { } } #endif - - if (rumbleCallback) { - if (rumbleUp) { - rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown)); - rumbleCallback(0, RETRO_RUMBLE_WEAK, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown)); - } else { - rumbleCallback(0, RETRO_RUMBLE_STRONG, 0); - rumbleCallback(0, RETRO_RUMBLE_WEAK, 0); - } - rumbleUp = 0; - rumbleDown = 0; - } } static void _setupMaps(struct mCore* core) { @@ -848,10 +834,8 @@ static void _setupMaps(struct mCore* core) { void retro_reset(void) { core->reset(core); + mRumbleIntegratorInit(&rumble); _setupMaps(core); - - rumbleUp = 0; - rumbleDown = 0; } bool retro_load_game(const struct retro_game_info* game) { @@ -1267,7 +1251,7 @@ static void _audioRateChanged(struct mAVStream* stream, unsigned rate) { environCallback(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info); } -static void _setRumble(struct mRumble* rumble, int enable) { +static void _setRumble(struct mRumbleIntegrator* rumble, float level) { UNUSED(rumble); if (!rumbleInitDone) { _initRumble(); @@ -1275,11 +1259,9 @@ static void _setRumble(struct mRumble* rumble, int enable) { if (!rumbleCallback) { return; } - if (enable) { - ++rumbleUp; - } else { - ++rumbleDown; - } + + rumbleCallback(0, RETRO_RUMBLE_STRONG, level * 0xFFFF); + rumbleCallback(0, RETRO_RUMBLE_WEAK, level * 0xFFFF); } static void _updateLux(struct GBALuminanceSource* lux) { diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index 0f456fa7e..be9ff4495 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -36,7 +36,6 @@ #include -#define RUMBLE_PWM 8 #define CDRAM_ALIGN 0x40000 mLOG_DECLARE_CATEGORY(GUI_PSP2); @@ -64,11 +63,7 @@ static struct mSceRotationSource { struct SceMotionSensorState state; } rotation; -static struct mSceRumble { - struct mRumble d; - struct mCircleBuffer history; - int current; -} rumble; +static struct mRumbleIntegrator rumble; static struct mSceImageSource { struct mImageSource d; @@ -157,17 +152,10 @@ static int32_t _readGyroZ(struct mRotationSource* source) { return rotation->state.gyro.z * -0x8000000; } -static void _setRumble(struct mRumble* source, int enable) { - struct mSceRumble* rumble = (struct mSceRumble*) source; - rumble->current += enable; - if (mCircleBufferSize(&rumble->history) == RUMBLE_PWM) { - int8_t oldLevel; - mCircleBufferRead8(&rumble->history, &oldLevel); - rumble->current -= oldLevel; - } - mCircleBufferWrite8(&rumble->history, enable); - int small = (rumble->current << 21) / 65793; - int big = ((rumble->current * rumble->current) << 18) / 65793; +static void _setRumble(struct mRumbleIntegrator* source, float level) { + UNUSED(source); + int small = level * 255; + int big = (level * level) * 255; struct SceCtrlActuator state = { small, big @@ -363,8 +351,8 @@ void mPSP2Setup(struct mGUIRunner* runner) { rotation.d.readGyroZ = _readGyroZ; runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d); - rumble.d.setRumble = _setRumble; - mCircleBufferInit(&rumble.history, RUMBLE_PWM); + mRumbleIntegratorInit(&rumble); + rumble.setRumble = _setRumble; runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d); camera.d.startRequestImage = _startRequestImage; @@ -509,7 +497,6 @@ void mPSP2Unpaused(struct mGUIRunner* runner) { void mPSP2Teardown(struct mGUIRunner* runner) { UNUSED(runner); - mCircleBufferDeinit(&rumble.history); mAudioResamplerDeinit(&audioContext.resampler); mAudioBufferDeinit(&audioContext.buffer); vita2d_free_texture(tex[0]); diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index 72d148057..402b92a12 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -112,7 +112,7 @@ void SDLInputDriver::bindDefaults(InputController* controller) { mRumble* SDLInputDriver::rumble() { #if SDL_VERSION_ATLEAST(2, 0, 0) if (m_playerAttached) { - return &m_sdlPlayer.rumble.d; + return &m_sdlPlayer.rumble.d.d; } #endif return nullptr; diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index b1b850e21..4debea3ee 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -157,7 +157,7 @@ int main(int argc, char** argv) { mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config)); #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer.core->setPeripheral(renderer.core, mPERIPH_RUMBLE, &renderer.player.rumble.d); + renderer.core->setPeripheral(renderer.core, mPERIPH_RUMBLE, &renderer.player.rumble.d.d); #endif int ret; diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index afeedba68..ca8345d22 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -22,15 +22,14 @@ #endif #define GYRO_STEPS 100 -#define RUMBLE_PWM 16 -#define RUMBLE_STEPS 2 +#define RUMBLE_THRESHOLD 1.f / 128.f mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events", "platform.sdl.events"); DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo); #if SDL_VERSION_ATLEAST(2, 0, 0) -static void _mSDLSetRumble(struct mRumble* rumble, int enable); +static void _mSDLSetRumble(struct mRumbleIntegrator* rumble, float level); #endif static int32_t _mSDLReadTiltX(struct mRotationSource* rumble); static int32_t _mSDLReadTiltY(struct mRotationSource* rumble); @@ -188,9 +187,8 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { } #if SDL_VERSION_ATLEAST(2, 0, 0) + mRumbleIntegratorInit(&player->rumble.d); player->rumble.d.setRumble = _mSDLSetRumble; - mCircleBufferInit(&player->rumble.history, RUMBLE_PWM); - player->rumble.level = 0; player->rumble.activeLevel = 0; player->rumble.p = player; #endif @@ -281,9 +279,6 @@ void mSDLDetachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { } --events->playersAttached; mCircleBufferDeinit(&player->rotation.zHistory); -#if SDL_VERSION_ATLEAST(2, 0, 0) - mCircleBufferDeinit(&player->rumble.history); -#endif } void mSDLPlayerLoadConfig(struct mSDLPlayer* context, const struct Configuration* config) { @@ -701,7 +696,7 @@ void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, } #if SDL_VERSION_ATLEAST(2, 0, 0) -static void _mSDLSetRumble(struct mRumble* rumble, int enable) { +static void _mSDLSetRumble(struct mRumbleIntegrator* rumble, float level) { struct mSDLRumble* sdlRumble = (struct mSDLRumble*) rumble; if (!sdlRumble->p->joystick) { return; @@ -719,36 +714,23 @@ static void _mSDLSetRumble(struct mRumble* rumble, int enable) { } #endif - int8_t originalLevel = sdlRumble->level; - sdlRumble->level += enable; - if (mCircleBufferSize(&sdlRumble->history) == RUMBLE_PWM) { - int8_t oldLevel; - mCircleBufferRead8(&sdlRumble->history, &oldLevel); - sdlRumble->level -= oldLevel; - } - mCircleBufferWrite8(&sdlRumble->history, enable); - if (sdlRumble->level == originalLevel) { - return; - } - float activeLevel = ceil(RUMBLE_STEPS * sdlRumble->level / (float) RUMBLE_PWM) / RUMBLE_STEPS; - if (fabsf(sdlRumble->activeLevel - activeLevel) < 0.75 / RUMBLE_STEPS) { - return; - } - sdlRumble->activeLevel = activeLevel; #if SDL_VERSION_ATLEAST(2, 0, 9) - if (sdlRumble->p->joystick->controller) { - SDL_GameControllerRumble(sdlRumble->p->joystick->controller, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); - } else { - SDL_JoystickRumble(sdlRumble->p->joystick->joystick, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); + if (sdlRumble->activeLevel > RUMBLE_THRESHOLD || level > RUMBLE_THRESHOLD) { + if (sdlRumble->p->joystick->controller) { + SDL_GameControllerRumble(sdlRumble->p->joystick->controller, level * 0xFFFF, level * 0xFFFF, 67); + } else { + SDL_JoystickRumble(sdlRumble->p->joystick->joystick, level * 0xFFFF, level * 0xFFFF, 67); + } } #else - if (sdlRumble->activeLevel > 0.5 / RUMBLE_STEPS) { + if (sdlRumble->activeLevel > RUMBLE_THRESHOLD || level > RUMBLE_THRESHOLD) { SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); - SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, activeLevel, 500); + SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, level, 500); } else { SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); } #endif + sdlRumble->activeLevel = level; } #endif diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 877818a01..59b7c7e92 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -71,12 +71,9 @@ struct mSDLPlayer { SDL_Window* window; struct mSDLRumble { - struct mRumble d; + struct mRumbleIntegrator d; struct mSDLPlayer* p; - - int level; float activeLevel; - struct mCircleBuffer history; } rumble; #else int newWidth; diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 9a3441b9b..c829ab9b0 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -82,9 +82,7 @@ static struct GUIFont* font; static color_t* frameBuffer; static struct mAVStream stream; static struct mSwitchRumble { - struct mRumble d; - int up; - int down; + struct mRumbleIntegrator d; HidVibrationValue value; } rumble; static struct mRotationSource rotation = {0}; @@ -298,7 +296,8 @@ static void _setup(struct mGUIRunner* runner) { } _updateRenderer(runner, fakeBool); - runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d); + mRumbleIntegratorInit(&rumble.d); + runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d.d); runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation); runner->core->setAVStream(runner->core, &stream); @@ -365,12 +364,12 @@ static void _gameLoaded(struct mGUIRunner* runner) { } } - rumble.up = 0; - rumble.down = 0; + mRumbleIntegratorInit(&rumble.d); } static void _gameUnloaded(struct mGUIRunner* runner) { UNUSED(runner); + mRumbleIntegratorInit(&rumble.d); HidVibrationValue values[4]; memcpy(&values[0], &vibrationStop, sizeof(rumble.value)); memcpy(&values[1], &vibrationStop, sizeof(rumble.value)); @@ -510,24 +509,12 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { _drawTex(runner, width, height, faded, false); } - HidVibrationValue values[4]; - if (rumble.up) { - rumble.value.amp_low = rumble.up / (float) (rumble.up + rumble.down); - rumble.value.amp_high = rumble.up / (float) (rumble.up + rumble.down); - memcpy(&values[0], &rumble.value, sizeof(rumble.value)); - memcpy(&values[1], &rumble.value, sizeof(rumble.value)); - memcpy(&values[2], &rumble.value, sizeof(rumble.value)); - memcpy(&values[3], &rumble.value, sizeof(rumble.value)); - } else { - memcpy(&values[0], &vibrationStop, sizeof(rumble.value)); - memcpy(&values[1], &vibrationStop, sizeof(rumble.value)); - memcpy(&values[2], &vibrationStop, sizeof(rumble.value)); - memcpy(&values[3], &vibrationStop, sizeof(rumble.value)); - } + memcpy(&values[0], &rumble.value, sizeof(rumble.value)); + memcpy(&values[1], &rumble.value, sizeof(rumble.value)); + memcpy(&values[2], &rumble.value, sizeof(rumble.value)); + memcpy(&values[3], &rumble.value, sizeof(rumble.value)); hidSendVibrationValues(vibrationDeviceHandles, values, 4); - rumble.up = 0; - rumble.down = 0; } static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { @@ -615,13 +602,10 @@ static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer* buff audrvUpdate(&audrenDriver); } -void _setRumble(struct mRumble* rumble, int enable) { +void _setRumble(struct mRumbleIntegrator* rumble, float level) { struct mSwitchRumble* sr = (struct mSwitchRumble*) rumble; - if (enable) { - ++sr->up; - } else { - ++sr->down; - } + sr->value.amp_low = level; + sr->value.amp_high = level; } void _sampleRotation(struct mRotationSource* source) { diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 55bae9738..f05e00897 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -79,7 +79,7 @@ static void _retraceCallback(u32 count); static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer*); static void _audioRateChanged(struct mAVStream* stream, unsigned); static void _audioDMA(void); -static void _setRumble(struct mRumble* rumble, int enable); +static void _setRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast); static void _sampleRotation(struct mRotationSource* source); static int32_t _readTiltX(struct mRotationSource* source); static int32_t _readTiltY(struct mRotationSource* source); @@ -1722,8 +1722,9 @@ void _incrementScreenMode(struct mGUIRunner* runner) { } } -void _setRumble(struct mRumble* rumble, int enable) { +void _setRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) { UNUSED(rumble); + UNUSED(sinceLast); WPAD_Rumble(0, enable); if (enable) { PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);