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