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,jint sampleRateInHertz,jint channelPositionMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jint memoryMode,jintArray jSession)216 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
217         jobject jaa,
218         jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
219         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
220 
221     ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d",
222         sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes);
223 
224     if (jaa == 0) {
225         ALOGE("Error creating AudioTrack: invalid audio attributes");
226         return (jint) AUDIO_JAVA_ERROR;
227     }
228 
229     // Invalid channel representations are caught by !audio_is_output_channel() below.
230     audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
231             channelPositionMask, channelIndexMask);
232     if (!audio_is_output_channel(nativeChannelMask)) {
233         ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
234         return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
235     }
236 
237     uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
238 
239     // check the format.
240     // This function was called from Java, so we compare the format against the Java constants
241     audio_format_t format = audioFormatToNative(audioFormat);
242     if (format == AUDIO_FORMAT_INVALID) {
243         ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
244         return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
245     }
246 
247     // compute the frame count
248     size_t frameCount;
249     if (audio_is_linear_pcm(format)) {
250         const size_t bytesPerSample = audio_bytes_per_sample(format);
251         frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
252     } else {
253         frameCount = buffSizeInBytes;
254     }
255 
256     jclass clazz = env->GetObjectClass(thiz);
257     if (clazz == NULL) {
258         ALOGE("Can't find %s when setting up callback.", kClassPathName);
259         return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
260     }
261 
262     if (jSession == NULL) {
263         ALOGE("Error creating AudioTrack: invalid session ID pointer");
264         return (jint) AUDIO_JAVA_ERROR;
265     }
266 
267     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
268     if (nSession == NULL) {
269         ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
270         return (jint) AUDIO_JAVA_ERROR;
271     }
272     int sessionId = nSession[0];
273     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
274     nSession = NULL;
275 
276     // create the native AudioTrack object
277     sp<AudioTrack> lpTrack = new AudioTrack();
278 
279     audio_attributes_t *paa = NULL;
280     // read the AudioAttributes values
281     paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
282     const jstring jtags =
283             (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
284     const char* tags = env->GetStringUTFChars(jtags, NULL);
285     // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
286     strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
287     env->ReleaseStringUTFChars(jtags, tags);
288     paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
289     paa->content_type =
290             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
291     paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
292 
293     ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
294             paa->usage, paa->content_type, paa->flags, paa->tags);
295 
296     // initialize the callback information:
297     // this data will be passed with every AudioTrack callback
298     AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
299     lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
300     // we use a weak reference so the AudioTrack object can be garbage collected.
301     lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
302     lpJniStorage->mCallbackData.busy = false;
303 
304     // initialize the native AudioTrack object
305     status_t status = NO_ERROR;
306     switch (memoryMode) {
307     case MODE_STREAM:
308 
309         status = lpTrack->set(
310                 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
311                 sampleRateInHertz,
312                 format,// word length, PCM
313                 nativeChannelMask,
314                 frameCount,
315                 AUDIO_OUTPUT_FLAG_NONE,
316                 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
317                 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
318                 0,// shared mem
319                 true,// thread can call Java
320                 sessionId,// audio session ID
321                 AudioTrack::TRANSFER_SYNC,
322                 NULL,                         // default offloadInfo
323                 -1, -1,                       // default uid, pid values
324                 paa);
325         break;
326 
327     case MODE_STATIC:
328         // AudioTrack is using shared memory
329 
330         if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
331             ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
332             goto native_init_failure;
333         }
334 
335         status = lpTrack->set(
336                 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
337                 sampleRateInHertz,
338                 format,// word length, PCM
339                 nativeChannelMask,
340                 frameCount,
341                 AUDIO_OUTPUT_FLAG_NONE,
342                 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
343                 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
344                 lpJniStorage->mMemBase,// shared mem
345                 true,// thread can call Java
346                 sessionId,// audio session ID
347                 AudioTrack::TRANSFER_SHARED,
348                 NULL,                         // default offloadInfo
349                 -1, -1,                       // default uid, pid values
350                 paa);
351         break;
352 
353     default:
354         ALOGE("Unknown mode %d", memoryMode);
355         goto native_init_failure;
356     }
357 
358     if (status != NO_ERROR) {
359         ALOGE("Error %d initializing AudioTrack", status);
360         goto native_init_failure;
361     }
362 
363     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
364     if (nSession == NULL) {
365         ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
366         goto native_init_failure;
367     }
368     // read the audio session ID back from AudioTrack in case we create a new session
369     nSession[0] = lpTrack->getSessionId();
370     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
371     nSession = NULL;
372 
373     {   // scope for the lock
374         Mutex::Autolock l(sLock);
375         sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
376     }
377     // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
378     // of the Java object (in mNativeTrackInJavaObj)
379     setAudioTrack(env, thiz, lpTrack);
380 
381     // save the JNI resources so we can free them later
382     //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
383     env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
384 
385     // since we had audio attributes, the stream type was derived from them during the
386     // creation of the native AudioTrack: push the same value to the Java object
387     env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
388     // audio attributes were copied in AudioTrack creation
389     free(paa);
390     paa = NULL;
391 
392 
393     return (jint) AUDIO_JAVA_SUCCESS;
394 
395     // failures:
396 native_init_failure:
397     if (paa != NULL) {
398         free(paa);
399     }
400     if (nSession != NULL) {
401         env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
402     }
403     env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
404     env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
405     delete lpJniStorage;
406     env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
407 
408     // lpTrack goes out of scope, so reference count drops to zero
409     return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
410 }
411 
412 
413 // ----------------------------------------------------------------------------
414 static void
android_media_AudioTrack_start(JNIEnv * env,jobject thiz)415 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
416 {
417     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
418     if (lpTrack == NULL) {
419         jniThrowException(env, "java/lang/IllegalStateException",
420             "Unable to retrieve AudioTrack pointer for start()");
421         return;
422     }
423 
424     lpTrack->start();
425 }
426 
427 
428 // ----------------------------------------------------------------------------
429 static void
android_media_AudioTrack_stop(JNIEnv * env,jobject thiz)430 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
431 {
432     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
433     if (lpTrack == NULL) {
434         jniThrowException(env, "java/lang/IllegalStateException",
435             "Unable to retrieve AudioTrack pointer for stop()");
436         return;
437     }
438 
439     lpTrack->stop();
440 }
441 
442 
443 // ----------------------------------------------------------------------------
444 static void
android_media_AudioTrack_pause(JNIEnv * env,jobject thiz)445 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
446 {
447     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
448     if (lpTrack == NULL) {
449         jniThrowException(env, "java/lang/IllegalStateException",
450             "Unable to retrieve AudioTrack pointer for pause()");
451         return;
452     }
453 
454     lpTrack->pause();
455 }
456 
457 
458 // ----------------------------------------------------------------------------
459 static void
android_media_AudioTrack_flush(JNIEnv * env,jobject thiz)460 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
461 {
462     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
463     if (lpTrack == NULL) {
464         jniThrowException(env, "java/lang/IllegalStateException",
465             "Unable to retrieve AudioTrack pointer for flush()");
466         return;
467     }
468 
469     lpTrack->flush();
470 }
471 
472 // ----------------------------------------------------------------------------
473 static void
android_media_AudioTrack_set_volume(JNIEnv * env,jobject thiz,jfloat leftVol,jfloat rightVol)474 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
475 {
476     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
477     if (lpTrack == NULL) {
478         jniThrowException(env, "java/lang/IllegalStateException",
479             "Unable to retrieve AudioTrack pointer for setVolume()");
480         return;
481     }
482 
483     lpTrack->setVolume(leftVol, rightVol);
484 }
485 
486 // ----------------------------------------------------------------------------
487 
488 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioTrack_release(JNIEnv * env,jobject thiz)489 static void android_media_AudioTrack_release(JNIEnv *env,  jobject thiz) {
490     sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
491     if (lpTrack == NULL) {
492         return;
493     }
494     //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
495     lpTrack->stop();
496 
497     // delete the JNI data
498     AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
499         thiz, javaAudioTrackFields.jniData);
500     // reset the native resources in the Java object so any attempt to access
501     // them after a call to release fails.
502     env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
503 
504     if (pJniStorage) {
505         Mutex::Autolock l(sLock);
506         audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
507         //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
508         while (lpCookie->busy) {
509             if (lpCookie->cond.waitRelative(sLock,
510                                             milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
511                                                     NO_ERROR) {
512                 break;
513             }
514         }
515         sAudioTrackCallBackCookies.remove(lpCookie);
516         // delete global refs created in native_setup
517         env->DeleteGlobalRef(lpCookie->audioTrack_class);
518         env->DeleteGlobalRef(lpCookie->audioTrack_ref);
519         delete pJniStorage;
520     }
521 }
522 
523 
524 // ----------------------------------------------------------------------------
android_media_AudioTrack_finalize(JNIEnv * env,jobject thiz)525 static void android_media_AudioTrack_finalize(JNIEnv *env,  jobject thiz) {
526     //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
527     android_media_AudioTrack_release(env, thiz);
528 }
529 
530 // overloaded JNI array helper functions (same as in android_media_AudioRecord)
531 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)532 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
533     return env->GetByteArrayElements(array, isCopy);
534 }
535 
536 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)537 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
538     env->ReleaseByteArrayElements(array, elems, mode);
539 }
540 
541 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)542 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
543     return env->GetShortArrayElements(array, isCopy);
544 }
545 
546 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)547 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
548     env->ReleaseShortArrayElements(array, elems, mode);
549 }
550 
551 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)552 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
553     return env->GetFloatArrayElements(array, isCopy);
554 }
555 
556 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)557 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
558     env->ReleaseFloatArrayElements(array, elems, mode);
559 }
560 
561 // ----------------------------------------------------------------------------
562 template <typename T>
writeToTrack(const sp<AudioTrack> & track,jint audioFormat,const T * data,jint offsetInSamples,jint sizeInSamples,bool blocking)563 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
564                          jint offsetInSamples, jint sizeInSamples, bool blocking) {
565     // give the data to the native AudioTrack object (the data starts at the offset)
566     ssize_t written = 0;
567     // regular write() or copy the data to the AudioTrack's shared memory?
568     size_t sizeInBytes = sizeInSamples * sizeof(T);
569     if (track->sharedBuffer() == 0) {
570         written = track->write(data + offsetInSamples, sizeInBytes, blocking);
571         // for compatibility with earlier behavior of write(), return 0 in this case
572         if (written == (ssize_t) WOULD_BLOCK) {
573             written = 0;
574         }
575     } else {
576         // writing to shared memory, check for capacity
577         if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
578             sizeInBytes = track->sharedBuffer()->size();
579         }
580         memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
581         written = sizeInBytes;
582     }
583     if (written > 0) {
584         return written / sizeof(T);
585     }
586     // for compatibility, error codes pass through unchanged
587     return written;
588 }
589 
590 // ----------------------------------------------------------------------------
591 template <typename T>
android_media_AudioTrack_writeArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jint javaAudioFormat,jboolean isWriteBlocking)592 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
593                                                 T javaAudioData,
594                                                 jint offsetInSamples, jint sizeInSamples,
595                                                 jint javaAudioFormat,
596                                                 jboolean isWriteBlocking) {
597     //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
598     //        offsetInSamples, sizeInSamples);
599     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
600     if (lpTrack == NULL) {
601         jniThrowException(env, "java/lang/IllegalStateException",
602             "Unable to retrieve AudioTrack pointer for write()");
603         return (jint)AUDIO_JAVA_INVALID_OPERATION;
604     }
605 
606     if (javaAudioData == NULL) {
607         ALOGE("NULL java array of audio data to play");
608         return (jint)AUDIO_JAVA_BAD_VALUE;
609     }
610 
611     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
612     // a way that it becomes much more efficient. When doing so, we will have to prevent the
613     // AudioSystem callback to be called while in critical section (in case of media server
614     // process crash for instance)
615 
616     // get the pointer for the audio data from the java array
617     auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
618     if (cAudioData == NULL) {
619         ALOGE("Error retrieving source of audio data to play");
620         return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
621     }
622 
623     jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
624             offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
625 
626     envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
627 
628     //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
629     //        (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
630     return samplesWritten;
631 }
632 
633 // ----------------------------------------------------------------------------
android_media_AudioTrack_write_native_bytes(JNIEnv * env,jobject thiz,jbyteArray javaBytes,jint byteOffset,jint sizeInBytes,jint javaAudioFormat,jboolean isWriteBlocking)634 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
635         jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
636         jint javaAudioFormat, jboolean isWriteBlocking) {
637     //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
638     //    offsetInBytes, sizeInBytes);
639     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
640     if (lpTrack == NULL) {
641         jniThrowException(env, "java/lang/IllegalStateException",
642                 "Unable to retrieve AudioTrack pointer for write()");
643         return (jint)AUDIO_JAVA_INVALID_OPERATION;
644     }
645 
646     ScopedBytesRO bytes(env, javaBytes);
647     if (bytes.get() == NULL) {
648         ALOGE("Error retrieving source of audio data to play, can't play");
649         return (jint)AUDIO_JAVA_BAD_VALUE;
650     }
651 
652     jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
653             sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
654 
655     return written;
656 }
657 
658 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_native_frame_count(JNIEnv * env,jobject thiz)659 static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
660     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
661     if (lpTrack == NULL) {
662         jniThrowException(env, "java/lang/IllegalStateException",
663             "Unable to retrieve AudioTrack pointer for frameCount()");
664         return (jint)AUDIO_JAVA_ERROR;
665     }
666 
667     return lpTrack->frameCount();
668 }
669 
670 
671 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_rate(JNIEnv * env,jobject thiz,jint sampleRateInHz)672 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
673         jint sampleRateInHz) {
674     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
675     if (lpTrack == NULL) {
676         jniThrowException(env, "java/lang/IllegalStateException",
677             "Unable to retrieve AudioTrack pointer for setSampleRate()");
678         return (jint)AUDIO_JAVA_ERROR;
679     }
680     return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
681 }
682 
683 
684 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_rate(JNIEnv * env,jobject thiz)685 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
686     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
687     if (lpTrack == NULL) {
688         jniThrowException(env, "java/lang/IllegalStateException",
689             "Unable to retrieve AudioTrack pointer for getSampleRate()");
690         return (jint)AUDIO_JAVA_ERROR;
691     }
692     return (jint) lpTrack->getSampleRate();
693 }
694 
695 
696 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_params(JNIEnv * env,jobject thiz,jobject params)697 static void android_media_AudioTrack_set_playback_params(JNIEnv *env,  jobject thiz,
698         jobject params) {
699     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
700     if (lpTrack == NULL) {
701         jniThrowException(env, "java/lang/IllegalStateException",
702             "AudioTrack not initialized");
703         return;
704     }
705 
706     PlaybackParams pbp;
707     pbp.fillFromJobject(env, gPlaybackParamsFields, params);
708 
709     ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
710             pbp.speedSet, pbp.audioRate.mSpeed,
711             pbp.pitchSet, pbp.audioRate.mPitch,
712             pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
713             pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
714 
715     // to simulate partially set params, we do a read-modify-write.
716     // TODO: pass in the valid set mask into AudioTrack.
717     AudioPlaybackRate rate = lpTrack->getPlaybackRate();
718     bool updatedRate = false;
719     if (pbp.speedSet) {
720         rate.mSpeed = pbp.audioRate.mSpeed;
721         updatedRate = true;
722     }
723     if (pbp.pitchSet) {
724         rate.mPitch = pbp.audioRate.mPitch;
725         updatedRate = true;
726     }
727     if (pbp.audioFallbackModeSet) {
728         rate.mFallbackMode = pbp.audioRate.mFallbackMode;
729         updatedRate = true;
730     }
731     if (pbp.audioStretchModeSet) {
732         rate.mStretchMode = pbp.audioRate.mStretchMode;
733         updatedRate = true;
734     }
735     if (updatedRate) {
736         if (lpTrack->setPlaybackRate(rate) != OK) {
737             jniThrowException(env, "java/lang/IllegalArgumentException",
738                     "arguments out of range");
739         }
740     }
741 }
742 
743 
744 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_params(JNIEnv * env,jobject thiz,jobject params)745 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env,  jobject thiz,
746         jobject params) {
747     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
748     if (lpTrack == NULL) {
749         jniThrowException(env, "java/lang/IllegalStateException",
750             "AudioTrack not initialized");
751         return NULL;
752     }
753 
754     PlaybackParams pbs;
755     pbs.audioRate = lpTrack->getPlaybackRate();
756     pbs.speedSet = true;
757     pbs.pitchSet = true;
758     pbs.audioFallbackModeSet = true;
759     pbs.audioStretchModeSet = true;
760     return pbs.asJobject(env, gPlaybackParamsFields);
761 }
762 
763 
764 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)765 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
766         jint markerPos) {
767     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
768     if (lpTrack == NULL) {
769         jniThrowException(env, "java/lang/IllegalStateException",
770             "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
771         return (jint)AUDIO_JAVA_ERROR;
772     }
773     return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
774 }
775 
776 
777 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_marker_pos(JNIEnv * env,jobject thiz)778 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
779     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
780     uint32_t markerPos = 0;
781 
782     if (lpTrack == NULL) {
783         jniThrowException(env, "java/lang/IllegalStateException",
784             "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
785         return (jint)AUDIO_JAVA_ERROR;
786     }
787     lpTrack->getMarkerPosition(&markerPos);
788     return (jint)markerPos;
789 }
790 
791 
792 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)793 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
794         jint period) {
795     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
796     if (lpTrack == NULL) {
797         jniThrowException(env, "java/lang/IllegalStateException",
798             "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
799         return (jint)AUDIO_JAVA_ERROR;
800     }
801     return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
802 }
803 
804 
805 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_pos_update_period(JNIEnv * env,jobject thiz)806 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
807     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
808     uint32_t period = 0;
809 
810     if (lpTrack == NULL) {
811         jniThrowException(env, "java/lang/IllegalStateException",
812             "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
813         return (jint)AUDIO_JAVA_ERROR;
814     }
815     lpTrack->getPositionUpdatePeriod(&period);
816     return (jint)period;
817 }
818 
819 
820 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_position(JNIEnv * env,jobject thiz,jint position)821 static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
822         jint position) {
823     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
824     if (lpTrack == NULL) {
825         jniThrowException(env, "java/lang/IllegalStateException",
826             "Unable to retrieve AudioTrack pointer for setPosition()");
827         return (jint)AUDIO_JAVA_ERROR;
828     }
829     return nativeToJavaStatus( lpTrack->setPosition(position) );
830 }
831 
832 
833 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_position(JNIEnv * env,jobject thiz)834 static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
835     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
836     uint32_t position = 0;
837 
838     if (lpTrack == NULL) {
839         jniThrowException(env, "java/lang/IllegalStateException",
840             "Unable to retrieve AudioTrack pointer for getPosition()");
841         return (jint)AUDIO_JAVA_ERROR;
842     }
843     lpTrack->getPosition(&position);
844     return (jint)position;
845 }
846 
847 
848 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_latency(JNIEnv * env,jobject thiz)849 static jint android_media_AudioTrack_get_latency(JNIEnv *env,  jobject thiz) {
850     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
851 
852     if (lpTrack == NULL) {
853         jniThrowException(env, "java/lang/IllegalStateException",
854             "Unable to retrieve AudioTrack pointer for latency()");
855         return (jint)AUDIO_JAVA_ERROR;
856     }
857     return (jint)lpTrack->latency();
858 }
859 
860 
861 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_timestamp(JNIEnv * env,jobject thiz,jlongArray jTimestamp)862 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env,  jobject thiz, jlongArray jTimestamp) {
863     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
864 
865     if (lpTrack == NULL) {
866         ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
867         return (jint)AUDIO_JAVA_ERROR;
868     }
869     AudioTimestamp timestamp;
870     status_t status = lpTrack->getTimestamp(timestamp);
871     if (status == OK) {
872         jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
873         if (nTimestamp == NULL) {
874             ALOGE("Unable to get array for getTimestamp()");
875             return (jint)AUDIO_JAVA_ERROR;
876         }
877         nTimestamp[0] = (jlong) timestamp.mPosition;
878         nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
879         env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
880     }
881     return (jint) nativeToJavaStatus(status);
882 }
883 
884 
885 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_loop(JNIEnv * env,jobject thiz,jint loopStart,jint loopEnd,jint loopCount)886 static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
887         jint loopStart, jint loopEnd, jint loopCount) {
888     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
889     if (lpTrack == NULL) {
890         jniThrowException(env, "java/lang/IllegalStateException",
891             "Unable to retrieve AudioTrack pointer for setLoop()");
892         return (jint)AUDIO_JAVA_ERROR;
893     }
894     return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
895 }
896 
897 
898 // ----------------------------------------------------------------------------
android_media_AudioTrack_reload(JNIEnv * env,jobject thiz)899 static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
900     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
901     if (lpTrack == NULL) {
902         jniThrowException(env, "java/lang/IllegalStateException",
903             "Unable to retrieve AudioTrack pointer for reload()");
904         return (jint)AUDIO_JAVA_ERROR;
905     }
906     return nativeToJavaStatus( lpTrack->reload() );
907 }
908 
909 
910 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_output_sample_rate(JNIEnv * env,jobject thiz,jint javaStreamType)911 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz,
912         jint javaStreamType) {
913     uint32_t afSamplingRate;
914     // convert the stream type from Java to native value
915     // FIXME: code duplication with android_media_AudioTrack_setup()
916     audio_stream_type_t nativeStreamType;
917     switch (javaStreamType) {
918     case AUDIO_STREAM_VOICE_CALL:
919     case AUDIO_STREAM_SYSTEM:
920     case AUDIO_STREAM_RING:
921     case AUDIO_STREAM_MUSIC:
922     case AUDIO_STREAM_ALARM:
923     case AUDIO_STREAM_NOTIFICATION:
924     case AUDIO_STREAM_BLUETOOTH_SCO:
925     case AUDIO_STREAM_DTMF:
926         nativeStreamType = (audio_stream_type_t) javaStreamType;
927         break;
928     default:
929         nativeStreamType = AUDIO_STREAM_DEFAULT;
930         break;
931     }
932 
933     status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
934     if (status != NO_ERROR) {
935         ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
936               "in AudioTrack JNI", status, nativeStreamType);
937         return DEFAULT_OUTPUT_SAMPLE_RATE;
938     } else {
939         return afSamplingRate;
940     }
941 }
942 
943 
944 // ----------------------------------------------------------------------------
945 // returns the minimum required size for the successful creation of a streaming AudioTrack
946 // 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)947 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
948     jint sampleRateInHertz, jint channelCount, jint audioFormat) {
949 
950     size_t frameCount;
951     const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
952             sampleRateInHertz);
953     if (status != NO_ERROR) {
954         ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
955                 sampleRateInHertz, status);
956         return -1;
957     }
958     const audio_format_t format = audioFormatToNative(audioFormat);
959     if (audio_is_linear_pcm(format)) {
960         const size_t bytesPerSample = audio_bytes_per_sample(format);
961         return frameCount * channelCount * bytesPerSample;
962     } else {
963         return frameCount;
964     }
965 }
966 
967 // ----------------------------------------------------------------------------
968 static jint
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv * env,jobject thiz,jfloat level)969 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
970 {
971     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
972     if (lpTrack == NULL ) {
973         jniThrowException(env, "java/lang/IllegalStateException",
974             "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
975         return -1;
976     }
977 
978     status_t status = lpTrack->setAuxEffectSendLevel(level);
979     if (status != NO_ERROR) {
980         ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
981                 level, status);
982     }
983     return (jint) status;
984 }
985 
986 // ----------------------------------------------------------------------------
android_media_AudioTrack_attachAuxEffect(JNIEnv * env,jobject thiz,jint effectId)987 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env,  jobject thiz,
988         jint effectId) {
989     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
990     if (lpTrack == NULL) {
991         jniThrowException(env, "java/lang/IllegalStateException",
992             "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
993         return (jint)AUDIO_JAVA_ERROR;
994     }
995     return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
996 }
997 
android_media_AudioTrack_setOutputDevice(JNIEnv * env,jobject thiz,jint device_id)998 static jboolean android_media_AudioTrack_setOutputDevice(
999                 JNIEnv *env,  jobject thiz, jint device_id) {
1000 
1001     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1002     if (lpTrack == 0) {
1003         return false;
1004     }
1005     return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1006 }
1007 
android_media_AudioTrack_getRoutedDeviceId(JNIEnv * env,jobject thiz)1008 static jint android_media_AudioTrack_getRoutedDeviceId(
1009                 JNIEnv *env,  jobject thiz) {
1010 
1011     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1012     if (lpTrack == NULL) {
1013         return 0;
1014     }
1015     return (jint)lpTrack->getRoutedDeviceId();
1016 }
1017 
android_media_AudioTrack_enableDeviceCallback(JNIEnv * env,jobject thiz)1018 static void android_media_AudioTrack_enableDeviceCallback(
1019                 JNIEnv *env,  jobject thiz) {
1020 
1021     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1022     if (lpTrack == NULL) {
1023         return;
1024     }
1025     AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1026         thiz, javaAudioTrackFields.jniData);
1027     if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1028         return;
1029     }
1030     pJniStorage->mDeviceCallback =
1031     new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1032                           javaAudioTrackFields.postNativeEventInJava);
1033     lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1034 }
1035 
android_media_AudioTrack_disableDeviceCallback(JNIEnv * env,jobject thiz)1036 static void android_media_AudioTrack_disableDeviceCallback(
1037                 JNIEnv *env,  jobject thiz) {
1038 
1039     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1040     if (lpTrack == NULL) {
1041         return;
1042     }
1043     AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1044         thiz, javaAudioTrackFields.jniData);
1045     if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1046         return;
1047     }
1048     lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1049     pJniStorage->mDeviceCallback.clear();
1050 }
1051 
1052 
1053 // ----------------------------------------------------------------------------
1054 // ----------------------------------------------------------------------------
1055 static JNINativeMethod gMethods[] = {
1056     // name,              signature,     funcPtr
1057     {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
1058     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
1059     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
1060     {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
1061     {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIIII[I)I",
1062                                          (void *)android_media_AudioTrack_setup},
1063     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
1064     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
1065     {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1066     {"native_write_native_bytes",
1067                              "(Ljava/lang/Object;IIIZ)I",
1068                                          (void *)android_media_AudioTrack_write_native_bytes},
1069     {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1070     {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1071     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
1072     {"native_get_native_frame_count",
1073                              "()I",      (void *)android_media_AudioTrack_get_native_frame_count},
1074     {"native_set_playback_rate",
1075                              "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
1076     {"native_get_playback_rate",
1077                              "()I",      (void *)android_media_AudioTrack_get_playback_rate},
1078     {"native_set_playback_params",
1079                              "(Landroid/media/PlaybackParams;)V",
1080                                          (void *)android_media_AudioTrack_set_playback_params},
1081     {"native_get_playback_params",
1082                              "()Landroid/media/PlaybackParams;",
1083                                          (void *)android_media_AudioTrack_get_playback_params},
1084     {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
1085     {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
1086     {"native_set_pos_update_period",
1087                              "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
1088     {"native_get_pos_update_period",
1089                              "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
1090     {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
1091     {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
1092     {"native_get_latency",   "()I",      (void *)android_media_AudioTrack_get_latency},
1093     {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
1094     {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
1095     {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
1096     {"native_get_output_sample_rate",
1097                              "(I)I",      (void *)android_media_AudioTrack_get_output_sample_rate},
1098     {"native_get_min_buff_size",
1099                              "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
1100     {"native_setAuxEffectSendLevel",
1101                              "(F)I",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1102     {"native_attachAuxEffect",
1103                              "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
1104     {"native_setOutputDevice", "(I)Z",
1105                              (void *)android_media_AudioTrack_setOutputDevice},
1106     {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1107     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1108     {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1109 };
1110 
1111 
1112 // field names found in android/media/AudioTrack.java
1113 #define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
1114 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
1115 #define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
1116 #define JAVA_STREAMTYPE_FIELD_NAME                      "mStreamType"
1117 
1118 // ----------------------------------------------------------------------------
1119 // preconditions:
1120 //    theClass is valid
android_media_getIntConstantFromClass(JNIEnv * pEnv,jclass theClass,const char * className,const char * constName,int * constVal)1121 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1122                              const char* constName, int* constVal) {
1123     jfieldID javaConst = NULL;
1124     javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1125     if (javaConst != NULL) {
1126         *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1127         return true;
1128     } else {
1129         ALOGE("Can't find %s.%s", className, constName);
1130         return false;
1131     }
1132 }
1133 
1134 
1135 // ----------------------------------------------------------------------------
register_android_media_AudioTrack(JNIEnv * env)1136 int register_android_media_AudioTrack(JNIEnv *env)
1137 {
1138     javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1139     javaAudioTrackFields.postNativeEventInJava = NULL;
1140 
1141     // Get the AudioTrack class
1142     jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1143 
1144     // Get the postEvent method
1145     javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1146             audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1147             "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1148 
1149     // Get the variables fields
1150     //      nativeTrackInJavaObj
1151     javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1152             audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1153     //      jniData
1154     javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1155             audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1156     //      fieldStreamType
1157     javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1158             audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1159 
1160     env->DeleteLocalRef(audioTrackClass);
1161 
1162     // Get the AudioAttributes class and fields
1163     jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1164     javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1165     javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1166             audioAttrClass, "mContentType", "I");
1167     javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1168     javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1169             audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
1170 
1171     env->DeleteLocalRef(audioAttrClass);
1172 
1173     // initialize PlaybackParams field info
1174     gPlaybackParamsFields.init(env);
1175 
1176     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1177 }
1178 
1179 
1180 // ----------------------------------------------------------------------------
1181