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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
//------------------
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
//=====================================================================
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.media.VolumeShaper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -132,4 +133,21 @@ public class PlayerProxy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param configuration
|
||||
* @param operation
|
||||
* @return volume shaper id or error
|
||||
*/
|
||||
public void applyVolumeShaper(
|
||||
@NonNull VolumeShaper.Configuration configuration,
|
||||
@NonNull VolumeShaper.Operation operation) {
|
||||
try {
|
||||
mConf.getIPlayer().applyVolumeShaper(configuration, operation);
|
||||
} catch (NullPointerException|RemoteException e) {
|
||||
throw new IllegalStateException(
|
||||
"No player to proxy for applyVolumeShaper operation,"
|
||||
+ " player already released?", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
@@ -388,6 +390,17 @@ public class SoundPool extends PlayerBase {
|
||||
_setVolume(streamID, leftVolume, rightVolume);
|
||||
}
|
||||
|
||||
@Override
|
||||
/* package */ int playerApplyVolumeShaper(
|
||||
@NonNull VolumeShaper.Configuration configuration,
|
||||
@Nullable VolumeShaper.Operation operation) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
/* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
|
||||
|
||||
21
media/java/android/media/VolumeShaper.aidl
Normal file
21
media/java/android/media/VolumeShaper.aidl
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
parcelable VolumeShaper.Configuration;
|
||||
parcelable VolumeShaper.Operation;
|
||||
parcelable VolumeShaper.State;
|
||||
1275
media/java/android/media/VolumeShaper.java
Normal file
1275
media/java/android/media/VolumeShaper.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,7 @@
|
||||
#include "android_media_MediaMetricsJNI.h"
|
||||
#include "android_media_PlaybackParams.h"
|
||||
#include "android_media_SyncParams.h"
|
||||
#include "android_media_VolumeShaper.h"
|
||||
#include "android_media_Utils.h"
|
||||
|
||||
#include "android_os_Parcel.h"
|
||||
@@ -160,6 +161,7 @@ static fields_t fields;
|
||||
static BufferingParams::fields_t gBufferingParamsFields;
|
||||
static PlaybackParams::fields_t gPlaybackParamsFields;
|
||||
static SyncParams::fields_t gSyncParamsFields;
|
||||
static VolumeShaperHelper::fields_t gVolumeShaperFields;
|
||||
|
||||
static Mutex sLock;
|
||||
|
||||
@@ -1088,6 +1090,7 @@ android_media_MediaPlayer_native_init(JNIEnv *env)
|
||||
|
||||
gPlaybackParamsFields.init(env);
|
||||
gSyncParamsFields.init(env);
|
||||
gVolumeShaperFields.init(env);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1259,6 +1262,51 @@ android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject
|
||||
;
|
||||
}
|
||||
|
||||
// Pass through the arguments to the MediaServer player implementation.
|
||||
static jint android_media_MediaPlayer_applyVolumeShaper(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<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == 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 = mp->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 MediaServer player implementation.
|
||||
static jobject android_media_MediaPlayer_getVolumeShaperState(JNIEnv *env, jobject thiz,
|
||||
jint id) {
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == nullptr) {
|
||||
return (jobject)nullptr;
|
||||
}
|
||||
|
||||
sp<VolumeShaper::State> state = mp->getVolumeShaperState((int)id);
|
||||
if (state.get() == nullptr) {
|
||||
return (jobject)nullptr;
|
||||
}
|
||||
return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// Modular DRM begin
|
||||
|
||||
@@ -1747,6 +1795,12 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
|
||||
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
|
||||
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer},
|
||||
{"native_applyVolumeShaper",
|
||||
"(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
|
||||
(void *)android_media_MediaPlayer_applyVolumeShaper},
|
||||
{"native_getVolumeShaperState",
|
||||
"(I)Landroid/media/VolumeShaper$State;",
|
||||
(void *)android_media_MediaPlayer_getVolumeShaperState},
|
||||
// Modular DRM
|
||||
{ "_prepareDrm", "([BI)V", (void *)android_media_MediaPlayer_prepareDrm },
|
||||
{ "_releaseDrm", "()V", (void *)android_media_MediaPlayer_releaseDrm },
|
||||
|
||||
221
media/jni/android_media_VolumeShaper.h
Normal file
221
media/jni/android_media_VolumeShaper.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2017, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_MEDIA_VOLUME_SHAPER_H_
|
||||
#define _ANDROID_MEDIA_VOLUME_SHAPER_H_
|
||||
|
||||
#include <media/VolumeShaper.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// This entire class is inline as it is used from both core and media
|
||||
struct VolumeShaperHelper {
|
||||
struct fields_t {
|
||||
// VolumeShaper.Configuration
|
||||
jclass coClazz;
|
||||
jmethodID coConstructId;
|
||||
jfieldID coTypeId;
|
||||
jfieldID coIdId;
|
||||
jfieldID coInterpolatorTypeId;
|
||||
jfieldID coOptionFlagsId;
|
||||
jfieldID coDurationMsId;
|
||||
jfieldID coTimesId;
|
||||
jfieldID coVolumesId;
|
||||
|
||||
// VolumeShaper.Operation
|
||||
jclass opClazz;
|
||||
jmethodID opConstructId;
|
||||
jfieldID opFlagsId;
|
||||
jfieldID opReplaceIdId;
|
||||
|
||||
// VolumeShaper.State
|
||||
jclass stClazz;
|
||||
jmethodID stConstructId;
|
||||
jfieldID stVolumeId;
|
||||
jfieldID stXOffsetId;
|
||||
|
||||
void init(JNIEnv *env) {
|
||||
jclass lclazz = env->FindClass("android/media/VolumeShaper$Configuration");
|
||||
if (lclazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
coClazz = (jclass)env->NewGlobalRef(lclazz);
|
||||
if (coClazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIID[F[F)V");
|
||||
coTypeId = env->GetFieldID(coClazz, "mType", "I");
|
||||
coIdId = env->GetFieldID(coClazz, "mId", "I");
|
||||
coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
|
||||
coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
|
||||
coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
|
||||
coTimesId = env->GetFieldID(coClazz, "mTimes", "[F");
|
||||
coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F");
|
||||
env->DeleteLocalRef(lclazz);
|
||||
|
||||
lclazz = env->FindClass("android/media/VolumeShaper$Operation");
|
||||
if (lclazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
opClazz = (jclass)env->NewGlobalRef(lclazz);
|
||||
if (opClazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
opConstructId = env->GetMethodID(opClazz, "<init>", "(II)V");
|
||||
opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
|
||||
opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
|
||||
env->DeleteLocalRef(lclazz);
|
||||
|
||||
lclazz = env->FindClass("android/media/VolumeShaper$State");
|
||||
if (lclazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
stClazz = (jclass)env->NewGlobalRef(lclazz);
|
||||
if (stClazz == nullptr) {
|
||||
return;
|
||||
}
|
||||
stConstructId = env->GetMethodID(stClazz, "<init>", "(FF)V");
|
||||
stVolumeId = env->GetFieldID(stClazz, "mVolume", "F");
|
||||
stXOffsetId = env->GetFieldID(stClazz, "mXOffset", "F");
|
||||
env->DeleteLocalRef(lclazz);
|
||||
}
|
||||
|
||||
void exit(JNIEnv *env) {
|
||||
env->DeleteGlobalRef(coClazz);
|
||||
coClazz = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static sp<VolumeShaper::Configuration> convertJobjectToConfiguration(
|
||||
JNIEnv *env, const fields_t &fields, jobject jshaper) {
|
||||
sp<VolumeShaper::Configuration> configuration = new VolumeShaper::Configuration();
|
||||
|
||||
configuration->setType(
|
||||
(VolumeShaper::Configuration::Type)env->GetIntField(jshaper, fields.coTypeId));
|
||||
configuration->setId(
|
||||
(int)env->GetIntField(jshaper, fields.coIdId));
|
||||
if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
|
||||
configuration->setInterpolatorType(
|
||||
(VolumeShaper::Configuration::InterpolatorType)
|
||||
env->GetIntField(jshaper, fields.coInterpolatorTypeId));
|
||||
configuration->setOptionFlags(
|
||||
(VolumeShaper::Configuration::OptionFlag)
|
||||
env->GetIntField(jshaper, fields.coOptionFlagsId));
|
||||
configuration->setDurationMs(
|
||||
(double)env->GetDoubleField(jshaper, fields.coDurationMsId));
|
||||
|
||||
// convert point arrays
|
||||
jobject xobj = env->GetObjectField(jshaper, fields.coTimesId);
|
||||
jfloatArray *xarray = reinterpret_cast<jfloatArray*>(&xobj);
|
||||
jsize xlen = env->GetArrayLength(*xarray);
|
||||
/* const */ float * const x =
|
||||
env->GetFloatArrayElements(*xarray, nullptr /* isCopy */);
|
||||
jobject yobj = env->GetObjectField(jshaper, fields.coVolumesId);
|
||||
jfloatArray *yarray = reinterpret_cast<jfloatArray*>(&yobj);
|
||||
jsize ylen = env->GetArrayLength(*yarray);
|
||||
/* const */ float * const y =
|
||||
env->GetFloatArrayElements(*yarray, nullptr /* isCopy */);
|
||||
if (xlen != ylen) {
|
||||
ALOGE("array size must match");
|
||||
return nullptr;
|
||||
}
|
||||
for (jsize i = 0; i < xlen; ++i) {
|
||||
configuration->emplace(x[i], y[i]);
|
||||
}
|
||||
env->ReleaseFloatArrayElements(*xarray, x, JNI_ABORT); // no need to copy back
|
||||
env->ReleaseFloatArrayElements(*yarray, y, JNI_ABORT);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
static jobject convertVolumeShaperToJobject(
|
||||
JNIEnv *env, const fields_t &fields,
|
||||
const sp<VolumeShaper::Configuration> &configuration) {
|
||||
jfloatArray xarray = nullptr;
|
||||
jfloatArray yarray = nullptr;
|
||||
if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
|
||||
// convert curve arrays
|
||||
jfloatArray xarray = env->NewFloatArray(configuration->size());
|
||||
jfloatArray yarray = env->NewFloatArray(configuration->size());
|
||||
float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
|
||||
float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
|
||||
float *xptr = x, *yptr = y;
|
||||
for (const auto &pt : *configuration.get()) {
|
||||
*xptr++ = pt.first;
|
||||
*yptr++ = pt.second;
|
||||
}
|
||||
env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
|
||||
env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
|
||||
}
|
||||
|
||||
// prepare constructor args
|
||||
jvalue args[7];
|
||||
args[0].i = (jint)configuration->getType();
|
||||
args[1].i = (jint)configuration->getId();
|
||||
args[2].i = (jint)configuration->getInterpolatorType();
|
||||
args[3].i = (jint)configuration->getOptionFlags();
|
||||
args[4].d = (jdouble)configuration->getDurationMs();
|
||||
args[5].l = xarray;
|
||||
args[6].l = yarray;
|
||||
jobject jshaper = env->NewObjectA(fields.coClazz, fields.coConstructId, args);
|
||||
return jshaper;
|
||||
}
|
||||
|
||||
static sp<VolumeShaper::Operation> convertJobjectToOperation(
|
||||
JNIEnv *env, const fields_t &fields, jobject joperation) {
|
||||
VolumeShaper::Operation::Flag flags =
|
||||
(VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId);
|
||||
int replaceId = env->GetIntField(joperation, fields.opReplaceIdId);
|
||||
|
||||
sp<VolumeShaper::Operation> operation = new VolumeShaper::Operation(flags, replaceId);
|
||||
return operation;
|
||||
}
|
||||
|
||||
static jobject convertOperationToJobject(
|
||||
JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
|
||||
// prepare constructor args
|
||||
jvalue args[2];
|
||||
args[0].i = (jint)operation->getFlags();
|
||||
args[1].i = (jint)operation->getReplaceId();
|
||||
|
||||
jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
|
||||
return joperation;
|
||||
}
|
||||
|
||||
static sp<VolumeShaper::State> convertJobjectToState(
|
||||
JNIEnv *env, const fields_t &fields, jobject jstate) {
|
||||
float volume = env->GetFloatField(jstate, fields.stVolumeId);
|
||||
float xOffset = env->GetFloatField(jstate, fields.stXOffsetId);
|
||||
|
||||
sp<VolumeShaper::State> state = new VolumeShaper::State(volume, xOffset);
|
||||
return state;
|
||||
}
|
||||
|
||||
static jobject convertStateToJobject(
|
||||
JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::State> &state) {
|
||||
// prepare constructor args
|
||||
jvalue args[2];
|
||||
args[0].f = (jfloat)state->getVolume();
|
||||
args[1].f = (jfloat)state->getXOffset();
|
||||
|
||||
jobject jstate = env->NewObjectA(fields.stClazz, fields.stConstructId, args);
|
||||
return jstate;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROID_MEDIA_VOLUME_SHAPER_H_
|
||||
Reference in New Issue
Block a user