feat: display beats as arrows with correct time offset
This commit is contained in:
parent
9eb8a29382
commit
e9ba842e21
|
@ -13,6 +13,24 @@ class Level extends StatefulWidget {
|
||||||
State<Level> createState() => _LevelState();
|
State<Level> createState() => _LevelState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ArrowDirection {
|
||||||
|
left(Icons.arrow_back),
|
||||||
|
down(Icons.arrow_downward),
|
||||||
|
up(Icons.arrow_upward),
|
||||||
|
right(Icons.arrow_forward);
|
||||||
|
|
||||||
|
const ArrowDirection(this.icon);
|
||||||
|
|
||||||
|
final IconData icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Note {
|
||||||
|
final double time;
|
||||||
|
final ArrowDirection direction;
|
||||||
|
|
||||||
|
const Note({required this.time, required this.direction});
|
||||||
|
}
|
||||||
|
|
||||||
class _LevelState extends State<Level> {
|
class _LevelState extends State<Level> {
|
||||||
final player = AudioPlayer();
|
final player = AudioPlayer();
|
||||||
Simfile? simfile;
|
Simfile? simfile;
|
||||||
|
@ -23,6 +41,8 @@ class _LevelState extends State<Level> {
|
||||||
StreamSubscription? _durationSubscription;
|
StreamSubscription? _durationSubscription;
|
||||||
StreamSubscription? _positionSubscription;
|
StreamSubscription? _positionSubscription;
|
||||||
|
|
||||||
|
List<Note> notes = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setState(VoidCallback fn) {
|
void setState(VoidCallback fn) {
|
||||||
// Subscriptions only can be closed asynchronously,
|
// Subscriptions only can be closed asynchronously,
|
||||||
|
@ -79,6 +99,25 @@ class _LevelState extends State<Level> {
|
||||||
simfile = Simfile(simfilePath);
|
simfile = Simfile(simfilePath);
|
||||||
simfile!.load();
|
simfile!.load();
|
||||||
|
|
||||||
|
double bpm = simfile!.bpms.entries.first.value;
|
||||||
|
|
||||||
|
for (final (measureIndex, measure)
|
||||||
|
in simfile!.chartSimplest!.measures!.indexed) {
|
||||||
|
for (final (noteIndex, noteData) in measure.indexed) {
|
||||||
|
int arrowIndex = noteData.indexOf('1');
|
||||||
|
if (arrowIndex < 0 || arrowIndex > 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
notes.add(Note(
|
||||||
|
time: (measureIndex * 4.0 +
|
||||||
|
(noteIndex.toDouble() / measure.length) * 4.0) *
|
||||||
|
1.0 /
|
||||||
|
bpm +
|
||||||
|
simfile!.offset / 60.0,
|
||||||
|
direction: ArrowDirection.values[arrowIndex]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
print(audioPath);
|
print(audioPath);
|
||||||
|
|
||||||
player.play(DeviceFileSource(audioPath));
|
player.play(DeviceFileSource(audioPath));
|
||||||
|
@ -86,7 +125,6 @@ class _LevelState extends State<Level> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
@ -123,18 +161,16 @@ class _LevelState extends State<Level> {
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
body: Stack(children: [
|
body: Stack(children: [
|
||||||
Arrow(
|
...notes.map((note) {
|
||||||
position: -100.0,
|
return Arrow(
|
||||||
),
|
position: _position != null
|
||||||
Arrow(
|
? (note.time - _position!.inMilliseconds / 60000.0) *
|
||||||
position: 00.0,
|
20 *
|
||||||
),
|
MediaQuery.of(context).size.height
|
||||||
Arrow(
|
: 0.0,
|
||||||
position: 100.0,
|
direction: note.direction,
|
||||||
),
|
);
|
||||||
Arrow(
|
}),
|
||||||
position: 200.0,
|
|
||||||
),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 50,
|
top: 50,
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
|
@ -171,15 +207,16 @@ class _LevelState extends State<Level> {
|
||||||
|
|
||||||
class Arrow extends StatelessWidget {
|
class Arrow extends StatelessWidget {
|
||||||
final double position;
|
final double position;
|
||||||
|
final ArrowDirection direction;
|
||||||
|
|
||||||
const Arrow({super.key, required this.position});
|
const Arrow({super.key, required this.position, required this.direction});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: MediaQuery.of(context).size.width / 2 - 25, // Center the arrow
|
left: MediaQuery.of(context).size.width / 2 - 50, // Center the arrow
|
||||||
top: position,
|
bottom: position + 50,
|
||||||
child: Icon(size: 100, Icons.arrow_forward),
|
child: Icon(size: 100, color: Colors.redAccent.shade400, direction.icon),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:ffi';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
enum Difficulty { Beginner, Easy, Medium, Hard, Challenge, Edit }
|
enum Difficulty { Beginner, Easy, Medium, Hard, Challenge, Edit }
|
||||||
|
@ -42,6 +43,9 @@ class Simfile {
|
||||||
|
|
||||||
Chart? chartSimplest;
|
Chart? chartSimplest;
|
||||||
|
|
||||||
|
Map<double, double> bpms = {};
|
||||||
|
double offset = 0;
|
||||||
|
|
||||||
Simfile(this.path);
|
Simfile(this.path);
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
|
@ -55,6 +59,22 @@ class Simfile {
|
||||||
List<String> keys =
|
List<String> keys =
|
||||||
fieldData[1]!.split(':').map((key) => key.trim()).toList();
|
fieldData[1]!.split(':').map((key) => key.trim()).toList();
|
||||||
String value = fieldData[2]!;
|
String value = fieldData[2]!;
|
||||||
|
if (keys[0] == "BPMS") {
|
||||||
|
for (final pairRaw in value.split(',')) {
|
||||||
|
List<String> pair = pairRaw.split('=');
|
||||||
|
if (pair.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double time = double.parse(pair[0]);
|
||||||
|
double bpm = double.parse(pair[1]);
|
||||||
|
bpms[time] = bpm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys[0] == "OFFSET") {
|
||||||
|
offset = double.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
if (keys[0] != "NOTES") {
|
if (keys[0] != "NOTES") {
|
||||||
tags[keys[0]] = value;
|
tags[keys[0]] = value;
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in a new issue