blob: b22668b83d5f3ae463f17601c9c5ab1edc040bc6 [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
Glenn Kastenc81d31c2012-03-13 14:46:23 -070021#include <jni.h>
22#include <JNIHelp.h>
23#include <android_runtime/AndroidRuntime.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
Glenn Kastenc81d31c2012-03-13 14:46:23 -070025#include <utils/Log.h>
26#include <media/AudioRecord.h>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070027
Dima Zavin34bb4192011-05-11 14:15:23 -070028#include <system/audio.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
30// ----------------------------------------------------------------------------
31
32using namespace android;
33
34// ----------------------------------------------------------------------------
35static const char* const kClassPathName = "android/media/AudioRecord";
36
37struct fields_t {
38 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039 jmethodID postNativeEventInJava; //... event post callback method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
41 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
42};
43static fields_t javaAudioRecordFields;
44
45struct audiorecord_callback_cookie {
46 jclass audioRecord_class;
47 jobject audioRecord_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070048 bool busy;
49 Condition cond;
50};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
Glenn Kasten9afc78d72013-08-01 07:26:04 -070052// keep these values in sync with AudioFormat.java
53#define ENCODING_PCM_16BIT 2
54#define ENCODING_PCM_8BIT 3
55
Eric Laurent532bc1c2012-04-20 12:45:03 -070056static Mutex sLock;
57static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
Dave Sparkse6335c92010-03-13 17:08:22 -080058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059// ----------------------------------------------------------------------------
60
61#define AUDIORECORD_SUCCESS 0
62#define AUDIORECORD_ERROR -1
63#define AUDIORECORD_ERROR_BAD_VALUE -2
64#define AUDIORECORD_ERROR_INVALID_OPERATION -3
65#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070066#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070068#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
70
71jint android_media_translateRecorderErrorCode(int code) {
Glenn Kasten18db49a2012-03-12 16:29:55 -070072 switch (code) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 case NO_ERROR:
74 return AUDIORECORD_SUCCESS;
75 case BAD_VALUE:
76 return AUDIORECORD_ERROR_BAD_VALUE;
77 case INVALID_OPERATION:
78 return AUDIORECORD_ERROR_INVALID_OPERATION;
79 default:
80 return AUDIORECORD_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -070081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082}
83
84
85// ----------------------------------------------------------------------------
86static void recorderCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -070087
88 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
89 {
90 Mutex::Autolock l(sLock);
91 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
92 return;
93 }
94 callbackInfo->busy = true;
95 }
Glenn Kasten18db49a2012-03-12 16:29:55 -070096
Glenn Kasten5b1576c2013-07-18 16:58:19 -070097 switch (event) {
98 case AudioRecord::EVENT_MARKER: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700100 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700102 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 javaAudioRecordFields.postNativeEventInJava,
104 callbackInfo->audioRecord_ref, event, 0,0, NULL);
105 if (env->ExceptionCheck()) {
106 env->ExceptionDescribe();
107 env->ExceptionClear();
108 }
109 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700110 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700112 case AudioRecord::EVENT_NEW_POS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700114 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700116 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 javaAudioRecordFields.postNativeEventInJava,
118 callbackInfo->audioRecord_ref, event, 0,0, NULL);
119 if (env->ExceptionCheck()) {
120 env->ExceptionDescribe();
121 env->ExceptionClear();
122 }
123 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700124 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700126
Eric Laurent532bc1c2012-04-20 12:45:03 -0700127 {
128 Mutex::Autolock l(sLock);
129 callbackInfo->busy = false;
130 callbackInfo->cond.broadcast();
131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132}
133
Eric Laurent532bc1c2012-04-20 12:45:03 -0700134// ----------------------------------------------------------------------------
135static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
136{
137 Mutex::Autolock l(sLock);
138 AudioRecord* const ar =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000139 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700140 return sp<AudioRecord>(ar);
141}
142
143static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
144{
145 Mutex::Autolock l(sLock);
146 sp<AudioRecord> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000147 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700148 if (ar.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800149 ar->incStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700150 }
151 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800152 old->decStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700153 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000154 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700155 return old;
156}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157
158// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000159static jint
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Glenn Kasten33c437d2013-07-18 17:04:21 -0700161 jint source, jint sampleRateInHertz, jint channelMask,
162 // Java channel masks map directly to the native definition
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700163 jint audioFormat, jint buffSizeInBytes, jintArray jSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164{
Steve Block71f2cf12011-10-20 11:56:00 +0100165 //ALOGV(">> Entering android_media_AudioRecord_setup");
Glenn Kasten33c437d2013-07-18 17:04:21 -0700166 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
167 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168
Glenn Kasten33c437d2013-07-18 17:04:21 -0700169 if (!audio_is_input_channel(channelMask)) {
170 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000171 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
Glenn Kasten33c437d2013-07-18 17:04:21 -0700173 uint32_t nbChannels = popcount(channelMask);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
175 // compare the format against the Java constants
Glenn Kasten9afc78d72013-08-01 07:26:04 -0700176 if ((audioFormat != ENCODING_PCM_16BIT)
177 && (audioFormat != ENCODING_PCM_8BIT)) {
Steve Block3762c312012-01-06 19:20:56 +0000178 ALOGE("Error creating AudioRecord: unsupported audio format.");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000179 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 }
181
Glenn Kasten9afc78d72013-08-01 07:26:04 -0700182 int bytesPerSample = audioFormat == ENCODING_PCM_16BIT ? 2 : 1;
183 audio_format_t format = audioFormat == ENCODING_PCM_16BIT ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700184 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
186 if (buffSizeInBytes == 0) {
Steve Block3762c312012-01-06 19:20:56 +0000187 ALOGE("Error creating AudioRecord: frameCount is 0.");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000188 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 }
190 int frameSize = nbChannels * bytesPerSample;
191 size_t frameCount = buffSizeInBytes / frameSize;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700192
Eric Laurent357263d2013-09-09 10:31:59 -0700193 if ((uint32_t(source) >= AUDIO_SOURCE_CNT) && (uint32_t(source) != AUDIO_SOURCE_HOTWORD)) {
Steve Block3762c312012-01-06 19:20:56 +0000194 ALOGE("Error creating AudioRecord: unknown source.");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000195 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 }
Eric Laurent4bc035a2009-05-22 09:18:15 -0700197
Eric Laurent532bc1c2012-04-20 12:45:03 -0700198 jclass clazz = env->GetObjectClass(thiz);
199 if (clazz == NULL) {
200 ALOGE("Can't find %s when setting up callback.", kClassPathName);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000201 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
Eric Laurent532bc1c2012-04-20 12:45:03 -0700202 }
203
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700204 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000205 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000206 return (jint) AUDIORECORD_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700207 }
208
209 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
210 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000211 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000212 return (jint) AUDIORECORD_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700213 }
214 int sessionId = nSession[0];
215 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
216 nSession = NULL;
217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 // create an uninitialized AudioRecord object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700219 sp<AudioRecord> lpRecorder = new AudioRecord();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 // create the callback information:
222 // this data will be passed with every AudioRecord callback
Eric Laurent532bc1c2012-04-20 12:45:03 -0700223 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
225 // we use a weak reference so the AudioRecord object can be garbage collected.
226 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700227 lpCallbackData->busy = false;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700228
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800229 lpRecorder->set((audio_source_t) source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 sampleRateInHertz,
231 format, // word length, PCM
Glenn Kasten33c437d2013-07-18 17:04:21 -0700232 channelMask,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 frameCount,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 recorderCallback,// callback_t
235 lpCallbackData,// void* user
236 0, // notificationFrames,
Glenn Kasten86fad472012-06-21 16:21:36 -0700237 true, // threadCanCallJava
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700238 sessionId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239
Glenn Kasten18db49a2012-03-12 16:29:55 -0700240 if (lpRecorder->initCheck() != NO_ERROR) {
Steve Block3762c312012-01-06 19:20:56 +0000241 ALOGE("Error creating AudioRecord instance: initialization check failed.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 goto native_init_failure;
243 }
244
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700245 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
246 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000247 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700248 goto native_init_failure;
249 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800250 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700251 nSession[0] = lpRecorder->getSessionId();
252 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
253 nSession = NULL;
254
Eric Laurent532bc1c2012-04-20 12:45:03 -0700255 { // scope for the lock
256 Mutex::Autolock l(sLock);
257 sAudioRecordCallBackCookies.add(lpCallbackData);
258 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700259 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700261 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 // save our newly created callback information in the "nativeCallbackCookie" field
264 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
Ashok Bhat075e9a12014-01-06 13:45:09 +0000265 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700266
Ashok Bhat075e9a12014-01-06 13:45:09 +0000267 return (jint) AUDIORECORD_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 // failure:
270native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700271 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
272 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 delete lpCallbackData;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000274 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700275
Ashok Bhat075e9a12014-01-06 13:45:09 +0000276 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277}
278
279
280
281// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000282static jint
Eric Laurent505e5c82012-03-29 15:19:36 -0700283android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700285 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 if (lpRecorder == NULL ) {
287 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000288 return (jint) AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700290
Ashok Bhat075e9a12014-01-06 13:45:09 +0000291 return (jint) android_media_translateRecorderErrorCode(
Eric Laurent505e5c82012-03-29 15:19:36 -0700292 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293}
294
295
296// ----------------------------------------------------------------------------
297static void
298android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
299{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700300 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 if (lpRecorder == NULL ) {
302 jniThrowException(env, "java/lang/IllegalStateException", NULL);
303 return;
304 }
305
306 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100307 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308}
309
310
311// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800312
Eric Laurent532bc1c2012-04-20 12:45:03 -0700313#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
314static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
315 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
316 if (lpRecorder == NULL) {
317 return;
318 }
319 ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
320 lpRecorder->stop();
321
Ashok Bhat075e9a12014-01-06 13:45:09 +0000322 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
Dave Sparkse6335c92010-03-13 17:08:22 -0800323 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324
Dave Sparkse6335c92010-03-13 17:08:22 -0800325 // reset the native resources in the Java object so any attempt to access
326 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000327 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Dave Sparkse6335c92010-03-13 17:08:22 -0800328
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700331 Mutex::Autolock l(sLock);
Steve Block71f2cf12011-10-20 11:56:00 +0100332 ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700333 while (lpCookie->busy) {
334 if (lpCookie->cond.waitRelative(sLock,
335 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
336 NO_ERROR) {
337 break;
338 }
339 }
340 sAudioRecordCallBackCookies.remove(lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700341 env->DeleteGlobalRef(lpCookie->audioRecord_class);
342 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 delete lpCookie;
344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345}
346
347
348// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800349static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
350 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351}
352
353
354// ----------------------------------------------------------------------------
355static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
356 jbyteArray javaAudioData,
357 jint offsetInBytes, jint sizeInBytes) {
358 jbyte* recordBuff = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700360 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 if (lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000362 ALOGE("Unable to retrieve AudioRecord object, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 return 0;
364 }
365
366 if (!javaAudioData) {
Steve Block3762c312012-01-06 19:20:56 +0000367 ALOGE("Invalid Java array to store recorded audio, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 return 0;
369 }
370
371 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800372 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
373 // a way that it becomes much more efficient. When doing so, we will have to prevent the
374 // AudioSystem callback to be called while in critical section (in case of media server
375 // process crash for instance)
376 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377
378 if (recordBuff == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000379 ALOGE("Error retrieving destination for recorded audio data, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 return 0;
381 }
382
383 // read the new audio data from the native AudioRecord object
384 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700385 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
386 sizeInBytes > (jint)recorderBuffSize ?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800388 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
Eric Laurent357263d2013-09-09 10:31:59 -0700390 if (readSize < 0) {
391 readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
392 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 return (jint) readSize;
394}
395
396// ----------------------------------------------------------------------------
397static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
398 jshortArray javaAudioData,
399 jint offsetInShorts, jint sizeInShorts) {
400
Glenn Kasten3f0af8b2013-07-18 17:00:10 -0700401 jint read = android_media_AudioRecord_readInByteArray(env, thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 (jbyteArray) javaAudioData,
Glenn Kasten3f0af8b2013-07-18 17:00:10 -0700403 offsetInShorts*2, sizeInShorts*2);
404 if (read > 0) {
405 read /= 2;
406 }
407 return read;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408}
409
410// ----------------------------------------------------------------------------
411static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
412 jobject jBuffer, jint sizeInBytes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700414 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700415 if (lpRecorder==NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 return 0;
417
418 // direct buffer and direct access supported?
419 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700420 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000422 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 return 0;
424 }
Steve Block71f2cf12011-10-20 11:56:00 +0100425 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700427 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000428 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 return 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700430 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431
432 // read new data from the recorder
Eric Laurent357263d2013-09-09 10:31:59 -0700433 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 capacity < sizeInBytes ? capacity : sizeInBytes);
Eric Laurent357263d2013-09-09 10:31:59 -0700435 if (readSize < 0) {
436 readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
437 }
438 return (jint)readSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439}
440
441
442// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700443static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700445
Eric Laurent532bc1c2012-04-20 12:45:03 -0700446 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
447 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 jniThrowException(env, "java/lang/IllegalStateException",
449 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
450 return AUDIORECORD_ERROR;
451 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700452 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453}
454
455
456// ----------------------------------------------------------------------------
457static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700458
Eric Laurent532bc1c2012-04-20 12:45:03 -0700459 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700461
Eric Laurent532bc1c2012-04-20 12:45:03 -0700462 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 jniThrowException(env, "java/lang/IllegalStateException",
464 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
465 return AUDIORECORD_ERROR;
466 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700467 lpRecorder->getMarkerPosition(&markerPos);
468 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469}
470
471
472// ----------------------------------------------------------------------------
473static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
474 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700475
Eric Laurent532bc1c2012-04-20 12:45:03 -0700476 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700477
Eric Laurent532bc1c2012-04-20 12:45:03 -0700478 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 jniThrowException(env, "java/lang/IllegalStateException",
480 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
481 return AUDIORECORD_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700482 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700483 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484}
485
486
487// ----------------------------------------------------------------------------
488static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700489
Eric Laurent532bc1c2012-04-20 12:45:03 -0700490 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700492
Eric Laurent532bc1c2012-04-20 12:45:03 -0700493 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 jniThrowException(env, "java/lang/IllegalStateException",
495 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
496 return AUDIORECORD_ERROR;
497 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700498 lpRecorder->getPositionUpdatePeriod(&period);
499 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500}
501
502
503// ----------------------------------------------------------------------------
504// returns the minimum required size for the successful creation of an AudioRecord instance.
505// returns 0 if the parameter combination is not supported.
506// return -1 if there was an error querying the buffer size.
507static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
508 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800509
Eric Laurent532bc1c2012-04-20 12:45:03 -0700510 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
511 sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800512
Glenn Kastenfd1e3df2012-11-13 15:21:06 -0800513 size_t frameCount = 0;
Chia-chi Yehc3308072010-08-19 17:14:36 +0800514 status_t result = AudioRecord::getMinFrameCount(&frameCount,
515 sampleRateInHertz,
Glenn Kasten9afc78d72013-08-01 07:26:04 -0700516 (audioFormat == ENCODING_PCM_16BIT ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700517 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
Glenn Kasten845b4712012-07-02 13:12:31 -0700518 audio_channel_in_mask_from_count(nbChannels));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800519
520 if (result == BAD_VALUE) {
521 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800523 if (result != NO_ERROR) {
524 return -1;
525 }
Glenn Kasten9afc78d72013-08-01 07:26:04 -0700526 return frameCount * nbChannels * (audioFormat == ENCODING_PCM_16BIT ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527}
528
529
530// ----------------------------------------------------------------------------
531// ----------------------------------------------------------------------------
532static JNINativeMethod gMethods[] = {
533 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700534 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700536 {"native_setup", "(Ljava/lang/Object;IIIII[I)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 (void *)android_media_AudioRecord_setup},
538 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
539 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700540 {"native_read_in_byte_array",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
542 {"native_read_in_short_array",
543 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
544 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
545 (void *)android_media_AudioRecord_readInDirectBuffer},
546 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
547 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
548 {"native_set_pos_update_period",
549 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
550 {"native_get_pos_update_period",
551 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
552 {"native_get_min_buff_size",
553 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
554};
555
556// field names found in android/media/AudioRecord.java
557#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
559#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561// ----------------------------------------------------------------------------
562int register_android_media_AudioRecord(JNIEnv *env)
563{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 javaAudioRecordFields.postNativeEventInJava = NULL;
565 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
566 javaAudioRecordFields.nativeCallbackCookie = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568
569 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700570 jclass audioRecordClass = env->FindClass(kClassPathName);
571 if (audioRecordClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000572 ALOGE("Can't find %s", kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 return -1;
574 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // Get the postEvent method
576 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700577 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
579 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000580 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 return -1;
582 }
583
584 // Get the variables
585 // mNativeRecorderInJavaObj
Glenn Kasten18db49a2012-03-12 16:29:55 -0700586 javaAudioRecordFields.nativeRecorderInJavaObj =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700587 env->GetFieldID(audioRecordClass,
Ashok Bhat075e9a12014-01-06 13:45:09 +0000588 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000590 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 return -1;
592 }
593 // mNativeCallbackCookie
594 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700595 audioRecordClass,
Ashok Bhat075e9a12014-01-06 13:45:09 +0000596 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000598 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 return -1;
600 }
601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 return AndroidRuntime::registerNativeMethods(env,
603 kClassPathName, gMethods, NELEM(gMethods));
604}
605
606// ----------------------------------------------------------------------------