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