blob: 197d2401be1679fc13839fed97a7474b153fd445 [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
Glenn Kastenc81d31c2012-03-13 14:46:23 -070026#include <jni.h>
27#include <JNIHelp.h>
28#include <android_runtime/AndroidRuntime.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
Glenn Kastenc81d31c2012-03-13 14:46:23 -070030#include <utils/Log.h>
Eric Laurent532bc1c2012-04-20 12:45:03 -070031#include <utils/SortedVector.h>
32#include <utils/threads.h>
Glenn Kastenc81d31c2012-03-13 14:46:23 -070033#include <media/AudioRecord.h>
34#include <media/mediarecorder.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
Dima Zavin24fc2fb2011-04-19 22:30:36 -070036#include <cutils/bitops.h>
37
Dima Zavin34bb4192011-05-11 14:15:23 -070038#include <system/audio.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40// ----------------------------------------------------------------------------
41
42using namespace android;
43
44// ----------------------------------------------------------------------------
45static const char* const kClassPathName = "android/media/AudioRecord";
46
47struct fields_t {
48 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 jmethodID postNativeEventInJava; //... event post callback method
50 int PCM16; //... format constants
51 int PCM8; //... format constants
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
53 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
54};
55static fields_t javaAudioRecordFields;
56
57struct audiorecord_callback_cookie {
58 jclass audioRecord_class;
59 jobject audioRecord_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070060 bool busy;
61 Condition cond;
62};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Eric Laurent532bc1c2012-04-20 12:45:03 -070064static Mutex sLock;
65static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
Dave Sparkse6335c92010-03-13 17:08:22 -080066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067// ----------------------------------------------------------------------------
68
69#define AUDIORECORD_SUCCESS 0
70#define AUDIORECORD_ERROR -1
71#define AUDIORECORD_ERROR_BAD_VALUE -2
72#define AUDIORECORD_ERROR_INVALID_OPERATION -3
73#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070074#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070076#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
78
79jint android_media_translateRecorderErrorCode(int code) {
Glenn Kasten18db49a2012-03-12 16:29:55 -070080 switch (code) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 case NO_ERROR:
82 return AUDIORECORD_SUCCESS;
83 case BAD_VALUE:
84 return AUDIORECORD_ERROR_BAD_VALUE;
85 case INVALID_OPERATION:
86 return AUDIORECORD_ERROR_INVALID_OPERATION;
87 default:
88 return AUDIORECORD_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -070089 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090}
91
92
93// ----------------------------------------------------------------------------
94static void recorderCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -070095
96 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
97 {
98 Mutex::Autolock l(sLock);
99 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
100 return;
101 }
102 callbackInfo->busy = true;
103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 if (event == AudioRecord::EVENT_MORE_DATA) {
105 // set size to 0 to signal we're not using the callback to read more data
106 AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700107 pBuff->size = 0;
108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 } else if (event == AudioRecord::EVENT_MARKER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 JNIEnv *env = AndroidRuntime::getJNIEnv();
111 if (user && env) {
112 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700113 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 javaAudioRecordFields.postNativeEventInJava,
115 callbackInfo->audioRecord_ref, event, 0,0, NULL);
116 if (env->ExceptionCheck()) {
117 env->ExceptionDescribe();
118 env->ExceptionClear();
119 }
120 }
121
122 } else if (event == AudioRecord::EVENT_NEW_POS) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 JNIEnv *env = AndroidRuntime::getJNIEnv();
124 if (user && env) {
125 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700126 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 javaAudioRecordFields.postNativeEventInJava,
128 callbackInfo->audioRecord_ref, event, 0,0, NULL);
129 if (env->ExceptionCheck()) {
130 env->ExceptionDescribe();
131 env->ExceptionClear();
132 }
133 }
134 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700135 {
136 Mutex::Autolock l(sLock);
137 callbackInfo->busy = false;
138 callbackInfo->cond.broadcast();
139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140}
141
Eric Laurent532bc1c2012-04-20 12:45:03 -0700142// ----------------------------------------------------------------------------
143static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
144{
145 Mutex::Autolock l(sLock);
146 AudioRecord* const ar =
147 (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
148 return sp<AudioRecord>(ar);
149}
150
151static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
152{
153 Mutex::Autolock l(sLock);
154 sp<AudioRecord> old =
155 (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
156 if (ar.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800157 ar->incStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700158 }
159 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800160 old->decStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700161 }
162 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
163 return old;
164}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165
166// ----------------------------------------------------------------------------
167static int
168android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Eric Laurenta553c252009-07-17 12:17:14 -0700169 jint source, jint sampleRateInHertz, jint channels,
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700170 jint audioFormat, jint buffSizeInBytes, jintArray jSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171{
Steve Block71f2cf12011-10-20 11:56:00 +0100172 //ALOGV(">> Entering android_media_AudioRecord_setup");
173 //ALOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
Eric Laurenta553c252009-07-17 12:17:14 -0700174 // sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700176 if (!audio_is_input_channel(channels)) {
Steve Block3762c312012-01-06 19:20:56 +0000177 ALOGE("Error creating AudioRecord: channel count is not 1 or 2.");
Eric Laurenta553c252009-07-17 12:17:14 -0700178 return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700180 uint32_t nbChannels = popcount(channels);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
182 // compare the format against the Java constants
Glenn Kasten18db49a2012-03-12 16:29:55 -0700183 if ((audioFormat != javaAudioRecordFields.PCM16)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 && (audioFormat != javaAudioRecordFields.PCM8)) {
Steve Block3762c312012-01-06 19:20:56 +0000185 ALOGE("Error creating AudioRecord: unsupported audio format.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
187 }
188
189 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
Glenn Kasten0a204ed2012-01-12 12:27:51 -0800190 audio_format_t format = audioFormat==javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700191 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
193 if (buffSizeInBytes == 0) {
Steve Block3762c312012-01-06 19:20:56 +0000194 ALOGE("Error creating AudioRecord: frameCount is 0.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
196 }
197 int frameSize = nbChannels * bytesPerSample;
198 size_t frameCount = buffSizeInBytes / frameSize;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700199
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800200 if (uint32_t(source) >= AUDIO_SOURCE_CNT) {
Steve Block3762c312012-01-06 19:20:56 +0000201 ALOGE("Error creating AudioRecord: unknown source.");
Eric Laurent4bc035a2009-05-22 09:18:15 -0700202 return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
Eric Laurent4bc035a2009-05-22 09:18:15 -0700204
Eric Laurent532bc1c2012-04-20 12:45:03 -0700205 jclass clazz = env->GetObjectClass(thiz);
206 if (clazz == NULL) {
207 ALOGE("Can't find %s when setting up callback.", kClassPathName);
208 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
209 }
210
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700211 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000212 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700213 return AUDIORECORD_ERROR;
214 }
215
216 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
217 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000218 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700219 return AUDIORECORD_ERROR;
220 }
221 int sessionId = nSession[0];
222 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
223 nSession = NULL;
224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 // create an uninitialized AudioRecord object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700226 sp<AudioRecord> lpRecorder = new AudioRecord();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 // create the callback information:
229 // this data will be passed with every AudioRecord callback
Eric Laurent532bc1c2012-04-20 12:45:03 -0700230 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
232 // we use a weak reference so the AudioRecord object can be garbage collected.
233 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700234 lpCallbackData->busy = false;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700235
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800236 lpRecorder->set((audio_source_t) source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 sampleRateInHertz,
238 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700239 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 frameCount,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 recorderCallback,// callback_t
242 lpCallbackData,// void* user
243 0, // notificationFrames,
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700244 true, // threadCanCallJava)
245 sessionId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246
Glenn Kasten18db49a2012-03-12 16:29:55 -0700247 if (lpRecorder->initCheck() != NO_ERROR) {
Steve Block3762c312012-01-06 19:20:56 +0000248 ALOGE("Error creating AudioRecord instance: initialization check failed.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 goto native_init_failure;
250 }
251
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700252 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
253 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000254 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700255 goto native_init_failure;
256 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800257 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700258 nSession[0] = lpRecorder->getSessionId();
259 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
260 nSession = NULL;
261
Eric Laurent532bc1c2012-04-20 12:45:03 -0700262 { // scope for the lock
263 Mutex::Autolock l(sLock);
264 sAudioRecordCallBackCookies.add(lpCallbackData);
265 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700266 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700268 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 // save our newly created callback information in the "nativeCallbackCookie" field
271 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
272 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700273
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 return AUDIORECORD_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 // failure:
277native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700278 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
279 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 delete lpCallbackData;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700282
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
284}
285
286
287
288// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800289static int
Eric Laurent505e5c82012-03-29 15:19:36 -0700290android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700292 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 if (lpRecorder == NULL ) {
294 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800295 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700297
Eric Laurent505e5c82012-03-29 15:19:36 -0700298 return android_media_translateRecorderErrorCode(
299 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300}
301
302
303// ----------------------------------------------------------------------------
304static void
305android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
306{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700307 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 if (lpRecorder == NULL ) {
309 jniThrowException(env, "java/lang/IllegalStateException", NULL);
310 return;
311 }
312
313 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100314 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315}
316
317
318// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800319
Eric Laurent532bc1c2012-04-20 12:45:03 -0700320#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
321static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
322 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
323 if (lpRecorder == NULL) {
324 return;
325 }
326 ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
327 lpRecorder->stop();
328
Dave Sparkse6335c92010-03-13 17:08:22 -0800329 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
330 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331
Dave Sparkse6335c92010-03-13 17:08:22 -0800332 // reset the native resources in the Java object so any attempt to access
333 // them after a call to release fails.
Dave Sparkse6335c92010-03-13 17:08:22 -0800334 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700338 Mutex::Autolock l(sLock);
Steve Block71f2cf12011-10-20 11:56:00 +0100339 ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700340 while (lpCookie->busy) {
341 if (lpCookie->cond.waitRelative(sLock,
342 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
343 NO_ERROR) {
344 break;
345 }
346 }
347 sAudioRecordCallBackCookies.remove(lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700348 env->DeleteGlobalRef(lpCookie->audioRecord_class);
349 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 delete lpCookie;
351 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352}
353
354
355// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800356static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
357 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358}
359
360
361// ----------------------------------------------------------------------------
362static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
363 jbyteArray javaAudioData,
364 jint offsetInBytes, jint sizeInBytes) {
365 jbyte* recordBuff = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700367 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 if (lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000369 ALOGE("Unable to retrieve AudioRecord object, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 return 0;
371 }
372
373 if (!javaAudioData) {
Steve Block3762c312012-01-06 19:20:56 +0000374 ALOGE("Invalid Java array to store recorded audio, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 return 0;
376 }
377
378 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800379 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
380 // a way that it becomes much more efficient. When doing so, we will have to prevent the
381 // AudioSystem callback to be called while in critical section (in case of media server
382 // process crash for instance)
383 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
385 if (recordBuff == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000386 ALOGE("Error retrieving destination for recorded audio data, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 return 0;
388 }
389
390 // read the new audio data from the native AudioRecord object
391 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700392 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
393 sizeInBytes > (jint)recorderBuffSize ?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800395 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396
397 return (jint) readSize;
398}
399
400// ----------------------------------------------------------------------------
401static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
402 jshortArray javaAudioData,
403 jint offsetInShorts, jint sizeInShorts) {
404
405 return (android_media_AudioRecord_readInByteArray(env, thiz,
406 (jbyteArray) javaAudioData,
407 offsetInShorts*2, sizeInShorts*2)
408 / 2);
409}
410
411// ----------------------------------------------------------------------------
412static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
413 jobject jBuffer, jint sizeInBytes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700415 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700416 if (lpRecorder==NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 return 0;
418
419 // direct buffer and direct access supported?
420 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700421 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000423 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 return 0;
425 }
Steve Block71f2cf12011-10-20 11:56:00 +0100426 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700428 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000429 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 return 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432
433 // read new data from the recorder
Glenn Kasten18db49a2012-03-12 16:29:55 -0700434 return (jint) lpRecorder->read(nativeFromJavaBuf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 capacity < sizeInBytes ? capacity : sizeInBytes);
436}
437
438
439// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700440static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700442
Eric Laurent532bc1c2012-04-20 12:45:03 -0700443 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
444 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 jniThrowException(env, "java/lang/IllegalStateException",
446 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
447 return AUDIORECORD_ERROR;
448 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700449 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450}
451
452
453// ----------------------------------------------------------------------------
454static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700455
Eric Laurent532bc1c2012-04-20 12:45:03 -0700456 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700458
Eric Laurent532bc1c2012-04-20 12:45:03 -0700459 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 jniThrowException(env, "java/lang/IllegalStateException",
461 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
462 return AUDIORECORD_ERROR;
463 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700464 lpRecorder->getMarkerPosition(&markerPos);
465 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466}
467
468
469// ----------------------------------------------------------------------------
470static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
471 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700472
Eric Laurent532bc1c2012-04-20 12:45:03 -0700473 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700474
Eric Laurent532bc1c2012-04-20 12:45:03 -0700475 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 jniThrowException(env, "java/lang/IllegalStateException",
477 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
478 return AUDIORECORD_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700479 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700480 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481}
482
483
484// ----------------------------------------------------------------------------
485static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700486
Eric Laurent532bc1c2012-04-20 12:45:03 -0700487 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700489
Eric Laurent532bc1c2012-04-20 12:45:03 -0700490 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 jniThrowException(env, "java/lang/IllegalStateException",
492 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
493 return AUDIORECORD_ERROR;
494 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700495 lpRecorder->getPositionUpdatePeriod(&period);
496 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497}
498
499
500// ----------------------------------------------------------------------------
501// returns the minimum required size for the successful creation of an AudioRecord instance.
502// returns 0 if the parameter combination is not supported.
503// return -1 if there was an error querying the buffer size.
504static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
505 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800506
Eric Laurent532bc1c2012-04-20 12:45:03 -0700507 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
508 sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800509
Glenn Kastenfd1e3df2012-11-13 15:21:06 -0800510 size_t frameCount = 0;
Chia-chi Yehc3308072010-08-19 17:14:36 +0800511 status_t result = AudioRecord::getMinFrameCount(&frameCount,
512 sampleRateInHertz,
513 (audioFormat == javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700514 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
Glenn Kasten845b4712012-07-02 13:12:31 -0700515 audio_channel_in_mask_from_count(nbChannels));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800516
517 if (result == BAD_VALUE) {
518 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800520 if (result != NO_ERROR) {
521 return -1;
522 }
523 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524}
525
526
527// ----------------------------------------------------------------------------
528// ----------------------------------------------------------------------------
529static JNINativeMethod gMethods[] = {
530 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700531 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700533 {"native_setup", "(Ljava/lang/Object;IIIII[I)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 (void *)android_media_AudioRecord_setup},
535 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
536 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700537 {"native_read_in_byte_array",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
539 {"native_read_in_short_array",
540 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
541 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
542 (void *)android_media_AudioRecord_readInDirectBuffer},
543 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
544 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
545 {"native_set_pos_update_period",
546 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
547 {"native_get_pos_update_period",
548 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
549 {"native_get_min_buff_size",
550 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
551};
552
553// field names found in android/media/AudioRecord.java
554#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
555#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
556#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
558#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
559
560#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
561
562// ----------------------------------------------------------------------------
563
Glenn Kasten18db49a2012-03-12 16:29:55 -0700564extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 jclass theClass, const char* className, const char* constName, int* constVal);
566
567// ----------------------------------------------------------------------------
568int register_android_media_AudioRecord(JNIEnv *env)
569{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 javaAudioRecordFields.postNativeEventInJava = NULL;
571 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
572 javaAudioRecordFields.nativeCallbackCookie = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700573
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574
575 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700576 jclass audioRecordClass = env->FindClass(kClassPathName);
577 if (audioRecordClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000578 ALOGE("Can't find %s", kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 return -1;
580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 // Get the postEvent method
582 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700583 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
585 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000586 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 return -1;
588 }
589
590 // Get the variables
591 // mNativeRecorderInJavaObj
Glenn Kasten18db49a2012-03-12 16:29:55 -0700592 javaAudioRecordFields.nativeRecorderInJavaObj =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700593 env->GetFieldID(audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
595 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000596 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 return -1;
598 }
599 // mNativeCallbackCookie
600 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700601 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
603 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000604 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 return -1;
606 }
607
608 // Get the format constants from the AudioFormat class
609 jclass audioFormatClass = NULL;
610 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
611 if (audioFormatClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000612 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 return -1;
614 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700615 if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
616 JAVA_AUDIOFORMAT_CLASS_NAME,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
Glenn Kasten18db49a2012-03-12 16:29:55 -0700618 || !android_media_getIntConstantFromClass(env, audioFormatClass,
619 JAVA_AUDIOFORMAT_CLASS_NAME,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700621 // error log performed in getIntConstantFromClass()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 return -1;
623 }
624
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 return AndroidRuntime::registerNativeMethods(env,
626 kClassPathName, gMethods, NELEM(gMethods));
627}
628
629// ----------------------------------------------------------------------------