private void OnHapticFeedbackReceived(object sender, HapticPulse hapticPulse) { lastHapticPulseLengthUs = hapticPulse.LengthUs; lastHapticPulseTime = DateTime.Now; RaisePropertyChanged(() => HapticPulseInfo); }
/// <summary> /// Perform a haptic feedback pulse /// </summary> /// <param name="node">Node on which to control the pulse</param> /// <param name="hapticPulse">Haptic pulse to perform</param> /// <param name="durationMultiplier">(Optional) Multiplier value applied to the hapticPulse duration</param> /// <param name="intensityMultiplier">(Optional) Multiplier value applied to the hapticPulse intensity</param> public static void Pulse(this IControlHaptics obj, Node?node, HapticPulse hapticPulse, float durationMultiplier = 1f, float intensityMultiplier = 1f) { pulse(node, hapticPulse, durationMultiplier, intensityMultiplier); }
// Sets up connection and returns true if all links are established. private bool TrySettingUpConnection() { lastConnectionAttempt = DateTime.Now; // Make sure API server is alive var status = api.GetStatus(timeoutThresholdMs); if (status == null) { return(false); } // Reset current connections, if exist DisconnectAllEndpoints(); try { if (capabilities.HasFlag(Capabilities.HeadTracking)) { var endpointStatus = status.Endpoints.FirstOrDefault(x => x.Name == EndpointNames.HeadTracking); if (endpointStatus == null || endpointStatus.Code != (int)ControlResponseCode.OK) { return(false); } head = new HeadRemote(api.CreateProxy <HeadTrackingProxy>()); } if (capabilities.HasFlag(Capabilities.Controllers)) { var endpointStatus = status.Endpoints.FirstOrDefault(x => x.Name == EndpointNames.Controller); if (endpointStatus == null || endpointStatus.Code != (int)ControlResponseCode.OK) { return(false); } controller = new ControllerRemote(api.CreateProxy <ControllerProxy>()); } // Subscribe to haptic pulse broadcasts, if controller proxy is in use if (controller != null) { broadcasts = api.CreateProxy <BroadcastProxy>(); // Forward the events to long-lived event so API user doesn't // have to resubscribe on reconnect broadcasts.HapticPulseReceived += (s, e) => HapticPulse?.Invoke(this, e); } return(true); } catch (Exception x) { Logger.Error("Error during API connection: " + x); // Cleanup possibly connected endpoints DisconnectAllEndpoints(); } return(false); }
/// <summary> /// Perform a haptic feedback pulse /// </summary> /// <param name="user">The functionality user</param> /// <param name="node">Node on which to control the pulse</param> /// <param name="hapticPulse">Haptic pulse to perform</param> /// <param name="durationMultiplier">(Optional) Multiplier value applied to the hapticPulse duration</param> /// <param name="intensityMultiplier">(Optional) Multiplier value applied to the hapticPulse intensity</param> public static void Pulse(this IUsesControlHaptics user, Node node, HapticPulse hapticPulse, float durationMultiplier = 1f, float intensityMultiplier = 1f) { #if !FI_AUTOFILL user.provider.Pulse(node, hapticPulse, durationMultiplier, intensityMultiplier); #endif }
/// <summary> /// Pulse haptic feedback /// </summary> /// <param name="node">Node on which to perform the pulse.</param> /// <param name="hapticPulse">Haptic pulse</param> /// <param name="durationMultiplier">(Optional) Multiplier value applied to the hapticPulse duration</param> /// <param name="intensityMultiplier">(Optional) Multiplier value applied to the hapticPulse intensity</param> public void Pulse(Node node, HapticPulse hapticPulse, float durationMultiplier = 1f, float intensityMultiplier = 1f) { // Clip buffer can hold up to 800 milliseconds of samples // At 320Hz, each sample is 3.125f milliseconds if (Mathf.Approximately(m_MasterIntensity, 0)) { return; } #if ENABLE_OVR_INPUT m_GeneratedHapticClip.Reset(); var duration = hapticPulse.duration * durationMultiplier; var intensity = hapticPulse.intensity * intensityMultiplier; var fadeIn = hapticPulse.fadeIn; var fadeOut = hapticPulse.fadeOut; if (duration > MaxDuration) { duration = Mathf.Clamp(duration, 0f, MaxDuration); // Clamp at maximum 800ms for sample buffer if (!m_SampleLengthWarningShown) { Debug.LogWarning("Pulse durations greater than 0.8f are not currently supported"); } m_SampleLengthWarningShown = true; } const int kSampleRateConversion = 490; // Samplerate conversion : 44100/90fps = 490 const int kIntensityIncreaseMultiplier = 255; // Maximum value of 255 for intensity const float kFadeInProportion = 0.25f; var fadeInSampleCount = duration * kSampleRateConversion * kFadeInProportion; var fadeOutSampleCount = fadeInSampleCount * 2; // FadeOut is less apparent than FadeIn unless FadeOut duration is increased duration *= kSampleRateConversion; var durationFadeOutPosition = duration - fadeOutSampleCount; intensity = Mathf.Clamp(Mathf.Clamp01(intensity) * kIntensityIncreaseMultiplier * m_MasterIntensity, 0, kIntensityIncreaseMultiplier); var hapticClipSample = Convert.ToByte(intensity); for (int i = 1; i < duration; ++i) { float sampleShaped = hapticClipSample; if (fadeIn && i < fadeInSampleCount) { sampleShaped = Mathf.Lerp(0, intensity, i / fadeInSampleCount); } else if (fadeOut && i > durationFadeOutPosition) { sampleShaped = Mathf.Lerp(0, intensity, (duration - i) / fadeOutSampleCount); } m_GeneratedHapticClip.WriteSample(Convert.ToByte(sampleShaped)); } const float kMaxSimultaneousClipDuration = 0.25f; var channel = GetTargetChannel(node); if (duration > kMaxSimultaneousClipDuration) { // Prevent multiple long clips from playing back simultaneously // If the new clip has a long duration, stop playback of any existing clips in order to prevent haptic feedback noise if (channel != null) { channel.Preempt(m_GeneratedHapticClip); } else { m_RHapticsChannel.Preempt(m_GeneratedHapticClip); m_LHapticsChannel.Preempt(m_GeneratedHapticClip); } } else { // Allow multiple short clips to play simultaneously if (channel != null) { channel.Mix(m_GeneratedHapticClip); } else { m_RHapticsChannel.Mix(m_GeneratedHapticClip); m_LHapticsChannel.Mix(m_GeneratedHapticClip); } } #endif }
/// <summary> /// Pulse haptic feedback /// </summary> /// <param name="node">Node on which to perform the pulse.</param> /// <param name="hapticPulse">Haptic pulse</param> /// <param name="durationMultiplier">(Optional) Multiplier value applied to the hapticPulse duration</param> /// <param name="intensityMultiplier">(Optional) Multiplier value applied to the hapticPulse intensity</param> public void Pulse(Node node, HapticPulse hapticPulse, float durationMultiplier = 1f, float intensityMultiplier = 1f) { // Clip buffer can hold up to 800 milliseconds of samples // At 320Hz, each sample is 3.125f milliseconds if (Mathf.Approximately(m_MasterIntensity, 0)) { return; } // Reset buffer m_GeneratedHapticClip.Seek(0, SeekOrigin.Begin); m_GeneratedHapticClip.SetLength(0); var duration = hapticPulse.duration * durationMultiplier; var intensity = hapticPulse.intensity * intensityMultiplier; var fadeIn = hapticPulse.fadeIn; var fadeOut = hapticPulse.fadeOut; if (duration > MaxDuration) { duration = Mathf.Clamp(duration, 0f, MaxDuration); // Clamp at maximum 800ms for sample buffer if (!m_SampleLengthWarningShown) { Debug.LogWarning("Pulse durations greater than 0.8f are not currently supported"); } m_SampleLengthWarningShown = true; } const int kSampleRateConversion = 490; // Samplerate conversion : 44100/90fps = 490 const int kIntensityIncreaseMultiplier = 255; // Maximum value of 255 for intensity const float kFadeInProportion = 0.25f; var fadeInSampleCount = duration * kSampleRateConversion * kFadeInProportion; var fadeOutSampleCount = fadeInSampleCount * 2; // FadeOut is less apparent than FadeIn unless FadeOut duration is increased duration *= kSampleRateConversion; var durationFadeOutPosition = duration - fadeOutSampleCount; intensity = Mathf.Clamp(Mathf.Clamp01(intensity) * kIntensityIncreaseMultiplier * m_MasterIntensity, 0, kIntensityIncreaseMultiplier); var hapticClipSample = Convert.ToByte(intensity); for (int i = 1; i < duration; ++i) { float sampleShaped = hapticClipSample; if (fadeIn && i < fadeInSampleCount) { sampleShaped = Mathf.Lerp(0, intensity, i / fadeInSampleCount); } else if (fadeOut && i > durationFadeOutPosition) { sampleShaped = Mathf.Lerp(0, intensity, (duration - i) / fadeOutSampleCount); } var sampleByte = Convert.ToByte(sampleShaped); m_GeneratedHapticClip.WriteByte(sampleByte); } const float kMaxSimultaneousClipDuration = 0.25f; var channel = GetTargetChannel(node); if (duration > kMaxSimultaneousClipDuration) { // Prevent multiple long clips from playing back simultaneously // If the new clip has a long duration, stop playback of any existing clips in order to prevent haptic feedback noise var buffer = m_GeneratedHapticClip.GetBuffer(); if (node == Node.None) { StopPulses(); if (m_Capabilites.supportsBuffer) { m_LeftHand.SendHapticBuffer(0, buffer); m_RightHand.SendHapticBuffer(0, buffer); } else { m_LeftHand.SendHapticImpulse(0, intensity, duration); m_RightHand.SendHapticImpulse(0, intensity, duration); } } else { StopPulses(node); if (m_Capabilites.supportsBuffer) { channel.SendHapticBuffer(0, buffer); } else { channel.SendHapticImpulse(0, intensity, duration); } } } }