bluefireteam / audioplayers

A Flutter package to play multiple audio files simultaneously (Android/iOS/web/Linux/Windows/macOS)
https://pub.dartlang.org/packages/audioplayers
MIT License
2.01k stars 845 forks source link

Audio fade in / fade out functionality? #945

Open Zayah117 opened 3 years ago

Zayah117 commented 3 years ago

I'm looking into this library to implement audio playback for a music app. I'm wondering if audio fade in and fade out is on your roadmap for future features. It would be extremely useful to be able to play a sound with fade in and fade out for volume.

It would be nice if I could pass in a 'fade in' and/or a 'fade-out' duration and volume for any audioplayer. You could, for example, have optional parameters for fading in the constructor:

AudioPlayer audioPlayer = AudioPlayer(
    fadeIn: Fade(0.0, 1.0, 500) // Fade in from 0% volume to 100% volume in 500 milliseconds
    fadeOut: Fade(1.0, 0.0, 500) // Fade out from 100% volume to 0% volume in 500 milliseconds
);

You could also make the fadeIn/fadeOut properties public so we can update the audioplayer on the fly:

audioplayer.fadeIn = Fade(0.0, 1.0, 2000) // Update fade in duration to 2000 milliseconds

This implementation would require defining some sort of 'Fade' class to pass into the audio player.

class Fade {
    double startVolume;
    double endVolume;
    double fadeDurationMilliseconds;

    Fade(this.startVolume, this.endVolume, this.fadeDurationMilliseconds);
}

Platforms I'm aiming for: Android and iOS, + web support if possible.

There is a small audio library on pub.dev called Howler that seems to implement audio fades: https://pub.dev/packages/howler

I have not tested this yet however.

alexrintt commented 3 years ago

I did implement a helper class that applies Fade to an AudioPlayer

Since was a quicky implementation, it not fill all usecases, but you can change anything to fill your needs. I did use the Howler Fade Custom implementation for the Web as base for this implementation, so it can help you if you'll change something.

Tooltip: you can convert it to Dart extension syntax instead a class if you want.

Helper class
```dart import 'dart:async'; import 'dart:math'; /// Remeber to import this library `audioplayers` /// Add transition feature to a given [player] by [fadeObservable] and [fade] class VolumeTransition { final AudioPlayer player; const VolumeTransition(this.player); /// Custom Howler Fade /// [audio transition implementation](https://github.com/gmpassos/howler.dart/blob/master/lib/src/howler_base.dart#L1691) /// /// Stream based fade Stream fadeObservable( {required Duration duration, required double from, required double to}) async* { final length = duration.inMilliseconds; final diff = to - from; final steps = (diff / 0.01).abs(); final period = max(4, (steps > 0) ? length ~/ steps : length); var lastTick = DateTime.now().millisecondsSinceEpoch; var volume = from; double computation(int computationCount) { if ((to < from && volume <= to) || (to > from && volume >= to)) { return double.nan; } final now = DateTime.now().millisecondsSinceEpoch; final tick = (now - lastTick) / length; lastTick = now; volume += diff * tick; if (diff < 0) { volume = max(to, volume); } else { volume = min(to, volume); } volume = (volume * 100).round() / 100; return volume; } final ticker = Stream.periodic(Duration(milliseconds: period), computation); await for (final volume in ticker) { final effectiveVolume = volume.isNaN ? to : volume; await player.setVolume(effectiveVolume); yield volume; if (volume.isNaN) break; } } /// Future based fade that uses [fadeObservable] with a [Completer] Future fade( {required Duration duration, required double to, required double from}) { final completer = Completer(); late StreamSubscription subscription; void listener(double volume) { if (volume.isNaN) { completer.complete(); subscription.cancel(); } } subscription = fadeObservable(duration: duration, to: to, from: from).listen(listener); return completer.future; } } ```
Usage
```dart /// Any AudioPlayer instance AudioPlayer player; final transition = VolumeTransition(player); /// Future based await transition.fade(from: 0, to: 1, duration: Duration(seconds: 5)); // Will complete when transition ends /// Stream based final stream = transition.fadeObservable(from: 0, to: 1, duration: Duration(seconds: 5)); stream.listen((volume) { if (volume.isNaN) print('Transition ended'); else print('Volume level: $volume'); }); ```

Anyway, thanks for opening this issue and sharing the Howler package.