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