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