blob: 1b6b24fff0927132ee2faebb3050279dfd80d6c3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 <stdio.h>
22#include <unistd.h>
23#include <fcntl.h>
24#include <math.h>
25
26#include "jni.h"
27#include "JNIHelp.h"
28#include "android_runtime/AndroidRuntime.h"
29
30#include "utils/Log.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031#include "media/AudioRecord.h"
Eric Laurenta553c252009-07-17 12:17:14 -070032#include "media/mediarecorder.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
34
35// ----------------------------------------------------------------------------
36
37using namespace android;
38
39// ----------------------------------------------------------------------------
40static const char* const kClassPathName = "android/media/AudioRecord";
41
42struct fields_t {
43 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 jmethodID postNativeEventInJava; //... event post callback method
45 int PCM16; //... format constants
46 int PCM8; //... format constants
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
48 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
49};
50static fields_t javaAudioRecordFields;
51
52struct audiorecord_callback_cookie {
53 jclass audioRecord_class;
54 jobject audioRecord_ref;
55 };
56
Dave Sparkse6335c92010-03-13 17:08:22 -080057Mutex sLock;
58
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059// ----------------------------------------------------------------------------
60
61#define AUDIORECORD_SUCCESS 0
62#define AUDIORECORD_ERROR -1
63#define AUDIORECORD_ERROR_BAD_VALUE -2
64#define AUDIORECORD_ERROR_INVALID_OPERATION -3
65#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070066#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070068#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
70
71jint android_media_translateRecorderErrorCode(int code) {
72 switch(code) {
73 case NO_ERROR:
74 return AUDIORECORD_SUCCESS;
75 case BAD_VALUE:
76 return AUDIORECORD_ERROR_BAD_VALUE;
77 case INVALID_OPERATION:
78 return AUDIORECORD_ERROR_INVALID_OPERATION;
79 default:
80 return AUDIORECORD_ERROR;
81 }
82}
83
84
85// ----------------------------------------------------------------------------
86static void recorderCallback(int event, void* user, void *info) {
87 if (event == AudioRecord::EVENT_MORE_DATA) {
88 // set size to 0 to signal we're not using the callback to read more data
89 AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
90 pBuff->size = 0;
91
92 } else if (event == AudioRecord::EVENT_MARKER) {
93 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
94 JNIEnv *env = AndroidRuntime::getJNIEnv();
95 if (user && env) {
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
106 } else if (event == AudioRecord::EVENT_NEW_POS) {
107 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
108 JNIEnv *env = AndroidRuntime::getJNIEnv();
109 if (user && env) {
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 }
120}
121
122
123// ----------------------------------------------------------------------------
124static int
125android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Eric Laurenta553c252009-07-17 12:17:14 -0700126 jint source, jint sampleRateInHertz, jint channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 jint audioFormat, jint buffSizeInBytes)
128{
129 //LOGV(">> Entering android_media_AudioRecord_setup");
Eric Laurenta553c252009-07-17 12:17:14 -0700130 //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
131 // sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
Eric Laurenta553c252009-07-17 12:17:14 -0700133 if (!AudioSystem::isInputChannel(channels)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
Eric Laurenta553c252009-07-17 12:17:14 -0700135 return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 }
Eric Laurenta553c252009-07-17 12:17:14 -0700137 uint32_t nbChannels = AudioSystem::popCount(channels);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
139 // compare the format against the Java constants
140 if ((audioFormat != javaAudioRecordFields.PCM16)
141 && (audioFormat != javaAudioRecordFields.PCM8)) {
142 LOGE("Error creating AudioRecord: unsupported audio format.");
143 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
144 }
145
146 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
147 int format = audioFormat==javaAudioRecordFields.PCM16 ?
148 AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
149
150 if (buffSizeInBytes == 0) {
151 LOGE("Error creating AudioRecord: frameCount is 0.");
152 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
153 }
154 int frameSize = nbChannels * bytesPerSample;
155 size_t frameCount = buffSizeInBytes / frameSize;
156
Eric Laurenta553c252009-07-17 12:17:14 -0700157 if (source >= AUDIO_SOURCE_LIST_END) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 LOGE("Error creating AudioRecord: unknown source.");
Eric Laurent4bc035a2009-05-22 09:18:15 -0700159 return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 }
Eric Laurent4bc035a2009-05-22 09:18:15 -0700161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 audiorecord_callback_cookie *lpCallbackData = NULL;
163 AudioRecord* lpRecorder = NULL;
164
165 // create an uninitialized AudioRecord object
166 lpRecorder = new AudioRecord();
167 if(lpRecorder == NULL) {
168 LOGE("Error creating AudioRecord instance.");
169 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
170 }
171
172 // create the callback information:
173 // this data will be passed with every AudioRecord callback
174 jclass clazz = env->GetObjectClass(thiz);
175 if (clazz == NULL) {
176 LOGE("Can't find %s when setting up callback.", kClassPathName);
177 goto native_track_failure;
178 }
179 lpCallbackData = new audiorecord_callback_cookie;
180 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
181 // we use a weak reference so the AudioRecord object can be garbage collected.
182 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
183
Eric Laurenta553c252009-07-17 12:17:14 -0700184 lpRecorder->set(source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 sampleRateInHertz,
186 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700187 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 frameCount,
189 0, // flags
190 recorderCallback,// callback_t
191 lpCallbackData,// void* user
192 0, // notificationFrames,
193 true); // threadCanCallJava)
194
195 if(lpRecorder->initCheck() != NO_ERROR) {
196 LOGE("Error creating AudioRecord instance: initialization check failed.");
197 goto native_init_failure;
198 }
199
200 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
201 // of the Java object
202 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
203
204 // save our newly created callback information in the "nativeCallbackCookie" field
205 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
206 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
207
208 return AUDIORECORD_SUCCESS;
209
210 // failure:
211native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700212 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
213 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 delete lpCallbackData;
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216native_track_failure:
217 delete lpRecorder;
218
219 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
220 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
221
222 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
223}
224
225
226
227// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800228static int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
230{
231 AudioRecord *lpRecorder =
232 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
233 if (lpRecorder == NULL ) {
234 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800235 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
237
Eric Laurent9cc489a2009-12-05 05:20:01 -0800238 return android_media_translateRecorderErrorCode(lpRecorder->start());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239}
240
241
242// ----------------------------------------------------------------------------
243static void
244android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
245{
246 AudioRecord *lpRecorder =
247 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
248 if (lpRecorder == NULL ) {
249 jniThrowException(env, "java/lang/IllegalStateException", NULL);
250 return;
251 }
252
253 lpRecorder->stop();
254 //LOGV("Called lpRecorder->stop()");
255}
256
257
258// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800259static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
260
261 // serialize access. Ugly, but functional.
262 Mutex::Autolock lock(&sLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 AudioRecord *lpRecorder =
264 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Dave Sparkse6335c92010-03-13 17:08:22 -0800265 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
266 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267
Dave Sparkse6335c92010-03-13 17:08:22 -0800268 // reset the native resources in the Java object so any attempt to access
269 // them after a call to release fails.
270 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
271 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
272
273 // delete the AudioRecord object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 if (lpRecorder) {
275 LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
276 lpRecorder->stop();
277 delete lpRecorder;
278 }
279
280 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 if (lpCookie) {
282 LOGV("deleting lpCookie: %x\n", (int)lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700283 env->DeleteGlobalRef(lpCookie->audioRecord_class);
284 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 delete lpCookie;
286 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287}
288
289
290// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800291static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
292 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293}
294
295
296// ----------------------------------------------------------------------------
297static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
298 jbyteArray javaAudioData,
299 jint offsetInBytes, jint sizeInBytes) {
300 jbyte* recordBuff = NULL;
301 AudioRecord *lpRecorder = NULL;
302
303 // get the audio recorder from which we'll read new audio samples
304 lpRecorder =
305 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
306 if (lpRecorder == NULL) {
307 LOGE("Unable to retrieve AudioRecord object, can't record");
308 return 0;
309 }
310
311 if (!javaAudioData) {
312 LOGE("Invalid Java array to store recorded audio, can't record");
313 return 0;
314 }
315
316 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800317 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
318 // a way that it becomes much more efficient. When doing so, we will have to prevent the
319 // AudioSystem callback to be called while in critical section (in case of media server
320 // process crash for instance)
321 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322
323 if (recordBuff == NULL) {
324 LOGE("Error retrieving destination for recorded audio data, can't record");
325 return 0;
326 }
327
328 // read the new audio data from the native AudioRecord object
329 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
330 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
331 sizeInBytes > (jint)recorderBuffSize ?
332 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800333 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 return (jint) readSize;
336}
337
338// ----------------------------------------------------------------------------
339static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
340 jshortArray javaAudioData,
341 jint offsetInShorts, jint sizeInShorts) {
342
343 return (android_media_AudioRecord_readInByteArray(env, thiz,
344 (jbyteArray) javaAudioData,
345 offsetInShorts*2, sizeInShorts*2)
346 / 2);
347}
348
349// ----------------------------------------------------------------------------
350static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
351 jobject jBuffer, jint sizeInBytes) {
352 AudioRecord *lpRecorder = NULL;
353 //LOGV("Entering android_media_AudioRecord_readInBuffer");
354
355 // get the audio recorder from which we'll read new audio samples
356 lpRecorder =
357 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
358 if(lpRecorder==NULL)
359 return 0;
360
361 // direct buffer and direct access supported?
362 long capacity = env->GetDirectBufferCapacity(jBuffer);
363 if(capacity == -1) {
364 // buffer direct access is not supported
365 LOGE("Buffer direct access is not supported, can't record");
366 return 0;
367 }
368 //LOGV("capacity = %ld", capacity);
369 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
370 if(nativeFromJavaBuf==NULL) {
371 LOGE("Buffer direct access is not supported, can't record");
372 return 0;
373 }
374
375 // read new data from the recorder
376 return (jint) lpRecorder->read(nativeFromJavaBuf,
377 capacity < sizeInBytes ? capacity : sizeInBytes);
378}
379
380
381// ----------------------------------------------------------------------------
382static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
383 jint markerPos) {
384
385 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
386 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
387
388 if (lpRecorder) {
389 return
390 android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
391 } else {
392 jniThrowException(env, "java/lang/IllegalStateException",
393 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
394 return AUDIORECORD_ERROR;
395 }
396}
397
398
399// ----------------------------------------------------------------------------
400static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
401
402 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
403 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
404 uint32_t markerPos = 0;
405
406 if (lpRecorder) {
407 lpRecorder->getMarkerPosition(&markerPos);
408 return (jint)markerPos;
409 } else {
410 jniThrowException(env, "java/lang/IllegalStateException",
411 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
412 return AUDIORECORD_ERROR;
413 }
414}
415
416
417// ----------------------------------------------------------------------------
418static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
419 jint period) {
420
421 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
422 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
423
424 if (lpRecorder) {
425 return
426 android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
427 } else {
428 jniThrowException(env, "java/lang/IllegalStateException",
429 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
430 return AUDIORECORD_ERROR;
431 }
432}
433
434
435// ----------------------------------------------------------------------------
436static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
437
438 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
439 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
440 uint32_t period = 0;
441
442 if (lpRecorder) {
443 lpRecorder->getPositionUpdatePeriod(&period);
444 return (jint)period;
445 } else {
446 jniThrowException(env, "java/lang/IllegalStateException",
447 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
448 return AUDIORECORD_ERROR;
449 }
450}
451
452
453// ----------------------------------------------------------------------------
454// returns the minimum required size for the successful creation of an AudioRecord instance.
455// returns 0 if the parameter combination is not supported.
456// return -1 if there was an error querying the buffer size.
457static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
458 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800461
462 int frameCount = 0;
463 status_t result = AudioRecord::getMinFrameCount(&frameCount,
464 sampleRateInHertz,
465 (audioFormat == javaAudioRecordFields.PCM16 ?
466 AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
467 nbChannels);
468
469 if (result == BAD_VALUE) {
470 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800472 if (result != NO_ERROR) {
473 return -1;
474 }
475 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476}
477
478
479// ----------------------------------------------------------------------------
480// ----------------------------------------------------------------------------
481static JNINativeMethod gMethods[] = {
482 // name, signature, funcPtr
Eric Laurent9cc489a2009-12-05 05:20:01 -0800483 {"native_start", "()I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
485 {"native_setup", "(Ljava/lang/Object;IIIII)I",
486 (void *)android_media_AudioRecord_setup},
487 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
488 {"native_release", "()V", (void *)android_media_AudioRecord_release},
489 {"native_read_in_byte_array",
490 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
491 {"native_read_in_short_array",
492 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
493 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
494 (void *)android_media_AudioRecord_readInDirectBuffer},
495 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
496 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
497 {"native_set_pos_update_period",
498 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
499 {"native_get_pos_update_period",
500 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
501 {"native_get_min_buff_size",
502 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
503};
504
505// field names found in android/media/AudioRecord.java
506#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
507#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
508#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
510#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
511
512#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
513
514// ----------------------------------------------------------------------------
515
516extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
517 jclass theClass, const char* className, const char* constName, int* constVal);
518
519// ----------------------------------------------------------------------------
520int register_android_media_AudioRecord(JNIEnv *env)
521{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 javaAudioRecordFields.postNativeEventInJava = NULL;
523 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
524 javaAudioRecordFields.nativeCallbackCookie = NULL;
525
526
527 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700528 jclass audioRecordClass = env->FindClass(kClassPathName);
529 if (audioRecordClass == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 LOGE("Can't find %s", kClassPathName);
531 return -1;
532 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 // Get the postEvent method
534 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700535 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
537 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
538 LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
539 return -1;
540 }
541
542 // Get the variables
543 // mNativeRecorderInJavaObj
544 javaAudioRecordFields.nativeRecorderInJavaObj =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700545 env->GetFieldID(audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
547 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
548 LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
549 return -1;
550 }
551 // mNativeCallbackCookie
552 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700553 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
555 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
556 LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
557 return -1;
558 }
559
560 // Get the format constants from the AudioFormat class
561 jclass audioFormatClass = NULL;
562 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
563 if (audioFormatClass == NULL) {
564 LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
565 return -1;
566 }
567 if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
568 JAVA_AUDIOFORMAT_CLASS_NAME,
569 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
570 || !android_media_getIntConstantFromClass(env, audioFormatClass,
571 JAVA_AUDIOFORMAT_CLASS_NAME,
572 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
573 // error log performed in getIntConstantFromClass()
574 return -1;
575 }
576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 return AndroidRuntime::registerNativeMethods(env,
578 kClassPathName, gMethods, NELEM(gMethods));
579}
580
581// ----------------------------------------------------------------------------