Home | History | Annotate | Download | only in soundpool
      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 
     28 using namespace android;
     29 
     30 static struct fields_t {
     31     jfieldID    mNativeContext;
     32     jmethodID   mPostEvent;
     33     jclass      mSoundPoolClass;
     34 } fields;
     35 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
     36     return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
     37 }
     38 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
     39 struct audio_attributes_fields_t {
     40     jfieldID  fieldUsage;        // AudioAttributes.mUsage
     41     jfieldID  fieldContentType;  // AudioAttributes.mContentType
     42     jfieldID  fieldFlags;        // AudioAttributes.mFlags
     43     jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
     44 };
     45 static audio_attributes_fields_t javaAudioAttrFields;
     46 
     47 // ----------------------------------------------------------------------------
     48 
     49 static jint
     50 android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
     51         jlong offset, jlong length, jint priority)
     52 {
     53     ALOGV("android_media_SoundPool_load_FD");
     54     SoundPool *ap = MusterSoundPool(env, thiz);
     55     if (ap == NULL) return 0;
     56     return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
     57             int64_t(offset), int64_t(length), int(priority));
     58 }
     59 
     60 static jboolean
     61 android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
     62     ALOGV("android_media_SoundPool_unload\n");
     63     SoundPool *ap = MusterSoundPool(env, thiz);
     64     if (ap == NULL) return JNI_FALSE;
     65     return ap->unload(sampleID) ? JNI_TRUE : JNI_FALSE;
     66 }
     67 
     68 static jint
     69 android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
     70         jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
     71         jfloat rate)
     72 {
     73     ALOGV("android_media_SoundPool_play\n");
     74     SoundPool *ap = MusterSoundPool(env, thiz);
     75     if (ap == NULL) return 0;
     76     return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
     77 }
     78 
     79 static void
     80 android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
     81 {
     82     ALOGV("android_media_SoundPool_pause");
     83     SoundPool *ap = MusterSoundPool(env, thiz);
     84     if (ap == NULL) return;
     85     ap->pause(channelID);
     86 }
     87 
     88 static void
     89 android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
     90 {
     91     ALOGV("android_media_SoundPool_resume");
     92     SoundPool *ap = MusterSoundPool(env, thiz);
     93     if (ap == NULL) return;
     94     ap->resume(channelID);
     95 }
     96 
     97 static void
     98 android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
     99 {
    100     ALOGV("android_media_SoundPool_autoPause");
    101     SoundPool *ap = MusterSoundPool(env, thiz);
    102     if (ap == NULL) return;
    103     ap->autoPause();
    104 }
    105 
    106 static void
    107 android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
    108 {
    109     ALOGV("android_media_SoundPool_autoResume");
    110     SoundPool *ap = MusterSoundPool(env, thiz);
    111     if (ap == NULL) return;
    112     ap->autoResume();
    113 }
    114 
    115 static void
    116 android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
    117 {
    118     ALOGV("android_media_SoundPool_stop");
    119     SoundPool *ap = MusterSoundPool(env, thiz);
    120     if (ap == NULL) return;
    121     ap->stop(channelID);
    122 }
    123 
    124 static void
    125 android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
    126         jfloat leftVolume, jfloat rightVolume)
    127 {
    128     ALOGV("android_media_SoundPool_setVolume");
    129     SoundPool *ap = MusterSoundPool(env, thiz);
    130     if (ap == NULL) return;
    131     ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
    132 }
    133 
    134 static void
    135 android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
    136         jint priority)
    137 {
    138     ALOGV("android_media_SoundPool_setPriority");
    139     SoundPool *ap = MusterSoundPool(env, thiz);
    140     if (ap == NULL) return;
    141     ap->setPriority(channelID, (int) priority);
    142 }
    143 
    144 static void
    145 android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
    146         int loop)
    147 {
    148     ALOGV("android_media_SoundPool_setLoop");
    149     SoundPool *ap = MusterSoundPool(env, thiz);
    150     if (ap == NULL) return;
    151     ap->setLoop(channelID, loop);
    152 }
    153 
    154 static void
    155 android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
    156        jfloat rate)
    157 {
    158     ALOGV("android_media_SoundPool_setRate");
    159     SoundPool *ap = MusterSoundPool(env, thiz);
    160     if (ap == NULL) return;
    161     ap->setRate(channelID, (float) rate);
    162 }
    163 
    164 static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
    165 {
    166     ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
    167     JNIEnv *env = AndroidRuntime::getJNIEnv();
    168     env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
    169 }
    170 
    171 static jint
    172 android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
    173         jint maxChannels, jobject jaa)
    174 {
    175     if (jaa == 0) {
    176         ALOGE("Error creating SoundPool: invalid audio attributes");
    177         return -1;
    178     }
    179 
    180     audio_attributes_t *paa = NULL;
    181     // read the AudioAttributes values
    182     paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
    183     const jstring jtags =
    184             (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
    185     const char* tags = env->GetStringUTFChars(jtags, NULL);
    186     // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
    187     strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
    188     env->ReleaseStringUTFChars(jtags, tags);
    189     paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
    190     paa->content_type =
    191             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
    192     paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
    193 
    194     ALOGV("android_media_SoundPool_native_setup");
    195     SoundPool *ap = new SoundPool(maxChannels, paa);
    196     if (ap == NULL) {
    197         return -1;
    198     }
    199 
    200     // save pointer to SoundPool C++ object in opaque field in Java object
    201     env->SetLongField(thiz, fields.mNativeContext, (jlong) ap);
    202 
    203     // set callback with weak reference
    204     jobject globalWeakRef = env->NewGlobalRef(weakRef);
    205     ap->setCallback(android_media_callback, globalWeakRef);
    206 
    207     // audio attributes were copied in SoundPool creation
    208     free(paa);
    209 
    210     return 0;
    211 }
    212 
    213 static void
    214 android_media_SoundPool_release(JNIEnv *env, jobject thiz)
    215 {
    216     ALOGV("android_media_SoundPool_release");
    217     SoundPool *ap = MusterSoundPool(env, thiz);
    218     if (ap != NULL) {
    219 
    220         // release weak reference and clear callback
    221         jobject weakRef = (jobject) ap->getUserData();
    222         ap->setCallback(NULL, NULL);
    223         if (weakRef != NULL) {
    224             env->DeleteGlobalRef(weakRef);
    225         }
    226 
    227         // clear native context
    228         env->SetLongField(thiz, fields.mNativeContext, 0);
    229         delete ap;
    230     }
    231 }
    232 
    233 // ----------------------------------------------------------------------------
    234 
    235 // Dalvik VM type signatures
    236 static JNINativeMethod gMethods[] = {
    237     {   "_load",
    238         "(Ljava/io/FileDescriptor;JJI)I",
    239         (void *)android_media_SoundPool_load_FD
    240     },
    241     {   "unload",
    242         "(I)Z",
    243         (void *)android_media_SoundPool_unload
    244     },
    245     {   "_play",
    246         "(IFFIIF)I",
    247         (void *)android_media_SoundPool_play
    248     },
    249     {   "pause",
    250         "(I)V",
    251         (void *)android_media_SoundPool_pause
    252     },
    253     {   "resume",
    254         "(I)V",
    255         (void *)android_media_SoundPool_resume
    256     },
    257     {   "autoPause",
    258         "()V",
    259         (void *)android_media_SoundPool_autoPause
    260     },
    261     {   "autoResume",
    262         "()V",
    263         (void *)android_media_SoundPool_autoResume
    264     },
    265     {   "stop",
    266         "(I)V",
    267         (void *)android_media_SoundPool_stop
    268     },
    269     {   "_setVolume",
    270         "(IFF)V",
    271         (void *)android_media_SoundPool_setVolume
    272     },
    273     {   "setPriority",
    274         "(II)V",
    275         (void *)android_media_SoundPool_setPriority
    276     },
    277     {   "setLoop",
    278         "(II)V",
    279         (void *)android_media_SoundPool_setLoop
    280     },
    281     {   "setRate",
    282         "(IF)V",
    283         (void *)android_media_SoundPool_setRate
    284     },
    285     {   "native_setup",
    286         "(Ljava/lang/Object;ILjava/lang/Object;)I",
    287         (void*)android_media_SoundPool_native_setup
    288     },
    289     {   "native_release",
    290         "()V",
    291         (void*)android_media_SoundPool_release
    292     }
    293 };
    294 
    295 static const char* const kClassPathName = "android/media/SoundPool";
    296 
    297 jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
    298 {
    299     JNIEnv* env = NULL;
    300     jint result = -1;
    301     jclass clazz;
    302 
    303     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    304         ALOGE("ERROR: GetEnv failed\n");
    305         return result;
    306     }
    307     assert(env != NULL);
    308 
    309     clazz = env->FindClass(kClassPathName);
    310     if (clazz == NULL) {
    311         ALOGE("Can't find %s", kClassPathName);
    312         return result;
    313     }
    314 
    315     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
    316     if (fields.mNativeContext == NULL) {
    317         ALOGE("Can't find SoundPool.mNativeContext");
    318         return result;
    319     }
    320 
    321     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
    322                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    323     if (fields.mPostEvent == NULL) {
    324         ALOGE("Can't find android/media/SoundPool.postEventFromNative");
    325         return result;
    326     }
    327 
    328     // create a reference to class. Technically, we're leaking this reference
    329     // since it's a static object.
    330     fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
    331 
    332     if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
    333         return result;
    334 
    335     // Get the AudioAttributes class and fields
    336     jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
    337     if (audioAttrClass == NULL) {
    338         ALOGE("Can't find %s", kAudioAttributesClassPathName);
    339         return result;
    340     }
    341     jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
    342     javaAudioAttrFields.fieldUsage = env->GetFieldID(audioAttributesClassRef, "mUsage", "I");
    343     javaAudioAttrFields.fieldContentType
    344                                    = env->GetFieldID(audioAttributesClassRef, "mContentType", "I");
    345     javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
    346     javaAudioAttrFields.fieldFormattedTags =
    347             env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
    348     env->DeleteGlobalRef(audioAttributesClassRef);
    349     if (javaAudioAttrFields.fieldUsage == NULL || javaAudioAttrFields.fieldContentType == NULL
    350             || javaAudioAttrFields.fieldFlags == NULL
    351             || javaAudioAttrFields.fieldFormattedTags == NULL) {
    352         ALOGE("Can't initialize AudioAttributes fields");
    353         return result;
    354     }
    355 
    356     /* success -- return valid version number */
    357     return JNI_VERSION_1_4;
    358 }
    359