section_loop 0.1.0
section_loop: ^0.1.0 copied to clipboard
Player-agnostic A-B section looping for audio apps. Loop a named part of a track on any player, with the seek-storm and queue-stall bugs handled.
example/section_loop_example.dart
import 'package:section_loop/section_loop.dart';
/// A stand-in for a real audio player, so the example runs anywhere.
class FakePlayer {
Duration position = Duration.zero;
bool playing = true;
final Duration duration = const Duration(minutes: 3);
Future<void> seek(Duration to) async {
position = to;
print(' seek -> ${to.inSeconds}s');
}
Future<void> pause() async {
playing = false;
print(' pause');
}
Future<void> play() async => playing = true;
}
Future<void> main() async {
final player = FakePlayer();
final engine = SectionLoopEngine(
onSeek: player.seek,
onPause: player.pause,
onPlay: player.play,
)..section = LoopSection(
name: 'Chorus',
start: const Duration(seconds: 48),
end: const Duration(seconds: 52),
);
print('Looping ${engine.section!.name} '
'(${engine.section!.start.inSeconds}s-${engine.section!.end.inSeconds}s)');
// Advance playback a second at a time. After the engine seeks, playback
// continues from the section start, so the loop repeats on its own.
player.position = const Duration(seconds: 50);
for (var tick = 0; tick < 8; tick++) {
print('at ${player.position.inSeconds}s');
engine.handlePosition(player.position, trackDuration: player.duration);
await Future<void>.delayed(Duration.zero); // let the seek settle
player.position += const Duration(seconds: 1);
}
// Sections persist as JSON.
final json = engine.section!.toJson();
print('stored: $json');
print('restored: ${LoopSection.fromJson(json)}');
}