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