/// <summary> /// The system calls this each frame to update haptics playback. /// </summary> public void Process() { var hapticsState = OVRPlugin.GetControllerHapticsState(m_controller); float elapsedTime = Time.realtimeSinceStartup - m_prevSamplesQueuedTime; if (m_prevSamplesQueued > 0) { int expectedSamples = m_prevSamplesQueued - (int)(elapsedTime * OVRHaptics.Config.SampleRateHz + 0.5f); if (expectedSamples < 0) { expectedSamples = 0; } if ((hapticsState.SamplesQueued - expectedSamples) == 0) { m_numPredictionHits++; } else { m_numPredictionMisses++; } //Debug.Log(hapticsState.SamplesAvailable + "a " + hapticsState.SamplesQueued + "q " + expectedSamples + "e " //+ "Prediction Accuracy: " + m_numPredictionHits / (float)(m_numPredictionMisses + m_numPredictionHits)); if ((expectedSamples > 0) && (hapticsState.SamplesQueued == 0)) { m_numUnderruns++; //Debug.LogError("Samples Underrun (" + m_controller + " #" + m_numUnderruns + ") -" // + " Expected: " + expectedSamples // + " Actual: " + hapticsState.SamplesQueued); } m_prevSamplesQueued = hapticsState.SamplesQueued; m_prevSamplesQueuedTime = Time.realtimeSinceStartup; } int desiredSamplesCount = OVRHaptics.Config.OptimalBufferSamplesCount; if (m_lowLatencyMode) { float sampleRateMs = 1000.0f / (float)OVRHaptics.Config.SampleRateHz; float elapsedMs = elapsedTime * 1000.0f; int samplesNeededPerFrame = (int)Mathf.Ceil(elapsedMs / sampleRateMs); int lowLatencySamplesCount = OVRHaptics.Config.MinimumSafeSamplesQueued + samplesNeededPerFrame; if (lowLatencySamplesCount < desiredSamplesCount) { desiredSamplesCount = lowLatencySamplesCount; } } if (hapticsState.SamplesQueued > desiredSamplesCount) { return; } if (desiredSamplesCount > OVRHaptics.Config.MaximumBufferSamplesCount) { desiredSamplesCount = OVRHaptics.Config.MaximumBufferSamplesCount; } if (desiredSamplesCount > hapticsState.SamplesAvailable) { desiredSamplesCount = hapticsState.SamplesAvailable; } int acquiredSamplesCount = 0; int clipIndex = 0; while (acquiredSamplesCount < desiredSamplesCount && clipIndex < m_pendingClips.Count) { int numSamplesToCopy = desiredSamplesCount - acquiredSamplesCount; int remainingSamplesInClip = m_pendingClips[clipIndex].Clip.Count - m_pendingClips[clipIndex].ReadCount; if (numSamplesToCopy > remainingSamplesInClip) { numSamplesToCopy = remainingSamplesInClip; } if (numSamplesToCopy > 0) { int numBytes = numSamplesToCopy * OVRHaptics.Config.SampleSizeInBytes; int dstOffset = acquiredSamplesCount * OVRHaptics.Config.SampleSizeInBytes; int srcOffset = m_pendingClips[clipIndex].ReadCount * OVRHaptics.Config.SampleSizeInBytes; Marshal.Copy(m_pendingClips[clipIndex].Clip.Samples, srcOffset, m_nativeBuffer.GetPointer(dstOffset), numBytes); m_pendingClips[clipIndex].ReadCount += numSamplesToCopy; acquiredSamplesCount += numSamplesToCopy; } clipIndex++; } for (int i = m_pendingClips.Count - 1; i >= 0 && m_pendingClips.Count > 0; i--) { if (m_pendingClips[i].ReadCount >= m_pendingClips[i].Clip.Count) { m_pendingClips.RemoveAt(i); } } int desiredPadding = desiredSamplesCount - (hapticsState.SamplesQueued + acquiredSamplesCount); if (desiredPadding < (OVRHaptics.Config.MinimumBufferSamplesCount - acquiredSamplesCount)) { desiredPadding = (OVRHaptics.Config.MinimumBufferSamplesCount - acquiredSamplesCount); } if (desiredPadding > hapticsState.SamplesAvailable) { desiredPadding = hapticsState.SamplesAvailable; } if (desiredPadding > 0) { int numBytes = desiredPadding * OVRHaptics.Config.SampleSizeInBytes; int dstOffset = acquiredSamplesCount * OVRHaptics.Config.SampleSizeInBytes; int srcOffset = 0; Marshal.Copy(m_paddingClip.Samples, srcOffset, m_nativeBuffer.GetPointer(dstOffset), numBytes); acquiredSamplesCount += desiredPadding; } if (acquiredSamplesCount > 0) { OVRPlugin.HapticsBuffer hapticsBuffer; hapticsBuffer.Samples = m_nativeBuffer.GetPointer(); hapticsBuffer.SamplesCount = acquiredSamplesCount; OVRPlugin.SetControllerHaptics(m_controller, hapticsBuffer); hapticsState = OVRPlugin.GetControllerHapticsState(m_controller); m_prevSamplesQueued = hapticsState.SamplesQueued; m_prevSamplesQueuedTime = Time.realtimeSinceStartup; } }