Core: Improve rumble emulation by averaging state over entire frame (fixes #3232)

This commit is contained in:
Vicki Pfau 2024-06-16 23:28:04 -07:00
parent 745e36e6bc
commit 458300b02e
21 changed files with 142 additions and 122 deletions

View File

@ -30,6 +30,7 @@ Other fixes:
- Updater: Fix updating appimage across filesystems - Updater: Fix updating appimage across filesystems
Misc: Misc:
- Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) - 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: Prevent incompatible BIOSes from being used on differing models
- GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GB Serialize: Add missing savestate support for MBC6 and NT (newer)
- GBA: Improve detection of valid ELF ROMs - GBA: Improve detection of valid ELF ROMs

View File

@ -110,9 +110,22 @@ struct mRTCGenericState {
void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core); void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core);
struct mRumble { 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 { struct mCoreChannelInfo {
size_t id; size_t id;
const char* internalName; const char* internalName;

View File

@ -339,6 +339,7 @@ struct GBMemory {
struct mRTCSource* rtc; struct mRTCSource* rtc;
struct mRotationSource* rotation; struct mRotationSource* rotation;
struct mRumble* rumble; struct mRumble* rumble;
int32_t lastRumble;
struct mImageSource* cam; struct mImageSource* cam;
}; };

View File

@ -91,6 +91,7 @@ struct GBA {
struct GBALuminanceSource* luminanceSource; struct GBALuminanceSource* luminanceSource;
struct mRTCSource* rtcSource; struct mRTCSource* rtcSource;
struct mRumble* rumble; struct mRumble* rumble;
int32_t lastRumble;
bool isPristine; bool isPristine;
size_t pristineRomSize; size_t pristineRomSize;

View File

@ -109,3 +109,44 @@ void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) {
rtc->d.serialize = _rtcGenericSerialize; rtc->d.serialize = _rtcGenericSerialize;
rtc->d.deserialize = _rtcGenericDeserialize; 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;
}

View File

@ -196,6 +196,7 @@ struct mScriptCoreAdapter {
struct mScriptDebugger debugger; struct mScriptDebugger debugger;
#endif #endif
struct mRumble rumble; struct mRumble rumble;
struct mRumbleIntegrator rumbleIntegrator;
struct mRumble* oldRumble; struct mRumble* oldRumble;
struct mRotationSource rotation; struct mRotationSource rotation;
struct mScriptValue* rotationCbTable; struct mScriptValue* rotationCbTable;
@ -1165,16 +1166,22 @@ mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter)
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core)
mSCRIPT_DEFINE_END; 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); struct mScriptCoreAdapter* adapter = containerof(rumble, struct mScriptCoreAdapter, rumble);
if (adapter->oldRumble) { 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; struct mScriptList args;
mScriptListInit(&args, 1); mScriptListInit(&args, 1);
*mScriptListAppend(&args) = mSCRIPT_MAKE_BOOL(!!enable); *mScriptListAppend(&args) = mSCRIPT_MAKE_F32(level);
mScriptContextTriggerCallback(adapter->context, "rumble", &args); mScriptContextTriggerCallback(adapter->context, "rumble", &args);
mScriptListDeinit(&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 = mSCRIPT_TYPE_MS_TABLE;
adapter->memory.type->alloc(&adapter->memory); adapter->memory.type->alloc(&adapter->memory);
mRumbleIntegratorInit(&adapter->rumbleIntegrator);
adapter->rumbleIntegrator.setRumble = _setRumbleFloat;
adapter->rumble.setRumble = _setRumble; adapter->rumble.setRumble = _setRumble;
adapter->rotation.sample = _rotationSample; adapter->rotation.sample = _rotationSample;
adapter->rotation.readTiltX = _rotationReadTiltX; adapter->rotation.readTiltX = _rotationReadTiltX;

View File

@ -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 // TODO: Move to common code
if (gb->stream && gb->stream->postVideoFrame) { if (gb->stream && gb->stream->postVideoFrame) {
const color_t* pixels; const color_t* pixels;

View File

@ -540,6 +540,7 @@ void GBMBCReset(struct GB* gb) {
gb->memory.cartBus = 0xFF; gb->memory.cartBus = 0xFF;
gb->memory.cartBusPc = 0; gb->memory.cartBusPc = 0;
gb->memory.cartBusDecay = 1; gb->memory.cartBusDecay = 1;
gb->memory.lastRumble = 0;
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
GBMBCInit(gb); GBMBCInit(gb);

View File

@ -253,7 +253,9 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
case 0x4: case 0x4:
case 0x5: case 0x5:
if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { 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; value &= ~8;
} }
GBMBCSwitchSramBank(gb, value & 0xF); GBMBCSwitchSramBank(gb, value & 0xF);

View File

@ -228,7 +228,9 @@ void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
} }
if (mbcState->rumble && memory->rumble) { 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; break;
} }

View File

@ -354,7 +354,9 @@ void _rumbleReadPins(struct GBACartridgeHardware* hw) {
return; 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 // == Light sensor

View File

@ -250,6 +250,7 @@ void GBAReset(struct ARMCore* cpu) {
gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
} }
gba->lastRumble = 0;
mTimingClear(&gba->timing); mTimingClear(&gba->timing);
GBAMemoryReset(gba); GBAMemoryReset(gba);
GBAVideoReset(&gba->video); GBAVideoReset(&gba->video);
@ -993,6 +994,12 @@ void GBAFrameEnded(struct GBA* gba) {
GBASIOPlayerUpdate(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; size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);

View File

@ -108,12 +108,13 @@ uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uin
if (gbp->txPosition < 12 && gbp->txPosition > 0) { if (gbp->txPosition < 12 && gbp->txPosition > 0) {
// TODO: Check expected // TODO: Check expected
} else if (gbp->txPosition >= 12) { } else if (gbp->txPosition >= 12) {
uint32_t mask = 0x33;
// 0x00 = Stop // 0x00 = Stop
// 0x11 = Hard Stop // 0x11 = Hard Stop
// 0x22 = Start // 0x22 = Start
if (gbp->p->rumble) { 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); mTimingDeschedule(&gbp->p->timing, &gbp->event);

View File

@ -33,7 +33,6 @@
* to calculating the average for the last 180 * to calculating the average for the last 180
* frames, or 3 seconds of runtime... */ * frames, or 3 seconds of runtime... */
#define SAMPLES_PER_FRAME_MOVING_AVG_ALPHA (1.0f / 180.0f) #define SAMPLES_PER_FRAME_MOVING_AVG_ALPHA (1.0f / 180.0f)
#define RUMBLE_PWM 35
#define EVENT_RATE 60 #define EVENT_RATE 60
#define VIDEO_WIDTH_MAX 256 #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 _postAudioBuffer(struct mAVStream*, struct mAudioBuffer*);
static void _audioRateChanged(struct mAVStream*, unsigned rate); 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 uint8_t _readLux(struct GBALuminanceSource* lux);
static void _updateLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux);
static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch); 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 struct mAVStream stream;
static bool sensorsInitDone; static bool sensorsInitDone;
static bool rumbleInitDone; static bool rumbleInitDone;
static int rumbleUp; static struct mRumbleIntegrator rumble;
static int rumbleDown;
static struct mRumble rumble;
static struct GBALuminanceSource lux; static struct GBALuminanceSource lux;
static struct mRotationSource rotation; static struct mRotationSource rotation;
static bool tiltEnabled; static bool tiltEnabled;
@ -467,6 +464,7 @@ void retro_init(void) {
// TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported // TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported
rumbleInitDone = false; rumbleInitDone = false;
mRumbleIntegratorInit(&rumble);
rumble.setRumble = _setRumble; rumble.setRumble = _setRumble;
rumbleCallback = 0; rumbleCallback = 0;
@ -645,18 +643,6 @@ void retro_run(void) {
} }
} }
#endif #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) { static void _setupMaps(struct mCore* core) {
@ -848,10 +834,8 @@ static void _setupMaps(struct mCore* core) {
void retro_reset(void) { void retro_reset(void) {
core->reset(core); core->reset(core);
mRumbleIntegratorInit(&rumble);
_setupMaps(core); _setupMaps(core);
rumbleUp = 0;
rumbleDown = 0;
} }
bool retro_load_game(const struct retro_game_info* game) { 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); 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); UNUSED(rumble);
if (!rumbleInitDone) { if (!rumbleInitDone) {
_initRumble(); _initRumble();
@ -1275,11 +1259,9 @@ static void _setRumble(struct mRumble* rumble, int enable) {
if (!rumbleCallback) { if (!rumbleCallback) {
return; return;
} }
if (enable) {
++rumbleUp; rumbleCallback(0, RETRO_RUMBLE_STRONG, level * 0xFFFF);
} else { rumbleCallback(0, RETRO_RUMBLE_WEAK, level * 0xFFFF);
++rumbleDown;
}
} }
static void _updateLux(struct GBALuminanceSource* lux) { static void _updateLux(struct GBALuminanceSource* lux) {

View File

@ -36,7 +36,6 @@
#include <vita2d.h> #include <vita2d.h>
#define RUMBLE_PWM 8
#define CDRAM_ALIGN 0x40000 #define CDRAM_ALIGN 0x40000
mLOG_DECLARE_CATEGORY(GUI_PSP2); mLOG_DECLARE_CATEGORY(GUI_PSP2);
@ -64,11 +63,7 @@ static struct mSceRotationSource {
struct SceMotionSensorState state; struct SceMotionSensorState state;
} rotation; } rotation;
static struct mSceRumble { static struct mRumbleIntegrator rumble;
struct mRumble d;
struct mCircleBuffer history;
int current;
} rumble;
static struct mSceImageSource { static struct mSceImageSource {
struct mImageSource d; struct mImageSource d;
@ -157,17 +152,10 @@ static int32_t _readGyroZ(struct mRotationSource* source) {
return rotation->state.gyro.z * -0x8000000; return rotation->state.gyro.z * -0x8000000;
} }
static void _setRumble(struct mRumble* source, int enable) { static void _setRumble(struct mRumbleIntegrator* source, float level) {
struct mSceRumble* rumble = (struct mSceRumble*) source; UNUSED(source);
rumble->current += enable; int small = level * 255;
if (mCircleBufferSize(&rumble->history) == RUMBLE_PWM) { int big = (level * level) * 255;
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;
struct SceCtrlActuator state = { struct SceCtrlActuator state = {
small, small,
big big
@ -363,8 +351,8 @@ void mPSP2Setup(struct mGUIRunner* runner) {
rotation.d.readGyroZ = _readGyroZ; rotation.d.readGyroZ = _readGyroZ;
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d); runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
rumble.d.setRumble = _setRumble; mRumbleIntegratorInit(&rumble);
mCircleBufferInit(&rumble.history, RUMBLE_PWM); rumble.setRumble = _setRumble;
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d); runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
camera.d.startRequestImage = _startRequestImage; camera.d.startRequestImage = _startRequestImage;
@ -509,7 +497,6 @@ void mPSP2Unpaused(struct mGUIRunner* runner) {
void mPSP2Teardown(struct mGUIRunner* runner) { void mPSP2Teardown(struct mGUIRunner* runner) {
UNUSED(runner); UNUSED(runner);
mCircleBufferDeinit(&rumble.history);
mAudioResamplerDeinit(&audioContext.resampler); mAudioResamplerDeinit(&audioContext.resampler);
mAudioBufferDeinit(&audioContext.buffer); mAudioBufferDeinit(&audioContext.buffer);
vita2d_free_texture(tex[0]); vita2d_free_texture(tex[0]);

View File

@ -112,7 +112,7 @@ void SDLInputDriver::bindDefaults(InputController* controller) {
mRumble* SDLInputDriver::rumble() { mRumble* SDLInputDriver::rumble() {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
if (m_playerAttached) { if (m_playerAttached) {
return &m_sdlPlayer.rumble.d; return &m_sdlPlayer.rumble.d.d;
} }
#endif #endif
return nullptr; return nullptr;

View File

@ -157,7 +157,7 @@ int main(int argc, char** argv) {
mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config)); mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config));
#if SDL_VERSION_ATLEAST(2, 0, 0) #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 #endif
int ret; int ret;

View File

@ -22,15 +22,14 @@
#endif #endif
#define GYRO_STEPS 100 #define GYRO_STEPS 100
#define RUMBLE_PWM 16 #define RUMBLE_THRESHOLD 1.f / 128.f
#define RUMBLE_STEPS 2
mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events", "platform.sdl.events"); mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events", "platform.sdl.events");
DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo); DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
static void _mSDLSetRumble(struct mRumble* rumble, int enable); static void _mSDLSetRumble(struct mRumbleIntegrator* rumble, float level);
#endif #endif
static int32_t _mSDLReadTiltX(struct mRotationSource* rumble); static int32_t _mSDLReadTiltX(struct mRotationSource* rumble);
static int32_t _mSDLReadTiltY(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) #if SDL_VERSION_ATLEAST(2, 0, 0)
mRumbleIntegratorInit(&player->rumble.d);
player->rumble.d.setRumble = _mSDLSetRumble; player->rumble.d.setRumble = _mSDLSetRumble;
mCircleBufferInit(&player->rumble.history, RUMBLE_PWM);
player->rumble.level = 0;
player->rumble.activeLevel = 0; player->rumble.activeLevel = 0;
player->rumble.p = player; player->rumble.p = player;
#endif #endif
@ -281,9 +279,6 @@ void mSDLDetachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) {
} }
--events->playersAttached; --events->playersAttached;
mCircleBufferDeinit(&player->rotation.zHistory); 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) { 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) #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; struct mSDLRumble* sdlRumble = (struct mSDLRumble*) rumble;
if (!sdlRumble->p->joystick) { if (!sdlRumble->p->joystick) {
return; return;
@ -719,36 +714,23 @@ static void _mSDLSetRumble(struct mRumble* rumble, int enable) {
} }
#endif #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 SDL_VERSION_ATLEAST(2, 0, 9)
if (sdlRumble->p->joystick->controller) { if (sdlRumble->activeLevel > RUMBLE_THRESHOLD || level > RUMBLE_THRESHOLD) {
SDL_GameControllerRumble(sdlRumble->p->joystick->controller, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); if (sdlRumble->p->joystick->controller) {
} else { SDL_GameControllerRumble(sdlRumble->p->joystick->controller, level * 0xFFFF, level * 0xFFFF, 67);
SDL_JoystickRumble(sdlRumble->p->joystick->joystick, activeLevel * 0xFFFF, activeLevel * 0xFFFF, 500); } else {
SDL_JoystickRumble(sdlRumble->p->joystick->joystick, level * 0xFFFF, level * 0xFFFF, 67);
}
} }
#else #else
if (sdlRumble->activeLevel > 0.5 / RUMBLE_STEPS) { if (sdlRumble->activeLevel > RUMBLE_THRESHOLD || level > RUMBLE_THRESHOLD) {
SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic);
SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, activeLevel, 500); SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, level, 500);
} else { } else {
SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic); SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic);
} }
#endif #endif
sdlRumble->activeLevel = level;
} }
#endif #endif

