Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b33c0eb0 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "VolumeShaper: Enable xOffset from Java" into oc-dev

parents 91f674ee 3c0f5d29
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -31,9 +31,9 @@ public interface VolumeAutomation {
     * @param configuration the {@link VolumeShaper.Configuration configuration}
     * @param configuration the {@link VolumeShaper.Configuration configuration}
     *        that specifies the curve and duration to use.
     *        that specifies the curve and duration to use.
     * @return a {@code VolumeShaper} object
     * @return a {@code VolumeShaper} object
     * @throws IllegalArgumentException if the configuration is not allowed by the player.
     * @throws IllegalArgumentException if the {@code configuration} is not allowed by the player.
     * @throws IllegalStateException if too many VolumeShapers are requested or the state of
     * @throws IllegalStateException if too many {@code VolumeShaper}s are requested
     * the player does not permit its creation (e.g. player is released).
     *         or the state of the player does not permit its creation (e.g. player is released).
     */
     */
    public @NonNull VolumeShaper createVolumeShaper(
    public @NonNull VolumeShaper createVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration);
            @NonNull VolumeShaper.Configuration configuration);
+122 −33
Original line number Original line Diff line number Diff line
@@ -31,8 +31,16 @@ import java.util.Objects;
/**
/**
 * The {@code VolumeShaper} class is used to automatically control audio volume during media
 * The {@code VolumeShaper} class is used to automatically control audio volume during media
 * playback, allowing simple implementation of transition effects and ducking.
 * playback, allowing simple implementation of transition effects and ducking.
 * It is created from implementations of {@code VolumeAutomation},
 * such as {@code MediaPlayer} and {@code AudioTrack} (referred to as "players" below),
 * by {@link MediaPlayer#createVolumeShaper} or {@link AudioTrack#createVolumeShaper}.
 *
 *
 * The {@link VolumeShaper} appears as an additional scaling on the audio output,
 * A {@code VolumeShaper} is intended for short volume changes.
 * If the audio output sink changes during
 * a {@code VolumeShaper} transition, the precise curve position may be lost, and the
 * {@code VolumeShaper} may advance to the end of the curve for the new audio output sink.
 *
 * The {@code VolumeShaper} appears as an additional scaling on the audio output,
 * and adjusts independently of track or stream volume controls.
 * and adjusts independently of track or stream volume controls.
 */
 */
