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 
24 using media::VolumeShaper;
25 
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;
34         jfieldID  coOptionFlagsId;
35         jfieldID  coDurationMsId;
36         jfieldID  coInterpolatorTypeId;
37         jfieldID  coTimesId;
38         jfieldID  coVolumesId;
39 
40         // VolumeShaper.Operation
41         jclass    opClazz;
42         jmethodID opConstructId;
43         jfieldID  opFlagsId;
44         jfieldID  opReplaceIdId;
45         jfieldID  opXOffsetId;
46 
47         // VolumeShaper.State
48         jclass    stClazz;
49         jmethodID stConstructId;
50         jfieldID  stVolumeId;
51         jfieldID  stXOffsetId;
52 
initVolumeShaperHelper::fields_t53         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             }
62             coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIDI[F[F)V");
63             coTypeId = env->GetFieldID(coClazz, "mType", "I");
64             coIdId = env->GetFieldID(coClazz, "mId", "I");
65             coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
66             coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
67             coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
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             }
80             opConstructId = env->GetMethodID(opClazz, "<init>", "(IIF)V");
81             opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
82             opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
83             opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F");
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 
exitVolumeShaperHelper::fields_t100         void exit(JNIEnv *env) {
101             env->DeleteGlobalRef(coClazz);
102             coClazz = nullptr;
103         }
104     };
105 
convertJobjectToConfigurationVolumeShaperHelper106     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) {
115             configuration->setOptionFlags(
116                 (VolumeShaper::Configuration::OptionFlag)
117                 env->GetIntField(jshaper, fields.coOptionFlagsId));
118             configuration->setDurationMs(
119                     (double)env->GetDoubleField(jshaper, fields.coDurationMsId));
120             configuration->setInterpolatorType(
121                 (VolumeShaper::Configuration::InterpolatorType)
122                 env->GetIntField(jshaper, fields.coInterpolatorTypeId));
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 
convertVolumeShaperToJobjectVolumeShaperHelper148     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();
172         args[2].i = (jint)configuration->getOptionFlags();
173         args[3].d = (jdouble)configuration->getDurationMs();
174         args[4].i = (jint)configuration->getInterpolatorType();
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 
convertJobjectToOperationVolumeShaperHelper181     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);
186         float xOffset = env->GetFloatField(joperation, fields.opXOffsetId);
187 
188         sp<VolumeShaper::Operation> operation =
189                 new VolumeShaper::Operation(flags, replaceId, xOffset);
190         return operation;
191     }
192 
convertOperationToJobjectVolumeShaperHelper193     static jobject convertOperationToJobject(
194             JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
195         // prepare constructor args
196         jvalue args[3];
197         args[0].i = (jint)operation->getFlags();
198         args[1].i = (jint)operation->getReplaceId();
199         args[2].f = (jfloat)operation->getXOffset();
200 
201         jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
202         return joperation;
203     }
204 
convertJobjectToStateVolumeShaperHelper205     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 
convertStateToJobjectVolumeShaperHelper214     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_
229