I'm using the just_audio flutter package to play asset audios.
I've made two functions and calling both methods in this way on a button press:
await Future.wait([playChordsAudio(chordsAudioPlayersList, c),playSecondAssetFolderAudios(drumsAudioPlayersList, c)]);
Now, inside both these functions, I'm setting assets and then playing those assets. Here is the implementation of both of my Functions:
Future<void> fillChordsAudioPlayersList(
List<AudioPlayer> chordsAudioPlayersList, Controller c) async {
for (int i = 0; i < c.assetSources.length; i++) {
chordsAudioPlayersList.add(AudioPlayer());
await chordsAudioPlayersList[i].setAsset(c.assetSources[i].toString());
}
}
Future<void> fillDrumsAudioPlayersList(
List<AudioPlayer> drumsAudioPlayersList, Controller c) async {
String path =
'assets/drum_beats/${c.rhythmsFolderSelected}/${c.bpmSliderValue.toInt()}BPM.mp3';
for (int i = 0; i < c.assetSources.length; i++) {
drumsAudioPlayersList.add(AudioPlayer());
await drumsAudioPlayersList[i].setAsset(path);
}
}
Future<void> playChordsAudio(List<AudioPlayer> chordsAudioPlayersList, Controller c) async {
await fillChordsAudioPlayersList(chordsAudioPlayersList, c);
for (int i = 0; i < c.assetSources.length; i++) {
chordsAudioPlayersList[i].play();
await Future.delayed(chordsAudioPlayersList[i].duration! -
const Duration(milliseconds: 100));
}
}
Future<void> playDrumsAudio(List<AudioPlayer> drumsAudioPlayersList, Controller c) async {
await fillDrumsAudioPlayersList(drumsAudioPlayersList, c);
for (int i = 0; i < c.assetSources.length; i++) {
drumsAudioPlayersList[i].play();
await Future.delayed(drumsAudioPlayersList[i].duration! -
const Duration(milliseconds: 100));
}
}
It's working but I want both of them to start playing audio simultaneously. Sometimes they start at same time But often they aren't synced.
While it is not possible to guarantee exact synchronisation, you might be able to get closer by restructuring your code.
You are doing a lot of work in each method before it actually gets around to starting to play. For example, await setAsset()
takes time to load the audio from storage and filling the buffer. You should separate your preparation code from your playback code and sequence things like this:
await prepare1();
await prepare2();
await Future.wait([play1(), play2()]);
where all that play1()
and play2()
do is immediately start playing what had already been prepared earlier.
Even then, you won't be able to get exact precision of spacing between drum beats using a loop that uses await Future.delayed(...)
each time because await
is inherently asynchronous by definition. In other words, the drum beats will end up not being perfectly spaced at 100ms. There will be an unpredictable waiting time before and after that 100ms delay due to the way the async scheduler works. You might want to look into the standard Dart API Timer.periodic(). It still won't guarantee exact 100ms intervals, but it should be "more" synchronised than your current approach.