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