Controlling amplitude with VolumeShaper

You can use a VolumeShaper in an audio app to perform
fade-ins, fade-outs, cross fades, ducking, and other short automated volume
transitions. The VolumeShaper class is available in Android 8.0 (API level 26)
and later.

You create a VolumeShaper by calling createVolumeShaper() on an instance of
AudioTrack or MediaPlayer. The
VolumeShaper only acts on the audio produced by the AudioTrack or MediaPlayer
that created it.

Note: On this page the term "shaper" refers to an instance of a VolumeShaper.

VolumeShaper.Configuration

The behavior of a VolumeShaper is defined by its
VolumeShaper.Configuration. The configuration specifies a
*volume curve, interpolator type, and duration.*

Volume curve

The volume curve represents amplitude change over time. It is defined by a pair
of float arrays, x[] and y[] that define a series of control points. Each (x, y)
pair represents time and volume respectively. The arrays must be of equal length
and contain at least 2 and no more than 16 values. (The maximum curve length is
defined in getMaximumCurvePoints().)

The time coordinates are given over the interval [0.0, 1.0]. The first time
point must be 0.0, the last must be 1.0, and the times must be monotonically
increasing.

The volume coordinates are specified in linear scale over the interval
[0.0, 1.0].

Interpolator type

The volume curve always passes through the specified control points. Values
between the control points are derived by a spline according to the
configuration's interpolator type. There are four constants for the available
VolumeShaper interpolator types:

VolumeShaper.Configuration.INTERPOLATOR_TYPE_STEP

VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR

VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC

VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC_MONOTONIC

Duration

The specified time coordinates in the interval [0.0, 1.0] are scaled to a
duration that you specify in milliseconds. This determines the actual length in
time of the volume curve when the shaper is running and applying the curve to
the audio output.

Using a VolumeShaper

Creating a configuration

Before building a VolumeShaper, you must create an instance of VolumeShaper.Configuration. Do this using a
VolumeShaper.Configuration.Builder():

With no arguments the VolumeShaper.Configuration.Builder constructor returns a
builder that creates a configuration with default settings:
INTERPOLATOR_TYPE_CUBIC, a one second duration, and no curve. You must add a
curve to the builder before calling build().

The framework provides constants for configurations with pre-built curves, each
with one second duration:

VolumeShaper.Configuration.LINEAR_RAMP

VolumeShaper.Configuration.CUBIC_RAMP

VolumeShaper.Configuration.SINE_RAMP

VolumeShaper.Configuration.SCURVE_RAMP

Creating a VolumeShaper

To create a VolumeShaper, call createVolumeShaper() on an instance of the
appropriate class, passing in a VolumeShaper.Configuration:

A single track or media player can have many shapers attached to it, and you can
control each shaper separately. The outputs of all the shapers on a track or
player are multiplied together. A VolumeShaper cannot be shared between
AudioTracks or MediaPlayers, but you can use the same configuration in calls
to createVolumeShaper to build identical shapers on multiple AudioTracks or
MediaPlayers.

When you create the shaper, its first control point (at t = 0) is applied to the
audio stream. If the initial volume is not 1.0 and your app is playing material
at create time, your audio might have an abrupt change in volume. Best practice
is to start playing audio from silence and use a VolumeShaper to implement a
fade-in when playback starts. Create a VolumeShaper that starts at 0 volume
and fades up. For example:

setCurve(new float[] {0.f, 1.f}, new float[] {0.f, 1.f})

Start playback and the shaper at the same time. This ensures that playback
begins from silence and the volume ramps up to full volume. This is explained in
the next section.

Running a VolumeShaper

Though the volume level of the first control point is applied to the audio path
as soon as the shaper is created, the shaper does not progress along the curve
until you call the apply() method with VolumeShaper.Operation.PLAY. After
creating the shaper, the first invocation of apply() must specify the PLAY
operation in order to start the shaper. This runs the curve from its first to
last control points:

shaper.apply(VolumeShaper.Operation.PLAY);

While the shaper is running you can issue alternating apply() calls specifying
REVERSE and PLAY operations. This changes the direction of readout of the
control points each time.

The shaper continuously adjusts the volume and passes through all control points
until it expires. This happens when the shaper reaches the last (for PLAY
operation) or first (for REVERSE operation) control point in the curve.

When the shaper expires, the volume remains at the last setting, which may be
the first or the last control point. You can call VolumeShaper.getVolume() for
the current volume level at any time.

After the shaper expires, you can issue another apply() call to run the curve
in the opposite direction. For example, if the shaper expired while running
PLAY, the next apply() must be REVERSE. Calling PLAY after PLAY has
expired, or REVERSE after REVERSE has expired has no effect.

You must alternate PLAY and REVERSE operations. There is no way to play a
curve from its first to last control points and then restart it again from the
first control point. You can use the replace() method, described in the next
section, to replace the curve with a copy of itself. This resets the shaper,
requiring the PLAY operation to start it again.

Changing the curve

Use the replace() method to change a VolumeShaper's curve. This method takes
a configuration, an operation, and a join parameter. You can call the
replace() method at any time, while the shaper is running or after it expires:

When you call replace() while the shaper is running, it stops changing the
volume and remains at its current value. Then the shaper tries to start the new
curve from the first control point. This means that the operation argument
controls whether or not the shaper runs after the call. Specify PLAY to
immediately start the new curve, specify REVERSE to leave the shaper paused at
the volume of the first control point in the new curve. You can start the shaper
later with apply(VolumeShaper.Operation.PLAY).

When you call replace() with join = false, the shaper starts its curve at
the level specified by its first control point. This can cause a discontinuity
in the volume. You can avoid this by calling replace() with join = true.
This sets the first control point of the new curve to the current level of the
shaper and scales the volume of all the control points between the first and
last to maintain the relative shape of the new curve (the last control point is
unchanged). The scaling operation permanently changes the control points in the
shaper's new curve.

Removing a VolumeShaper

The system closes and garbage collects a VolumeShaper when its AudioTrack or
MediaPlayer is released or no longer in use. You can call the close() method
on a shaper to destroy it immediately. The system removes the shaper from the
audio pipeline within about 20 ms. Be careful when closing a VolumeShaper
while audio is playing. If the shaper has a volume less than 1.0 when you call
close(), the shaper's volume scale changes to 1.0. This can cause a sudden
increase in volume.