From cf6b58ee452bf60ed915d837f0c4ec272e02e89c Mon Sep 17 00:00:00 2001 From: Bob Helander Date: Wed, 21 Apr 2021 20:02:07 -0600 Subject: [PATCH] Additional methods. Renamed base class --- EDForceFeedback/App.config | 4 + EDForceFeedback/EDForceFeedback.csproj | 10 +- EDForceFeedback/Forces/CenterSpringXY.ffe | Bin 0 -> 296 bytes EDForceFeedback/Forces/Damper.ffe | Bin 0 -> 296 bytes EDForceFeedback/packages.config | 2 +- EDForceFeedback/settings.json | 6 +- ...dewinder.cs => ForceFeedbackController.cs} | 104 ++++++++++++++---- .../ForceFeedbackSharpDx.csproj | 1 + Journals/Client.cs | 7 +- Journals/DeviceEvents.cs | 2 +- 10 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 EDForceFeedback/Forces/CenterSpringXY.ffe create mode 100644 EDForceFeedback/Forces/Damper.ffe rename ForceFeedbackSharpDx/{MicrosoftSidewinder.cs => ForceFeedbackController.cs} (61%) diff --git a/EDForceFeedback/App.config b/EDForceFeedback/App.config index 1c5ea9d..e420377 100644 --- a/EDForceFeedback/App.config +++ b/EDForceFeedback/App.config @@ -9,6 +9,10 @@ + + + + \ No newline at end of file diff --git a/EDForceFeedback/EDForceFeedback.csproj b/EDForceFeedback/EDForceFeedback.csproj index 4ee88c3..f68400b 100644 --- a/EDForceFeedback/EDForceFeedback.csproj +++ b/EDForceFeedback/EDForceFeedback.csproj @@ -61,8 +61,8 @@ ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll - - ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll @@ -125,6 +125,12 @@ Always + + Always + + + Always + Always diff --git a/EDForceFeedback/Forces/CenterSpringXY.ffe b/EDForceFeedback/Forces/CenterSpringXY.ffe new file mode 100644 index 0000000000000000000000000000000000000000..34ae9cea3ed7230e0b55fa31ba155510eb0f269a GIT binary patch literal 296 zcmWIYbaP_>0yqC4=a}+3$z3Lw1y^5XIN;xROx?#bIE00ffgv?5xx_g&uOzi7xS%LA zFFhiXfuZILFT>)}{>VN~GX^1`nyDWkVjF;ByMTCtg+Itx1{iGtWGeyje;@#f3#dcb zAU+e2uK>gjK->Vt20+XJG!q2WWkQ6F`z{F1y1=mDy1utFT-0E37;rT_o{ literal 0 HcmV?d00001 diff --git a/EDForceFeedback/Forces/Damper.ffe b/EDForceFeedback/Forces/Damper.ffe new file mode 100644 index 0000000000000000000000000000000000000000..55fc7f0354b440db20bb662dea4cb76c0ce8d51d GIT binary patch literal 296 zcmWIYbaP_>0yqC4=a}+3$z3Lw1y^5XIN;xROx?#bIE00ffgv?5xx^(gw;;91DZeDc zu_CjWfuZILFT>)}{zwi^MFt_DnyDWkVjF;ByMQ?3nkUFu1{iGtWGeyje;@#f3#dcb zAU+e2uK>gjK->Vt20+XJG!q0gWI}|E`z{F1y1=mDy1utFT-0G@w5;s5{u literal 0 HcmV?d00001 diff --git a/EDForceFeedback/packages.config b/EDForceFeedback/packages.config index 10d63b8..c1da3c8 100644 --- a/EDForceFeedback/packages.config +++ b/EDForceFeedback/packages.config @@ -5,7 +5,7 @@ - + diff --git a/EDForceFeedback/settings.json b/EDForceFeedback/settings.json index 32c20ac..bc946fe 100644 --- a/EDForceFeedback/settings.json +++ b/EDForceFeedback/settings.json @@ -12,8 +12,10 @@ "Devices": [ { - "ProductGuid": "02dd045e-0000-0000-0000-504944564944", - "ProductName": "Controller (Xbox One For Windows)", + "ProductGuid": "001b045e-0000-0000-0000-504944564944", + "ProductName": "SideWinder Force Feedback 2 Joystick", + "AutoCenter": true, + "ForceFeedbackGain": 10000, "StatusEvents": [ { diff --git a/ForceFeedbackSharpDx/MicrosoftSidewinder.cs b/ForceFeedbackSharpDx/ForceFeedbackController.cs similarity index 61% rename from ForceFeedbackSharpDx/MicrosoftSidewinder.cs rename to ForceFeedbackSharpDx/ForceFeedbackController.cs index a416256..78f8870 100644 --- a/ForceFeedbackSharpDx/MicrosoftSidewinder.cs +++ b/ForceFeedbackSharpDx/ForceFeedbackController.cs @@ -5,21 +5,26 @@ using System.Threading.Tasks; using SharpDX.DirectInput; using System.Linq; +using SharpDX; namespace ForceFeedbackSharpDx { - public class MicrosoftSidewinder + public class ForceFeedbackController { private const uint WINDOW_HANDLE_ERROR = 0x80070006; - // Microsoft Force Feedback 2 - //private static Guid product = new Guid("001b045e-0000-0000-0000-504944564944"); - private Joystick joystick = null; private readonly Dictionary knownEffects = new Dictionary(); private readonly Dictionary> fileEffects = new Dictionary>(); - public bool ForceFeedback2(string productGuid, string productName, bool AutoCenter, int ForceFeedbackGain) + public bool Initialize( + string productGuid, + string productName, + string forceFilesFolder, + bool autoCenter, + int forceFeedbackGain, + int deadzone = 0, + int saturation = 10000) { // Initialize DirectInput var directInput = new DirectInput(); @@ -33,6 +38,8 @@ public bool ForceFeedback2(string productGuid, string productName, bool AutoCent var directInputDevices = new List(); directInputDevices.AddRange(directInput.GetDevices()); + + // Filtered //directInputDevices.AddRange(directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices)); //directInputDevices.AddRange(directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)); //directInputDevices.AddRange(directInput.GetDevices(DeviceType.Driving, DeviceEnumerationFlags.AllDevices)); @@ -70,7 +77,7 @@ public bool ForceFeedback2(string productGuid, string productName, bool AutoCent } // Load all of the effect files - var forcesFolder = new DirectoryInfo(@".\Forces"); + var forcesFolder = new DirectoryInfo(forceFilesFolder); foreach (var file in forcesFolder.GetFiles("*.ffe")) { @@ -87,9 +94,10 @@ public bool ForceFeedback2(string productGuid, string productName, bool AutoCent try { + // Exclusive is required to control the forces joystick.SetCooperativeLevel(handle, CooperativeLevel.Exclusive | CooperativeLevel.Background); } - catch(SharpDX.SharpDXException ex ) when ((uint)ex.HResult == WINDOW_HANDLE_ERROR) + catch (SharpDX.SharpDXException ex) when ((uint)ex.HResult == WINDOW_HANDLE_ERROR) { Console.WriteLine(); Console.WriteLine(); @@ -105,10 +113,11 @@ public bool ForceFeedback2(string productGuid, string productName, bool AutoCent try { - // Autocenter on - joystick.Properties.AutoCenter = AutoCenter; + // Autocenter: For the MSFF2 joystick this value is a spring force that plays in the background. + // If all effects are stopped. The spring will stop too. Reset will turn it back on. + joystick.Properties.AutoCenter = autoCenter; } - catch (Exception _) + catch (SharpDXException) { // Some devices do not support this setting. } @@ -116,9 +125,11 @@ public bool ForceFeedback2(string productGuid, string productName, bool AutoCent // Acquire the joystick joystick.Acquire(); - //var test = joystick.Properties.ForceFeedbackGain; + SetActuators(true); - joystick.Properties.ForceFeedbackGain = ForceFeedbackGain; + joystick.Properties.DeadZone = deadzone; + joystick.Properties.Saturation = saturation; + joystick.Properties.ForceFeedbackGain = forceFeedbackGain; return true; } @@ -139,46 +150,95 @@ private void PlayEffects(IEnumerable effects) PlayEffect(effect); } - private void StopEffects(IEnumerable effects) + /// + /// Stop any effects that were started from the PlayFileEffect() method. + /// + /// + public void StopEffects(IEnumerable effects) { foreach (var effect in effects) - effect.Stop(); + { + try { effect.Stop(); } + catch (SharpDXException) { } + } foreach (var effect in effects) - effect.Dispose(); + effect?.Dispose(); - Reset(); + //Reset(); // This will kill all effects } - private void Reset() + /// + /// Stop all effects. Reset to Default. Note: This reenables the center spring if autocenter is set. + /// + public void Reset() { joystick.SendForceFeedbackCommand(ForceFeedbackCommand.Reset); } - public void PlayFileEffect(string name, int duration = 250) + /// + /// Stop all effects. Note: This will disable the autocenter effect. To reenable call Reset(). + /// + public void StopAllEffects() + { + joystick.SendForceFeedbackCommand(ForceFeedbackCommand.StopAll); + } + + /// + /// Set the actuators on/off + /// + public void SetActuators(bool on = true) + { + if (on) + joystick.SendForceFeedbackCommand(ForceFeedbackCommand.SetActuatorsOn); + else + joystick.SendForceFeedbackCommand(ForceFeedbackCommand.SetActuatorsOff); + } + + /// + /// Copy the effect file and begin playing it. If the duration is greater than zero the effect will play for that many milliseconds + /// and then be stopped. + /// + /// Effect file name + /// Zero and below will play until stopped. Above zero will play for that many milliseconds. Default: 250. + /// The effects being played as a List or null if the effect file was not found. + public List PlayFileEffect(string name, int duration = 250) { try { var fileEffect = fileEffects[name]; + // Create a new List<> of effects + var forceEffects = fileEffect.ConvertAll(x => new Effect(joystick, x.Guid, x.Parameters)); + _ = Task.Run(async () => { - // Create a new list of effects - var forceEffects = fileEffect.Select(x => new Effect(joystick, x.Guid, x.Parameters)).ToList(); PlayEffects(forceEffects); - await Task.Delay(duration).ConfigureAwait(false); - StopEffects(forceEffects); + if (duration > 0) + { + await Task.Delay(duration).ConfigureAwait(false); + StopEffects(forceEffects); + } + else + { + // Wait a moment, effects don't seem to play if we exit fast + await Task.Delay(100).ConfigureAwait(false); + } }).ContinueWith(t => { if (t.IsCanceled) Console.WriteLine($"Effect {name} cancelled"); else if (t.IsFaulted) Console.WriteLine($"Effect {name} Exception {t.Exception.InnerException?.Message}"); else Console.WriteLine($"Effect {name} complete"); }); + + return forceEffects; } catch (Exception ex) { Console.WriteLine($"Exception {ex.Message}"); } + + return null; } } } diff --git a/ForceFeedbackSharpDx/ForceFeedbackSharpDx.csproj b/ForceFeedbackSharpDx/ForceFeedbackSharpDx.csproj index 3a29620..cb35878 100644 --- a/ForceFeedbackSharpDx/ForceFeedbackSharpDx.csproj +++ b/ForceFeedbackSharpDx/ForceFeedbackSharpDx.csproj @@ -2,6 +2,7 @@ netstandard2.0 + d3981932-571e-4587-9ab1-a84feafe7953 diff --git a/Journals/Client.cs b/Journals/Client.cs index 84ce566..7fb508e 100644 --- a/Journals/Client.cs +++ b/Journals/Client.cs @@ -24,10 +24,11 @@ public void Initialize(Settings settings) foreach (var device in settings.Devices) { - var ffDevice = new MicrosoftSidewinder(); - if (ffDevice.ForceFeedback2( + var ffDevice = new ForceFeedbackController(); + if (ffDevice.Initialize( device.ProductGuid, device.ProductName, + @".\Forces", device.AutoCenter, device.ForceFeedbackGain) == false) continue; @@ -58,7 +59,7 @@ private void Events_AllEvent(object sender, dynamic e) if (device.EventSettings.ContainsKey(key)) { var eventConfig = device.EventSettings[key]; - device.Device?.PlayFileEffect(eventConfig.ForceFile, eventConfig.Duration); + var test = device.Device?.PlayFileEffect(eventConfig.ForceFile, eventConfig.Duration); } } } diff --git a/Journals/DeviceEvents.cs b/Journals/DeviceEvents.cs index e22c357..34af6a5 100644 --- a/Journals/DeviceEvents.cs +++ b/Journals/DeviceEvents.cs @@ -7,7 +7,7 @@ namespace Journals { public class DeviceEvents { - public MicrosoftSidewinder Device { get; set; } + public ForceFeedbackController Device { get; set; } public Dictionary EventSettings { get; set; } = new Dictionary(); } }