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
22namespace android {
23
24// This entire class is inline as it is used from both core and media
25struct VolumeShaperHelper {
26    struct fields_t {
27        // VolumeShaper.Configuration
28        jclass    coClazz;
29        jmethodID coConstructId;
30        jfieldID  coTypeId;
31        jfieldID  coIdId;
32        jfieldID  coOptionFlagsId;
33        jfieldID  coDurationMsId;
34        jfieldID  coInterpolatorTypeId;
35        jfieldID  coTimesId;
36        jfieldID  coVolumesId;
37
38        // VolumeShaper.Operation
39        jclass    opClazz;
40        jmethodID opConstructId;
41        jfieldID  opFlagsId;
42        jfieldID  opReplaceIdId;
43        jfieldID  opXOffsetId;
44
45        // VolumeShaper.State
46        jclass    stClazz;
47        jmethodID stConstructId;
48        jfieldID  stVolumeId;
49        jfieldID  stXOffsetId;
50
51        void init(JNIEnv *env) {
52            jclass lclazz = env->FindClass("android/media/VolumeShaper$Configuration");
53            if (lclazz == nullptr) {
54                return;
55            }
56            coClazz = (jclass)env->NewGlobalRef(lclazz);
57            if (coClazz == nullptr) {
58                return;
59            }
60            coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIDI[F[F)V");
61            coTypeId = env->GetFieldID(coClazz, "mType", "I");
62            coIdId = env->GetFieldID(coClazz, "mId", "I");
63            coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
64            coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
65            coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
66            coTimesId = env->GetFieldID(coClazz, "mTimes", "[F");
67            coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F");
68            env->DeleteLocalRef(lclazz);
69
70            lclazz = env->FindClass("android/media/VolumeShaper$Operation");
71            if (lclazz == nullptr) {
72                return;
73            }
74            opClazz = (jclass)env->NewGlobalRef(lclazz);
75            if (opClazz == nullptr) {
76                return;
77            }
78            opConstructId = env->GetMethodID(opClazz, "<init>", "(IIF)V");
79            opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
80            opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
81            opXOffsetId = env->GetFieldID(opClazz, "mXOffset", "F");
82            env->DeleteLocalRef(lclazz);
83
84            lclazz = env->FindClass("android/media/VolumeShaper$State");
85            if (lclazz == nullptr) {
86                return;
87            }
88            stClazz = (jclass)env->NewGlobalRef(lclazz);
89            if (stClazz == nullptr) {
90                return;
91            }
92            stConstructId = env->GetMethodID(stClazz, "<init>", "(FF)V");
93            stVolumeId = env->GetFieldID(stClazz, "mVolume", "F");
94            stXOffsetId = env->GetFieldID(stClazz, "mXOffset", "F");
95            env->DeleteLocalRef(lclazz);
96        }
97
98        void exit(JNIEnv *env) {
99            env->DeleteGlobalRef(coClazz);
100            coClazz = nullptr;
101        }
102    };
103
104    static sp<VolumeShaper::Configuration> convertJobjectToConfiguration(
105            JNIEnv *env, const fields_t &fields, jobject jshaper) {
106        sp<VolumeShaper::Configuration> configuration = new VolumeShaper::Configuration();
107
108        configuration->setType(
109            (VolumeShaper::Configuration::Type)env->GetIntField(jshaper, fields.coTypeId));
110        configuration->setId(
111            (int)env->GetIntField(jshaper, fields.coIdId));
112        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
113            configuration->setOptionFlags(
114                (VolumeShaper::Configuration::OptionFlag)
115                env->GetIntField(jshaper, fields.coOptionFlagsId));
116            configuration->setDurationMs(
117                    (double)env->GetDoubleField(jshaper, fields.coDurationMsId));
118            configuration->setInterpolatorType(
119                (VolumeShaper::Configuration::InterpolatorType)
120                env->GetIntField(jshaper, fields.coInterpolatorTypeId));
121
122            // convert point arrays
123            jobject xobj = env->GetObjectField(jshaper, fields.coTimesId);
124            jfloatArray *xarray = reinterpret_cast<jfloatArray*>(&xobj);
125            jsize xlen = env->GetArrayLength(*xarray);
126            /* const */ float * const x =
127                    env->GetFloatArrayElements(*xarray, nullptr /* isCopy */);
128            jobject yobj = env->GetObjectField(jshaper, fields.coVolumesId);
129            jfloatArray *yarray = reinterpret_cast<jfloatArray*>(&yobj);
130            jsize ylen = env->GetArrayLength(*yarray);
131            /* const */ float * const y =
132                    env->GetFloatArrayElements(*yarray, nullptr /* isCopy */);
133            if (xlen != ylen) {
134                ALOGE("array size must match");
135                return nullptr;
136            }
137            for (jsize i = 0; i < xlen; ++i) {
138                configuration->emplace(x[i], y[i]);
139            }
140            env->ReleaseFloatArrayElements(*xarray, x, JNI_ABORT); // no need to copy back
141            env->ReleaseFloatArrayElements(*yarray, y, JNI_ABORT);
142        }
143        return configuration;
144    }
145
146    static jobject convertVolumeShaperToJobject(
147            JNIEnv *env, const fields_t &fields,
148            const sp<VolumeShaper::Configuration> &configuration) {
149        jfloatArray xarray = nullptr;
150        jfloatArray yarray = nullptr;
151        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
152            // convert curve arrays
153            jfloatArray xarray = env->NewFloatArray(configuration->size());
154            jfloatArray yarray = env->NewFloatArray(configuration->size());
155            float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
156            float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
157            float *xptr = x, *yptr = y;
158            for (const auto &pt : *configuration.get()) {
159                *xptr++ = pt.first;
160                *yptr++ = pt.second;
161            }
162            env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
163            env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
164        }
165
166        // prepare constructor args
167        jvalue args[7];
168        args[0].i = (jint)configuration->getType();
169        args[1].i = (jint)configuration->getId();
170        args[2].i = (jint)configuration->getOptionFlags();
171        args[3].d = (jdouble)configuration->getDurationMs();
172        args[4].i = (jint)configuration->getInterpolatorType();
173        args[5].l = xarray;
174        args[6].l = yarray;
175        jobject jshaper = env->NewObjectA(fields.coClazz, fields.coConstructId, args);
176        return jshaper;
177    }
178
179    static sp<VolumeShaper::Operation> convertJobjectToOperation(
180            JNIEnv *env, const fields_t &fields, jobject joperation) {
181        VolumeShaper::Operation::Flag flags =
182            (VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId);
183        int replaceId = env->GetIntField(joperation, fields.opReplaceIdId);
184        float xOffset = env->GetFloatField(joperation, fields.opXOffsetId);
185
186        sp<VolumeShaper::Operation> operation =
187                new VolumeShaper::Operation(flags, replaceId, xOffset);
188        return operation;
189    }
190
191    static jobject convertOperationToJobject(
192            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
193        // prepare constructor args
194        jvalue args[3];
195        args[0].i = (jint)operation->getFlags();
196        args[1].i = (jint)operation->getReplaceId();
197        args[2].f = (jfloat)operation->getXOffset();
198
199        jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
200        return joperation;
201    }
202
203    static sp<VolumeShaper::State> convertJobjectToState(
204            JNIEnv *env, const fields_t &fields, jobject jstate) {
205        float volume = env->GetFloatField(jstate, fields.stVolumeId);
206        float xOffset = env->GetFloatField(jstate, fields.stXOffsetId);
207
208        sp<VolumeShaper::State> state = new VolumeShaper::State(volume, xOffset);
209        return state;
210    }
211
212    static jobject convertStateToJobject(
213            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::State> &state) {
214        // prepare constructor args
215        jvalue args[2];
216        args[0].f = (jfloat)state->getVolume();
217        args[1].f = (jfloat)state->getXOffset();
218
219        jobject jstate = env->NewObjectA(fields.stClazz, fields.stConstructId, args);
220        return jstate;
221    }
222};
223
224}  // namespace android
225
226#endif // _ANDROID_MEDIA_VOLUME_SHAPER_H_
227