Playing two sound tracks at the time [just audio] - flutter

I'm trying to play two sound tracks at the time, the first one work as a BGM and the other works as a SFX
What happened now is the BGM is playing and when a SFX sound start to play the BGM is stopped and then starts from the beginning.
I want both sounds to be played without stopping, I searched the docs and I couldn't found a clear answer to this.

You can use asset_audio_player to open multiple audio instances.
///play 3 songs in parallel
AssetsAudioPlayer.newPlayer().open(
Audio.asset("assets/audios/song1.mp3")
);
AssetsAudioPlayer.newPlayer().open(
Audio.asset("assets/audios/song2.mp3")
);
//another way, with create, open, play & dispose the player on finish
AssetsAudioPlayer.playAndForget(
Audio.asset("assets/audios/song3.mp3")
);
You can read the documentation fore more info.

You can refer to this question for a more comprehensive answer, but you can simply create two players, one to play the BGM and one to play the SFX:
final bgmPlayer = AudioPlayer();
final sfxPlayer = AudioPlayer();
await bgmPlayer.setAsset('music.mp3');
await sfxPlayer.setAsset('effect.mp3');
bgmPlayer.play(); // start playing background music
// now you can play the sound effect any time you want
// while the BGM continues to play:
await sfxPlayer.play();
await sfxPlayer.play();
await sfxPlayer.play();

Related

Сompletion playlist and resetting index in just_audio

I'm using the just_audio package, I have a playlist, and after playing all the tracks, the current index remains equal to the index of the last track. Is there a way to make the index equal to 0 when the playlist ends?
final myPlayList = ConcatenatingAudioSource(
children: listAudios,
);
await _player.setAudioSource(myPlayList, initialIndex: 0, preload: false);
Listen for the completed state and seek to index 0. Assuming you also want to stop playing at that point, call pause() too:
_player.processingStateStream.listen((state) async {
if (state == ProcessingState.completed) {
await _player.pause();
await _player.seek(Duration.zero, index: 0);
}
});
Regarding the call to pause(): just_audio state is a composite of playing and processingState and you will only hear audio when playing=true, processingState=ready which is what you have during normal playback. The playing state only ever changes when you tell it to change, but the processingState may change independently depending on what is happening in the audio.
When reaching the end of the playlist, it will transition to playing=true, processingState=completed, hence no audio. If you seek back to the beginning, it will be playing=true, processingState=ready so you'll hear the audio automatically start back up. This may surprise you, but think of it as comparable to what happens in a YouTube video after it completes, but if you seek to the beginning it continues playing (because it's now ready, not completed).
If you want it to stop playing, you need to actually set playing=false with either pause() or stop(), where the former keeps things in memory, and the latter releases all resources.
For more information about the state model and its rationale, see The State Model section of the README.

how to play specific url audio from Audio List

i am new with using just_audio plugin and i have list of url that i set them into Player like following
AudioPlayer _audioPlayer;
_audioPlayer = AudioPlayer();
_audioPlayer
.setAudioSource(ConcatenatingAudioSource(children: [
AudioSource.uri(Uri.parse(
"https://archive.org/download/IGM-V7/IGM%20-%20Vol.%207/25%20Diablo%20-%20Tristram%20%28Blizzard%29.mp3")),
AudioSource.uri(Uri.parse(
"https://archive.org/download/igm-v8_202101/IGM%20-%20Vol.%208/15%20Pokemon%20Red%20-%20Cerulean%20City%20%28Game%20Freak%29.mp3")),
AudioSource.uri(Uri.parse(
"https://scummbar.com/mi2/MI1-CD/01%20-%20Opening%20Themes%20-%20Introduction.mp3")),
]))
Now How can i use _audioPlayer.play()with one specific url
in other word i need to tell him play the url that has index 3
how implement this , thanks
It seems that the purpose of ConcatenatingAudioSource is to play the children one after the other (or randomly with the shuffle options), as if it was one source. The doc doesn't seem to mention seeking to a specific index. I think you can achieve what you want just by re-setting the audio source every time, no ?

Silence between tracks in just_audio

