android_media_SoundPool.cpp revision bc1d77b6cbce23fbe25f7231651037ae195bc90e
1/*
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#include <stdio.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "SoundPool-JNI"
21
22#include <utils/Log.h>
23#include <nativehelper/jni.h>
24#include <nativehelper/JNIHelp.h>
25#include <android_runtime/AndroidRuntime.h>
26#include "SoundPool.h"
27
28using namespace android;
29
30static struct fields_t {
31    jfieldID    mNativeContext;
32    jmethodID   mPostEvent;
33    jclass      mSoundPoolClass;
34} fields;
35
36static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
37    return (SoundPool*)env->GetIntField(thiz, fields.mNativeContext);
38}
39
40// ----------------------------------------------------------------------------
41static int
42android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
43{
44    ALOGV("android_media_SoundPool_load_URL");
45    SoundPool *ap = MusterSoundPool(env, thiz);
46    if (path == NULL) {
47        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
48        return 0;
49    }
50    const char* s = env->GetStringUTFChars(path, NULL);
51    int id = ap->load(s, priority);
52    env->ReleaseStringUTFChars(path, s);
53    return id;
54}
55
56static int
57android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
58        jlong offset, jlong length, jint priority)
59{
60    ALOGV("android_media_SoundPool_load_FD");
61    SoundPool *ap = MusterSoundPool(env, thiz);
62    if (ap == NULL) return 0;
63    return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
64            int64_t(offset), int64_t(length), int(priority));
65}
66
67static bool
68android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
69    ALOGV("android_media_SoundPool_unload\n");
70    SoundPool *ap = MusterSoundPool(env, thiz);
71    if (ap == NULL) return 0;
72    return ap->unload(sampleID);
73}
74
75static int
76android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
77        jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
78        jfloat rate)
79{
80    ALOGV("android_media_SoundPool_play\n");
81    SoundPool *ap = MusterSoundPool(env, thiz);
82    if (ap == NULL) return 0;
83    return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
84}
85
86static void
87android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
88{
89    ALOGV("android_media_SoundPool_pause");
90    SoundPool *ap = MusterSoundPool(env, thiz);
91    if (ap == NULL) return;
92    ap->pause(channelID);
93}
94
95static void
96android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
97{
98    ALOGV("android_media_SoundPool_resume");
99    SoundPool *ap = MusterSoundPool(env, thiz);
100    if (ap == NULL) return;
101    ap->resume(channelID);
102}
103
104static void
105android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
106{
107    ALOGV("android_media_SoundPool_autoPause");
108    SoundPool *ap = MusterSoundPool(env, thiz);
109    if (ap == NULL) return;
110    ap->autoPause();
111}
112
113static void
114android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
115{
116    ALOGV("android_media_SoundPool_autoResume");
117    SoundPool *ap = MusterSoundPool(env, thiz);
118    if (ap == NULL) return;
119    ap->autoResume();
120}
121
122static void
123android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
124{
125    ALOGV("android_media_SoundPool_stop");
126    SoundPool *ap = MusterSoundPool(env, thiz);
127    if (ap == NULL) return;
128    ap->stop(channelID);
129}
130
131static void
132android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
133        float leftVolume, float rightVolume)
134{
135    ALOGV("android_media_SoundPool_setVolume");
136    SoundPool *ap = MusterSoundPool(env, thiz);
137    if (ap == NULL) return;
138    ap->setVolume(channelID, leftVolume, rightVolume);
139}
140
141static void
142android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
143        int priority)
144{
145    ALOGV("android_media_SoundPool_setPriority");
146    SoundPool *ap = MusterSoundPool(env, thiz);
147    if (ap == NULL) return;
148    ap->setPriority(channelID, priority);
149}
150
151static void
152android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
153        int loop)
154{
155    ALOGV("android_media_SoundPool_setLoop");
156    SoundPool *ap = MusterSoundPool(env, thiz);
157    if (ap == NULL) return;
158    ap->setLoop(channelID, loop);
159}
160
161static void
162android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
163        float rate)
164{
165    ALOGV("android_media_SoundPool_setRate");
166    SoundPool *ap = MusterSoundPool(env, thiz);
167    if (ap == NULL) return;
168    ap->setRate(channelID, rate);
169}
170
171static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
172{
173    ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
174    JNIEnv *env = AndroidRuntime::getJNIEnv();
175    env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
176}
177
178static jint
179android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
180{
181    ALOGV("android_media_SoundPool_native_setup");
182    SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
183    if (ap == NULL) {
184        return -1;
185    }
186
187    // save pointer to SoundPool C++ object in opaque field in Java object
188    env->SetIntField(thiz, fields.mNativeContext, (int)ap);
189
190    // set callback with weak reference
191    jobject globalWeakRef = env->NewGlobalRef(weakRef);
192    ap->setCallback(android_media_callback, globalWeakRef);
193    return 0;
194}
195
196static void
197android_media_SoundPool_release(JNIEnv *env, jobject thiz)
198{
199    ALOGV("android_media_SoundPool_release");
200    SoundPool *ap = MusterSoundPool(env, thiz);
201    if (ap != NULL) {
202
203        // release weak reference
204        jobject weakRef = (jobject) ap->getUserData();
205        if (weakRef != NULL) {
206            env->DeleteGlobalRef(weakRef);
207        }
208
209        // clear callback and native context
210        ap->setCallback(NULL, NULL);
211        env->SetIntField(thiz, fields.mNativeContext, 0);
212        delete ap;
213    }
214}
215
216// ----------------------------------------------------------------------------
217
218// Dalvik VM type signatures
219static JNINativeMethod gMethods[] = {
220    {   "_load",
221        "(Ljava/lang/String;I)I",
222        (void *)android_media_SoundPool_load_URL
223    },
224    {   "_load",
225        "(Ljava/io/FileDescriptor;JJI)I",
226        (void *)android_media_SoundPool_load_FD
227    },
228    {   "unload",
229        "(I)Z",
230        (void *)android_media_SoundPool_unload
231    },
232    {   "play",
233        "(IFFIIF)I",
234        (void *)android_media_SoundPool_play
235    },
236    {   "pause",
237        "(I)V",
238        (void *)android_media_SoundPool_pause
239    },
240    {   "resume",
241        "(I)V",
242        (void *)android_media_SoundPool_resume
243    },
244    {   "autoPause",
245        "()V",
246        (void *)android_media_SoundPool_autoPause
247    },
248    {   "autoResume",
249        "()V",
250        (void *)android_media_SoundPool_autoResume
251    },
252    {   "stop",
253        "(I)V",
254        (void *)android_media_SoundPool_stop
255    },
256    {   "setVolume",
257        "(IFF)V",
258        (void *)android_media_SoundPool_setVolume
259    },
260    {   "setPriority",
261        "(II)V",
262        (void *)android_media_SoundPool_setPriority
263    },
264    {   "setLoop",
265        "(II)V",
266        (void *)android_media_SoundPool_setLoop
267    },
268    {   "setRate",
269        "(IF)V",
270        (void *)android_media_SoundPool_setRate
271    },
272    {   "native_setup",
273        "(Ljava/lang/Object;III)I",
274        (void*)android_media_SoundPool_native_setup
275    },
276    {   "release",
277        "()V",
278        (void*)android_media_SoundPool_release
279    }
280};
281
282static const char* const kClassPathName = "android/media/SoundPool";
283
284jint JNI_OnLoad(JavaVM* vm, void* reserved)
285{
286    JNIEnv* env = NULL;
287    jint result = -1;
288    jclass clazz;
289
290    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
291        ALOGE("ERROR: GetEnv failed\n");
292        goto bail;
293    }
294    assert(env != NULL);
295
296    clazz = env->FindClass(kClassPathName);
297    if (clazz == NULL) {
298        ALOGE("Can't find %s", kClassPathName);
299        goto bail;
300    }
301
302    fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
303    if (fields.mNativeContext == NULL) {
304        ALOGE("Can't find SoundPool.mNativeContext");
305        goto bail;
306    }
307
308    fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
309                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
310    if (fields.mPostEvent == NULL) {
311        ALOGE("Can't find android/media/SoundPool.postEventFromNative");
312        goto bail;
313    }
314
315    // create a reference to class. Technically, we're leaking this reference
316    // since it's a static object.
317    fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
318
319    if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
320        goto bail;
321
322    /* success -- return valid version number */
323    result = JNI_VERSION_1_4;
324
325bail:
326    return result;
327}
328