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