As a first app in flutter, I want to build a metronome app. The UI is already built, but I still encounter the following problems with the actual metronome functionality:
sometimes, the metronome lags a bit, just enough, so you notice it. Is there a way in flutter to achieve a 100% precision of the metronome?
not changing subdivision while playing (you have to stop and start the metronome). How can the values "tempo" and "subdivision" be automatically applied to the metronome subscription, if they change? I know that Flutter provides tools like Listenable, Stream, InheritedWidget, etc. but I haven’t figured out a way how you can implement these in the existing code.
Acreenshot of the UI:
Here is the code (it's not entirely written by me -> credits):
import 'dart:io' show File;
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:quiver/async.dart';
import 'package:audioplayers/audioplayers.dart' show AudioPlayer;
import 'package:flutter/services.dart' show ByteData, rootBundle;
import 'package:path_provider/path_provider.dart' show getTemporaryDirectory;
//credits: "Andi Qu", https://stackoverflow.com/questions/51048402/flutter-audioplayers-or-metronome-lagging
ValueNotifier<int> tempo = ValueNotifier(100);
int subdivision = 1;
bool isPlaying = false;
int soundIndex = 1;
File _soundFile;
StreamSubscription<DateTime> _subscription;
Future<ByteData> _loadSound() async {
return await rootBundle.load('assets/sounds/sound_$soundIndex.wav');
}
void _writeSound() async {
_soundFile = File(
'${(await getTemporaryDirectory()).path}/sounds/sound_$soundIndex.wav');
await _soundFile.writeAsBytes((await _loadSound()).buffer.asUint8List());
print("_writeSound executed");
}
void _playLocal() async {
final AudioPlayer _audioPlayer = AudioPlayer();
AudioPlayer.logEnabled = false;
await _audioPlayer.play(_soundFile.path, isLocal: true);
}
/// The actual method that plays the metronome
void playpause() {
print("playpause triggered");
if (_soundFile == null) {
print("_soundFile = null ---> Soundfile written");
_writeSound();
}
if (isPlaying) {
_subscription.cancel();
isPlaying = false;
print("metronome stopped");
} else {
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
isPlaying = true;
print("metronome started");
}
}
void increasetempo(int tempochange) {
tempo.value = tempo.value + tempochange;
if (isPlaying) {
_subscription.cancel();
print("_subscription canceled");
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
}
print("tempo changed to ${tempo.value}");
}
void decreasetempo(int tempochange) {
tempo.value = tempo.value - tempochange;
if (isPlaying) {
_subscription.cancel();
print("_subscription canceled");
_subscription = Metronome.periodic(new Duration(
milliseconds: (60000 / (tempo.value * subdivision)).floor()))
.listen((d) => _playLocal());
}
print("tempo changed to ${tempo.value}");
}