You can use a [VolumeShaper](https://developer.android.com/reference/android/media/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](https://developer.android.com/reference/android/media/AudioTrack) or [MediaPlayer](https://developer.android.com/reference/android/media/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](https://developer.android.com/reference/android/media/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()](https://developer.android.com/reference/android/media/VolumeShaper.Configuration#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](https://developer.android.com/reference/android/media/VolumeShaper.Configuration). Do this using a
`VolumeShaper.Configuration.Builder()`:  

### Kotlin

```kotlin
val config: VolumeShaper.Configuration = VolumeShaper.Configuration.Builder()
        .setDuration(3000)
        .setCurve(floatArrayOf(0f, 1f), floatArrayOf(0f, 1f))
        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
        .build()
```

### Java

```java
VolumeShaper.Configuration config =
  new VolumeShaper.Configuration.Builder()
      .setDuration(3000)
      .setCurve(new float[] {0.f, 1.f}, new float[] {0.f, 1.f})
      .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
      .build();
```

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:  

### Kotlin

```kotlin
volumeShaper = myMediaPlayer.createVolumeShaper(config)
volumeShaper = myAudioTrack.createVolumeShaper(config)
```

### Java

```java
volumeShaper = myMediaPlayer.createVolumeShaper(config);
volumeShaper = myAudioTrack.createVolumeShaper(config);
```

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:  

### Kotlin

```kotlin
shaper.apply(VolumeShaper.Operation.PLAY)
```

### Java

```java
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:  

### Kotlin

```kotlin
val newConfig = VolumeShaper.Configuration.Builder()
        .setDuration(1000)
        .setCurve(floatArrayOf(0f, 0.5f), floatArrayOf(0f, 1f))
        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
        .build()
val join = true
shaper.replace(newConfig, VolumeShaper.Operation.PLAY, join)
```

### Java

```java
VolumeShaper.Configuration newConfig =
  new VolumeShaper.Configuration.Builder()
    .setDuration(1000)
    .setCurve(new float[] {0.f, 0.5f}, new float[] {0.f, 1.f})
    .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
    .build();
boolean join = true;
shaper.replace(newConfig, VolumeShaper.Operation.PLAY, join);
```

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.