android_media_AudioTrack.cpp revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
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 <utils/MemoryHeapBase.h>
34#include <utils/MemoryBase.h>
35
36
37// ----------------------------------------------------------------------------
38
39using namespace android;
40
41// ----------------------------------------------------------------------------
42static const char* const kClassPathName = "android/media/AudioTrack";
43
44struct fields_t {
45    // these fields provide access from C++ to the...
46    jclass    audioTrackClass;       //... AudioTrack class
47    jmethodID postNativeEventInJava; //... event post callback method
48    int       PCM16;                 //...  format constants
49    int       PCM8;                  //...  format constants
50    int       STREAM_VOICE_CALL;     //...  stream type constants
51    int       STREAM_SYSTEM;         //...  stream type constants
52    int       STREAM_RING;           //...  stream type constants
53    int       STREAM_MUSIC;          //...  stream type constants
54    int       STREAM_ALARM;          //...  stream type constants
55    int       STREAM_NOTIFICATION;   //...  stream type constants
56    int       STREAM_BLUETOOTH_SCO;   //...  stream type constants
57    int       MODE_STREAM;           //...  memory mode
58    int       MODE_STATIC;           //...  memory mode
59    jfieldID  nativeTrackInJavaObj; // stores in Java the native AudioTrack object
60    jfieldID  jniData;      // stores in Java additional resources used by the native AudioTrack
61};
62static fields_t javaAudioTrackFields;
63
64struct audiotrack_callback_cookie {
65    jclass      audioTrack_class;
66    jobject     audioTrack_ref;
67 };
68
69// ----------------------------------------------------------------------------
70class AudioTrackJniStorage {
71    public:
72        sp<MemoryHeapBase>         mMemHeap;
73        sp<MemoryBase>             mMemBase;
74        audiotrack_callback_cookie mCallbackData;
75        int                        mStreamType;
76
77    AudioTrackJniStorage() {
78    }
79
80    ~AudioTrackJniStorage() {
81        mMemBase.clear();
82        mMemHeap.clear();
83    }
84
85    bool allocSharedMem(int sizeInBytes) {
86        mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
87        if (mMemHeap->getHeapID() < 0) {
88            return false;
89        }
90        mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
91        return true;
92    }
93};
94
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    // compute the frame count
215    int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
216    int format = audioFormat == javaAudioTrackFields.PCM16 ?
217            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
218    int frameCount;
219    if (buffSizeInBytes == -1) {
220        // compute the frame count based on the system's output frame count
221        // and the native sample rate
222        frameCount = (sampleRateInHertz*afFrameCount)/afSampleRate;
223    } else {
224        // compute the frame count based on the specified buffer size
225        frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
226    }
227
228    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
229
230    // initialize the callback information:
231    // this data will be passed with every AudioTrack callback
232    jclass clazz = env->GetObjectClass(thiz);
233    if (clazz == NULL) {
234        LOGE("Can't find %s when setting up callback.", kClassPathName);
235        delete lpJniStorage;
236        return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
237    }
238    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
239    // we use a weak reference so the AudioTrack object can be garbage collected.
240    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
241
242    lpJniStorage->mStreamType = atStreamType;
243
244    // create the native AudioTrack object
245    AudioTrack* lpTrack = new AudioTrack();
246    if (lpTrack == NULL) {
247        LOGE("Error creating uninitialized AudioTrack");
248        goto native_track_failure;
249    }
250
251    // initialize the native AudioTrack object
252    if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
253
254        lpTrack->set(
255            atStreamType,// stream type
256            sampleRateInHertz,
257            format,// word length, PCM
258            nbChannels,
259            frameCount,
260            0,// flags
261            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
262            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
263            0,// shared mem
264            true);// thread can call Java
265
266    } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
267        // AudioTrack is using shared memory
268
269        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
270            LOGE("Error creating AudioTrack in static mode: error creating mem heap base");
271            goto native_init_failure;
272        }
273
274        lpTrack->set(
275            atStreamType,// stream type
276            sampleRateInHertz,
277            format,// word length, PCM
278            nbChannels,
279            frameCount,
280            0,// flags
281            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
282            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
283            lpJniStorage->mMemBase,// shared mem
284            true);// thread can call Java
285    }
286
287    if (lpTrack->initCheck() != NO_ERROR) {
288        LOGE("Error initializing AudioTrack");
289        goto native_init_failure;
290    }
291
292    // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
293    // of the Java object (in mNativeTrackInJavaObj)
294    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
295
296    // save the JNI resources so we can free them later
297    //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
298    env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
299
300    return AUDIOTRACK_SUCCESS;
301
302    // failures:
303native_init_failure:
304    delete lpTrack;
305    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
306
307native_track_failure:
308    delete lpJniStorage;
309    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
310    return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
311
312}
313
314
315// ----------------------------------------------------------------------------
316static void
317android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
318{
319    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
320        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
321    if (lpTrack == NULL ) {
322        jniThrowException(env, "java/lang/IllegalStateException",
323            "Unable to retrieve AudioTrack pointer for start()");
324    }
325
326    lpTrack->start();
327}
328
329
330// ----------------------------------------------------------------------------
331static void
332android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
333{
334    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
335        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
336    if (lpTrack == NULL ) {
337        jniThrowException(env, "java/lang/IllegalStateException",
338            "Unable to retrieve AudioTrack pointer for stop()");
339    }
340
341    lpTrack->stop();
342}
343
344
345// ----------------------------------------------------------------------------
346static void
347android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
348{
349    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
350        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
351    if (lpTrack == NULL ) {
352        jniThrowException(env, "java/lang/IllegalStateException",
353            "Unable to retrieve AudioTrack pointer for pause()");
354    }
355
356    lpTrack->pause();
357}
358
359
360// ----------------------------------------------------------------------------
361static void
362android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
363{
364    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
365        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
366    if (lpTrack == NULL ) {
367        jniThrowException(env, "java/lang/IllegalStateException",
368            "Unable to retrieve AudioTrack pointer for flush()");
369    }
370
371    lpTrack->flush();
372}
373
374// ----------------------------------------------------------------------------
375static void
376android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
377{
378    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
379        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
380    if (lpTrack == NULL ) {
381        jniThrowException(env, "java/lang/IllegalStateException",
382            "Unable to retrieve AudioTrack pointer for setVolume()");
383    }
384
385    lpTrack->setVolume(leftVol, rightVol);
386}
387
388// ----------------------------------------------------------------------------
389static void android_media_AudioTrack_native_finalize(JNIEnv *env,  jobject thiz) {
390    LOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
391
392    // delete the AudioTrack object
393    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
394        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
395    if (lpTrack) {
396        LOGV("deleting lpTrack: %x\n", (int)lpTrack);
397        lpTrack->stop();
398        delete lpTrack;
399    }
400
401    // delete the JNI data
402    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
403        thiz, javaAudioTrackFields.jniData);
404    if (pJniStorage) {
405        LOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
406        delete pJniStorage;
407    }
408}
409
410// ----------------------------------------------------------------------------
411static void android_media_AudioTrack_native_release(JNIEnv *env,  jobject thiz) {
412
413    // do everything a call to finalize would
414    android_media_AudioTrack_native_finalize(env, thiz);
415    // + reset the native resources in the Java object so any attempt to access
416    // them after a call to release fails.
417    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
418    env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
419}
420
421
422// ----------------------------------------------------------------------------
423static jint android_media_AudioTrack_native_write(JNIEnv *env,  jobject thiz,
424                                                  jbyteArray javaAudioData,
425                                                  jint offsetInBytes, jint sizeInBytes) {
426    jbyte* cAudioData = NULL;
427    AudioTrack *lpTrack = NULL;
428    //LOGV("android_media_AudioTrack_native_write(offset=%d, sizeInBytes=%d) called",
429    //    offsetInBytes, sizeInBytes);
430
431    // get the audio track to load with samples
432    lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
433    if (lpTrack == NULL) {
434        jniThrowException(env, "java/lang/IllegalStateException",
435            "Unable to retrieve AudioTrack pointer for write()");
436    }
437
438    // get the pointer for the audio data from the java array
439    if (javaAudioData) {
440        cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
441        if (cAudioData == NULL) {
442            LOGE("Error retrieving source of audio data to play, can't play");
443            return 0; // out of memory or no data to load
444        }
445    } else {
446        LOGE("NULL java array of audio data to play, can't play");
447        return 0;
448    }
449
450    // give the data to the native AudioTrack object (the data starts at the offset)
451    ssize_t written = 0;
452    // regular write() or copy the data to the AudioTrack's shared memory?
453    if (lpTrack->sharedBuffer() == 0) {
454        written = lpTrack->write(cAudioData + offsetInBytes, sizeInBytes);
455    } else {
456        memcpy(lpTrack->sharedBuffer()->pointer(), cAudioData + offsetInBytes, sizeInBytes);
457        written = sizeInBytes;
458    }
459
460    env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
461
462    //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
463    //     (int)written, (int)(sizeInBytes), (int)offsetInBytes);
464    return (int)written;
465}
466
467
468// ----------------------------------------------------------------------------
469static jint android_media_AudioTrack_native_write_short(JNIEnv *env,  jobject thiz,
470                                                  jshortArray javaAudioData,
471                                                  jint offsetInShorts, jint sizeInShorts) {
472    return (android_media_AudioTrack_native_write(env, thiz,
473                                                 (jbyteArray) javaAudioData,
474                                                 offsetInShorts*2, sizeInShorts*2)
475            / 2);
476}
477
478
479// ----------------------------------------------------------------------------
480static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
481    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
482        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
483
484    if (lpTrack) {
485        return lpTrack->frameCount();
486    } else {
487        jniThrowException(env, "java/lang/IllegalStateException",
488            "Unable to retrieve AudioTrack pointer for frameCount()");
489        return AUDIOTRACK_ERROR;
490    }
491}
492
493
494// ----------------------------------------------------------------------------
495static void android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
496        jint sampleRateInHz) {
497    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
498                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
499
500    if (lpTrack) {
501        lpTrack->setSampleRate(sampleRateInHz);
502    } else {
503        jniThrowException(env, "java/lang/IllegalStateException",
504            "Unable to retrieve AudioTrack pointer for setSampleRate()");
505    }
506}
507
508
509// ----------------------------------------------------------------------------
510static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
511    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
512                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
513
514    if (lpTrack) {
515        return (jint) lpTrack->getSampleRate();
516    } else {
517        jniThrowException(env, "java/lang/IllegalStateException",
518            "Unable to retrieve AudioTrack pointer for getSampleRate()");
519        return AUDIOTRACK_ERROR;
520    }
521}
522
523
524// ----------------------------------------------------------------------------
525static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
526        jint markerPos) {
527
528    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
529                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
530
531    if (lpTrack) {
532        return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
533    } else {
534        jniThrowException(env, "java/lang/IllegalStateException",
535            "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
536        return AUDIOTRACK_ERROR;
537    }
538}
539
540
541// ----------------------------------------------------------------------------
542static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
543
544    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
545                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
546    uint32_t markerPos = 0;
547
548    if (lpTrack) {
549        lpTrack->getMarkerPosition(&markerPos);
550        return (jint)markerPos;
551    } else {
552        jniThrowException(env, "java/lang/IllegalStateException",
553            "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
554        return AUDIOTRACK_ERROR;
555    }
556}
557
558
559// ----------------------------------------------------------------------------
560static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
561        jint period) {
562
563    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
564                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
565
566    if (lpTrack) {
567        return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
568    } else {
569        jniThrowException(env, "java/lang/IllegalStateException",
570            "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
571        return AUDIOTRACK_ERROR;
572    }
573}
574
575
576// ----------------------------------------------------------------------------
577static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
578
579    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
580                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
581    uint32_t period = 0;
582
583    if (lpTrack) {
584        lpTrack->getPositionUpdatePeriod(&period);
585        return (jint)period;
586    } else {
587        jniThrowException(env, "java/lang/IllegalStateException",
588            "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
589        return AUDIOTRACK_ERROR;
590    }
591}
592
593
594// ----------------------------------------------------------------------------
595static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
596        jint position) {
597
598    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
599                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
600
601    if (lpTrack) {
602        return android_media_translateErrorCode( lpTrack->setPosition(position) );
603    } else {
604        jniThrowException(env, "java/lang/IllegalStateException",
605            "Unable to retrieve AudioTrack pointer for setPosition()");
606        return AUDIOTRACK_ERROR;
607    }
608}
609
610
611// ----------------------------------------------------------------------------
612static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
613
614    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
615                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
616    uint32_t position = 0;
617
618    if (lpTrack) {
619        lpTrack->getPosition(&position);
620        return (jint)position;
621    }  else {
622        jniThrowException(env, "java/lang/IllegalStateException",
623            "Unable to retrieve AudioTrack pointer for getPosition()");
624        return AUDIOTRACK_ERROR;
625    }
626}
627
628
629// ----------------------------------------------------------------------------
630static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
631        jint loopStart, jint loopEnd, jint loopCount) {
632
633     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
634                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
635     if (lpTrack) {
636        return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
637     }  else {
638        jniThrowException(env, "java/lang/IllegalStateException",
639            "Unable to retrieve AudioTrack pointer for setLoop()");
640        return AUDIOTRACK_ERROR;
641    }
642}
643
644
645// ----------------------------------------------------------------------------
646static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
647
648     AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
649                thiz, javaAudioTrackFields.nativeTrackInJavaObj);
650     if (lpTrack) {
651        return android_media_translateErrorCode( lpTrack->reload() );
652     } else {
653        jniThrowException(env, "java/lang/IllegalStateException",
654            "Unable to retrieve AudioTrack pointer for reload()");
655        return AUDIOTRACK_ERROR;
656    }
657}
658
659
660// ----------------------------------------------------------------------------
661static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz) {
662    int afSamplingRate;
663    AudioTrackJniStorage* lpJniStorage = (AudioTrackJniStorage *)env->GetIntField(
664        thiz, javaAudioTrackFields.jniData);
665    if (lpJniStorage == NULL) {
666        return DEFAULT_OUTPUT_SAMPLE_RATE;
667    }
668
669    if (AudioSystem::getOutputSamplingRate(&afSamplingRate, lpJniStorage->mStreamType) != NO_ERROR) {
670        return DEFAULT_OUTPUT_SAMPLE_RATE;
671    } else {
672        return afSamplingRate;
673    }
674}
675
676
677// ----------------------------------------------------------------------------
678// returns the minimum required size for the successful creation of a streaming AudioTrack
679static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
680    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
681    int afSamplingRate;
682    int afFrameCount;
683    uint32_t afLatency;
684
685    if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
686        return -1;
687    }
688    if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
689        return -1;
690    }
691
692    if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
693        return -1;
694    }
695
696    // Ensure that buffer depth covers at least audio hardware latency
697    uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
698    uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
699    int minBuffSize = minFrameCount
700            * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
701            * nbChannels;
702    return minBuffSize;
703}
704
705
706// ----------------------------------------------------------------------------
707// ----------------------------------------------------------------------------
708static JNINativeMethod gMethods[] = {
709    // name,              signature,     funcPtr
710    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
711    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
712    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
713    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
714    {"native_setup",         "(Ljava/lang/Object;IIIIII)I",
715                                         (void *)android_media_AudioTrack_native_setup},
716    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_native_finalize},
717    {"native_release",       "()V",      (void *)android_media_AudioTrack_native_release},
718    {"native_write_byte",    "([BII)I",  (void *)android_media_AudioTrack_native_write},
719    {"native_write_short",   "([SII)I",  (void *)android_media_AudioTrack_native_write_short},
720    {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
721    {"native_get_native_frame_count",
722                             "()I",      (void *)android_media_AudioTrack_get_native_frame_count},
723    {"native_set_playback_rate",
724                             "(I)V",     (void *)android_media_AudioTrack_set_playback_rate},
725    {"native_get_playback_rate",
726                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
727    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
728    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
729    {"native_set_pos_update_period",
730                             "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
731    {"native_get_pos_update_period",
732                             "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
733    {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
734    {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
735    {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
736    {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
737    {"native_get_output_sample_rate",
738                             "()I",      (void *)android_media_AudioTrack_get_output_sample_rate},
739    {"native_get_min_buff_size",
740                             "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
741};
742
743
744// field names found in android/media/AudioTrack.java
745#define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
746#define JAVA_CONST_PCM16_NAME                           "ENCODING_PCM_16BIT"
747#define JAVA_CONST_PCM8_NAME                            "ENCODING_PCM_8BIT"
748#define JAVA_CONST_BUFFER_COUNT_NAME                    "BUFFER_COUNT"
749#define JAVA_CONST_STREAM_VOICE_CALL_NAME               "STREAM_VOICE_CALL"
750#define JAVA_CONST_STREAM_SYSTEM_NAME                   "STREAM_SYSTEM"
751#define JAVA_CONST_STREAM_RING_NAME                     "STREAM_RING"
752#define JAVA_CONST_STREAM_MUSIC_NAME                    "STREAM_MUSIC"
753#define JAVA_CONST_STREAM_ALARM_NAME                    "STREAM_ALARM"
754#define JAVA_CONST_STREAM_NOTIFICATION_NAME             "STREAM_NOTIFICATION"
755#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME            "STREAM_BLUETOOTH_SCO"
756#define JAVA_CONST_MODE_STREAM_NAME                     "MODE_STREAM"
757#define JAVA_CONST_MODE_STATIC_NAME                     "MODE_STATIC"
758#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
759#define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
760
761#define JAVA_AUDIOFORMAT_CLASS_NAME             "android/media/AudioFormat"
762#define JAVA_AUDIOMANAGER_CLASS_NAME            "android/media/AudioManager"
763
764// ----------------------------------------------------------------------------
765// preconditions:
766//    theClass is valid
767bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
768                             const char* constName, int* constVal) {
769    jfieldID javaConst = NULL;
770    javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
771    if (javaConst != NULL) {
772        *constVal = pEnv->GetStaticIntField(theClass, javaConst);
773        return true;
774    } else {
775        LOGE("Can't find %s.%s", className, constName);
776        return false;
777    }
778}
779
780
781// ----------------------------------------------------------------------------
782int register_android_media_AudioTrack(JNIEnv *env)
783{
784    javaAudioTrackFields.audioTrackClass = NULL;
785    javaAudioTrackFields.nativeTrackInJavaObj = NULL;
786    javaAudioTrackFields.postNativeEventInJava = NULL;
787
788    // Get the AudioTrack class
789    javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName);
790    if (javaAudioTrackFields.audioTrackClass == NULL) {
791        LOGE("Can't find %s", kClassPathName);
792        return -1;
793    }
794
795    // Get the postEvent method
796    javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID(
797            javaAudioTrackFields.audioTrackClass,
798            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
799    if (javaAudioTrackFields.postNativeEventInJava == NULL) {
800        LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME);
801        return -1;
802    }
803
804    // Get the variables fields
805    //      nativeTrackInJavaObj
806    javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID(
807            javaAudioTrackFields.audioTrackClass,
808            JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I");
809    if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) {
810        LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME);
811        return -1;
812    }
813    //      jniData;
814    javaAudioTrackFields.jniData = env->GetFieldID(
815            javaAudioTrackFields.audioTrackClass,
816            JAVA_JNIDATA_FIELD_NAME, "I");
817    if (javaAudioTrackFields.jniData == NULL) {
818        LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME);
819        return -1;
820    }
821
822    // Get the memory mode constants
823    if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
824               kClassPathName,
825               JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC))
826         || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
827               kClassPathName,
828               JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) {
829        // error log performed in android_media_getIntConstantFromClass()
830        return -1;
831    }
832
833    // Get the format constants from the AudioFormat class
834    jclass audioFormatClass = NULL;
835    audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
836    if (audioFormatClass == NULL) {
837        LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
838        return -1;
839    }
840    if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
841                JAVA_AUDIOFORMAT_CLASS_NAME,
842                JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16))
843           || !android_media_getIntConstantFromClass(env, audioFormatClass,
844                JAVA_AUDIOFORMAT_CLASS_NAME,
845                JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) {
846        // error log performed in android_media_getIntConstantFromClass()
847        return -1;
848    }
849
850    // Get the stream types from the AudioManager class
851    jclass audioManagerClass = NULL;
852    audioManagerClass = env->FindClass(JAVA_AUDIOMANAGER_CLASS_NAME);
853    if (audioManagerClass == NULL) {
854       LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME);
855       return -1;
856    }
857    if ( !android_media_getIntConstantFromClass(env, audioManagerClass,
858               JAVA_AUDIOMANAGER_CLASS_NAME,
859               JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL))
860          || !android_media_getIntConstantFromClass(env, audioManagerClass,
861               JAVA_AUDIOMANAGER_CLASS_NAME,
862               JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC))
863          || !android_media_getIntConstantFromClass(env, audioManagerClass,
864               JAVA_AUDIOMANAGER_CLASS_NAME,
865               JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM))
866          || !android_media_getIntConstantFromClass(env, audioManagerClass,
867               JAVA_AUDIOMANAGER_CLASS_NAME,
868               JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING))
869          || !android_media_getIntConstantFromClass(env, audioManagerClass,
870               JAVA_AUDIOMANAGER_CLASS_NAME,
871               JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM))
872          || !android_media_getIntConstantFromClass(env, audioManagerClass,
873               JAVA_AUDIOMANAGER_CLASS_NAME,
874               JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION))
875          || !android_media_getIntConstantFromClass(env, audioManagerClass,
876               JAVA_AUDIOMANAGER_CLASS_NAME,
877               JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME,
878               &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) {
879       // error log performed in android_media_getIntConstantFromClass()
880       return -1;
881    }
882
883    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
884}
885
886
887// ----------------------------------------------------------------------------
888