Skip to content

Commit

Permalink
fix: add controller and remove audio players
Browse files Browse the repository at this point in the history
  • Loading branch information
AmirJabbari committed Dec 5, 2023
1 parent 4bc8de0 commit ad1361b
Show file tree
Hide file tree
Showing 7 changed files with 864 additions and 378 deletions.
14 changes: 10 additions & 4 deletions example/lib/widgets/bubble.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:sizer/sizer.dart';
import 'package:voice_message_package/voice_message_package.dart';
Expand Down Expand Up @@ -37,8 +35,16 @@ class Bubble extends StatelessWidget {

Widget _bubble(BuildContext context) => voice
? VoiceMessage(
audioSrc: 'https://sounds-mp3.com/mp3/0012660.mp3',
me: index == 5 ? false : true,
controller: VoiceController(
id: '1',
audioSrc:
'https://dl.musicdel.ir/Music/1400/08/morteza_pashaei_setayesh%20128.mp3',
maxDuration: Duration(seconds: 30),
isFile: false,
onComplete: (String id) {},
onPause: (String id) {},
onPlaying: (String id) {},
),
)
: Container(
constraints: BoxConstraints(maxWidth: 100.w * .7),
Expand Down
22 changes: 22 additions & 0 deletions lib/src/helpers/play_status.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
enum PlayStatus { init, playing, pause, stop, downloading, downloadError }

enum PlaySpeed { x1, x1_25, x1_5, x1_75, x2, x2_25 }

extension GetSpeed on PlaySpeed {
double get getSpeed {
switch (this) {
case PlaySpeed.x1:
return 1.0;
case PlaySpeed.x1_25:
return 1.25;
case PlaySpeed.x1_5:
return 1.50;
case PlaySpeed.x1_75:
return 1.75;
case PlaySpeed.x2:
return 2.00;
case PlaySpeed.x2_25:
return 2.25;
}
}
}
258 changes: 258 additions & 0 deletions lib/src/voice_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import 'dart:async';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:just_audio/just_audio.dart';
import 'package:voice_message_package/src/helpers/play_status.dart';
import 'package:voice_message_package/src/helpers/utils.dart';

class VoiceController extends MyTicker {
final String audioSrc;
late Duration maxDuration;
Duration currentDuration = Duration.zero;
final Function(String id) onComplete;
final Function(String id) onPlaying;
final Function(String id) onPause;
final double noiseWidth = 50.5.w();
final String id;
late AnimationController animController;
final AudioPlayer _player = AudioPlayer();
final bool isFile;
PlayStatus playStatus = PlayStatus.init;
PlaySpeed speed = PlaySpeed.x1;
ValueNotifier updater = ValueNotifier(null);
final randoms = <double>[];
StreamSubscription? positionStream;
StreamSubscription? playerStateStream;

double get currentMillSeconds {
final c = currentDuration.inMilliseconds.toDouble();
if (c >= maxMillSeconds) {
return maxMillSeconds;
}
return c;
}

bool isSeeking = false;

bool get isPlaying => playStatus == PlayStatus.playing;

bool get isInit => playStatus == PlayStatus.init;

bool get isDownloading => playStatus == PlayStatus.downloading;

bool get isDownloadError => playStatus == PlayStatus.downloadError;

bool get isStop => playStatus == PlayStatus.stop;

bool get isPause => playStatus == PlayStatus.pause;

double get maxMillSeconds => maxDuration.inMilliseconds.toDouble();

VoiceController({
required this.id,
required this.audioSrc,
required this.maxDuration,
required this.isFile,
required this.onComplete,
required this.onPause,
required this.onPlaying,
}) {
_setRandoms();
animController = AnimationController(
vsync: this,
upperBound: noiseWidth,
duration: maxDuration,
);
_listenToRemindingTime();
_listenToPlayerState();
// animController.addListener(() {
// print("value is "+animController.value.toString());
// });
}

Future initAndPlay() async {
playStatus = PlayStatus.downloading;
_updateUi();
try {
await setMaxDuration(audioSrc);
await startPlaying(audioSrc);

onPlaying(id);
} catch (err) {
playStatus = PlayStatus.downloadError;
_updateUi();
rethrow;
}
}

void _listenToRemindingTime() {
positionStream = _player.positionStream.listen((Duration p) async {
currentDuration = p;
final value = (noiseWidth * currentMillSeconds) / maxMillSeconds;
animController.value = value;
_updateUi();
if (p.inMilliseconds >= maxMillSeconds) {
await _player.stop();
currentDuration = Duration.zero;
playStatus = PlayStatus.init;
animController.reset();
_updateUi();
onComplete(id);
}
});
}

void _updateUi() {
updater.notifyListeners();
}

Future stopPlaying() async {
_player.pause();
playStatus = PlayStatus.stop;
}

Future startPlaying(String path) async {
Uri audioUri = isFile ? Uri.file(audioSrc) : Uri.parse(audioSrc);
await _player.setAudioSource(
AudioSource.uri(audioUri),
initialPosition: currentDuration,
);
_player.play();
_player.setSpeed(speed.getSpeed);
}

Future<void> dispose() async {
await _player.dispose();
positionStream?.cancel();
playerStateStream?.cancel();
animController.dispose();
}

void onSeek(Duration duration) {
isSeeking = false;
currentDuration = duration;
_updateUi();
_player.seek(duration);
}

void pausePlaying() {
_player.pause();
playStatus = PlayStatus.pause;
_updateUi();
onPause(id);
}

void _listenToPlayerState() {
playerStateStream = _player.playerStateStream.listen((event) async {
if (event.processingState == ProcessingState.completed) {
// await _player.stop();
// currentDuration = Duration.zero;
// playStatus = PlayStatus.init;
// animController.reset();
// _updateUi();
// onComplete(id);
} else if (event.playing) {
playStatus = PlayStatus.playing;
_updateUi();
}
});
}

String get playSpeedStr {
switch (speed) {
case PlaySpeed.x1:
return "1.00x";
case PlaySpeed.x1_25:
return "1.25x";
case PlaySpeed.x1_5:
return "1.50x";
case PlaySpeed.x1_75:
return "1.75x";
case PlaySpeed.x2:
return "2.00x";
case PlaySpeed.x2_25:
return "2.25x";
}
}

void changeSpeed() {
switch (speed) {
case PlaySpeed.x1:
speed = PlaySpeed.x1_25;
break;
case PlaySpeed.x1_25:
speed = PlaySpeed.x1_5;
break;
case PlaySpeed.x1_5:
speed = PlaySpeed.x1_75;
break;
case PlaySpeed.x1_75:
speed = PlaySpeed.x2;
break;
case PlaySpeed.x2:
speed = PlaySpeed.x2_25;
break;
case PlaySpeed.x2_25:
speed = PlaySpeed.x1;
break;
}
_player.setSpeed(speed.getSpeed);
_updateUi();
}

void onChangeSliderStart(double value) {
isSeeking = true;
pausePlaying();
}

void _setRandoms() {
for (var i = 0; i < 50; i++) {
randoms.add(5.74.w() * Random().nextDouble() + .26.w());
}
}

void onChanging(double d) {
currentDuration = Duration(milliseconds: d.toInt());
final value = (noiseWidth * d) / maxMillSeconds;
animController.value = value;
_updateUi();
}

String get remindingTime {
if (currentDuration == Duration.zero) {
return maxDuration.formattedTime;
}
if (isSeeking) {
return currentDuration.formattedTime;
}
if (isPause || isInit) {
return maxDuration.formattedTime;
}
return currentDuration.formattedTime;
}

Future setMaxDuration(String path) async {
try {
final maxDuration =
isFile ? await _player.setFilePath(path) : await _player.setUrl(path);
if (maxDuration != null) {
this.maxDuration = maxDuration;
animController.duration = maxDuration;
}
} catch (err) {
if (kDebugMode) {
print("cant get the max duration from the path $path");
}
}
}
}

class MyTicker extends TickerProvider {
@override
Ticker createTicker(TickerCallback onTick) {
return Ticker(onTick);
}
}
Loading

0 comments on commit ad1361b

Please sign in to comment.