View File

@ -71,12 +71,9 @@ struct mSDLPlayer {
SDL_Window* window; SDL_Window* window;
struct mSDLRumble { struct mSDLRumble {
struct mRumble d; struct mRumbleIntegrator d;
struct mSDLPlayer* p; struct mSDLPlayer* p;
int level;
float activeLevel; float activeLevel;
struct mCircleBuffer history;
} rumble; } rumble;
#else #else
int newWidth; int newWidth;

View File

@ -82,9 +82,7 @@ static struct GUIFont* font;
static color_t* frameBuffer; static color_t* frameBuffer;
static struct mAVStream stream; static struct mAVStream stream;
static struct mSwitchRumble { static struct mSwitchRumble {
struct mRumble d; struct mRumbleIntegrator d;
int up;
int down;
HidVibrationValue value; HidVibrationValue value;
} rumble; } rumble;
static struct mRotationSource rotation = {0}; static struct mRotationSource rotation = {0};
@ -298,7 +296,8 @@ static void _setup(struct mGUIRunner* runner) {
} }
_updateRenderer(runner, fakeBool); _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->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
runner->core->setAVStream(runner->core, &stream); runner->core->setAVStream(runner->core, &stream);
@ -365,12 +364,12 @@ static void _gameLoaded(struct mGUIRunner* runner) {
} }
} }
rumble.up = 0; mRumbleIntegratorInit(&rumble.d);
rumble.down = 0;
} }
static void _gameUnloaded(struct mGUIRunner* runner) { static void _gameUnloaded(struct mGUIRunner* runner) {
UNUSED(runner); UNUSED(runner);
mRumbleIntegratorInit(&rumble.d);
HidVibrationValue values[4]; HidVibrationValue values[4];
memcpy(&values[0], &vibrationStop, sizeof(rumble.value)); memcpy(&values[0], &vibrationStop, sizeof(rumble.value));
memcpy(&values[1], &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); _drawTex(runner, width, height, faded, false);
} }
HidVibrationValue values[4]; HidVibrationValue values[4];
if (rumble.up) { memcpy(&values[0], &rumble.value, sizeof(rumble.value));
rumble.value.amp_low = rumble.up / (float) (rumble.up + rumble.down); memcpy(&values[1], &rumble.value, sizeof(rumble.value));
rumble.value.amp_high = rumble.up / (float) (rumble.up + rumble.down); memcpy(&values[2], &rumble.value, sizeof(rumble.value));
memcpy(&values[0], &rumble.value, sizeof(rumble.value)); memcpy(&values[3], &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));
}
hidSendVibrationValues(vibrationDeviceHandles, values, 4); 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) { 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); audrvUpdate(&audrenDriver);
} }
void _setRumble(struct mRumble* rumble, int enable) { void _setRumble(struct mRumbleIntegrator* rumble, float level) {
struct mSwitchRumble* sr = (struct mSwitchRumble*) rumble; struct mSwitchRumble* sr = (struct mSwitchRumble*) rumble;
if (enable) { sr->value.amp_low = level;
++sr->up; sr->value.amp_high = level;
} else {
++sr->down;
}
} }
void _sampleRotation(struct mRotationSource* source) { void _sampleRotation(struct mRotationSource* source) {

View File

@ -79,7 +79,7 @@ static void _retraceCallback(u32 count);
static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer*); static void _postAudioBuffer(struct mAVStream* stream, struct mAudioBuffer*);
static void _audioRateChanged(struct mAVStream* stream, unsigned); static void _audioRateChanged(struct mAVStream* stream, unsigned);
static void _audioDMA(void); 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 void _sampleRotation(struct mRotationSource* source);
static int32_t _readTiltX(struct mRotationSource* source); static int32_t _readTiltX(struct mRotationSource* source);
static int32_t _readTiltY(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(rumble);
UNUSED(sinceLast);
WPAD_Rumble(0, enable); WPAD_Rumble(0, enable);
if (enable) { if (enable) {
PAD_ControlMotor(0, PAD_MOTOR_RUMBLE); PAD_ControlMotor(0, PAD_MOTOR_RUMBLE);