android_media_AudioTrack.cpp revision 076357b8567458d4b6dfdcf839ef751634cd2bfb
1861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian/*
2861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * Copyright (C) 2008 The Android Open Source Project
3861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian *
4861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * Licensed under the Apache License, Version 2.0 (the "License");
5861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * you may not use this file except in compliance with the License.
6861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * You may obtain a copy of the License at
7861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian *
8861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian *      http://www.apache.org/licenses/LICENSE-2.0
9861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian *
10861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * Unless required by applicable law or agreed to in writing, software
11861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * distributed under the License is distributed on an "AS IS" BASIS,
12861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * See the License for the specific language governing permissions and
14861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian * limitations under the License.
15861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian */
16861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian//#define LOG_NDEBUG 0
17861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
18861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#define LOG_TAG "AudioTrack-JNI"
19861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
20861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <stdio.h>
21861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <unistd.h>
22861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <fcntl.h>
23861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <math.h>
24861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
25861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "jni.h"
26861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "JNIHelp.h"
27861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "android_runtime/AndroidRuntime.h"
28861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
29861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "utils/Log.h"
30861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "media/AudioSystem.h"
31861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include "media/AudioTrack.h"
32861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
33861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <utils/MemoryHeapBase.h>
34861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian#include <utils/MemoryBase.h>
35861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
36861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
37861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian// ----------------------------------------------------------------------------
38861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
39861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianusing namespace android;
40861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
41861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian// ----------------------------------------------------------------------------
42861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianstatic const char* const kClassPathName = "android/media/AudioTrack";
43861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
44861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianstruct fields_t {
45861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    // these fields provide access from C++ to the...
46861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jclass    audioTrackClass;       //... AudioTrack class
47861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jmethodID postNativeEventInJava; //... event post callback method
48861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       PCM16;                 //...  format constants
49861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       PCM8;                  //...  format constants
50861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_VOICE_CALL;     //...  stream type constants
51861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_SYSTEM;         //...  stream type constants
52861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_RING;           //...  stream type constants
53861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_MUSIC;          //...  stream type constants
54861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_ALARM;          //...  stream type constants
55861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_NOTIFICATION;   //...  stream type constants
56861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       STREAM_BLUETOOTH_SCO;   //...  stream type constants
57861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       MODE_STREAM;           //...  memory mode
58861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    int       MODE_STATIC;           //...  memory mode
59861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jfieldID  nativeTrackInJavaObj; // stores in Java the native AudioTrack object
60861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jfieldID  jniData;      // stores in Java additional resources used by the native AudioTrack
61861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian};
62861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianstatic fields_t javaAudioTrackFields;
63861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
64861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianstruct audiotrack_callback_cookie {
65861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jclass      audioTrack_class;
66861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    jobject     audioTrack_ref;
67861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian };
68861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
69861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian// ----------------------------------------------------------------------------
70861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopianclass AudioTrackJniStorage {
71861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    public:
72861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        sp<MemoryHeapBase>         mMemHeap;
73861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        sp<MemoryBase>             mMemBase;
74861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        audiotrack_callback_cookie mCallbackData;
75861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        int                        mStreamType;
76861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
77861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    AudioTrackJniStorage() {
78861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    }
79861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian
80861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    ~AudioTrackJniStorage() {
81861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        mMemBase.clear();
82861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian        mMemHeap.clear();
83861aa2f0c9136298380496bd78d6a18e69331c53Mathias Agopian    }
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
95// ----------------------------------------------------------------------------
96#define DEFAULT_OUTPUT_SAMPLE_RATE   44100
97
98#define AUDIOTRACK_SUCCESS                         0
99#define AUDIOTRACK_ERROR                           -1
100#define AUDIOTRACK_ERROR_BAD_VALUE                 -2
101#define AUDIOTRACK_ERROR_INVALID_OPERATION         -3
102#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM         -16
103#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -17
104#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT       -18
105#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE   -19
106#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED    -20
107
108
109jint android_media_translateErrorCode(int code) {
110    switch(code) {
111    case NO_ERROR:
112        return AUDIOTRACK_SUCCESS;
113    case BAD_VALUE:
114        return AUDIOTRACK_ERROR_BAD_VALUE;
115    case INVALID_OPERATION:
116        return AUDIOTRACK_ERROR_INVALID_OPERATION;
117    default:
118        return AUDIOTRACK_ERROR;
119    }
120}
121
122
123// ----------------------------------------------------------------------------
124static void audioCallback(int event, void* user, void *info) {
125    if (event == AudioTrack::EVENT_MORE_DATA) {
126        // set size to 0 to signal we're not using the callback to write more data
127        AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
128        pBuff->size = 0;
129
130    } else if (event == AudioTrack::EVENT_MARKER) {
131        audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
132        JNIEnv *env = AndroidRuntime::getJNIEnv();
133        if (user && env) {
134            env->CallStaticVoidMethod(
135                callbackInfo->audioTrack_class,
136                javaAudioTrackFields.postNativeEventInJava,
137                callbackInfo->audioTrack_ref, event, 0,0, NULL);
138            if (env->ExceptionCheck()) {
139                env->ExceptionDescribe();
140                env->ExceptionClear();
141            }
142        }
143
144    } else if (event == AudioTrack::EVENT_NEW_POS) {
145        audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
146        JNIEnv *env = AndroidRuntime::getJNIEnv();
147        if (user && env) {
148            env->CallStaticVoidMethod(
149                callbackInfo->audioTrack_class,
150                javaAudioTrackFields.postNativeEventInJava,
151                callbackInfo->audioTrack_ref, event, 0,0, NULL);
152            if (env->ExceptionCheck()) {
153                env->ExceptionDescribe();
154                env->ExceptionClear();
155            }
156        }
157    }
158}
159
160
161// ----------------------------------------------------------------------------
162static int
163android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
164        jint streamType, jint sampleRateInHertz, jint nbChannels,
165        jint audioFormat, jint buffSizeInBytes, jint memoryMode)
166{
167    LOGV("sampleRate=%d, audioFormat(from Java)=%d, nbChannels=%d, buffSize=%d",
168        sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes);
169    int afSampleRate;
170    int afFrameCount;
171
172    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
173        LOGE("Error creating AudioTrack: Could not get AudioSystem frame count.");
174        return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
175    }
176    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
177        LOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate.");
178        return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
179    }
180
181    if ((nbChannels == 0) || (nbChannels > 2)) {
182        LOGE("Error creating AudioTrack: channel count is not 1 or 2.");
183        return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT;
184    }
185
186    // check the stream type
187    AudioSystem::stream_type atStreamType;
188    if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) {
189        atStreamType = AudioSystem::VOICE_CALL;
190    } else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) {
191        atStreamType = AudioSystem::SYSTEM;
192    } else if (streamType == javaAudioTrackFields.STREAM_RING) {
193        atStreamType = AudioSystem::RING;
194    } else if (streamType == javaAudioTrackFields.STREAM_MUSIC) {
195        atStreamType = AudioSystem::MUSIC;
196    } else if (streamType == javaAudioTrackFields.STREAM_ALARM) {
197        atStreamType = AudioSystem::ALARM;
198    } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
199        atStreamType = AudioSystem::NOTIFICATION;
200    } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
201        atStreamType = AudioSystem::BLUETOOTH_SCO;
202    } else {
203        LOGE("Error creating AudioTrack: unknown stream type.");
204        return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
205    }
206
207    // check the format.
208    // This function was called from Java, so we compare the format against the Java constants
209    if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) {
210        LOGE("Error creating AudioTrack: unsupported audio format.");
211        return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
212    }
213
214    // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class
215    // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled
216    // in android_media_AudioTrack_native_write()
217    if ((audioFormat == javaAudioTrackFields.PCM8)
218        && (memoryMode == javaAudioTrackFields.MODE_STATIC)) {
219        LOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \
220            buff size of %dbytes, switching to 16bit, buff size of %dbytes",
221            buffSizeInBytes, 2*buffSizeInBytes);
222        audioFormat = javaAudioTrackFields.PCM16;
223        // we will need twice the memory to store the data
224        buffSizeInBytes *= 2;
225    }
226
227    // compute the frame count
228    int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
229    int format = audioFormat == javaAudioTrackFields.PCM16 ?
230            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
231    int frameCount;
232    if (buffSizeInBytes == -1) {
233        // compute the frame count based on the system's output frame count
234        // and the native sample rate
235        frameCount = (sampleRateInHertz*afFrameCount)/afSampleRate;
236    } else {
237        // compute the frame count based on the specified buffer size
238        frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
239    }
240
241    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
242
243    // initialize the callback information:
244    // this data will be passed with every AudioTrack callback
245    jclass clazz = env->GetObjectClass(thiz);
246    if (clazz == NULL) {
247        LOGE("Can't find %s when setting up callback.", kClassPathName);
248        delete lpJniStorage;
249        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
250    }
251    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
252    // we use a weak reference so the AudioTrack object can be garbage collected.
253    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
254
255    lpJniStorage->mStreamType = atStreamType;
256
257    // create the native AudioTrack object
258    AudioTrack* lpTrack = new AudioTrack();
259    if (lpTrack == NULL) {
260        LOGE("Error creating uninitialized AudioTrack");
261        goto native_track_failure;
262    }
263
264    // initialize the native AudioTrack object
265    if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
266
267        lpTrack->set(
268            atStreamType,// stream type
269            sampleRateInHertz,
270            format,// word length, PCM
271            nbChannels,
272            frameCount,
273            0,// flags
274            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
275            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
276            0,// shared mem
277            true);// thread can call Java
278
279    } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
280        // AudioTrack is using shared memory
281
282        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
283            LOGE("Error creating AudioTrack in static mode: error creating mem heap base");
284            goto native_init_failure;
285        }
286
287        lpTrack->set(
288            atStreamType,// stream type
289            sampleRateInHertz,
290            format,// word length, PCM
291            nbChannels,
292            frameCount,
293            0,// flags
294            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
295            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
296            lpJniStorage->mMemBase,// shared mem
297            true);// thread can call Java
298    }
299
300    if (lpTrack->initCheck() != NO_ERROR) {
301        LOGE("Error initializing AudioTrack");
302        goto native_init_failure;
303    }
304
305    // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
306    // of the Java object (in mNativeTrackInJavaObj)
307    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
308
309    // save the JNI resources so we can free them later
310    //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
311    env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
312
313    return AUDIOTRACK_SUCCESS;
314
315    // failures:
316native_init_failure:
317    delete lpTrack;
318    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
319
320native_track_failure:
321    delete lpJniStorage;
322    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
323    return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
324
325}
326
327
328// ----------------------------------------------------------------------------
329static void
330android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
331{
332    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
333        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
334    if (lpTrack == NULL ) {
335        jniThrowException(env, "java/lang/IllegalStateException",
336            "Unable to retrieve AudioTrack pointer for start()");
337    }
338
339    lpTrack->start();
340}
341
342
343// ----------------------------------------------------------------------------
344static void
345android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
346{
347    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
348        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
349    if (lpTrack == NULL ) {
350        jniThrowException(env, "java/lang/IllegalStateException",
351            "Unable to retrieve AudioTrack pointer for stop()");
352    }
353
354    lpTrack->stop();
355}
356
357
358// ----------------------------------------------------------------------------
359static void
360android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
361{
362    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
363        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
364    if (lpTrack == NULL ) {
365        jniThrowException(env, "java/lang/IllegalStateException",
366            "Unable to retrieve AudioTrack pointer for pause()");
367    }
368
369    lpTrack->pause();
370}
371
372
373// ----------------------------------------------------------------------------
374static void
375android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
376{
377    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
378        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
379    if (lpTrack == NULL ) {
380        jniThrowException(env, "java/lang/IllegalStateException",
381            "Unable to retrieve AudioTrack pointer for flush()");
382    }
383
384    lpTrack->flush();
385}
386
387// ----------------------------------------------------------------------------
388static void
389android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
390{
391    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
392        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
393    if (lpTrack == NULL ) {
394        jniThrowException(env, "java/lang/IllegalStateException",
395            "Unable to retrieve AudioTrack pointer for setVolume()");
396    }
397
398    lpTrack->setVolume(leftVol, rightVol);
399}
400
401// ----------------------------------------------------------------------------
402static void android_media_AudioTrack_native_finalize(JNIEnv *env,  jobject thiz) {
403    //LOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
404
405    // delete the AudioTrack object
406    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
407        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
408    if (lpTrack) {
409        //LOGV("deleting lpTrack: %x\n", (int)lpTrack);
410        lpTrack->stop();
411        delete lpTrack;
412    }
413
414    // delete the JNI data
415    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
416        thiz, javaAudioTrackFields.jniData);
417    if (pJniStorage) {
418        //LOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
419        delete pJniStorage;
420    }
421}
422
423// ----------------------------------------------------------------------------
424static void android_media_AudioTrack_native_release(JNIEnv *env,  jobject thiz) {
425
426    // do everything a call to finalize would
427    android_media_AudioTrack_native_finalize(env, thiz);
428    // + reset the native resources in the Java object so any attempt to access
429    // them after a call to release fails.
430    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
431    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
432}
433
434
435// ----------------------------------------------------------------------------
436static jint android_media_AudioTrack_native_write(JNIEnv *env,  jobject thiz,
437                                                  jbyteArray javaAudioData,
438                                                  jint offsetInBytes, jint sizeInBytes,
439                                                  jint javaAudioFormat) {
440    jbyte* cAudioData = NULL;
441    AudioTrack *lpTrack = NULL;
442    //LOGV("android_media_AudioTrack_native_write(offset=%d, sizeInBytes=%d) called",
443    //    offsetInBytes, sizeInBytes);
444
445    // get the audio track to load with samples
446    lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
447    if (lpTrack == NULL) {
448        jniThrowException(env, "java/lang/IllegalStateException",
449            "Unable to retrieve AudioTrack pointer for write()");
450    }
451
452    // get the pointer for the audio data from the java array
453    if (javaAudioData) {
454        cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
455        if (cAudioData == NULL) {
456            LOGE("Error retrieving source of audio data to play, can't play");
457            return 0; // out of memory or no data to load
458        }
459    } else {
460        LOGE("NULL java array of audio data to play, can't play");
461        return 0;
462    }
463
464    // give the data to the native AudioTrack object (the data starts at the offset)
465    ssize_t written = 0;
466    // regular write() or copy the data to the AudioTrack's shared memory?
467    if (lpTrack->sharedBuffer() == 0) {
468        written = lpTrack->write(cAudioData + offsetInBytes, sizeInBytes);
469    } else {
470        if (javaAudioFormat == javaAudioTrackFields.PCM16) {
471            memcpy(lpTrack->sharedBuffer()->pointer(), cAudioData + offsetInBytes, sizeInBytes);
472            written = sizeInBytes;
473        } else if (javaAudioFormat == javaAudioTrackFields.PCM8) {
474            // cAudioData contains 8bit data we need to expand to 16bit before copying
475            // to the shared memory
476            int count = sizeInBytes;
477            int16_t *dst = (int16_t *)lpTrack->sharedBuffer()->pointer();
478            const int8_t *src = (const int8_t *)(cAudioData + offsetInBytes);
479            while(count--) {
480                *dst++ = (int16_t)(*src++^0x80) << 8;
481            }
482            // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide
483            // the 8bit mixer restriction from the user of this function
484            written = sizeInBytes;
485        }
486    }
487
488    env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
489
490    //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
491    //     (int)written, (int)(sizeInBytes), (int)offsetInBytes);
492    return (int)written;
493}
494
495
496// ----------------------------------------------------------------------------
497static jint android_media_AudioTrack_native_write_short(JNIEnv *env,  jobject thiz,
498                                                  jshortArray javaAudioData,
499                                                  jint offsetInShorts, jint sizeInShorts,
500                                                  jint javaAudioFormat) {
501    return (android_media_AudioTrack_native_write(env, thiz,
502                                                 (jbyteArray) javaAudioData,
503                                                 offsetInShorts*2, sizeInShorts*2,
504                                                 javaAudioFormat)
505            / 2);
506}
507
508
509// ----------------------------------------------------------------------------
510static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
511    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
512        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
513
514    if (lpTrack) {
515        return lpTrack->frameCount();
516    } else {
517        jniThrowException(env, "java/lang/IllegalStateException",
518            "Unable to retrieve AudioTrack pointer for frameCount()");
519        return AUDIOTRACK_ERROR;
520    }
521}
522
523
524// ----------------------------------------------------------------------------
525static void android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
526        jint sampleRateInHz) {
527    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
528                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
529
530    if (lpTrack) {
531        lpTrack->setSampleRate(sampleRateInHz);
532    } else {
533        jniThrowException(env, "java/lang/IllegalStateException",
534            "Unable to retrieve AudioTrack pointer for setSampleRate()");
535    }
536}
537
538
539// ----------------------------------------------------------------------------
540static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
541    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
542                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
543
544    if (lpTrack) {
545        return (jint) lpTrack->getSampleRate();
546    } else {
547        jniThrowException(env, "java/lang/IllegalStateException",
548            "Unable to retrieve AudioTrack pointer for getSampleRate()");
549        return AUDIOTRACK_ERROR;
550    }
551}
552
553
554// ----------------------------------------------------------------------------
555static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
556        jint markerPos) {
557
558    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
559                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
560
561    if (lpTrack) {
562        return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
563    } else {
564        jniThrowException(env, "java/lang/IllegalStateException",
565            "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
566        return AUDIOTRACK_ERROR;
567    }
568}
569
570
571// ----------------------------------------------------------------------------
572static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
573
574    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
575                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
576    uint32_t markerPos = 0;
577
578    if (lpTrack) {
579        lpTrack->getMarkerPosition(&markerPos);
580        return (jint)markerPos;
581    } else {
582        jniThrowException(env, "java/lang/IllegalStateException",
583            "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
584        return AUDIOTRACK_ERROR;
585    }
586}
587
588
589// ----------------------------------------------------------------------------
590static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
591        jint period) {
592
593    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
594                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
595
596    if (lpTrack) {
597        return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
598    } else {
599        jniThrowException(env, "java/lang/IllegalStateException",
600            "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
601        return AUDIOTRACK_ERROR;
602    }
603}
604
605
606// ----------------------------------------------------------------------------
607static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
608
609    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
610                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
611    uint32_t period = 0;
612
613    if (lpTrack) {
614        lpTrack->getPositionUpdatePeriod(&period);
615        return (jint)period;
616    } else {
617        jniThrowException(env, "java/lang/IllegalStateException",
618            "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
619        return AUDIOTRACK_ERROR;
620    }
621}
622
623
624// ----------------------------------------------------------------------------
625static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
626        jint position) {
627
628    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
629                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
630
631    if (lpTrack) {
632        return android_media_translateErrorCode( lpTrack->setPosition(position) );
633    } else {
634        jniThrowException(env, "java/lang/IllegalStateException",
635            "Unable to retrieve AudioTrack pointer for setPosition()");
636        return AUDIOTRACK_ERROR;
637    }
638}
639
640
641// ----------------------------------------------------------------------------
642static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
643
644    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
645                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
646    uint32_t position = 0;
647
648    if (lpTrack) {
649        lpTrack->getPosition(&position);
650        return (jint)position;
651    }  else {
652        jniThrowException(env, "java/lang/IllegalStateException",
653            "Unable to retrieve AudioTrack pointer for getPosition()");
654        return AUDIOTRACK_ERROR;
655    }
656}
657
658
659// ----------------------------------------------------------------------------
660static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
661        jint loopStart, jint loopEnd, jint loopCount) {
662
663     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
664                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
665     if (lpTrack) {
666        return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
667     }  else {
668        jniThrowException(env, "java/lang/IllegalStateException",
669            "Unable to retrieve AudioTrack pointer for setLoop()");
670        return AUDIOTRACK_ERROR;
671    }
672}
673
674
675// ----------------------------------------------------------------------------
676static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
677
678     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
679                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
680     if (lpTrack) {
681        return android_media_translateErrorCode( lpTrack->reload() );
682     } else {
683        jniThrowException(env, "java/lang/IllegalStateException",
684            "Unable to retrieve AudioTrack pointer for reload()");
685        return AUDIOTRACK_ERROR;
686    }
687}
688
689
690// ----------------------------------------------------------------------------
691static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz) {
692    int afSamplingRate;
693    AudioTrackJniStorage* lpJniStorage = (AudioTrackJniStorage *)env->GetIntField(
694        thiz, javaAudioTrackFields.jniData);
695    if (lpJniStorage == NULL) {
696        return DEFAULT_OUTPUT_SAMPLE_RATE;
697    }
698
699    if (AudioSystem::getOutputSamplingRate(&afSamplingRate, lpJniStorage->mStreamType) != NO_ERROR) {
700        return DEFAULT_OUTPUT_SAMPLE_RATE;
701    } else {
702        return afSamplingRate;
703    }
704}
705
706
707// ----------------------------------------------------------------------------
708// returns the minimum required size for the successful creation of a streaming AudioTrack
709// returns -1 if there was an error querying the hardware.
710static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
711    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
712    int afSamplingRate;
713    int afFrameCount;
714    uint32_t afLatency;
715
716    if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
717        return -1;
718    }
719    if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
720        return -1;
721    }
722
723    if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
724        return -1;
725    }
726
727    // Ensure that buffer depth covers at least audio hardware latency
728    uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
729    uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
730    int minBuffSize = minFrameCount
731            * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
732            * nbChannels;
733    return minBuffSize;
734}
735
736
737// ----------------------------------------------------------------------------
738// ----------------------------------------------------------------------------
739static JNINativeMethod gMethods[] = {
740    // name,              signature,     funcPtr
741    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
742    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
743    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
744    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
745    {"native_setup",         "(Ljava/lang/Object;IIIIII)I",
746                                         (void *)android_media_AudioTrack_native_setup},
747    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_native_finalize},
748    {"native_release",       "()V",      (void *)android_media_AudioTrack_native_release},
749    {"native_write_byte",    "([BIII)I", (void *)android_media_AudioTrack_native_write},
750    {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_native_write_short},
751    {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
752    {"native_get_native_frame_count",
753                             "()I",      (void *)android_media_AudioTrack_get_native_frame_count},
754    {"native_set_playback_rate",
755                             "(I)V",     (void *)android_media_AudioTrack_set_playback_rate},
756    {"native_get_playback_rate",
757                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
758    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
759    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
760    {"native_set_pos_update_period",
761                             "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
762    {"native_get_pos_update_period",
763                             "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
764    {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
765    {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
766    {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
767    {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
768    {"native_get_output_sample_rate",
769                             "()I",      (void *)android_media_AudioTrack_get_output_sample_rate},
770    {"native_get_min_buff_size",
771                             "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
772};
773
774
775// field names found in android/media/AudioTrack.java
776#define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
777#define JAVA_CONST_PCM16_NAME                           "ENCODING_PCM_16BIT"
778#define JAVA_CONST_PCM8_NAME                            "ENCODING_PCM_8BIT"
779#define JAVA_CONST_BUFFER_COUNT_NAME                    "BUFFER_COUNT"
780#define JAVA_CONST_STREAM_VOICE_CALL_NAME               "STREAM_VOICE_CALL"
781#define JAVA_CONST_STREAM_SYSTEM_NAME                   "STREAM_SYSTEM"
782#define JAVA_CONST_STREAM_RING_NAME                     "STREAM_RING"
783#define JAVA_CONST_STREAM_MUSIC_NAME                    "STREAM_MUSIC"
784#define JAVA_CONST_STREAM_ALARM_NAME                    "STREAM_ALARM"
785#define JAVA_CONST_STREAM_NOTIFICATION_NAME             "STREAM_NOTIFICATION"
786#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME            "STREAM_BLUETOOTH_SCO"
787#define JAVA_CONST_MODE_STREAM_NAME                     "MODE_STREAM"
788#define JAVA_CONST_MODE_STATIC_NAME                     "MODE_STATIC"
789#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
790#define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
791
792#define JAVA_AUDIOFORMAT_CLASS_NAME             "android/media/AudioFormat"
793#define JAVA_AUDIOMANAGER_CLASS_NAME            "android/media/AudioManager"
794
795// ----------------------------------------------------------------------------
796// preconditions:
797//    theClass is valid
798bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
799                             const char* constName, int* constVal) {
800    jfieldID javaConst = NULL;
801    javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
802    if (javaConst != NULL) {
803        *constVal = pEnv->GetStaticIntField(theClass, javaConst);
804        return true;
805    } else {
806        LOGE("Can't find %s.%s", className, constName);
807        return false;
808    }
809}
810
811
812// ----------------------------------------------------------------------------
813int register_android_media_AudioTrack(JNIEnv *env)
814{
815    javaAudioTrackFields.audioTrackClass = NULL;
816    javaAudioTrackFields.nativeTrackInJavaObj = NULL;
817    javaAudioTrackFields.postNativeEventInJava = NULL;
818
819    // Get the AudioTrack class
820    javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName);
821    if (javaAudioTrackFields.audioTrackClass == NULL) {
822        LOGE("Can't find %s", kClassPathName);
823        return -1;
824    }
825
826    // Get the postEvent method
827    javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID(
828            javaAudioTrackFields.audioTrackClass,
829            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
830    if (javaAudioTrackFields.postNativeEventInJava == NULL) {
831        LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME);
832        return -1;
833    }
834
835    // Get the variables fields
836    //      nativeTrackInJavaObj
837    javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID(
838            javaAudioTrackFields.audioTrackClass,
839            JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I");
840    if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) {
841        LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME);
842        return -1;
843    }
844    //      jniData;
845    javaAudioTrackFields.jniData = env->GetFieldID(
846            javaAudioTrackFields.audioTrackClass,
847            JAVA_JNIDATA_FIELD_NAME, "I");
848    if (javaAudioTrackFields.jniData == NULL) {
849        LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME);
850        return -1;
851    }
852
853    // Get the memory mode constants
854    if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
855               kClassPathName,
856               JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC))
857         || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
858               kClassPathName,
859               JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) {
860        // error log performed in android_media_getIntConstantFromClass()
861        return -1;
862    }
863
864    // Get the format constants from the AudioFormat class
865    jclass audioFormatClass = NULL;
866    audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
867    if (audioFormatClass == NULL) {
868        LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
869        return -1;
870    }
871    if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
872                JAVA_AUDIOFORMAT_CLASS_NAME,
873                JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16))
874           || !android_media_getIntConstantFromClass(env, audioFormatClass,
875                JAVA_AUDIOFORMAT_CLASS_NAME,
876                JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) {
877        // error log performed in android_media_getIntConstantFromClass()
878        return -1;
879    }
880
881    // Get the stream types from the AudioManager class
882    jclass audioManagerClass = NULL;
883    audioManagerClass = env->FindClass(JAVA_AUDIOMANAGER_CLASS_NAME);
884    if (audioManagerClass == NULL) {
885       LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME);
886       return -1;
887    }
888    if ( !android_media_getIntConstantFromClass(env, audioManagerClass,
889               JAVA_AUDIOMANAGER_CLASS_NAME,
890               JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL))
891          || !android_media_getIntConstantFromClass(env, audioManagerClass,
892               JAVA_AUDIOMANAGER_CLASS_NAME,
893               JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC))
894          || !android_media_getIntConstantFromClass(env, audioManagerClass,
895               JAVA_AUDIOMANAGER_CLASS_NAME,
896               JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM))
897          || !android_media_getIntConstantFromClass(env, audioManagerClass,
898               JAVA_AUDIOMANAGER_CLASS_NAME,
899               JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING))
900          || !android_media_getIntConstantFromClass(env, audioManagerClass,
901               JAVA_AUDIOMANAGER_CLASS_NAME,
902               JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM))
903          || !android_media_getIntConstantFromClass(env, audioManagerClass,
904               JAVA_AUDIOMANAGER_CLASS_NAME,
905               JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION))
906          || !android_media_getIntConstantFromClass(env, audioManagerClass,
907               JAVA_AUDIOMANAGER_CLASS_NAME,
908               JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME,
909               &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) {
910       // error log performed in android_media_getIntConstantFromClass()
911       return -1;
912    }
913
914    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
915}
916
917
918// ----------------------------------------------------------------------------
919