android_media_AudioRecord.cpp revision 2d6de4c38c899707e0596b7fa4dad9bbb3eb6b60
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
17//#define LOG_NDEBUG 0
18
19#define LOG_TAG "AudioRecord-JNI"
20
21#include <inttypes.h>
22#include <jni.h>
23#include <JNIHelp.h>
24#include "core_jni_helpers.h"
25
26#include <utils/Log.h>
27#include <media/AudioRecord.h>
28
29#include "android_media_AudioFormat.h"
30#include "android_media_AudioErrors.h"
31
32// ----------------------------------------------------------------------------
33
34using namespace android;
35
36// ----------------------------------------------------------------------------
37static const char* const kClassPathName = "android/media/AudioRecord";
38static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
39
40struct audio_record_fields_t {
41    // these fields provide access from C++ to the...
42    jmethodID postNativeEventInJava; //... event post callback method
43    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
44    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
45};
46struct audio_attributes_fields_t {
47    jfieldID  fieldRecSource;    // AudioAttributes.mSource
48    jfieldID  fieldFlags;        // AudioAttributes.mFlags
49    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
50};
51static audio_attributes_fields_t javaAudioAttrFields;
52static audio_record_fields_t     javaAudioRecordFields;
53
54struct audiorecord_callback_cookie {
55    jclass      audioRecord_class;
56    jobject     audioRecord_ref;
57    bool        busy;
58    Condition   cond;
59};
60
61static Mutex sLock;
62static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
63
64// ----------------------------------------------------------------------------
65
66#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
67#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
68#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
69#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
70#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
71
72// ----------------------------------------------------------------------------
73static void recorderCallback(int event, void* user, void *info) {
74
75    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
76    {
77        Mutex::Autolock l(sLock);
78        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
79            return;
80        }
81        callbackInfo->busy = true;
82    }
83
84    switch (event) {
85    case AudioRecord::EVENT_MARKER: {
86        JNIEnv *env = AndroidRuntime::getJNIEnv();
87        if (user != NULL && env != NULL) {
88            env->CallStaticVoidMethod(
89                callbackInfo->audioRecord_class,
90                javaAudioRecordFields.postNativeEventInJava,
91                callbackInfo->audioRecord_ref, event, 0,0, NULL);
92            if (env->ExceptionCheck()) {
93                env->ExceptionDescribe();
94                env->ExceptionClear();
95            }
96        }
97        } break;
98
99    case AudioRecord::EVENT_NEW_POS: {
100        JNIEnv *env = AndroidRuntime::getJNIEnv();
101        if (user != NULL && env != NULL) {
102            env->CallStaticVoidMethod(
103                callbackInfo->audioRecord_class,
104                javaAudioRecordFields.postNativeEventInJava,
105                callbackInfo->audioRecord_ref, event, 0,0, NULL);
106            if (env->ExceptionCheck()) {
107                env->ExceptionDescribe();
108                env->ExceptionClear();
109            }
110        }
111        } break;
112    }
113
114    {
115        Mutex::Autolock l(sLock);
116        callbackInfo->busy = false;
117        callbackInfo->cond.broadcast();
118    }
119}
120
121// ----------------------------------------------------------------------------
122static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
123{
124    Mutex::Autolock l(sLock);
125    AudioRecord* const ar =
126            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
127    return sp<AudioRecord>(ar);
128}
129
130static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
131{
132    Mutex::Autolock l(sLock);
133    sp<AudioRecord> old =
134            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
135    if (ar.get()) {
136        ar->incStrong((void*)setAudioRecord);
137    }
138    if (old != 0) {
139        old->decStrong((void*)setAudioRecord);
140    }
141    env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
142    return old;
143}
144
145// ----------------------------------------------------------------------------
146static jint
147android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
148        jobject jaa, jint sampleRateInHertz, jint channelMask,
149                // Java channel masks map directly to the native definition
150        jint audioFormat, jint buffSizeInBytes, jintArray jSession)
151{
152    //ALOGV(">> Entering android_media_AudioRecord_setup");
153    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
154    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
155
156    if (jaa == 0) {
157        ALOGE("Error creating AudioRecord: invalid audio attributes");
158        return (jint) AUDIO_JAVA_ERROR;
159    }
160
161    if (!audio_is_input_channel(channelMask)) {
162        ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
163        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
164    }
165    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
166
167    // compare the format against the Java constants
168    audio_format_t format = audioFormatToNative(audioFormat);
169    if (format == AUDIO_FORMAT_INVALID) {
170        ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
171        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
172    }
173
174    size_t bytesPerSample = audio_bytes_per_sample(format);
175
176    if (buffSizeInBytes == 0) {
177         ALOGE("Error creating AudioRecord: frameCount is 0.");
178        return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
179    }
180    size_t frameSize = channelCount * bytesPerSample;
181    size_t frameCount = buffSizeInBytes / frameSize;
182
183    jclass clazz = env->GetObjectClass(thiz);
184    if (clazz == NULL) {
185        ALOGE("Can't find %s when setting up callback.", kClassPathName);
186        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
187    }
188
189    if (jSession == NULL) {
190        ALOGE("Error creating AudioRecord: invalid session ID pointer");
191        return (jint) AUDIO_JAVA_ERROR;
192    }
193
194    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
195    if (nSession == NULL) {
196        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
197        return (jint) AUDIO_JAVA_ERROR;
198    }
199    int sessionId = nSession[0];
200    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
201    nSession = NULL;
202
203    // create an uninitialized AudioRecord object
204    sp<AudioRecord> lpRecorder = new AudioRecord();
205
206    audio_attributes_t *paa = NULL;
207    // read the AudioAttributes values
208    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
209    const jstring jtags =
210            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
211    const char* tags = env->GetStringUTFChars(jtags, NULL);
212    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
213    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
214    env->ReleaseStringUTFChars(jtags, tags);
215    paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
216    paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
217    ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
218
219    audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
220    if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
221        flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
222    }
223    // create the callback information:
224    // this data will be passed with every AudioRecord callback
225    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
226    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
227    // we use a weak reference so the AudioRecord object can be garbage collected.
228    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
229    lpCallbackData->busy = false;
230
231    const status_t status = lpRecorder->set(paa->source,
232        sampleRateInHertz,
233        format,        // word length, PCM
234        channelMask,
235        frameCount,
236        recorderCallback,// callback_t
237        lpCallbackData,// void* user
238        0,             // notificationFrames,
239        true,          // threadCanCallJava
240        sessionId,
241        AudioRecord::TRANSFER_DEFAULT,
242        flags,
243        paa);
244
245    if (status != NO_ERROR) {
246        ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
247                status);
248        goto native_init_failure;
249    }
250
251    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
252    if (nSession == NULL) {
253        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
254        goto native_init_failure;
255    }
256    // read the audio session ID back from AudioRecord in case a new session was created during set()
257    nSession[0] = lpRecorder->getSessionId();
258    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
259    nSession = NULL;
260
261    {   // scope for the lock
262        Mutex::Autolock l(sLock);
263        sAudioRecordCallBackCookies.add(lpCallbackData);
264    }
265    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
266    // of the Java object
267    setAudioRecord(env, thiz, lpRecorder);
268
269    // save our newly created callback information in the "nativeCallbackCookie" field
270    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
271    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
272
273    return (jint) AUDIO_JAVA_SUCCESS;
274
275    // failure:
276native_init_failure:
277    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
278    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
279    delete lpCallbackData;
280    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
281
282    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
283}
284
285
286
287// ----------------------------------------------------------------------------
288static jint
289android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
290{
291    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
292    if (lpRecorder == NULL ) {
293        jniThrowException(env, "java/lang/IllegalStateException", NULL);
294        return (jint) AUDIO_JAVA_ERROR;
295    }
296
297    return nativeToJavaStatus(
298            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
299}
300
301
302// ----------------------------------------------------------------------------
303static void
304android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
305{
306    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
307    if (lpRecorder == NULL ) {
308        jniThrowException(env, "java/lang/IllegalStateException", NULL);
309        return;
310    }
311
312    lpRecorder->stop();
313    //ALOGV("Called lpRecorder->stop()");
314}
315
316
317// ----------------------------------------------------------------------------
318
319#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
320static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
321    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
322    if (lpRecorder == NULL) {
323        return;
324    }
325    ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
326    lpRecorder->stop();
327
328    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
329        thiz, javaAudioRecordFields.nativeCallbackCookie);
330
331    // reset the native resources in the Java object so any attempt to access
332    // them after a call to release fails.
333    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
334
335    // delete the callback information
336    if (lpCookie) {
337        Mutex::Autolock l(sLock);
338        ALOGV("deleting lpCookie: %p", lpCookie);
339        while (lpCookie->busy) {
340            if (lpCookie->cond.waitRelative(sLock,
341                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
342                                                    NO_ERROR) {
343                break;
344            }
345        }
346        sAudioRecordCallBackCookies.remove(lpCookie);
347        env->DeleteGlobalRef(lpCookie->audioRecord_class);
348        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
349        delete lpCookie;
350    }
351}
352
353
354// ----------------------------------------------------------------------------
355static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
356    android_media_AudioRecord_release(env, thiz);
357}
358
359// overloaded JNI array helper functions
360static inline
361jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
362    return env->GetByteArrayElements(array, isCopy);
363}
364
365static inline
366void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
367    env->ReleaseByteArrayElements(array, elems, mode);
368}
369
370static inline
371jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
372    return env->GetShortArrayElements(array, isCopy);
373}
374
375static inline
376void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
377    env->ReleaseShortArrayElements(array, elems, mode);
378}
379
380static inline
381jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
382    return env->GetFloatArrayElements(array, isCopy);
383}
384
385static inline
386void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
387    env->ReleaseFloatArrayElements(array, elems, mode);
388}
389
390static inline
391jint interpretReadSizeError(ssize_t readSize) {
392    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
393    switch (readSize) {
394    case WOULD_BLOCK:
395        return (jint)0;
396    case BAD_VALUE:
397        return (jint)AUDIO_JAVA_BAD_VALUE;
398    default:
399        // may be possible for other errors such as
400        // NO_INIT to happen if restoreRecord_l fails.
401    case INVALID_OPERATION:
402        return (jint)AUDIO_JAVA_INVALID_OPERATION;
403    }
404}
405
406template <typename T>
407static jint android_media_AudioRecord_readInArray(JNIEnv *env,  jobject thiz,
408                                                  T javaAudioData,
409                                                  jint offsetInSamples, jint sizeInSamples,
410                                                  jboolean isReadBlocking) {
411    // get the audio recorder from which we'll read new audio samples
412    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
413    if (lpRecorder == NULL) {
414        ALOGE("Unable to retrieve AudioRecord object");
415        return (jint)AUDIO_JAVA_INVALID_OPERATION;
416    }
417
418    if (javaAudioData == NULL) {
419        ALOGE("Invalid Java array to store recorded audio");
420        return (jint)AUDIO_JAVA_BAD_VALUE;
421    }
422
423    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
424    // a way that it becomes much more efficient. When doing so, we will have to prevent the
425    // AudioSystem callback to be called while in critical section (in case of media server
426    // process crash for instance)
427
428    // get the pointer to where we'll record the audio
429    auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
430    if (recordBuff == NULL) {
431        ALOGE("Error retrieving destination for recorded audio data");
432        return (jint)AUDIO_JAVA_BAD_VALUE;
433    }
434
435    // read the new audio data from the native AudioRecord object
436    const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
437    ssize_t readSize = lpRecorder->read(
438            recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
439
440    envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
441
442    if (readSize < 0) {
443        return interpretReadSizeError(readSize);
444    }
445    return (jint)(readSize / sizeof(*recordBuff));
446}
447
448// ----------------------------------------------------------------------------
449static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
450                                                         jobject jBuffer, jint sizeInBytes,
451                                                         jboolean isReadBlocking) {
452    // get the audio recorder from which we'll read new audio samples
453    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
454    if (lpRecorder==NULL)
455        return (jint)AUDIO_JAVA_INVALID_OPERATION;
456
457    // direct buffer and direct access supported?
458    long capacity = env->GetDirectBufferCapacity(jBuffer);
459    if (capacity == -1) {
460        // buffer direct access is not supported
461        ALOGE("Buffer direct access is not supported, can't record");
462        return (jint)AUDIO_JAVA_BAD_VALUE;
463    }
464    //ALOGV("capacity = %ld", capacity);
465    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
466    if (nativeFromJavaBuf==NULL) {
467        ALOGE("Buffer direct access is not supported, can't record");
468        return (jint)AUDIO_JAVA_BAD_VALUE;
469    }
470
471    // read new data from the recorder
472    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
473                                        capacity < sizeInBytes ? capacity : sizeInBytes,
474                                        isReadBlocking == JNI_TRUE /* blocking */);
475    if (readSize < 0) {
476        return interpretReadSizeError(readSize);
477    }
478    return (jint)readSize;
479}
480
481// ----------------------------------------------------------------------------
482static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env,  jobject thiz) {
483    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
484    if (lpRecorder == NULL) {
485        jniThrowException(env, "java/lang/IllegalStateException",
486            "Unable to retrieve AudioRecord pointer for getNativeFrameCount()");
487        return (jint)AUDIO_JAVA_ERROR;
488    }
489    return lpRecorder->frameCount();
490}
491
492// ----------------------------------------------------------------------------
493static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
494        jint markerPos) {
495
496    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
497    if (lpRecorder == NULL) {
498        jniThrowException(env, "java/lang/IllegalStateException",
499            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
500        return (jint)AUDIO_JAVA_ERROR;
501    }
502    return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
503}
504
505
506// ----------------------------------------------------------------------------
507static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
508
509    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
510    uint32_t markerPos = 0;
511
512    if (lpRecorder == NULL) {
513        jniThrowException(env, "java/lang/IllegalStateException",
514            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
515        return (jint)AUDIO_JAVA_ERROR;
516    }
517    lpRecorder->getMarkerPosition(&markerPos);
518    return (jint)markerPos;
519}
520
521
522// ----------------------------------------------------------------------------
523static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
524        jint period) {
525
526    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
527
528    if (lpRecorder == NULL) {
529        jniThrowException(env, "java/lang/IllegalStateException",
530            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
531        return (jint)AUDIO_JAVA_ERROR;
532    }
533    return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
534}
535
536
537// ----------------------------------------------------------------------------
538static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
539
540    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
541    uint32_t period = 0;
542
543    if (lpRecorder == NULL) {
544        jniThrowException(env, "java/lang/IllegalStateException",
545            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
546        return (jint)AUDIO_JAVA_ERROR;
547    }
548    lpRecorder->getPositionUpdatePeriod(&period);
549    return (jint)period;
550}
551
552
553// ----------------------------------------------------------------------------
554// returns the minimum required size for the successful creation of an AudioRecord instance.
555// returns 0 if the parameter combination is not supported.
556// return -1 if there was an error querying the buffer size.
557static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
558    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
559
560    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
561          sampleRateInHertz, channelCount, audioFormat);
562
563    size_t frameCount = 0;
564    audio_format_t format = audioFormatToNative(audioFormat);
565    status_t result = AudioRecord::getMinFrameCount(&frameCount,
566            sampleRateInHertz,
567            format,
568            audio_channel_in_mask_from_count(channelCount));
569
570    if (result == BAD_VALUE) {
571        return 0;
572    }
573    if (result != NO_ERROR) {
574        return -1;
575    }
576    return frameCount * channelCount * audio_bytes_per_sample(format);
577}
578
579static jboolean android_media_AudioRecord_setInputDevice(
580        JNIEnv *env,  jobject thiz, jint device_id) {
581
582//    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
583//    return lpRecorder->setInputDevice(device_id) == NO_ERROR;
584    return false;
585}
586
587// ----------------------------------------------------------------------------
588// ----------------------------------------------------------------------------
589static JNINativeMethod gMethods[] = {
590    // name,               signature,  funcPtr
591    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
592    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
593    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I",
594                                       (void *)android_media_AudioRecord_setup},
595    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
596    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
597    {"native_read_in_byte_array",
598                             "([BIIZ)I",
599                                     (void *)android_media_AudioRecord_readInArray<jbyteArray>},
600    {"native_read_in_short_array",
601                             "([SIIZ)I",
602                                     (void *)android_media_AudioRecord_readInArray<jshortArray>},
603    {"native_read_in_float_array",
604                             "([FIIZ)I",
605                                     (void *)android_media_AudioRecord_readInArray<jfloatArray>},
606    {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
607                                       (void *)android_media_AudioRecord_readInDirectBuffer},
608    {"native_get_native_frame_count",
609                             "()I",    (void *)android_media_AudioRecord_get_native_frame_count},
610    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
611    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
612    {"native_set_pos_update_period",
613                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
614    {"native_get_pos_update_period",
615                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
616    {"native_get_min_buff_size",
617                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
618    {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
619};
620
621// field names found in android/media/AudioRecord.java
622#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
623#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
624#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
625
626// ----------------------------------------------------------------------------
627int register_android_media_AudioRecord(JNIEnv *env)
628{
629    javaAudioRecordFields.postNativeEventInJava = NULL;
630    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
631    javaAudioRecordFields.nativeCallbackCookie = NULL;
632
633
634    // Get the AudioRecord class
635    jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
636    // Get the postEvent method
637    javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
638            audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
639            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
640
641    // Get the variables
642    //    mNativeRecorderInJavaObj
643    javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
644            audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
645    //     mNativeCallbackCookie
646    javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
647            audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
648
649    // Get the AudioAttributes class and fields
650    jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
651    javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
652    javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
653    javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
654            audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
655
656    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
657}
658
659// ----------------------------------------------------------------------------
660