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