SoundPlayer

Overview

The SoundPlayer class provide an API for playing audio.

The SoundPlayer provides detailed control over audio playback (pause/resume/seek) including monitoring playback progress.

The SoundPlayer operates in one of two modes:

Type

Constructor

Description

Headless

SoundPlayer.noUI()

audio is played back with no UI. You need to build your own UI to control playback.

OS Shade

SoundPlayer.withShadeUI()

the OS' built in media player (often referred to as the Shade or Notification Area) is displayed allowing audio playback to be controlled. Using the Shade allows the user to control the audio even if their phone is locked.

A SoundPlayer can be reused to play mulitiple audio tracks. This is particularly useful if you are using the SoundPlayer.withShadeUI() constructor as it means you can transition from one Track to the next track without the OS' media player 'flickering' as you change tracks.

Alternatives

If you simply want to play an audio file from start to end without any controls (e.g. play a beep) the you should consider using QuickPlay.

If you need a Widget to allow the user to control playback then use SoundPlayerUI.noUI().

If you need to play audio and allow the user to control the audio via the OS' Shade (the OS' built in audio player) then use SoundPlayerUI.withShadeUI().

Supported Media

Sounds only support audio media formats (codec) that the OS supports natively. If you need to play audio from a non-native source then you need to trans-code the media first.

See MediaFormat for additional details on supported formats.

Headless Playback (no UI)

The SoundPlayer uses the Track class as the source of audio.

To playback a headless Track (i.e. without a UI)

var track = Track.fromAsset('assets/billyrock.acc');
var player = SoundPlayer.noUI();
player.onStopped = ({wasUser}) => player.release();
player.play(track);

Its important to call player.release() when you have finished with the playback otherwise you can leave the playback hardware locked and unavailable to other apps.

OS Shade (using the OS' media UI)

Both Android and iOS allow you to play audio via OS' own media player.

The OS media player is displayed on the lock screen and/or the notification area sometimes referred to as a Shade.

Sounds allows you to play a Track using the OS' Shade.

var player = SoundPlayer.withShadeUI();
player.onStopped = ({wasUser}) => player.release();
player.play(Track.fromFile('sample.blit'));
var player = SoundPlayer.withShadeUI();
player.onStopped = ({wasUser}) => player.release();
player.play(Track.fromFile('sample.blit'));

Avoiding flicker

When using the OS' Media Player (via SoundPlayer.withShadeUI()) and you transition between tracks make certain you use the same instance of the SoundPlayer.

If you create a new instance of the SoundPlayer for each track then the OS' Media Player will flicker between tracks.

In this case you won't want to call player.release() when the track stops playing. Rather you need to track the player yourself and call release once you have stopped playing tracks.

This will often mean that you should call player.release() in your widgets dispose() method.

SoundPlayer player;

@override
void initState()
{
    super.initState();
    player = SoundPlayer.withShadeUI();
    // called when the user taps the skip forward button on the OS' Media Player
    player.onSkipForward = onSkipForward;
}

/// some 
void onSkipForward()
{
   var track = getNextTrack();
    player.play(track);
}

@override
void dispose()
{
    player.release();
    super.dispose();
}

Play in background

If you use the OS' Shade then you can instruct Sounds to play the audio in the background.

Normally when playing audio, if your application is switched to the background then your audio will stop. If you pass the playInBackground flag then your audio will continue playing even whilst your app is in the background.

var player = SoundPlayer.withShadeUI(playInBackground:true);
player.onStopped = ({wasUser}) => player.release();
player.play(Track.fromFile('sample.blit'));

Control the OSs' UI

The OSs' media player has three buttons, skip forward, skip backwards and pause. By default the skip buttons are disabled and the pause button enabled.

You can modify the the state of these buttons with the SoundPlayer.withShadeUI constructor.

var player = SoundPlayer.withShadeUI(canPause:true, canSkipBackward:false
    , canSkipForward: true);
player.onStopped =  ({wasUser}) => player.release();
player.play(Track.fromFile('sample.blit'));

Display artist details

You can also have the OS' audio player display the artist details by specifying properties on a Track.

var track = Track.fromFile('sample.aac');
track.title = 'Reckless';
track.artist = 'Sounds';
track.albumArtUrl = 'http://some image url';

var player = SoundPlayer.withShadeUI()
player.onStopped = ({wasUser}) => player.release();
player.fromTrack(track);

The title, artist and album art will be displayed on the OSs' Audio Player.

Focus

Sounds allows you to control the Audio Focus during playback.

Audio Focus allows you to control how your playback interacts with any other app which is also doing playback.

The app that currently has the 'Audio Focus' is considered to be the primary audio which normally equates to 'this one should be the loudest'. The different focus modes dictate how the volume of any other audio being played will be affected.

AudioFocus supports the following Modes:

Mode

Description

focusAndKeepOthers

request focus and allow other audio to continue playing at their current volume.

focusAndStopOthers

request focus and stop other audio playing

focusAndHushOthers

request focus and reduce the volume of other players. In the Android world this is know as 'Duck Others' (but that is a really stupid name :).

abandonFocus

relinquish the audio focus. If another app takes the focus we will now be treated as the 'other' app.

Each of the Sounds media players will automatically request the focus using AudioFocus.focusAndHushOthers.

You can explicitly request and abandon focus as you play audio:

player.requestFocus(); // Get the audio focus
player.play(track);
// wait
player.play(track2);
player.abandonFocus(); // Release the audio focus

Seek player

When using the SoundPlayer you can seek to a specific position in the audio stream before or whilst playing the audio.

await player.seekTo(Duration(seconds: 1));

Setting volume.

The volume is a value between 0.0 and 1.0. The volume defaults to 1.0.

Note: this method is under review and may be moved to an argument on the play method. Currently, volume can only be changed when the player is running. You must ensure that the play method has completed before calling setVolume.

var player = SoundPlayer.noUI();
await player.play(fileUri);
player.setVolume(0.1);

Monitoring

Sounds uses Dart streams to allow you to monitor both recording and playback progress.

If you are playing from a URL (Track.fromURL) then the progress will also reflect the download progress.

The PlaybackDisposition class contains the following fields:

  • state - indicates the playback state.

  • progress - when the state is loading indicates the loading progress using a value from 0.0 to 1.0. 1.0 indicating that loading has completed. When loading from a URL source we may not be able to show progress in which case the value will remain at 0.0 for the entire download. At the end of the download the progress will be set to 1.0.

  • position - when state is playing then indicates the playback position as a time offset from the start of the audio.

  • duration - the duration of the audio. Reflects the duration of the audio being played.

You can use a StreamBuilder which will greatly simplify the construction of UI components (or you can use one of the new Sounds UI widgets).

To obtain a subscription to the SoundPlayer stream:

var Stream<PlaybackDisposition> = SoundPlayer.noUI().dispositionStream(interval);

The result is a stream of PlaybackDispositions which includes both the audio's duration (length) and current position.

System pauses

Sounds aims to be a good citizen and play nicely with other media players. To this end Sounds listens to OS application pause/resume events. This essentially means that if you application is sent to the background then Sounds will stop playback and release any audio resources that it is holding.

When you app is brought back to the foreground Sounds will automatically resume playback from where the audio left off.

Cleanup

Once you have finished using the SoundPlayer you MUST call the release method to release any audio resources held by the player.

You MUST also ensure that the player has been released when your widget is detached from the ui.

You will often do this in a widgets dispose method:

@override
void dispose() {
    player.release();
    super.dispose();
}

Last updated