summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorOrangerot <purple@orangerot.dev>2025-01-10 20:33:31 +0100
committerOrangerot <purple@orangerot.dev>2025-01-10 20:33:31 +0100
commit3682fa551a759b12c28610951e28d648143191e2 (patch)
tree0e796e98c955485df0cd42c8cf03f789b58c72e0 /lib
parentf589cf8e92c043f6cfee7ab9507f5253404a97c4 (diff)
feat: read gyroscope as InputDirection and buttonChange to pause/resume
Diffstat (limited to 'lib')
-rw-r--r--lib/esense_input.dart91
-rw-r--r--lib/level.dart35
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<String> 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<void> _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<ButtonEventChanged> 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<int> 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<void> 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<Level> with SingleTickerProviderStateMixin {
@@ -30,6 +38,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription;
+ StreamSubscription? _buttonSubscription;
final FocusNode _focusNode = FocusNode();
InputDirection inputDirection = InputDirection();
@@ -53,6 +62,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
+ ESenseInput.instance.resetAngles();
_animationController = AnimationController(
vsync: this,
@@ -77,6 +87,22 @@ class _LevelState extends State<Level> 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<Level> 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<Level> with SingleTickerProviderStateMixin {
note.wasHit = true;
_animationController.reset();
_animationController.forward();
+ inputDirection.reset();
setState(() {
hitOrMissMessage = 'Great!';
});
@@ -133,6 +166,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
note.wasHit = false;
_animationController.reset();
_animationController.forward();
+ inputDirection.reset();
setState(() {
hitOrMissMessage = 'Missed';
});
@@ -254,6 +288,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
_animationController.dispose();
_durationSubscription?.cancel();
_positionSubscription?.cancel();
+ _buttonSubscription?.cancel();
player.dispose();
super.dispose();
}