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