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 <android_runtime/AndroidRuntime.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
34 using namespace android;
35
36 // ----------------------------------------------------------------------------
37 static const char* const kClassPathName = "android/media/AudioRecord";
38 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
39
40 struct 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 };
46 struct audio_attributes_fields_t {
47 jfieldID fieldRecSource; // AudioAttributes.mSource
48 jfieldID fieldFlags; // AudioAttributes.mFlags
49 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
50 };
51 static audio_attributes_fields_t javaAudioAttrFields;
52 static audio_record_fields_t javaAudioRecordFields;
53
54 struct audiorecord_callback_cookie {
55 jclass audioRecord_class;
56 jobject audioRecord_ref;
57 bool busy;
58 Condition cond;
59 };
60
61 static Mutex sLock;
62 static 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 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)73 static 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 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)122 static 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
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)130 static 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 // ----------------------------------------------------------------------------
146 static jint
android_media_AudioRecord_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jint sampleRateInHertz,jint channelMask,jint audioFormat,jint buffSizeInBytes,jintArray jSession)147 android_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:
276 native_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 // ----------------------------------------------------------------------------
288 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)289 android_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 // ----------------------------------------------------------------------------
303 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)304 android_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
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)320 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)355 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
356 android_media_AudioRecord_release(env, thiz);
357 }
358
359
360 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInByteArray(JNIEnv * env,jobject thiz,jbyteArray javaAudioData,jint offsetInBytes,jint sizeInBytes)361 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
362 jbyteArray javaAudioData,
363 jint offsetInBytes, jint sizeInBytes) {
364 jbyte* recordBuff = NULL;
365 // get the audio recorder from which we'll read new audio samples
366 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
367 if (lpRecorder == NULL) {
368 ALOGE("Unable to retrieve AudioRecord object, can't record");
369 return 0;
370 }
371
372 if (!javaAudioData) {
373 ALOGE("Invalid Java array to store recorded audio, can't record");
374 return 0;
375 }
376
377 // get the pointer to where we'll record the audio
378 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
379 // a way that it becomes much more efficient. When doing so, we will have to prevent the
380 // AudioSystem callback to be called while in critical section (in case of media server
381 // process crash for instance)
382 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
383
384 if (recordBuff == NULL) {
385 ALOGE("Error retrieving destination for recorded audio data, can't record");
386 return 0;
387 }
388
389 // read the new audio data from the native AudioRecord object
390 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
391 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
392 sizeInBytes > (jint)recorderBuffSize ?
393 (jint)recorderBuffSize : sizeInBytes );
394 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
395
396 if (readSize < 0) {
397 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
398 }
399 return (jint) readSize;
400 }
401
402 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInShortArray(JNIEnv * env,jobject thiz,jshortArray javaAudioData,jint offsetInShorts,jint sizeInShorts)403 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
404 jshortArray javaAudioData,
405 jint offsetInShorts, jint sizeInShorts) {
406
407 jshort* recordBuff = NULL;
408 // get the audio recorder from which we'll read new audio samples
409 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
410 if (lpRecorder == NULL) {
411 ALOGE("Unable to retrieve AudioRecord object, can't record");
412 return 0;
413 }
414
415 if (!javaAudioData) {
416 ALOGE("Invalid Java array to store recorded audio, can't record");
417 return 0;
418 }
419
420 // get the pointer to where we'll record the audio
421 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
422 // a way that it becomes much more efficient. When doing so, we will have to prevent the
423 // AudioSystem callback to be called while in critical section (in case of media server
424 // process crash for instance)
425 recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
426
427 if (recordBuff == NULL) {
428 ALOGE("Error retrieving destination for recorded audio data, can't record");
429 return 0;
430 }
431
432 // read the new audio data from the native AudioRecord object
433 const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
434 const size_t sizeInBytes = sizeInShorts * sizeof(short);
435 ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts,
436 sizeInBytes > recorderBuffSize ?
437 recorderBuffSize : sizeInBytes);
438
439 env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
440
441 if (readSize < 0) {
442 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
443 } else {
444 readSize /= sizeof(short);
445 }
446 return (jint) readSize;
447 }
448
449 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes)450 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
451 jobject jBuffer, jint sizeInBytes) {
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 0;
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 0;
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 0;
469 }
470
471 // read new data from the recorder
472 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
473 capacity < sizeInBytes ? capacity : sizeInBytes);
474 if (readSize < 0) {
475 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
476 }
477 return (jint)readSize;
478 }
479
480
481 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)482 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
483 jint markerPos) {
484
485 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
486 if (lpRecorder == NULL) {
487 jniThrowException(env, "java/lang/IllegalStateException",
488 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
489 return (jint)AUDIO_JAVA_ERROR;
490 }
491 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
492 }
493
494
495 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)496 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
497
498 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
499 uint32_t markerPos = 0;
500
501 if (lpRecorder == NULL) {
502 jniThrowException(env, "java/lang/IllegalStateException",
503 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
504 return (jint)AUDIO_JAVA_ERROR;
505 }
506 lpRecorder->getMarkerPosition(&markerPos);
507 return (jint)markerPos;
508 }
509
510
511 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)512 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
513 jint period) {
514
515 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
516
517 if (lpRecorder == NULL) {
518 jniThrowException(env, "java/lang/IllegalStateException",
519 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
520 return (jint)AUDIO_JAVA_ERROR;
521 }
522 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
523 }
524
525
526 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)527 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
528
529 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
530 uint32_t period = 0;
531
532 if (lpRecorder == NULL) {
533 jniThrowException(env, "java/lang/IllegalStateException",
534 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
535 return (jint)AUDIO_JAVA_ERROR;
536 }
537 lpRecorder->getPositionUpdatePeriod(&period);
538 return (jint)period;
539 }
540
541
542 // ----------------------------------------------------------------------------
543 // returns the minimum required size for the successful creation of an AudioRecord instance.
544 // returns 0 if the parameter combination is not supported.
545 // return -1 if there was an error querying the buffer size.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)546 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
547 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
548
549 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
550 sampleRateInHertz, channelCount, audioFormat);
551
552 size_t frameCount = 0;
553 audio_format_t format = audioFormatToNative(audioFormat);
554 status_t result = AudioRecord::getMinFrameCount(&frameCount,
555 sampleRateInHertz,
556 format,
557 audio_channel_in_mask_from_count(channelCount));
558
559 if (result == BAD_VALUE) {
560 return 0;
561 }
562 if (result != NO_ERROR) {
563 return -1;
564 }
565 return frameCount * channelCount * audio_bytes_per_sample(format);
566 }
567
568
569 // ----------------------------------------------------------------------------
570 // ----------------------------------------------------------------------------
571 static JNINativeMethod gMethods[] = {
572 // name, signature, funcPtr
573 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
574 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
575 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I",
576 (void *)android_media_AudioRecord_setup},
577 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
578 {"native_release", "()V", (void *)android_media_AudioRecord_release},
579 {"native_read_in_byte_array",
580 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
581 {"native_read_in_short_array",
582 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
583 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
584 (void *)android_media_AudioRecord_readInDirectBuffer},
585 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
586 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
587 {"native_set_pos_update_period",
588 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
589 {"native_get_pos_update_period",
590 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
591 {"native_get_min_buff_size",
592 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
593 };
594
595 // field names found in android/media/AudioRecord.java
596 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
597 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
598 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
599
600 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)601 int register_android_media_AudioRecord(JNIEnv *env)
602 {
603 javaAudioRecordFields.postNativeEventInJava = NULL;
604 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
605 javaAudioRecordFields.nativeCallbackCookie = NULL;
606
607
608 // Get the AudioRecord class
609 jclass audioRecordClass = env->FindClass(kClassPathName);
610 if (audioRecordClass == NULL) {
611 ALOGE("Can't find %s", kClassPathName);
612 return -1;
613 }
614 // Get the postEvent method
615 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
616 audioRecordClass,
617 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
618 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
619 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
620 return -1;
621 }
622
623 // Get the variables
624 // mNativeRecorderInJavaObj
625 javaAudioRecordFields.nativeRecorderInJavaObj =
626 env->GetFieldID(audioRecordClass,
627 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
628 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
629 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
630 return -1;
631 }
632 // mNativeCallbackCookie
633 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
634 audioRecordClass,
635 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
636 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
637 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
638 return -1;
639 }
640
641 // Get the AudioAttributes class and fields
642 jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
643 if (audioAttrClass == NULL) {
644 ALOGE("Can't find %s", kAudioAttributesClassPathName);
645 return -1;
646 }
647 jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
648 javaAudioAttrFields.fieldRecSource = env->GetFieldID(audioAttributesClassRef, "mSource", "I");
649 javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
650 javaAudioAttrFields.fieldFormattedTags =
651 env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
652 env->DeleteGlobalRef(audioAttributesClassRef);
653 if (javaAudioAttrFields.fieldRecSource == NULL
654 || javaAudioAttrFields.fieldFlags == NULL
655 || javaAudioAttrFields.fieldFormattedTags == NULL) {
656 ALOGE("Can't initialize AudioAttributes fields");
657 return -1;
658 }
659
660 return AndroidRuntime::registerNativeMethods(env,
661 kClassPathName, gMethods, NELEM(gMethods));
662 }
663
664 // ----------------------------------------------------------------------------
665