I want to have a variable length pause between tracks in a playlist created with a just_audio AudioPlayer instance (there is a background track which I want playing during this interval). Something to the effect of:
_voiceAudioPlayer.currentIndexStream.listen((event) {
_voiceAudioPlayer.pause();
Future.delayed(const Duration(seconds: 4), () => _voiceAudioPlayer.play());
});
This throws an error:
"Unhandled Exception: Bad state: Cannot fire new event. Controller is already firing an event"
Is there a clean way to do this? I'm considering adding silent mp3s at every other track in the playlist, but feel there there ought to be a better way.
This error happens because currentIndexStream is a "sync" broadcast stream, so you can't trigger another state change event while the current event is being processed (i.e. in the same cycle of the event loop). But you can get around that by scheduling a microtask to happen after the current cycle:
_voiceAudioPlayer.currentIndexStream.listen((index) {
scheduleMicrotask(() async {
_voiceAudioPlayer.pause();
await Future.delayed(Duration(seconds: 4));
_voiceAudioPlayer.play();
});
});
Still, I wouldn't depend on this callback being executed soon enough due to the gapless nature of just_audio's playlists. That is, the next audio track will begin playing immediately, so you're bound to hear at least a fraction of the next item's audio before the pause happens.
There is an open feature request for a SilenceAudioSource which could be inserted into a playlist (you can vote for that issue by clicking the thumbs up button if you'd like it to be implemented.) A silent audio file which you proposed is actually the simplest alternative to SilenceAudioSource.
Otherwise, another approach would be to not use the gapless playlists feature at all (since you don't need the gapless feature anyway), and just implement your own logic to advance the queue:
final queue = [source1, source2, source3, ...];
for (var source in queue) {
await _voiceAudioPlayer.setAudioSource(source);
await _voiceAudioPlayer.play();
await Future.delayed(seconds: 4);
}
The above example does not handle pause/resume logic, but it is just to show that it is possible for you to take the playlist logic into your own hands if you don't require the gapless feature.

Flutter record front facing camera at exact same time as playing video

I've been playing around with Flutter and trying to get it so I can record the front facing camera (using the camera plugin [https://pub.dev/packages/camera]) as well as playing a video to the user (using the video_player plugin [https://pub.dev/packages/video_player]).
Next I use ffmpeg to horizontally stack the videos together. This all works fine but when I play back the final output there is a slight delay when listening to the audio. I'm calling Future.wait([cameraController.startVideoRecording(filePath), videoController.play()]); but there is a slight delay in these tasks actually starting. I don't even need them to fire at the exact same time (which I'm realising is probably impossible), instead if I knew exactly when each of the tasks begun then I can use the time difference to sync the audio using ffmpeg or similar.
I've tried adding a listener on the videoController to see when isPlaying first returns true, and also watching the output directory for when the recorded video appears on the filesystem:
listener = () {
if (videoController.value.isPlaying) {
isPlaying = DateTime.now().microsecondsSinceEpoch;
log('isPlaying '+isPlaying.toString());
}
videoController.removeListener(listener);
};
videoController.addListener(listener);
var watcher = DirectoryWatcher('${extDir.path}/');
watcher.events.listen((event) {
if (event.type == ChangeType.ADD) {
fileAdded = DateTime.now().microsecondsSinceEpoch;
log('added '+fileAdded.toString());
}
});
Then likewise for checking if the camera is recording:
var listener;
listener = () {
if (cameraController.value.isRecordingVideo) {
log('isRecordingVideo '+DateTime.now().microsecondsSinceEpoch.toString());
//cameraController.removeListener(listener);
}
};
cameraController.addListener(listener);
This results in (for example) the following order and microseconds for each event:
is playing: 1606478994797247
is recording: 1606478995492889 (695642 microseconds later)
added: 1606478995839676 (346787 microseconds later)
However, when I play back the video the syncing is off by approx 0.152 seconds so doesn't marry up to the time differences reported above.
Does anyone have any idea how I could accomplish near perfect syncing when combining 2 videos? Thanks.

Synchronizing Playback of Multiple Audio Files in Audio Kit

I am developing a small audio sequencer application using AudioKit. I only need to play back 4 channels of audio. However I need to play them back perfectly synchronized down to the sample level. When I run a test using just two audio files, I can hear that they are not synchronized. The difference is only a few samples, but even a one sample discrepancy would be a problem. I am currently using multiple AKClipPlayer objects routed to an AKMixer object. I called him with the basics for loop like this:
private var clipPlayers : [AKClipPlayer] = []
func play(){
for player in clipPlayers{
player.play()
}
}
Is sample accurate playback timing of multiple audio files possible using AudioKit?
Yes, you need to schedule playback to start in the future with play(at:).
// This can take longer than expected, so do this before choosing a future time
clipPlayers.forEach { $0.prepare(withFrameCount: 10_000) }
let nearFuture = AVAudioTime.now() + 0.2
clipPlayers.forEach { $0.play(at: nearFuture) }