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