public final class VolumeShaper implements AutoCloseable {
public final class VolumeShaper implements AutoCloseable {
@@ -52,7 +60,19 @@ public final class VolumeShaper implements AutoCloseable {


    /**
    /**
     * Applies the {@link VolumeShaper.Operation} to the {@code VolumeShaper}.
     * Applies the {@link VolumeShaper.Operation} to the {@code VolumeShaper}.
     *
     * Applying {@link VolumeShaper.Operation#PLAY} after {@code PLAY}
     * or {@link VolumeShaper.Operation#REVERSE} after
     * {@code REVERSE} has no effect.
     *
     * Applying {@link VolumeShaper.Operation#PLAY} when the player
     * hasn't started will synchronously start the {@code VolumeShaper} when
     * playback begins.
     *
     * @param operation the {@code operation} to apply.
     * @param operation the {@code operation} to apply.
     * @throws IllegalStateException if the player is uninitialized or if there
     *         is a critical failure. In that case, the {@code VolumeShaper} should be
     *         recreated.
     */
     */
    public void apply(@NonNull Operation operation) {
    public void apply(@NonNull Operation operation) {
        /* void */ applyPlayer(new VolumeShaper.Configuration(mId), operation);
        /* void */ applyPlayer(new VolumeShaper.Configuration(mId), operation);
@@ -65,11 +85,24 @@ public final class VolumeShaper implements AutoCloseable {
     * This allows the user to change the volume shape
     * This allows the user to change the volume shape
     * while the existing {@code VolumeShaper} is in effect.
     * while the existing {@code VolumeShaper} is in effect.
     *
     *
     * The effect of {@code replace()} is similar to an atomic close of
     * the existing {@code VolumeShaper} and creation of a new {@code VolumeShaper}.
     *
     * If the {@code operation} is {@link VolumeShaper.Operation#PLAY} then the
     * new curve starts immediately.
     *
     * If the {@code operation} is
     * {@link VolumeShaper.Operation#REVERSE}, then the new curve will
     * be delayed until {@code PLAY} is applied.
     *
     * @param configuration the new {@code configuration} to use.
     * @param configuration the new {@code configuration} to use.
     * @param operation the operation to apply to the {@code VolumeShaper}
     * @param operation the {@code operation} to apply to the {@code VolumeShaper}
     * @param join if true, match the start volume of the
     * @param join if true, match the start volume of the
     *             new {@code configuration} to the current volume of the existing
     *             new {@code configuration} to the current volume of the existing
     *             {@code VolumeShaper}, to avoid discontinuity.
     *             {@code VolumeShaper}, to avoid discontinuity.
     * @throws IllegalStateException if the player is uninitialized or if there
     *         is a critical failure. In that case, the {@code VolumeShaper} should be
     *         recreated.
     */
     */
    public void replace(
    public void replace(
            @NonNull Configuration configuration, @NonNull Operation operation, boolean join) {
            @NonNull Configuration configuration, @NonNull Operation operation, boolean join) {
@@ -81,7 +114,14 @@ public final class VolumeShaper implements AutoCloseable {
    /**
    /**
     * Returns the current volume scale attributable to the {@code VolumeShaper}.
     * Returns the current volume scale attributable to the {@code VolumeShaper}.
     *
     *
     * This is the last volume from the {@code VolumeShaper} used for the player,
     * or the initial volume if the {@code VolumeShaper} hasn't been started with
     * {@link VolumeShaper.Operation#PLAY}.
     *
     * @return the volume, linearly represented as a value between 0.f and 1.f.
     * @return the volume, linearly represented as a value between 0.f and 1.f.
     * @throws IllegalStateException if the player is uninitialized or if there
     *         is a critical failure.  In that case, the {@code VolumeShaper} should be
     *         recreated.
     */
     */
    public float getVolume() {
    public float getVolume() {
        return getStatePlayer(mId).getVolume();
        return getStatePlayer(mId).getVolume();
@@ -89,7 +129,14 @@ public final class VolumeShaper implements AutoCloseable {


    /**
    /**
     * Releases the {@code VolumeShaper} object; any volume scale due to the
     * Releases the {@code VolumeShaper} object; any volume scale due to the
     * {@code VolumeShaper} is removed.
     * {@code VolumeShaper} is removed after closing.
     *
     * If the volume does not reach 1.f when the {@code VolumeShaper} is closed
     * (or finalized), there may be an abrupt change of volume.
     *
     * {@code close()} may be safely called after a prior {@code close()}.
     * This class implements the Java {@code AutoClosable} interface and
     * may be used with try-with-resources.
     */
     */
    @Override
    @Override
    public void close() {
    public void close() {
@@ -107,11 +154,11 @@ public final class VolumeShaper implements AutoCloseable {


    @Override
    @Override
    protected void finalize() {
    protected void finalize() {
        close(); // ensure we remove the native volume shaper
        close(); // ensure we remove the native VolumeShaper
    }
    }


    /**
    /**
     * Internal call to apply the configuration and operation to the Player.
     * Internal call to apply the {@code configuration} and {@code operation} to the player.
     * Returns a valid shaper id or throws the appropriate exception.
     * Returns a valid shaper id or throws the appropriate exception.
     * @param configuration
     * @param configuration
     * @param operation
     * @param operation
@@ -137,7 +184,7 @@ public final class VolumeShaper implements AutoCloseable {
            // Due to RPC handling, we translate integer codes to exceptions right before
            // Due to RPC handling, we translate integer codes to exceptions right before
            // delivering to the user.
            // delivering to the user.
            if (id == VOLUME_SHAPER_INVALID_OPERATION) {
            if (id == VOLUME_SHAPER_INVALID_OPERATION) {
                throw new IllegalStateException("player or volume shaper deallocated");
                throw new IllegalStateException("player or VolumeShaper deallocated");
            } else {
            } else {
                throw new IllegalArgumentException("invalid configuration or operation: " + id);
                throw new IllegalArgumentException("invalid configuration or operation: " + id);
            }
            }
@@ -146,9 +193,9 @@ public final class VolumeShaper implements AutoCloseable {
    }
    }


    /**
    /**
     * Internal call to retrieve the current VolumeShaper state.
     * Internal call to retrieve the current {@code VolumeShaper} state.
     * @param id
     * @param id
     * @return the current {@vode VolumeShaper.State}
     * @return the current {@code VolumeShaper.State}
     * @throws IllegalStateException if the player has been deallocated or is uninitialized.
     * @throws IllegalStateException if the player has been deallocated or is uninitialized.
     */
     */
    private @NonNull VolumeShaper.State getStatePlayer(int id) {
    private @NonNull VolumeShaper.State getStatePlayer(int id) {
@@ -180,6 +227,9 @@ public final class VolumeShaper implements AutoCloseable {
     * by {@link VolumeShaper#replace(Configuration, Operation, boolean)
     * by {@link VolumeShaper#replace(Configuration, Operation, boolean)
     * VolumeShaper.replace(Configuration, Operation, boolean)}
     * VolumeShaper.replace(Configuration, Operation, boolean)}
     * to replace an existing {@code configuration}.
     * to replace an existing {@code configuration}.
     * <p>
     * The {@link AudioTrack} and {@link MediaPlayer} classes implement
     * the {@link VolumeAutomation} interface.
     */
     */
    public static final class Configuration implements Parcelable {
    public static final class Configuration implements Parcelable {
        private static final int MAXIMUM_CURVE_POINTS = 16;
        private static final int MAXIMUM_CURVE_POINTS = 16;
@@ -485,7 +535,7 @@ public final class VolumeShaper implements AutoCloseable {


        /**
        /**
         * @hide
         * @hide
         * Constructs a volume shaper from an id.
         * Constructs a {@code VolumeShaper} from an id.
         *
         *
         * This is an opaque handle for controlling a {@code VolumeShaper} that has
         * This is an opaque handle for controlling a {@code VolumeShaper} that has
         * already been sent to a player.  The {@code id} is returned from the
         * already been sent to a player.  The {@code id} is returned from the
@@ -756,7 +806,7 @@ public final class VolumeShaper implements AutoCloseable {
            /**
            /**
             * Sets the interpolator type.
             * Sets the interpolator type.
             *
             *
             * If omitted the interplator type is {@link #INTERPOLATOR_TYPE_CUBIC}.
             * If omitted the default interpolator type is {@link #INTERPOLATOR_TYPE_CUBIC}.
             *
             *
             * @param interpolatorType method of interpolation used for the volume curve.
             * @param interpolatorType method of interpolation used for the volume curve.
             *        One of {@link #INTERPOLATOR_TYPE_STEP},
             *        One of {@link #INTERPOLATOR_TYPE_STEP},
@@ -802,7 +852,7 @@ public final class VolumeShaper implements AutoCloseable {
            }
            }


            /**
            /**
             * Sets the volume shaper duration in milliseconds.
             * Sets the {@code VolumeShaper} duration in milliseconds.
             *
             *
             * If omitted, the default duration is 1 second.
             * If omitted, the default duration is 1 second.
             *
             *
@@ -1059,13 +1109,13 @@ public final class VolumeShaper implements AutoCloseable {


        /**
        /**
         * Defer playback until next operation is sent. This is used
         * Defer playback until next operation is sent. This is used
         * when starting a VolumeShaper effect.
         * when starting a {@code VolumeShaper} effect.
         */
         */
        private static final int FLAG_DEFER = 1 << 3;
        private static final int FLAG_DEFER = 1 << 3;


        /**
        /**
         * Use the id specified in the configuration, creating
         * Use the id specified in the configuration, creating
         * VolumeShaper as needed; the configuration should be
         * {@code VolumeShaper} as needed; the configuration should be
         * TYPE_SCALE.
         * TYPE_SCALE.
         */
         */
        private static final int FLAG_CREATE_IF_NEEDED = 1 << 4;
        private static final int FLAG_CREATE_IF_NEEDED = 1 << 4;
@@ -1074,18 +1124,20 @@ public final class VolumeShaper implements AutoCloseable {


        private final int mFlags;
        private final int mFlags;
        private final int mReplaceId;
        private final int mReplaceId;
        private final float mXOffset;


        @Override
        @Override
        public String toString() {
        public String toString() {
            return "VolumeShaper.Operation{"
            return "VolumeShaper.Operation{"
                    + "mFlags = 0x" + Integer.toHexString(mFlags).toUpperCase()
                    + "mFlags = 0x" + Integer.toHexString(mFlags).toUpperCase()
                    + ", mReplaceId = " + mReplaceId
                    + ", mReplaceId = " + mReplaceId
                    + ", mXOffset = " + mXOffset
                    + "}";
                    + "}";
        }
        }


        @Override
        @Override
        public int hashCode() {
        public int hashCode() {
            return Objects.hash(mFlags, mReplaceId);
            return Objects.hash(mFlags, mReplaceId, mXOffset);
        }
        }


        @Override
        @Override
@@ -1093,10 +1145,10 @@ public final class VolumeShaper implements AutoCloseable {
            if (!(o instanceof Operation)) return false;
            if (!(o instanceof Operation)) return false;
            if (o == this) return true;
            if (o == this) return true;
            final Operation other = (Operation) o;
            final Operation other = (Operation) o;
            // if xOffset (native field only) is brought into Java

            // we need to do proper NaN comparison as that is allowed.
            return mFlags == other.mFlags
            return mFlags == other.mFlags
                    && mReplaceId == other.mReplaceId;
                    && mReplaceId == other.mReplaceId
                    && Float.compare(mXOffset, other.mXOffset) == 0;
        }
        }


        @Override
        @Override
@@ -1109,7 +1161,7 @@ public final class VolumeShaper implements AutoCloseable {
            // this needs to match the native VolumeShaper.Operation parceling
            // this needs to match the native VolumeShaper.Operation parceling
            dest.writeInt(mFlags);
            dest.writeInt(mFlags);
            dest.writeInt(mReplaceId);
            dest.writeInt(mReplaceId);
            dest.writeFloat(Float.NaN); // xOffset (ignored at Java level)
            dest.writeFloat(mXOffset);
        }
        }


        public static final Parcelable.Creator<VolumeShaper.Operation> CREATOR
        public static final Parcelable.Creator<VolumeShaper.Operation> CREATOR
@@ -1119,11 +1171,12 @@ public final class VolumeShaper implements AutoCloseable {
                // this needs to match the native VolumeShaper.Operation parceling
                // this needs to match the native VolumeShaper.Operation parceling
                final int flags = p.readInt();
                final int flags = p.readInt();
                final int replaceId = p.readInt();
                final int replaceId = p.readInt();
                final float xOffset = p.readFloat(); // ignored at Java level
                final float xOffset = p.readFloat();


                return new VolumeShaper.Operation(
                return new VolumeShaper.Operation(
                        flags
                        flags
                        , replaceId);
                        , replaceId
                        , xOffset);
            }
            }


            @Override
            @Override
@@ -1132,9 +1185,10 @@ public final class VolumeShaper implements AutoCloseable {
            }
            }
        };
        };


        private Operation(@Flag int flags, int replaceId) {
        private Operation(@Flag int flags, int replaceId, float xOffset) {
            mFlags = flags;
            mFlags = flags;
            mReplaceId = replaceId;
            mReplaceId = replaceId;
            mXOffset = xOffset;
        }
        }


        /**
        /**
@@ -1146,6 +1200,7 @@ public final class VolumeShaper implements AutoCloseable {
        public static final class Builder {
        public static final class Builder {
            int mFlags;
            int mFlags;
            int mReplaceId;
            int mReplaceId;
            float mXOffset;


            /**
            /**
             * Constructs a new {@code Builder} with the defaults.
             * Constructs a new {@code Builder} with the defaults.
@@ -1153,23 +1208,27 @@ public final class VolumeShaper implements AutoCloseable {
            public Builder() {
            public Builder() {
                mFlags = 0;
                mFlags = 0;
                mReplaceId = -1;
                mReplaceId = -1;
                mXOffset = Float.NaN;
            }
            }


            /**
            /**
             * Constructs a new Builder from a given {@code VolumeShaper.Operation}
             * Constructs a new {@code Builder} from a given {@code VolumeShaper.Operation}
             * @param operation the {@code VolumeShaper.operation} whose data will be
             * @param operation the {@code VolumeShaper.operation} whose data will be
             *        reused in the new Builder.
             *        reused in the new {@code Builder}.
             */
             */
            public Builder(@NonNull VolumeShaper.Operation operation) {
            public Builder(@NonNull VolumeShaper.Operation operation) {
                mReplaceId = operation.mReplaceId;
                mReplaceId = operation.mReplaceId;
                mFlags = operation.mFlags;
                mFlags = operation.mFlags;
                mXOffset = operation.mXOffset;
            }
            }


            /**
            /**
             * Replaces the previous {@code VolumeShaper} specified by id.
             * Replaces the previous {@code VolumeShaper} specified by {@code id}.
             * It has no other effect if the {@code VolumeShaper} is
             *
             * already expired.
             * The {@code VolumeShaper} specified by the {@code id} is removed
             * @param id the id of the previous {@code VolumeShaper}.
             * if it exists. The configuration should be TYPE_SCALE.
             *
             * @param id the {@code id} of the previous {@code VolumeShaper}.
             * @param join if true, match the volume of the previous
             * @param join if true, match the volume of the previous
             * shaper to the start volume of the new {@code VolumeShaper}.
             * shaper to the start volume of the new {@code VolumeShaper}.
             * @return the same {@code Builder} instance.
             * @return the same {@code Builder} instance.
@@ -1194,8 +1253,9 @@ public final class VolumeShaper implements AutoCloseable {
            }
            }


            /**
            /**
             * Terminates the VolumeShaper.
             * Terminates the {@code VolumeShaper}.
             * Do not call directly, use {@link VolumeShaper#release()}.
             *
             * Do not call directly, use {@link VolumeShaper#close()}.
             * @return the same {@code Builder} instance.
             * @return the same {@code Builder} instance.
             */
             */
            public @NonNull Builder terminate() {
            public @NonNull Builder terminate() {
@@ -1214,8 +1274,12 @@ public final class VolumeShaper implements AutoCloseable {


            /**
            /**
             * Use the id specified in the configuration, creating
             * Use the id specified in the configuration, creating
             * VolumeShaper as needed; the configuration should be
             * {@code VolumeShaper} only as needed; the configuration should be
             * TYPE_SCALE.
             * TYPE_SCALE.
             *
             * If the {@code VolumeShaper} with the same id already exists
             * then the operation has no effect.
             *
             * @return the same {@code Builder} instance.
             * @return the same {@code Builder} instance.
             */
             */
            public @NonNull Builder createIfNeeded() {
            public @NonNull Builder createIfNeeded() {
@@ -1223,6 +1287,28 @@ public final class VolumeShaper implements AutoCloseable {
                return this;
                return this;
            }
            }


            /**
             * Sets the {@code xOffset} to use for the {@code VolumeShaper}.
             *
             * The {@code xOffset} is the position on the volume curve,
             * and setting takes effect when the {@code VolumeShaper} is used next.
             *
             * @param xOffset a value between (or equal to) 0.f and 1.f, or Float.NaN to ignore.
             * @return the same {@code Builder} instance.
             * @throws IllegalArgumentException if {@code xOffset} is not between 0.f and 1.f,
             *         or a Float.NaN.
             */
            public @NonNull Builder setXOffset(float xOffset) {
                if (xOffset < -0.f) {
                    throw new IllegalArgumentException("Negative xOffset not allowed");
                } else if (xOffset > 1.f) {
                    throw new IllegalArgumentException("xOffset > 1.f not allowed");
                }
                // Float.NaN passes through
                mXOffset = xOffset;
                return this;
            }

            /**
            /**
             * Sets the operation flag.  Do not call this directly but one of the
             * Sets the operation flag.  Do not call this directly but one of the
             * other builder methods.
             * other builder methods.
@@ -1245,7 +1331,7 @@ public final class VolumeShaper implements AutoCloseable {
             * @return a new {@code VolumeShaper.Operation} object
             * @return a new {@code VolumeShaper.Operation} object
             */
             */
            public @NonNull Operation build() {
            public @NonNull Operation build() {
                return new Operation(mFlags, mReplaceId);
                return new Operation(mFlags, mReplaceId, mXOffset);
            }
            }
        } // Operation.Builder
        } // Operation.Builder
    } // Operation
    } // Operation
@@ -1316,15 +1402,18 @@ public final class VolumeShaper implements AutoCloseable {


        /**
        /**
         * Gets the volume of the {@link VolumeShaper.State}.
         * Gets the volume of the {@link VolumeShaper.State}.
         * @return linear volume between 0.f and 1.f.
         */
         */
        public float getVolume() {
        public float getVolume() {
            return mVolume;
            return mVolume;
        }
        }


        /**
        /**
         * Gets the elapsed ms of the {@link VolumeShaper.State}
         * Gets the {@code xOffset} position on the normalized curve
         * of the {@link VolumeShaper.State}.
         * @return the curve x position between 0.f and 1.f.
         */
         */
        public double getXOffset() {
        public float getXOffset() {
            return mXOffset;
            return mXOffset;
        }
        }
    } // State
    } // State
+8 −3
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ struct VolumeShaperHelper {
        jmethodID opConstructId;
        jmethodID opConstructId;
        jfieldID  opFlagsId;
        jfieldID  opFlagsId;
        jfieldID  opReplaceIdId;
        jfieldID  opReplaceIdId;
        jfieldID  opXOffsetId;


        // VolumeShaper.State
        // VolumeShaper.State
        jclass    stClazz;
        jclass    stClazz;
@@ -74,9 +75,10 @@ struct VolumeShaperHelper {
            if (opClazz == nullptr) {
            if (opClazz == nullptr) {
                return;
                return;
            }
            }
            opConstructId = env->GetMethodID(opClazz, "<init>", "(II)V");
            opConstructId = env->GetMethodID(opClazz, "<init>", "(IIF)V");
            opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
            opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
            opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
            opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
            opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F");
            env->DeleteLocalRef(lclazz);
            env->DeleteLocalRef(lclazz);


            lclazz = env->FindClass("android/media/VolumeShaper$State");
            lclazz = env->FindClass("android/media/VolumeShaper$State");
@@ -179,17 +181,20 @@ struct VolumeShaperHelper {
        VolumeShaper::Operation::Flag flags =
        VolumeShaper::Operation::Flag flags =
            (VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId);
            (VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId);
        int replaceId = env->GetIntField(joperation, fields.opReplaceIdId);
        int replaceId = env->GetIntField(joperation, fields.opReplaceIdId);
        float xOffset = env->GetFloatField(joperation, fields.opXOffsetId);


        sp<VolumeShaper::Operation> operation = new VolumeShaper::Operation(flags, replaceId);
        sp<VolumeShaper::Operation> operation =
                new VolumeShaper::Operation(flags, replaceId, xOffset);
        return operation;
        return operation;
    }
    }


    static jobject convertOperationToJobject(
    static jobject convertOperationToJobject(
            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
        // prepare constructor args
        // prepare constructor args
        jvalue args[2];
        jvalue args[3];
        args[0].i = (jint)operation->getFlags();
        args[0].i = (jint)operation->getFlags();
        args[1].i = (jint)operation->getReplaceId();
        args[1].i = (jint)operation->getReplaceId();
        args[2].f = (jfloat)operation->getXOffset();


        jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
        jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
        return joperation;
        return joperation;