android_media_SoundPool.cpp revision f6e43bf29084760b39257b2273e0f04c2815fdc5
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    LOGV("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    LOGV("android_media_SoundPool_load_FD");
61    SoundPool *ap = MusterSoundPool(env, thiz);
62    if (ap == NULL) return 0;
63    return ap->load(getParcelFileDescriptorFD(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    LOGV("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    LOGV("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    LOGV("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    LOGV("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_stop(JNIEnv *env, jobject thiz, jint channelID)
106{
107    LOGV("android_media_SoundPool_stop");
108    SoundPool *ap = MusterSoundPool(env, thiz);
109    if (ap == NULL) return;
110    ap->stop(channelID);
111}
112
113static void
114android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
115        float leftVolume, float rightVolume)
116{
117    LOGV("android_media_SoundPool_setVolume");
118    SoundPool *ap = MusterSoundPool(env, thiz);
119    if (ap == NULL) return;
120    ap->setVolume(channelID, leftVolume, rightVolume);
121}
122
123static void
124android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
125        int priority)
126{
127    LOGV("android_media_SoundPool_setPriority");
128    SoundPool *ap = MusterSoundPool(env, thiz);
129    if (ap == NULL) return;
130    ap->setPriority(channelID, priority);
131}
132
133static void
134android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
135        int loop)
136{
137    LOGV("android_media_SoundPool_setLoop");
138    SoundPool *ap = MusterSoundPool(env, thiz);
139    if (ap == NULL) return;
140    ap->setLoop(channelID, loop);
141}
142
143static void
144android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
145        float rate)
146{
147    LOGV("android_media_SoundPool_setRate");
148    SoundPool *ap = MusterSoundPool(env, thiz);
149    if (ap == NULL) return;
150    ap->setRate(channelID, rate);
151}
152
153static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
154{
155    LOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
156    JNIEnv *env = AndroidRuntime::getJNIEnv();
157    env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
158}
159
160static jint
161android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
162{
163    LOGV("android_media_SoundPool_native_setup");
164    SoundPool *ap = new SoundPool(maxChannels, streamType, srcQuality);
165    if (ap == NULL) {
166        return -1;
167    }
168
169    // save pointer to SoundPool C++ object in opaque field in Java object
170    env->SetIntField(thiz, fields.mNativeContext, (int)ap);
171
172    // set callback with weak reference
173    jobject globalWeakRef = env->NewGlobalRef(weakRef);
174    ap->setCallback(android_media_callback, globalWeakRef);
175    return 0;
176}
177
178static void
179android_media_SoundPool_release(JNIEnv *env, jobject thiz)
180{
181    LOGV("android_media_SoundPool_release");
182    SoundPool *ap = MusterSoundPool(env, thiz);
183    if (ap != NULL) {
184
185        // release weak reference
186        jobject weakRef = (jobject) ap->getUserData();
187        if (weakRef != NULL) {
188            env->DeleteGlobalRef(weakRef);
189        }
190
191        // clear callback and native context
192        ap->setCallback(NULL, NULL);
193        env->SetIntField(thiz, fields.mNativeContext, 0);
194        delete ap;
195    }
196}
197
198// ----------------------------------------------------------------------------
199
200// Dalvik VM type signatures
201static JNINativeMethod gMethods[] = {
202    {   "_load",
203        "(Ljava/lang/String;I)I",
204        (void *)android_media_SoundPool_load_URL
205    },
206    {   "_load",
207        "(Ljava/io/FileDescriptor;JJI)I",
208        (void *)android_media_SoundPool_load_FD
209    },
210    {   "unload",
211        "(I)Z",
212        (void *)android_media_SoundPool_unload
213    },
214    {   "play",
215        "(IFFIIF)I",
216        (void *)android_media_SoundPool_play
217    },
218    {   "pause",
219        "(I)V",
220        (void *)android_media_SoundPool_pause
221    },
222    {   "resume",
223        "(I)V",
224        (void *)android_media_SoundPool_resume
225    },
226    {   "stop",
227        "(I)V",
228        (void *)android_media_SoundPool_stop
229    },
230    {   "setVolume",
231        "(IFF)V",
232        (void *)android_media_SoundPool_setVolume
233    },
234    {   "setPriority",
235        "(II)V",
236        (void *)android_media_SoundPool_setPriority
237    },
238    {   "setLoop",
239        "(II)V",
240        (void *)android_media_SoundPool_setLoop
241    },
242    {   "setRate",
243        "(IF)V",
244        (void *)android_media_SoundPool_setRate
245    },
246    {   "native_setup",
247        "(Ljava/lang/Object;III)I",
248        (void*)android_media_SoundPool_native_setup
249    },
250    {   "release",
251        "()V",
252        (void*)android_media_SoundPool_release
253    }
254};
255
256static const char* const kClassPathName = "android/media/SoundPool";
257
258jint JNI_OnLoad(JavaVM* vm, void* reserved)
259{
260    JNIEnv* env = NULL;
261    jint result = -1;
262    jclass clazz;
263
264    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
265        LOGE("ERROR: GetEnv failed\n");
266        goto bail;
267    }
268    assert(env != NULL);
269
270    clazz = env->FindClass(kClassPathName);
271    if (clazz == NULL) {
272        LOGE("Can't find %s", kClassPathName);
273        goto bail;
274    }
275
276    fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
277    if (fields.mNativeContext == NULL) {
278        LOGE("Can't find SoundPool.mNativeContext");
279        goto bail;
280    }
281
282    fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
283                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
284    if (fields.mPostEvent == NULL) {
285        LOGE("Can't find android/media/SoundPool.postEventFromNative");
286        return -1;
287    }
288
289    if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
290        goto bail;
291
292    /* success -- return valid version number */
293    result = JNI_VERSION_1_4;
294
295bail:
296    return result;
297}
298