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