Audio_service: Support For Multi-Player

Created on 18 Sep 2020  路  7Comments  路  Source: ryanheise/audio_service

Is your feature request related to a problem? Please describe.
My use-case is a primary vocal track with a backing musical composition where the players (implemented in my case using just_audio) can be controlled independently.

Describe the solution you'd like
It would be nice to tag an 'id' for each service call so that players can be controlled independently of one another, so one might call:
AudioService.play(int playerId)
This variable could be defaulted (say to 0) so that the current API isn't broken. Background task methods such as onPlay(), onPause() etc. would be handed the argument (could also be optional so as not to break the current API) so that they can distinguish between players.

Describe alternatives you've considered
I've implemented my (rather hacky) solution by abandoning the use of the standard audio_service calls, implementing them all with:
AudioService.customAction(), for the following actions:
play(), pause(), seekTo(), setVolume(), setSpeed(), setLooping() etc., in addition to actions to "create" and "dispose" the players. One additional action "setFocus" sets the focus of the particular player that I want targeted by background controls (this can be wildcarded in my case).

Ditto for the return path
I've also implemented several custom events to take care of the return path to the foreground task, namely for "completed" and "position" information.

Additional context

1 backlog enhancement

All 7 comments

This is an interesting feature, and there might be a way to natively support this on the Android side since it is possible to have multiple media sessions in one service. However, I'm not sure if this is supported on iOS. That's not to say we couldn't have a feature that's only supported on Android. Although I'd be interested to know if you know a way to support this on iOS?

It's simple enough to just implement it the way you have been doing it with custom actions, but what I'm referring to here is the ability to have the remote controls in the notifications / control center to link to these multiple media sessions. My understanding is that there can only be one active "now playing" at a time, while in Android you can have multiple of them.

The problem with tying a group of players (or channels within one player) to the notifications/control center is that there are controls that might not make sense in this context.

Take the use case of a vocal track that governs the "duration", and a background track that loops indefinitely (perhaps at half volume) until the vocal completes. IOS includes a seek-bar, and in its case, which track would this apply to? In my case, I'd like it to be:
Vocal - governs duration for seek bar - seek bar seeks to position in the track
Background - loops and is shorter in duration than vocal - seek bar seeks to a postion governed by Vocal, modulo the length of the Background track
But other folks would surely like something different.

There are also all kinds of other variants, and when one starts to muse about variables like speed, volume, etc. it becomes even more complex. For example, does setVolume() set the absolute volume for all of the channels, or should it (in the case of 0-1.0) multiply with the volume setting for each channel so that the channel volumes remain consistent in terms of relativity to one another (this would also presume that volume settings are linear and not, say, log scale).......

In my implementation, operations like pause() and play() affect all tracks, whereas operations like seek() apply to my "focused" track only (the vocal in my case).

Quite a bit to consider.

In terms of your IOS question, I'm not aware of IOS implementing audio mixing like with Media/Exo-player, but I have to admit, it's been a while since I looked.

Hopefully this ramble makes sense!

I think custom actions is the right solution to this.

The main issue with using AudioService.play and other of these "standard" protocol methods is that these methods are tied to standard protocols on Android and iOS for allowing remote control of background audio from notifications and the control center, or headset buttons or Android auto, etc. So AudioService.play should be viewed as the callback for a standard operation that you can invoke from one of these standardised clients. Then, anything non-standard would be defined as custom actions.

One thing that I was thinking of recently was to write up a document conveying guidelines on how to extend audio_service with additional features. Specifically, in this case what you could do is provide some extension methods on AudioService that add non-standard media controls in a more convenient way, so that you could do something like AudioService.playOn(int playerId). I haven't fully thought about the other end of things, i.e. how to make it more convenient to define custom callbacks in BackgroundAudioTask but the solution may involve using build_runner.

I also want to have separate volume controls for multiple audio tracks playing the same time in background mode. Are there any updates on this?

This is an old issue that can probably be closed. @ProgrammingLife, I'm not sure that audio_service is at issue here for your use case.

We currently run up to 12 audio channels (i.e. up to 12 just_audio players) with independent volume controls (The app is the Fuzing: Relax, De-Stress and Sleep Well, in case you want to see it in action - go to the sound tab and mess with one of the mixes to see what we're doing from an end user perspective).

At the end of a "mix" we gradually fade to silence by adjusting volumes of individual players using a multiplier function - simplified as follows:

Let's say we have 3 tracks (simply_audio players) playing:
Track 1 -> just_audio player 1 -> Vocal -> volume=1.0
Track 2 -> just_audio player 2 -> Nature sound 1 - River -> volume=0.75
Track 3 -> just_audio player 3 -> Nature sound 2 - Birds -> volume=0.6

Once we start to "fade the mix" we use a master variable that decays from 1.0 to 0.0 as a multiplier to set the volumes of the individual players. For example, where our "master volume" is set to 0.7......
Track 1 -> just_audio player 1 -> Vocal -> volume=1.0 * 0.7 = 0.7
Track 2 -> just_audio player 2 -> Nature sound 1 - River -> volume=0.75 * 0.7 = 0.525
Track 3 -> just_audio player 3 -> Nature sound 2 - Birds -> volume=0.6 * 0.7 = 0.42

(again, this is a simplification of the function we use - ours is non-linear - we use a bezier function to interpolate actual values).

I'm happy to provide more info if you need assistance,

Peter B.

The app is the Fuzing: Relax, De-Stress and Sleep Well, in case you want to see it in action - go to the sound tab and mess with one of the mixes to see what we're doing from an end user perspective

Nice app! I was curious myself about how many decoder instances you could create on Android.

@ProgrammingLife for the audio_service side of things, any additional methods on top of the provided standard protocol methods can be added as custom actions. I have written an example in the FAQ:

https://github.com/ryanheise/audio_service/wiki/FAQ#how-do-i-send-a-custom-action

A custom action consists of an action name and arguments where the arguments any data structure built out of maps, lists, strings, ints, doubles and bools. I would recommend passing a map for the arguments since in a future version of audio_service it will be required to be a map (this is to make it compatible with a similar feature on Android for sending custom actions). So you could design a custom action like this:

await AudioService.customAction('setTrackVolume', {'track': 2, 'volume': 0.5});

And handle it like this:

```dart
@override
Future onCustomAction(String name, dynamic arguments) async {
if (name == 'setTrackVolume') {
_players[arguments['track'].setVolume(arguments['volume']);
}
}

Closing this issue based on the custom actions recommendation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lain-ke picture lain-ke  路  6Comments

pblinux picture pblinux  路  5Comments

mohammadne picture mohammadne  路  7Comments

getmmg picture getmmg  路  3Comments

MahdiPishguy picture MahdiPishguy  路  5Comments