Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017, The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef _ANDROID_MEDIA_VOLUME_SHAPER_H_ |
| 18 | #define _ANDROID_MEDIA_VOLUME_SHAPER_H_ |
| 19 | |
| 20 | #include <media/VolumeShaper.h> |
| 21 | |
| 22 | namespace android { |
| 23 | |
Ivan Lozano | 330d876 | 2017-08-08 12:51:06 -0700 | [diff] [blame] | 24 | using media::VolumeShaper; |
| 25 | |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 26 | // This entire class is inline as it is used from both core and media |
| 27 | struct VolumeShaperHelper { |
| 28 | struct fields_t { |
| 29 | // VolumeShaper.Configuration |
| 30 | jclass coClazz; |
| 31 | jmethodID coConstructId; |
| 32 | jfieldID coTypeId; |
| 33 | jfieldID coIdId; |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 34 | jfieldID coOptionFlagsId; |
| 35 | jfieldID coDurationMsId; |
Andy Hung | d4f1e86 | 2017-03-06 11:39:10 -0800 | [diff] [blame] | 36 | jfieldID coInterpolatorTypeId; |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 37 | jfieldID coTimesId; |
| 38 | jfieldID coVolumesId; |
| 39 | |
| 40 | // VolumeShaper.Operation |
| 41 | jclass opClazz; |
| 42 | jmethodID opConstructId; |
| 43 | jfieldID opFlagsId; |
| 44 | jfieldID opReplaceIdId; |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 45 | jfieldID opXOffsetId; |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 46 | |
| 47 | // VolumeShaper.State |
| 48 | jclass stClazz; |
| 49 | jmethodID stConstructId; |
| 50 | jfieldID stVolumeId; |
| 51 | jfieldID stXOffsetId; |
| 52 | |
| 53 | void init(JNIEnv *env) { |
| 54 | jclass lclazz = env->FindClass("android/media/VolumeShaper$Configuration"); |
| 55 | if (lclazz == nullptr) { |
| 56 | return; |
| 57 | } |
| 58 | coClazz = (jclass)env->NewGlobalRef(lclazz); |
| 59 | if (coClazz == nullptr) { |
| 60 | return; |
| 61 | } |
Andy Hung | d4f1e86 | 2017-03-06 11:39:10 -0800 | [diff] [blame] | 62 | coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIDI[F[F)V"); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 63 | coTypeId = env->GetFieldID(coClazz, "mType", "I"); |
| 64 | coIdId = env->GetFieldID(coClazz, "mId", "I"); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 65 | coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I"); |
| 66 | coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D"); |
Andy Hung | d4f1e86 | 2017-03-06 11:39:10 -0800 | [diff] [blame] | 67 | coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I"); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 68 | coTimesId = env->GetFieldID(coClazz, "mTimes", "[F"); |
| 69 | coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F"); |
| 70 | env->DeleteLocalRef(lclazz); |
| 71 | |
| 72 | lclazz = env->FindClass("android/media/VolumeShaper$Operation"); |
| 73 | if (lclazz == nullptr) { |
| 74 | return; |
| 75 | } |
| 76 | opClazz = (jclass)env->NewGlobalRef(lclazz); |
| 77 | if (opClazz == nullptr) { |
| 78 | return; |
| 79 | } |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 80 | opConstructId = env->GetMethodID(opClazz, "<init>", "(IIF)V"); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 81 | opFlagsId = env->GetFieldID(opClazz, "mFlags", "I"); |
| 82 | opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I"); |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 83 | opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F"); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 84 | env->DeleteLocalRef(lclazz); |
| 85 | |
| 86 | lclazz = env->FindClass("android/media/VolumeShaper$State"); |
| 87 | if (lclazz == nullptr) { |
| 88 | return; |
| 89 | } |
| 90 | stClazz = (jclass)env->NewGlobalRef(lclazz); |
| 91 | if (stClazz == nullptr) { |
| 92 | return; |
| 93 | } |
| 94 | stConstructId = env->GetMethodID(stClazz, "<init>", "(FF)V"); |
| 95 | stVolumeId = env->GetFieldID(stClazz, "mVolume", "F"); |
| 96 | stXOffsetId = env->GetFieldID(stClazz, "mXOffset", "F"); |
| 97 | env->DeleteLocalRef(lclazz); |
| 98 | } |
| 99 | |
| 100 | void exit(JNIEnv *env) { |
| 101 | env->DeleteGlobalRef(coClazz); |
| 102 | coClazz = nullptr; |
| 103 | } |
| 104 | }; |
| 105 | |
| 106 | static sp<VolumeShaper::Configuration> convertJobjectToConfiguration( |
| 107 | JNIEnv *env, const fields_t &fields, jobject jshaper) { |
| 108 | sp<VolumeShaper::Configuration> configuration = new VolumeShaper::Configuration(); |
| 109 | |
| 110 | configuration->setType( |
| 111 | (VolumeShaper::Configuration::Type)env->GetIntField(jshaper, fields.coTypeId)); |
| 112 | configuration->setId( |
| 113 | (int)env->GetIntField(jshaper, fields.coIdId)); |
| 114 | if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) { |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 115 | configuration->setOptionFlags( |
| 116 | (VolumeShaper::Configuration::OptionFlag) |
| 117 | env->GetIntField(jshaper, fields.coOptionFlagsId)); |
| 118 | configuration->setDurationMs( |
| 119 | (double)env->GetDoubleField(jshaper, fields.coDurationMsId)); |
Andy Hung | d4f1e86 | 2017-03-06 11:39:10 -0800 | [diff] [blame] | 120 | configuration->setInterpolatorType( |
| 121 | (VolumeShaper::Configuration::InterpolatorType) |
| 122 | env->GetIntField(jshaper, fields.coInterpolatorTypeId)); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 123 | |
| 124 | // convert point arrays |
| 125 | jobject xobj = env->GetObjectField(jshaper, fields.coTimesId); |
| 126 | jfloatArray *xarray = reinterpret_cast<jfloatArray*>(&xobj); |
| 127 | jsize xlen = env->GetArrayLength(*xarray); |
| 128 | /* const */ float * const x = |
| 129 | env->GetFloatArrayElements(*xarray, nullptr /* isCopy */); |
| 130 | jobject yobj = env->GetObjectField(jshaper, fields.coVolumesId); |
| 131 | jfloatArray *yarray = reinterpret_cast<jfloatArray*>(&yobj); |
| 132 | jsize ylen = env->GetArrayLength(*yarray); |
| 133 | /* const */ float * const y = |
| 134 | env->GetFloatArrayElements(*yarray, nullptr /* isCopy */); |
| 135 | if (xlen != ylen) { |
| 136 | ALOGE("array size must match"); |
| 137 | return nullptr; |
| 138 | } |
| 139 | for (jsize i = 0; i < xlen; ++i) { |
| 140 | configuration->emplace(x[i], y[i]); |
| 141 | } |
| 142 | env->ReleaseFloatArrayElements(*xarray, x, JNI_ABORT); // no need to copy back |
| 143 | env->ReleaseFloatArrayElements(*yarray, y, JNI_ABORT); |
| 144 | } |
| 145 | return configuration; |
| 146 | } |
| 147 | |
| 148 | static jobject convertVolumeShaperToJobject( |
| 149 | JNIEnv *env, const fields_t &fields, |
| 150 | const sp<VolumeShaper::Configuration> &configuration) { |
| 151 | jfloatArray xarray = nullptr; |
| 152 | jfloatArray yarray = nullptr; |
| 153 | if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) { |
| 154 | // convert curve arrays |
| 155 | jfloatArray xarray = env->NewFloatArray(configuration->size()); |
| 156 | jfloatArray yarray = env->NewFloatArray(configuration->size()); |
| 157 | float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */); |
| 158 | float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */); |
| 159 | float *xptr = x, *yptr = y; |
| 160 | for (const auto &pt : *configuration.get()) { |
| 161 | *xptr++ = pt.first; |
| 162 | *yptr++ = pt.second; |
| 163 | } |
| 164 | env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */); |
| 165 | env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */); |
| 166 | } |
| 167 | |
| 168 | // prepare constructor args |
| 169 | jvalue args[7]; |
| 170 | args[0].i = (jint)configuration->getType(); |
| 171 | args[1].i = (jint)configuration->getId(); |
Andy Hung | d4f1e86 | 2017-03-06 11:39:10 -0800 | [diff] [blame] | 172 | args[2].i = (jint)configuration->getOptionFlags(); |
| 173 | args[3].d = (jdouble)configuration->getDurationMs(); |
| 174 | args[4].i = (jint)configuration->getInterpolatorType(); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 175 | args[5].l = xarray; |
| 176 | args[6].l = yarray; |
| 177 | jobject jshaper = env->NewObjectA(fields.coClazz, fields.coConstructId, args); |
| 178 | return jshaper; |
| 179 | } |
| 180 | |
| 181 | static sp<VolumeShaper::Operation> convertJobjectToOperation( |
| 182 | JNIEnv *env, const fields_t &fields, jobject joperation) { |
| 183 | VolumeShaper::Operation::Flag flags = |
| 184 | (VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId); |
| 185 | int replaceId = env->GetIntField(joperation, fields.opReplaceIdId); |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 186 | float xOffset = env->GetFloatField(joperation, fields.opXOffsetId); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 187 | |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 188 | sp<VolumeShaper::Operation> operation = |
| 189 | new VolumeShaper::Operation(flags, replaceId, xOffset); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 190 | return operation; |
| 191 | } |
| 192 | |
| 193 | static jobject convertOperationToJobject( |
| 194 | JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) { |
| 195 | // prepare constructor args |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 196 | jvalue args[3]; |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 197 | args[0].i = (jint)operation->getFlags(); |
| 198 | args[1].i = (jint)operation->getReplaceId(); |
Andy Hung | 3c0f5d2 | 2017-05-15 15:41:14 -0700 | [diff] [blame] | 199 | args[2].f = (jfloat)operation->getXOffset(); |
Andy Hung | 035d4ec | 2017-01-24 13:45:02 -0800 | [diff] [blame] | 200 | |
| 201 | jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args); |
| 202 | return joperation; |
| 203 | } |
| 204 | |
| 205 | static sp<VolumeShaper::State> convertJobjectToState( |
| 206 | JNIEnv *env, const fields_t &fields, jobject jstate) { |
| 207 | float volume = env->GetFloatField(jstate, fields.stVolumeId); |
| 208 | float xOffset = env->GetFloatField(jstate, fields.stXOffsetId); |
| 209 | |
| 210 | sp<VolumeShaper::State> state = new VolumeShaper::State(volume, xOffset); |
| 211 | return state; |
| 212 | } |
| 213 | |
| 214 | static jobject convertStateToJobject( |
| 215 | JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::State> &state) { |
| 216 | // prepare constructor args |
| 217 | jvalue args[2]; |
| 218 | args[0].f = (jfloat)state->getVolume(); |
| 219 | args[1].f = (jfloat)state->getXOffset(); |
| 220 | |
| 221 | jobject jstate = env->NewObjectA(fields.stClazz, fields.stConstructId, args); |
| 222 | return jstate; |
| 223 | } |
| 224 | }; |
| 225 | |
| 226 | } // namespace android |
| 227 | |
| 228 | #endif // _ANDROID_MEDIA_VOLUME_SHAPER_H_ |