<div align="center">
<img src="assets/icon/icon.svg" width="300">

# Sense the Rhythm
> Rhythm game where you have to headbang in the right direction to hit the notes.   

[Source Code](https://source.orangerot.dev/university/sense_the_rhythm)
</div>

## Idee
Das Rhythmusspiel **Sense the Rhythm** ist eine Flutter-App für Android, bei dem
der Nutzer durch Neigen des Kopfes im richtigen Moment Noten eines Liedes
treffen muss, die ihm in Form von Pfeilen entgegenkommen. 

## Benutzung

Um die App nutzen zu können, werden Simfiles benötigt, welche Lieder, Bilder und
Notendaten enthalten. Getestet wurde mit den Dateien, die man über 'Alle
herunterladen' von dem [In the Groove Pack](https://drive.google.com/drive/folders/1sIhv4_STnyqXiauMx3BUco0m-a5VEpYy)
bekommt. Die ZIP-Datei muss in einen Ordner auf dem Android-Gerät entpackt
werden. Generell sind `Beginner Packs`, zum Beispiel von itgpacks.com, zu
empfehlen, bei denen wenige Noten hintereinander und keine Noten parallel
gespielt werden. 

Sobald sich ein Ordner mit Simfiles auf dem Gerät befindet, kann dieser über
den `+`-Knopf ausgewählt werden, woraufhin die Level in der Levelauswahl zu
sehen sind. 

Soll das ESense Earable als Eingabe genutzen werden (Pfeiltasten auf der Tastatur
als Backup), muss das Bluetooth-Symbol oben rechts berührt werden. Daraufhin
erscheint ein Dialog, in welchem der Name des linken Earbuds eingegeben wird. Der
Name kann herausgefunden werden, indem der linken Earbud in den
Bluetooth-Pairing-Modus gebracht und in den Bluetooth-Einstellungen des
Betriebssystems nach den verfügbaren Geräten geschaut wird. Mehr Informationen
gibt es in der offiziellen eSense-User-Documentation. Wurde der Name in den
Dialog eingegeben, wird auf `Connect` gedrückt und gewartet bis der Text `connected`
(nicht nur `connecting...`!) erscheint. Jetzt kann der Dialog mit `Close`
geschlossen werden. 

In der Levelauswahl kann nun ein Level ausgewählt werden, worauf das Level
startet. Hier kann das Level pausiert und verlassen werden. Um das Level
abzuschließen müssen die Noten getroffen werden, indem das ESense in die
richtige Richtung geneigt oder die richtige Pfeiltaste in die auf einer
Tastatur gedrückt wird. 

Am Ende erscheint eine Statistik wie viele Noten getroffen und verfehlt wurden,
sowie die Option das Level zu wiederholen oder zu verlassen. 

## Code-Qualität

Der Code ist in eine Ordnerstruktur gegliedert, welche aufgeteilt ist in
`/models`, `/utils`, `/screens` und `/widgets`. Alle Imports sind durch absolute
Pfade repräsentiert. 

Alle Dateien wurden einheitlich mit dem Code-Formatter des Dart-Language-Servers
formatiert. 

## Dokumentation

Alle Methoden werden durch dart doc comments `///` beschrieben. Komplizierte
Code-Stellen werden durch Kommentare erweitert. Diese `README` dient dem Nutzer
als Dokumentation zur Nutzung der App. 

## Technische Raffinesse

> Ihre Umsetzung ist nicht trivial (zum Beispiel verarbeiten Sie
Sensordaten, Nutzen APIs etc.), Gesamtumfang > 400 Zeilen

Für die Umsetzung der App wurde ein eigener Parser für
[Simfiles](https://github.com/stepmania/stepmania/wiki/sm) geschrieben. Dieser
kann Tags, wie Titel, BPM oder Dateinamen zu Banner und Musik einlesen. Außerdem
werden die `Charts` mit sämtlichen Noten eingelesen. Diese Daten werden sowohl
in den Levels, als auch in der Levelauswahl benutzt. 

Zum Laden des ITG Packs wird das `file-picker` Plugin benutzt und Berechtigungen
im Manifest und zur Laufzeit gesetzt (für neue Android-Versionen ist dafür
leider kompletter Speicherzugriff nötig). Damit der ausgewählte Ordner beim
nächsten Starten der App bestehen bleibt, wird der Pfad mit dem Plugin
`shared_preferences` persistent gespeichert. 

Die Musik wird mit dem `audioplayer` Plugin abgespielt. Außerdem werden die
Zeitinformationen genutzt, um die Noten als Pfeile zu zeigen, welche sich
basierend auf dem Fortschritt des Liedes bewegen. Die Daten des ESense und
Tastatur werden in den Zeitabfragen genutzt, um zu prüfen, ob die Note getroffen
oder verfehlt wurde und die entsprechende Nachricht anzuzeigen. Für die
Nachricht wurde sich mit dem AnimationController befasst. 

Das `esense_flutter` Plugin wird direkt vom GitHub-Repo bezogen, damit es bei
aktuellen Flutter-Versionen benutzt werden kann. Der ESense-Input wird als
globaler Service implementiert, wodurch er in jedem Widget verfügbar ist, ohne
dass man es jedem Widget übergeben müsste. Von dem ESense Earable werden Daten
über die Verbindung, den Zustand des Knopfes und Gyroskop-Daten abgefragt. Über
den Knopf lässt sich das Level pausieren und weiterspielen. Die Neigung des
ESense wird über die Gyroskop-Daten berechnet, indem die Bewegungsänderungen mit
dem Standard-Skalierungsfaktor und der Delta-Zeit skaliert und für jede Messung
mit der bisherigen Neigung verrechnet wird. Die Neigung wird bei jeder
getroffenen Note neu kalibriert. Ob die Note getroffen wurde wird geprüft, indem
getestet wird ob sich die Neigung in einem definierten Wertebereich befindet. 

Die Launcher-Icons und Launcher-Name wurden durch die Plugins
`flutter_launcher_icons` und `flutter_app_name` automatisch für Größen und
gewünschten Plattformen erstellt. 

## Präsentation

Siehe `/presentation`

## Used Libraries

- https://github.com/cph-cachet/flutter-plugins.git
- https://pub.dev/packages/shared_preferences
- https://pub.dev/packages/file_picker
- https://pub.dev/packages/audioplayers
- https://pub.dev/packages/permission_handler
- https://pub.dev/packages/flutter_launcher_icons
- https://pub.dev/packages/flutter_app_name

## License

This project is licensed under the GPL-3 License - see the `LICENSE` file for details
