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}");
}
Try to use a library called flutter_sequencer, it helped me to create Metronome without lagging, while any other solution and library didn't work.
https://github.com/mikeperri/flutter_sequencer
https://pub.dev/packages/flutter_sequencer
Related
I am working on a basic audio streaming app. Everything is working fine Play/Pause , Skip the song... The only problem that i have is to go forward in a Song.
My goal is to have a button to skip 10seconds of the music.
Here is the class AudioManager that i am using :
class AudioManager {
late AudioPlayer _audioPlayer;
String lastBeatType = "";
Data data = Data(id: "",channelTitle: "",title: "",link:"");
AudioManager({required this.lastBeatType});
void dispose() {
_audioPlayer.dispose();
}
void initAudio() async {
_audioPlayer = AudioPlayer();
}
void play() {
_audioPlayer.play();
}
void pause() {
_audioPlayer.pause();
}
void goForward() async{
await _audioPlayer.seek(const Duration(seconds: _audioPlayer.position.inSeconds + 10) );
}
void goBackward() {
_audioPlayer.seek(Duration(seconds: _audioPlayer.position.inSeconds - 10));
}
//Function getting the buffers of the songs
getBeat(String type) async {
print("Entering Get Beat");
lastBeatType = type;
AudioManager _audioManager = AudioManager(lastBeatType: type);
final response =
await http.get(Uri.parse("http://localhost:5000/beat?type=$type"));
print("Response has been getted");
if (response.statusCode == 200) {
final songdata = jsonDecode(response.body);
_audioPlayer.setUrl("http://localhost:5000/audio?videoId=${songdata["id"]}");
}
return response.bodyBytes;
}
The function goForward/goBackward are the one that i am using.
But when i press the button here is the error i have
Client returned a buffer it does not own according to our record: 0
Any help would be very appreciate !
I have been searching for long to know the best approach to listen to internet connection in flutter/dart app. I think this approach is better for now and it can be of help to some like me who has been searching. I have used many connectivity plugins, but it didn't work. I have equally used data_connection_checker, lookUpAddress etc as suggested by many but to no avail. But below helped.
Use the below plugins to check or listen to Internet Connection / Network Connectivity in dart, flutter app.
connectivity_plus
internet_connection_checker
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
class ConnectionUtil {
static final ConnectionUtil _singleton = new ConnectionUtil._internal();
ConnectionUtil._internal();
static ConnectionUtil getInstance() => _singleton;
bool hasConnection = false;
StreamController connectionChangeController = StreamController();
final Connectivity _connectivity = Connectivity();
void initialize() {
_connectivity.onConnectivityChanged.listen(_connectionChange);
}
void _connectionChange(ConnectivityResult result) {
_hasInternetInternetConnection();
}
Stream get connectionChange => connectionChangeController.stream;
Future<bool> _hasInternetInternetConnection() async {
bool previousConnection = hasConnection;
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi) {
// this is the different
if (await InternetConnectionChecker().hasConnection) {
hasConnection = true;
} else {
hasConnection = false;
}
} else {
hasConnection = false;
}
if (previousConnection != hasConnection) {
connectionChangeController.add(hasConnection);
}
return hasConnection;
}
}
Implement this code on the stateful widget.....
bool hasInterNetConnection = false;
#override
initState() {
ConnectionUtil connectionStatus = ConnectionUtil.getInstance();
connectionStatus.initialize();
connectionStatus.connectionChange.listen(connectionChanged);
super.initState();
}
void connectionChanged(dynamic hasConnection) {
setState(() {
hasInterNetConnection = hasConnection;
});
}
Good luck
I had faced a similar problem a few weeks ago. This is a good approach. The internet_connection_checker plugin allows one to address issues at the network layer that the connectivity_plus plugin cannot address. I have carried out an implementation of these two plugins using the bloc library. For more information and code refer to this Stackoverflow post and this Github issue.
I used the pedometer package version ^1.2.0 in my flutter app and I noticed that it doesn't work with some devices like Samsung A30 and others, the version I used doesn't need any permissions and it worked on my device OPPO F7 (Used Android 10) so what is the problem?
Here's the code
StreamSubscription<int> _subscription;
Box<int> stepsBox = Hive.box('steps');
int todaySteps;
#override
void initState() {
super.initState();
startListening();
}
#override
void dispose() {
stopListening();
super.dispose();
}
void startListening() {
_pedometer = Pedometer();
_subscription = _pedometer.pedometerStream.listen(
getTodaySteps,
onError: _onError,
onDone: _onDone,
cancelOnError: true,
);
}
void _onDone() => print("Finished pedometer tracking");
void _onError(error) => print("Flutter Pedometer Error: $error");
Future<int> getTodaySteps(int value) async {
print(value);
int savedStepsCountKey = 999999;
int savedStepsCount = stepsBox.get(savedStepsCountKey, defaultValue: 0);
int todayDayNo = Jiffy(DateTime.now()).dayOfYear;
if (value < savedStepsCount) {
// Upon device reboot, pedometer resets. When this happens, the saved counter must be reset as well.
savedStepsCount = 0;
// persist this value using a package of your choice here
stepsBox.put(savedStepsCountKey, savedStepsCount);
}
// load the last day saved using a package of your choice here
int lastDaySavedKey = 888888;
int lastDaySaved = stepsBox.get(lastDaySavedKey, defaultValue: 0);
// When the day changes, reset the daily steps count
// and Update the last day saved as the day changes.
if (lastDaySaved < todayDayNo) {
lastDaySaved = todayDayNo;
savedStepsCount = value;
stepsBox
..put(lastDaySavedKey, lastDaySaved)
..put(savedStepsCountKey, savedStepsCount);
}
setState(() {
todaySteps = value - savedStepsCount;
});
stepsBox.put(todayDayNo, todaySteps);
return todaySteps; // this is your daily steps value.
}
void stopListening() {
_subscription.cancel();
}
check for permission.
await Permission.activityRecognition.request().isGranted
this will check for permission and request permission if the permission is not allowed
if (await Permission.activityRecognition.request().isGranted) {
_pedestrianStatusStream = Pedometer.pedestrianStatusStream;
_pedestrianStatusStream
.listen(onPedestrianStatusChanged)
.onError(onPedestrianStatusError);
_stepCountStream = Pedometer.stepCountStream;
_stepCountStream.listen(onStepCount).onError(onStepCountError);
}else{
}
if (!mounted) return;
I think that in this case it would be better to ask the question directly in the lib repository, open an issue with your problem.
https://github.com/cph-cachet/flutter-plugins/issues
I'm working on switching library but my interface's getDuration return Duration and I can't change this due to meny references.
So I have to return Duration instead of Future<Duration> in AudioPlayer class.
Is there any way to do it?
Here is executable code that explains what I want to do.
import 'dart:core';
import 'dart:async';
// This interface is hardly to modify becase this is referenced from many place.
abstract class IAudioPlayer {
Duration getDuration();
}
// This is old library that returns `Duration`
class OldLibraryAudioPlayer {
Duration getDuration() {
return Duration(seconds: 1);
}
}
// This is new library that returns `Future<Duration>`
class NewLibraryAudioPlayer {
Future<Duration> getDuration() async {
var completer = new Completer<Duration>();
new Timer(new Duration(seconds: 1),
() => completer.complete(Duration(seconds: 1)));
return completer.future;
}
}
class AudioPlayer implements IAudioPlayer {
// OldLibraryAudioPlayer _player = OldLibraryAudioPlayer();
NewLibraryAudioPlayer _player = NewLibraryAudioPlayer();
#override
Duration getDuration() {
// I want to return Duration instead of Future<Duration>.
return _player.getDuration();
}
}
void main() {
final player = AudioPlayer();
final duration = player.getDuration();
print('$duration');
}
You need to convert the function to async or directly return the result.
Example:
Future<Duration> getDuration() async {
final duration = await player.getDuration();
return duration;
}
or
Future<Duration> getDuration() {
return player.getDuration();
}
Hope that helps!
If you want to return Duration:
Future<Duration> getDuration() async{
final duration = await player.getDuration(); // await here
return duration; // return your duration
}
If you want to return Future<Duration>:
Future<Duration> getDuration() {
return player.getDuration(); // return Future<Duration>
}
I am making a metronome app in Flutter. I used the AudioPlayers plugin along with
the Metronome class from the quiver.async library. Here is the part of the code that I used to achieve this:
import 'dart:async' show Future, StreamSubscription;
import 'dart:io' show File;
import 'package:flutter/services.dart' show ByteData, rootBundle;
import 'package:path_provider/path_provider.dart' show getTemporaryDirectory;
import 'package:audioplayers/audioplayer.dart' show AudioPlayer;
import 'package:quiver/async.dart' show Metronome;
...
File _soundFile;
/// List of possible sounds
List<String> _sounds = ['bottle', 'click', 'tamborine'];
int _soundIndex = 0;
/// Tempo of the metronome
static int tempo = 100;
bool _isPlaying = false;
Metronome _metronome = Metronome.periodic(Duration(milliseconds: (60000 / tempo).floor()));
StreamSubscription<DateTime> _subscription;
Future<ByteData> _loadSound() async {
return await rootBundle.load('assets/${_sounds[_soundIndex]}.mp3');
}
void _writeSound() async {
_soundFile = File(
'${(await getTemporaryDirectory()).path}/${_sounds[_soundIndex]}.mp3');
await _soundFile.writeAsBytes((await _loadSound()).buffer.asUint8List());
}
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 playMetronome() {
if (_soundFile == null) {
_writeSound();
}
setState(() {
if (_isPlaying) {
_subscription.cancel();
_isPlaying = false;
} else {
_subscription = _metronome.listen((d) => _playLocal());
_isPlaying = true;
}
});
}
Sometimes though, the metronome lags just a bit, but more than enough to be noticeable. I can't tell if it's because of the AudioPlayer or the Metronome. How can I fix this?
(The AudioPlayers plugin that I'm using is this one)