mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-29 12:52:13 +02:00
Ease the lift before each highlight and let it settle more gradually for held words. Keep timed pronunciation and translation lines stationary while preserving their color sweep.
184 lines
5.4 KiB
Dart
184 lines
5.4 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:spotiflac_android/utils/synced_lyrics_scroll.dart';
|
|
|
|
void main() {
|
|
test('symmetric padding lets the final lyric line reach center', () {
|
|
const viewport = 500.0;
|
|
const lineExtent = 64.0;
|
|
const lineCount = 20;
|
|
final padding = syncedLyricsCenterPadding(
|
|
viewportDimension: viewport,
|
|
estimatedLineExtent: lineExtent,
|
|
);
|
|
final contentExtent = (padding * 2) + (lineCount * lineExtent);
|
|
final maxScrollExtent = contentExtent - viewport;
|
|
final lastLineOffset = syncedLyricsEstimatedOffset(
|
|
index: lineCount - 1,
|
|
estimatedLineExtent: lineExtent,
|
|
);
|
|
|
|
expect(padding, 218);
|
|
expect(lastLineOffset, maxScrollExtent);
|
|
});
|
|
|
|
test('small viewports retain minimum breathing room', () {
|
|
expect(
|
|
syncedLyricsCenterPadding(viewportDimension: 80, estimatedLineExtent: 64),
|
|
24,
|
|
);
|
|
});
|
|
|
|
test('advances at the exact next line boundary', () {
|
|
const starts = [
|
|
Duration(seconds: 1),
|
|
Duration(seconds: 3),
|
|
Duration(seconds: 3),
|
|
Duration(seconds: 6),
|
|
];
|
|
|
|
expect(
|
|
syncedLyricsDueLineIndex(
|
|
lineStarts: starts,
|
|
currentIndex: 0,
|
|
position: const Duration(milliseconds: 2999),
|
|
),
|
|
0,
|
|
);
|
|
expect(
|
|
syncedLyricsDueLineIndex(
|
|
lineStarts: starts,
|
|
currentIndex: 0,
|
|
position: const Duration(seconds: 3),
|
|
),
|
|
2,
|
|
);
|
|
});
|
|
|
|
group('smooth timed lyric highlight', () {
|
|
double liftAt(int position, {int end = 1500}) => syncedLyricSegmentLift(
|
|
position: Duration(milliseconds: position),
|
|
start: const Duration(seconds: 1),
|
|
end: Duration(milliseconds: end),
|
|
);
|
|
|
|
test('short words anticipate gently and remain raised after singing', () {
|
|
expect(liftAt(800), 0);
|
|
expect(liftAt(950), inExclusiveRange(0, 1));
|
|
expect(liftAt(1000), greaterThan(liftAt(950)));
|
|
expect(liftAt(1000), lessThan(0.2));
|
|
expect(liftAt(1200), inExclusiveRange(0.4, 0.7));
|
|
expect(liftAt(1500), lessThan(1));
|
|
expect(liftAt(1540), 1);
|
|
expect(liftAt(3000), 1);
|
|
});
|
|
|
|
test('held words rise further then settle to the normal highlight', () {
|
|
final peak = liftAt(2300, end: 4000);
|
|
expect(liftAt(1200, end: 4000), inExclusiveRange(0.4, 0.7));
|
|
expect(peak, greaterThan(1));
|
|
expect(liftAt(3900, end: 4000), inExclusiveRange(1, peak));
|
|
expect(liftAt(4000, end: 4000), 1);
|
|
expect(liftAt(4200, end: 4000), 1);
|
|
expect(liftAt(2300, end: 4000), peak);
|
|
expect(liftAt(800, end: 4000), 0);
|
|
});
|
|
|
|
test('short syllables keep the slow rise and trail their color sweep', () {
|
|
expect(liftAt(1100, end: 1100), lessThan(0.4));
|
|
expect(liftAt(1200, end: 1100), lessThan(0.7));
|
|
expect(liftAt(1540, end: 1100), 1);
|
|
for (var position = 820; position < 1540; position += 16) {
|
|
final before = liftAt(position, end: 1100);
|
|
final after = liftAt(position + 16, end: 1100);
|
|
expect(after - before, inInclusiveRange(0, 0.034));
|
|
}
|
|
});
|
|
|
|
test('invalid or zero-length timing does not move the text', () {
|
|
expect(liftAt(1200, end: 1000), 0);
|
|
expect(liftAt(1200, end: 500), 0);
|
|
});
|
|
|
|
test('interpolates position only while playback is advancing', () {
|
|
expect(
|
|
interpolatedSyncedLyricsPosition(
|
|
anchorPosition: const Duration(seconds: 10),
|
|
elapsedSinceAnchor: const Duration(milliseconds: 250),
|
|
isPlaying: true,
|
|
),
|
|
const Duration(milliseconds: 10250),
|
|
);
|
|
expect(
|
|
interpolatedSyncedLyricsPosition(
|
|
anchorPosition: const Duration(seconds: 10),
|
|
elapsedSinceAnchor: const Duration(milliseconds: 250),
|
|
isPlaying: false,
|
|
),
|
|
const Duration(seconds: 10),
|
|
);
|
|
});
|
|
|
|
test('blends small clock corrections and applies seeks immediately', () {
|
|
expect(
|
|
reconcileSyncedLyricsPosition(
|
|
predictedPosition: const Duration(milliseconds: 1000),
|
|
reportedPosition: const Duration(milliseconds: 1100),
|
|
),
|
|
const Duration(milliseconds: 1025),
|
|
);
|
|
expect(
|
|
reconcileSyncedLyricsPosition(
|
|
predictedPosition: const Duration(seconds: 1),
|
|
reportedPosition: const Duration(seconds: 5),
|
|
),
|
|
const Duration(seconds: 5),
|
|
);
|
|
});
|
|
|
|
test('calculates continuous progress inside a timed segment', () {
|
|
const start = Duration(seconds: 2);
|
|
const end = Duration(seconds: 3);
|
|
|
|
expect(
|
|
syncedLyricSegmentProgress(
|
|
position: const Duration(milliseconds: 1500),
|
|
start: start,
|
|
end: end,
|
|
),
|
|
0,
|
|
);
|
|
expect(
|
|
syncedLyricSegmentProgress(
|
|
position: const Duration(milliseconds: 2250),
|
|
start: start,
|
|
end: end,
|
|
),
|
|
0.25,
|
|
);
|
|
expect(
|
|
syncedLyricSegmentProgress(
|
|
position: const Duration(milliseconds: 3500),
|
|
start: start,
|
|
end: end,
|
|
),
|
|
1,
|
|
);
|
|
});
|
|
|
|
test('moves the reveal boundary from left to right', () {
|
|
expect(
|
|
syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0),
|
|
10,
|
|
);
|
|
expect(
|
|
syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0.5),
|
|
60,
|
|
);
|
|
expect(
|
|
syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 1),
|
|
110,
|
|
);
|
|
});
|
|
});
|
|
}
|