XrHapticPcmVibrationFB
struct in function xrApplyHapticFeedback
:typedef struct XrHapticPcmVibrationFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
uint32_t bufferSize;
const float* buffer;
float sampleRate;
XrBool32 append;
uint32_t* samplesConsumed;
} XrHapticPcmVibrationFB;
Field | Description |
---|---|
buffer | a float array that represents the haptic feedback samples. If you consider the haptic effect as a sampled analog audio, then this buffer will contain the samples representing that effect. The values in this buffer are expected to be in the range [-1.0, 1.0]. |
next | next is NULL or a pointer to the next structure in a structure chain. For more details, read Passing Function Parameters through Structs. |
bufferSize | number of elements in buffer |
sampleRate | a float value representing the number of samples to be played from buffer per second. This is used to determine the duration of the effect. Based on this sample rate, the runtime resamples the buffer to match the device’s sample rate. |
append | to support long haptic effects, set this flag to true , which means that the effect will be played after the currently-playing effect is finished. If append is false , then the provided effect will begin playing immediately. |
samplesConsumed | this value will be populated to tell the application about how many samples were consumed from the buffer you have provided. This helps you estimate when you can make the next API call and whether to resend the samples which were not consumed from the previous API call. |
xrApplyHapticFeedback
function by passing this new struct as a function parameter. Here is an example on how to create a XrHapticPcmVibrationFB
struct and pass it to xrApplyHapticFeedback
:XrHapticPcmVibrationFB v{XR_TYPE_HAPTIC_PCM_VIBRATION_FB, nullptr};
v.sampleRate = sampleRate;
v.bufferSize = bufferSize;
v.buffer = pcmBuffer.data();
uint32_t samplesUsed = 0;
v.samplesConsumed = &samplesUsed;
v.append = XR_FALSE;
XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr};
hai.action = action;
hai.subactionPath = subactionPath;
OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v));
samplesUsed = *(v.samplesConsumed);
XrHapticPcmVibrationFB v{XR_TYPE_HAPTIC_PCM_VIBRATION_FB, nullptr};
float buffer[] = {1};
v.sampleRate = 1;
v.bufferSize = 1;
v.buffer = buffer.data();
uint32_t samplesUsed = 0;
v.samplesConsumed = &samplesUsed;
v.append = XR_FALSE;
xrGetDeviceSampleRateFB
function. This function returns the haptic sample rate on the last connected controller. If the controller does not support PCM haptics, the function will return 0. The returned value will change if the connected controller changes, so call this function whenever necessary to ensure your application has the latest information on the device sample rate.xrGetDeviceSampleRateFB
definition is as follows:XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB(
XrSession session,
const XrHapticActionInfo* hapticActionInfo,
XrDevicePcmSampleRateGetInfoFB* deviceSampleRate);
deviceSampleRate
struct, which is defined as follows:typedef struct XrDevicePcmSampleRateGetInfoFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
float sampleRate;
} XrDevicePcmSampleRateGetInfoFB;
xrGetDeviceSampleRateFB
:PFN_xrGetDeviceSampleRateFB xrGetDeviceSampleRateFB = nullptr;
OXR(xrGetInstanceProcAddr(
GetInstance(),
"xrGetDeviceSampleRateFB",
(PFN_xrVoidFunction*)(&xrGetDeviceSampleRateFB)));
OXR(xrGetDeviceSampleRateFB(Session, &hai, &leftDeviceSampleRate_));
cout << leftDeviceSampleRate_.sampleRate;
sampleRate
you provide in xrHapticPcmVibrationFB
is different from the value returned by xrGetDeviceSampleRateFB
, then the runtime will resample the buffer.buffer
, and plays an equivalent haptic effect on the controller. If you do not want the runtime to perform any resampling, it is recommended that you use the sampleRate
returned by xrGetDeviceSampleRateFB
function and generate the haptics data based on this sampleRate
.
PCM haptics is also supported on Meta Quest Touch Controller. When PCM haptics is triggered on the Touch Controller, the runtime resamples the data in buffer
, and plays an equivalent haptic effect on the controller. If you do not want the runtime to perform any resampling, then it is recommended that you use the sampleRate
returned by xrGetDeviceSampleRateFB
function and generate the haptics data based on this sampleRate
.XrHapticAmplitudeEnvelopeVibrationFB
struct to the xrApplyHapticFeedback
function. The struct definition is the following:typedef struct XrHapticAmplitudeEnvelopeVibrationFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrDuration duration;
uint32_t amplitudeCount;
const float* amplitudes;
} XrHapticAmplitudeEnvelopeVibrationFB;
Field | Description |
---|---|
next | next is NULL or a pointer to the next structure in a structure chain. For more details, read Passing Function Parameters through Structs. |
duration | an XrDuration that represents the duration in nanoseconds for which the effect will be played. |
amplitudeCount | number of elements in the array |
amplitudes | array representing the amplitude envelope values |
xrApplyHapticFeedback
function by passing this new struct as a function parameter. Here is an example on how to create the struct and pass to xrApplyHapticFeedback
:XrHapticAmplitudeEnvelopeVibrationFB v{
XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB, nullptr};
v.duration = durationNanoSecs;
v.amplitudeCount = (uint32_t)envelopeSize;
v.amplitudes = amplitudes.data();
XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr};
hai.action = action;
hai.subactionPath = subactionPath;
OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v));
amplitudes
based on the duration
and amplitudeCount
, along with the device sample rate.// Create a thumb action
ThumbHapticAction = CreateAction(
BaseActionSet,
XR_ACTION_TYPE_VIBRATION_OUTPUT,
"the_thumb_haptic",
nullptr,
2,
handSubactionPaths);
XrHapticVibration v{XR_TYPE_HAPTIC_VIBRATION, nullptr};
v.amplitude = 1;
v.duration = ToXrTime(1); //1s
XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr};
hai.action = ThumbHapticAction;
hai.subactionPath = LeftHandPath;
OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v));
XR_PATH_NULL
in subactionPath of XrHapticActionInfo, then haptics are played for double the time on the right controller’s thumb LRA. To mitigate this, the application will have to call xrStopHapticFeedback
on the right controller.