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