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