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 <nativehelper/JNIHelp.h> 24 #include "core_jni_helpers.h" 25 26 #include <utils/Log.h> 27 #include <media/AudioRecord.h> 28 29 #include <nativehelper/ScopedUtfChars.h> 30 31 #include "android_media_AudioFormat.h" 32 #include "android_media_AudioErrors.h" 33 #include "android_media_DeviceCallback.h" 34 35 // ---------------------------------------------------------------------------- 36 37 using namespace android; 38 39 // ---------------------------------------------------------------------------- 40 static const char* const kClassPathName = "android/media/AudioRecord"; 41 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; 42 43 struct audio_record_fields_t { 44 // these fields provide access from C++ to the... 45 jmethodID postNativeEventInJava; //... event post callback method 46 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object 47 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data 48 jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance 49 }; 50 struct audio_attributes_fields_t { 51 jfieldID fieldRecSource; // AudioAttributes.mSource 52 jfieldID fieldFlags; // AudioAttributes.mFlags 53 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags 54 }; 55 static audio_attributes_fields_t javaAudioAttrFields; 56 static audio_record_fields_t javaAudioRecordFields; 57 static struct { 58 jfieldID fieldFramePosition; // AudioTimestamp.framePosition 59 jfieldID fieldNanoTime; // AudioTimestamp.nanoTime 60 } javaAudioTimestampFields; 61 62 struct audiorecord_callback_cookie { 63 jclass audioRecord_class; 64 jobject audioRecord_ref; 65 bool busy; 66 Condition cond; 67 }; 68 69 static Mutex sLock; 70 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies; 71 72 // ---------------------------------------------------------------------------- 73 74 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16) 75 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17) 76 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18) 77 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19) 78 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20) 79 80 // ---------------------------------------------------------------------------- 81 static void recorderCallback(int event, void* user, void *info) { 82 83 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; 84 { 85 Mutex::Autolock l(sLock); 86 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) { 87 return; 88 } 89 callbackInfo->busy = true; 90 } 91 92 switch (event) { 93 case AudioRecord::EVENT_MARKER: { 94 JNIEnv *env = AndroidRuntime::getJNIEnv(); 95 if (user != NULL && env != NULL) { 96 env->CallStaticVoidMethod( 97 callbackInfo->audioRecord_class, 98 javaAudioRecordFields.postNativeEventInJava, 99 callbackInfo->audioRecord_ref, event, 0,0, NULL); 100 if (env->ExceptionCheck()) { 101 env->ExceptionDescribe(); 102 env->ExceptionClear(); 103 } 104 } 105 } break; 106 107 case AudioRecord::EVENT_NEW_POS: { 108 JNIEnv *env = AndroidRuntime::getJNIEnv(); 109 if (user != NULL && env != NULL) { 110 env->CallStaticVoidMethod( 111 callbackInfo->audioRecord_class, 112 javaAudioRecordFields.postNativeEventInJava, 113 callbackInfo->audioRecord_ref, event, 0,0, NULL); 114 if (env->ExceptionCheck()) { 115 env->ExceptionDescribe(); 116 env->ExceptionClear(); 117 } 118 } 119 } break; 120 } 121 122 { 123 Mutex::Autolock l(sLock); 124 callbackInfo->busy = false; 125 callbackInfo->cond.broadcast(); 126 } 127 } 128 129 static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz) 130 { 131 Mutex::Autolock l(sLock); 132 JNIDeviceCallback* const cb = 133 (JNIDeviceCallback*)env->GetLongField(thiz, 134 javaAudioRecordFields.nativeDeviceCallback); 135 return sp<JNIDeviceCallback>(cb); 136 } 137 138 static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env, 139 jobject thiz, 140 const sp<JNIDeviceCallback>& cb) 141 { 142 Mutex::Autolock l(sLock); 143 sp<JNIDeviceCallback> old = 144 (JNIDeviceCallback*)env->GetLongField(thiz, 145 javaAudioRecordFields.nativeDeviceCallback); 146 if (cb.get()) { 147 cb->incStrong((void*)setJniDeviceCallback); 148 } 149 if (old != 0) { 150 old->decStrong((void*)setJniDeviceCallback); 151 } 152 env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get()); 153 return old; 154 } 155 156 // ---------------------------------------------------------------------------- 157 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) 158 { 159 Mutex::Autolock l(sLock); 160 AudioRecord* const ar = 161 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 162 return sp<AudioRecord>(ar); 163 } 164 165 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar) 166 { 167 Mutex::Autolock l(sLock); 168 sp<AudioRecord> old = 169 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 170 if (ar.get()) { 171 ar->incStrong((void*)setAudioRecord); 172 } 173 if (old != 0) { 174 old->decStrong((void*)setAudioRecord); 175 } 176 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get()); 177 return old; 178 } 179 180 // ---------------------------------------------------------------------------- 181 static jint 182 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, 183 jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask, 184 jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName, 185 jlong nativeRecordInJavaObj) 186 { 187 //ALOGV(">> Entering android_media_AudioRecord_setup"); 188 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d " 189 // "nativeRecordInJavaObj=0x%llX", 190 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj); 191 audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask); 192 193 if (jSession == NULL) { 194 ALOGE("Error creating AudioRecord: invalid session ID pointer"); 195 return (jint) AUDIO_JAVA_ERROR; 196 } 197 198 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 199 if (nSession == NULL) { 200 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 201 return (jint) AUDIO_JAVA_ERROR; 202 } 203 audio_session_t sessionId = (audio_session_t) nSession[0]; 204 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 205 nSession = NULL; 206 207 audio_attributes_t *paa = NULL; 208 sp<AudioRecord> lpRecorder = 0; 209 audiorecord_callback_cookie *lpCallbackData = NULL; 210 211 jclass clazz = env->GetObjectClass(thiz); 212 if (clazz == NULL) { 213 ALOGE("Can't find %s when setting up callback.", kClassPathName); 214 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 215 } 216 217 // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one. 218 if (nativeRecordInJavaObj == 0) { 219 if (jaa == 0) { 220 ALOGE("Error creating AudioRecord: invalid audio attributes"); 221 return (jint) AUDIO_JAVA_ERROR; 222 } 223 224 if (jSampleRate == 0) { 225 ALOGE("Error creating AudioRecord: invalid sample rates"); 226 return (jint) AUDIO_JAVA_ERROR; 227 } 228 jint elements[1]; 229 env->GetIntArrayRegion(jSampleRate, 0, 1, elements); 230 int sampleRateInHertz = elements[0]; 231 232 // channel index mask takes priority over channel position masks. 233 if (channelIndexMask) { 234 // Java channel index masks need the representation bits set. 235 localChanMask = audio_channel_mask_from_representation_and_bits( 236 AUDIO_CHANNEL_REPRESENTATION_INDEX, 237 channelIndexMask); 238 } 239 // Java channel position masks map directly to the native definition 240 241 if (!audio_is_input_channel(localChanMask)) { 242 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask); 243 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; 244 } 245 uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask); 246 247 // compare the format against the Java constants 248 audio_format_t format = audioFormatToNative(audioFormat); 249 if (format == AUDIO_FORMAT_INVALID) { 250 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat); 251 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; 252 } 253 254 size_t bytesPerSample = audio_bytes_per_sample(format); 255 256 if (buffSizeInBytes == 0) { 257 ALOGE("Error creating AudioRecord: frameCount is 0."); 258 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; 259 } 260 size_t frameSize = channelCount * bytesPerSample; 261 size_t frameCount = buffSizeInBytes / frameSize; 262 263 ScopedUtfChars opPackageNameStr(env, opPackageName); 264 265 // create an uninitialized AudioRecord object 266 lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str())); 267 268 // read the AudioAttributes values 269 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); 270 const jstring jtags = 271 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); 272 const char* tags = env->GetStringUTFChars(jtags, NULL); 273 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it 274 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); 275 env->ReleaseStringUTFChars(jtags, tags); 276 paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); 277 paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); 278 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); 279 280 audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; 281 if (paa->flags & AUDIO_FLAG_HW_HOTWORD) { 282 flags = AUDIO_INPUT_FLAG_HW_HOTWORD; 283 } 284 // create the callback information: 285 // this data will be passed with every AudioRecord callback 286 lpCallbackData = new audiorecord_callback_cookie; 287 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); 288 // we use a weak reference so the AudioRecord object can be garbage collected. 289 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); 290 lpCallbackData->busy = false; 291 292 const status_t status = lpRecorder->set(paa->source, 293 sampleRateInHertz, 294 format, // word length, PCM 295 localChanMask, 296 frameCount, 297 recorderCallback,// callback_t 298 lpCallbackData,// void* user 299 0, // notificationFrames, 300 true, // threadCanCallJava 301 sessionId, 302 AudioRecord::TRANSFER_DEFAULT, 303 flags, 304 -1, -1, // default uid, pid 305 paa); 306 307 if (status != NO_ERROR) { 308 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", 309 status); 310 goto native_init_failure; 311 } 312 } else { // end if nativeRecordInJavaObj == 0) 313 lpRecorder = (AudioRecord*)nativeRecordInJavaObj; 314 // TODO: We need to find out which members of the Java AudioRecord might need to be 315 // initialized from the Native AudioRecord 316 // these are directly returned from getters: 317 // mSampleRate 318 // mRecordSource 319 // mAudioFormat 320 // mChannelMask 321 // mChannelCount 322 // mState (?) 323 // mRecordingState (?) 324 // mPreferredDevice 325 326 // create the callback information: 327 // this data will be passed with every AudioRecord callback 328 lpCallbackData = new audiorecord_callback_cookie; 329 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); 330 // we use a weak reference so the AudioRecord object can be garbage collected. 331 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); 332 lpCallbackData->busy = false; 333 } 334 335 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 336 if (nSession == NULL) { 337 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 338 goto native_init_failure; 339 } 340 // read the audio session ID back from AudioRecord in case a new session was created during set() 341 nSession[0] = lpRecorder->getSessionId(); 342 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 343 nSession = NULL; 344 345 { 346 const jint elements[1] = { (jint) lpRecorder->getSampleRate() }; 347 env->SetIntArrayRegion(jSampleRate, 0, 1, elements); 348 } 349 350 { // scope for the lock 351 Mutex::Autolock l(sLock); 352 sAudioRecordCallBackCookies.add(lpCallbackData); 353 } 354 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field 355 // of the Java object 356 setAudioRecord(env, thiz, lpRecorder); 357 358 // save our newly created callback information in the "nativeCallbackCookie" field 359 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() 360 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData); 361 362 return (jint) AUDIO_JAVA_SUCCESS; 363 364 // failure: 365 native_init_failure: 366 env->DeleteGlobalRef(lpCallbackData->audioRecord_class); 367 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); 368 delete lpCallbackData; 369 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 370 371 // lpRecorder goes out of scope, so reference count drops to zero 372 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 373 } 374 375 376 377 // ---------------------------------------------------------------------------- 378 static jint 379 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession) 380 { 381 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 382 if (lpRecorder == NULL ) { 383 jniThrowException(env, "java/lang/IllegalStateException", NULL); 384 return (jint) AUDIO_JAVA_ERROR; 385 } 386 387 return nativeToJavaStatus( 388 lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession)); 389 } 390 391 392 // ---------------------------------------------------------------------------- 393 static void 394 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) 395 { 396 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 397 if (lpRecorder == NULL ) { 398 jniThrowException(env, "java/lang/IllegalStateException", NULL); 399 return; 400 } 401 402 lpRecorder->stop(); 403 //ALOGV("Called lpRecorder->stop()"); 404 } 405 406 407 // ---------------------------------------------------------------------------- 408 409 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 410 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { 411 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); 412 if (lpRecorder == NULL) { 413 return; 414 } 415 ALOGV("About to delete lpRecorder: %p", lpRecorder.get()); 416 lpRecorder->stop(); 417 418 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField( 419 thiz, javaAudioRecordFields.nativeCallbackCookie); 420 421 // reset the native resources in the Java object so any attempt to access 422 // them after a call to release fails. 423 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 424 425 // delete the callback information 426 if (lpCookie) { 427 Mutex::Autolock l(sLock); 428 ALOGV("deleting lpCookie: %p", lpCookie); 429 while (lpCookie->busy) { 430 if (lpCookie->cond.waitRelative(sLock, 431 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 432 NO_ERROR) { 433 break; 434 } 435 } 436 sAudioRecordCallBackCookies.remove(lpCookie); 437 env->DeleteGlobalRef(lpCookie->audioRecord_class); 438 env->DeleteGlobalRef(lpCookie->audioRecord_ref); 439 delete lpCookie; 440 } 441 } 442 443 444 // ---------------------------------------------------------------------------- 445 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { 446 android_media_AudioRecord_release(env, thiz); 447 } 448 449 // overloaded JNI array helper functions 450 static inline 451 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { 452 return env->GetByteArrayElements(array, isCopy); 453 } 454 455 static inline 456 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { 457 env->ReleaseByteArrayElements(array, elems, mode); 458 } 459 460 static inline 461 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { 462 return env->GetShortArrayElements(array, isCopy); 463 } 464 465 static inline 466 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { 467 env->ReleaseShortArrayElements(array, elems, mode); 468 } 469 470 static inline 471 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { 472 return env->GetFloatArrayElements(array, isCopy); 473 } 474 475 static inline 476 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { 477 env->ReleaseFloatArrayElements(array, elems, mode); 478 } 479 480 static inline 481 jint interpretReadSizeError(ssize_t readSize) { 482 if (readSize == WOULD_BLOCK) { 483 return (jint)0; 484 } else if (readSize == NO_INIT) { 485 return AUDIO_JAVA_DEAD_OBJECT; 486 } else { 487 ALOGE("Error %zd during AudioRecord native read", readSize); 488 return nativeToJavaStatus(readSize); 489 } 490 } 491 492 template <typename T> 493 static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz, 494 T javaAudioData, 495 jint offsetInSamples, jint sizeInSamples, 496 jboolean isReadBlocking) { 497 // get the audio recorder from which we'll read new audio samples 498 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 499 if (lpRecorder == NULL) { 500 ALOGE("Unable to retrieve AudioRecord object"); 501 return (jint)AUDIO_JAVA_INVALID_OPERATION; 502 } 503 504 if (javaAudioData == NULL) { 505 ALOGE("Invalid Java array to store recorded audio"); 506 return (jint)AUDIO_JAVA_BAD_VALUE; 507 } 508 509 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 510 // a way that it becomes much more efficient. When doing so, we will have to prevent the 511 // AudioSystem callback to be called while in critical section (in case of media server 512 // process crash for instance) 513 514 // get the pointer to where we'll record the audio 515 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL); 516 if (recordBuff == NULL) { 517 ALOGE("Error retrieving destination for recorded audio data"); 518 return (jint)AUDIO_JAVA_BAD_VALUE; 519 } 520 521 // read the new audio data from the native AudioRecord object 522 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff); 523 ssize_t readSize = lpRecorder->read( 524 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */); 525 526 envReleaseArrayElements(env, javaAudioData, recordBuff, 0); 527 528 if (readSize < 0) { 529 return interpretReadSizeError(readSize); 530 } 531 return (jint)(readSize / sizeof(*recordBuff)); 532 } 533 534 // ---------------------------------------------------------------------------- 535 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 536 jobject jBuffer, jint sizeInBytes, 537 jboolean isReadBlocking) { 538 // get the audio recorder from which we'll read new audio samples 539 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 540 if (lpRecorder==NULL) 541 return (jint)AUDIO_JAVA_INVALID_OPERATION; 542 543 // direct buffer and direct access supported? 544 long capacity = env->GetDirectBufferCapacity(jBuffer); 545 if (capacity == -1) { 546 // buffer direct access is not supported 547 ALOGE("Buffer direct access is not supported, can't record"); 548 return (jint)AUDIO_JAVA_BAD_VALUE; 549 } 550 //ALOGV("capacity = %ld", capacity); 551 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 552 if (nativeFromJavaBuf==NULL) { 553 ALOGE("Buffer direct access is not supported, can't record"); 554 return (jint)AUDIO_JAVA_BAD_VALUE; 555 } 556 557 // read new data from the recorder 558 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, 559 capacity < sizeInBytes ? capacity : sizeInBytes, 560 isReadBlocking == JNI_TRUE /* blocking */); 561 if (readSize < 0) { 562 return interpretReadSizeError(readSize); 563 } 564 return (jint)readSize; 565 } 566 567 // ---------------------------------------------------------------------------- 568 static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) { 569 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 570 if (lpRecorder == NULL) { 571 jniThrowException(env, "java/lang/IllegalStateException", 572 "Unable to retrieve AudioRecord pointer for frameCount()"); 573 return (jint)AUDIO_JAVA_ERROR; 574 } 575 return lpRecorder->frameCount(); 576 } 577 578 // ---------------------------------------------------------------------------- 579 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 580 jint markerPos) { 581 582 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 583 if (lpRecorder == NULL) { 584 jniThrowException(env, "java/lang/IllegalStateException", 585 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 586 return (jint)AUDIO_JAVA_ERROR; 587 } 588 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) ); 589 } 590 591 592 // ---------------------------------------------------------------------------- 593 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 594 595 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 596 uint32_t markerPos = 0; 597 598 if (lpRecorder == NULL) { 599 jniThrowException(env, "java/lang/IllegalStateException", 600 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 601 return (jint)AUDIO_JAVA_ERROR; 602 } 603 lpRecorder->getMarkerPosition(&markerPos); 604 return (jint)markerPos; 605 } 606 607 608 // ---------------------------------------------------------------------------- 609 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 610 jint period) { 611 612 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 613 614 if (lpRecorder == NULL) { 615 jniThrowException(env, "java/lang/IllegalStateException", 616 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 617 return (jint)AUDIO_JAVA_ERROR; 618 } 619 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) ); 620 } 621 622 623 // ---------------------------------------------------------------------------- 624 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 625 626 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 627 uint32_t period = 0; 628 629 if (lpRecorder == NULL) { 630 jniThrowException(env, "java/lang/IllegalStateException", 631 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 632 return (jint)AUDIO_JAVA_ERROR; 633 } 634 lpRecorder->getPositionUpdatePeriod(&period); 635 return (jint)period; 636 } 637 638 639 // ---------------------------------------------------------------------------- 640 // returns the minimum required size for the successful creation of an AudioRecord instance. 641 // returns 0 if the parameter combination is not supported. 642 // return -1 if there was an error querying the buffer size. 643 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 644 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 645 646 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 647 sampleRateInHertz, channelCount, audioFormat); 648 649 size_t frameCount = 0; 650 audio_format_t format = audioFormatToNative(audioFormat); 651 status_t result = AudioRecord::getMinFrameCount(&frameCount, 652 sampleRateInHertz, 653 format, 654 audio_channel_in_mask_from_count(channelCount)); 655 656 if (result == BAD_VALUE) { 657 return 0; 658 } 659 if (result != NO_ERROR) { 660 return -1; 661 } 662 return frameCount * channelCount * audio_bytes_per_sample(format); 663 } 664 665 static jboolean android_media_AudioRecord_setInputDevice( 666 JNIEnv *env, jobject thiz, jint device_id) { 667 668 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 669 if (lpRecorder == 0) { 670 return false; 671 } 672 return lpRecorder->setInputDevice(device_id) == NO_ERROR; 673 } 674 675 static jint android_media_AudioRecord_getRoutedDeviceId( 676 JNIEnv *env, jobject thiz) { 677 678 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 679 if (lpRecorder == 0) { 680 return 0; 681 } 682 return (jint)lpRecorder->getRoutedDeviceId(); 683 } 684 685 static void android_media_AudioRecord_enableDeviceCallback( 686 JNIEnv *env, jobject thiz) { 687 688 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 689 if (lpRecorder == 0) { 690 return; 691 } 692 sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz); 693 if (cb != 0) { 694 return; 695 } 696 audiorecord_callback_cookie *cookie = 697 (audiorecord_callback_cookie *)env->GetLongField(thiz, 698 javaAudioRecordFields.nativeCallbackCookie); 699 if (cookie == NULL) { 700 return; 701 } 702 703 cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref, 704 javaAudioRecordFields.postNativeEventInJava); 705 status_t status = lpRecorder->addAudioDeviceCallback(cb); 706 if (status == NO_ERROR) { 707 setJniDeviceCallback(env, thiz, cb); 708 } 709 } 710 711 static void android_media_AudioRecord_disableDeviceCallback( 712 JNIEnv *env, jobject thiz) { 713 714 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 715 if (lpRecorder == 0) { 716 return; 717 } 718 sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0); 719 if (cb != 0) { 720 lpRecorder->removeAudioDeviceCallback(cb); 721 } 722 } 723 724 // ---------------------------------------------------------------------------- 725 static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz, 726 jobject timestamp, jint timebase) { 727 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 728 729 if (lpRecorder == NULL) { 730 jniThrowException(env, "java/lang/IllegalStateException", 731 "Unable to retrieve AudioRecord pointer for getTimestamp()"); 732 return (jint)AUDIO_JAVA_ERROR; 733 } 734 735 ExtendedTimestamp ts; 736 jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts)); 737 738 if (status == AUDIO_JAVA_SUCCESS) { 739 // set the data 740 int64_t position, time; 741 742 status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase)); 743 if (status == AUDIO_JAVA_SUCCESS) { 744 env->SetLongField( 745 timestamp, javaAudioTimestampFields.fieldFramePosition, position); 746 env->SetLongField( 747 timestamp, javaAudioTimestampFields.fieldNanoTime, time); 748 } 749 } 750 return status; 751 } 752 753 // ---------------------------------------------------------------------------- 754 // ---------------------------------------------------------------------------- 755 static const JNINativeMethod gMethods[] = { 756 // name, signature, funcPtr 757 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 758 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 759 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I", 760 (void *)android_media_AudioRecord_setup}, 761 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 762 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 763 {"native_read_in_byte_array", 764 "([BIIZ)I", 765 (void *)android_media_AudioRecord_readInArray<jbyteArray>}, 766 {"native_read_in_short_array", 767 "([SIIZ)I", 768 (void *)android_media_AudioRecord_readInArray<jshortArray>}, 769 {"native_read_in_float_array", 770 "([FIIZ)I", 771 (void *)android_media_AudioRecord_readInArray<jfloatArray>}, 772 {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I", 773 (void *)android_media_AudioRecord_readInDirectBuffer}, 774 {"native_get_buffer_size_in_frames", 775 "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames}, 776 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 777 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 778 {"native_set_pos_update_period", 779 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 780 {"native_get_pos_update_period", 781 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 782 {"native_get_min_buff_size", 783 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 784 {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice}, 785 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId}, 786 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback}, 787 {"native_disableDeviceCallback", "()V", 788 (void *)android_media_AudioRecord_disableDeviceCallback}, 789 {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I", 790 (void *)android_media_AudioRecord_get_timestamp}, 791 }; 792 793 // field names found in android/media/AudioRecord.java 794 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 795 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 796 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 797 #define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback" 798 799 // ---------------------------------------------------------------------------- 800 int register_android_media_AudioRecord(JNIEnv *env) 801 { 802 javaAudioRecordFields.postNativeEventInJava = NULL; 803 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 804 javaAudioRecordFields.nativeCallbackCookie = NULL; 805 javaAudioRecordFields.nativeDeviceCallback = NULL; 806 807 808 // Get the AudioRecord class 809 jclass audioRecordClass = FindClassOrDie(env, kClassPathName); 810 // Get the postEvent method 811 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, 812 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME, 813 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 814 815 // Get the variables 816 // mNativeRecorderInJavaObj 817 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env, 818 audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J"); 819 // mNativeCallbackCookie 820 javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env, 821 audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J"); 822 823 javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env, 824 audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J"); 825 826 // Get the AudioAttributes class and fields 827 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); 828 javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I"); 829 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); 830 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, 831 audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); 832 833 // Get the RecordTimestamp class and fields 834 jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp"); 835 javaAudioTimestampFields.fieldFramePosition = 836 GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J"); 837 javaAudioTimestampFields.fieldNanoTime = 838 GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J"); 839 840 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 841 } 842 843 // ---------------------------------------------------------------------------- 844