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