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 <stdio.h>
     21 #include <unistd.h>
     22 #include <fcntl.h>
     23 #include <math.h>
     24 
     25 #include "jni.h"
     26 #include "JNIHelp.h"
     27 #include "android_runtime/AndroidRuntime.h"
     28 
     29 #include "utils/Log.h"
     30 #include "media/AudioSystem.h"
     31 #include "media/AudioTrack.h"
     32 
     33 #include <binder/MemoryHeapBase.h>
     34 #include <binder/MemoryBase.h>
     35 
     36 
     37 // ----------------------------------------------------------------------------
     38 
     39 using namespace android;
     40 
     41 // ----------------------------------------------------------------------------
     42 static const char* const kClassPathName = "android/media/AudioTrack";
     43 
     44 struct fields_t {
     45     // these fields provide access from C++ to the...
     46     jclass    audioTrackClass;       //... AudioTrack class
     47     jmethodID postNativeEventInJava; //... event post callback method
     48     int       PCM16;                 //...  format constants
     49     int       PCM8;                  //...  format constants
     50     int       STREAM_VOICE_CALL;     //...  stream type constants
     51     int       STREAM_SYSTEM;         //...  stream type constants
     52     int       STREAM_RING;           //...  stream type constants
     53     int       STREAM_MUSIC;          //...  stream type constants
     54     int       STREAM_ALARM;          //...  stream type constants
     55     int       STREAM_NOTIFICATION;   //...  stream type constants
     56     int       STREAM_BLUETOOTH_SCO;  //...  stream type constants
     57     int       STREAM_DTMF;           //...  stream type constants
     58     int       MODE_STREAM;           //...  memory mode
     59     int       MODE_STATIC;           //...  memory mode
     60     jfieldID  nativeTrackInJavaObj;  // stores in Java the native AudioTrack object
     61     jfieldID  jniData;      // stores in Java additional resources used by the native AudioTrack
     62 };
     63 static fields_t javaAudioTrackFields;
     64 
     65 struct audiotrack_callback_cookie {
     66     jclass      audioTrack_class;
     67     jobject     audioTrack_ref;
     68  };
     69 
     70 // ----------------------------------------------------------------------------
     71 class AudioTrackJniStorage {
     72     public:
     73         sp<MemoryHeapBase>         mMemHeap;
     74         sp<MemoryBase>             mMemBase;
     75         audiotrack_callback_cookie mCallbackData;
     76         int                        mStreamType;
     77 
     78     AudioTrackJniStorage() {
     79         mCallbackData.audioTrack_class = 0;
     80         mCallbackData.audioTrack_ref = 0;
     81         mStreamType = AudioSystem::DEFAULT;
     82     }
     83 
     84     ~AudioTrackJniStorage() {
     85         mMemBase.clear();
     86         mMemHeap.clear();
     87     }
     88 
     89     bool allocSharedMem(int sizeInBytes) {
     90         mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
     91         if (mMemHeap->getHeapID() < 0) {
     92             return false;
     93         }
     94         mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
     95         return true;
     96     }
     97 };
     98 
     99 // ----------------------------------------------------------------------------
    100 #define DEFAULT_OUTPUT_SAMPLE_RATE   44100
    101 
    102 #define AUDIOTRACK_SUCCESS                         0
    103 #define AUDIOTRACK_ERROR                           -1
    104 #define AUDIOTRACK_ERROR_BAD_VALUE                 -2
    105 #define AUDIOTRACK_ERROR_INVALID_OPERATION         -3
    106 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM         -16
    107 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17
    108 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT       -18
    109 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE   -19
    110 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED    -20
    111 
    112 
    113 jint android_media_translateErrorCode(int code) {
    114     switch(code) {
    115     case NO_ERROR:
    116         return AUDIOTRACK_SUCCESS;
    117     case BAD_VALUE:
    118         return AUDIOTRACK_ERROR_BAD_VALUE;
    119     case INVALID_OPERATION:
    120         return AUDIOTRACK_ERROR_INVALID_OPERATION;
    121     default:
    122         return AUDIOTRACK_ERROR;
    123     }
    124 }
    125 
    126 
    127 // ----------------------------------------------------------------------------
    128 static void audioCallback(int event, void* user, void *info) {
    129     if (event == AudioTrack::EVENT_MORE_DATA) {
    130         // set size to 0 to signal we're not using the callback to write more data
    131         AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
    132         pBuff->size = 0;
    133 
    134     } else if (event == AudioTrack::EVENT_MARKER) {
    135         audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
    136         JNIEnv *env = AndroidRuntime::getJNIEnv();
    137         if (user && env) {
    138             env->CallStaticVoidMethod(
    139                 callbackInfo->audioTrack_class,
    140                 javaAudioTrackFields.postNativeEventInJava,
    141                 callbackInfo->audioTrack_ref, event, 0,0, NULL);
    142             if (env->ExceptionCheck()) {
    143                 env->ExceptionDescribe();
    144                 env->ExceptionClear();
    145             }
    146         }
    147 
    148     } else if (event == AudioTrack::EVENT_NEW_POS) {
    149         audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
    150         JNIEnv *env = AndroidRuntime::getJNIEnv();
    151         if (user && env) {
    152             env->CallStaticVoidMethod(
    153                 callbackInfo->audioTrack_class,
    154                 javaAudioTrackFields.postNativeEventInJava,
    155                 callbackInfo->audioTrack_ref, event, 0,0, NULL);
    156             if (env->ExceptionCheck()) {
    157                 env->ExceptionDescribe();
    158                 env->ExceptionClear();
    159             }
    160         }
    161     }
    162 }
    163 
    164 
    165 // ----------------------------------------------------------------------------
    166 static int
    167 android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
    168         jint streamType, jint sampleRateInHertz, jint channels,
    169         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession)
    170 {
    171     LOGV("sampleRate=%d, audioFormat(from Java)=%d, channels=%x, buffSize=%d",
    172         sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
    173     int afSampleRate;
    174     int afFrameCount;
    175 
    176     if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
    177         LOGE("Error creating AudioTrack: Could not get AudioSystem frame count.");
    178         return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
    179     }
    180     if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
    181         LOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate.");
    182         return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
    183     }
    184 
    185     if (!AudioSystem::isOutputChannel(channels)) {
    186         LOGE("Error creating AudioTrack: invalid channel mask.");
    187         return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
    188     }
    189     int nbChannels = AudioSystem::popCount(channels);
    190 
    191     // check the stream type
    192     AudioSystem::stream_type atStreamType;
    193     if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) {
    194         atStreamType = AudioSystem::VOICE_CALL;
    195     } else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) {
    196         atStreamType = AudioSystem::SYSTEM;
    197     } else if (streamType == javaAudioTrackFields.STREAM_RING) {
    198         atStreamType = AudioSystem::RING;
    199     } else if (streamType == javaAudioTrackFields.STREAM_MUSIC) {
    200         atStreamType = AudioSystem::MUSIC;
    201     } else if (streamType == javaAudioTrackFields.STREAM_ALARM) {
    202         atStreamType = AudioSystem::ALARM;
    203     } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
    204         atStreamType = AudioSystem::NOTIFICATION;
    205     } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
    206         atStreamType = AudioSystem::BLUETOOTH_SCO;
    207     } else if (streamType == javaAudioTrackFields.STREAM_DTMF) {
    208         atStreamType = AudioSystem::DTMF;
    209     } else {
    210         LOGE("Error creating AudioTrack: unknown stream type.");
    211         return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
    212     }
    213 
    214     // check the format.
    215     // This function was called from Java, so we compare the format against the Java constants
    216     if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) {
    217         LOGE("Error creating AudioTrack: unsupported audio format.");
    218         return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
    219     }
    220 
    221     // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class
    222     // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled
    223     // in android_media_AudioTrack_native_write()
    224     if ((audioFormat == javaAudioTrackFields.PCM8)
    225         && (memoryMode == javaAudioTrackFields.MODE_STATIC)) {
    226         LOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \
    227             buff size of %dbytes, switching to 16bit, buff size of %dbytes",
    228             buffSizeInBytes, 2*buffSizeInBytes);
    229         audioFormat = javaAudioTrackFields.PCM16;
    230         // we will need twice the memory to store the data
    231         buffSizeInBytes *= 2;
    232     }
    233 
    234     // compute the frame count
    235     int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
    236     int format = audioFormat == javaAudioTrackFields.PCM16 ?
    237             AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
    238     int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
    239 
    240     AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
    241 
    242     // initialize the callback information:
    243     // this data will be passed with every AudioTrack callback
    244     jclass clazz = env->GetObjectClass(thiz);
    245     if (clazz == NULL) {
    246         LOGE("Can't find %s when setting up callback.", kClassPathName);
    247         delete lpJniStorage;
    248         return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
    249     }
    250     lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
    251     // we use a weak reference so the AudioTrack object can be garbage collected.
    252     lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
    253 
    254     lpJniStorage->mStreamType = atStreamType;
    255 
    256     if (jSession == NULL) {
    257         LOGE("Error creating AudioTrack: invalid session ID pointer");
    258         delete lpJniStorage;
    259         return AUDIOTRACK_ERROR;
    260     }
    261 
    262     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    263     if (nSession == NULL) {
    264         LOGE("Error creating AudioTrack: Error retrieving session id pointer");
    265         delete lpJniStorage;
    266         return AUDIOTRACK_ERROR;
    267     }
    268     int sessionId = nSession[0];
    269     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    270     nSession = NULL;
    271 
    272     // create the native AudioTrack object
    273     AudioTrack* lpTrack = new AudioTrack();
    274     if (lpTrack == NULL) {
    275         LOGE("Error creating uninitialized AudioTrack");
    276         goto native_track_failure;
    277     }
    278 
    279     // initialize the native AudioTrack object
    280     if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
    281 
    282         lpTrack->set(
    283             atStreamType,// stream type
    284             sampleRateInHertz,
    285             format,// word length, PCM
    286             channels,
    287             frameCount,
    288             0,// flags
    289             audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
    290             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
    291             0,// shared mem
    292             true,// thread can call Java
    293             sessionId);// audio session ID
    294 
    295     } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
    296         // AudioTrack is using shared memory
    297 
    298         if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
    299             LOGE("Error creating AudioTrack in static mode: error creating mem heap base");
    300             goto native_init_failure;
    301         }
    302 
    303         lpTrack->set(
    304             atStreamType,// stream type
    305             sampleRateInHertz,
    306             format,// word length, PCM
    307             channels,
    308             frameCount,
    309             0,// flags
    310             audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
    311             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
    312             lpJniStorage->mMemBase,// shared mem
    313             true,// thread can call Java
    314             sessionId);// audio session ID
    315     }
    316 
    317     if (lpTrack->initCheck() != NO_ERROR) {
    318         LOGE("Error initializing AudioTrack");
    319         goto native_init_failure;
    320     }
    321 
    322     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    323     if (nSession == NULL) {
    324         LOGE("Error creating AudioTrack: Error retrieving session id pointer");
    325         goto native_init_failure;
    326     }
    327     // read the audio session ID back from AudioTrack in case we create a new session
    328     nSession[0] = lpTrack->getSessionId();
    329     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    330     nSession = NULL;
    331 
    332     // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
    333     // of the Java object (in mNativeTrackInJavaObj)
    334     env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
    335 
    336     // save the JNI resources so we can free them later
    337     //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
    338     env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
    339 
    340     return AUDIOTRACK_SUCCESS;
    341 
    342     // failures:
    343 native_init_failure:
    344     delete lpTrack;
    345     env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
    346 
    347 native_track_failure:
    348     if (nSession != NULL) {
    349         env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    350     }
    351     env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
    352     env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
    353     delete lpJniStorage;
    354     env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
    355     return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
    356 
    357 }
    358 
    359 
    360 // ----------------------------------------------------------------------------
    361 static void
    362 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
    363 {
    364     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    365         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    366     if (lpTrack == NULL ) {
    367         jniThrowException(env, "java/lang/IllegalStateException",
    368             "Unable to retrieve AudioTrack pointer for start()");
    369         return;
    370     }
    371 
    372     lpTrack->start();
    373 }
    374 
    375 
    376 // ----------------------------------------------------------------------------
    377 static void
    378 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
    379 {
    380     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    381         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    382     if (lpTrack == NULL ) {
    383         jniThrowException(env, "java/lang/IllegalStateException",
    384             "Unable to retrieve AudioTrack pointer for stop()");
    385         return;
    386     }
    387 
    388     lpTrack->stop();
    389 }
    390 
    391 
    392 // ----------------------------------------------------------------------------
    393 static void
    394 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
    395 {
    396     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    397         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    398     if (lpTrack == NULL ) {
    399         jniThrowException(env, "java/lang/IllegalStateException",
    400             "Unable to retrieve AudioTrack pointer for pause()");
    401         return;
    402     }
    403 
    404     lpTrack->pause();
    405 }
    406 
    407 
    408 // ----------------------------------------------------------------------------
    409 static void
    410 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
    411 {
    412     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    413         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    414     if (lpTrack == NULL ) {
    415         jniThrowException(env, "java/lang/IllegalStateException",
    416             "Unable to retrieve AudioTrack pointer for flush()");
    417         return;
    418     }
    419 
    420     lpTrack->flush();
    421 }
    422 
    423 // ----------------------------------------------------------------------------
    424 static void
    425 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
    426 {
    427     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    428         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    429     if (lpTrack == NULL ) {
    430         jniThrowException(env, "java/lang/IllegalStateException",
    431             "Unable to retrieve AudioTrack pointer for setVolume()");
    432         return;
    433     }
    434 
    435     lpTrack->setVolume(leftVol, rightVol);
    436 }
    437 
    438 // ----------------------------------------------------------------------------
    439 static void android_media_AudioTrack_native_finalize(JNIEnv *env,  jobject thiz) {
    440     //LOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
    441 
    442     // delete the AudioTrack object
    443     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    444         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    445     if (lpTrack) {
    446         //LOGV("deleting lpTrack: %x\n", (int)lpTrack);
    447         lpTrack->stop();
    448         delete lpTrack;
    449     }
    450 
    451     // delete the JNI data
    452     AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
    453         thiz, javaAudioTrackFields.jniData);
    454     if (pJniStorage) {
    455         // delete global refs created in native_setup
    456         env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_class);
    457         env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_ref);
    458         //LOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
    459         delete pJniStorage;
    460     }
    461 }
    462 
    463 // ----------------------------------------------------------------------------
    464 static void android_media_AudioTrack_native_release(JNIEnv *env,  jobject thiz) {
    465 
    466     // do everything a call to finalize would
    467     android_media_AudioTrack_native_finalize(env, thiz);
    468     // + reset the native resources in the Java object so any attempt to access
    469     // them after a call to release fails.
    470     env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
    471     env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
    472 }
    473 
    474 
    475 // ----------------------------------------------------------------------------
    476 jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
    477                   jint offsetInBytes, jint sizeInBytes) {
    478     // give the data to the native AudioTrack object (the data starts at the offset)
    479     ssize_t written = 0;
    480     // regular write() or copy the data to the AudioTrack's shared memory?
    481     if (pTrack->sharedBuffer() == 0) {
    482         written = pTrack->write(data + offsetInBytes, sizeInBytes);
    483     } else {
    484         if (audioFormat == javaAudioTrackFields.PCM16) {
    485             // writing to shared memory, check for capacity
    486             if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
    487                 sizeInBytes = pTrack->sharedBuffer()->size();
    488             }
    489             memcpy(pTrack->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
    490             written = sizeInBytes;
    491         } else if (audioFormat == javaAudioTrackFields.PCM8) {
    492             // data contains 8bit data we need to expand to 16bit before copying
    493             // to the shared memory
    494             // writing to shared memory, check for capacity,
    495             // note that input data will occupy 2X the input space due to 8 to 16bit conversion
    496             if (((size_t)sizeInBytes)*2 > pTrack->sharedBuffer()->size()) {
    497                 sizeInBytes = pTrack->sharedBuffer()->size() / 2;
    498             }
    499             int count = sizeInBytes;
    500             int16_t *dst = (int16_t *)pTrack->sharedBuffer()->pointer();
    501             const int8_t *src = (const int8_t *)(data + offsetInBytes);
    502             while(count--) {
    503                 *dst++ = (int16_t)(*src++^0x80) << 8;
    504             }
    505             // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide
    506             // the 8bit mixer restriction from the user of this function
    507             written = sizeInBytes;
    508         }
    509     }
    510     return written;
    511 
    512 }
    513 
    514 // ----------------------------------------------------------------------------
    515 static jint android_media_AudioTrack_native_write(JNIEnv *env,  jobject thiz,
    516                                                   jbyteArray javaAudioData,
    517                                                   jint offsetInBytes, jint sizeInBytes,
    518                                                   jint javaAudioFormat) {
    519     jbyte* cAudioData = NULL;
    520     AudioTrack *lpTrack = NULL;
    521     //LOGV("android_media_AudioTrack_native_write(offset=%d, sizeInBytes=%d) called",
    522     //    offsetInBytes, sizeInBytes);
    523 
    524     // get the audio track to load with samples
    525     lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    526     if (lpTrack == NULL) {
    527         jniThrowException(env, "java/lang/IllegalStateException",
    528             "Unable to retrieve AudioTrack pointer for write()");
    529         return 0;
    530     }
    531 
    532     // get the pointer for the audio data from the java array
    533     if (javaAudioData) {
    534         cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
    535         if (cAudioData == NULL) {
    536             LOGE("Error retrieving source of audio data to play, can't play");
    537             return 0; // out of memory or no data to load
    538         }
    539     } else {
    540         LOGE("NULL java array of audio data to play, can't play");
    541         return 0;
    542     }
    543 
    544     jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
    545 
    546     env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
    547 
    548     //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
    549     //     (int)written, (int)(sizeInBytes), (int)offsetInBytes);
    550     return written;
    551 }
    552 
    553 
    554 // ----------------------------------------------------------------------------
    555 static jint android_media_AudioTrack_native_write_short(JNIEnv *env,  jobject thiz,
    556                                                   jshortArray javaAudioData,
    557                                                   jint offsetInShorts, jint sizeInShorts,
    558                                                   jint javaAudioFormat) {
    559     return (android_media_AudioTrack_native_write(env, thiz,
    560                                                  (jbyteArray) javaAudioData,
    561                                                  offsetInShorts*2, sizeInShorts*2,
    562                                                  javaAudioFormat)
    563             / 2);
    564 }
    565 
    566 
    567 // ----------------------------------------------------------------------------
    568 static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
    569     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    570         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    571 
    572     if (lpTrack) {
    573         return lpTrack->frameCount();
    574     } else {
    575         jniThrowException(env, "java/lang/IllegalStateException",
    576             "Unable to retrieve AudioTrack pointer for frameCount()");
    577         return AUDIOTRACK_ERROR;
    578     }
    579 }
    580 
    581 
    582 // ----------------------------------------------------------------------------
    583 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
    584         jint sampleRateInHz) {
    585     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    586                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    587 
    588     if (lpTrack) {
    589         return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz));
    590     } else {
    591         jniThrowException(env, "java/lang/IllegalStateException",
    592             "Unable to retrieve AudioTrack pointer for setSampleRate()");
    593         return AUDIOTRACK_ERROR;
    594     }
    595 }
    596 
    597 
    598 // ----------------------------------------------------------------------------
    599 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
    600     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    601                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    602 
    603     if (lpTrack) {
    604         return (jint) lpTrack->getSampleRate();
    605     } else {
    606         jniThrowException(env, "java/lang/IllegalStateException",
    607             "Unable to retrieve AudioTrack pointer for getSampleRate()");
    608         return AUDIOTRACK_ERROR;
    609     }
    610 }
    611 
    612 
    613 // ----------------------------------------------------------------------------
    614 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
    615         jint markerPos) {
    616 
    617     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    618                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    619 
    620     if (lpTrack) {
    621         return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
    622     } else {
    623         jniThrowException(env, "java/lang/IllegalStateException",
    624             "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
    625         return AUDIOTRACK_ERROR;
    626     }
    627 }
    628 
    629 
    630 // ----------------------------------------------------------------------------
    631 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
    632 
    633     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    634                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    635     uint32_t markerPos = 0;
    636 
    637     if (lpTrack) {
    638         lpTrack->getMarkerPosition(&markerPos);
    639         return (jint)markerPos;
    640     } else {
    641         jniThrowException(env, "java/lang/IllegalStateException",
    642             "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
    643         return AUDIOTRACK_ERROR;
    644     }
    645 }
    646 
    647 
    648 // ----------------------------------------------------------------------------
    649 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
    650         jint period) {
    651 
    652     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    653                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    654 
    655     if (lpTrack) {
    656         return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
    657     } else {
    658         jniThrowException(env, "java/lang/IllegalStateException",
    659             "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
    660         return AUDIOTRACK_ERROR;
    661     }
    662 }
    663 
    664 
    665 // ----------------------------------------------------------------------------
    666 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
    667 
    668     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    669                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    670     uint32_t period = 0;
    671 
    672     if (lpTrack) {
    673         lpTrack->getPositionUpdatePeriod(&period);
    674         return (jint)period;
    675     } else {
    676         jniThrowException(env, "java/lang/IllegalStateException",
    677             "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
    678         return AUDIOTRACK_ERROR;
    679     }
    680 }
    681 
    682 
    683 // ----------------------------------------------------------------------------
    684 static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
    685         jint position) {
    686 
    687     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    688                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    689 
    690     if (lpTrack) {
    691         return android_media_translateErrorCode( lpTrack->setPosition(position) );
    692     } else {
    693         jniThrowException(env, "java/lang/IllegalStateException",
    694             "Unable to retrieve AudioTrack pointer for setPosition()");
    695         return AUDIOTRACK_ERROR;
    696     }
    697 }
    698 
    699 
    700 // ----------------------------------------------------------------------------
    701 static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
    702 
    703     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    704                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    705     uint32_t position = 0;
    706 
    707     if (lpTrack) {
    708         lpTrack->getPosition(&position);
    709         return (jint)position;
    710     }  else {
    711         jniThrowException(env, "java/lang/IllegalStateException",
    712             "Unable to retrieve AudioTrack pointer for getPosition()");
    713         return AUDIOTRACK_ERROR;
    714     }
    715 }
    716 
    717 
    718 // ----------------------------------------------------------------------------
    719 static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
    720         jint loopStart, jint loopEnd, jint loopCount) {
    721 
    722      AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    723                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    724      if (lpTrack) {
    725         return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
    726      }  else {
    727         jniThrowException(env, "java/lang/IllegalStateException",
    728             "Unable to retrieve AudioTrack pointer for setLoop()");
    729         return AUDIOTRACK_ERROR;
    730     }
    731 }
    732 
    733 
    734 // ----------------------------------------------------------------------------
    735 static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
    736 
    737      AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    738                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    739      if (lpTrack) {
    740         return android_media_translateErrorCode( lpTrack->reload() );
    741      } else {
    742         jniThrowException(env, "java/lang/IllegalStateException",
    743             "Unable to retrieve AudioTrack pointer for reload()");
    744         return AUDIOTRACK_ERROR;
    745     }
    746 }
    747 
    748 
    749 // ----------------------------------------------------------------------------
    750 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz,
    751         jint javaStreamType) {
    752     int afSamplingRate;
    753     // convert the stream type from Java to native value
    754     // FIXME: code duplication with android_media_AudioTrack_native_setup()
    755     AudioSystem::stream_type nativeStreamType;
    756     if (javaStreamType == javaAudioTrackFields.STREAM_VOICE_CALL) {
    757         nativeStreamType = AudioSystem::VOICE_CALL;
    758     } else if (javaStreamType == javaAudioTrackFields.STREAM_SYSTEM) {
    759         nativeStreamType = AudioSystem::SYSTEM;
    760     } else if (javaStreamType == javaAudioTrackFields.STREAM_RING) {
    761         nativeStreamType = AudioSystem::RING;
    762     } else if (javaStreamType == javaAudioTrackFields.STREAM_MUSIC) {
    763         nativeStreamType = AudioSystem::MUSIC;
    764     } else if (javaStreamType == javaAudioTrackFields.STREAM_ALARM) {
    765         nativeStreamType = AudioSystem::ALARM;
    766     } else if (javaStreamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
    767         nativeStreamType = AudioSystem::NOTIFICATION;
    768     } else if (javaStreamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
    769         nativeStreamType = AudioSystem::BLUETOOTH_SCO;
    770     } else if (javaStreamType == javaAudioTrackFields.STREAM_DTMF) {
    771         nativeStreamType = AudioSystem::DTMF;
    772     } else {
    773         nativeStreamType = AudioSystem::DEFAULT;
    774     }
    775 
    776     if (AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType) != NO_ERROR) {
    777         LOGE("AudioSystem::getOutputSamplingRate() for stream type %d failed in AudioTrack JNI",
    778             nativeStreamType);
    779         return DEFAULT_OUTPUT_SAMPLE_RATE;
    780     } else {
    781         return afSamplingRate;
    782     }
    783 }
    784 
    785 
    786 // ----------------------------------------------------------------------------
    787 // returns the minimum required size for the successful creation of a streaming AudioTrack
    788 // returns -1 if there was an error querying the hardware.
    789 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
    790     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
    791 
    792     int frameCount = 0;
    793     if (AudioTrack::getMinFrameCount(&frameCount, AudioSystem::DEFAULT,
    794             sampleRateInHertz) != NO_ERROR) {
    795         return -1;
    796     }
    797     return frameCount * nbChannels * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1);
    798 }
    799 
    800 // ----------------------------------------------------------------------------
    801 static void
    802 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
    803 {
    804     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    805         thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    806     if (lpTrack == NULL ) {
    807         jniThrowException(env, "java/lang/IllegalStateException",
    808             "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
    809         return;
    810     }
    811 
    812     lpTrack->setAuxEffectSendLevel(level);
    813 }
    814 
    815 // ----------------------------------------------------------------------------
    816 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env,  jobject thiz,
    817         jint effectId) {
    818 
    819     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
    820                 thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    821 
    822     if (lpTrack) {
    823         return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
    824     } else {
    825         jniThrowException(env, "java/lang/IllegalStateException",
    826             "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
    827         return AUDIOTRACK_ERROR;
    828     }
    829 }
    830 
    831 // ----------------------------------------------------------------------------
    832 // ----------------------------------------------------------------------------
    833 static JNINativeMethod gMethods[] = {
    834     // name,              signature,     funcPtr
    835     {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
    836     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
    837     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
    838     {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
    839     {"native_setup",         "(Ljava/lang/Object;IIIIII[I)I",
    840                                          (void *)android_media_AudioTrack_native_setup},
    841     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_native_finalize},
    842     {"native_release",       "()V",      (void *)android_media_AudioTrack_native_release},
    843     {"native_write_byte",    "([BIII)I", (void *)android_media_AudioTrack_native_write},
    844     {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_native_write_short},
    845     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
    846     {"native_get_native_frame_count",
    847                              "()I",      (void *)android_media_AudioTrack_get_native_frame_count},
    848     {"native_set_playback_rate",
    849                              "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
    850     {"native_get_playback_rate",
    851                              "()I",      (void *)android_media_AudioTrack_get_playback_rate},
    852     {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
    853     {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
    854     {"native_set_pos_update_period",
    855                              "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
    856     {"native_get_pos_update_period",
    857                              "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
    858     {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
    859     {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
    860     {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
    861     {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
    862     {"native_get_output_sample_rate",
    863                              "(I)I",      (void *)android_media_AudioTrack_get_output_sample_rate},
    864     {"native_get_min_buff_size",
    865                              "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
    866     {"native_setAuxEffectSendLevel",
    867                              "(F)V",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
    868     {"native_attachAuxEffect",
    869                              "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
    870 };
    871 
    872 
    873 // field names found in android/media/AudioTrack.java
    874 #define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
    875 #define JAVA_CONST_PCM16_NAME                           "ENCODING_PCM_16BIT"
    876 #define JAVA_CONST_PCM8_NAME                            "ENCODING_PCM_8BIT"
    877 #define JAVA_CONST_BUFFER_COUNT_NAME                    "BUFFER_COUNT"
    878 #define JAVA_CONST_STREAM_VOICE_CALL_NAME               "STREAM_VOICE_CALL"
    879 #define JAVA_CONST_STREAM_SYSTEM_NAME                   "STREAM_SYSTEM"
    880 #define JAVA_CONST_STREAM_RING_NAME                     "STREAM_RING"
    881 #define JAVA_CONST_STREAM_MUSIC_NAME                    "STREAM_MUSIC"
    882 #define JAVA_CONST_STREAM_ALARM_NAME                    "STREAM_ALARM"
    883 #define JAVA_CONST_STREAM_NOTIFICATION_NAME             "STREAM_NOTIFICATION"
    884 #define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME            "STREAM_BLUETOOTH_SCO"
    885 #define JAVA_CONST_STREAM_DTMF_NAME                     "STREAM_DTMF"
    886 #define JAVA_CONST_MODE_STREAM_NAME                     "MODE_STREAM"
    887 #define JAVA_CONST_MODE_STATIC_NAME                     "MODE_STATIC"
    888 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
    889 #define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
    890 
    891 #define JAVA_AUDIOFORMAT_CLASS_NAME             "android/media/AudioFormat"
    892 #define JAVA_AUDIOMANAGER_CLASS_NAME            "android/media/AudioManager"
    893 
    894 // ----------------------------------------------------------------------------
    895 // preconditions:
    896 //    theClass is valid
    897 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
    898                              const char* constName, int* constVal) {
    899     jfieldID javaConst = NULL;
    900     javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
    901     if (javaConst != NULL) {
    902         *constVal = pEnv->GetStaticIntField(theClass, javaConst);
    903         return true;
    904     } else {
    905         LOGE("Can't find %s.%s", className, constName);
    906         return false;
    907     }
    908 }
    909 
    910 
    911 // ----------------------------------------------------------------------------
    912 int register_android_media_AudioTrack(JNIEnv *env)
    913 {
    914     javaAudioTrackFields.audioTrackClass = NULL;
    915     javaAudioTrackFields.nativeTrackInJavaObj = NULL;
    916     javaAudioTrackFields.postNativeEventInJava = NULL;
    917 
    918     // Get the AudioTrack class
    919     javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName);
    920     if (javaAudioTrackFields.audioTrackClass == NULL) {
    921         LOGE("Can't find %s", kClassPathName);
    922         return -1;
    923     }
    924 
    925     // Get the postEvent method
    926     javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID(
    927             javaAudioTrackFields.audioTrackClass,
    928             JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    929     if (javaAudioTrackFields.postNativeEventInJava == NULL) {
    930         LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME);
    931         return -1;
    932     }
    933 
    934     // Get the variables fields
    935     //      nativeTrackInJavaObj
    936     javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID(
    937             javaAudioTrackFields.audioTrackClass,
    938             JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I");
    939     if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) {
    940         LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME);
    941         return -1;
    942     }
    943     //      jniData;
    944     javaAudioTrackFields.jniData = env->GetFieldID(
    945             javaAudioTrackFields.audioTrackClass,
    946             JAVA_JNIDATA_FIELD_NAME, "I");
    947     if (javaAudioTrackFields.jniData == NULL) {
    948         LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME);
    949         return -1;
    950     }
    951 
    952     // Get the memory mode constants
    953     if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
    954                kClassPathName,
    955                JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC))
    956          || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
    957                kClassPathName,
    958                JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) {
    959         // error log performed in android_media_getIntConstantFromClass()
    960         return -1;
    961     }
    962 
    963     // Get the format constants from the AudioFormat class
    964     jclass audioFormatClass = NULL;
    965     audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
    966     if (audioFormatClass == NULL) {
    967         LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
    968         return -1;
    969     }
    970     if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
    971                 JAVA_AUDIOFORMAT_CLASS_NAME,
    972                 JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16))
    973            || !android_media_getIntConstantFromClass(env, audioFormatClass,
    974                 JAVA_AUDIOFORMAT_CLASS_NAME,
    975                 JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) {
    976         // error log performed in android_media_getIntConstantFromClass()
    977         return -1;
    978     }
    979 
    980     // Get the stream types from the AudioManager class
    981     jclass audioManagerClass = NULL;
    982     audioManagerClass = env->FindClass(JAVA_AUDIOMANAGER_CLASS_NAME);
    983     if (audioManagerClass == NULL) {
    984        LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME);
    985        return -1;
    986     }
    987     if ( !android_media_getIntConstantFromClass(env, audioManagerClass,
    988                JAVA_AUDIOMANAGER_CLASS_NAME,
    989                JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL))
    990           || !android_media_getIntConstantFromClass(env, audioManagerClass,
    991                JAVA_AUDIOMANAGER_CLASS_NAME,
    992                JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC))
    993           || !android_media_getIntConstantFromClass(env, audioManagerClass,
    994                JAVA_AUDIOMANAGER_CLASS_NAME,
    995                JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM))
    996           || !android_media_getIntConstantFromClass(env, audioManagerClass,
    997                JAVA_AUDIOMANAGER_CLASS_NAME,
    998                JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING))
    999           || !android_media_getIntConstantFromClass(env, audioManagerClass,
   1000                JAVA_AUDIOMANAGER_CLASS_NAME,
   1001                JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM))
   1002           || !android_media_getIntConstantFromClass(env, audioManagerClass,
   1003                JAVA_AUDIOMANAGER_CLASS_NAME,
   1004                JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION))
   1005           || !android_media_getIntConstantFromClass(env, audioManagerClass,
   1006                JAVA_AUDIOMANAGER_CLASS_NAME,
   1007                JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))
   1008           || !android_media_getIntConstantFromClass(env, audioManagerClass,
   1009                JAVA_AUDIOMANAGER_CLASS_NAME,
   1010                JAVA_CONST_STREAM_DTMF_NAME, &(javaAudioTrackFields.STREAM_DTMF))) {
   1011        // error log performed in android_media_getIntConstantFromClass()
   1012        return -1;
   1013     }
   1014 
   1015     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
   1016 }
   1017 
   1018 
   1019 // ----------------------------------------------------------------------------
   1020