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