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