startHeadTracking method

Future<HeadTrackingResponse> startHeadTracking({
  1. VitureImuFrequency imuFrequency = VitureImuFrequency.freq120Hz,
  2. bool setDarwinOpenExclusive = false,
})

Implementation

Future<HeadTrackingResponse> startHeadTracking({
  VitureImuFrequency imuFrequency = VitureImuFrequency.freq120Hz,
  bool setDarwinOpenExclusive = false,
}) async {
  const imuMode = VitureImuMode.pose;

  if (_isHeadTrackingActive || _isStarting) {
    return HeadTrackingResponse(
      status: true,
      message: "Successfully started head tracking.",
      code: bindings.VITURE_GLASSES_SUCCESS,
    );
  }
  if (_isReleasing) {
    throw StateError(
      'Cannot start head tracking while release is in progress.',
    );
  }

  _isStarting = true;
  _imuMode = imuMode;

  try {
    _sensorController ??= StreamController<VitureSensorData>.broadcast();

    final result = await _opQueue.then((_) async {
      await connect(setDarwinOpenExclusive: setDarwinOpenExclusive);
      final api = _api;
      final provider = _provider;
      if (api == null || provider == null || provider == ffi.nullptr) {
        return HeadTrackingResponse(
          status: false,
          message: "Unable to find the glasses.",
          code: bindings.VITURE_GLASSES_ERROR_DEVICE_REJECTED,
        );
      }

      _deviceType = api.xr_device_provider_get_device_type(provider);

      if (_deviceType != VitureDeviceType.carina) {
        if (imuMode == VitureImuMode.raw) {
          _rawCallable =
              ffi.NativeCallable<
                bindings.VitureImuRawCallbackFunction
              >.listener((
                ffi.Pointer<ffi.Float> dataPtr,
                int timestamp,
                int vsync,
              ) {
                if (!_isHeadTrackingActive || dataPtr == ffi.nullptr) return;
                final controller = _sensorController;
                if (controller == null || controller.isClosed) return;
                try {
                  controller.add(
                    VitureSensorData.raw(
                      gyroX: dataPtr[0],
                      gyroY: dataPtr[1],
                      gyroZ: dataPtr[2],
                      accelX: dataPtr[3],
                      accelY: dataPtr[4],
                      accelZ: dataPtr[5],
                      magX: dataPtr[6],
                      magY: dataPtr[7],
                      magZ: dataPtr[8],
                      temperature: dataPtr[9],
                      timestamp: timestamp,
                      vsync: vsync,
                    ),
                  );
                } catch (_) {}
              });
          api.xr_device_provider_register_imu_raw_callback(
            provider,
            _rawCallable!.nativeFunction,
          );
        } else {
          _poseCallable =
              ffi.NativeCallable<
                bindings.VitureImuPoseCallbackFunction
              >.listener((ffi.Pointer<ffi.Float> dataPtr, int timestamp) {
                if (!_isHeadTrackingActive || dataPtr == ffi.nullptr) return;
                final controller = _sensorController;
                if (controller == null || controller.isClosed) return;
                try {
                  controller.add(
                    VitureSensorData.pose(
                      roll: dataPtr[0],
                      pitch: dataPtr[1],
                      yaw: dataPtr[2],
                      quatW: dataPtr[3],
                      quatX: dataPtr[4],
                      quatY: dataPtr[5],
                      quatZ: dataPtr[6],
                      timestamp: timestamp,
                    ),
                  );
                } catch (_) {}
              });
          api.xr_device_provider_register_imu_pose_callback(
            provider,
            _poseCallable!.nativeFunction,
          );
        }

        final openResult = api.xr_device_provider_open_imu(
          provider,
          imuMode.value,
          imuFrequency.value,
        );
        if (openResult < 0) {
          await _forceCleanupHeadTracking();
          return HeadTrackingResponse(
            status: false,
            message: "Unable to find the glasses.",
            code: bindings.VITURE_GLASSES_ERROR_DEVICE_REJECTED,
          );
        }
      } else {
        _posePtr = calloc<ffi.Float>(7);
        _statusPtr = calloc<ffi.Int>();

        _carinaTimer = Timer.periodic(const Duration(milliseconds: 2), (_) {
          if (!_isHeadTrackingActive) return;
          final api = _api;
          final provider = _provider;
          final posePtr = _posePtr;
          final statusPtr = _statusPtr;
          if (api == null ||
              provider == null ||
              posePtr == null ||
              statusPtr == null) {
            return;
          }
          try {
            api.xr_device_provider_get_gl_pose_carina(
              provider,
              posePtr,
              0.0,
              statusPtr,
            );
            if (statusPtr.value == 0) {
              final controller = _sensorController;
              if (controller == null || controller.isClosed) return;
              controller.add(
                VitureSensorData.pose(
                  roll: posePtr[0],
                  pitch: posePtr[1],
                  yaw: posePtr[2],
                  quatW: posePtr[3],
                  quatX: posePtr[4],
                  quatY: posePtr[5],
                  quatZ: posePtr[6],
                  timestamp: DateTime.now().millisecondsSinceEpoch,
                ),
              );
            }
          } catch (_) {}
        });
      }

      return HeadTrackingResponse(
        status: true,
        message: "Successfully started head tracking.",
        code: bindings.VITURE_GLASSES_SUCCESS,
      );
    });
    _opQueue = _opQueue.then((_) {}, onError: (_) {});

    if (result.status) {
      _isHeadTrackingActive = true;
    }
    return result;
  } catch (e) {
    await _forceCleanupHeadTracking();
    return HeadTrackingResponse(
      status: false,
      message: "Unable to find the glasses.",
      code: bindings.VITURE_GLASSES_ERROR_DEVICE_REJECTED,
    );
  } finally {
    _isStarting = false;
  }
}