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