// Xbox One gamepads are equipped with a total of four independent vibration motors: // Two large motors located in the gamepad body: // - Left motor provides rough, high-amplitude vibration. // - Right motor provides gentler, more subtle vibration. // Two small motors located inside each trigger, // that provide sharp bursts of vibration directly to the user's trigger fingers. public bool SetDeviceForces(UserDevice ud, Device device, PadSetting ps, Vibration v) { var motorsChanged = Changed(ref old_ForceSwapMotor, ps.ForceSwapMotor); bool swapMotor = false; if (motorsChanged) { // Find and assign actuators. var actuators = ud.DeviceObjects.Where(x => x.Flags.HasFlag(DeviceObjectTypeFlags.ForceFeedbackActuator)).ToList(); DeviceObjectItem xMotor = null; // If actuator available then... if (actuators.Count > 0) { // Try to find left actuator. xMotor = actuators.FirstOrDefault(x => x.Type == ObjectGuid.XAxis); //var actuator = actuators[0]; // If default actuator not found then take default. if (xMotor == null) { xMotor = actuators[0]; } actuators.Remove(xMotor); } DeviceObjectItem yMotor = null; // If actuator available then... if (actuators.Count > 0) { // Try to find right actuator. yMotor = actuators.FirstOrDefault(x => x.Type == ObjectGuid.YAxis); //var actuator = actuators[0]; // If default actuator not found then take default. if (yMotor == null) { yMotor = actuators[0]; } actuators.Remove(yMotor); } swapMotor = TryParse(ps.ForceSwapMotor) == 1; // Allow to swap if both motors exist. if (swapMotor && xMotor != null && yMotor != null) { actuatorL = yMotor; actuatorR = xMotor; } else { actuatorL = xMotor; actuatorR = yMotor; } } // Return if force feedback actuators not found. if (actuatorL == null) { return(false); } Effect effectL = null; Effect effectR = null; // If device already have effects then... if (device.CreatedEffects.Count > 0) { effectL = device.CreatedEffects[0]; effectL.Download(); } if (device.CreatedEffects.Count > 1) { effectR = device.CreatedEffects[1]; effectL.Download(); } // Effect type changed. bool forceChanged = Changed(ref old_ForceType, ps.ForceType); ForceEffectType forceType = 0; if (motorsChanged || forceChanged) { // Update values. forceType = (ForceEffectType)TryParse(ps.ForceType); if (forceType.HasFlag(ForceEffectType.PeriodicSine)) { GUID_Force = EffectGuid.Sine; } else if (forceType.HasFlag(ForceEffectType.PeriodicSawtooth)) { GUID_Force = EffectGuid.SawtoothDown; } else { GUID_Force = EffectGuid.ConstantForce; } // Force change requires to dispose old effects. // Stop old effects. if (effectL != null) { effectL.Stop(); effectL.Dispose(); effectL = null; } // Stop old effects. if (effectR != null) { effectR.Stop(); effectR.Dispose(); effectR = null; } } // If device already do not have effects then. if (paramsL != null && device.CreatedEffects.Count < 1) { forceChanged = true; } if (paramsR != null && device.CreatedEffects.Count < 2) { forceChanged = true; } // Tells which effect parameters to modify. var flagsL = EffectParameterFlags.None; var flagsR = EffectParameterFlags.None; if (motorsChanged || forceChanged) { // If 2 actuators available if (actuatorR != null) { paramsL = GetParameters(); paramsR = GetParameters(); // Unfortunately SpeedLink GamePad needs both axis specified in order to operate motors separately. // Which is counter-intuitive. if (forceType.HasFlag(ForceEffectType._Type2)) { // Note: Second axis direction will be set to zero i.e. motor will be not used by effect. // Directions must be set to 'Positive' on both first axis via force feedback settings interface. paramsL.Axes = new int[2] { actuatorL.ObjectId, actuatorR.ObjectId }; paramsR.Axes = new int[2] { actuatorR.ObjectId, actuatorL.ObjectId }; // There is no need to set this flag or DIERR_ALREADYINITIALIZED error will be thrown. //flagsR |= EffectParameterFlags.Axes; } // Used for normal devices like Logitech. Use one axis per Effect/Parameter. else { paramsL.Axes = new int[1] { actuatorL.ObjectId }; paramsR.Axes = new int[1] { actuatorR.ObjectId }; // There is no need to set this flag or DIERR_ALREADYINITIALIZED error will be thrown. //flagsR |= EffectParameterFlags.Axes; } } // If one actuator available. else if (actuatorL != null) { paramsL = GetParameters(); paramsL.Axes = new int[1] { actuatorL.ObjectId }; } } // Direction changed. // Right-handed Cartesian direction: // x: -1 = left, 1 = right, 0 - no direction // y: -1 = backward, 1 = forward, 0 - no direction // z: -1 = down, 1 = up, 0 - no direction var directionLChanged = Changed(ref old_LeftDirection, ps.LeftMotorDirection); var directionRChanged = Changed(ref old_RightDirection, ps.RightMotorDirection); // Direction needs to be updated when force or direction change. if (motorsChanged || forceChanged || directionLChanged) { var directionL = TryParse(old_LeftDirection); var dirL = new int[paramsL.Axes.Length]; dirL[0] = directionL; paramsL.Directions = dirL; flagsL |= EffectParameterFlags.Direction; } // Direction needs to be updated when force or direction change. if (actuatorR != null && (motorsChanged || forceChanged || directionRChanged)) { var directionR = TryParse(old_RightDirection); var dirR = new int[paramsR.Axes.Length]; dirR[0] = directionR; paramsR.Directions = dirR; flagsR |= EffectParameterFlags.Direction; } var strengthChanged = Changed(ref old_OveralStrength, ps.ForceOverall); var strengthLChanged = Changed(ref old_LeftStrength, ps.LeftMotorStrength); var strengthRChanged = Changed(ref old_RightStrength, ps.RightMotorStrength); if (motorsChanged || forceChanged || strengthChanged || strengthLChanged) { int overalStrength = ConvertHelper.ConvertRange(0, 100, 0, DI_FFNOMINALMAX, ps.GetForceOverall()); int leftGain = ConvertHelper.ConvertRange(0, 100, 0, overalStrength, ps.GetLeftMotorStrength()); paramsL.Gain = leftGain; flagsL |= EffectParameterFlags.Gain; } if (actuatorR != null && (motorsChanged || forceChanged || strengthChanged || strengthRChanged)) { int overalStrength = ConvertHelper.ConvertRange(0, 100, 0, DI_FFNOMINALMAX, ps.GetForceOverall()); int rightGain = ConvertHelper.ConvertRange(0, 100, 0, overalStrength, ps.GetRightMotorStrength()); paramsR.Gain = rightGain; flagsR |= EffectParameterFlags.Gain; } var periodLChanged = Changed(ref old_LeftPeriod, ps.LeftMotorPeriod); var periodRChanged = Changed(ref old_RightPeriod, ps.RightMotorPeriod); var speedLChanged = Changed(ref old_LeftMotorSpeed, v.LeftMotorSpeed); var speedRChanged = Changed(ref old_RightMotorSpeed, v.RightMotorSpeed); // Convert speed into magnitude/amplitude. int leftMagnitudeAdjusted; int rightMagnitudeAdjusted = 0; int leftPeriod; int rightPeriod = 0; // If device have only one force feedback actuator (probably wheel). var combine = actuatorR == null; // Get right values first for possible combine later. if (motorsChanged || forceChanged || periodRChanged || speedRChanged || combine) { rightMagnitudeAdjusted = ConvertHelper.ConvertRange(short.MinValue, short.MaxValue, 0, DI_FFNOMINALMAX, old_RightMotorSpeed); rightPeriod = TryParse(old_RightPeriod) * 1000; if (actuatorR != null) { // Update force values. if (GUID_Force == EffectGuid.ConstantForce) { ConstantForceR.Magnitude = rightMagnitudeAdjusted; } else { PeriodicForceR.Magnitude = rightMagnitudeAdjusted; PeriodicForceR.Period = rightPeriod; } // Update flags to indicate that specific force parameters changed. flagsR |= EffectParameterFlags.TypeSpecificParameters; } } // Calculate left later for possible combine. if (motorsChanged || forceChanged || periodLChanged || speedLChanged || combine) { // Convert speed into magnitude/amplitude. leftMagnitudeAdjusted = ConvertHelper.ConvertRange(short.MinValue, short.MaxValue, 0, DI_FFNOMINALMAX, old_LeftMotorSpeed); leftPeriod = TryParse(old_LeftPeriod) * 1000; // If device have only one force feedback actuator (probably wheel). if (combine) { // Forces must be combined. var combinedMagnitudeAdjusted = Math.Max(leftMagnitudeAdjusted, rightMagnitudeAdjusted); var combinedPeriod = 0; // If at least one speed is specified then... if (leftMagnitudeAdjusted > 0 || rightMagnitudeAdjusted > 0) { // Get combined period depending on magnitudes. combinedPeriod = ((leftPeriod * leftMagnitudeAdjusted) + (rightPeriod * rightMagnitudeAdjusted)) / (leftMagnitudeAdjusted + rightMagnitudeAdjusted); } // Update force properties. leftMagnitudeAdjusted = combinedMagnitudeAdjusted; leftPeriod = combinedPeriod; } // Update force values. if (GUID_Force == EffectGuid.ConstantForce) { ConstantForceL.Magnitude = leftMagnitudeAdjusted; } else { PeriodicForceL.Magnitude = leftMagnitudeAdjusted; PeriodicForceL.Period = leftPeriod; } // Update flags to indicate that specific force parameters changed. flagsL |= EffectParameterFlags.TypeSpecificParameters; } // Recreate effects if force changed. if (motorsChanged || forceChanged) { // Update Left force paramsL.Parameters = GUID_Force == EffectGuid.ConstantForce ? ConstantForceL as TypeSpecificParameters : PeriodicForceL; // Note: Device must be acquired in exclusive mode before effect can be created. effectL = new Effect(device, GUID_Force, paramsL); if (actuatorR != null) { // Update Right force paramsR.Parameters = GUID_Force == EffectGuid.ConstantForce ? ConstantForceR as TypeSpecificParameters : PeriodicForceR; effectR = new Effect(device, GUID_Force, paramsR); } } if (flagsL != EffectParameterFlags.None) { SetParamaters(effectL, paramsL, flagsL); } if (flagsR != EffectParameterFlags.None) { SetParamaters(effectR, paramsR, flagsR); } return(true); }
public bool SetDeviceForces(Joystick device, PadSetting ps, Vibration v) { // Return if force feedback actuators not found. if (actuatorL == null) { return(false); } Effect effectL = null; Effect effectR = null; // If device already have effects then... if (device.CreatedEffects.Count > 0) { effectL = device.CreatedEffects[0]; } if (device.CreatedEffects.Count > 1) { effectR = device.CreatedEffects[1]; } // Effect type changed. bool forceChanged = Changed(ref old_ForceType, ps.ForceType); if (forceChanged) { // Update values. var forceType = (ForceFeedBackType)TryParse(ps.ForceType); // Forces for vibrating motors (Game pads). // 0 - Constant. Good for vibrating motors. // Forces for torque motors (Wheels). // 1 - Periodic 'Sine Wave'. Good for car/plane engine vibration. // 2 - Periodic 'Sawtooth Down Wave'. Good for gun recoil. switch (forceType) { case ForceFeedBackType.PeriodicSine: GUID_Force = EffectGuid.Sine; break; case ForceFeedBackType.PeriodicSawtooth: GUID_Force = EffectGuid.SawtoothDown; break; default: GUID_Force = EffectGuid.ConstantForce; break; } // Force change requires to dispose old effects. // Stop old effects. if (effectL != null) { effectL.Stop(); effectL.Dispose(); effectL = null; } // Stop old effects. if (effectR != null) { effectR.Stop(); effectR.Dispose(); effectR = null; } } // If device already do not have effects then. if (paramsL != null && device.CreatedEffects.Count < 1) { forceChanged = true; } if (paramsR != null && device.CreatedEffects.Count < 2) { forceChanged = true; } // Tells which effect parameters to modify. var flagsL = EffectParameterFlags.None; var flagsR = EffectParameterFlags.None; if (forceChanged) { if (actuatorL != null) { paramsL = GetParameters(actuatorL.ObjectId); flagsL |= EffectParameterFlags.Axes; } if (actuatorR != null) { paramsR = GetParameters(actuatorR.ObjectId); flagsR |= EffectParameterFlags.Axes; } } // Direction changed. // Right-handed Cartesian direction: // x: -1 = left, 1 = right, 0 - no direction // y: -1 = backward, 1 = forward, 0 - no direction // z: -1 = down, 1 = up, 0 - no direction var directionLChanged = Changed(ref old_LeftDirection, ps.LeftMotorDirection); var directionRChanged = Changed(ref old_RightDirection, ps.RightMotorDirection); // Direction needs to be updated when force or direction change. if (forceChanged || directionLChanged) { var directionL = TryParse(old_LeftDirection); paramsL.Directions = new int[1] { directionL }; flagsL |= EffectParameterFlags.Direction; } // Direction needs to be updated when force or direction change. if (actuatorR != null && (forceChanged || directionRChanged)) { var directionR = TryParse(old_RightDirection); paramsR.Directions = new int[1] { directionR }; flagsR |= EffectParameterFlags.Direction; } var strengthChanged = Changed(ref old_OveralStrength, ps.ForceOverall); var strengthLChanged = Changed(ref old_LeftStrength, ps.LeftMotorStrength); var strengthRChanged = Changed(ref old_RightStrength, ps.RightMotorStrength); if (forceChanged || strengthChanged || strengthLChanged) { int overalStrength = ConvertHelper.ConvertRange(0, 100, 0, DI_FFNOMINALMAX, ps.GetForceOverall()); int leftGain = ConvertHelper.ConvertRange(0, 100, 0, overalStrength, ps.GetLeftMotorStrength()); paramsL.Gain = leftGain; flagsL |= EffectParameterFlags.Gain; } if (actuatorR != null && (forceChanged || strengthChanged || strengthRChanged)) { int overalStrength = ConvertHelper.ConvertRange(0, 100, 0, DI_FFNOMINALMAX, ps.GetForceOverall()); int rightGain = ConvertHelper.ConvertRange(0, 100, 0, overalStrength, ps.GetRightMotorStrength()); paramsR.Gain = rightGain; flagsR |= EffectParameterFlags.Gain; } var periodLChanged = Changed(ref old_LeftPeriod, ps.LeftMotorPeriod); var periodRChanged = Changed(ref old_RightPeriod, ps.RightMotorPeriod); var speedLChanged = Changed(ref old_LeftMotorSpeed, v.LeftMotorSpeed); var speedRChanged = Changed(ref old_RightMotorSpeed, v.RightMotorSpeed); // Convert speed into magnitude/amplitude. int leftMagnitudeAdjusted; int rightMagnitudeAdjusted = 0; int leftPeriod; int rightPeriod = 0; // If device have only one force feedback actuator (probably wheel). var combine = actuatorR == null; // Get right values first for possible combine later. if (forceChanged || periodRChanged || speedRChanged || combine) { rightMagnitudeAdjusted = ConvertHelper.ConvertRange(short.MinValue, short.MaxValue, 0, DI_FFNOMINALMAX, old_RightMotorSpeed); rightPeriod = TryParse(old_RightPeriod) * 1000; if (actuatorR != null) { // Update force values. if (GUID_Force == EffectGuid.ConstantForce) { ConstantForceR.Magnitude = rightMagnitudeAdjusted; } else { PeriodicForceR.Magnitude = rightMagnitudeAdjusted; PeriodicForceR.Period = rightPeriod; } // Update flags to indicate that specific force parameters changed. flagsR |= EffectParameterFlags.TypeSpecificParameters; } } // Calculate left later for possible combine. if (forceChanged || periodLChanged || speedLChanged || combine) { // Convert speed into magnitude/amplitude. leftMagnitudeAdjusted = ConvertHelper.ConvertRange(short.MinValue, short.MaxValue, 0, DI_FFNOMINALMAX, old_LeftMotorSpeed); leftPeriod = TryParse(old_LeftPeriod) * 1000; // If device have only one force feedback actuator (probably wheel). if (combine) { // Forces must be combined. var combinedMagnitudeAdjusted = Math.Max(leftMagnitudeAdjusted, rightMagnitudeAdjusted); var combinedPeriod = 0; // If at least one speed is specified then... if (leftMagnitudeAdjusted > 0 || rightMagnitudeAdjusted > 0) { // Get combined period depending on magnitudes. combinedPeriod = ((leftPeriod * leftMagnitudeAdjusted) + (rightPeriod * rightMagnitudeAdjusted)) / (leftMagnitudeAdjusted + rightMagnitudeAdjusted); } // Update force properties. leftMagnitudeAdjusted = combinedMagnitudeAdjusted; leftPeriod = combinedPeriod; } // Update force values. if (GUID_Force == EffectGuid.ConstantForce) { ConstantForceL.Magnitude = leftMagnitudeAdjusted; } else { PeriodicForceL.Magnitude = leftMagnitudeAdjusted; PeriodicForceL.Period = leftPeriod; } // Update flags to indicate that specific force parameters changed. flagsL |= EffectParameterFlags.TypeSpecificParameters; } // Recreate effects if force changed. if (forceChanged) { // Update Left force paramsL.Parameters = GUID_Force == EffectGuid.ConstantForce ? ConstantForceL as TypeSpecificParameters : PeriodicForceL; // Note: Device must be acquired in exclusive mode before effect can be created. try { effectL = new Effect(device, GUID_Force, paramsL); } catch { throw; } if (actuatorR != null) { // Update Right force paramsR.Parameters = GUID_Force == EffectGuid.ConstantForce ? ConstantForceR as TypeSpecificParameters : PeriodicForceR; try { effectR = new Effect(device, GUID_Force, paramsR); } catch { throw; } } } if (flagsL != EffectParameterFlags.None) { try { SetParamaters(effectL, paramsL, flagsL); } catch { throw; } } if (flagsR != EffectParameterFlags.None) { SetParamaters(effectR, paramsR, flagsR); } return(true); }