/* * 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 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, "", "(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, "", "(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, "", "(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 convertJobjectToConfiguration( JNIEnv *env, const fields_t &fields, jobject jshaper) { sp 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(&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(&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 &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 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 operation = new VolumeShaper::Operation(flags, replaceId, xOffset); return operation; } static jobject convertOperationToJobject( JNIEnv *env, const fields_t &fields, const sp &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 convertJobjectToState( JNIEnv *env, const fields_t &fields, jobject jstate) { float volume = env->GetFloatField(jstate, fields.stVolumeId); float xOffset = env->GetFloatField(jstate, fields.stXOffsetId); sp state = new VolumeShaper::State(volume, xOffset); return state; } static jobject convertStateToJobject( JNIEnv *env, const fields_t &fields, const sp &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_