blob: 6d9605a4e38fe98d41ccd7a469f5f202fd123f04 [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"
Wei Jia51b69562018-02-05 16:17:13 -080021#include "mediaplayer2/JAudioTrack.h"
Hyundo Moon660a74e2017-12-13 11:29:45 +090022
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
Hyundo Moon904183e2018-01-21 20:43:41 +0900115uint32_t JAudioTrack::latency() {
116 // TODO: Currently hard-coded as returning zero.
117 return 0;
118}
119
Hyundo Moon9b26e942017-12-14 10:46:54 +0900120status_t JAudioTrack::getPosition(uint32_t *position) {
121 if (position == NULL) {
122 return BAD_VALUE;
123 }
124
125 JNIEnv *env = AndroidRuntime::getJNIEnv();
126 jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
127 mAudioTrackCls, "getPlaybackHeadPosition", "()I");
128 *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
129
130 return NO_ERROR;
131}
132
Hyundo Moon904183e2018-01-21 20:43:41 +0900133bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
Hyundo Moonfd328172017-12-14 10:46:54 +0900134 JNIEnv *env = AndroidRuntime::getJNIEnv();
135
136 jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
137 jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
138
139 jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
140 jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
141
142 jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
143 "getTimestamp", "(Landroid/media/AudioTimestamp)B");
144 bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
145
146 if (!success) {
147 return false;
148 }
149
150 long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
151 long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
152
153 struct timespec ts;
154 const long long secondToNano = 1000000000LL; // 1E9
155 ts.tv_sec = nanoTime / secondToNano;
156 ts.tv_nsec = nanoTime % secondToNano;
157 timestamp.mTime = ts;
158 timestamp.mPosition = (uint32_t) framePosition;
159
160 return true;
161}
162
163status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
164 // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
165 // Should we do the same thing?
166 JNIEnv *env = AndroidRuntime::getJNIEnv();
167
168 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
169 jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
170 jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
171
172 jmethodID jSetAudioFallbackMode = env->GetMethodID(
173 jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
174 jPlaybackParamsObj = env->CallObjectMethod(
175 jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
176
177 jmethodID jSetAudioStretchMode = env->GetMethodID(
178 jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
179 jPlaybackParamsObj = env->CallObjectMethod(
180 jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
181
182 jmethodID jSetPitch = env->GetMethodID(
183 jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
184 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
185
186 jmethodID jSetSpeed = env->GetMethodID(
187 jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
188 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
189
190
191 // Set this Java PlaybackParams object into Java AudioTrack.
192 jmethodID jSetPlaybackParams = env->GetMethodID(
193 mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
194 env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
195 // TODO: Should we catch the Java IllegalArgumentException?
196
197 return NO_ERROR;
198}
199
200const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
201 JNIEnv *env = AndroidRuntime::getJNIEnv();
202
203 jmethodID jGetPlaybackParams = env->GetMethodID(
204 mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
205 jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
206
207 AudioPlaybackRate playbackRate;
208 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
209
210 jmethodID jGetAudioFallbackMode = env->GetMethodID(
211 jPlaybackParamsCls, "getAudioFallbackMode", "()I");
212 // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
213 // The enum is internal only, so it is not defined in PlaybackParmas.java.
214 // TODO: Is this right way to convert an int to an enum?
215 playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
216 env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
217
218 jmethodID jGetAudioStretchMode = env->GetMethodID(
219 jPlaybackParamsCls, "getAudioStretchMode", "()I");
220 playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
221 env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
222
223 jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
224 playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
225
226 jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
227 playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
228
229 return playbackRate;
230}
231
232media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
233 const sp<media::VolumeShaper::Configuration>& configuration,
234 const sp<media::VolumeShaper::Operation>& operation) {
235
236 jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
237 jobject jOperationObj = createVolumeShaperOperationObj(operation);
238
239 if (jConfigurationObj == NULL || jOperationObj == NULL) {
240 return media::VolumeShaper::Status(BAD_VALUE);
241 }
242
243 JNIEnv *env = AndroidRuntime::getJNIEnv();
244
245 jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
246 "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
247 jobject jVolumeShaperObj = env->CallObjectMethod(
248 mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
249
250 jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
251 jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
252 "(Landroid/media/VolumeShaper$Operation;)V");
253 env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
254
255 return media::VolumeShaper::Status(NO_ERROR);
256}
257
Hyundo Moon9b26e942017-12-14 10:46:54 +0900258status_t JAudioTrack::setAuxEffectSendLevel(float level) {
259 JNIEnv *env = AndroidRuntime::getJNIEnv();
260 jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
261 mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
262 int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
263 return javaToNativeStatus(result);
264}
265
266status_t JAudioTrack::attachAuxEffect(int effectId) {
267 JNIEnv *env = AndroidRuntime::getJNIEnv();
268 jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
269 int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
270 return javaToNativeStatus(result);
271}
272
273status_t JAudioTrack::setVolume(float left, float right) {
274 JNIEnv *env = AndroidRuntime::getJNIEnv();
275 // TODO: Java setStereoVolume is deprecated. Do we really need this method?
276 jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
277 int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
278 return javaToNativeStatus(result);
279}
280
281status_t JAudioTrack::setVolume(float volume) {
282 JNIEnv *env = AndroidRuntime::getJNIEnv();
283 jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
284 int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
285 return javaToNativeStatus(result);
286}
287
288status_t JAudioTrack::start() {
289 JNIEnv *env = AndroidRuntime::getJNIEnv();
290 jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
291 // TODO: Should we catch the Java IllegalStateException from play()?
292 env->CallVoidMethod(mAudioTrackObj, jPlay);
293 return NO_ERROR;
294}
295
Hyundo Moonfd328172017-12-14 10:46:54 +0900296ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
297 if (buffer == NULL) {
298 return BAD_VALUE;
299 }
300
301 JNIEnv *env = AndroidRuntime::getJNIEnv();
302 jbyteArray jAudioData = env->NewByteArray(size);
303 env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
304
305 jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
306 jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
307 jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
308
309 int writeMode = 0;
310 if (blocking) {
311 jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
312 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
313 } else {
314 jfieldID jWriteNonBlocking = env->GetStaticFieldID(
315 mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
316 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
317 }
318
319 jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
320 int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
321
322 if (result >= 0) {
323 return result;
324 } else {
325 return javaToNativeStatus(result);
326 }
327}
328
Hyundo Moon660a74e2017-12-13 11:29:45 +0900329void JAudioTrack::stop() {
330 JNIEnv *env = AndroidRuntime::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900331 jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
Hyundo Moon660a74e2017-12-13 11:29:45 +0900332 env->CallVoidMethod(mAudioTrackObj, jStop);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900333 // TODO: Should we catch IllegalStateException?
334}
335
336// TODO: Is the right implementation?
337bool JAudioTrack::stopped() const {
338 return !isPlaying();
339}
340
341void JAudioTrack::flush() {
342 JNIEnv *env = AndroidRuntime::getJNIEnv();
343 jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
344 env->CallVoidMethod(mAudioTrackObj, jFlush);
345}
346
347void JAudioTrack::pause() {
348 JNIEnv *env = AndroidRuntime::getJNIEnv();
349 jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
350 env->CallVoidMethod(mAudioTrackObj, jPause);
351 // TODO: Should we catch IllegalStateException?
352}
353
354bool JAudioTrack::isPlaying() const {
355 JNIEnv *env = AndroidRuntime::getJNIEnv();
356 jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
357 int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
358
359 // TODO: In Java AudioTrack, there is no STOPPING state.
360 // This means while stopping, isPlaying() will return different value in two class.
361 // - in existing native AudioTrack: true
362 // - in JAudioTrack: false
363 // If not okay, also modify the implementation of stopped().
364 jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
365 int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
366 return currentPlayState == statePlaying;
367}
368
369uint32_t JAudioTrack::getSampleRate() {
370 JNIEnv *env = AndroidRuntime::getJNIEnv();
371 jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
372 return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
373}
374
Hyundo Moonfd328172017-12-14 10:46:54 +0900375status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
376 if (duration == nullptr) {
377 return BAD_VALUE;
378 }
379
380 JNIEnv *env = AndroidRuntime::getJNIEnv();
381 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
382 mAudioTrackCls, "getBufferSizeInFrames", "()I");
383 int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
384
385 const double secondToMicro = 1000000LL; // 1E6
386 int sampleRate = JAudioTrack::getSampleRate();
387 float speed = JAudioTrack::getPlaybackRate().mSpeed;
388
389 *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
390 return NO_ERROR;
391}
392
Hyundo Moon9b26e942017-12-14 10:46:54 +0900393audio_format_t JAudioTrack::format() {
394 JNIEnv *env = AndroidRuntime::getJNIEnv();
395 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
396 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
397 return audioFormatToNative(javaFormat);
398}
399
Hyundo Moon904183e2018-01-21 20:43:41 +0900400status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
401{
402 String8 result;
403
404 result.append(" JAudioTrack::dump\n");
405
406 // TODO: Remove logs that includes unavailable information from below.
407// result.appendFormat(" status(%d), state(%d), session Id(%d), flags(%#x)\n",
408// mStatus, mState, mSessionId, mFlags);
409// result.appendFormat(" stream type(%d), left - right volume(%f, %f)\n",
410// (mStreamType == AUDIO_STREAM_DEFAULT) ?
411// audio_attributes_to_stream_type(&mAttributes) : mStreamType,
412// mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
413// result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n",
414// format(), mChannelMask, channelCount());
415// result.appendFormat(" sample rate(%u), original sample rate(%u), speed(%f)\n",
416// getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
417// result.appendFormat(" frame count(%zu), req. frame count(%zu)\n",
418// frameCount(), mReqFrameCount);
419// result.appendFormat(" notif. frame count(%u), req. notif. frame count(%u),"
420// " req. notif. per buff(%u)\n",
421// mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
422// result.appendFormat(" latency (%d), selected device Id(%d), routed device Id(%d)\n",
423// latency(), mSelectedDeviceId, getRoutedDeviceId());
424// result.appendFormat(" output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
425// mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
426 ::write(fd, result.string(), result.size());
427 return NO_ERROR;
428}
429
430audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
431 JNIEnv *env = AndroidRuntime::getJNIEnv();
432 jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
433 "()Landroid/media/AudioDeviceInfo;");
434 jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
435 if (env->IsSameObject(jAudioDeviceInfoObj, NULL)) {
436 return AUDIO_PORT_HANDLE_NONE;
437 }
438
439 jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
440 jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
441 jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
442 return routedDeviceId;
443}
444
Hyundo Moonfd328172017-12-14 10:46:54 +0900445jobject JAudioTrack::createVolumeShaperConfigurationObj(
446 const sp<media::VolumeShaper::Configuration>& config) {
447
448 // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
449 if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
450 return NULL;
451 }
452
453 JNIEnv *env = AndroidRuntime::getJNIEnv();
454
455 // Referenced "android_media_VolumeShaper.h".
456 jfloatArray xarray = nullptr;
457 jfloatArray yarray = nullptr;
458 if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
459 // convert curve arrays
460 xarray = env->NewFloatArray(config->size());
461 yarray = env->NewFloatArray(config->size());
462 float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
463 float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
464 float *xptr = x, *yptr = y;
465 for (const auto &pt : *config.get()) {
466 *xptr++ = pt.first;
467 *yptr++ = pt.second;
468 }
469 env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
470 env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
471 }
472
473 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
474 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
475 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
476
477 jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
478 "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
479 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
480
481 jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
482 "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
483 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
484 config->getInterpolatorType());
485
486 jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
487 "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
488 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
489
490 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
491 "()Landroid/media/VolumeShaper$Configuration;");
492 return env->CallObjectMethod(jBuilderObj, jBuild);
493}
494
495jobject JAudioTrack::createVolumeShaperOperationObj(
496 const sp<media::VolumeShaper::Operation>& operation) {
497
498 JNIEnv *env = AndroidRuntime::getJNIEnv();
499
500 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
501 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
502 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
503
504 // Set XOffset
505 jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
506 "(F)Landroid/media/VolumeShaper$Operation$Builder;");
507 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
508
509 int32_t flags = operation->getFlags();
510
511 if (operation->getReplaceId() >= 0) {
512 jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
513 "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
514 bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
515 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
516 }
517
518 if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
519 jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
520 "()Landroid/media/VolumeShaper$Operation$Builder;");
521 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
522 }
523
524 // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
525 if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
526 jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
527 "()Landroid/media/VolumeShaper$Operation$Builder;");
528 jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
529 }
530
531 if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
532 jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
533 "()Landroid/media/VolumeShaper$Operation$Builder;");
534 jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
535 }
536
537 if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
538 jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
539 "()Landroid/media/VolumeShaper$Operation$Builder;");
540 jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
541 }
542
543 // TODO: Handle error case (can it be NULL?)
544 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
545 "()Landroid/media/VolumeShaper$Operation;");
546 return env->CallObjectMethod(jBuilderObj, jBuild);
547}
548
Hyundo Moon9b26e942017-12-14 10:46:54 +0900549status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
550 switch (javaStatus) {
551 case AUDIO_JAVA_SUCCESS:
552 return NO_ERROR;
553 case AUDIO_JAVA_BAD_VALUE:
554 return BAD_VALUE;
555 case AUDIO_JAVA_INVALID_OPERATION:
556 return INVALID_OPERATION;
557 case AUDIO_JAVA_PERMISSION_DENIED:
558 return PERMISSION_DENIED;
559 case AUDIO_JAVA_NO_INIT:
560 return NO_INIT;
561 case AUDIO_JAVA_WOULD_BLOCK:
562 return WOULD_BLOCK;
563 case AUDIO_JAVA_DEAD_OBJECT:
564 return DEAD_OBJECT;
565 default:
566 return UNKNOWN_ERROR;
567 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900568}
569
570} // namespace android