/*
* 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 {
using media::VolumeShaper;
// 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 coOptionFlagsId;
jfieldID coDurationMsId;
jfieldID coInterpolatorTypeId;
jfieldID coTimesId;
jfieldID coVolumesId;
// VolumeShaper.Operation
jclass opClazz;
jmethodID opConstructId;
jfieldID opFlagsId;
jfieldID opReplaceIdId;
jfieldID opXOffsetId;
// 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>", "(IIIDI[F[F)V");
coTypeId = env->GetFieldID(coClazz, "mType", "I");
coIdId = env->GetFieldID(coClazz, "mId", "I");
coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
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>", "(IIF)V");
opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F");
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->setOptionFlags(
(VolumeShaper::Configuration::OptionFlag)
env->GetIntField(jshaper, fields.coOptionFlagsId));
configuration->setDurationMs(
(double)env->GetDoubleField(jshaper, fields.coDurationMsId));
configuration->setInterpolatorType(
(VolumeShaper::Configuration::InterpolatorType)
env->GetIntField(jshaper, fields.coInterpolatorTypeId));
// 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->getOptionFlags();
args[3].d = (jdouble)configuration->getDurationMs();
args[4].i = (jint)configuration->getInterpolatorType();
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);
float xOffset = env->GetFloatField(joperation, fields.opXOffsetId);
sp<VolumeShaper::Operation> operation =
new VolumeShaper::Operation(flags, replaceId, xOffset);
return operation;
}
static jobject convertOperationToJobject(
JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
// prepare constructor args
jvalue args[3];
args[0].i = (jint)operation->getFlags();
args[1].i = (jint)operation->getReplaceId();
args[2].f = (jfloat)operation->getXOffset();
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_