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