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