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