From 3682fa551a759b12c28610951e28d648143191e2 Mon Sep 17 00:00:00 2001 From: Orangerot Date: Fri, 10 Jan 2025 20:33:31 +0100 Subject: [PATCH] feat: read gyroscope as InputDirection and buttonChange to pause/resume --- lib/esense_input.dart | 91 +++++++++++++++++++++++++++++++++++++++++-- lib/level.dart | 35 +++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) diff --git a/lib/esense_input.dart b/lib/esense_input.dart index 705bd5f..c8fc22a 100644 --- a/lib/esense_input.dart +++ b/lib/esense_input.dart @@ -4,17 +4,27 @@ import 'dart:io'; import 'package:esense_flutter/esense.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:sense_the_rhythm/arrows.dart'; +import 'package:sense_the_rhythm/level.dart'; class ESenseInput { static final instance = ESenseInput._(); ESenseManager eSenseManager = ESenseManager('unknown'); ValueNotifier deviceStatus = ValueNotifier(''); + StreamSubscription? subscription; String eSenseDeviceName = ''; bool connected = false; bool sampling = false; + int sampleRate = 20; + + InputDirection inputDirection = InputDirection(); + int x = 0; + int y = 0; + int z = 0; + ESenseInput._() { _listenToESense(); } @@ -22,7 +32,8 @@ class ESenseInput { Future _askForPermissions() async { if (!Platform.isAndroid && !Platform.isIOS) return; if (!(await Permission.bluetoothScan.request().isGranted && - await Permission.bluetoothConnect.request().isGranted)) { + await Permission.bluetoothConnect.request().isGranted && + await Permission.bluetooth.request().isGranted)) { print( 'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.'); } @@ -35,10 +46,10 @@ class ESenseInput { } } - StreamSubscription _listenToESense() { + void _listenToESense() { // if you want to get the connection events when connecting, // set up the listener BEFORE connecting... - return eSenseManager.connectionEvents.listen((event) { + eSenseManager.connectionEvents.listen((event) { print('CONNECTION event: $event'); // when we're connected to the eSense device, we can start listening to events from it @@ -49,6 +60,7 @@ class ESenseInput { case ConnectionType.connected: deviceStatus.value = 'connected'; connected = true; + _startListenToSensorEvents(); break; case ConnectionType.unknown: deviceStatus.value = 'unknown'; @@ -56,6 +68,7 @@ class ESenseInput { case ConnectionType.disconnected: deviceStatus.value = 'disconnected'; sampling = false; + _pauseListenToSensorEvents(); break; case ConnectionType.device_found: deviceStatus.value = 'device_found'; @@ -67,6 +80,75 @@ class ESenseInput { }); } + Stream buttonEvents() { + return eSenseManager.eSenseEvents + .where((event) => event.runtimeType == ButtonEventChanged) + .cast(); + } + + void _startListenToSensorEvents() async { + // // any changes to the sampling frequency must be done BEFORE listening to sensor events + print('setting sampling frequency...'); + bool successs = await eSenseManager.setSamplingRate(sampleRate); + if (successs) { + print('setSamplingRate success'); + } else { + print('setSamplingRate fail'); + } + + // subscribe to sensor event from the eSense device + subscription = eSenseManager.sensorEvents.listen((event) { + // print('SENSOR event: $event'); + if (event.gyro != null) { + _parseGyroData(event.gyro!); + } + }); + sampling = true; + } + + void _pauseListenToSensorEvents() async { + subscription?.cancel(); + sampling = false; + } + + void _parseGyroData(List data) { + // Float value in deg/s = Gyro value / Gyro scale factor + // The default configuration is +- 500deg/s for the gyroscope. + x = (x + (15 * data[0] ~/ (500 * sampleRate))) % 360; + y = (y + (15 * data[1] ~/ (500 * sampleRate))) % 360; + z = (z + (15 * data[2] ~/ (500 * sampleRate))) % 360; + print('$x, $y, $z'); + // print('${(z.toDouble() / 500.0 * (1.0 / sampleRate.toDouble())) * 7.5}'); + // print('${z.toDouble() / 500.0 * (1.0 / 10.0)}'); + } + + void resetAngles() { + inputDirection.reset(); + x = 0; + y = 0; + z = 0; + } + + InputDirection getInputDirection(ArrowDirection expect) { + inputDirection.up = z > 270 && z < 340; + inputDirection.down = z > 40 && z < 180; + inputDirection.left = y > 40 && y < 180; + inputDirection.right = y > 270 && y < 340; + + if (expect == ArrowDirection.up && inputDirection.up || + expect == ArrowDirection.down && inputDirection.down) { + y = 0; + print("ehit"); + } + if (expect == ArrowDirection.left && inputDirection.left || + expect == ArrowDirection.right && inputDirection.right) { + z = 0; + print("ehit"); + } + + return inputDirection; + } + Future connectToESense(String deviceName) async { if (!connected) { await _askForPermissions(); @@ -74,7 +156,8 @@ class ESenseInput { eSenseDeviceName = deviceName; eSenseManager.deviceName = deviceName; connected = await eSenseManager.connect(); - print('Trying to connect to eSense device namend \'${eSenseManager.deviceName}\''); + print( + 'Trying to connect to eSense device namend \'${eSenseManager.deviceName}\''); deviceStatus.value = connected ? 'connecting...' : 'connection failed'; print(deviceStatus.value); diff --git a/lib/level.dart b/lib/level.dart index 4f69b67..c57f34c 100644 --- a/lib/level.dart +++ b/lib/level.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/services.dart'; import 'package:sense_the_rhythm/arrows.dart'; +import 'package:sense_the_rhythm/esense_input.dart'; import 'package:sense_the_rhythm/game_over_stats.dart'; import 'package:sense_the_rhythm/simfile.dart'; @@ -20,6 +21,13 @@ class InputDirection { bool down = false; bool left = false; bool right = false; + + void reset() { + up = false; + down = false; + left = false; + right = false; + } } class _LevelState extends State with SingleTickerProviderStateMixin { @@ -30,6 +38,7 @@ class _LevelState extends State with SingleTickerProviderStateMixin { StreamSubscription? _durationSubscription; StreamSubscription? _positionSubscription; + StreamSubscription? _buttonSubscription; final FocusNode _focusNode = FocusNode(); InputDirection inputDirection = InputDirection(); @@ -53,6 +62,7 @@ class _LevelState extends State with SingleTickerProviderStateMixin { @override void initState() { super.initState(); + ESenseInput.instance.resetAngles(); _animationController = AnimationController( vsync: this, @@ -77,6 +87,22 @@ class _LevelState extends State with SingleTickerProviderStateMixin { setState(() => _duration = duration); }); + _buttonSubscription = ESenseInput.instance.buttonEvents().listen((event) { + if (!event.pressed) { + if (_isPlaying) { + player.pause(); + setState(() { + _isPlaying = false; + }); + } else { + player.resume(); + setState(() { + _isPlaying = true; + }); + } + } + }); + _positionSubscription = player.onPositionChanged.listen( (p) => setState(() => _position = p), ); @@ -104,6 +130,12 @@ class _LevelState extends State with SingleTickerProviderStateMixin { continue; } if (note.position.abs() < 0.5 * 1.0 / 60.0) { + InputDirection esenseDirection = + ESenseInput.instance.getInputDirection(note.direction); + inputDirection.up |= esenseDirection.up; + inputDirection.down |= esenseDirection.down; + inputDirection.left |= esenseDirection.left; + inputDirection.right |= esenseDirection.right; bool keypressCorrect = false; switch (note.direction) { case ArrowDirection.up: @@ -124,6 +156,7 @@ class _LevelState extends State with SingleTickerProviderStateMixin { note.wasHit = true; _animationController.reset(); _animationController.forward(); + inputDirection.reset(); setState(() { hitOrMissMessage = 'Great!'; }); @@ -133,6 +166,7 @@ class _LevelState extends State with SingleTickerProviderStateMixin { note.wasHit = false; _animationController.reset(); _animationController.forward(); + inputDirection.reset(); setState(() { hitOrMissMessage = 'Missed'; }); @@ -254,6 +288,7 @@ class _LevelState extends State with SingleTickerProviderStateMixin { _animationController.dispose(); _durationSubscription?.cancel(); _positionSubscription?.cancel(); + _buttonSubscription?.cancel(); player.dispose(); super.dispose(); }