android_media_AudioTrack.cpp revision 9b09e533ac67d3d25465e3312b4957bc90b0c84f
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    int sessionId = 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    lpTrack->stop();
545
546    // delete the JNI data
547    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
548        thiz, javaAudioTrackFields.jniData);
549    // reset the native resources in the Java object so any attempt to access
550    // them after a call to release fails.
551    env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
552
553    if (pJniStorage) {
554        Mutex::Autolock l(sLock);
555        audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
556        //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
557        while (lpCookie->busy) {
558            if (lpCookie->cond.waitRelative(sLock,
559                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
560                                                    NO_ERROR) {
561                break;
562            }
563        }
564        sAudioTrackCallBackCookies.remove(lpCookie);
565        // delete global refs created in native_setup
566        env->DeleteGlobalRef(lpCookie->audioTrack_class);
567        env->DeleteGlobalRef(lpCookie->audioTrack_ref);
568        delete pJniStorage;
569    }
570}
571
572
573// ----------------------------------------------------------------------------
574static void android_media_AudioTrack_finalize(JNIEnv *env,  jobject thiz) {
575    //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
576    android_media_AudioTrack_release(env, thiz);
577}
578
579// overloaded JNI array helper functions (same as in android_media_AudioRecord)
580static inline
581jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
582    return env->GetByteArrayElements(array, isCopy);
583}
584
585static inline
586void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
587    env->ReleaseByteArrayElements(array, elems, mode);
588}
589
590static inline
591jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
592    return env->GetShortArrayElements(array, isCopy);
593}
594
595static inline
596void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
597    env->ReleaseShortArrayElements(array, elems, mode);
598}
599
600static inline
601jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
602    return env->GetFloatArrayElements(array, isCopy);
603}
604
605static inline
606void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
607    env->ReleaseFloatArrayElements(array, elems, mode);
608}
609
610// ----------------------------------------------------------------------------
611template <typename T>
612static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
613                         jint offsetInSamples, jint sizeInSamples, bool blocking) {
614    // give the data to the native AudioTrack object (the data starts at the offset)
615    ssize_t written = 0;
616    // regular write() or copy the data to the AudioTrack's shared memory?
617    size_t sizeInBytes = sizeInSamples * sizeof(T);
618    if (track->sharedBuffer() == 0) {
619        written = track->write(data + offsetInSamples, sizeInBytes, blocking);
620        // for compatibility with earlier behavior of write(), return 0 in this case
621        if (written == (ssize_t) WOULD_BLOCK) {
622            written = 0;
623        }
624    } else {
625        // writing to shared memory, check for capacity
626        if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
627            sizeInBytes = track->sharedBuffer()->size();
628        }
629        memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
630        written = sizeInBytes;
631    }
632    if (written > 0) {
633        return written / sizeof(T);
634    }
635    // for compatibility, error codes pass through unchanged
636    return written;
637}
638
639// ----------------------------------------------------------------------------
640template <typename T>
641static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
642                                                T javaAudioData,
643                                                jint offsetInSamples, jint sizeInSamples,
644                                                jint javaAudioFormat,
645                                                jboolean isWriteBlocking) {
646    //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
647    //        offsetInSamples, sizeInSamples);
648    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
649    if (lpTrack == NULL) {
650        jniThrowException(env, "java/lang/IllegalStateException",
651            "Unable to retrieve AudioTrack pointer for write()");
652        return (jint)AUDIO_JAVA_INVALID_OPERATION;
653    }
654
655    if (javaAudioData == NULL) {
656        ALOGE("NULL java array of audio data to play");
657        return (jint)AUDIO_JAVA_BAD_VALUE;
658    }
659
660    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
661    // a way that it becomes much more efficient. When doing so, we will have to prevent the
662    // AudioSystem callback to be called while in critical section (in case of media server
663    // process crash for instance)
664
665    // get the pointer for the audio data from the java array
666    auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
667    if (cAudioData == NULL) {
668        ALOGE("Error retrieving source of audio data to play");
669        return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
670    }
671
672    jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
673            offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
674
675    envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
676
677    //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
678    //        (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
679    return samplesWritten;
680}
681
682// ----------------------------------------------------------------------------
683static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
684        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
685        jint javaAudioFormat, jboolean isWriteBlocking) {
686    //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
687    //    offsetInBytes, sizeInBytes);
688    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
689    if (lpTrack == NULL) {
690        jniThrowException(env, "java/lang/IllegalStateException",
691                "Unable to retrieve AudioTrack pointer for write()");
692        return (jint)AUDIO_JAVA_INVALID_OPERATION;
693    }
694
695    ScopedBytesRO bytes(env, javaBytes);
696    if (bytes.get() == NULL) {
697        ALOGE("Error retrieving source of audio data to play, can't play");
698        return (jint)AUDIO_JAVA_BAD_VALUE;
699    }
700
701    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
702            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
703
704    return written;
705}
706
707// ----------------------------------------------------------------------------
708static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env,  jobject thiz) {
709    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
710    if (lpTrack == NULL) {
711        jniThrowException(env, "java/lang/IllegalStateException",
712            "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
713        return (jint)AUDIO_JAVA_ERROR;
714    }
715
716    ssize_t result = lpTrack->getBufferSizeInFrames();
717    if (result < 0) {
718        jniThrowException(env, "java/lang/IllegalStateException",
719            "Internal error detected in getBufferSizeInFrames() = " + result);
720        return (jint)AUDIO_JAVA_ERROR;
721    }
722    return (jint)result;
723}
724
725// ----------------------------------------------------------------------------
726static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
727        jobject thiz, jint bufferSizeInFrames) {
728    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
729    if (lpTrack == NULL) {
730        jniThrowException(env, "java/lang/IllegalStateException",
731            "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
732        return (jint)AUDIO_JAVA_ERROR;
733    }
734    // Value will be coerced into the valid range.
735    // But internal values are unsigned, size_t, so we need to clip
736    // against zero here where it is signed.
737    if (bufferSizeInFrames < 0) {
738        bufferSizeInFrames = 0;
739    }
740    ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
741    if (result < 0) {
742        jniThrowException(env, "java/lang/IllegalStateException",
743            "Internal error detected in setBufferSizeInFrames() = " + result);
744        return (jint)AUDIO_JAVA_ERROR;
745    }
746    return (jint)result;
747}
748
749// ----------------------------------------------------------------------------
750static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env,  jobject thiz) {
751    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
752    if (lpTrack == NULL) {
753        jniThrowException(env, "java/lang/IllegalStateException",
754            "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
755        return (jint)AUDIO_JAVA_ERROR;
756    }
757
758    return lpTrack->frameCount();
759}
760
761// ----------------------------------------------------------------------------
762static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
763        jint sampleRateInHz) {
764    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
765    if (lpTrack == NULL) {
766        jniThrowException(env, "java/lang/IllegalStateException",
767            "Unable to retrieve AudioTrack pointer for setSampleRate()");
768        return (jint)AUDIO_JAVA_ERROR;
769    }
770    return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
771}
772
773
774// ----------------------------------------------------------------------------
775static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
776    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
777    if (lpTrack == NULL) {
778        jniThrowException(env, "java/lang/IllegalStateException",
779            "Unable to retrieve AudioTrack pointer for getSampleRate()");
780        return (jint)AUDIO_JAVA_ERROR;
781    }
782    return (jint) lpTrack->getSampleRate();
783}
784
785
786// ----------------------------------------------------------------------------
787static void android_media_AudioTrack_set_playback_params(JNIEnv *env,  jobject thiz,
788        jobject params) {
789    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
790    if (lpTrack == NULL) {
791        jniThrowException(env, "java/lang/IllegalStateException",
792            "AudioTrack not initialized");
793        return;
794    }
795
796    PlaybackParams pbp;
797    pbp.fillFromJobject(env, gPlaybackParamsFields, params);
798
799    ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
800            pbp.speedSet, pbp.audioRate.mSpeed,
801            pbp.pitchSet, pbp.audioRate.mPitch,
802            pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
803            pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
804
805    // to simulate partially set params, we do a read-modify-write.
806    // TODO: pass in the valid set mask into AudioTrack.
807    AudioPlaybackRate rate = lpTrack->getPlaybackRate();
808    bool updatedRate = false;
809    if (pbp.speedSet) {
810        rate.mSpeed = pbp.audioRate.mSpeed;
811        updatedRate = true;
812    }
813    if (pbp.pitchSet) {
814        rate.mPitch = pbp.audioRate.mPitch;
815        updatedRate = true;
816    }
817    if (pbp.audioFallbackModeSet) {
818        rate.mFallbackMode = pbp.audioRate.mFallbackMode;
819        updatedRate = true;
820    }
821    if (pbp.audioStretchModeSet) {
822        rate.mStretchMode = pbp.audioRate.mStretchMode;
823        updatedRate = true;
824    }
825    if (updatedRate) {
826        if (lpTrack->setPlaybackRate(rate) != OK) {
827            jniThrowException(env, "java/lang/IllegalArgumentException",
828                    "arguments out of range");
829        }
830    }
831}
832
833
834// ----------------------------------------------------------------------------
835static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env,  jobject thiz,
836        jobject params) {
837    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
838    if (lpTrack == NULL) {
839        jniThrowException(env, "java/lang/IllegalStateException",
840            "AudioTrack not initialized");
841        return NULL;
842    }
843
844    PlaybackParams pbs;
845    pbs.audioRate = lpTrack->getPlaybackRate();
846    pbs.speedSet = true;
847    pbs.pitchSet = true;
848    pbs.audioFallbackModeSet = true;
849    pbs.audioStretchModeSet = true;
850    return pbs.asJobject(env, gPlaybackParamsFields);
851}
852
853
854// ----------------------------------------------------------------------------
855static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
856        jint markerPos) {
857    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
858    if (lpTrack == NULL) {
859        jniThrowException(env, "java/lang/IllegalStateException",
860            "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
861        return (jint)AUDIO_JAVA_ERROR;
862    }
863    return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
864}
865
866
867// ----------------------------------------------------------------------------
868static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
869    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
870    uint32_t markerPos = 0;
871
872    if (lpTrack == NULL) {
873        jniThrowException(env, "java/lang/IllegalStateException",
874            "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
875        return (jint)AUDIO_JAVA_ERROR;
876    }
877    lpTrack->getMarkerPosition(&markerPos);
878    return (jint)markerPos;
879}
880
881
882// ----------------------------------------------------------------------------
883static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
884        jint period) {
885    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
886    if (lpTrack == NULL) {
887        jniThrowException(env, "java/lang/IllegalStateException",
888            "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
889        return (jint)AUDIO_JAVA_ERROR;
890    }
891    return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
892}
893
894
895// ----------------------------------------------------------------------------
896static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
897    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
898    uint32_t period = 0;
899
900    if (lpTrack == NULL) {
901        jniThrowException(env, "java/lang/IllegalStateException",
902            "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
903        return (jint)AUDIO_JAVA_ERROR;
904    }
905    lpTrack->getPositionUpdatePeriod(&period);
906    return (jint)period;
907}
908
909
910// ----------------------------------------------------------------------------
911static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
912        jint position) {
913    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
914    if (lpTrack == NULL) {
915        jniThrowException(env, "java/lang/IllegalStateException",
916            "Unable to retrieve AudioTrack pointer for setPosition()");
917        return (jint)AUDIO_JAVA_ERROR;
918    }
919    return nativeToJavaStatus( lpTrack->setPosition(position) );
920}
921
922
923// ----------------------------------------------------------------------------
924static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
925    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
926    uint32_t position = 0;
927
928    if (lpTrack == NULL) {
929        jniThrowException(env, "java/lang/IllegalStateException",
930            "Unable to retrieve AudioTrack pointer for getPosition()");
931        return (jint)AUDIO_JAVA_ERROR;
932    }
933    lpTrack->getPosition(&position);
934    return (jint)position;
935}
936
937
938// ----------------------------------------------------------------------------
939static jint android_media_AudioTrack_get_latency(JNIEnv *env,  jobject thiz) {
940    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
941
942    if (lpTrack == NULL) {
943        jniThrowException(env, "java/lang/IllegalStateException",
944            "Unable to retrieve AudioTrack pointer for latency()");
945        return (jint)AUDIO_JAVA_ERROR;
946    }
947    return (jint)lpTrack->latency();
948}
949
950// ----------------------------------------------------------------------------
951static jint android_media_AudioTrack_get_underrun_count(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 getUnderrunCount()");
957        return (jint)AUDIO_JAVA_ERROR;
958    }
959    return (jint)lpTrack->getUnderrunCount();
960}
961
962// ----------------------------------------------------------------------------
963static jint android_media_AudioTrack_get_timestamp(JNIEnv *env,  jobject thiz, jlongArray jTimestamp) {
964    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
965
966    if (lpTrack == NULL) {
967        ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
968        return (jint)AUDIO_JAVA_ERROR;
969    }
970    AudioTimestamp timestamp;
971    status_t status = lpTrack->getTimestamp(timestamp);
972    if (status == OK) {
973        jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
974        if (nTimestamp == NULL) {
975            ALOGE("Unable to get array for getTimestamp()");
976            return (jint)AUDIO_JAVA_ERROR;
977        }
978        nTimestamp[0] = (jlong) timestamp.mPosition;
979        nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
980        env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
981    }
982    return (jint) nativeToJavaStatus(status);
983}
984
985
986// ----------------------------------------------------------------------------
987static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
988        jint loopStart, jint loopEnd, jint loopCount) {
989    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
990    if (lpTrack == NULL) {
991        jniThrowException(env, "java/lang/IllegalStateException",
992            "Unable to retrieve AudioTrack pointer for setLoop()");
993        return (jint)AUDIO_JAVA_ERROR;
994    }
995    return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
996}
997
998
999// ----------------------------------------------------------------------------
1000static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
1001    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1002    if (lpTrack == NULL) {
1003        jniThrowException(env, "java/lang/IllegalStateException",
1004            "Unable to retrieve AudioTrack pointer for reload()");
1005        return (jint)AUDIO_JAVA_ERROR;
1006    }
1007    return nativeToJavaStatus( lpTrack->reload() );
1008}
1009
1010
1011// ----------------------------------------------------------------------------
1012static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz,
1013        jint javaStreamType) {
1014    uint32_t afSamplingRate;
1015    // convert the stream type from Java to native value
1016    // FIXME: code duplication with android_media_AudioTrack_setup()
1017    audio_stream_type_t nativeStreamType;
1018    switch (javaStreamType) {
1019    case AUDIO_STREAM_VOICE_CALL:
1020    case AUDIO_STREAM_SYSTEM:
1021    case AUDIO_STREAM_RING:
1022    case AUDIO_STREAM_MUSIC:
1023    case AUDIO_STREAM_ALARM:
1024    case AUDIO_STREAM_NOTIFICATION:
1025    case AUDIO_STREAM_BLUETOOTH_SCO:
1026    case AUDIO_STREAM_DTMF:
1027        nativeStreamType = (audio_stream_type_t) javaStreamType;
1028        break;
1029    default:
1030        nativeStreamType = AUDIO_STREAM_DEFAULT;
1031        break;
1032    }
1033
1034    status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1035    if (status != NO_ERROR) {
1036        ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1037              "in AudioTrack JNI", status, nativeStreamType);
1038        return DEFAULT_OUTPUT_SAMPLE_RATE;
1039    } else {
1040        return afSamplingRate;
1041    }
1042}
1043
1044
1045// ----------------------------------------------------------------------------
1046// returns the minimum required size for the successful creation of a streaming AudioTrack
1047// returns -1 if there was an error querying the hardware.
1048static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
1049    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
1050
1051    size_t frameCount;
1052    const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1053            sampleRateInHertz);
1054    if (status != NO_ERROR) {
1055        ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1056                sampleRateInHertz, status);
1057        return -1;
1058    }
1059    const audio_format_t format = audioFormatToNative(audioFormat);
1060    if (audio_has_proportional_frames(format)) {
1061        const size_t bytesPerSample = audio_bytes_per_sample(format);
1062        return frameCount * channelCount * bytesPerSample;
1063    } else {
1064        return frameCount;
1065    }
1066}
1067
1068// ----------------------------------------------------------------------------
1069static jint
1070android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1071{
1072    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1073    if (lpTrack == NULL ) {
1074        jniThrowException(env, "java/lang/IllegalStateException",
1075            "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
1076        return -1;
1077    }
1078
1079    status_t status = lpTrack->setAuxEffectSendLevel(level);
1080    if (status != NO_ERROR) {
1081        ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1082                level, status);
1083    }
1084    return (jint) status;
1085}
1086
1087// ----------------------------------------------------------------------------
1088static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env,  jobject thiz,
1089        jint effectId) {
1090    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1091    if (lpTrack == NULL) {
1092        jniThrowException(env, "java/lang/IllegalStateException",
1093            "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
1094        return (jint)AUDIO_JAVA_ERROR;
1095    }
1096    return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
1097}
1098
1099static jboolean android_media_AudioTrack_setOutputDevice(
1100                JNIEnv *env,  jobject thiz, jint device_id) {
1101
1102    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1103    if (lpTrack == 0) {
1104        return false;
1105    }
1106    return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1107}
1108
1109static jint android_media_AudioTrack_getRoutedDeviceId(
1110                JNIEnv *env,  jobject thiz) {
1111
1112    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1113    if (lpTrack == NULL) {
1114        return 0;
1115    }
1116    return (jint)lpTrack->getRoutedDeviceId();
1117}
1118
1119static void android_media_AudioTrack_enableDeviceCallback(
1120                JNIEnv *env,  jobject thiz) {
1121
1122    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1123    if (lpTrack == NULL) {
1124        return;
1125    }
1126    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1127        thiz, javaAudioTrackFields.jniData);
1128    if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1129        return;
1130    }
1131    pJniStorage->mDeviceCallback =
1132    new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1133                          javaAudioTrackFields.postNativeEventInJava);
1134    lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1135}
1136
1137static void android_media_AudioTrack_disableDeviceCallback(
1138                JNIEnv *env,  jobject thiz) {
1139
1140    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1141    if (lpTrack == NULL) {
1142        return;
1143    }
1144    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1145        thiz, javaAudioTrackFields.jniData);
1146    if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1147        return;
1148    }
1149    lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1150    pJniStorage->mDeviceCallback.clear();
1151}
1152
1153static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
1154    return FCC_8;
1155}
1156
1157
1158// ----------------------------------------------------------------------------
1159// ----------------------------------------------------------------------------
1160static const JNINativeMethod gMethods[] = {
1161    // name,              signature,     funcPtr
1162    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
1163    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
1164    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
1165    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
1166    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
1167                                         (void *)android_media_AudioTrack_setup},
1168    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
1169    {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
1170    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1171    {"native_write_native_bytes",
1172                             "(Ljava/lang/Object;IIIZ)I",
1173                                         (void *)android_media_AudioTrack_write_native_bytes},
1174    {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1175    {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1176    {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
1177    {"native_get_buffer_size_frames",
1178                             "()I",      (void *)android_media_AudioTrack_get_buffer_size_frames},
1179    {"native_set_buffer_size_frames",
1180                             "(I)I",     (void *)android_media_AudioTrack_set_buffer_size_frames},
1181    {"native_get_buffer_capacity_frames",
1182                             "()I",      (void *)android_media_AudioTrack_get_buffer_capacity_frames},
1183    {"native_set_playback_rate",
1184                             "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
1185    {"native_get_playback_rate",
1186                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
1187    {"native_set_playback_params",
1188                             "(Landroid/media/PlaybackParams;)V",
1189                                         (void *)android_media_AudioTrack_set_playback_params},
1190    {"native_get_playback_params",
1191                             "()Landroid/media/PlaybackParams;",
1192                                         (void *)android_media_AudioTrack_get_playback_params},
1193    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
1194    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
1195    {"native_set_pos_update_period",
1196                             "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
1197    {"native_get_pos_update_period",
1198                             "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
1199    {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
1200    {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
1201    {"native_get_latency",   "()I",      (void *)android_media_AudioTrack_get_latency},
1202    {"native_get_underrun_count", "()I",      (void *)android_media_AudioTrack_get_underrun_count},
1203    {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
1204    {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
1205    {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
1206    {"native_get_output_sample_rate",
1207                             "(I)I",      (void *)android_media_AudioTrack_get_output_sample_rate},
1208    {"native_get_min_buff_size",
1209                             "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
1210    {"native_setAuxEffectSendLevel",
1211                             "(F)I",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1212    {"native_attachAuxEffect",
1213                             "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
1214    {"native_setOutputDevice", "(I)Z",
1215                             (void *)android_media_AudioTrack_setOutputDevice},
1216    {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1217    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1218    {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1219    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
1220};
1221
1222
1223// field names found in android/media/AudioTrack.java
1224#define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
1225#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
1226#define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
1227#define JAVA_STREAMTYPE_FIELD_NAME                      "mStreamType"
1228
1229// ----------------------------------------------------------------------------
1230// preconditions:
1231//    theClass is valid
1232bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1233                             const char* constName, int* constVal) {
1234    jfieldID javaConst = NULL;
1235    javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1236    if (javaConst != NULL) {
1237        *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1238        return true;
1239    } else {
1240        ALOGE("Can't find %s.%s", className, constName);
1241        return false;
1242    }
1243}
1244
1245
1246// ----------------------------------------------------------------------------
1247int register_android_media_AudioTrack(JNIEnv *env)
1248{
1249    // must be first
1250    int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1251
1252    javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1253    javaAudioTrackFields.postNativeEventInJava = NULL;
1254
1255    // Get the AudioTrack class
1256    jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1257
1258    // Get the postEvent method
1259    javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1260            audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1261            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1262
1263    // Get the variables fields
1264    //      nativeTrackInJavaObj
1265    javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1266            audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1267    //      jniData
1268    javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1269            audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1270    //      fieldStreamType
1271    javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1272            audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1273
1274    env->DeleteLocalRef(audioTrackClass);
1275
1276    // Get the AudioAttributes class and fields
1277    jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1278    javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1279    javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1280            audioAttrClass, "mContentType", "I");
1281    javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1282    javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1283            audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
1284
1285    env->DeleteLocalRef(audioAttrClass);
1286
1287    // initialize PlaybackParams field info
1288    gPlaybackParamsFields.init(env);
1289
1290    return res;
1291}
1292
1293
1294// ----------------------------------------------------------------------------
1295