blob: ac0cc5775ecba12f31ca5e50fba8a8a29019fe4f [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
Hyundo Moon42a6dec2018-01-22 19:26:47 +090035 callback_t cbf, // Offload
36 void* user, // Offload
Hyundo Moon660a74e2017-12-13 11:29:45 +090037 size_t frameCount, // bufferSizeInBytes
38 audio_session_t sessionId, // AudioTrack
39 const audio_attributes_t* pAttributes, // AudioAttributes
40 float maxRequiredSpeed) { // bufferSizeInBytes
41
42 JNIEnv *env = AndroidRuntime::getJNIEnv();
43 jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
Hyundo Moon9b26e942017-12-14 10:46:54 +090044 mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
Hyundo Moon660a74e2017-12-13 11:29:45 +090045
46 maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
47
48 int bufferSizeInBytes = 0;
49 if (sampleRate == 0 || frameCount > 0) {
50 // Manually calculate buffer size.
51 bufferSizeInBytes = audio_channel_count_from_out_mask(channelMask)
52 * audio_bytes_per_sample(format) * (frameCount > 0 ? frameCount : 1);
53 } else if (sampleRate > 0) {
54 // Call Java AudioTrack::getMinBufferSize().
55 jmethodID jGetMinBufferSize =
Hyundo Moon9b26e942017-12-14 10:46:54 +090056 env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
57 bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
Hyundo Moon660a74e2017-12-13 11:29:45 +090058 sampleRate, outChannelMaskFromNative(channelMask), audioFormatFromNative(format));
59 }
60 bufferSizeInBytes = (int) (bufferSizeInBytes * maxRequiredSpeed);
61
62 // Create a Java AudioTrack object through its Builder.
63 jclass jBuilderCls = env->FindClass("android/media/AudioTrack$Builder");
64 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
65 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
66
67 jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
68 "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
69 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
70 JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
71
72 jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
73 "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
74 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
75 JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
76
77 jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
78 "(I)Landroid/media/AudioTrack$Builder;");
79 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
80
81 // We only use streaming mode of Java AudioTrack.
Hyundo Moon9b26e942017-12-14 10:46:54 +090082 jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
83 jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
Hyundo Moon660a74e2017-12-13 11:29:45 +090084 jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
85 "(I)Landroid/media/AudioTrack$Builder;");
86 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
87 transferMode /* Java AudioTrack::MODE_STREAM */);
88
89 if (sessionId != 0) {
90 jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
91 "(I)Landroid/media/AudioTrack$Builder;");
92 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
93 }
94
Hyundo Moon42a6dec2018-01-22 19:26:47 +090095 if (cbf != NULL) {
96 jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
97 "(Z)Landroid/media/AudioTrack$Builder;");
98 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
99 mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
100 }
101
Hyundo Moon660a74e2017-12-13 11:29:45 +0900102 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
103 mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900104
105 if (cbf != NULL) {
106 // Set offload mode callback
107 jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
108 jobject jExecutorObj = createCallbackExecutor();
109 jmethodID jSetStreamEventCallback = env->GetMethodID(
110 jAudioTrackCls,
111 "setStreamEventCallback",
112 "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
113 env->CallVoidMethod(
114 mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
115 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900116}
117
Hyundo Moon9b26e942017-12-14 10:46:54 +0900118JAudioTrack::~JAudioTrack() {
119 JNIEnv *env = AndroidRuntime::getJNIEnv();
120 env->DeleteGlobalRef(mAudioTrackCls);
121}
122
123size_t JAudioTrack::frameCount() {
124 JNIEnv *env = AndroidRuntime::getJNIEnv();
125 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
126 mAudioTrackCls, "getBufferSizeInFrames", "()I");
127 return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
128}
129
130size_t JAudioTrack::channelCount() {
131 JNIEnv *env = AndroidRuntime::getJNIEnv();
132 jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
133 return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
134}
135
Hyundo Moon904183e2018-01-21 20:43:41 +0900136uint32_t JAudioTrack::latency() {
137 // TODO: Currently hard-coded as returning zero.
138 return 0;
139}
140
Hyundo Moon9b26e942017-12-14 10:46:54 +0900141status_t JAudioTrack::getPosition(uint32_t *position) {
142 if (position == NULL) {
143 return BAD_VALUE;
144 }
145
146 JNIEnv *env = AndroidRuntime::getJNIEnv();
147 jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
148 mAudioTrackCls, "getPlaybackHeadPosition", "()I");
149 *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
150
151 return NO_ERROR;
152}
153
Hyundo Moon904183e2018-01-21 20:43:41 +0900154bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
Hyundo Moonfd328172017-12-14 10:46:54 +0900155 JNIEnv *env = AndroidRuntime::getJNIEnv();
156
157 jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
158 jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
159
160 jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
161 jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
162
163 jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
164 "getTimestamp", "(Landroid/media/AudioTimestamp)B");
165 bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
166
167 if (!success) {
168 return false;
169 }
170
171 long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
172 long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
173
174 struct timespec ts;
175 const long long secondToNano = 1000000000LL; // 1E9
176 ts.tv_sec = nanoTime / secondToNano;
177 ts.tv_nsec = nanoTime % secondToNano;
178 timestamp.mTime = ts;
179 timestamp.mPosition = (uint32_t) framePosition;
180
181 return true;
182}
183
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900184status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
185 // TODO: Implement this after appropriate Java AudioTrack method is available.
186 return NO_ERROR;
187}
188
Hyundo Moonfd328172017-12-14 10:46:54 +0900189status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
190 // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
191 // Should we do the same thing?
192 JNIEnv *env = AndroidRuntime::getJNIEnv();
193
194 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
195 jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
196 jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
197
198 jmethodID jSetAudioFallbackMode = env->GetMethodID(
199 jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
200 jPlaybackParamsObj = env->CallObjectMethod(
201 jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
202
203 jmethodID jSetAudioStretchMode = env->GetMethodID(
204 jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
205 jPlaybackParamsObj = env->CallObjectMethod(
206 jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
207
208 jmethodID jSetPitch = env->GetMethodID(
209 jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
210 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
211
212 jmethodID jSetSpeed = env->GetMethodID(
213 jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
214 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
215
216
217 // Set this Java PlaybackParams object into Java AudioTrack.
218 jmethodID jSetPlaybackParams = env->GetMethodID(
219 mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
220 env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
221 // TODO: Should we catch the Java IllegalArgumentException?
222
223 return NO_ERROR;
224}
225
226const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
227 JNIEnv *env = AndroidRuntime::getJNIEnv();
228
229 jmethodID jGetPlaybackParams = env->GetMethodID(
230 mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
231 jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
232
233 AudioPlaybackRate playbackRate;
234 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
235
236 jmethodID jGetAudioFallbackMode = env->GetMethodID(
237 jPlaybackParamsCls, "getAudioFallbackMode", "()I");
238 // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
239 // The enum is internal only, so it is not defined in PlaybackParmas.java.
240 // TODO: Is this right way to convert an int to an enum?
241 playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
242 env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
243
244 jmethodID jGetAudioStretchMode = env->GetMethodID(
245 jPlaybackParamsCls, "getAudioStretchMode", "()I");
246 playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
247 env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
248
249 jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
250 playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
251
252 jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
253 playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
254
255 return playbackRate;
256}
257
258media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
259 const sp<media::VolumeShaper::Configuration>& configuration,
260 const sp<media::VolumeShaper::Operation>& operation) {
261
262 jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
263 jobject jOperationObj = createVolumeShaperOperationObj(operation);
264
265 if (jConfigurationObj == NULL || jOperationObj == NULL) {
266 return media::VolumeShaper::Status(BAD_VALUE);
267 }
268
269 JNIEnv *env = AndroidRuntime::getJNIEnv();
270
271 jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
272 "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
273 jobject jVolumeShaperObj = env->CallObjectMethod(
274 mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
275
276 jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
277 jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
278 "(Landroid/media/VolumeShaper$Operation;)V");
279 env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
280
281 return media::VolumeShaper::Status(NO_ERROR);
282}
283
Hyundo Moon9b26e942017-12-14 10:46:54 +0900284status_t JAudioTrack::setAuxEffectSendLevel(float level) {
285 JNIEnv *env = AndroidRuntime::getJNIEnv();
286 jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
287 mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
288 int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
289 return javaToNativeStatus(result);
290}
291
292status_t JAudioTrack::attachAuxEffect(int effectId) {
293 JNIEnv *env = AndroidRuntime::getJNIEnv();
294 jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
295 int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
296 return javaToNativeStatus(result);
297}
298
299status_t JAudioTrack::setVolume(float left, float right) {
300 JNIEnv *env = AndroidRuntime::getJNIEnv();
301 // TODO: Java setStereoVolume is deprecated. Do we really need this method?
302 jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
303 int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
304 return javaToNativeStatus(result);
305}
306
307status_t JAudioTrack::setVolume(float volume) {
308 JNIEnv *env = AndroidRuntime::getJNIEnv();
309 jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
310 int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
311 return javaToNativeStatus(result);
312}
313
314status_t JAudioTrack::start() {
315 JNIEnv *env = AndroidRuntime::getJNIEnv();
316 jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
317 // TODO: Should we catch the Java IllegalStateException from play()?
318 env->CallVoidMethod(mAudioTrackObj, jPlay);
319 return NO_ERROR;
320}
321
Hyundo Moonfd328172017-12-14 10:46:54 +0900322ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
323 if (buffer == NULL) {
324 return BAD_VALUE;
325 }
326
327 JNIEnv *env = AndroidRuntime::getJNIEnv();
328 jbyteArray jAudioData = env->NewByteArray(size);
329 env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
330
331 jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
332 jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
333 jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
334
335 int writeMode = 0;
336 if (blocking) {
337 jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
338 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
339 } else {
340 jfieldID jWriteNonBlocking = env->GetStaticFieldID(
341 mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
342 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
343 }
344
345 jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
346 int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
347
348 if (result >= 0) {
349 return result;
350 } else {
351 return javaToNativeStatus(result);
352 }
353}
354
Hyundo Moon660a74e2017-12-13 11:29:45 +0900355void JAudioTrack::stop() {
356 JNIEnv *env = AndroidRuntime::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900357 jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
Hyundo Moon660a74e2017-12-13 11:29:45 +0900358 env->CallVoidMethod(mAudioTrackObj, jStop);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900359 // TODO: Should we catch IllegalStateException?
360}
361
362// TODO: Is the right implementation?
363bool JAudioTrack::stopped() const {
364 return !isPlaying();
365}
366
367void JAudioTrack::flush() {
368 JNIEnv *env = AndroidRuntime::getJNIEnv();
369 jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
370 env->CallVoidMethod(mAudioTrackObj, jFlush);
371}
372
373void JAudioTrack::pause() {
374 JNIEnv *env = AndroidRuntime::getJNIEnv();
375 jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
376 env->CallVoidMethod(mAudioTrackObj, jPause);
377 // TODO: Should we catch IllegalStateException?
378}
379
380bool JAudioTrack::isPlaying() const {
381 JNIEnv *env = AndroidRuntime::getJNIEnv();
382 jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
383 int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
384
385 // TODO: In Java AudioTrack, there is no STOPPING state.
386 // This means while stopping, isPlaying() will return different value in two class.
387 // - in existing native AudioTrack: true
388 // - in JAudioTrack: false
389 // If not okay, also modify the implementation of stopped().
390 jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
391 int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
392 return currentPlayState == statePlaying;
393}
394
395uint32_t JAudioTrack::getSampleRate() {
396 JNIEnv *env = AndroidRuntime::getJNIEnv();
397 jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
398 return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
399}
400
Hyundo Moonfd328172017-12-14 10:46:54 +0900401status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
402 if (duration == nullptr) {
403 return BAD_VALUE;
404 }
405
406 JNIEnv *env = AndroidRuntime::getJNIEnv();
407 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
408 mAudioTrackCls, "getBufferSizeInFrames", "()I");
409 int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
410
411 const double secondToMicro = 1000000LL; // 1E6
412 int sampleRate = JAudioTrack::getSampleRate();
413 float speed = JAudioTrack::getPlaybackRate().mSpeed;
414
415 *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
416 return NO_ERROR;
417}
418
Hyundo Moon9b26e942017-12-14 10:46:54 +0900419audio_format_t JAudioTrack::format() {
420 JNIEnv *env = AndroidRuntime::getJNIEnv();
421 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
422 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
423 return audioFormatToNative(javaFormat);
424}
425
Hyundo Moon904183e2018-01-21 20:43:41 +0900426status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
427{
428 String8 result;
429
430 result.append(" JAudioTrack::dump\n");
431
432 // TODO: Remove logs that includes unavailable information from below.
433// result.appendFormat(" status(%d), state(%d), session Id(%d), flags(%#x)\n",
434// mStatus, mState, mSessionId, mFlags);
435// result.appendFormat(" stream type(%d), left - right volume(%f, %f)\n",
436// (mStreamType == AUDIO_STREAM_DEFAULT) ?
437// audio_attributes_to_stream_type(&mAttributes) : mStreamType,
438// mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
439// result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n",
440// format(), mChannelMask, channelCount());
441// result.appendFormat(" sample rate(%u), original sample rate(%u), speed(%f)\n",
442// getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
443// result.appendFormat(" frame count(%zu), req. frame count(%zu)\n",
444// frameCount(), mReqFrameCount);
445// result.appendFormat(" notif. frame count(%u), req. notif. frame count(%u),"
446// " req. notif. per buff(%u)\n",
447// mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
448// result.appendFormat(" latency (%d), selected device Id(%d), routed device Id(%d)\n",
449// latency(), mSelectedDeviceId, getRoutedDeviceId());
450// result.appendFormat(" output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
451// mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
452 ::write(fd, result.string(), result.size());
453 return NO_ERROR;
454}
455
456audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
457 JNIEnv *env = AndroidRuntime::getJNIEnv();
458 jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
459 "()Landroid/media/AudioDeviceInfo;");
460 jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
461 if (env->IsSameObject(jAudioDeviceInfoObj, NULL)) {
462 return AUDIO_PORT_HANDLE_NONE;
463 }
464
465 jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
466 jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
467 jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
468 return routedDeviceId;
469}
470
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900471audio_session_t JAudioTrack::getAudioSessionId() {
472 JNIEnv *env = AndroidRuntime::getJNIEnv();
473 jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
474 jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
475 return (audio_session_t) sessionId;
476}
477
478status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
479 JNIEnv *env = AndroidRuntime::getJNIEnv();
480 jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
481 jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
482 jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
483 jboolean result = env->CallStaticBooleanMethod(
484 jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
485 return result == true ? NO_ERROR : BAD_VALUE;
486}
487
488status_t JAudioTrack::pendingDuration(int32_t *msec) {
489 if (msec == nullptr) {
490 return BAD_VALUE;
491 }
492
493 bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
494 if (!isPurePcmData) {
495 return INVALID_OPERATION;
496 }
497
498 // TODO: Need to know the difference btw. client and server time.
499 // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
500 // (copied from AudioTrack.cpp)
501
502// ExtendedTimestamp ets;
503// ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
504// if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
505// int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
506// - ets.mPosition[location];
507// if (diff < 0) {
508// *msec = 0;
509// } else {
510// // ms is the playback time by frames
511// int64_t ms = (int64_t)((double)diff * 1000 /
512// ((double)mSampleRate * mPlaybackRate.mSpeed));
513// // clockdiff is the timestamp age (negative)
514// int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
515// ets.mTimeNs[location]
516// + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
517// - systemTime(SYSTEM_TIME_MONOTONIC);
518//
519// //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
520// static const int NANOS_PER_MILLIS = 1000000;
521// *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
522// }
523// return NO_ERROR;
524// }
525
526 return NO_ERROR;
527}
528
529status_t JAudioTrack::addAudioDeviceCallback(
530 const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
531 // TODO: Implement this after appropriate Java AudioTrack method is available.
532 return NO_ERROR;
533}
534
535status_t JAudioTrack::removeAudioDeviceCallback(
536 const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
537 // TODO: Implement this after appropriate Java AudioTrack method is available.
538 return NO_ERROR;
539}
540
541/////////////////////////////////////////////////////////////
542/// Private method begins ///
543/////////////////////////////////////////////////////////////
544
Hyundo Moonfd328172017-12-14 10:46:54 +0900545jobject JAudioTrack::createVolumeShaperConfigurationObj(
546 const sp<media::VolumeShaper::Configuration>& config) {
547
548 // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
549 if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
550 return NULL;
551 }
552
553 JNIEnv *env = AndroidRuntime::getJNIEnv();
554
555 // Referenced "android_media_VolumeShaper.h".
556 jfloatArray xarray = nullptr;
557 jfloatArray yarray = nullptr;
558 if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
559 // convert curve arrays
560 xarray = env->NewFloatArray(config->size());
561 yarray = env->NewFloatArray(config->size());
562 float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
563 float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
564 float *xptr = x, *yptr = y;
565 for (const auto &pt : *config.get()) {
566 *xptr++ = pt.first;
567 *yptr++ = pt.second;
568 }
569 env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
570 env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
571 }
572
573 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
574 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
575 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
576
577 jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
578 "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
579 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
580
581 jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
582 "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
583 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
584 config->getInterpolatorType());
585
586 jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
587 "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
588 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
589
590 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
591 "()Landroid/media/VolumeShaper$Configuration;");
592 return env->CallObjectMethod(jBuilderObj, jBuild);
593}
594
595jobject JAudioTrack::createVolumeShaperOperationObj(
596 const sp<media::VolumeShaper::Operation>& operation) {
597
598 JNIEnv *env = AndroidRuntime::getJNIEnv();
599
600 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
601 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
602 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
603
604 // Set XOffset
605 jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
606 "(F)Landroid/media/VolumeShaper$Operation$Builder;");
607 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
608
609 int32_t flags = operation->getFlags();
610
611 if (operation->getReplaceId() >= 0) {
612 jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
613 "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
614 bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
615 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
616 }
617
618 if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
619 jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
620 "()Landroid/media/VolumeShaper$Operation$Builder;");
621 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
622 }
623
624 // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
625 if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
626 jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
627 "()Landroid/media/VolumeShaper$Operation$Builder;");
628 jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
629 }
630
631 if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
632 jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
633 "()Landroid/media/VolumeShaper$Operation$Builder;");
634 jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
635 }
636
637 if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
638 jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
639 "()Landroid/media/VolumeShaper$Operation$Builder;");
640 jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
641 }
642
643 // TODO: Handle error case (can it be NULL?)
644 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
645 "()Landroid/media/VolumeShaper$Operation;");
646 return env->CallObjectMethod(jBuilderObj, jBuild);
647}
648
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900649jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
650 JNIEnv *env = AndroidRuntime::getJNIEnv();
651 jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
652 jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
653 jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
654 return jCallbackObj;
655}
656
657jobject JAudioTrack::createCallbackExecutor() {
658 JNIEnv *env = AndroidRuntime::getJNIEnv();
659 jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
660 jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
661 "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
662 jobject jSingleThreadExecutorObj =
663 env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
664 return jSingleThreadExecutorObj;
665}
666
Hyundo Moon9b26e942017-12-14 10:46:54 +0900667status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
668 switch (javaStatus) {
669 case AUDIO_JAVA_SUCCESS:
670 return NO_ERROR;
671 case AUDIO_JAVA_BAD_VALUE:
672 return BAD_VALUE;
673 case AUDIO_JAVA_INVALID_OPERATION:
674 return INVALID_OPERATION;
675 case AUDIO_JAVA_PERMISSION_DENIED:
676 return PERMISSION_DENIED;
677 case AUDIO_JAVA_NO_INIT:
678 return NO_INIT;
679 case AUDIO_JAVA_WOULD_BLOCK:
680 return WOULD_BLOCK;
681 case AUDIO_JAVA_DEAD_OBJECT:
682 return DEAD_OBJECT;
683 default:
684 return UNKNOWN_ERROR;
685 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900686}
687
688} // namespace android