The app plays some background audio (wav) in an endless loop. Audio playback was flawless using just_audio package, but after switching to audioplayers, all looped audio now contain a short gap of silence before they restart at the beginning. Question now is: How to get rid of the gap?
The code to load and play the files is quite simple:
Future<void> loadAmbient() async {
_audioAmbient = AudioPlayer();
await _audioAmbient!.setSource(AssetSource('audio/ambient.wav'));
await _audioAmbient!.setReleaseMode(ReleaseMode.loop);
}
void playAmbient() async {
if (PlayerState.playing != _audioAmbient?.state) {
await _audioAmbient?.seek(Duration.zero);
_audioAmbient?.resume();
}
}
According to the audioplayers docs, this seems to be a known issue:
The Getting Started docs state:
Note: there are caveats when looping audio without gaps. Depending on the file format and platform, when audioplayers uses the native implementation of the "looping" feature, there will be gaps between plays, witch might not be noticeable for non-continuous SFX but will definitely be noticeable for looping songs. Please check out the Gapless Loop section on our Troubleshooting Guide for more details.
Following the link to the [Trouble Shooting Guide] (https://github.com/bluefireteam/audioplayers/blob/main/troubleshooting.md) reveals:
Gapless Looping
Depending on the file format and platform, when audioplayers uses the native implementation of the "looping" feature, there will be gaps between plays, witch might not be noticeable for non-continuous SFX but will definitely be noticeable for looping songs.
TODO(luan): break down alternatives here, low latency mode, audio pool, gapless_audioplayer, ocarina, etc
Interesting is the Depending on the file format and platform, ... part, which sounds as if gap-less loops could somehow be doable. Question is, how?
Any ideas how to get audio to loop flawless (i.e. without gap) are very much appreciated. Thank you.
You can pass this parameter
_audioAmbient = AudioPlayer(mode: PlayerMode.LOW_LATENCY);
if not solved u can try something new like https://pub.dev/packages/ocarina
I fixed it this way:
Created 2 players, set asset path to both of them and created the method that starts second player before first is completed
import 'package:just_audio/just_audio.dart' as audio;
audio.AudioPlayer player1 = audio.AudioPlayer();
audio.AudioPlayer player2 = audio.AudioPlayer();
bool isFirstPlayerActive = true;
StreamSubscription? subscription;
audio.AudioPlayer getActivePlayer() {
return isFirstPlayerActive ? player1 : player2;
}
void setPlayerAsset(String asset) async {
if (isFirstPlayerActive) {
await player1.setAsset("assets/$asset");
await player2.setAsset("assets/$asset");
} else {
await player2.setAsset("assets/$asset");
await player1.setAsset("assets/$asset");
}
subscription?.cancel();
_loopPlayer(getActivePlayer(), isFirstPlayerActive ? player2 : player1);
}
audio.AudioPlayer getActivePlayer() {
return isFirstPlayerActive ? player1 : player2;
}
void _loopPlayer(audio.AudioPlayer player1, audio.AudioPlayer player2) async {
Duration? duration = await player1.durationFuture;
subscription = player1.positionStream.listen((event) {
if (duration != null &&
event.inMilliseconds >= duration.inMilliseconds - 110) {
log('finished');
player2.play();
isFirstPlayerActive = !isFirstPlayerActive;
StreamSubscription? tempSubscription;
tempSubscription = player1.playerStateStream.listen((event) {
if (event.processingState == audio.ProcessingState.completed) {
log('completed');
player1.seek(const Duration(seconds: 0));
player1.pause();
tempSubscription?.cancel();
}
});
subscription?.cancel();
_loopPlayer(player2, player1);
}
});
}
Related
Below code shows how i am using audioplayers without audio service
playFromFile(List textList, seek) async {
for (int i = seek; i < textList.length; i++) {
setState(() {
newSeek = i;
});
itemScrollController.scrollTo(
index: i,
duration: const Duration(seconds: 2),
curve: Curves.easeInOutCubic);
String text = textList[i].join(' ');
final bytes = await getAudio(text);
await audioPlugin.playBytes(bytes);
while (audioPlugin.state == PlayerState.PLAYING && !playFresh) {
await Future.delayed(const Duration(seconds: 1));
if (audioPlugin.state == PlayerState.PLAYING) {
audioPlugin.onPlayerCompletion.listen((onDone) async {
audioPlugin.state = PlayerState.COMPLETED;
await audioPlugin.release();
});
}
if (audioPlugin.state == PlayerState.COMPLETED) {
await audioPlugin.release();
break;
}
}
if (playFresh) break;
}
}
I want to implement this code using audio service so i can play audio in background . How to implement this with audio service so it will play in background. Please help as i am new to flutter and unable to solve this for days.
Solution will be quite simple.
You implement this using different layer.
UI Layer - This will be your UI from where you going to click the play button.
Intermediate layer or Audio service Layer(Audio service class) - From your UI layer you going to called this audio service class play method.
Final layer (Actual audio player methods) - This will be final layer where your actual audio package mehtods resides. You called this layer from your intermediate layer.
Summary - UI layer play button -> Audio service Play method -> Audio player Play method
guys, I'm building a music app in flutter and I have a problem which I don't know how to resolve, so if someone can help me out on this.
So this is the thing I have songs that are stored on CloudFront and they work perfectly
play(song, context) async {
int result = await audioPlayer.play(song); //path to the song
setState(() {
isPlaying = true;
});
// int seek = await audioPlayer.seek(Duration(minutes: 10, seconds: 10));
if (result == 1) {
return 'succes';
}
}
but when I give another path from podbean it will not start playing "https://mcdn.podbean.com/mf/play/9i7qz8/Japji_Sahib_Pauri_3.mp3" -> this is the link you can play it in browser
Error message. Shows after some time on the initial play of the podcast
Any idea what I can do about this problem.
Thanks in advance.
How does Media.release() work. Looking at the docs it feels that you have to use it like this:
MediaService.loadMedia('sounds/connection-error.wav').then(function(media){
media.play();
media.release();
});
But I googled enough to know that is wrong. We have to explicitly release the core instances on Android.
But how to do that? If I have 8 views in my app and if I play a sound file on each of those views does that count as 8 core instances being used? And can I go back to say view number 1 and again play the sound associated with that view? If so, would that count as a 9th instances ?
Straight away calling media.release() just like above does not play any sound at all.
Most common way to play sounds using Cordova Media plugin is following:
function playAudio(src) {
// HTML5 Audio
if (typeof Audio != "undefined") {
new Audio(src).play() ;
// Phonegap media
} else if (typeof device != "undefined") {
// Android needs the search path explicitly specified
if (device.platform == 'Android') {
src = '/android_asset/www/' + src;
}
var mediaRes = new Media(src,
function onSuccess() {
// release the media resource once finished playing
mediaRes.release();
},
function onError(e){
console.log("error playing sound: " + JSON.stringify(e));
});
mediaRes.play();
} else {
console.log("no sound API to play: " + src);
}
}
I am a complete newcomer to as3, having started about 3 months ago as part of a university project, so apologies if I am asking an obvious question or I provide the wrong information.
I am creating a game where a 'monster' from an array collides with a static movie clip which the user has to avoid by killing the monster before they get there.
When the monster reaches the movie clip, I want it to play a 'munch' sound once, then not play it again for a few seconds, but the code as it is means that the sound plays over and over again while the array item and movie clip collide, which does not sound right.
I tried to correct this by adding a timer which allows the sound to continue for half a second and then stops the sound channel, but this does not seem to have worked.
How can I make the sound play once, not play again for the next few seconds and then play again if the objects are still colliding?
Thanks in advance.
Heres my code:
var sc: SoundChannel;
var munch: Sound = new Sound(new URLRequest("audio/munch.mp3"));
var muteTest: Boolean = false;
var munchStop:Timer = new Timer (500, 1);
munchStop.addEventListener(TimerEvent.TIMER, afterMunchStop);
function afterMunchStop(event: TimerEvent): void {
sc.stop();
}
if (monster1Array[i].hitTestObject(centre_mc)) {
if (muteTest == false) {
sc = munch.play();
munchStop.start();
}
var sc: SoundChannel;
var munch: Sound = new Sound(new URLRequest("audio/munch.mp3"));
var soundIsPlaying: Boolean = false;
//sound should loop until objects no longer collide
var munchStop:Timer = new Timer(500);
munchStop.addEventListener(TimerEvent.TIMER, afterMunchStop);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//play sound if monsters collide and sound is not already playing
if (monster1Array[i].hitTestObject(centre_mc) && !soundIsPlaying)
{
sc = munch.play();
soundIsPlaying = true;
//start the timer if it's not running
if(!munchStop.running)
munchStop.start();
} else {
//stop the sound if it's playing
if(sc)
sc.stop();
soundIsPlaying = false;
//reset the timer when the objects are no longer colliding
if(munchStop.running)
munchStop.reset();
}
}
function afterMunchStop(event: TimerEvent): void {
soundIsPlaying = false;
}
I need to play the "correct" sound if user answer the question correctly and "wrong" if incorrect. The sound is playing at first but after at the fifth time I answer correctly which output the fourth time "correct" sound already the fifth time, no sound can be heard. It seems the music can only played at most 4th times.
What is the possible reason that cause this?
updated*
I added media.release in my stopAudio() but so far the sound can be heard for 6th (improved) but it is still not suits my case. (Many questions that need the same sound effect)
Js.file
// Audio player
var my_media = null;
var mediaTimer = null;
// Play audio
function playAudio(src) {
// Create Media object from src
my_media = new Media(src, null, soundCB);
my_media.play();
}
function soundCB(err){
console.log("playAudio():Audio Error: "+err);
}
// Stop audio
//
function stopAudio() {
if (my_media) {
alert("AQA");
my_media.stop();
my_media.release();
}
clearInterval(mediaTimer);
mediaTimer = null;
}
/////question part//////
$("#answer").live('tap', function(event){
if(buttonAns==true) {
$this= $(this);
buttonAns = false;
var choice = $this.attr("class");
if((correctAnsNo == 0 && choice =="answer0")||(correctAnsNo == 1 && choice =="answer1")||(correctAnsNo == 2 && choice =="answer2")){
playAudio('/android_asset/www/audio/fall.mp3'); //falling sound
correct = parseInt(correct)+2;
playAudio('/android_asset/www/audio/correct.mp3'); //dingdong
}else{
playAudio('/android_asset/www/audio/wrong1.wav');
}
if(quesNo < wordsNo ){
setTimeout(function() {
buttonAns = true;
db.transaction(queryQuesDB, errorCB);
},1800);
}else{
endLevel();
}
} else {
event.preventDefault();
}
});
Android has a finite amount of resources in order to play sounds. You keep creating a new Media object each time you want to play a file. You need to release the sounds you are not using. If you need to play those sounds multiple times each then consider creating separate media objects to hold a reference to those sounds.
Read up on media.release.
I had the same problem.
The solution I did.:
if(media){
media.stop();
media.release();
}
media = new Media(...);
Here's my trick: I released it upon it finished playing or an error occurred.
function playAudio(url) {
try {
var my_media = new Media(url,
// success callback
function () {
**my_media.release();**
},
// error callback
function (err) {
**my_media.release();**
});
// Play audio
my_media.play();
} catch (e) {
alert(e.message);
}
}