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