1 /*
2 * Copyright (C) 2008 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 //#define LOG_NDEBUG 0
17
18 #define LOG_TAG "AudioTrack-JNI"
19
20 #include "android_media_AudioTrack.h"
21
22 #include <nativehelper/JNIHelp.h>
23 #include "core_jni_helpers.h"
24
25 #include <utils/Log.h>
26 #include <media/AudioParameter.h>
27 #include <media/AudioSystem.h>
28 #include <media/AudioTrack.h>
29
30 #include <android-base/macros.h>
31 #include <binder/MemoryHeapBase.h>
32 #include <binder/MemoryBase.h>
33
34 #include "android_media_AudioFormat.h"
35 #include "android_media_AudioErrors.h"
36 #include "android_media_MediaMetricsJNI.h"
37 #include "android_media_PlaybackParams.h"
38 #include "android_media_DeviceCallback.h"
39 #include "android_media_VolumeShaper.h"
40 #include "android_media_AudioAttributes.h"
41
42 #include <cinttypes>
43
44 // ----------------------------------------------------------------------------
45
46 using namespace android;
47
48 using ::android::media::VolumeShaper;
49
50 // ----------------------------------------------------------------------------
51 static const char* const kClassPathName = "android/media/AudioTrack";
52
53 struct audio_track_fields_t {
54 // these fields provide access from C++ to the...
55 jmethodID postNativeEventInJava; //... event post callback method
56 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
57 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
58 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
59 };
60 static audio_track_fields_t javaAudioTrackFields;
61 static PlaybackParams::fields_t gPlaybackParamsFields;
62 static VolumeShaperHelper::fields_t gVolumeShaperFields;
63
64 struct audiotrack_callback_cookie {
65 jclass audioTrack_class;
66 jobject audioTrack_ref;
67 bool busy;
68 Condition cond;
69 bool isOffload;
70 };
71
72 // keep these values in sync with AudioTrack.java
73 #define MODE_STATIC 0
74 #define MODE_STREAM 1
75
76 // ----------------------------------------------------------------------------
77 class AudioTrackJniStorage {
78 public:
79 sp<MemoryHeapBase> mMemHeap;
80 sp<MemoryBase> mMemBase;
81 audiotrack_callback_cookie mCallbackData;
82 sp<JNIDeviceCallback> mDeviceCallback;
83
AudioTrackJniStorage()84 AudioTrackJniStorage() {
85 mCallbackData.audioTrack_class = 0;
86 mCallbackData.audioTrack_ref = 0;
87 mCallbackData.isOffload = false;
88 }
89
~AudioTrackJniStorage()90 ~AudioTrackJniStorage() {
91 mMemBase.clear();
92 mMemHeap.clear();
93 }
94
allocSharedMem(int sizeInBytes)95 bool allocSharedMem(int sizeInBytes) {
96 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
97 if (mMemHeap->getHeapID() < 0) {
98 return false;
99 }
100 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
101 return true;
102 }
103 };
104
105 static Mutex sLock;
106 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
107
108 // ----------------------------------------------------------------------------
109 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100
110
111 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM (-16)
112 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK (-17)
113 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT (-18)
114 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19)
115 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20)
116
117 // ----------------------------------------------------------------------------
audioCallback(int event,void * user,void * info)118 static void audioCallback(int event, void* user, void *info) {
119
120 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
121 {
122 Mutex::Autolock l(sLock);
123 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
124 return;
125 }
126 callbackInfo->busy = true;
127 }
128
129 // used as default argument when event callback doesn't have any, or number of
130 // frames for EVENT_CAN_WRITE_MORE_DATA
131 int arg = 0;
132 bool postEvent = false;
133 switch (event) {
134 // Offload only events
135 case AudioTrack::EVENT_CAN_WRITE_MORE_DATA:
136 // this event will read the info return parameter of the callback:
137 // for JNI offload, use the returned size to indicate:
138 // 1/ no data is returned through callback, as it's all done through write()
139 // 2/ do not wait as AudioTrack does when it receives 0 bytes
140 if (callbackInfo->isOffload) {
141 AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info;
142 const size_t availableForWrite = pBuffer->size;
143 arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
144 pBuffer->size = 0;
145 }
146 FALLTHROUGH_INTENDED;
147 case AudioTrack::EVENT_STREAM_END:
148 case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down
149 if (callbackInfo->isOffload) {
150 postEvent = true;
151 }
152 break;
153
154 // PCM and offload events
155 case AudioTrack::EVENT_MARKER:
156 case AudioTrack::EVENT_NEW_POS:
157 postEvent = true;
158 break;
159 default:
160 // event will not be posted
161 break;
162 }
163
164 if (postEvent) {
165 JNIEnv *env = AndroidRuntime::getJNIEnv();
166 if (env != NULL) {
167 env->CallStaticVoidMethod(
168 callbackInfo->audioTrack_class,
169 javaAudioTrackFields.postNativeEventInJava,
170 callbackInfo->audioTrack_ref, event, arg, 0, NULL);
171 if (env->ExceptionCheck()) {
172 env->ExceptionDescribe();
173 env->ExceptionClear();
174 }
175 }
176 }
177
178 {
179 Mutex::Autolock l(sLock);
180 callbackInfo->busy = false;
181 callbackInfo->cond.broadcast();
182 }
183 }
184
185
186 // ----------------------------------------------------------------------------
getAudioTrack(JNIEnv * env,jobject thiz)187 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
188 {
189 Mutex::Autolock l(sLock);
190 AudioTrack* const at =
191 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
192 return sp<AudioTrack>(at);
193 }
194
setAudioTrack(JNIEnv * env,jobject thiz,const sp<AudioTrack> & at)195 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
196 {
197 Mutex::Autolock l(sLock);
198 sp<AudioTrack> old =
199 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
200 if (at.get()) {
201 at->incStrong((void*)setAudioTrack);
202 }
203 if (old != 0) {
204 old->decStrong((void*)setAudioTrack);
205 }
206 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
207 return old;
208 }
209
210 // ----------------------------------------------------------------------------
android_media_AudioTrack_getAudioTrack(JNIEnv * env,jobject audioTrackObj)211 sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
212 return getAudioTrack(env, audioTrackObj);
213 }
214
215 // ----------------------------------------------------------------------------
216 static jint
android_media_AudioTrack_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelPositionMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jint memoryMode,jintArray jSession,jlong nativeAudioTrack,jboolean offload)217 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
218 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
219 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
220 jlong nativeAudioTrack, jboolean offload) {
221
222 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
223 " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
224 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
225 nativeAudioTrack, offload);
226
227 sp<AudioTrack> lpTrack = 0;
228
229 if (jSession == NULL) {
230 ALOGE("Error creating AudioTrack: invalid session ID pointer");
231 return (jint) AUDIO_JAVA_ERROR;
232 }
233
234 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
235 if (nSession == NULL) {
236 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
237 return (jint) AUDIO_JAVA_ERROR;
238 }
239 audio_session_t sessionId = (audio_session_t) nSession[0];
240 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
241 nSession = NULL;
242
243 AudioTrackJniStorage* lpJniStorage = NULL;
244
245 jclass clazz = env->GetObjectClass(thiz);
246 if (clazz == NULL) {
247 ALOGE("Can't find %s when setting up callback.", kClassPathName);
248 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
249 }
250
251 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
252 if (nativeAudioTrack == 0) {
253 if (jaa == 0) {
254 ALOGE("Error creating AudioTrack: invalid audio attributes");
255 return (jint) AUDIO_JAVA_ERROR;
256 }
257
258 if (jSampleRate == 0) {
259 ALOGE("Error creating AudioTrack: invalid sample rates");
260 return (jint) AUDIO_JAVA_ERROR;
261 }
262
263 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
264 int sampleRateInHertz = sampleRates[0];
265 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
266
267 // Invalid channel representations are caught by !audio_is_output_channel() below.
268 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
269 channelPositionMask, channelIndexMask);
270 if (!audio_is_output_channel(nativeChannelMask)) {
271 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
272 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
273 }
274
275 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
276
277 // check the format.
278 // This function was called from Java, so we compare the format against the Java constants
279 audio_format_t format = audioFormatToNative(audioFormat);
280 if (format == AUDIO_FORMAT_INVALID) {
281 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
282 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
283 }
284
285 // compute the frame count
286 size_t frameCount;
287 if (audio_has_proportional_frames(format)) {
288 const size_t bytesPerSample = audio_bytes_per_sample(format);
289 frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
290 } else {
291 frameCount = buffSizeInBytes;
292 }
293
294 // create the native AudioTrack object
295 lpTrack = new AudioTrack();
296
297 // read the AudioAttributes values
298 auto paa = JNIAudioAttributeHelper::makeUnique();
299 jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
300 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
301 return jStatus;
302 }
303 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
304 paa->usage, paa->content_type, paa->flags, paa->tags);
305
306 // initialize the callback information:
307 // this data will be passed with every AudioTrack callback
308 lpJniStorage = new AudioTrackJniStorage();
309 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
310 // we use a weak reference so the AudioTrack object can be garbage collected.
311 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
312 lpJniStorage->mCallbackData.isOffload = offload;
313 lpJniStorage->mCallbackData.busy = false;
314
315 audio_offload_info_t offloadInfo;
316 if (offload == JNI_TRUE) {
317 offloadInfo = AUDIO_INFO_INITIALIZER;
318 offloadInfo.format = format;
319 offloadInfo.sample_rate = sampleRateInHertz;
320 offloadInfo.channel_mask = nativeChannelMask;
321 offloadInfo.has_video = false;
322 offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
323 }
324
325 // initialize the native AudioTrack object
326 status_t status = NO_ERROR;
327 switch (memoryMode) {
328 case MODE_STREAM:
329 status = lpTrack->set(
330 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
331 sampleRateInHertz,
332 format,// word length, PCM
333 nativeChannelMask,
334 offload ? 0 : frameCount,
335 offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
336 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
337 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
338 0,// shared mem
339 true,// thread can call Java
340 sessionId,// audio session ID
341 offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
342 offload ? &offloadInfo : NULL,
343 -1, -1, // default uid, pid values
344 paa.get());
345
346 break;
347
348 case MODE_STATIC:
349 // AudioTrack is using shared memory
350
351 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
352 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
353 goto native_init_failure;
354 }
355
356 status = lpTrack->set(
357 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
358 sampleRateInHertz,
359 format,// word length, PCM
360 nativeChannelMask,
361 frameCount,
362 AUDIO_OUTPUT_FLAG_NONE,
363 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
364 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
365 lpJniStorage->mMemBase,// shared mem
366 true,// thread can call Java
367 sessionId,// audio session ID
368 AudioTrack::TRANSFER_SHARED,
369 NULL, // default offloadInfo
370 -1, -1, // default uid, pid values
371 paa.get());
372 break;
373
374 default:
375 ALOGE("Unknown mode %d", memoryMode);
376 goto native_init_failure;
377 }
378
379 if (status != NO_ERROR) {
380 ALOGE("Error %d initializing AudioTrack", status);
381 goto native_init_failure;
382 }
383 } else { // end if (nativeAudioTrack == 0)
384 lpTrack = (AudioTrack*)nativeAudioTrack;
385 // TODO: We need to find out which members of the Java AudioTrack might
386 // need to be initialized from the Native AudioTrack
387 // these are directly returned from getters:
388 // mSampleRate
389 // mAudioFormat
390 // mStreamType
391 // mChannelConfiguration
392 // mChannelCount
393 // mState (?)
394 // mPlayState (?)
395 // these may be used internally (Java AudioTrack.audioParamCheck():
396 // mChannelMask
397 // mChannelIndexMask
398 // mDataLoadMode
399
400 // initialize the callback information:
401 // this data will be passed with every AudioTrack callback
402 lpJniStorage = new AudioTrackJniStorage();
403 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
404 // we use a weak reference so the AudioTrack object can be garbage collected.
405 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
406 lpJniStorage->mCallbackData.busy = false;
407 }
408
409 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
410 if (nSession == NULL) {
411 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
412 goto native_init_failure;
413 }
414 // read the audio session ID back from AudioTrack in case we create a new session
415 nSession[0] = lpTrack->getSessionId();
416 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
417 nSession = NULL;
418
419 {
420 const jint elements[1] = { (jint) lpTrack->getSampleRate() };
421 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
422 }
423
424 { // scope for the lock
425 Mutex::Autolock l(sLock);
426 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
427 }
428 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
429 // of the Java object (in mNativeTrackInJavaObj)
430 setAudioTrack(env, thiz, lpTrack);
431
432 // save the JNI resources so we can free them later
433 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
434 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
435
436 // since we had audio attributes, the stream type was derived from them during the
437 // creation of the native AudioTrack: push the same value to the Java object
438 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
439
440 return (jint) AUDIO_JAVA_SUCCESS;
441
442 // failures:
443 native_init_failure:
444 if (nSession != NULL) {
445 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
446 }
447 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
448 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
449 delete lpJniStorage;
450 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
451
452 // lpTrack goes out of scope, so reference count drops to zero
453 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
454 }
455
456 // ----------------------------------------------------------------------------
457 static jboolean
android_media_AudioTrack_is_direct_output_supported(JNIEnv * env,jobject thiz,jint encoding,jint sampleRate,jint channelMask,jint channelIndexMask,jint contentType,jint usage,jint flags)458 android_media_AudioTrack_is_direct_output_supported(JNIEnv *env, jobject thiz,
459 jint encoding, jint sampleRate,
460 jint channelMask, jint channelIndexMask,
461 jint contentType, jint usage, jint flags) {
462 audio_config_base_t config = {};
463 audio_attributes_t attributes = {};
464 config.format = static_cast<audio_format_t>(audioFormatToNative(encoding));
465 config.sample_rate = static_cast<uint32_t>(sampleRate);
466 config.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
467 attributes.content_type = static_cast<audio_content_type_t>(contentType);
468 attributes.usage = static_cast<audio_usage_t>(usage);
469 attributes.flags = static_cast<audio_flags_mask_t>(flags);
470 // ignore source and tags attributes as they don't affect querying whether output is supported
471 return AudioTrack::isDirectOutputSupported(config, attributes);
472 }
473
474 // ----------------------------------------------------------------------------
475 static void
android_media_AudioTrack_start(JNIEnv * env,jobject thiz)476 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
477 {
478 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
479 if (lpTrack == NULL) {
480 jniThrowException(env, "java/lang/IllegalStateException",
481 "Unable to retrieve AudioTrack pointer for start()");
482 return;
483 }
484
485 lpTrack->start();
486 }
487
488
489 // ----------------------------------------------------------------------------
490 static void
android_media_AudioTrack_stop(JNIEnv * env,jobject thiz)491 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
492 {
493 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
494 if (lpTrack == NULL) {
495 jniThrowException(env, "java/lang/IllegalStateException",
496 "Unable to retrieve AudioTrack pointer for stop()");
497 return;
498 }
499
500 lpTrack->stop();
501 }
502
503
504 // ----------------------------------------------------------------------------
505 static void
android_media_AudioTrack_pause(JNIEnv * env,jobject thiz)506 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
507 {
508 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
509 if (lpTrack == NULL) {
510 jniThrowException(env, "java/lang/IllegalStateException",
511 "Unable to retrieve AudioTrack pointer for pause()");
512 return;
513 }
514
515 lpTrack->pause();
516 }
517
518
519 // ----------------------------------------------------------------------------
520 static void
android_media_AudioTrack_flush(JNIEnv * env,jobject thiz)521 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
522 {
523 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
524 if (lpTrack == NULL) {
525 jniThrowException(env, "java/lang/IllegalStateException",
526 "Unable to retrieve AudioTrack pointer for flush()");
527 return;
528 }
529
530 lpTrack->flush();
531 }
532
533 // ----------------------------------------------------------------------------
534 static void
android_media_AudioTrack_set_volume(JNIEnv * env,jobject thiz,jfloat leftVol,jfloat rightVol)535 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
536 {
537 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
538 if (lpTrack == NULL) {
539 jniThrowException(env, "java/lang/IllegalStateException",
540 "Unable to retrieve AudioTrack pointer for setVolume()");
541 return;
542 }
543
544 lpTrack->setVolume(leftVol, rightVol);
545 }
546
547 // ----------------------------------------------------------------------------
548
549 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioTrack_release(JNIEnv * env,jobject thiz)550 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
551 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
552 if (lpTrack == NULL) {
553 return;
554 }
555 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
556
557 // delete the JNI data
558 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
559 thiz, javaAudioTrackFields.jniData);
560 // reset the native resources in the Java object so any attempt to access
561 // them after a call to release fails.
562 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
563
564 if (pJniStorage) {
565 Mutex::Autolock l(sLock);
566 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
567 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
568 while (lpCookie->busy) {
569 if (lpCookie->cond.waitRelative(sLock,
570 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
571 NO_ERROR) {
572 break;
573 }
574 }
575 sAudioTrackCallBackCookies.remove(lpCookie);
576 // delete global refs created in native_setup
577 env->DeleteGlobalRef(lpCookie->audioTrack_class);
578 env->DeleteGlobalRef(lpCookie->audioTrack_ref);
579 delete pJniStorage;
580 }
581 }
582
583
584 // ----------------------------------------------------------------------------
android_media_AudioTrack_finalize(JNIEnv * env,jobject thiz)585 static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) {
586 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
587 android_media_AudioTrack_release(env, thiz);
588 }
589
590 // overloaded JNI array helper functions (same as in android_media_AudioRecord)
591 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)592 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
593 return env->GetByteArrayElements(array, isCopy);
594 }
595
596 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)597 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
598 env->ReleaseByteArrayElements(array, elems, mode);
599 }
600
601 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)602 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
603 return env->GetShortArrayElements(array, isCopy);
604 }
605
606 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)607 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
608 env->ReleaseShortArrayElements(array, elems, mode);
609 }
610
611 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)612 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
613 return env->GetFloatArrayElements(array, isCopy);
614 }
615
616 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)617 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
618 env->ReleaseFloatArrayElements(array, elems, mode);
619 }
620
621 static inline
interpretWriteSizeError(ssize_t writeSize)622 jint interpretWriteSizeError(ssize_t writeSize) {
623 if (writeSize == WOULD_BLOCK) {
624 return (jint)0;
625 } else if (writeSize == NO_INIT) {
626 return AUDIO_JAVA_DEAD_OBJECT;
627 } else {
628 ALOGE("Error %zd during AudioTrack native read", writeSize);
629 return nativeToJavaStatus(writeSize);
630 }
631 }
632
633 // ----------------------------------------------------------------------------
634 template <typename T>
writeToTrack(const sp<AudioTrack> & track,jint audioFormat,const T * data,jint offsetInSamples,jint sizeInSamples,bool blocking)635 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
636 jint offsetInSamples, jint sizeInSamples, bool blocking) {
637 // give the data to the native AudioTrack object (the data starts at the offset)
638 ssize_t written = 0;
639 // regular write() or copy the data to the AudioTrack's shared memory?
640 size_t sizeInBytes = sizeInSamples * sizeof(T);
641 if (track->sharedBuffer() == 0) {
642 written = track->write(data + offsetInSamples, sizeInBytes, blocking);
643 // for compatibility with earlier behavior of write(), return 0 in this case
644 if (written == (ssize_t) WOULD_BLOCK) {
645 written = 0;
646 }
647 } else {
648 // writing to shared memory, check for capacity
649 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
650 sizeInBytes = track->sharedBuffer()->size();
651 }
652 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
653 written = sizeInBytes;
654 }
655 if (written >= 0) {
656 return written / sizeof(T);
657 }
658 return interpretWriteSizeError(written);
659 }
660
661 // ----------------------------------------------------------------------------
662 template <typename T>
android_media_AudioTrack_writeArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jint javaAudioFormat,jboolean isWriteBlocking)663 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
664 T javaAudioData,
665 jint offsetInSamples, jint sizeInSamples,
666 jint javaAudioFormat,
667 jboolean isWriteBlocking) {
668 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
669 // offsetInSamples, sizeInSamples);
670 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
671 if (lpTrack == NULL) {
672 jniThrowException(env, "java/lang/IllegalStateException",
673 "Unable to retrieve AudioTrack pointer for write()");
674 return (jint)AUDIO_JAVA_INVALID_OPERATION;
675 }
676
677 if (javaAudioData == NULL) {
678 ALOGE("NULL java array of audio data to play");
679 return (jint)AUDIO_JAVA_BAD_VALUE;
680 }
681
682 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
683 // a way that it becomes much more efficient. When doing so, we will have to prevent the
684 // AudioSystem callback to be called while in critical section (in case of media server
685 // process crash for instance)
686
687 // get the pointer for the audio data from the java array
688 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
689 if (cAudioData == NULL) {
690 ALOGE("Error retrieving source of audio data to play");
691 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
692 }
693
694 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
695 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
696
697 envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
698
699 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
700 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
701 return samplesWritten;
702 }
703
704 // ----------------------------------------------------------------------------
android_media_AudioTrack_write_native_bytes(JNIEnv * env,jobject thiz,jobject javaByteBuffer,jint byteOffset,jint sizeInBytes,jint javaAudioFormat,jboolean isWriteBlocking)705 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
706 jobject javaByteBuffer, jint byteOffset, jint sizeInBytes,
707 jint javaAudioFormat, jboolean isWriteBlocking) {
708 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
709 // offsetInBytes, sizeInBytes);
710 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
711 if (lpTrack == NULL) {
712 jniThrowException(env, "java/lang/IllegalStateException",
713 "Unable to retrieve AudioTrack pointer for write()");
714 return (jint)AUDIO_JAVA_INVALID_OPERATION;
715 }
716
717 const jbyte* bytes =
718 reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(javaByteBuffer));
719 if (bytes == NULL) {
720 ALOGE("Error retrieving source of audio data to play, can't play");
721 return (jint)AUDIO_JAVA_BAD_VALUE;
722 }
723
724 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes, byteOffset,
725 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
726
727 return written;
728 }
729
730 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_size_frames(JNIEnv * env,jobject thiz)731 static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) {
732 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
733 if (lpTrack == NULL) {
734 jniThrowException(env, "java/lang/IllegalStateException",
735 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
736 return (jint)AUDIO_JAVA_ERROR;
737 }
738
739 ssize_t result = lpTrack->getBufferSizeInFrames();
740 if (result < 0) {
741 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
742 "Internal error detected in getBufferSizeInFrames() = %zd", result);
743 return (jint)AUDIO_JAVA_ERROR;
744 }
745 return (jint)result;
746 }
747
748 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_buffer_size_frames(JNIEnv * env,jobject thiz,jint bufferSizeInFrames)749 static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
750 jobject thiz, jint bufferSizeInFrames) {
751 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
752 if (lpTrack == NULL) {
753 jniThrowException(env, "java/lang/IllegalStateException",
754 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
755 return (jint)AUDIO_JAVA_ERROR;
756 }
757 // Value will be coerced into the valid range.
758 // But internal values are unsigned, size_t, so we need to clip
759 // against zero here where it is signed.
760 if (bufferSizeInFrames < 0) {
761 bufferSizeInFrames = 0;
762 }
763 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
764 if (result < 0) {
765 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
766 "Internal error detected in setBufferSizeInFrames() = %zd", result);
767 return (jint)AUDIO_JAVA_ERROR;
768 }
769 return (jint)result;
770 }
771
772 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv * env,jobject thiz)773 static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) {
774 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
775 if (lpTrack == NULL) {
776 jniThrowException(env, "java/lang/IllegalStateException",
777 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
778 return (jint)AUDIO_JAVA_ERROR;
779 }
780
781 return lpTrack->frameCount();
782 }
783
784 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_rate(JNIEnv * env,jobject thiz,jint sampleRateInHz)785 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
786 jint sampleRateInHz) {
787 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
788 if (lpTrack == NULL) {
789 jniThrowException(env, "java/lang/IllegalStateException",
790 "Unable to retrieve AudioTrack pointer for setSampleRate()");
791 return (jint)AUDIO_JAVA_ERROR;
792 }
793 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
794 }
795
796
797 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_rate(JNIEnv * env,jobject thiz)798 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
799 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
800 if (lpTrack == NULL) {
801 jniThrowException(env, "java/lang/IllegalStateException",
802 "Unable to retrieve AudioTrack pointer for getSampleRate()");
803 return (jint)AUDIO_JAVA_ERROR;
804 }
805 return (jint) lpTrack->getSampleRate();
806 }
807
808
809 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_params(JNIEnv * env,jobject thiz,jobject params)810 static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz,
811 jobject params) {
812 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
813 if (lpTrack == NULL) {
814 jniThrowException(env, "java/lang/IllegalStateException",
815 "AudioTrack not initialized");
816 return;
817 }
818
819 PlaybackParams pbp;
820 pbp.fillFromJobject(env, gPlaybackParamsFields, params);
821
822 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
823 pbp.speedSet, pbp.audioRate.mSpeed,
824 pbp.pitchSet, pbp.audioRate.mPitch,
825 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
826 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
827
828 // to simulate partially set params, we do a read-modify-write.
829 // TODO: pass in the valid set mask into AudioTrack.
830 AudioPlaybackRate rate = lpTrack->getPlaybackRate();
831 bool updatedRate = false;
832 if (pbp.speedSet) {
833 rate.mSpeed = pbp.audioRate.mSpeed;
834 updatedRate = true;
835 }
836 if (pbp.pitchSet) {
837 rate.mPitch = pbp.audioRate.mPitch;
838 updatedRate = true;
839 }
840 if (pbp.audioFallbackModeSet) {
841 rate.mFallbackMode = pbp.audioRate.mFallbackMode;
842 updatedRate = true;
843 }
844 if (pbp.audioStretchModeSet) {
845 rate.mStretchMode = pbp.audioRate.mStretchMode;
846 updatedRate = true;
847 }
848 if (updatedRate) {
849 if (lpTrack->setPlaybackRate(rate) != OK) {
850 jniThrowException(env, "java/lang/IllegalArgumentException",
851 "arguments out of range");
852 }
853 }
854 }
855
856
857 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_params(JNIEnv * env,jobject thiz,jobject params)858 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz,
859 jobject params) {
860 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
861 if (lpTrack == NULL) {
862 jniThrowException(env, "java/lang/IllegalStateException",
863 "AudioTrack not initialized");
864 return NULL;
865 }
866
867 PlaybackParams pbs;
868 pbs.audioRate = lpTrack->getPlaybackRate();
869 pbs.speedSet = true;
870 pbs.pitchSet = true;
871 pbs.audioFallbackModeSet = true;
872 pbs.audioStretchModeSet = true;
873 return pbs.asJobject(env, gPlaybackParamsFields);
874 }
875
876
877 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)878 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
879 jint markerPos) {
880 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
881 if (lpTrack == NULL) {
882 jniThrowException(env, "java/lang/IllegalStateException",
883 "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
884 return (jint)AUDIO_JAVA_ERROR;
885 }
886 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
887 }
888
889
890 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_marker_pos(JNIEnv * env,jobject thiz)891 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
892 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
893 uint32_t markerPos = 0;
894
895 if (lpTrack == NULL) {
896 jniThrowException(env, "java/lang/IllegalStateException",
897 "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
898 return (jint)AUDIO_JAVA_ERROR;
899 }
900 lpTrack->getMarkerPosition(&markerPos);
901 return (jint)markerPos;
902 }
903
904
905 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)906 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
907 jint period) {
908 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
909 if (lpTrack == NULL) {
910 jniThrowException(env, "java/lang/IllegalStateException",
911 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
912 return (jint)AUDIO_JAVA_ERROR;
913 }
914 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
915 }
916
917
918 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_pos_update_period(JNIEnv * env,jobject thiz)919 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
920 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
921 uint32_t period = 0;
922
923 if (lpTrack == NULL) {
924 jniThrowException(env, "java/lang/IllegalStateException",
925 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
926 return (jint)AUDIO_JAVA_ERROR;
927 }
928 lpTrack->getPositionUpdatePeriod(&period);
929 return (jint)period;
930 }
931
932
933 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_position(JNIEnv * env,jobject thiz,jint position)934 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
935 jint position) {
936 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
937 if (lpTrack == NULL) {
938 jniThrowException(env, "java/lang/IllegalStateException",
939 "Unable to retrieve AudioTrack pointer for setPosition()");
940 return (jint)AUDIO_JAVA_ERROR;
941 }
942 return nativeToJavaStatus( lpTrack->setPosition(position) );
943 }
944
945
946 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_position(JNIEnv * env,jobject thiz)947 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
948 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
949 uint32_t position = 0;
950
951 if (lpTrack == NULL) {
952 jniThrowException(env, "java/lang/IllegalStateException",
953 "Unable to retrieve AudioTrack pointer for getPosition()");
954 return (jint)AUDIO_JAVA_ERROR;
955 }
956 lpTrack->getPosition(&position);
957 return (jint)position;
958 }
959
960
961 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_latency(JNIEnv * env,jobject thiz)962 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) {
963 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
964
965 if (lpTrack == NULL) {
966 jniThrowException(env, "java/lang/IllegalStateException",
967 "Unable to retrieve AudioTrack pointer for latency()");
968 return (jint)AUDIO_JAVA_ERROR;
969 }
970 return (jint)lpTrack->latency();
971 }
972
973 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_underrun_count(JNIEnv * env,jobject thiz)974 static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) {
975 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
976
977 if (lpTrack == NULL) {
978 jniThrowException(env, "java/lang/IllegalStateException",
979 "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
980 return (jint)AUDIO_JAVA_ERROR;
981 }
982 return (jint)lpTrack->getUnderrunCount();
983 }
984
985 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_flags(JNIEnv * env,jobject thiz)986 static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) {
987 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
988
989 if (lpTrack == NULL) {
990 jniThrowException(env, "java/lang/IllegalStateException",
991 "Unable to retrieve AudioTrack pointer for getFlags()");
992 return (jint)AUDIO_JAVA_ERROR;
993 }
994 return (jint)lpTrack->getFlags();
995 }
996
997 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_timestamp(JNIEnv * env,jobject thiz,jlongArray jTimestamp)998 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) {
999 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1000
1001 if (lpTrack == NULL) {
1002 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
1003 return (jint)AUDIO_JAVA_ERROR;
1004 }
1005 AudioTimestamp timestamp;
1006 status_t status = lpTrack->getTimestamp(timestamp);
1007 if (status == OK) {
1008 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
1009 if (nTimestamp == NULL) {
1010 ALOGE("Unable to get array for getTimestamp()");
1011 return (jint)AUDIO_JAVA_ERROR;
1012 }
1013 nTimestamp[0] = (jlong) timestamp.mPosition;
1014 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
1015 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
1016 }
1017 return (jint) nativeToJavaStatus(status);
1018 }
1019
1020 // ----------------------------------------------------------------------------
1021 static jobject
android_media_AudioTrack_native_getMetrics(JNIEnv * env,jobject thiz)1022 android_media_AudioTrack_native_getMetrics(JNIEnv *env, jobject thiz)
1023 {
1024 ALOGD("android_media_AudioTrack_native_getMetrics");
1025
1026 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1027
1028 if (lpTrack == NULL) {
1029 ALOGE("Unable to retrieve AudioTrack pointer for getMetrics()");
1030 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1031 return (jobject) NULL;
1032 }
1033
1034 // get what we have for the metrics from the track
1035 MediaAnalyticsItem *item = NULL;
1036
1037 status_t err = lpTrack->getMetrics(item);
1038 if (err != OK) {
1039 ALOGE("getMetrics failed");
1040 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1041 return (jobject) NULL;
1042 }
1043
1044 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
1045
1046 // housekeeping
1047 delete item;
1048 item = NULL;
1049
1050 return mybundle;
1051 }
1052
1053
1054 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_loop(JNIEnv * env,jobject thiz,jint loopStart,jint loopEnd,jint loopCount)1055 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
1056 jint loopStart, jint loopEnd, jint loopCount) {
1057 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1058 if (lpTrack == NULL) {
1059 jniThrowException(env, "java/lang/IllegalStateException",
1060 "Unable to retrieve AudioTrack pointer for setLoop()");
1061 return (jint)AUDIO_JAVA_ERROR;
1062 }
1063 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
1064 }
1065
1066
1067 // ----------------------------------------------------------------------------
android_media_AudioTrack_reload(JNIEnv * env,jobject thiz)1068 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
1069 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1070 if (lpTrack == NULL) {
1071 jniThrowException(env, "java/lang/IllegalStateException",
1072 "Unable to retrieve AudioTrack pointer for reload()");
1073 return (jint)AUDIO_JAVA_ERROR;
1074 }
1075 return nativeToJavaStatus( lpTrack->reload() );
1076 }
1077
1078
1079 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_output_sample_rate(JNIEnv * env,jobject thiz,jint javaStreamType)1080 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz,
1081 jint javaStreamType) {
1082 uint32_t afSamplingRate;
1083 // convert the stream type from Java to native value
1084 // FIXME: code duplication with android_media_AudioTrack_setup()
1085 audio_stream_type_t nativeStreamType;
1086 switch (javaStreamType) {
1087 case AUDIO_STREAM_VOICE_CALL:
1088 case AUDIO_STREAM_SYSTEM:
1089 case AUDIO_STREAM_RING:
1090 case AUDIO_STREAM_MUSIC:
1091 case AUDIO_STREAM_ALARM:
1092 case AUDIO_STREAM_NOTIFICATION:
1093 case AUDIO_STREAM_BLUETOOTH_SCO:
1094 case AUDIO_STREAM_DTMF:
1095 nativeStreamType = (audio_stream_type_t) javaStreamType;
1096 break;
1097 default:
1098 nativeStreamType = AUDIO_STREAM_DEFAULT;
1099 break;
1100 }
1101
1102 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1103 if (status != NO_ERROR) {
1104 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1105 "in AudioTrack JNI", status, nativeStreamType);
1106 return DEFAULT_OUTPUT_SAMPLE_RATE;
1107 } else {
1108 return afSamplingRate;
1109 }
1110 }
1111
1112
1113 // ----------------------------------------------------------------------------
1114 // returns the minimum required size for the successful creation of a streaming AudioTrack
1115 // returns -1 if there was an error querying the hardware.
android_media_AudioTrack_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)1116 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
1117 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
1118
1119 size_t frameCount;
1120 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1121 sampleRateInHertz);
1122 if (status != NO_ERROR) {
1123 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1124 sampleRateInHertz, status);
1125 return -1;
1126 }
1127 const audio_format_t format = audioFormatToNative(audioFormat);
1128 if (audio_has_proportional_frames(format)) {
1129 const size_t bytesPerSample = audio_bytes_per_sample(format);
1130 return frameCount * channelCount * bytesPerSample;
1131 } else {
1132 return frameCount;
1133 }
1134 }
1135
1136 // ----------------------------------------------------------------------------
1137 static jint
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv * env,jobject thiz,jfloat level)1138 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1139 {
1140 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1141 if (lpTrack == NULL ) {
1142 jniThrowException(env, "java/lang/IllegalStateException",
1143 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
1144 return -1;
1145 }
1146
1147 status_t status = lpTrack->setAuxEffectSendLevel(level);
1148 if (status != NO_ERROR) {
1149 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1150 level, status);
1151 }
1152 return (jint) status;
1153 }
1154
1155 // ----------------------------------------------------------------------------
android_media_AudioTrack_attachAuxEffect(JNIEnv * env,jobject thiz,jint effectId)1156 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
1157 jint effectId) {
1158 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1159 if (lpTrack == NULL) {
1160 jniThrowException(env, "java/lang/IllegalStateException",
1161 "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
1162 return (jint)AUDIO_JAVA_ERROR;
1163 }
1164 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
1165 }
1166
android_media_AudioTrack_setOutputDevice(JNIEnv * env,jobject thiz,jint device_id)1167 static jboolean android_media_AudioTrack_setOutputDevice(
1168 JNIEnv *env, jobject thiz, jint device_id) {
1169
1170 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1171 if (lpTrack == 0) {
1172 return false;
1173 }
1174 return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1175 }
1176
android_media_AudioTrack_getRoutedDeviceId(JNIEnv * env,jobject thiz)1177 static jint android_media_AudioTrack_getRoutedDeviceId(
1178 JNIEnv *env, jobject thiz) {
1179
1180 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1181 if (lpTrack == NULL) {
1182 return 0;
1183 }
1184 return (jint)lpTrack->getRoutedDeviceId();
1185 }
1186
android_media_AudioTrack_enableDeviceCallback(JNIEnv * env,jobject thiz)1187 static void android_media_AudioTrack_enableDeviceCallback(
1188 JNIEnv *env, jobject thiz) {
1189
1190 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1191 if (lpTrack == NULL) {
1192 return;
1193 }
1194 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1195 thiz, javaAudioTrackFields.jniData);
1196 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1197 return;
1198 }
1199 pJniStorage->mDeviceCallback =
1200 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1201 javaAudioTrackFields.postNativeEventInJava);
1202 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1203 }
1204
android_media_AudioTrack_disableDeviceCallback(JNIEnv * env,jobject thiz)1205 static void android_media_AudioTrack_disableDeviceCallback(
1206 JNIEnv *env, jobject thiz) {
1207
1208 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1209 if (lpTrack == NULL) {
1210 return;
1211 }
1212 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1213 thiz, javaAudioTrackFields.jniData);
1214 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1215 return;
1216 }
1217 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1218 pJniStorage->mDeviceCallback.clear();
1219 }
1220
1221 // Pass through the arguments to the AudioFlinger track implementation.
android_media_AudioTrack_apply_volume_shaper(JNIEnv * env,jobject thiz,jobject jconfig,jobject joperation)1222 static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
1223 jobject jconfig, jobject joperation) {
1224 // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1225 const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1226
1227 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1228 if (lpTrack == nullptr) {
1229 return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1230 }
1231
1232 sp<VolumeShaper::Configuration> configuration;
1233 sp<VolumeShaper::Operation> operation;
1234 if (jconfig != nullptr) {
1235 configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1236 env, gVolumeShaperFields, jconfig);
1237 ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1238 }
1239 if (joperation != nullptr) {
1240 operation = VolumeShaperHelper::convertJobjectToOperation(
1241 env, gVolumeShaperFields, joperation);
1242 ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1243 }
1244 VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
1245 if (status == INVALID_OPERATION) {
1246 status = VOLUME_SHAPER_INVALID_OPERATION;
1247 }
1248 return (jint)status; // if status < 0 an error, else a VolumeShaper id
1249 }
1250
1251 // Pass through the arguments to the AudioFlinger track implementation.
android_media_AudioTrack_get_volume_shaper_state(JNIEnv * env,jobject thiz,jint id)1252 static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
1253 jint id) {
1254 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1255 if (lpTrack == nullptr) {
1256 return (jobject)nullptr;
1257 }
1258
1259 sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
1260 if (state.get() == nullptr) {
1261 return (jobject)nullptr;
1262 }
1263 return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1264 }
1265
android_media_AudioTrack_setPresentation(JNIEnv * env,jobject thiz,jint presentationId,jint programId)1266 static int android_media_AudioTrack_setPresentation(
1267 JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
1268 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1269 if (lpTrack == NULL) {
1270 jniThrowException(env, "java/lang/IllegalStateException",
1271 "AudioTrack not initialized");
1272 return (jint)AUDIO_JAVA_ERROR;
1273 }
1274
1275 return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
1276 }
1277
1278 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_port_id(JNIEnv * env,jobject thiz)1279 static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) {
1280 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1281 if (lpTrack == NULL) {
1282 jniThrowException(env, "java/lang/IllegalStateException",
1283 "AudioTrack not initialized");
1284 return (jint)AUDIO_PORT_HANDLE_NONE;
1285 }
1286 return (jint)lpTrack->getPortId();
1287 }
1288
1289 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_delay_padding(JNIEnv * env,jobject thiz,jint delayInFrames,jint paddingInFrames)1290 static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thiz,
1291 jint delayInFrames, jint paddingInFrames) {
1292 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1293 if (lpTrack == NULL) {
1294 jniThrowException(env, "java/lang/IllegalStateException",
1295 "AudioTrack not initialized");
1296 return;
1297 }
1298 AudioParameter param = AudioParameter();
1299 param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), (int) delayInFrames);
1300 param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), (int) paddingInFrames);
1301 lpTrack->setParameters(param.toString());
1302 }
1303
1304 // ----------------------------------------------------------------------------
1305 // ----------------------------------------------------------------------------
1306 static const JNINativeMethod gMethods[] = {
1307 // name, signature, funcPtr
1308 {"native_is_direct_output_supported",
1309 "(IIIIIII)Z",
1310 (void *)android_media_AudioTrack_is_direct_output_supported},
1311 {"native_start", "()V", (void *)android_media_AudioTrack_start},
1312 {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
1313 {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
1314 {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
1315 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
1316 (void *)android_media_AudioTrack_setup},
1317 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
1318 {"native_release", "()V", (void *)android_media_AudioTrack_release},
1319 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1320 {"native_write_native_bytes",
1321 "(Ljava/nio/ByteBuffer;IIIZ)I",
1322 (void *)android_media_AudioTrack_write_native_bytes},
1323 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1324 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1325 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
1326 {"native_get_buffer_size_frames",
1327 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
1328 {"native_set_buffer_size_frames",
1329 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
1330 {"native_get_buffer_capacity_frames",
1331 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
1332 {"native_set_playback_rate",
1333 "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
1334 {"native_get_playback_rate",
1335 "()I", (void *)android_media_AudioTrack_get_playback_rate},
1336 {"native_set_playback_params",
1337 "(Landroid/media/PlaybackParams;)V",
1338 (void *)android_media_AudioTrack_set_playback_params},
1339 {"native_get_playback_params",
1340 "()Landroid/media/PlaybackParams;",
1341 (void *)android_media_AudioTrack_get_playback_params},
1342 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
1343 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
1344 {"native_set_pos_update_period",
1345 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
1346 {"native_get_pos_update_period",
1347 "()I", (void *)android_media_AudioTrack_get_pos_update_period},
1348 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
1349 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
1350 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
1351 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
1352 {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
1353 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
1354 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
1355 (void *)android_media_AudioTrack_native_getMetrics},
1356 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
1357 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
1358 {"native_get_output_sample_rate",
1359 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
1360 {"native_get_min_buff_size",
1361 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
1362 {"native_setAuxEffectSendLevel",
1363 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1364 {"native_attachAuxEffect",
1365 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
1366 {"native_setOutputDevice", "(I)Z",
1367 (void *)android_media_AudioTrack_setOutputDevice},
1368 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1369 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1370 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1371 {"native_applyVolumeShaper",
1372 "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1373 (void *)android_media_AudioTrack_apply_volume_shaper},
1374 {"native_getVolumeShaperState",
1375 "(I)Landroid/media/VolumeShaper$State;",
1376 (void *)android_media_AudioTrack_get_volume_shaper_state},
1377 {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
1378 {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
1379 {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
1380 };
1381
1382
1383 // field names found in android/media/AudioTrack.java
1384 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
1385 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
1386 #define JAVA_JNIDATA_FIELD_NAME "mJniData"
1387 #define JAVA_STREAMTYPE_FIELD_NAME "mStreamType"
1388
1389 // ----------------------------------------------------------------------------
1390 // preconditions:
1391 // theClass is valid
android_media_getIntConstantFromClass(JNIEnv * pEnv,jclass theClass,const char * className,const char * constName,int * constVal)1392 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1393 const char* constName, int* constVal) {
1394 jfieldID javaConst = NULL;
1395 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1396 if (javaConst != NULL) {
1397 *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1398 return true;
1399 } else {
1400 ALOGE("Can't find %s.%s", className, constName);
1401 return false;
1402 }
1403 }
1404
1405 // ----------------------------------------------------------------------------
register_android_media_AudioTrack(JNIEnv * env)1406 int register_android_media_AudioTrack(JNIEnv *env)
1407 {
1408 // must be first
1409 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1410
1411 javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1412 javaAudioTrackFields.postNativeEventInJava = NULL;
1413
1414 // Get the AudioTrack class
1415 jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1416
1417 // Get the postEvent method
1418 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1419 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1420 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1421
1422 // Get the variables fields
1423 // nativeTrackInJavaObj
1424 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1425 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1426 // jniData
1427 javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1428 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1429 // fieldStreamType
1430 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1431 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1432
1433 env->DeleteLocalRef(audioTrackClass);
1434
1435 // initialize PlaybackParams field info
1436 gPlaybackParamsFields.init(env);
1437
1438 gVolumeShaperFields.init(env);
1439 return res;
1440 }
1441
1442
1443 // ----------------------------------------------------------------------------
1444