audio_waveforms
audio_waveforms copied to clipboard
Is there any way to pause the current audio while start playing another one?
Similar to the WhatsApp voice note, when we play any new voice note the current gets paused or stopped.
same issues
You can do this by calling pausePlayer
when another audio is played for every other PlayerController but that would be cumbersome to do so putting it under enhancement.
I have solved this problem by applying small hacks using the provider, I checked all the available player controllers and the file path of those controllers, and if the path isn't the same one I'm playing currently then the provider will notify the listeners to pause the other voice note. There are might be better approaches to solve it but this how I solved it for now, you may give it a shot.
I have a chat app - that lazy loads and caches players for each item and keeps track of the currently playing player and ensures only player is playing at any one time.
An abridged version of the code I wrote is here:
Each voice message just calls the voiceController.startOrStopPlayer(path)
on tap gesture. The VoiceMessageController is insantiated at the top level widget which has children for the messages but also message bar.
Note - canCancel is based on whether the user has dragged their finger to the cancel area - which is why it's reset to false when the recording starts - as obviously they're not at the cancel zone.
It seems to work well - not done any profiling on it though
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/foundation.dart';
class VoiceMessageController {
late final RecorderController recorderController;
final ValueNotifier<bool> isRecordingNotifier = ValueNotifier(false);
final ValueNotifier<bool> canCancel = ValueNotifier(false);
Map<String, PlayerController> playerControllers = {};
PlayerController? _currentPlayer;
VoiceMessageController() {
recorderController = RecorderController()
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4
..normalizationFactor = Platform.isAndroid ? 60 : 25;
}
void startOrStopPlayer(String path) async {
_currentPlayer?.pausePlayer();
_currentPlayer = await getPlayerController(path);
_currentPlayer!.startPlayer(finishMode: FinishMode.pause);
}
Future<PlayerController> getPlayerController(String path) async {
if (playerControllers.containsKey(path)) {
return playerControllers[path]!;
}
final playerController = PlayerController();
await playerController.preparePlayer(path, 1);
playerControllers[path] = playerController;
return playerController;
}
void startRecording(String path) async {
canCancel.value = false;
await recorderController.record(path);
isRecordingNotifier.value = true;
}
Future stopRecording({bool cancel = false}) async {
final path = await recorderController.stop(true);
isRecordingNotifier.value = false;
if (cancel) {
if (path != null) {
File(path).delete();
}
return null;
}
return await _onVoidRecordComplete(path!);
}
Future _onVoidRecordComplete(String filePath) async {
// do what you want with the file here - I create a player controller to find duration and return a custom object
}
dispose() {
recorderController.dispose();
playerControllers.forEach((key, value) {
value.dispose();
});
}
}
@codecoded Thank you for the code snippet. And also you can use playerKey
available in PlayerController instead of the path for the key of the playerControllers
map. A playerKey will be unique for each player.
I actually pass in a custom object which has a unique asset id which I use as the key, rather than the path - just this is an abridged snippet.
I guess you could pass in the player key or null if new and also the path - so a quick pseduo revised version for those interested
Future<PlayerController> getPlayerController({ required String path, String playerKey?, double? volume=1}) async {
if (playerKey != null && playerControllers.containsKey(playerKey)) {
return playerControllers[playerKey]!;
}
final playerController = PlayerController();
await playerController.preparePlayer(path, volume);
playerControllers[playerController.playerKey] = playerController;
return playerController;
}
I have a chat app - that lazy loads and caches players for each item and keeps track of the currently playing player and ensures only player is playing at any one time.
An abridged version of the code I wrote is here:
Each voice message just calls the
voiceController.startOrStopPlayer(path)
on tap gesture. The VoiceMessageController is insantiated at the top level widget which has children for the messages but also message bar.Note - canCancel is based on whether the user has dragged their finger to the cancel area - which is why it's reset to false when the recording starts - as obviously they're not at the cancel zone.
It seems to work well - not done any profiling on it though
import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:flutter/foundation.dart'; class VoiceMessageController { late final RecorderController recorderController; final ValueNotifier<bool> isRecordingNotifier = ValueNotifier(false); final ValueNotifier<bool> canCancel = ValueNotifier(false); Map<String, PlayerController> playerControllers = {}; PlayerController? _currentPlayer; VoiceMessageController() { recorderController = RecorderController() ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC ..androidEncoder = AndroidEncoder.aac ..androidOutputFormat = AndroidOutputFormat.mpeg4 ..normalizationFactor = Platform.isAndroid ? 60 : 25; } void startOrStopPlayer(String path) async { _currentPlayer?.pausePlayer(); _currentPlayer = await getPlayerController(path); _currentPlayer!.startPlayer(finishMode: FinishMode.pause); } Future<PlayerController> getPlayerController(String path) async { if (playerControllers.containsKey(path)) { return playerControllers[path]!; } final playerController = PlayerController(); await playerController.preparePlayer(path, 1); playerControllers[path] = playerController; return playerController; } void startRecording(String path) async { canCancel.value = false; await recorderController.record(path); isRecordingNotifier.value = true; } Future stopRecording({bool cancel = false}) async { final path = await recorderController.stop(true); isRecordingNotifier.value = false; if (cancel) { if (path != null) { File(path).delete(); } return null; } return await _onVoidRecordComplete(path!); } Future _onVoidRecordComplete(String filePath) async { // do what you want with the file here - I create a player controller to find duration and return a custom object } dispose() { recorderController.dispose(); playerControllers.forEach((key, value) { value.dispose(); }); } }
I think from this reference it can be easily implemented. We can use controller.playerKey
to distinguish between players and implement function similar to play this and pause other.
Closing this as not planned.