blob: c76cb6447857973a24c87e67a4e649cca1489994 [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()) {
157 ar->incStrong(thiz);
158 }
159 if (old != 0) {
160 old->decStrong(thiz);
161 }
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();
227 if (lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000228 ALOGE("Error creating AudioRecord instance.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
230 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 // create the callback information:
233 // this data will be passed with every AudioRecord callback
Eric Laurent532bc1c2012-04-20 12:45:03 -0700234 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
236 // we use a weak reference so the AudioRecord object can be garbage collected.
237 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700238 lpCallbackData->busy = false;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700239
Glenn Kasten0f0fbd92012-01-23 13:58:49 -0800240 lpRecorder->set((audio_source_t) source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 sampleRateInHertz,
242 format, // word length, PCM
Eric Laurenta553c252009-07-17 12:17:14 -0700243 channels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 frameCount,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 recorderCallback,// callback_t
246 lpCallbackData,// void* user
247 0, // notificationFrames,
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700248 true, // threadCanCallJava)
249 sessionId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250
Glenn Kasten18db49a2012-03-12 16:29:55 -0700251 if (lpRecorder->initCheck() != NO_ERROR) {
Steve Block3762c312012-01-06 19:20:56 +0000252 ALOGE("Error creating AudioRecord instance: initialization check failed.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 goto native_init_failure;
254 }
255
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700256 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
257 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000258 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700259 goto native_init_failure;
260 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800261 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700262 nSession[0] = lpRecorder->getSessionId();
263 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
264 nSession = NULL;
265
Eric Laurent532bc1c2012-04-20 12:45:03 -0700266 { // scope for the lock
267 Mutex::Autolock l(sLock);
268 sAudioRecordCallBackCookies.add(lpCallbackData);
269 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700270 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700272 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700273
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 // save our newly created callback information in the "nativeCallbackCookie" field
275 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
276 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 return AUDIORECORD_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700279
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 // failure:
281native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700282 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
283 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 delete lpCallbackData;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700286
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
288}
289
290
291
292// ----------------------------------------------------------------------------
Eric Laurent9cc489a2009-12-05 05:20:01 -0800293static int
Eric Laurent505e5c82012-03-29 15:19:36 -0700294android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700296 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 if (lpRecorder == NULL ) {
298 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurent9cc489a2009-12-05 05:20:01 -0800299 return AUDIORECORD_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700301
Eric Laurent505e5c82012-03-29 15:19:36 -0700302 return android_media_translateRecorderErrorCode(
303 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304}
305
306
307// ----------------------------------------------------------------------------
308static void
309android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
310{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700311 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 if (lpRecorder == NULL ) {
313 jniThrowException(env, "java/lang/IllegalStateException", NULL);
314 return;
315 }
316
317 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100318 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319}
320
321
322// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800323
Eric Laurent532bc1c2012-04-20 12:45:03 -0700324#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
325static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
326 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
327 if (lpRecorder == NULL) {
328 return;
329 }
330 ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
331 lpRecorder->stop();
332
Dave Sparkse6335c92010-03-13 17:08:22 -0800333 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
334 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335
Dave Sparkse6335c92010-03-13 17:08:22 -0800336 // reset the native resources in the Java object so any attempt to access
337 // them after a call to release fails.
Dave Sparkse6335c92010-03-13 17:08:22 -0800338 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700342 Mutex::Autolock l(sLock);
Steve Block71f2cf12011-10-20 11:56:00 +0100343 ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700344 while (lpCookie->busy) {
345 if (lpCookie->cond.waitRelative(sLock,
346 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
347 NO_ERROR) {
348 break;
349 }
350 }
351 sAudioRecordCallBackCookies.remove(lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700352 env->DeleteGlobalRef(lpCookie->audioRecord_class);
353 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 delete lpCookie;
355 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356}
357
358
359// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800360static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
361 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362}
363
364
365// ----------------------------------------------------------------------------
366static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz,
367 jbyteArray javaAudioData,
368 jint offsetInBytes, jint sizeInBytes) {
369 jbyte* recordBuff = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700371 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 if (lpRecorder == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000373 ALOGE("Unable to retrieve AudioRecord object, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 return 0;
375 }
376
377 if (!javaAudioData) {
Steve Block3762c312012-01-06 19:20:56 +0000378 ALOGE("Invalid Java array to store recorded audio, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 return 0;
380 }
381
382 // get the pointer to where we'll record the audio
Eric Laurent421ddc02011-03-07 14:52:59 -0800383 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
384 // a way that it becomes much more efficient. When doing so, we will have to prevent the
385 // AudioSystem callback to be called while in critical section (in case of media server
386 // process crash for instance)
387 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388
389 if (recordBuff == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000390 ALOGE("Error retrieving destination for recorded audio data, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 return 0;
392 }
393
394 // read the new audio data from the native AudioRecord object
395 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700396 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
397 sizeInBytes > (jint)recorderBuffSize ?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 (jint)recorderBuffSize : sizeInBytes );
Eric Laurent421ddc02011-03-07 14:52:59 -0800399 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
401 return (jint) readSize;
402}
403
404// ----------------------------------------------------------------------------
405static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz,
406 jshortArray javaAudioData,
407 jint offsetInShorts, jint sizeInShorts) {
408
409 return (android_media_AudioRecord_readInByteArray(env, thiz,
410 (jbyteArray) javaAudioData,
411 offsetInShorts*2, sizeInShorts*2)
412 / 2);
413}
414
415// ----------------------------------------------------------------------------
416static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
417 jobject jBuffer, jint sizeInBytes) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700419 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700420 if (lpRecorder==NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 return 0;
422
423 // direct buffer and direct access supported?
424 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700425 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000427 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 return 0;
429 }
Steve Block71f2cf12011-10-20 11:56:00 +0100430 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700432 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000433 ALOGE("Buffer direct access is not supported, can't record");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 return 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700435 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436
437 // read new data from the recorder
Glenn Kasten18db49a2012-03-12 16:29:55 -0700438 return (jint) lpRecorder->read(nativeFromJavaBuf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 capacity < sizeInBytes ? capacity : sizeInBytes);
440}
441
442
443// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700444static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700446
Eric Laurent532bc1c2012-04-20 12:45:03 -0700447 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
448 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 jniThrowException(env, "java/lang/IllegalStateException",
450 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
451 return AUDIORECORD_ERROR;
452 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700453 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454}
455
456
457// ----------------------------------------------------------------------------
458static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700459
Eric Laurent532bc1c2012-04-20 12:45:03 -0700460 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700462
Eric Laurent532bc1c2012-04-20 12:45:03 -0700463 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 jniThrowException(env, "java/lang/IllegalStateException",
465 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
466 return AUDIORECORD_ERROR;
467 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700468 lpRecorder->getMarkerPosition(&markerPos);
469 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470}
471
472
473// ----------------------------------------------------------------------------
474static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
475 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700476
Eric Laurent532bc1c2012-04-20 12:45:03 -0700477 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700478
Eric Laurent532bc1c2012-04-20 12:45:03 -0700479 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 jniThrowException(env, "java/lang/IllegalStateException",
481 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
482 return AUDIORECORD_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700483 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700484 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485}
486
487
488// ----------------------------------------------------------------------------
489static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700490
Eric Laurent532bc1c2012-04-20 12:45:03 -0700491 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700493
Eric Laurent532bc1c2012-04-20 12:45:03 -0700494 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 jniThrowException(env, "java/lang/IllegalStateException",
496 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
497 return AUDIORECORD_ERROR;
498 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700499 lpRecorder->getPositionUpdatePeriod(&period);
500 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501}
502
503
504// ----------------------------------------------------------------------------
505// returns the minimum required size for the successful creation of an AudioRecord instance.
506// returns 0 if the parameter combination is not supported.
507// return -1 if there was an error querying the buffer size.
508static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
509 jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800510
Eric Laurent532bc1c2012-04-20 12:45:03 -0700511 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
512 sampleRateInHertz, nbChannels, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800513
514 int frameCount = 0;
515 status_t result = AudioRecord::getMinFrameCount(&frameCount,
516 sampleRateInHertz,
517 (audioFormat == javaAudioRecordFields.PCM16 ?
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700518 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
Glenn Kasten845b4712012-07-02 13:12:31 -0700519 audio_channel_in_mask_from_count(nbChannels));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800520
521 if (result == BAD_VALUE) {
522 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800524 if (result != NO_ERROR) {
525 return -1;
526 }
527 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528}
529
530
531// ----------------------------------------------------------------------------
532// ----------------------------------------------------------------------------
533static JNINativeMethod gMethods[] = {
534 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700535 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700537 {"native_setup", "(Ljava/lang/Object;IIIII[I)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 (void *)android_media_AudioRecord_setup},
539 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
540 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700541 {"native_read_in_byte_array",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
543 {"native_read_in_short_array",
544 "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
545 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
546 (void *)android_media_AudioRecord_readInDirectBuffer},
547 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
548 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
549 {"native_set_pos_update_period",
550 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
551 {"native_get_pos_update_period",
552 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
553 {"native_get_min_buff_size",
554 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
555};
556
557// field names found in android/media/AudioRecord.java
558#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
559#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT"
560#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
562#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
563
564#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
565
566// ----------------------------------------------------------------------------
567
Glenn Kasten18db49a2012-03-12 16:29:55 -0700568extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 jclass theClass, const char* className, const char* constName, int* constVal);
570
571// ----------------------------------------------------------------------------
572int register_android_media_AudioRecord(JNIEnv *env)
573{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 javaAudioRecordFields.postNativeEventInJava = NULL;
575 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
576 javaAudioRecordFields.nativeCallbackCookie = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578
579 // Get the AudioRecord class
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700580 jclass audioRecordClass = env->FindClass(kClassPathName);
581 if (audioRecordClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000582 ALOGE("Can't find %s", kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 return -1;
584 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 // Get the postEvent method
586 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700587 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
589 if (javaAudioRecordFields.postNativeEventInJava == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000590 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 return -1;
592 }
593
594 // Get the variables
595 // mNativeRecorderInJavaObj
Glenn Kasten18db49a2012-03-12 16:29:55 -0700596 javaAudioRecordFields.nativeRecorderInJavaObj =
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700597 env->GetFieldID(audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
599 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000600 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 return -1;
602 }
603 // mNativeCallbackCookie
604 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700605 audioRecordClass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
607 if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000608 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 return -1;
610 }
611
612 // Get the format constants from the AudioFormat class
613 jclass audioFormatClass = NULL;
614 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
615 if (audioFormatClass == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000616 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 return -1;
618 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700619 if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
620 JAVA_AUDIOFORMAT_CLASS_NAME,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
Glenn Kasten18db49a2012-03-12 16:29:55 -0700622 || !android_media_getIntConstantFromClass(env, audioFormatClass,
623 JAVA_AUDIOFORMAT_CLASS_NAME,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700625 // error log performed in getIntConstantFromClass()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 return -1;
627 }
628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 return AndroidRuntime::registerNativeMethods(env,
630 kClassPathName, gMethods, NELEM(gMethods));
631}
632
633// ----------------------------------------------------------------------------