blob: 50d9ca1d9e5dd0e2eb55f7c97a6e7b80a0960d69 [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
Dima Zavin24fc2fb2011-04-19 22:30:36 -070034#include <cutils/bitops.h>
35
Dima Zavin34bb4192011-05-11 14:15:23 -070036#include <system/audio.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
38// ----------------------------------------------------------------------------
39
40using namespace android;
41
42// ----------------------------------------------------------------------------
43static const char* const kClassPathName = "android/media/AudioRecord";
44
45struct fields_t {
46 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 jmethodID postNativeEventInJava; //... event post callback method
48 int PCM16; //... format constants
49 int PCM8; //... format constants
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
51 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
52};
53static fields_t javaAudioRecordFields;
54
55struct audiorecord_callback_cookie {
56 jclass audioRecord_class;
57 jobject audioRecord_ref;
58 };
59
Dave Sparkse6335c92010-03-13 17:08:22 -080060Mutex sLock;
61
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062// ----------------------------------------------------------------------------
63
64#define AUDIORECORD_SUCCESS 0
65#define AUDIORECORD_ERROR -1
66#define AUDIORECORD_ERROR_BAD_VALUE -2
67#define AUDIORECORD_ERROR_INVALID_OPERATION -3
68#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070069#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070071#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
73
74jint android_media_translateRecorderErrorCode(int code) {
75 switch(code) {
76 case NO_ERROR:
77 return AUDIORECORD_SUCCESS;
78 case BAD_VALUE:
79 return AUDIORECORD_ERROR_BAD_VALUE;
80 case INVALID_OPERATION:
81 return AUDIORECORD_ERROR_INVALID_OPERATION;
82 default:
83 return AUDIORECORD_ERROR;
84 }
85}
86
87
88// ----------------------------------------------------------------------------
89static void recorderCallback(int event, void* user, void *info) {
90 if (event == AudioRecord::EVENT_MORE_DATA) {
91 // set size to 0 to signal we're not using the callback to read more data
92 AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
93 pBuff->size = 0;
94
95 } else if (event == AudioRecord::EVENT_MARKER) {
96 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
97 JNIEnv *env = AndroidRuntime::getJNIEnv();
98 if (user && env) {
99 env->CallStaticVoidMethod(
100 callbackInfo->audioRecord_class,
101 javaAudioRecordFields.postNativeEventInJava,
102 callbackInfo->audioRecord_ref, event, 0,0, NULL);
103 if (env->ExceptionCheck()) {
104 env->ExceptionDescribe();
105 env->ExceptionClear();
106 }
107 }
108
109 } else if (event == AudioRecord::EVENT_NEW_POS) {
110 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
111 JNIEnv *env = AndroidRuntime::getJNIEnv();
112 if (user && env) {
113 env->CallStaticVoidMethod(
114 callbackInfo->audioRecord_class,
115 javaAudioRecordFields.postNativeEventInJava,
116 callbackInfo->audioRecord_ref, event, 0,0, NULL);
117 if (env->ExceptionCheck()) {
118 env->ExceptionDescribe();
119 env->ExceptionClear();
120 }
121 }
122 }
123}
124
125
126// ----------------------------------------------------------------------------
127static int
128android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Eric Laurenta553c252009-07-17 12:17:14 -0700129 jint source, jint sampleRateInHertz, jint channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 jint audioFormat, jint buffSizeInBytes)
131{
132 //LOGV(">> Entering android_media_AudioRecord_setup");
Eric Laurenta553c252009-07-17 12:17:14 -0700133 //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
134 // sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700136 if (!audio_is_input_channel(channels)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
Eric Laurenta553c252009-07-17 12:17:14 -0700138 return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 }
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700140 uint32_t nbChannels = popcount(channels);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141
142 // compare the format against the Java constants
143 if ((audioFormat != javaAudioRecordFields.PCM16)
144 && (audioFormat != javaAudioRecordFields.PCM8)) {
145 LOGE("Error creating AudioRecord: unsupported audio format.");
146 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
147 }
148
149 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
150 int format = audioFormat==javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700151 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
153 if (buffSizeInBytes == 0) {
154 LOGE("Error creating AudioRecord: frameCount is 0.");
155 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
156 }
157 int frameSize = nbChannels * bytesPerSample;
158 size_t frameCount = buffSizeInBytes / frameSize;
159
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700160 if (source >= AUDIO_SOURCE_CNT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 LOGE("Error creating AudioRecord: unknown source.");
Eric Laurent4bc035a2009-05-22 09:18:15 -0700162 return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 }
Eric Laurent4bc035a2009-05-22 09:18:15 -0700164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 audiorecord_callback_cookie *lpCallbackData = NULL;
166 AudioRecord* lpRecorder = NULL;
167
168 // create an uninitialized AudioRecord object
169 lpRecorder = new AudioRecord();
170 if(lpRecorder == NULL) {
171 LOGE("Error creating AudioRecord instance.");
172 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
173 }
174
175 // create the callback information:
176 // this data will be passed with every AudioRecord callback
177 jclass clazz = env->GetObjectClass(thiz);
178 if (clazz == NULL) {
179 LOGE("Can't find %s when setting up callback.", kClassPathName);
180 goto native_track_failure;
181 }
182 lpCallbackData = new audiorecord_callback_cookie;
183 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
184 // we use a weak reference so the AudioRecord object can be garbage collected.
185 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
186
Eric Laurenta553c252009-07-17 12:17:14 -0700187 lpRecorder->set(source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 sampleRateInHertz,
189 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700190 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 frameCount,
192 0, // flags
193 recorderCallback,// callback_t
194 lpCallbackData,// void* user
195 0, // notificationFrames,
196 true); // threadCanCallJava)
197
198 if(lpRecorder->initCheck() != NO_ERROR) {
199 LOGE("Error creating AudioRecord instance: initialization check failed.");
200 goto native_init_failure;
201 }
202
203 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
204 // of the Java object
205 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
206
207 // save our newly created callback information in the "nativeCallbackCookie" field
208 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
209 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
210
211 return AUDIORECORD_SUCCESS;
212
213 // failure:
214native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700215 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
216 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 delete lpCallbackData;
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219native_track_failure:
220 delete lpRecorder;
221
222 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
223 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
224
225 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
226}
227
228
229
230// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800231static int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
233{
234 AudioRecord *lpRecorder =
235 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
236 if (lpRecorder == NULL ) {
237 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800238 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 }
240
Eric Laurent9cc489a2009-12-05 05:20:01 -0800241 return android_media_translateRecorderErrorCode(lpRecorder->start());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242}
243
244
245// ----------------------------------------------------------------------------
246static void
247android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
248{
249 AudioRecord *lpRecorder =
250 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
251 if (lpRecorder == NULL ) {
252 jniThrowException(env, "java/lang/IllegalStateException", NULL);
253 return;
254 }
255
256 lpRecorder->stop();
257 //LOGV("Called lpRecorder->stop()");
258}
259
260
261// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800262static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
263
264 // serialize access. Ugly, but functional.
265 Mutex::Autolock lock(&sLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 AudioRecord *lpRecorder =
267 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Dave Sparkse6335c92010-03-13 17:08:22 -0800268 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
269 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
Dave Sparkse6335c92010-03-13 17:08:22 -0800271 // reset the native resources in the Java object so any attempt to access
272 // them after a call to release fails.
273 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
274 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
275
276 // delete the AudioRecord object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 if (lpRecorder) {
278 LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
279 lpRecorder->stop();
280 delete lpRecorder;
281 }
282
283 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 if (lpCookie) {
285 LOGV("deleting lpCookie: %x\n", (int)lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700286 env->DeleteGlobalRef(lpCookie->audioRecord_class);
287 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 delete lpCookie;
289 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290}
291
292
293// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800294static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
295 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296}
297
298
299// ----------------------------------------------------------------------------
300static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
301 jbyteArray javaAudioData,
302 jint offsetInBytes, jint sizeInBytes) {
303 jbyte* recordBuff = NULL;
304 AudioRecord *lpRecorder = NULL;
305
306 // get the audio recorder from which we'll read new audio samples
307 lpRecorder =
308 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
309 if (lpRecorder == NULL) {
310 LOGE("Unable to retrieve AudioRecord object, can't record");
311 return 0;
312 }
313
314 if (!javaAudioData) {
315 LOGE("Invalid Java array to store recorded audio, can't record");
316 return 0;
317 }
318
319 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800320 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
321 // a way that it becomes much more efficient. When doing so, we will have to prevent the
322 // AudioSystem callback to be called while in critical section (in case of media server
323 // process crash for instance)
324 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
326 if (recordBuff == NULL) {
327 LOGE("Error retrieving destination for recorded audio data, can't record");
328 return 0;
329 }
330
331 // read the new audio data from the native AudioRecord object
332 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
333 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
334 sizeInBytes > (jint)recorderBuffSize ?
335 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800336 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
338 return (jint) readSize;
339}
340
341// ----------------------------------------------------------------------------
342static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
343 jshortArray javaAudioData,
344 jint offsetInShorts, jint sizeInShorts) {
345
346 return (android_media_AudioRecord_readInByteArray(env, thiz,
347 (jbyteArray) javaAudioData,
348 offsetInShorts*2, sizeInShorts*2)
349 / 2);
350}
351
352// ----------------------------------------------------------------------------
353static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
354 jobject jBuffer, jint sizeInBytes) {
355 AudioRecord *lpRecorder = NULL;
356 //LOGV("Entering android_media_AudioRecord_readInBuffer");
357
358 // get the audio recorder from which we'll read new audio samples
359 lpRecorder =
360 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
361 if(lpRecorder==NULL)
362 return 0;
363
364 // direct buffer and direct access supported?
365 long capacity = env->GetDirectBufferCapacity(jBuffer);
366 if(capacity == -1) {
367 // buffer direct access is not supported
368 LOGE("Buffer direct access is not supported, can't record");
369 return 0;
370 }
371 //LOGV("capacity = %ld", capacity);
372 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
373 if(nativeFromJavaBuf==NULL) {
374 LOGE("Buffer direct access is not supported, can't record");
375 return 0;
376 }
377
378 // read new data from the recorder
379 return (jint) lpRecorder->read(nativeFromJavaBuf,
380 capacity < sizeInBytes ? capacity : sizeInBytes);
381}
382
383
384// ----------------------------------------------------------------------------
385static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
386 jint markerPos) {
387
388 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
389 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
390
391 if (lpRecorder) {
392 return
393 android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
394 } else {
395 jniThrowException(env, "java/lang/IllegalStateException",
396 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
397 return AUDIORECORD_ERROR;
398 }
399}
400
401
402// ----------------------------------------------------------------------------
403static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
404
405 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
406 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
407 uint32_t markerPos = 0;
408
409 if (lpRecorder) {
410 lpRecorder->getMarkerPosition(&markerPos);
411 return (jint)markerPos;
412 } else {
413 jniThrowException(env, "java/lang/IllegalStateException",
414 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
415 return AUDIORECORD_ERROR;
416 }
417}
418
419
420// ----------------------------------------------------------------------------
421static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
422 jint period) {
423
424 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
425 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
426
427 if (lpRecorder) {
428 return
429 android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
430 } else {
431 jniThrowException(env, "java/lang/IllegalStateException",
432 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
433 return AUDIORECORD_ERROR;
434 }
435}
436
437
438// ----------------------------------------------------------------------------
439static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
440
441 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
442 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
443 uint32_t period = 0;
444
445 if (lpRecorder) {
446 lpRecorder->getPositionUpdatePeriod(&period);
447 return (jint)period;
448 } else {
449 jniThrowException(env, "java/lang/IllegalStateException",
450 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
451 return AUDIORECORD_ERROR;
452 }
453}
454
455
456// ----------------------------------------------------------------------------
457// returns the minimum required size for the successful creation of an AudioRecord instance.
458// returns 0 if the parameter combination is not supported.
459// return -1 if there was an error querying the buffer size.
460static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
461 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800464
465 int frameCount = 0;
466 status_t result = AudioRecord::getMinFrameCount(&frameCount,
467 sampleRateInHertz,
468 (audioFormat == javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700469 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
Chia-chi Yehc3308072010-08-19 17:14:36 +0800470 nbChannels);
471
472 if (result == BAD_VALUE) {
473 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800475 if (result != NO_ERROR) {
476 return -1;
477 }
478 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479}
480
481
482// ----------------------------------------------------------------------------
483// ----------------------------------------------------------------------------
484static JNINativeMethod gMethods[] = {
485 // name, signature, funcPtr
Eric Laurent9cc489a2009-12-05 05:20:01 -0800486 {"native_start", "()I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
488 {"native_setup", "(Ljava/lang/Object;IIIII)I",
489 (void *)android_media_AudioRecord_setup},
490 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
491 {"native_release", "()V", (void *)android_media_AudioRecord_release},
492 {"native_read_in_byte_array",
493 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
494 {"native_read_in_short_array",
495 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
496 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
497 (void *)android_media_AudioRecord_readInDirectBuffer},
498 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
499 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
500 {"native_set_pos_update_period",
501 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
502 {"native_get_pos_update_period",
503 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
504 {"native_get_min_buff_size",
505 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
506};
507
508// field names found in android/media/AudioRecord.java
509#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
510#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
511#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
513#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
514
515#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
516
517// ----------------------------------------------------------------------------
518
519extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
520 jclass theClass, const char* className, const char* constName, int* constVal);
521
522// ----------------------------------------------------------------------------
523int register_android_media_AudioRecord(JNIEnv *env)
524{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 javaAudioRecordFields.postNativeEventInJava = NULL;
526 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
527 javaAudioRecordFields.nativeCallbackCookie = NULL;
528
529
530 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700531 jclass audioRecordClass = env->FindClass(kClassPathName);
532 if (audioRecordClass == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 LOGE("Can't find %s", kClassPathName);
534 return -1;
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 // Get the postEvent method
537 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700538 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 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 =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700548 env->GetFieldID(audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 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(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700556 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 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// ----------------------------------------------------------------------------