blob: b228d8ba79b5f521cccd385b31797fd63c7b4755 [file] [log] [blame]
Hyundo Moon660a74e2017-12-13 11:29:45 +09001/*
2 * Copyright 2018 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#define LOG_TAG "JAudioTrack"
18
19#include "media/JAudioAttributes.h"
20#include "media/JAudioFormat.h"
21#include "media/JAudioTrack.h"
22
Hyundo Moon9b26e942017-12-14 10:46:54 +090023#include <android_media_AudioErrors.h>
Hyundo Moon660a74e2017-12-13 11:29:45 +090024#include <android_runtime/AndroidRuntime.h>
Hyundo Moon660a74e2017-12-13 11:29:45 +090025
26namespace android {
27
Hyundo Moon9b26e942017-12-14 10:46:54 +090028// TODO: Store Java class/methodID as a member variable in the class.
Hyundo Moon660a74e2017-12-13 11:29:45 +090029// TODO: Add NULL && Exception checks after every JNI call.
30JAudioTrack::JAudioTrack( // < Usages of the arguments are below >
31 audio_stream_type_t streamType, // AudioAudioAttributes
32 uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
33 audio_format_t format, // AudioFormat && bufferSizeInBytes
34 audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
35 size_t frameCount, // bufferSizeInBytes
36 audio_session_t sessionId, // AudioTrack
37 const audio_attributes_t* pAttributes, // AudioAttributes
38 float maxRequiredSpeed) { // bufferSizeInBytes
39
40 JNIEnv *env = AndroidRuntime::getJNIEnv();
41 jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
Hyundo Moon9b26e942017-12-14 10:46:54 +090042 mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
Hyundo Moon660a74e2017-12-13 11:29:45 +090043
44 maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
45
46 int bufferSizeInBytes = 0;
47 if (sampleRate == 0 || frameCount > 0) {
48 // Manually calculate buffer size.
49 bufferSizeInBytes = audio_channel_count_from_out_mask(channelMask)
50 * audio_bytes_per_sample(format) * (frameCount > 0 ? frameCount : 1);
51 } else if (sampleRate > 0) {
52 // Call Java AudioTrack::getMinBufferSize().
53 jmethodID jGetMinBufferSize =
Hyundo Moon9b26e942017-12-14 10:46:54 +090054 env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
55 bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
Hyundo Moon660a74e2017-12-13 11:29:45 +090056 sampleRate, outChannelMaskFromNative(channelMask), audioFormatFromNative(format));
57 }
58 bufferSizeInBytes = (int) (bufferSizeInBytes * maxRequiredSpeed);
59
60 // Create a Java AudioTrack object through its Builder.
61 jclass jBuilderCls = env->FindClass("android/media/AudioTrack$Builder");
62 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
63 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
64
65 jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
66 "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
67 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
68 JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
69
70 jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
71 "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
72 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
73 JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
74
75 jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
76 "(I)Landroid/media/AudioTrack$Builder;");
77 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
78
79 // We only use streaming mode of Java AudioTrack.
Hyundo Moon9b26e942017-12-14 10:46:54 +090080 jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
81 jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
Hyundo Moon660a74e2017-12-13 11:29:45 +090082 jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
83 "(I)Landroid/media/AudioTrack$Builder;");
84 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
85 transferMode /* Java AudioTrack::MODE_STREAM */);
86
87 if (sessionId != 0) {
88 jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
89 "(I)Landroid/media/AudioTrack$Builder;");
90 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
91 }
92
93 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
94 mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
95}
96
Hyundo Moon9b26e942017-12-14 10:46:54 +090097JAudioTrack::~JAudioTrack() {
98 JNIEnv *env = AndroidRuntime::getJNIEnv();
99 env->DeleteGlobalRef(mAudioTrackCls);
100}
101
102size_t JAudioTrack::frameCount() {
103 JNIEnv *env = AndroidRuntime::getJNIEnv();
104 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
105 mAudioTrackCls, "getBufferSizeInFrames", "()I");
106 return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
107}
108
109size_t JAudioTrack::channelCount() {
110 JNIEnv *env = AndroidRuntime::getJNIEnv();
111 jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
112 return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
113}
114
115status_t JAudioTrack::getPosition(uint32_t *position) {
116 if (position == NULL) {
117 return BAD_VALUE;
118 }
119
120 JNIEnv *env = AndroidRuntime::getJNIEnv();
121 jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
122 mAudioTrackCls, "getPlaybackHeadPosition", "()I");
123 *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
124
125 return NO_ERROR;
126}
127
Hyundo Moonfd328172017-12-14 10:46:54 +0900128bool JAudioTrack::getTimeStamp(AudioTimestamp& timestamp) {
129 JNIEnv *env = AndroidRuntime::getJNIEnv();
130
131 jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
132 jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
133
134 jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
135 jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
136
137 jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
138 "getTimestamp", "(Landroid/media/AudioTimestamp)B");
139 bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
140
141 if (!success) {
142 return false;
143 }
144
145 long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
146 long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
147
148 struct timespec ts;
149 const long long secondToNano = 1000000000LL; // 1E9
150 ts.tv_sec = nanoTime / secondToNano;
151 ts.tv_nsec = nanoTime % secondToNano;
152 timestamp.mTime = ts;
153 timestamp.mPosition = (uint32_t) framePosition;
154
155 return true;
156}
157
158status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
159 // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
160 // Should we do the same thing?
161 JNIEnv *env = AndroidRuntime::getJNIEnv();
162
163 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
164 jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
165 jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
166
167 jmethodID jSetAudioFallbackMode = env->GetMethodID(
168 jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
169 jPlaybackParamsObj = env->CallObjectMethod(
170 jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
171
172 jmethodID jSetAudioStretchMode = env->GetMethodID(
173 jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
174 jPlaybackParamsObj = env->CallObjectMethod(
175 jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
176
177 jmethodID jSetPitch = env->GetMethodID(
178 jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
179 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
180
181 jmethodID jSetSpeed = env->GetMethodID(
182 jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
183 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
184
185
186 // Set this Java PlaybackParams object into Java AudioTrack.
187 jmethodID jSetPlaybackParams = env->GetMethodID(
188 mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
189 env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
190 // TODO: Should we catch the Java IllegalArgumentException?
191
192 return NO_ERROR;
193}
194
195const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
196 JNIEnv *env = AndroidRuntime::getJNIEnv();
197
198 jmethodID jGetPlaybackParams = env->GetMethodID(
199 mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
200 jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
201
202 AudioPlaybackRate playbackRate;
203 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
204
205 jmethodID jGetAudioFallbackMode = env->GetMethodID(
206 jPlaybackParamsCls, "getAudioFallbackMode", "()I");
207 // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
208 // The enum is internal only, so it is not defined in PlaybackParmas.java.
209 // TODO: Is this right way to convert an int to an enum?
210 playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
211 env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
212
213 jmethodID jGetAudioStretchMode = env->GetMethodID(
214 jPlaybackParamsCls, "getAudioStretchMode", "()I");
215 playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
216 env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
217
218 jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
219 playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
220
221 jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
222 playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
223
224 return playbackRate;
225}
226
227media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
228 const sp<media::VolumeShaper::Configuration>& configuration,
229 const sp<media::VolumeShaper::Operation>& operation) {
230
231 jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
232 jobject jOperationObj = createVolumeShaperOperationObj(operation);
233
234 if (jConfigurationObj == NULL || jOperationObj == NULL) {
235 return media::VolumeShaper::Status(BAD_VALUE);
236 }
237
238 JNIEnv *env = AndroidRuntime::getJNIEnv();
239
240 jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
241 "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
242 jobject jVolumeShaperObj = env->CallObjectMethod(
243 mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
244
245 jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
246 jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
247 "(Landroid/media/VolumeShaper$Operation;)V");
248 env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
249
250 return media::VolumeShaper::Status(NO_ERROR);
251}
252
Hyundo Moon9b26e942017-12-14 10:46:54 +0900253status_t JAudioTrack::setAuxEffectSendLevel(float level) {
254 JNIEnv *env = AndroidRuntime::getJNIEnv();
255 jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
256 mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
257 int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
258 return javaToNativeStatus(result);
259}
260
261status_t JAudioTrack::attachAuxEffect(int effectId) {
262 JNIEnv *env = AndroidRuntime::getJNIEnv();
263 jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
264 int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
265 return javaToNativeStatus(result);
266}
267
268status_t JAudioTrack::setVolume(float left, float right) {
269 JNIEnv *env = AndroidRuntime::getJNIEnv();
270 // TODO: Java setStereoVolume is deprecated. Do we really need this method?
271 jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
272 int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
273 return javaToNativeStatus(result);
274}
275
276status_t JAudioTrack::setVolume(float volume) {
277 JNIEnv *env = AndroidRuntime::getJNIEnv();
278 jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
279 int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
280 return javaToNativeStatus(result);
281}
282
283status_t JAudioTrack::start() {
284 JNIEnv *env = AndroidRuntime::getJNIEnv();
285 jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
286 // TODO: Should we catch the Java IllegalStateException from play()?
287 env->CallVoidMethod(mAudioTrackObj, jPlay);
288 return NO_ERROR;
289}
290
Hyundo Moonfd328172017-12-14 10:46:54 +0900291ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
292 if (buffer == NULL) {
293 return BAD_VALUE;
294 }
295
296 JNIEnv *env = AndroidRuntime::getJNIEnv();
297 jbyteArray jAudioData = env->NewByteArray(size);
298 env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
299
300 jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
301 jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
302 jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
303
304 int writeMode = 0;
305 if (blocking) {
306 jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
307 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
308 } else {
309 jfieldID jWriteNonBlocking = env->GetStaticFieldID(
310 mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
311 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
312 }
313
314 jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
315 int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
316
317 if (result >= 0) {
318 return result;
319 } else {
320 return javaToNativeStatus(result);
321 }
322}
323
Hyundo Moon660a74e2017-12-13 11:29:45 +0900324void JAudioTrack::stop() {
325 JNIEnv *env = AndroidRuntime::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900326 jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
Hyundo Moon660a74e2017-12-13 11:29:45 +0900327 env->CallVoidMethod(mAudioTrackObj, jStop);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900328 // TODO: Should we catch IllegalStateException?
329}
330
331// TODO: Is the right implementation?
332bool JAudioTrack::stopped() const {
333 return !isPlaying();
334}
335
336void JAudioTrack::flush() {
337 JNIEnv *env = AndroidRuntime::getJNIEnv();
338 jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
339 env->CallVoidMethod(mAudioTrackObj, jFlush);
340}
341
342void JAudioTrack::pause() {
343 JNIEnv *env = AndroidRuntime::getJNIEnv();
344 jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
345 env->CallVoidMethod(mAudioTrackObj, jPause);
346 // TODO: Should we catch IllegalStateException?
347}
348
349bool JAudioTrack::isPlaying() const {
350 JNIEnv *env = AndroidRuntime::getJNIEnv();
351 jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
352 int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
353
354 // TODO: In Java AudioTrack, there is no STOPPING state.
355 // This means while stopping, isPlaying() will return different value in two class.
356 // - in existing native AudioTrack: true
357 // - in JAudioTrack: false
358 // If not okay, also modify the implementation of stopped().
359 jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
360 int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
361 return currentPlayState == statePlaying;
362}
363
364uint32_t JAudioTrack::getSampleRate() {
365 JNIEnv *env = AndroidRuntime::getJNIEnv();
366 jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
367 return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
368}
369
Hyundo Moonfd328172017-12-14 10:46:54 +0900370status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
371 if (duration == nullptr) {
372 return BAD_VALUE;
373 }
374
375 JNIEnv *env = AndroidRuntime::getJNIEnv();
376 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
377 mAudioTrackCls, "getBufferSizeInFrames", "()I");
378 int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
379
380 const double secondToMicro = 1000000LL; // 1E6
381 int sampleRate = JAudioTrack::getSampleRate();
382 float speed = JAudioTrack::getPlaybackRate().mSpeed;
383
384 *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
385 return NO_ERROR;
386}
387
Hyundo Moon9b26e942017-12-14 10:46:54 +0900388audio_format_t JAudioTrack::format() {
389 JNIEnv *env = AndroidRuntime::getJNIEnv();
390 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
391 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
392 return audioFormatToNative(javaFormat);
393}
394
Hyundo Moonfd328172017-12-14 10:46:54 +0900395jobject JAudioTrack::createVolumeShaperConfigurationObj(
396 const sp<media::VolumeShaper::Configuration>& config) {
397
398 // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
399 if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
400 return NULL;
401 }
402
403 JNIEnv *env = AndroidRuntime::getJNIEnv();
404
405 // Referenced "android_media_VolumeShaper.h".
406 jfloatArray xarray = nullptr;
407 jfloatArray yarray = nullptr;
408 if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
409 // convert curve arrays
410 xarray = env->NewFloatArray(config->size());
411 yarray = env->NewFloatArray(config->size());
412 float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
413 float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
414 float *xptr = x, *yptr = y;
415 for (const auto &pt : *config.get()) {
416 *xptr++ = pt.first;
417 *yptr++ = pt.second;
418 }
419 env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
420 env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
421 }
422
423 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
424 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
425 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
426
427 jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
428 "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
429 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
430
431 jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
432 "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
433 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
434 config->getInterpolatorType());
435
436 jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
437 "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
438 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
439
440 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
441 "()Landroid/media/VolumeShaper$Configuration;");
442 return env->CallObjectMethod(jBuilderObj, jBuild);
443}
444
445jobject JAudioTrack::createVolumeShaperOperationObj(
446 const sp<media::VolumeShaper::Operation>& operation) {
447
448 JNIEnv *env = AndroidRuntime::getJNIEnv();
449
450 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
451 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
452 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
453
454 // Set XOffset
455 jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
456 "(F)Landroid/media/VolumeShaper$Operation$Builder;");
457 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
458
459 int32_t flags = operation->getFlags();
460
461 if (operation->getReplaceId() >= 0) {
462 jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
463 "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
464 bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
465 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
466 }
467
468 if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
469 jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
470 "()Landroid/media/VolumeShaper$Operation$Builder;");
471 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
472 }
473
474 // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
475 if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
476 jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
477 "()Landroid/media/VolumeShaper$Operation$Builder;");
478 jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
479 }
480
481 if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
482 jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
483 "()Landroid/media/VolumeShaper$Operation$Builder;");
484 jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
485 }
486
487 if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
488 jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
489 "()Landroid/media/VolumeShaper$Operation$Builder;");
490 jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
491 }
492
493 // TODO: Handle error case (can it be NULL?)
494 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
495 "()Landroid/media/VolumeShaper$Operation;");
496 return env->CallObjectMethod(jBuilderObj, jBuild);
497}
498
Hyundo Moon9b26e942017-12-14 10:46:54 +0900499status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
500 switch (javaStatus) {
501 case AUDIO_JAVA_SUCCESS:
502 return NO_ERROR;
503 case AUDIO_JAVA_BAD_VALUE:
504 return BAD_VALUE;
505 case AUDIO_JAVA_INVALID_OPERATION:
506 return INVALID_OPERATION;
507 case AUDIO_JAVA_PERMISSION_DENIED:
508 return PERMISSION_DENIED;
509 case AUDIO_JAVA_NO_INIT:
510 return NO_INIT;
511 case AUDIO_JAVA_WOULD_BLOCK:
512 return WOULD_BLOCK;
513 case AUDIO_JAVA_DEAD_OBJECT:
514 return DEAD_OBJECT;
515 default:
516 return UNKNOWN_ERROR;
517 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900518}
519
520} // namespace android