blob: b34dbce91c292f1d28d216331e4b750fefd94cb5 [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,
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700130 jint audioFormat, jint buffSizeInBytes, jintArray jSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131{
Steve Block71f2cf12011-10-20 11:56:00 +0100132 //ALOGV(">> Entering android_media_AudioRecord_setup");
133 //ALOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
Eric Laurenta553c252009-07-17 12:17:14 -0700134 // 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)) {
Steve Block3762c312012-01-06 19:20:56 +0000137 ALOGE("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)) {
Steve Block3762c312012-01-06 19:20:56 +0000145 ALOGE("Error creating AudioRecord: unsupported audio format.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
147 }
148
149 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
Glenn Kasten0a204ed2012-01-12 12:27:51 -0800150 audio_format_t 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) {
Steve Block3762c312012-01-06 19:20:56 +0000154 ALOGE("Error creating AudioRecord: frameCount is 0.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
156 }
157 int frameSize = nbChannels * bytesPerSample;
158 size_t frameCount = buffSizeInBytes / frameSize;
159
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800160 if (uint32_t(source) >= AUDIO_SOURCE_CNT) {
Steve Block3762c312012-01-06 19:20:56 +0000161 ALOGE("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
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700165 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000166 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700167 return AUDIORECORD_ERROR;
168 }
169
170 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
171 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000172 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700173 return AUDIORECORD_ERROR;
174 }
175 int sessionId = nSession[0];
176 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
177 nSession = NULL;
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 audiorecord_callback_cookie *lpCallbackData = NULL;
180 AudioRecord* lpRecorder = NULL;
181
182 // create an uninitialized AudioRecord object
183 lpRecorder = new AudioRecord();
184 if(lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000185 ALOGE("Error creating AudioRecord instance.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
187 }
188
189 // create the callback information:
190 // this data will be passed with every AudioRecord callback
191 jclass clazz = env->GetObjectClass(thiz);
192 if (clazz == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000193 ALOGE("Can't find %s when setting up callback.", kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 goto native_track_failure;
195 }
196 lpCallbackData = new audiorecord_callback_cookie;
197 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
198 // we use a weak reference so the AudioRecord object can be garbage collected.
199 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
200
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800201 lpRecorder->set((audio_source_t) source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 sampleRateInHertz,
203 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700204 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 frameCount,
Glenn Kastenf60a5d72012-03-07 09:04:02 -0800206 (AudioRecord::record_flags) 0, // flags
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 recorderCallback,// callback_t
208 lpCallbackData,// void* user
209 0, // notificationFrames,
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700210 true, // threadCanCallJava)
211 sessionId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212
213 if(lpRecorder->initCheck() != NO_ERROR) {
Steve Block3762c312012-01-06 19:20:56 +0000214 ALOGE("Error creating AudioRecord instance: initialization check failed.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 goto native_init_failure;
216 }
217
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700218 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
219 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000220 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700221 goto native_init_failure;
222 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800223 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700224 nSession[0] = lpRecorder->getSessionId();
225 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
226 nSession = NULL;
227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
229 // of the Java object
230 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
231
232 // save our newly created callback information in the "nativeCallbackCookie" field
233 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
234 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
235
236 return AUDIORECORD_SUCCESS;
237
238 // failure:
239native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700240 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
241 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 delete lpCallbackData;
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244native_track_failure:
245 delete lpRecorder;
246
247 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
248 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
249
250 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
251}
252
253
254
255// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800256static int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
258{
259 AudioRecord *lpRecorder =
260 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
261 if (lpRecorder == NULL ) {
262 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800263 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 }
265
Eric Laurent9cc489a2009-12-05 05:20:01 -0800266 return android_media_translateRecorderErrorCode(lpRecorder->start());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267}
268
269
270// ----------------------------------------------------------------------------
271static void
272android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
273{
274 AudioRecord *lpRecorder =
275 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
276 if (lpRecorder == NULL ) {
277 jniThrowException(env, "java/lang/IllegalStateException", NULL);
278 return;
279 }
280
281 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100282 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283}
284
285
286// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800287static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
288
289 // serialize access. Ugly, but functional.
290 Mutex::Autolock lock(&sLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 AudioRecord *lpRecorder =
292 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Dave Sparkse6335c92010-03-13 17:08:22 -0800293 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
294 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295
Dave Sparkse6335c92010-03-13 17:08:22 -0800296 // reset the native resources in the Java object so any attempt to access
297 // them after a call to release fails.
298 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
299 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
300
301 // delete the AudioRecord object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 if (lpRecorder) {
Steve Block71f2cf12011-10-20 11:56:00 +0100303 ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 lpRecorder->stop();
305 delete lpRecorder;
306 }
307
308 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 if (lpCookie) {
Steve Block71f2cf12011-10-20 11:56:00 +0100310 ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700311 env->DeleteGlobalRef(lpCookie->audioRecord_class);
312 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 delete lpCookie;
314 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315}
316
317
318// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800319static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
320 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321}
322
323
324// ----------------------------------------------------------------------------
325static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
326 jbyteArray javaAudioData,
327 jint offsetInBytes, jint sizeInBytes) {
328 jbyte* recordBuff = NULL;
329 AudioRecord *lpRecorder = NULL;
330
331 // get the audio recorder from which we'll read new audio samples
332 lpRecorder =
333 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
334 if (lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000335 ALOGE("Unable to retrieve AudioRecord object, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 return 0;
337 }
338
339 if (!javaAudioData) {
Steve Block3762c312012-01-06 19:20:56 +0000340 ALOGE("Invalid Java array to store recorded audio, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 return 0;
342 }
343
344 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800345 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
346 // a way that it becomes much more efficient. When doing so, we will have to prevent the
347 // AudioSystem callback to be called while in critical section (in case of media server
348 // process crash for instance)
349 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350
351 if (recordBuff == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000352 ALOGE("Error retrieving destination for recorded audio data, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 return 0;
354 }
355
356 // read the new audio data from the native AudioRecord object
357 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
358 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
359 sizeInBytes > (jint)recorderBuffSize ?
360 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800361 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362
363 return (jint) readSize;
364}
365
366// ----------------------------------------------------------------------------
367static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
368 jshortArray javaAudioData,
369 jint offsetInShorts, jint sizeInShorts) {
370
371 return (android_media_AudioRecord_readInByteArray(env, thiz,
372 (jbyteArray) javaAudioData,
373 offsetInShorts*2, sizeInShorts*2)
374 / 2);
375}
376
377// ----------------------------------------------------------------------------
378static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
379 jobject jBuffer, jint sizeInBytes) {
380 AudioRecord *lpRecorder = NULL;
Steve Block71f2cf12011-10-20 11:56:00 +0100381 //ALOGV("Entering android_media_AudioRecord_readInBuffer");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382
383 // get the audio recorder from which we'll read new audio samples
384 lpRecorder =
385 (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
386 if(lpRecorder==NULL)
387 return 0;
388
389 // direct buffer and direct access supported?
390 long capacity = env->GetDirectBufferCapacity(jBuffer);
391 if(capacity == -1) {
392 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000393 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return 0;
395 }
Steve Block71f2cf12011-10-20 11:56:00 +0100396 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
398 if(nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000399 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 return 0;
401 }
402
403 // read new data from the recorder
404 return (jint) lpRecorder->read(nativeFromJavaBuf,
405 capacity < sizeInBytes ? capacity : sizeInBytes);
406}
407
408
409// ----------------------------------------------------------------------------
410static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
411 jint markerPos) {
412
413 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
414 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
415
416 if (lpRecorder) {
417 return
418 android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
419 } else {
420 jniThrowException(env, "java/lang/IllegalStateException",
421 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
422 return AUDIORECORD_ERROR;
423 }
424}
425
426
427// ----------------------------------------------------------------------------
428static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
429
430 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
431 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
432 uint32_t markerPos = 0;
433
434 if (lpRecorder) {
435 lpRecorder->getMarkerPosition(&markerPos);
436 return (jint)markerPos;
437 } else {
438 jniThrowException(env, "java/lang/IllegalStateException",
439 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
440 return AUDIORECORD_ERROR;
441 }
442}
443
444
445// ----------------------------------------------------------------------------
446static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
447 jint period) {
448
449 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
450 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
451
452 if (lpRecorder) {
453 return
454 android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
455 } else {
456 jniThrowException(env, "java/lang/IllegalStateException",
457 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
458 return AUDIORECORD_ERROR;
459 }
460}
461
462
463// ----------------------------------------------------------------------------
464static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
465
466 AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
467 thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
468 uint32_t period = 0;
469
470 if (lpRecorder) {
471 lpRecorder->getPositionUpdatePeriod(&period);
472 return (jint)period;
473 } else {
474 jniThrowException(env, "java/lang/IllegalStateException",
475 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
476 return AUDIORECORD_ERROR;
477 }
478}
479
480
481// ----------------------------------------------------------------------------
482// returns the minimum required size for the successful creation of an AudioRecord instance.
483// returns 0 if the parameter combination is not supported.
484// return -1 if there was an error querying the buffer size.
485static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
486 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800487
Steve Block71f2cf12011-10-20 11:56:00 +0100488 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800489
490 int frameCount = 0;
491 status_t result = AudioRecord::getMinFrameCount(&frameCount,
492 sampleRateInHertz,
493 (audioFormat == javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700494 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
Chia-chi Yehc3308072010-08-19 17:14:36 +0800495 nbChannels);
496
497 if (result == BAD_VALUE) {
498 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800500 if (result != NO_ERROR) {
501 return -1;
502 }
503 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504}
505
506
507// ----------------------------------------------------------------------------
508// ----------------------------------------------------------------------------
509static JNINativeMethod gMethods[] = {
510 // name, signature, funcPtr
Eric Laurent9cc489a2009-12-05 05:20:01 -0800511 {"native_start", "()I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700513 {"native_setup", "(Ljava/lang/Object;IIIII[I)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 (void *)android_media_AudioRecord_setup},
515 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
516 {"native_release", "()V", (void *)android_media_AudioRecord_release},
517 {"native_read_in_byte_array",
518 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
519 {"native_read_in_short_array",
520 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
521 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
522 (void *)android_media_AudioRecord_readInDirectBuffer},
523 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
524 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
525 {"native_set_pos_update_period",
526 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
527 {"native_get_pos_update_period",
528 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
529 {"native_get_min_buff_size",
530 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
531};
532
533// field names found in android/media/AudioRecord.java
534#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
535#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
536#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
538#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
539
540#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
541
542// ----------------------------------------------------------------------------
543
544extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
545 jclass theClass, const char* className, const char* constName, int* constVal);
546
547// ----------------------------------------------------------------------------
548int register_android_media_AudioRecord(JNIEnv *env)
549{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 javaAudioRecordFields.postNativeEventInJava = NULL;
551 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
552 javaAudioRecordFields.nativeCallbackCookie = NULL;
553
554
555 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700556 jclass audioRecordClass = env->FindClass(kClassPathName);
557 if (audioRecordClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000558 ALOGE("Can't find %s", kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 return -1;
560 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 // Get the postEvent method
562 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700563 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
565 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000566 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 return -1;
568 }
569
570 // Get the variables
571 // mNativeRecorderInJavaObj
572 javaAudioRecordFields.nativeRecorderInJavaObj =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700573 env->GetFieldID(audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
575 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000576 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 return -1;
578 }
579 // mNativeCallbackCookie
580 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700581 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
583 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000584 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 return -1;
586 }
587
588 // Get the format constants from the AudioFormat class
589 jclass audioFormatClass = NULL;
590 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
591 if (audioFormatClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000592 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 return -1;
594 }
595 if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
596 JAVA_AUDIOFORMAT_CLASS_NAME,
597 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
598 || !android_media_getIntConstantFromClass(env, audioFormatClass,
599 JAVA_AUDIOFORMAT_CLASS_NAME,
600 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
601 // error log performed in getIntConstantFromClass()
602 return -1;
603 }
604
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 return AndroidRuntime::registerNativeMethods(env,
606 kClassPathName, gMethods, NELEM(gMethods));
607}
608
609// ----------------------------------------------------------------------------