1/*---------------------------------------------------------------------------*
2 *  android_speech_srec_MicrophoneInputStream.cpp                            *
3 *                                                                           *
4 *  Copyright 2007 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20
21#include <string.h>
22#include <stdio.h>
23#include <stdlib.h>
24
25#define LOG_TAG "srec_jni"
26#include <utils/Log.h>
27
28#include <media/AudioRecord.h>
29#include <media/mediarecorder.h>
30
31#include <system/audio.h>
32
33#include <jni.h>
34
35using namespace android;
36
37
38
39//
40// helper function to throw an exception
41//
42static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
43    if (jclass cls = env->FindClass(ex)) {
44        char msg[1000];
45        sprintf(msg, fmt, data);
46        env->ThrowNew(cls, msg);
47        env->DeleteLocalRef(cls);
48    }
49}
50
51
52///////////////////////////////////////////////////////////////////////////////
53// MicrophoneInputStream JNI implememtations
54///////////////////////////////////////////////////////////////////////////////
55
56// Java uses an int to hold a raw pointer, which is already ugly.
57// But we need to hold an sp<>, which is of unknown size.
58// So we wrap the sp<> within a class, and give Java the int version of a pointer to this class.
59class AudioRecordWrapper {
60public:
61    AudioRecordWrapper(AudioRecord *audioRecord) : mAudioRecord(audioRecord) { }
62    ~AudioRecordWrapper() { }
63    AudioRecord* get() const { return mAudioRecord.get(); }
64private:
65    const sp<AudioRecord> mAudioRecord;
66};
67
68static JNIEXPORT jlong JNICALL Java_android_speech_srec_Recognizer_AudioRecordNew
69        (JNIEnv *env, jclass clazz, jint sampleRate, jint fifoFrames) {
70
71    AudioRecordWrapper *ar = new AudioRecordWrapper(new AudioRecord(
72            AUDIO_SOURCE_VOICE_RECOGNITION, sampleRate,
73            AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO,
74            fifoFrames));
75    status_t s = ar->get()->initCheck();
76    if (s != NO_ERROR) {
77        delete ar;
78        ar = NULL;
79        ALOGE("initCheck error %d ", s);
80    }
81    return (jlong)ar;
82}
83
84static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordStart
85        (JNIEnv *env, jclass clazz, jlong audioRecord) {
86    return (jint)(((AudioRecordWrapper*)audioRecord)->get()->start());
87}
88
89static JNIEXPORT jint JNICALL Java_android_speech_srec_Recognizer_AudioRecordRead
90        (JNIEnv *env, jclass clazz, jlong audioRecord, jbyteArray array, jint offset, jint length) {
91    jbyte buffer[4096];
92    if (length > (int)sizeof(buffer)) length = sizeof(buffer);
93    length = ((AudioRecordWrapper*)audioRecord)->get()->read(buffer, length);
94    if (length < 0) {
95        throwException(env, "java/io/IOException", "AudioRecord::read failed %d", length);
96        return -1;
97    }
98    env->SetByteArrayRegion(array, offset, length, buffer);
99    return length;
100}
101
102static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordStop
103        (JNIEnv *env, jclass clazz, jlong audioRecord) {
104    ((AudioRecordWrapper*)audioRecord)->get()->stop();
105}
106
107static JNIEXPORT void JNICALL Java_android_speech_srec_Recognizer_AudioRecordDelete
108        (JNIEnv *env, jclass clazz, jlong audioRecord) {
109    delete (AudioRecordWrapper*)audioRecord;
110}
111
112
113/*
114 * Table of methods associated with a single class.
115 */
116static JNINativeMethod gMethods[] = {
117    /* name, signature, funcPtr */
118    {"AudioRecordNew",    "(II)J",    (void*)Java_android_speech_srec_Recognizer_AudioRecordNew},
119    {"AudioRecordStart",  "(J)I",     (void*)Java_android_speech_srec_Recognizer_AudioRecordStart},
120    {"AudioRecordRead",   "(J[BII)I", (void*)Java_android_speech_srec_Recognizer_AudioRecordRead},
121    {"AudioRecordStop",   "(J)V",     (void*)Java_android_speech_srec_Recognizer_AudioRecordStop},
122    {"AudioRecordDelete", "(J)V",     (void*)Java_android_speech_srec_Recognizer_AudioRecordDelete},
123};
124
125/*
126 * Set some test stuff up.
127 *
128 * Returns the JNI version on success, -1 on failure.
129 */
130jint register_android_speech_srec_MicrophoneInputStream(JavaVM* vm, void* reserved)
131{
132    JNIEnv* env = NULL;
133    jclass clazz = NULL;
134    const char* className = "android/speech/srec/MicrophoneInputStream";
135
136    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
137        ALOGE("ERROR: GetEnv failed\n");
138        return -1;
139    }
140    assert(env != NULL);
141
142    clazz = env->FindClass(className);
143    if (clazz == NULL) {
144        ALOGE("Native registration unable to find class '%s'\n", className);
145        return -1;
146    }
147    if (env->RegisterNatives(clazz, gMethods,
148            sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
149        ALOGE("RegisterNatives failed for '%s'\n", className);
150        return -1;
151    }
152
153    /* success -- return valid version number */
154    return JNI_VERSION_1_4;;
155}
156