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