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