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

Commit 035d4ec7 authored by Andy Hung's avatar Andy Hung
Browse files

VolumeShaper: Initial implementation

The VolumeShaper is used to apply a volume
envelope to an AudioTrack or a MediaPlayer.

Test: CTS
Bug: 30920125
Bug: 31015569
Change-Id: If8b4bed29760aa3bd15a4b54cae60e40b4f518ee
parent 30110eae
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include "android_media_AudioErrors.h"
#include "android_media_PlaybackParams.h"
#include "android_media_DeviceCallback.h"
#include "android_media_VolumeShaper.h"

#include <cinttypes>

@@ -64,6 +65,7 @@ struct audio_attributes_fields_t {
static audio_track_fields_t      javaAudioTrackFields;
static audio_attributes_fields_t javaAudioAttrFields;
static PlaybackParams::fields_t gPlaybackParamsFields;
static VolumeShaperHelper::fields_t gVolumeShaperFields;

struct audiotrack_callback_cookie {
    jclass      audioTrack_class;
@@ -1178,6 +1180,50 @@ static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
    return FCC_8;
}

// Pass through the arguments to the AudioFlinger track implementation.
static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
        jobject jconfig, jobject joperation) {
    // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
    const int VOLUME_SHAPER_INVALID_OPERATION = -38;

    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == nullptr) {
        return (jint)VOLUME_SHAPER_INVALID_OPERATION;
    }

    sp<VolumeShaper::Configuration> configuration;
    sp<VolumeShaper::Operation> operation;
    if (jconfig != nullptr) {
        configuration = VolumeShaperHelper::convertJobjectToConfiguration(
                env, gVolumeShaperFields, jconfig);
        ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
    }
    if (joperation != nullptr) {
        operation = VolumeShaperHelper::convertJobjectToOperation(
                env, gVolumeShaperFields, joperation);
        ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
    }
    VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
    if (status == INVALID_OPERATION) {
        status = VOLUME_SHAPER_INVALID_OPERATION;
    }
    return (jint)status; // if status < 0 an error, else a VolumeShaper id
}

// Pass through the arguments to the AudioFlinger track implementation.
static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
        jint id) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == nullptr) {
        return (jobject)nullptr;
    }

    sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
    if (state.get() == nullptr) {
        return (jobject)nullptr;
    }
    return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
}

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -1242,6 +1288,12 @@ static const JNINativeMethod gMethods[] = {
    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
    {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
    {"native_applyVolumeShaper",
            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
                                         (void *)android_media_AudioTrack_apply_volume_shaper},
    {"native_getVolumeShaperState",
            "(I)Landroid/media/VolumeShaper$State;",
                                        (void *)android_media_AudioTrack_get_volume_shaper_state},
};


@@ -1312,6 +1364,7 @@ int register_android_media_AudioTrack(JNIEnv *env)
    // initialize PlaybackParams field info
    gPlaybackParamsFields.init(env);

    gVolumeShaperFields.init(env);
    return res;
}

+17 −0
Original line number Diff line number Diff line
@@ -1741,6 +1741,17 @@ public class AudioTrack extends PlayerBase
        return setStereoVolume(gain, gain);
    }

    @Override
    /* package */ int playerApplyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation) {
        return native_applyVolumeShaper(configuration, operation);
    }

    @Override
    /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
        return native_getVolumeShaperState(id);
    }

    /**
     * Sets the playback sample rate for this track. This sets the sampling rate at which
@@ -3093,6 +3104,12 @@ public class AudioTrack extends PlayerBase
    private native final void native_disableDeviceCallback();
    static private native int native_get_FCC_8();

    private native int native_applyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation);

    private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);

    //---------------------------------------------------------
    // Utility methods
    //------------------
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.media.VolumeShaper;

/**
 * @hide
@@ -27,4 +28,6 @@ interface IPlayer {
    oneway void setVolume(float vol);
    oneway void setPan(float pan);
    oneway void setStartDelayMs(int delayMs);
    oneway void applyVolumeShaper(in VolumeShaper.Configuration configuration,
                                  in VolumeShaper.Operation operation);
}
+18 −0
Original line number Diff line number Diff line
@@ -1331,6 +1331,24 @@ public class MediaPlayer extends PlayerBase
        stop();
    }

    @Override
    /* package */ int playerApplyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation) {
        return native_applyVolumeShaper(configuration, operation);
    }

    @Override
    /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
        return native_getVolumeShaperState(id);
    }

    private native int native_applyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation);

    private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);

    /**
     * Set the low-level power management behavior for this MediaPlayer.  This
     * can be used when the MediaPlayer is not playing through a SurfaceHolder
+45 −0
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.media.VolumeShaper;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -357,6 +359,42 @@ public abstract class PlayerBase {
     * @param rightVolume the right volume to use if muting is false
     */
    abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);

    /**
     * Abstract method to apply a {@link VolumeShaper.Configuration}
     * and a {@link VolumeShaper.Operation} to the Player.
     * This should be overridden by the Player to call into the native
     * VolumeShaper implementation. Multiple {@code VolumeShapers} may be
     * concurrently active for a given Player, each accessible by the
     * {@code VolumeShaper} id.
     *
     * The {@code VolumeShaper} implementation caches the id returned
     * when applying a fully specified configuration
     * from {VolumeShaper.Configuration.Builder} to track later
     * operation changes requested on it.
     *
     * @param configuration a {@code VolumeShaper.Configuration} object
     *        created by {@link VolumeShaper.Configuration.Builder} or
     *        an created from a {@code VolumeShaper} id
     *        by the {@link VolumeShaper.Configuration} constructor.
     * @param operation a {@code VolumeShaper.Operation}.
     * @return a negative error status or a
     *         non-negative {@code VolumeShaper} id on success.
     */
    /* package */ abstract int playerApplyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation);

    /**
     * Abstract method to get the current VolumeShaper state.
     * @param id the {@code VolumeShaper} id returned from
     *           sending a fully specified {@code VolumeShaper.Configuration}
     *           through {@link #playerApplyVolumeShaper}
     * @return a {@code VolumeShaper.State} object or null if
     *         there is no {@code VolumeShaper} for the id.
     */
    /* package */ abstract @Nullable VolumeShaper.State playerGetVolumeShaperState(int id);

    abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
    abstract void playerStart();
    abstract void playerPause();
@@ -396,6 +434,13 @@ public abstract class PlayerBase {
        public void setStartDelayMs(int delayMs) {
            baseSetStartDelayMs(delayMs);
        }

        @Override
        public void applyVolumeShaper(
                @NonNull VolumeShaper.Configuration configuration,
                @NonNull VolumeShaper.Operation operation) {
            /* void */ playerApplyVolumeShaper(configuration, operation);
        }
    };

    //=====================================================================
Loading