blob: b6619ab3d06f261ad4596f8a65167a72932760da [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...
44 jclass audioRecordClass; //... AudioRecord class
45 jmethodID postNativeEventInJava; //... event post callback method
46 int PCM16; //... format constants
47 int PCM8; //... format constants
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
49 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
50};
51static fields_t javaAudioRecordFields;
52
53struct audiorecord_callback_cookie {
54 jclass audioRecord_class;
55 jobject audioRecord_ref;
56 };
57
Dave Sparkse6335c92010-03-13 17:08:22 -080058Mutex sLock;
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060// ----------------------------------------------------------------------------
61
62#define AUDIORECORD_SUCCESS 0
63#define AUDIORECORD_ERROR -1
64#define AUDIORECORD_ERROR_BAD_VALUE -2
65#define AUDIORECORD_ERROR_INVALID_OPERATION -3
66#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070067#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070069#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
71
72jint android_media_translateRecorderErrorCode(int code) {
73 switch(code) {
74 case NO_ERROR:
75 return AUDIORECORD_SUCCESS;
76 case BAD_VALUE:
77 return AUDIORECORD_ERROR_BAD_VALUE;
78 case INVALID_OPERATION:
79 return AUDIORECORD_ERROR_INVALID_OPERATION;
80 default:
81 return AUDIORECORD_ERROR;
82 }
83}
84
85
86// ----------------------------------------------------------------------------
87static void recorderCallback(int event, void* user, void *info) {
88 if (event == AudioRecord::EVENT_MORE_DATA) {
89 // set size to 0 to signal we're not using the callback to read more data
90 AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
91 pBuff->size = 0;
92
93 } else if (event == AudioRecord::EVENT_MARKER) {
94 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
95 JNIEnv *env = AndroidRuntime::getJNIEnv();
96 if (user && env) {
97 env->CallStaticVoidMethod(
98 callbackInfo->audioRecord_class,
99 javaAudioRecordFields.postNativeEventInJava,
100 callbackInfo->audioRecord_ref, event, 0,0, NULL);
101 if (env->ExceptionCheck()) {
102 env->ExceptionDescribe();
103 env->ExceptionClear();
104 }
105 }
106
107 } else if (event == AudioRecord::EVENT_NEW_POS) {
108 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
109 JNIEnv *env = AndroidRuntime::getJNIEnv();
110 if (user && env) {
111 env->CallStaticVoidMethod(
112 callbackInfo->audioRecord_class,
113 javaAudioRecordFields.postNativeEventInJava,
114 callbackInfo->audioRecord_ref, event, 0,0, NULL);
115 if (env->ExceptionCheck()) {
116 env->ExceptionDescribe();
117 env->ExceptionClear();
118 }
119 }
120 }
121}
122
123
124// ----------------------------------------------------------------------------
125static int
126android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Eric Laurenta553c252009-07-17 12:17:14 -0700127 jint source, jint sampleRateInHertz, jint channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 jint audioFormat, jint buffSizeInBytes)
129{
130 //LOGV(">> Entering android_media_AudioRecord_setup");
Eric Laurenta553c252009-07-17 12:17:14 -0700131 //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
132 // sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
Eric Laurenta553c252009-07-17 12:17:14 -0700134 if (!AudioSystem::isInputChannel(channels)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
Eric Laurenta553c252009-07-17 12:17:14 -0700136 return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 }
Eric Laurenta553c252009-07-17 12:17:14 -0700138 uint32_t nbChannels = AudioSystem::popCount(channels);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
140 // compare the format against the Java constants
141 if ((audioFormat != javaAudioRecordFields.PCM16)
142 && (audioFormat != javaAudioRecordFields.PCM8)) {
143 LOGE("Error creating AudioRecord: unsupported audio format.");
144 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
145 }
146
147 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
148 int format = audioFormat==javaAudioRecordFields.PCM16 ?
149 AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
150
151 if (buffSizeInBytes == 0) {
152 LOGE("Error creating AudioRecord: frameCount is 0.");
153 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
154 }
155 int frameSize = nbChannels * bytesPerSample;
156 size_t frameCount = buffSizeInBytes / frameSize;
157
Eric Laurenta553c252009-07-17 12:17:14 -0700158 if (source >= AUDIO_SOURCE_LIST_END) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 LOGE("Error creating AudioRecord: unknown source.");
Eric Laurent4bc035a2009-05-22 09:18:15 -0700160 return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 }
Eric Laurent4bc035a2009-05-22 09:18:15 -0700162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 audiorecord_callback_cookie *lpCallbackData = NULL;
164 AudioRecord* lpRecorder = NULL;
165
166 // create an uninitialized AudioRecord object
167 lpRecorder = new AudioRecord();
168 if(lpRecorder == NULL) {
169 LOGE("Error creating AudioRecord instance.");
170 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
171 }
172
173 // create the callback information:
174 // this data will be passed with every AudioRecord callback
175 jclass clazz = env->GetObjectClass(thiz);
176 if (clazz == NULL) {
177 LOGE("Can't find %s when setting up callback.", kClassPathName);
178 goto native_track_failure;
179 }
180 lpCallbackData = new audiorecord_callback_cookie;
181 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
182 // we use a weak reference so the AudioRecord object can be garbage collected.
183 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
184
Eric Laurenta553c252009-07-17 12:17:14 -0700185 lpRecorder->set(source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 sampleRateInHertz,
187 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700188 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 frameCount,
190 0, // flags
191 recorderCallback,// callback_t
192 lpCallbackData,// void* user
193 0, // notificationFrames,
194 true); // threadCanCallJava)
195
196 if(lpRecorder->initCheck() != NO_ERROR) {
197 LOGE("Error creating AudioRecord instance: initialization check failed.");
198 goto native_init_failure;
199 }
200
201 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
202 // of the Java object
203 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
204
205 // save our newly created callback information in the "nativeCallbackCookie" field
206 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
207 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
208
209 return AUDIORECORD_SUCCESS;
210
211 // failure:
212native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700213 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
214 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 delete lpCallbackData;
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217native_track_failure:
218 delete lpRecorder;
219
220 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
221 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
222
223 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
224}
225
226
227
228// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800229static int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
231{
232 AudioRecord *lpRecorder =
233 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
234 if (lpRecorder == NULL ) {
235 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800236 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
238
Eric Laurent9cc489a2009-12-05 05:20:01 -0800239 return android_media_translateRecorderErrorCode(lpRecorder->start());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240}
241
242
243// ----------------------------------------------------------------------------
244static void
245android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
246{
247 AudioRecord *lpRecorder =
248 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
249 if (lpRecorder == NULL ) {
250 jniThrowException(env, "java/lang/IllegalStateException", NULL);
251 return;
252 }
253
254 lpRecorder->stop();
255 //LOGV("Called lpRecorder->stop()");
256}
257
258
259// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800260static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
261
262 // serialize access. Ugly, but functional.
263 Mutex::Autolock lock(&sLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 AudioRecord *lpRecorder =
265 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Dave Sparkse6335c92010-03-13 17:08:22 -0800266 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
267 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268
Dave Sparkse6335c92010-03-13 17:08:22 -0800269 // reset the native resources in the Java object so any attempt to access
270 // them after a call to release fails.
271 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
272 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
273
274 // delete the AudioRecord object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 if (lpRecorder) {
276 LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
277 lpRecorder->stop();
278 delete lpRecorder;
279 }
280
281 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 if (lpCookie) {
283 LOGV("deleting lpCookie: %x\n", (int)lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700284 env->DeleteGlobalRef(lpCookie->audioRecord_class);
285 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 delete lpCookie;
287 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288}
289
290
291// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800292static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
293 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294}
295
296
297// ----------------------------------------------------------------------------
298static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
299 jbyteArray javaAudioData,
300 jint offsetInBytes, jint sizeInBytes) {
301 jbyte* recordBuff = NULL;
302 AudioRecord *lpRecorder = NULL;
303
304 // get the audio recorder from which we'll read new audio samples
305 lpRecorder =
306 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
307 if (lpRecorder == NULL) {
308 LOGE("Unable to retrieve AudioRecord object, can't record");
309 return 0;
310 }
311
312 if (!javaAudioData) {
313 LOGE("Invalid Java array to store recorded audio, can't record");
314 return 0;
315 }
316
317 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800318 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
319 // a way that it becomes much more efficient. When doing so, we will have to prevent the
320 // AudioSystem callback to be called while in critical section (in case of media server
321 // process crash for instance)
322 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323
324 if (recordBuff == NULL) {
325 LOGE("Error retrieving destination for recorded audio data, can't record");
326 return 0;
327 }
328
329 // read the new audio data from the native AudioRecord object
330 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
331 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
332 sizeInBytes > (jint)recorderBuffSize ?
333 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800334 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335
336 return (jint) readSize;
337}
338
339// ----------------------------------------------------------------------------
340static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
341 jshortArray javaAudioData,
342 jint offsetInShorts, jint sizeInShorts) {
343
344 return (android_media_AudioRecord_readInByteArray(env, thiz,
345 (jbyteArray) javaAudioData,
346 offsetInShorts*2, sizeInShorts*2)
347 / 2);
348}
349
350// ----------------------------------------------------------------------------
351static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
352 jobject jBuffer, jint sizeInBytes) {
353 AudioRecord *lpRecorder = NULL;
354 //LOGV("Entering android_media_AudioRecord_readInBuffer");
355
356 // get the audio recorder from which we'll read new audio samples
357 lpRecorder =
358 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
359 if(lpRecorder==NULL)
360 return 0;
361
362 // direct buffer and direct access supported?
363 long capacity = env->GetDirectBufferCapacity(jBuffer);
364 if(capacity == -1) {
365 // buffer direct access is not supported
366 LOGE("Buffer direct access is not supported, can't record");
367 return 0;
368 }
369 //LOGV("capacity = %ld", capacity);
370 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
371 if(nativeFromJavaBuf==NULL) {
372 LOGE("Buffer direct access is not supported, can't record");
373 return 0;
374 }
375
376 // read new data from the recorder
377 return (jint) lpRecorder->read(nativeFromJavaBuf,
378 capacity < sizeInBytes ? capacity : sizeInBytes);
379}
380
381
382// ----------------------------------------------------------------------------
383static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
384 jint markerPos) {
385
386 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
387 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
388
389 if (lpRecorder) {
390 return
391 android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
392 } else {
393 jniThrowException(env, "java/lang/IllegalStateException",
394 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
395 return AUDIORECORD_ERROR;
396 }
397}
398
399
400// ----------------------------------------------------------------------------
401static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
402
403 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
404 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
405 uint32_t markerPos = 0;
406
407 if (lpRecorder) {
408 lpRecorder->getMarkerPosition(&markerPos);
409 return (jint)markerPos;
410 } else {
411 jniThrowException(env, "java/lang/IllegalStateException",
412 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
413 return AUDIORECORD_ERROR;
414 }
415}
416
417
418// ----------------------------------------------------------------------------
419static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
420 jint period) {
421
422 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
423 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
424
425 if (lpRecorder) {
426 return
427 android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
428 } else {
429 jniThrowException(env, "java/lang/IllegalStateException",
430 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
431 return AUDIORECORD_ERROR;
432 }
433}
434
435
436// ----------------------------------------------------------------------------
437static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
438
439 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
440 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
441 uint32_t period = 0;
442
443 if (lpRecorder) {
444 lpRecorder->getPositionUpdatePeriod(&period);
445 return (jint)period;
446 } else {
447 jniThrowException(env, "java/lang/IllegalStateException",
448 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
449 return AUDIORECORD_ERROR;
450 }
451}
452
453
454// ----------------------------------------------------------------------------
455// returns the minimum required size for the successful creation of an AudioRecord instance.
456// returns 0 if the parameter combination is not supported.
457// return -1 if there was an error querying the buffer size.
458static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
459 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800462
463 int frameCount = 0;
464 status_t result = AudioRecord::getMinFrameCount(&frameCount,
465 sampleRateInHertz,
466 (audioFormat == javaAudioRecordFields.PCM16 ?
467 AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
468 nbChannels);
469
470 if (result == BAD_VALUE) {
471 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800473 if (result != NO_ERROR) {
474 return -1;
475 }
476 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477}
478
479
480// ----------------------------------------------------------------------------
481// ----------------------------------------------------------------------------
482static JNINativeMethod gMethods[] = {
483 // name, signature, funcPtr
Eric Laurent9cc489a2009-12-05 05:20:01 -0800484 {"native_start", "()I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
486 {"native_setup", "(Ljava/lang/Object;IIIII)I",
487 (void *)android_media_AudioRecord_setup},
488 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
489 {"native_release", "()V", (void *)android_media_AudioRecord_release},
490 {"native_read_in_byte_array",
491 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
492 {"native_read_in_short_array",
493 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
494 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
495 (void *)android_media_AudioRecord_readInDirectBuffer},
496 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
497 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
498 {"native_set_pos_update_period",
499 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
500 {"native_get_pos_update_period",
501 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
502 {"native_get_min_buff_size",
503 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
504};
505
506// field names found in android/media/AudioRecord.java
507#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
508#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
509#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
511#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
512
513#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
514
515// ----------------------------------------------------------------------------
516
517extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
518 jclass theClass, const char* className, const char* constName, int* constVal);
519
520// ----------------------------------------------------------------------------
521int register_android_media_AudioRecord(JNIEnv *env)
522{
523 javaAudioRecordFields.audioRecordClass = NULL;
524 javaAudioRecordFields.postNativeEventInJava = NULL;
525 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
526 javaAudioRecordFields.nativeCallbackCookie = NULL;
527
528
529 // Get the AudioRecord class
530 javaAudioRecordFields.audioRecordClass = env->FindClass(kClassPathName);
531 if (javaAudioRecordFields.audioRecordClass == NULL) {
532 LOGE("Can't find %s", kClassPathName);
533 return -1;
534 }
535
536 // Get the postEvent method
537 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
538 javaAudioRecordFields.audioRecordClass,
539 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
540 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
541 LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
542 return -1;
543 }
544
545 // Get the variables
546 // mNativeRecorderInJavaObj
547 javaAudioRecordFields.nativeRecorderInJavaObj =
548 env->GetFieldID(javaAudioRecordFields.audioRecordClass,
549 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
550 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
551 LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
552 return -1;
553 }
554 // mNativeCallbackCookie
555 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
556 javaAudioRecordFields.audioRecordClass,
557 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
558 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
559 LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
560 return -1;
561 }
562
563 // Get the format constants from the AudioFormat class
564 jclass audioFormatClass = NULL;
565 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
566 if (audioFormatClass == NULL) {
567 LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
568 return -1;
569 }
570 if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
571 JAVA_AUDIOFORMAT_CLASS_NAME,
572 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
573 || !android_media_getIntConstantFromClass(env, audioFormatClass,
574 JAVA_AUDIOFORMAT_CLASS_NAME,
575 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
576 // error log performed in getIntConstantFromClass()
577 return -1;
578 }
579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 return AndroidRuntime::registerNativeMethods(env,
581 kClassPathName, gMethods, NELEM(gMethods));
582}
583
584// ----------------------------------------------------------------------------