Audio becomes choppy when in the background
ntatko opened this issue · 4 comments
I'm running soundpool: ^2.2.0
on flutter 2.8.1.
WidgetsBinding.instance?.addObserver(this);
is used to keep the event loop running in the background. I'm using a set of recursive timers that call one another to make the sound play, in the foreground and the background.
Untitled.mp4
Looking for some insight. Lemme know what I can do to make this stop, eh?
@ntatko, can you provide a minimal example showing the problem? I don't have idea on how to reproduce the issue.
Yeah, here's a simple widget that would have this effect - simple being a relative word. My implementation is a bit more complex, but this should hit the main points.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:soundpool/soundpool.dart';
import 'package:flutter/services.dart';
class BreatheBar extends StatefulWidget {
const BreatheBar({Key? key}) : super(key: key);
@override
_BreathBarState createState() => _BreathBarState();
}
class _BreathBarState extends State<BreatheBar> with WidgetsBindingObserver {
Timer? backgroundTimer;
late Map<String, Map<String, int>> soundIds;
Soundpool pool = Soundpool.fromOptions(
options: const SoundpoolOptions(
streamType: StreamType.alarm,
iosOptions: SoundpoolOptionsIos(
audioSessionCategory: AudioSessionCategory.ambient)));
void waitingPlaySound() {
setState(() {
backgroundTimer = Timer(const Duration(seconds: 5), () {
playSound(pool, soundIds);
waitingPlaySound();
});
});
}
@override
void initState() {
loadSounds(pool).then((Map<String, Map<String, int>> codes) {
soundIds = codes;
}).then((_) {
waitingPlaySound();
});
WidgetsBinding.instance?.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(30),
child: const Text("Playing a sound every 5 seconds"));
}
}
Map<String, Map<String, String>> sounds = {
'Notify': {
'inhale': 'assets/sounds/notify_inhale.mp3',
'exhale': 'assets/sounds/notify_exhale.mp3'
}
};
Future<Map<String, Map<String, int>>> loadSounds(Soundpool pool) async {
Map<String, Map<String, int>> loadedSounds = {};
sounds.forEach((key, value) async {
int inId =
await rootBundle.load(value['inhale']!).then((ByteData soundData) {
return pool.load(soundData);
});
int outId =
await rootBundle.load(value['exhale']!).then((ByteData soundData) {
return pool.load(soundData);
});
loadedSounds[key] = {'inhale': inId, 'exhale': outId};
});
return loadedSounds;
}
void playSound(Soundpool pool, Map<String, Map<String, int>> loaded) {
pool.play(loaded['Notify']!['inhale']!);
}
Thank you for the sample.
Unfortunately I could not reproduce the problem using the code you provided. I tried both on simulator and real device. I noticed that on real device (iPhone SE 2020) the sound was silenced when app was moved to the background unless I had changed the audioSessionCategory
to AudioSessionCategory.playback
.
Can you reproduce the problem with this sample?
How long are sounds in your application? I wonder if they may overlap somehow and do this "mixed" sound.
PS. I suggest to change the recursive call chain with Timer.periodic()
factory method. I won't promise it would fix anything but it may simplify your code.
I've prepared a sample app using your example. Source code.