/*---------------------------------------------------------------------------*
* android_speech_srec_MicrophoneInputStream.cpp *
* *
* Copyright 2007 Nuance Communciations, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the 'License'); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an 'AS IS' BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*---------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define LOG_TAG "srec_jni"
#include <utils/Log.h>
#include <media/AudioRecord.h>
#include <media/mediarecorder.h>
#include <system/audio.h>
#include <jni.h>
using namespace android;
//
// helper function to throw an exception
//
static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
if (jclass cls = env->FindClass(ex)) {
char msg[1000];
sprintf(msg, fmt, data);
env->ThrowNew(cls, msg);
env->DeleteLocalRef(cls);
}
}
///////////////////////////////////////////////////////////////////////////////
// MicrophoneInputStream JNI implememtations
///////////////////////////////////////////////////////////////////////////////
// Java uses an int to hold a raw pointer, which is already ugly.
// But we need to hold an sp<>, which is of unknown size.
// So we wrap the sp<> within a class, and give Java the int version of a pointer to this class.
class AudioRecordWrapper {
public:
AudioRecordWrapper(AudioRecord *audioRecord) : mAudioRecord(audioRecord) { }
~AudioRecordWrapper() { }
AudioRecord* get() const { return mAudioRecord.get(); }
private:
const sp<AudioRecord> mAudioRecord;
};
static JNIEXPORT jlong JNICALL Java_android_speech_srec_Recognizer_AudioRecordNew
(JNIEnv *env, jclass clazz, jint sampleRate, jint fifoFrames) {
AudioRecordWrapper *ar = new AudioRecordWrapper(new AudioRecord(
AUDIO_SOURCE_VOICE_RECOGNITION, sampleRate,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO,
fifoFrames));
status_t s = ar->get()->initCheck();
if (s != NO_ERROR) {
delete ar;
ar = NULL;
ALOGE("initCheck error %d ", s);
}
return (jlong)ar;
}
static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordStart
(JNIEnv *env, jclass clazz, jlong audioRecord) {
return (jint)(((AudioRecordWrapper*)audioRecord)->get()->start());
}
static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordRead
(JNIEnv *env, jclass clazz, jlong audioRecord, jbyteArray array, jint offset, jint length) {
jbyte buffer[4096];
if (length > (int)sizeof(buffer)) length = sizeof(buffer);
length = ((AudioRecordWrapper*)audioRecord)->get()->read(buffer, length);
if (length < 0) {
throwException(env, "java/io/IOException", "AudioRecord::read failed %d", length);
return -1;
}
env->SetByteArrayRegion(array, offset, length, buffer);
return length;
}
static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordStop
(JNIEnv *env, jclass clazz, jlong audioRecord) {
((AudioRecordWrapper*)audioRecord)->get()->stop();
}
static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordDelete
(JNIEnv *env, jclass clazz, jlong audioRecord) {
delete (AudioRecordWrapper*)audioRecord;
}
/*
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"AudioRecordNew", "(II)J", (void*)Java_android_speech_srec_Recognizer_AudioRecordNew},
{"AudioRecordStart", "(J)I", (void*)Java_android_speech_srec_Recognizer_AudioRecordStart},
{"AudioRecordRead", "(J[BII)I", (void*)Java_android_speech_srec_Recognizer_AudioRecordRead},
{"AudioRecordStop", "(J)V", (void*)Java_android_speech_srec_Recognizer_AudioRecordStop},
{"AudioRecordDelete", "(J)V", (void*)Java_android_speech_srec_Recognizer_AudioRecordDelete},
};
/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
jint register_android_speech_srec_MicrophoneInputStream(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jclass clazz = NULL;
const char* className = "android/speech/srec/MicrophoneInputStream";
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
return -1;
}
assert(env != NULL);
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'\n", className);
return -1;
}
if (env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
ALOGE("RegisterNatives failed for '%s'\n", className);
return -1;
}
/* success -- return valid version number */
return JNI_VERSION_1_4;;
}