| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| |
| #define LOG_TAG "AudioRecord-JNI" |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <math.h> |
| |
| #include "jni.h" |
| #include "JNIHelp.h" |
| #include "android_runtime/AndroidRuntime.h" |
| |
| #include "utils/Log.h" |
| #include "media/AudioRecord.h" |
| #include "media/mediarecorder.h" |
| |
| |
| // ---------------------------------------------------------------------------- |
| |
| using namespace android; |
| |
| // ---------------------------------------------------------------------------- |
| static const char* const kClassPathName = "android/media/AudioRecord"; |
| |
| struct fields_t { |
| // these fields provide access from C++ to the... |
| jclass audioRecordClass; //... AudioRecord class |
| jmethodID postNativeEventInJava; //... event post callback method |
| int PCM16; //... format constants |
| int PCM8; //... format constants |
| jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object |
| jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data |
| }; |
| static fields_t javaAudioRecordFields; |
| |
| struct audiorecord_callback_cookie { |
| jclass audioRecord_class; |
| jobject audioRecord_ref; |
| }; |
| |
| Mutex sLock; |
| |
| // ---------------------------------------------------------------------------- |
| |
| #define AUDIORECORD_SUCCESS 0 |
| #define AUDIORECORD_ERROR -1 |
| #define AUDIORECORD_ERROR_BAD_VALUE -2 |
| #define AUDIORECORD_ERROR_INVALID_OPERATION -3 |
| #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 |
| #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17 |
| #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 |
| #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19 |
| #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 |
| |
| jint android_media_translateRecorderErrorCode(int code) { |
| switch(code) { |
| case NO_ERROR: |
| return AUDIORECORD_SUCCESS; |
| case BAD_VALUE: |
| return AUDIORECORD_ERROR_BAD_VALUE; |
| case INVALID_OPERATION: |
| return AUDIORECORD_ERROR_INVALID_OPERATION; |
| default: |
| return AUDIORECORD_ERROR; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static void recorderCallback(int event, void* user, void *info) { |
| if (event == AudioRecord::EVENT_MORE_DATA) { |
| // set size to 0 to signal we're not using the callback to read more data |
| AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info; |
| pBuff->size = 0; |
| |
| } else if (event == AudioRecord::EVENT_MARKER) { |
| audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| if (user && env) { |
| env->CallStaticVoidMethod( |
| callbackInfo->audioRecord_class, |
| javaAudioRecordFields.postNativeEventInJava, |
| callbackInfo->audioRecord_ref, event, 0,0, NULL); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| } |
| |
| } else if (event == AudioRecord::EVENT_NEW_POS) { |
| audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| if (user && env) { |
| env->CallStaticVoidMethod( |
| callbackInfo->audioRecord_class, |
| javaAudioRecordFields.postNativeEventInJava, |
| callbackInfo->audioRecord_ref, event, 0,0, NULL); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| } |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static int |
| android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, |
| jint source, jint sampleRateInHertz, jint channels, |
| jint audioFormat, jint buffSizeInBytes) |
| { |
| //LOGV(">> Entering android_media_AudioRecord_setup"); |
| //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d", |
| // sampleRateInHertz, audioFormat, channels, buffSizeInBytes); |
| |
| if (!AudioSystem::isInputChannel(channels)) { |
| LOGE("Error creating AudioRecord: channel count is not 1 or 2."); |
| return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; |
| } |
| uint32_t nbChannels = AudioSystem::popCount(channels); |
| |
| // compare the format against the Java constants |
| if ((audioFormat != javaAudioRecordFields.PCM16) |
| && (audioFormat != javaAudioRecordFields.PCM8)) { |
| LOGE("Error creating AudioRecord: unsupported audio format."); |
| return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; |
| } |
| |
| int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1; |
| int format = audioFormat==javaAudioRecordFields.PCM16 ? |
| AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT; |
| |
| if (buffSizeInBytes == 0) { |
| LOGE("Error creating AudioRecord: frameCount is 0."); |
| return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; |
| } |
| int frameSize = nbChannels * bytesPerSample; |
| size_t frameCount = buffSizeInBytes / frameSize; |
| |
| if (source >= AUDIO_SOURCE_LIST_END) { |
| LOGE("Error creating AudioRecord: unknown source."); |
| return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; |
| } |
| |
| audiorecord_callback_cookie *lpCallbackData = NULL; |
| AudioRecord* lpRecorder = NULL; |
| |
| // create an uninitialized AudioRecord object |
| lpRecorder = new AudioRecord(); |
| if(lpRecorder == NULL) { |
| LOGE("Error creating AudioRecord instance."); |
| return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; |
| } |
| |
| // create the callback information: |
| // this data will be passed with every AudioRecord callback |
| jclass clazz = env->GetObjectClass(thiz); |
| if (clazz == NULL) { |
| LOGE("Can't find %s when setting up callback.", kClassPathName); |
| goto native_track_failure; |
| } |
| lpCallbackData = new audiorecord_callback_cookie; |
| lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); |
| // we use a weak reference so the AudioRecord object can be garbage collected. |
| lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); |
| |
| lpRecorder->set(source, |
| sampleRateInHertz, |
| format, // word length, PCM |
| channels, |
| frameCount, |
| 0, // flags |
| recorderCallback,// callback_t |
| lpCallbackData,// void* user |
| 0, // notificationFrames, |
| true); // threadCanCallJava) |
| |
| if(lpRecorder->initCheck() != NO_ERROR) { |
| LOGE("Error creating AudioRecord instance: initialization check failed."); |
| goto native_init_failure; |
| } |
| |
| // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field |
| // of the Java object |
| env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder); |
| |
| // save our newly created callback information in the "nativeCallbackCookie" field |
| // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() |
| env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData); |
| |
| return AUDIORECORD_SUCCESS; |
| |
| // failure: |
| native_init_failure: |
| env->DeleteGlobalRef(lpCallbackData->audioRecord_class); |
| env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); |
| delete lpCallbackData; |
| |
| native_track_failure: |
| delete lpRecorder; |
| |
| env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); |
| env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); |
| |
| return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; |
| } |
| |
| |
| |
| // ---------------------------------------------------------------------------- |
| static int |
| android_media_AudioRecord_start(JNIEnv *env, jobject thiz) |
| { |
| AudioRecord *lpRecorder = |
| (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| if (lpRecorder == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return AUDIORECORD_ERROR; |
| } |
| |
| return android_media_translateRecorderErrorCode(lpRecorder->start()); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static void |
| android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) |
| { |
| AudioRecord *lpRecorder = |
| (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| if (lpRecorder == NULL ) { |
| jniThrowException(env, "java/lang/IllegalStateException", NULL); |
| return; |
| } |
| |
| lpRecorder->stop(); |
| //LOGV("Called lpRecorder->stop()"); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { |
| |
| // serialize access. Ugly, but functional. |
| Mutex::Autolock lock(&sLock); |
| AudioRecord *lpRecorder = |
| (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField( |
| thiz, javaAudioRecordFields.nativeCallbackCookie); |
| |
| // reset the native resources in the Java object so any attempt to access |
| // them after a call to release fails. |
| env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); |
| env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); |
| |
| // delete the AudioRecord object |
| if (lpRecorder) { |
| LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); |
| lpRecorder->stop(); |
| delete lpRecorder; |
| } |
| |
| // delete the callback information |
| if (lpCookie) { |
| LOGV("deleting lpCookie: %x\n", (int)lpCookie); |
| env->DeleteGlobalRef(lpCookie->audioRecord_class); |
| env->DeleteGlobalRef(lpCookie->audioRecord_ref); |
| delete lpCookie; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { |
| android_media_AudioRecord_release(env, thiz); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, |
| jbyteArray javaAudioData, |
| jint offsetInBytes, jint sizeInBytes) { |
| jbyte* recordBuff = NULL; |
| AudioRecord *lpRecorder = NULL; |
| |
| // get the audio recorder from which we'll read new audio samples |
| lpRecorder = |
| (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| if (lpRecorder == NULL) { |
| LOGE("Unable to retrieve AudioRecord object, can't record"); |
| return 0; |
| } |
| |
| if (!javaAudioData) { |
| LOGE("Invalid Java array to store recorded audio, can't record"); |
| return 0; |
| } |
| |
| // get the pointer to where we'll record the audio |
| recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); |
| |
| if (recordBuff == NULL) { |
| LOGE("Error retrieving destination for recorded audio data, can't record"); |
| return 0; |
| } |
| |
| // read the new audio data from the native AudioRecord object |
| ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); |
| ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, |
| sizeInBytes > (jint)recorderBuffSize ? |
| (jint)recorderBuffSize : sizeInBytes ); |
| env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0); |
| |
| return (jint) readSize; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, |
| jshortArray javaAudioData, |
| jint offsetInShorts, jint sizeInShorts) { |
| |
| return (android_media_AudioRecord_readInByteArray(env, thiz, |
| (jbyteArray) javaAudioData, |
| offsetInShorts*2, sizeInShorts*2) |
| / 2); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, |
| jobject jBuffer, jint sizeInBytes) { |
| AudioRecord *lpRecorder = NULL; |
| //LOGV("Entering android_media_AudioRecord_readInBuffer"); |
| |
| // get the audio recorder from which we'll read new audio samples |
| lpRecorder = |
| (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| if(lpRecorder==NULL) |
| return 0; |
| |
| // direct buffer and direct access supported? |
| long capacity = env->GetDirectBufferCapacity(jBuffer); |
| if(capacity == -1) { |
| // buffer direct access is not supported |
| LOGE("Buffer direct access is not supported, can't record"); |
| return 0; |
| } |
| //LOGV("capacity = %ld", capacity); |
| jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); |
| if(nativeFromJavaBuf==NULL) { |
| LOGE("Buffer direct access is not supported, can't record"); |
| return 0; |
| } |
| |
| // read new data from the recorder |
| return (jint) lpRecorder->read(nativeFromJavaBuf, |
| capacity < sizeInBytes ? capacity : sizeInBytes); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, |
| jint markerPos) { |
| |
| AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( |
| thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| |
| if (lpRecorder) { |
| return |
| android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) ); |
| } else { |
| jniThrowException(env, "java/lang/IllegalStateException", |
| "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); |
| return AUDIORECORD_ERROR; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { |
| |
| AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( |
| thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| uint32_t markerPos = 0; |
| |
| if (lpRecorder) { |
| lpRecorder->getMarkerPosition(&markerPos); |
| return (jint)markerPos; |
| } else { |
| jniThrowException(env, "java/lang/IllegalStateException", |
| "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); |
| return AUDIORECORD_ERROR; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, |
| jint period) { |
| |
| AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( |
| thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| |
| if (lpRecorder) { |
| return |
| android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); |
| } else { |
| jniThrowException(env, "java/lang/IllegalStateException", |
| "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); |
| return AUDIORECORD_ERROR; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { |
| |
| AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( |
| thiz, javaAudioRecordFields.nativeRecorderInJavaObj); |
| uint32_t period = 0; |
| |
| if (lpRecorder) { |
| lpRecorder->getPositionUpdatePeriod(&period); |
| return (jint)period; |
| } else { |
| jniThrowException(env, "java/lang/IllegalStateException", |
| "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); |
| return AUDIORECORD_ERROR; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // returns the minimum required size for the successful creation of an AudioRecord instance. |
| // returns 0 if the parameter combination is not supported. |
| // return -1 if there was an error querying the buffer size. |
| static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, |
| jint sampleRateInHertz, jint nbChannels, jint audioFormat) { |
| |
| size_t inputBuffSize = 0; |
| LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat); |
| |
| status_t result = AudioSystem::getInputBufferSize( |
| sampleRateInHertz, |
| (audioFormat == javaAudioRecordFields.PCM16 ? |
| AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT), |
| nbChannels, &inputBuffSize); |
| switch(result) { |
| case(NO_ERROR): |
| if(inputBuffSize == 0) { |
| LOGV("Recording parameters are not supported: %dHz, %d channel(s), (java) format %d", |
| sampleRateInHertz, nbChannels, audioFormat); |
| return 0; |
| } else { |
| // the minimum buffer size is twice the hardware input buffer size |
| return 2*inputBuffSize; |
| } |
| break; |
| case(PERMISSION_DENIED): |
| default: |
| return -1; |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // ---------------------------------------------------------------------------- |
| static JNINativeMethod gMethods[] = { |
| // name, signature, funcPtr |
| {"native_start", "()I", (void *)android_media_AudioRecord_start}, |
| {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, |
| {"native_setup", "(Ljava/lang/Object;IIIII)I", |
| (void *)android_media_AudioRecord_setup}, |
| {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, |
| {"native_release", "()V", (void *)android_media_AudioRecord_release}, |
| {"native_read_in_byte_array", |
| "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, |
| {"native_read_in_short_array", |
| "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, |
| {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", |
| (void *)android_media_AudioRecord_readInDirectBuffer}, |
| {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, |
| {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, |
| {"native_set_pos_update_period", |
| "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, |
| {"native_get_pos_update_period", |
| "()I", (void *)android_media_AudioRecord_get_pos_update_period}, |
| {"native_get_min_buff_size", |
| "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, |
| }; |
| |
| // field names found in android/media/AudioRecord.java |
| #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" |
| #define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" |
| #define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" |
| #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" |
| #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" |
| |
| #define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" |
| |
| // ---------------------------------------------------------------------------- |
| |
| extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv, |
| jclass theClass, const char* className, const char* constName, int* constVal); |
| |
| // ---------------------------------------------------------------------------- |
| int register_android_media_AudioRecord(JNIEnv *env) |
| { |
| javaAudioRecordFields.audioRecordClass = NULL; |
| javaAudioRecordFields.postNativeEventInJava = NULL; |
| javaAudioRecordFields.nativeRecorderInJavaObj = NULL; |
| javaAudioRecordFields.nativeCallbackCookie = NULL; |
| |
| |
| // Get the AudioRecord class |
| javaAudioRecordFields.audioRecordClass = env->FindClass(kClassPathName); |
| if (javaAudioRecordFields.audioRecordClass == NULL) { |
| LOGE("Can't find %s", kClassPathName); |
| return -1; |
| } |
| |
| // Get the postEvent method |
| javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( |
| javaAudioRecordFields.audioRecordClass, |
| JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); |
| if (javaAudioRecordFields.postNativeEventInJava == NULL) { |
| LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); |
| return -1; |
| } |
| |
| // Get the variables |
| // mNativeRecorderInJavaObj |
| javaAudioRecordFields.nativeRecorderInJavaObj = |
| env->GetFieldID(javaAudioRecordFields.audioRecordClass, |
| JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I"); |
| if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { |
| LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); |
| return -1; |
| } |
| // mNativeCallbackCookie |
| javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( |
| javaAudioRecordFields.audioRecordClass, |
| JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I"); |
| if (javaAudioRecordFields.nativeCallbackCookie == NULL) { |
| LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); |
| return -1; |
| } |
| |
| // Get the format constants from the AudioFormat class |
| jclass audioFormatClass = NULL; |
| audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); |
| if (audioFormatClass == NULL) { |
| LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); |
| return -1; |
| } |
| if ( !android_media_getIntConstantFromClass(env, audioFormatClass, |
| JAVA_AUDIOFORMAT_CLASS_NAME, |
| JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16)) |
| || !android_media_getIntConstantFromClass(env, audioFormatClass, |
| JAVA_AUDIOFORMAT_CLASS_NAME, |
| JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) { |
| // error log performed in getIntConstantFromClass() |
| return -1; |
| } |
| |
| return AndroidRuntime::registerNativeMethods(env, |
| kClassPathName, gMethods, NELEM(gMethods)); |
| } |
| |
| // ---------------------------------------------------------------------------- |