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