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 //#define LOG_NDEBUG 0 17 18 #define LOG_TAG "AudioTrack-JNI" 19 20 #include "android_media_AudioTrack.h" 21 22 #include <nativehelper/JNIHelp.h> 23 #include <nativehelper/JniConstants.h> 24 #include "core_jni_helpers.h" 25 26 #include <nativehelper/ScopedBytes.h> 27 28 #include <utils/Log.h> 29 #include <media/AudioSystem.h> 30 #include <media/AudioTrack.h> 31 32 #include <binder/MemoryHeapBase.h> 33 #include <binder/MemoryBase.h> 34 35 #include "android_media_AudioFormat.h" 36 #include "android_media_AudioErrors.h" 37 #include "android_media_MediaMetricsJNI.h" 38 #include "android_media_PlaybackParams.h" 39 #include "android_media_DeviceCallback.h" 40 #include "android_media_VolumeShaper.h" 41 42 #include <cinttypes> 43 44 // ---------------------------------------------------------------------------- 45 46 using namespace android; 47 48 using ::android::media::VolumeShaper; 49 50 // ---------------------------------------------------------------------------- 51 static const char* const kClassPathName = "android/media/AudioTrack"; 52 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; 53 54 struct audio_track_fields_t { 55 // these fields provide access from C++ to the... 56 jmethodID postNativeEventInJava; //... event post callback method 57 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object 58 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack 59 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object 60 }; 61 struct audio_attributes_fields_t { 62 jfieldID fieldUsage; // AudioAttributes.mUsage 63 jfieldID fieldContentType; // AudioAttributes.mContentType 64 jfieldID fieldFlags; // AudioAttributes.mFlags 65 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags 66 }; 67 static audio_track_fields_t javaAudioTrackFields; 68 static audio_attributes_fields_t javaAudioAttrFields; 69 static PlaybackParams::fields_t gPlaybackParamsFields; 70 static VolumeShaperHelper::fields_t gVolumeShaperFields; 71 72 struct audiotrack_callback_cookie { 73 jclass audioTrack_class; 74 jobject audioTrack_ref; 75 bool busy; 76 Condition cond; 77 bool isOffload; 78 }; 79 80 // keep these values in sync with AudioTrack.java 81 #define MODE_STATIC 0 82 #define MODE_STREAM 1 83 84 // ---------------------------------------------------------------------------- 85 class AudioTrackJniStorage { 86 public: 87 sp<MemoryHeapBase> mMemHeap; 88 sp<MemoryBase> mMemBase; 89 audiotrack_callback_cookie mCallbackData; 90 sp<JNIDeviceCallback> mDeviceCallback; 91 92 AudioTrackJniStorage() { 93 mCallbackData.audioTrack_class = 0; 94 mCallbackData.audioTrack_ref = 0; 95 mCallbackData.isOffload = false; 96 } 97 98 ~AudioTrackJniStorage() { 99 mMemBase.clear(); 100 mMemHeap.clear(); 101 } 102 103 bool allocSharedMem(int sizeInBytes) { 104 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); 105 if (mMemHeap->getHeapID() < 0) { 106 return false; 107 } 108 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); 109 return true; 110 } 111 }; 112 113 static Mutex sLock; 114 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; 115 116 // ---------------------------------------------------------------------------- 117 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 118 119 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM (-16) 120 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK (-17) 121 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT (-18) 122 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19) 123 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20) 124 125 // ---------------------------------------------------------------------------- 126 static void audioCallback(int event, void* user, void *info) { 127 128 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; 129 { 130 Mutex::Autolock l(sLock); 131 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) { 132 return; 133 } 134 callbackInfo->busy = true; 135 } 136 137 switch (event) { 138 // Offload only events 139 case AudioTrack::EVENT_STREAM_END: 140 case AudioTrack::EVENT_MORE_DATA: 141 // a.k.a. tear down 142 case AudioTrack::EVENT_NEW_IAUDIOTRACK: 143 if (callbackInfo->isOffload) { 144 JNIEnv *env = AndroidRuntime::getJNIEnv(); 145 if (user != NULL && env != NULL) { 146 env->CallStaticVoidMethod( 147 callbackInfo->audioTrack_class, 148 javaAudioTrackFields.postNativeEventInJava, 149 callbackInfo->audioTrack_ref, event, 0,0, NULL); 150 if (env->ExceptionCheck()) { 151 env->ExceptionDescribe(); 152 env->ExceptionClear(); 153 } 154 } 155 } break; 156 157 // PCM and offload events 158 case AudioTrack::EVENT_MARKER: 159 case AudioTrack::EVENT_NEW_POS: { 160 JNIEnv *env = AndroidRuntime::getJNIEnv(); 161 if (user != NULL && env != NULL) { 162 env->CallStaticVoidMethod( 163 callbackInfo->audioTrack_class, 164 javaAudioTrackFields.postNativeEventInJava, 165 callbackInfo->audioTrack_ref, event, 0,0, NULL); 166 if (env->ExceptionCheck()) { 167 env->ExceptionDescribe(); 168 env->ExceptionClear(); 169 } 170 } 171 } break; 172 } 173 174 { 175 Mutex::Autolock l(sLock); 176 callbackInfo->busy = false; 177 callbackInfo->cond.broadcast(); 178 } 179 } 180 181 182 // ---------------------------------------------------------------------------- 183 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) 184 { 185 Mutex::Autolock l(sLock); 186 AudioTrack* const at = 187 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 188 return sp<AudioTrack>(at); 189 } 190 191 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at) 192 { 193 Mutex::Autolock l(sLock); 194 sp<AudioTrack> old = 195 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 196 if (at.get()) { 197 at->incStrong((void*)setAudioTrack); 198 } 199 if (old != 0) { 200 old->decStrong((void*)setAudioTrack); 201 } 202 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get()); 203 return old; 204 } 205 206 // ---------------------------------------------------------------------------- 207 sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) { 208 return getAudioTrack(env, audioTrackObj); 209 } 210 211 // ---------------------------------------------------------------------------- 212 static jint 213 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, 214 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, 215 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, 216 jlong nativeAudioTrack, jboolean offload) { 217 218 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d" 219 "nativeAudioTrack=0x%" PRIX64, 220 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, 221 nativeAudioTrack); 222 223 sp<AudioTrack> lpTrack = 0; 224 225 if (jSession == NULL) { 226 ALOGE("Error creating AudioTrack: invalid session ID pointer"); 227 return (jint) AUDIO_JAVA_ERROR; 228 } 229 230 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 231 if (nSession == NULL) { 232 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 233 return (jint) AUDIO_JAVA_ERROR; 234 } 235 audio_session_t sessionId = (audio_session_t) nSession[0]; 236 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 237 nSession = NULL; 238 239 AudioTrackJniStorage* lpJniStorage = NULL; 240 241 audio_attributes_t *paa = NULL; 242 243 jclass clazz = env->GetObjectClass(thiz); 244 if (clazz == NULL) { 245 ALOGE("Can't find %s when setting up callback.", kClassPathName); 246 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 247 } 248 249 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. 250 if (nativeAudioTrack == 0) { 251 if (jaa == 0) { 252 ALOGE("Error creating AudioTrack: invalid audio attributes"); 253 return (jint) AUDIO_JAVA_ERROR; 254 } 255 256 if (jSampleRate == 0) { 257 ALOGE("Error creating AudioTrack: invalid sample rates"); 258 return (jint) AUDIO_JAVA_ERROR; 259 } 260 261 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL); 262 int sampleRateInHertz = sampleRates[0]; 263 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT); 264 265 // Invalid channel representations are caught by !audio_is_output_channel() below. 266 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks( 267 channelPositionMask, channelIndexMask); 268 if (!audio_is_output_channel(nativeChannelMask)) { 269 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask); 270 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; 271 } 272 273 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask); 274 275 // check the format. 276 // This function was called from Java, so we compare the format against the Java constants 277 audio_format_t format = audioFormatToNative(audioFormat); 278 if (format == AUDIO_FORMAT_INVALID) { 279 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat); 280 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; 281 } 282 283 // compute the frame count 284 size_t frameCount; 285 if (audio_has_proportional_frames(format)) { 286 const size_t bytesPerSample = audio_bytes_per_sample(format); 287 frameCount = buffSizeInBytes / (channelCount * bytesPerSample); 288 } else { 289 frameCount = buffSizeInBytes; 290 } 291 292 // create the native AudioTrack object 293 lpTrack = new AudioTrack(); 294 295 // read the AudioAttributes values 296 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); 297 const jstring jtags = 298 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); 299 const char* tags = env->GetStringUTFChars(jtags, NULL); 300 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it 301 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); 302 env->ReleaseStringUTFChars(jtags, tags); 303 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); 304 paa->content_type = 305 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); 306 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); 307 308 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", 309 paa->usage, paa->content_type, paa->flags, paa->tags); 310 311 // initialize the callback information: 312 // this data will be passed with every AudioTrack callback 313 lpJniStorage = new AudioTrackJniStorage(); 314 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 315 // we use a weak reference so the AudioTrack object can be garbage collected. 316 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 317 lpJniStorage->mCallbackData.isOffload = offload; 318 lpJniStorage->mCallbackData.busy = false; 319 320 audio_offload_info_t offloadInfo; 321 if (offload) { 322 offloadInfo = AUDIO_INFO_INITIALIZER; 323 offloadInfo.format = format; 324 offloadInfo.sample_rate = sampleRateInHertz; 325 offloadInfo.channel_mask = nativeChannelMask; 326 offloadInfo.has_video = false; 327 offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload 328 } 329 330 // initialize the native AudioTrack object 331 status_t status = NO_ERROR; 332 switch (memoryMode) { 333 case MODE_STREAM: 334 335 status = lpTrack->set( 336 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 337 sampleRateInHertz, 338 format,// word length, PCM 339 nativeChannelMask, 340 frameCount, 341 AUDIO_OUTPUT_FLAG_NONE, 342 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 343 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 344 0,// shared mem 345 true,// thread can call Java 346 sessionId,// audio session ID 347 AudioTrack::TRANSFER_SYNC, 348 offload ? &offloadInfo : NULL, 349 -1, -1, // default uid, pid values 350 paa); 351 break; 352 353 case MODE_STATIC: 354 // AudioTrack is using shared memory 355 356 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { 357 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); 358 goto native_init_failure; 359 } 360 361 status = lpTrack->set( 362 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 363 sampleRateInHertz, 364 format,// word length, PCM 365 nativeChannelMask, 366 frameCount, 367 AUDIO_OUTPUT_FLAG_NONE, 368 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 369 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 370 lpJniStorage->mMemBase,// shared mem 371 true,// thread can call Java 372 sessionId,// audio session ID 373 AudioTrack::TRANSFER_SHARED, 374 NULL, // default offloadInfo 375 -1, -1, // default uid, pid values 376 paa); 377 break; 378 379 default: 380 ALOGE("Unknown mode %d", memoryMode); 381 goto native_init_failure; 382 } 383 384 if (status != NO_ERROR) { 385 ALOGE("Error %d initializing AudioTrack", status); 386 goto native_init_failure; 387 } 388 } else { // end if (nativeAudioTrack == 0) 389 lpTrack = (AudioTrack*)nativeAudioTrack; 390 // TODO: We need to find out which members of the Java AudioTrack might 391 // need to be initialized from the Native AudioTrack 392 // these are directly returned from getters: 393 // mSampleRate 394 // mAudioFormat 395 // mStreamType 396 // mChannelConfiguration 397 // mChannelCount 398 // mState (?) 399 // mPlayState (?) 400 // these may be used internally (Java AudioTrack.audioParamCheck(): 401 // mChannelMask 402 // mChannelIndexMask 403 // mDataLoadMode 404 405 // initialize the callback information: 406 // this data will be passed with every AudioTrack callback 407 lpJniStorage = new AudioTrackJniStorage(); 408 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 409 // we use a weak reference so the AudioTrack object can be garbage collected. 410 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 411 lpJniStorage->mCallbackData.busy = false; 412 } 413 414 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 415 if (nSession == NULL) { 416 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 417 goto native_init_failure; 418 } 419 // read the audio session ID back from AudioTrack in case we create a new session 420 nSession[0] = lpTrack->getSessionId(); 421 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 422 nSession = NULL; 423 424 { 425 const jint elements[1] = { (jint) lpTrack->getSampleRate() }; 426 env->SetIntArrayRegion(jSampleRate, 0, 1, elements); 427 } 428 429 { // scope for the lock 430 Mutex::Autolock l(sLock); 431 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData); 432 } 433 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field 434 // of the Java object (in mNativeTrackInJavaObj) 435 setAudioTrack(env, thiz, lpTrack); 436 437 // save the JNI resources so we can free them later 438 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage); 439 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); 440 441 // since we had audio attributes, the stream type was derived from them during the 442 // creation of the native AudioTrack: push the same value to the Java object 443 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); 444 if (paa != NULL) { 445 // audio attributes were copied in AudioTrack creation 446 free(paa); 447 paa = NULL; 448 } 449 450 451 return (jint) AUDIO_JAVA_SUCCESS; 452 453 // failures: 454 native_init_failure: 455 if (paa != NULL) { 456 free(paa); 457 } 458 if (nSession != NULL) { 459 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 460 } 461 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); 462 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); 463 delete lpJniStorage; 464 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 465 466 // lpTrack goes out of scope, so reference count drops to zero 467 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 468 } 469 470 // ---------------------------------------------------------------------------- 471 static void 472 android_media_AudioTrack_start(JNIEnv *env, jobject thiz) 473 { 474 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 475 if (lpTrack == NULL) { 476 jniThrowException(env, "java/lang/IllegalStateException", 477 "Unable to retrieve AudioTrack pointer for start()"); 478 return; 479 } 480 481 lpTrack->start(); 482 } 483 484 485 // ---------------------------------------------------------------------------- 486 static void 487 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) 488 { 489 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 490 if (lpTrack == NULL) { 491 jniThrowException(env, "java/lang/IllegalStateException", 492 "Unable to retrieve AudioTrack pointer for stop()"); 493 return; 494 } 495 496 lpTrack->stop(); 497 } 498 499 500 // ---------------------------------------------------------------------------- 501 static void 502 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) 503 { 504 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 505 if (lpTrack == NULL) { 506 jniThrowException(env, "java/lang/IllegalStateException", 507 "Unable to retrieve AudioTrack pointer for pause()"); 508 return; 509 } 510 511 lpTrack->pause(); 512 } 513 514 515 // ---------------------------------------------------------------------------- 516 static void 517 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) 518 { 519 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 520 if (lpTrack == NULL) { 521 jniThrowException(env, "java/lang/IllegalStateException", 522 "Unable to retrieve AudioTrack pointer for flush()"); 523 return; 524 } 525 526 lpTrack->flush(); 527 } 528 529 // ---------------------------------------------------------------------------- 530 static void 531 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) 532 { 533 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 534 if (lpTrack == NULL) { 535 jniThrowException(env, "java/lang/IllegalStateException", 536 "Unable to retrieve AudioTrack pointer for setVolume()"); 537 return; 538 } 539 540 lpTrack->setVolume(leftVol, rightVol); 541 } 542 543 // ---------------------------------------------------------------------------- 544 545 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 546 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) { 547 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); 548 if (lpTrack == NULL) { 549 return; 550 } 551 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); 552 553 // delete the JNI data 554 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 555 thiz, javaAudioTrackFields.jniData); 556 // reset the native resources in the Java object so any attempt to access 557 // them after a call to release fails. 558 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 559 560 if (pJniStorage) { 561 Mutex::Autolock l(sLock); 562 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData; 563 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage); 564 while (lpCookie->busy) { 565 if (lpCookie->cond.waitRelative(sLock, 566 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 567 NO_ERROR) { 568 break; 569 } 570 } 571 sAudioTrackCallBackCookies.remove(lpCookie); 572 // delete global refs created in native_setup 573 env->DeleteGlobalRef(lpCookie->audioTrack_class); 574 env->DeleteGlobalRef(lpCookie->audioTrack_ref); 575 delete pJniStorage; 576 } 577 } 578 579 580 // ---------------------------------------------------------------------------- 581 static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { 582 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz); 583 android_media_AudioTrack_release(env, thiz); 584 } 585 586 // overloaded JNI array helper functions (same as in android_media_AudioRecord) 587 static inline 588 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { 589 return env->GetByteArrayElements(array, isCopy); 590 } 591 592 static inline 593 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { 594 env->ReleaseByteArrayElements(array, elems, mode); 595 } 596 597 static inline 598 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { 599 return env->GetShortArrayElements(array, isCopy); 600 } 601 602 static inline 603 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { 604 env->ReleaseShortArrayElements(array, elems, mode); 605 } 606 607 static inline 608 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { 609 return env->GetFloatArrayElements(array, isCopy); 610 } 611 612 static inline 613 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { 614 env->ReleaseFloatArrayElements(array, elems, mode); 615 } 616 617 static inline 618 jint interpretWriteSizeError(ssize_t writeSize) { 619 if (writeSize == WOULD_BLOCK) { 620 return (jint)0; 621 } else if (writeSize == NO_INIT) { 622 return AUDIO_JAVA_DEAD_OBJECT; 623 } else { 624 ALOGE("Error %zd during AudioTrack native read", writeSize); 625 return nativeToJavaStatus(writeSize); 626 } 627 } 628 629 // ---------------------------------------------------------------------------- 630 template <typename T> 631 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data, 632 jint offsetInSamples, jint sizeInSamples, bool blocking) { 633 // give the data to the native AudioTrack object (the data starts at the offset) 634 ssize_t written = 0; 635 // regular write() or copy the data to the AudioTrack's shared memory? 636 size_t sizeInBytes = sizeInSamples * sizeof(T); 637 if (track->sharedBuffer() == 0) { 638 written = track->write(data + offsetInSamples, sizeInBytes, blocking); 639 // for compatibility with earlier behavior of write(), return 0 in this case 640 if (written == (ssize_t) WOULD_BLOCK) { 641 written = 0; 642 } 643 } else { 644 // writing to shared memory, check for capacity 645 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { 646 sizeInBytes = track->sharedBuffer()->size(); 647 } 648 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes); 649 written = sizeInBytes; 650 } 651 if (written >= 0) { 652 return written / sizeof(T); 653 } 654 return interpretWriteSizeError(written); 655 } 656 657 // ---------------------------------------------------------------------------- 658 template <typename T> 659 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz, 660 T javaAudioData, 661 jint offsetInSamples, jint sizeInSamples, 662 jint javaAudioFormat, 663 jboolean isWriteBlocking) { 664 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called", 665 // offsetInSamples, sizeInSamples); 666 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 667 if (lpTrack == NULL) { 668 jniThrowException(env, "java/lang/IllegalStateException", 669 "Unable to retrieve AudioTrack pointer for write()"); 670 return (jint)AUDIO_JAVA_INVALID_OPERATION; 671 } 672 673 if (javaAudioData == NULL) { 674 ALOGE("NULL java array of audio data to play"); 675 return (jint)AUDIO_JAVA_BAD_VALUE; 676 } 677 678 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 679 // a way that it becomes much more efficient. When doing so, we will have to prevent the 680 // AudioSystem callback to be called while in critical section (in case of media server 681 // process crash for instance) 682 683 // get the pointer for the audio data from the java array 684 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL); 685 if (cAudioData == NULL) { 686 ALOGE("Error retrieving source of audio data to play"); 687 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load 688 } 689 690 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData, 691 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */); 692 693 envReleaseArrayElements(env, javaAudioData, cAudioData, 0); 694 695 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d", 696 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples); 697 return samplesWritten; 698 } 699 700 // ---------------------------------------------------------------------------- 701 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, 702 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes, 703 jint javaAudioFormat, jboolean isWriteBlocking) { 704 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called", 705 // offsetInBytes, sizeInBytes); 706 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 707 if (lpTrack == NULL) { 708 jniThrowException(env, "java/lang/IllegalStateException", 709 "Unable to retrieve AudioTrack pointer for write()"); 710 return (jint)AUDIO_JAVA_INVALID_OPERATION; 711 } 712 713 ScopedBytesRO bytes(env, javaBytes); 714 if (bytes.get() == NULL) { 715 ALOGE("Error retrieving source of audio data to play, can't play"); 716 return (jint)AUDIO_JAVA_BAD_VALUE; 717 } 718 719 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset, 720 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); 721 722 return written; 723 } 724 725 // ---------------------------------------------------------------------------- 726 static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) { 727 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 728 if (lpTrack == NULL) { 729 jniThrowException(env, "java/lang/IllegalStateException", 730 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()"); 731 return (jint)AUDIO_JAVA_ERROR; 732 } 733 734 ssize_t result = lpTrack->getBufferSizeInFrames(); 735 if (result < 0) { 736 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", 737 "Internal error detected in getBufferSizeInFrames() = %zd", result); 738 return (jint)AUDIO_JAVA_ERROR; 739 } 740 return (jint)result; 741 } 742 743 // ---------------------------------------------------------------------------- 744 static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env, 745 jobject thiz, jint bufferSizeInFrames) { 746 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 747 if (lpTrack == NULL) { 748 jniThrowException(env, "java/lang/IllegalStateException", 749 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()"); 750 return (jint)AUDIO_JAVA_ERROR; 751 } 752 // Value will be coerced into the valid range. 753 // But internal values are unsigned, size_t, so we need to clip 754 // against zero here where it is signed. 755 if (bufferSizeInFrames < 0) { 756 bufferSizeInFrames = 0; 757 } 758 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames); 759 if (result < 0) { 760 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", 761 "Internal error detected in setBufferSizeInFrames() = %zd", result); 762 return (jint)AUDIO_JAVA_ERROR; 763 } 764 return (jint)result; 765 } 766 767 // ---------------------------------------------------------------------------- 768 static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) { 769 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 770 if (lpTrack == NULL) { 771 jniThrowException(env, "java/lang/IllegalStateException", 772 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()"); 773 return (jint)AUDIO_JAVA_ERROR; 774 } 775 776 return lpTrack->frameCount(); 777 } 778 779 // ---------------------------------------------------------------------------- 780 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, 781 jint sampleRateInHz) { 782 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 783 if (lpTrack == NULL) { 784 jniThrowException(env, "java/lang/IllegalStateException", 785 "Unable to retrieve AudioTrack pointer for setSampleRate()"); 786 return (jint)AUDIO_JAVA_ERROR; 787 } 788 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz)); 789 } 790 791 792 // ---------------------------------------------------------------------------- 793 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { 794 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 795 if (lpTrack == NULL) { 796 jniThrowException(env, "java/lang/IllegalStateException", 797 "Unable to retrieve AudioTrack pointer for getSampleRate()"); 798 return (jint)AUDIO_JAVA_ERROR; 799 } 800 return (jint) lpTrack->getSampleRate(); 801 } 802 803 804 // ---------------------------------------------------------------------------- 805 static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz, 806 jobject params) { 807 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 808 if (lpTrack == NULL) { 809 jniThrowException(env, "java/lang/IllegalStateException", 810 "AudioTrack not initialized"); 811 return; 812 } 813 814 PlaybackParams pbp; 815 pbp.fillFromJobject(env, gPlaybackParamsFields, params); 816 817 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u", 818 pbp.speedSet, pbp.audioRate.mSpeed, 819 pbp.pitchSet, pbp.audioRate.mPitch, 820 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode, 821 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode); 822 823 // to simulate partially set params, we do a read-modify-write. 824 // TODO: pass in the valid set mask into AudioTrack. 825 AudioPlaybackRate rate = lpTrack->getPlaybackRate(); 826 bool updatedRate = false; 827 if (pbp.speedSet) { 828 rate.mSpeed = pbp.audioRate.mSpeed; 829 updatedRate = true; 830 } 831 if (pbp.pitchSet) { 832 rate.mPitch = pbp.audioRate.mPitch; 833 updatedRate = true; 834 } 835 if (pbp.audioFallbackModeSet) { 836 rate.mFallbackMode = pbp.audioRate.mFallbackMode; 837 updatedRate = true; 838 } 839 if (pbp.audioStretchModeSet) { 840 rate.mStretchMode = pbp.audioRate.mStretchMode; 841 updatedRate = true; 842 } 843 if (updatedRate) { 844 if (lpTrack->setPlaybackRate(rate) != OK) { 845 jniThrowException(env, "java/lang/IllegalArgumentException", 846 "arguments out of range"); 847 } 848 } 849 } 850 851 852 // ---------------------------------------------------------------------------- 853 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz, 854 jobject params) { 855 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 856 if (lpTrack == NULL) { 857 jniThrowException(env, "java/lang/IllegalStateException", 858 "AudioTrack not initialized"); 859 return NULL; 860 } 861 862 PlaybackParams pbs; 863 pbs.audioRate = lpTrack->getPlaybackRate(); 864 pbs.speedSet = true; 865 pbs.pitchSet = true; 866 pbs.audioFallbackModeSet = true; 867 pbs.audioStretchModeSet = true; 868 return pbs.asJobject(env, gPlaybackParamsFields); 869 } 870 871 872 // ---------------------------------------------------------------------------- 873 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, 874 jint markerPos) { 875 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 876 if (lpTrack == NULL) { 877 jniThrowException(env, "java/lang/IllegalStateException", 878 "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); 879 return (jint)AUDIO_JAVA_ERROR; 880 } 881 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) ); 882 } 883 884 885 // ---------------------------------------------------------------------------- 886 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { 887 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 888 uint32_t markerPos = 0; 889 890 if (lpTrack == NULL) { 891 jniThrowException(env, "java/lang/IllegalStateException", 892 "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); 893 return (jint)AUDIO_JAVA_ERROR; 894 } 895 lpTrack->getMarkerPosition(&markerPos); 896 return (jint)markerPos; 897 } 898 899 900 // ---------------------------------------------------------------------------- 901 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, 902 jint period) { 903 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 904 if (lpTrack == NULL) { 905 jniThrowException(env, "java/lang/IllegalStateException", 906 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); 907 return (jint)AUDIO_JAVA_ERROR; 908 } 909 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) ); 910 } 911 912 913 // ---------------------------------------------------------------------------- 914 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { 915 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 916 uint32_t period = 0; 917 918 if (lpTrack == NULL) { 919 jniThrowException(env, "java/lang/IllegalStateException", 920 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); 921 return (jint)AUDIO_JAVA_ERROR; 922 } 923 lpTrack->getPositionUpdatePeriod(&period); 924 return (jint)period; 925 } 926 927 928 // ---------------------------------------------------------------------------- 929 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, 930 jint position) { 931 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 932 if (lpTrack == NULL) { 933 jniThrowException(env, "java/lang/IllegalStateException", 934 "Unable to retrieve AudioTrack pointer for setPosition()"); 935 return (jint)AUDIO_JAVA_ERROR; 936 } 937 return nativeToJavaStatus( lpTrack->setPosition(position) ); 938 } 939 940 941 // ---------------------------------------------------------------------------- 942 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { 943 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 944 uint32_t position = 0; 945 946 if (lpTrack == NULL) { 947 jniThrowException(env, "java/lang/IllegalStateException", 948 "Unable to retrieve AudioTrack pointer for getPosition()"); 949 return (jint)AUDIO_JAVA_ERROR; 950 } 951 lpTrack->getPosition(&position); 952 return (jint)position; 953 } 954 955 956 // ---------------------------------------------------------------------------- 957 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) { 958 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 959 960 if (lpTrack == NULL) { 961 jniThrowException(env, "java/lang/IllegalStateException", 962 "Unable to retrieve AudioTrack pointer for latency()"); 963 return (jint)AUDIO_JAVA_ERROR; 964 } 965 return (jint)lpTrack->latency(); 966 } 967 968 // ---------------------------------------------------------------------------- 969 static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) { 970 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 971 972 if (lpTrack == NULL) { 973 jniThrowException(env, "java/lang/IllegalStateException", 974 "Unable to retrieve AudioTrack pointer for getUnderrunCount()"); 975 return (jint)AUDIO_JAVA_ERROR; 976 } 977 return (jint)lpTrack->getUnderrunCount(); 978 } 979 980 // ---------------------------------------------------------------------------- 981 static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) { 982 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 983 984 if (lpTrack == NULL) { 985 jniThrowException(env, "java/lang/IllegalStateException", 986 "Unable to retrieve AudioTrack pointer for getFlags()"); 987 return (jint)AUDIO_JAVA_ERROR; 988 } 989 return (jint)lpTrack->getFlags(); 990 } 991 992 // ---------------------------------------------------------------------------- 993 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) { 994 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 995 996 if (lpTrack == NULL) { 997 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()"); 998 return (jint)AUDIO_JAVA_ERROR; 999 } 1000 AudioTimestamp timestamp; 1001 status_t status = lpTrack->getTimestamp(timestamp); 1002 if (status == OK) { 1003 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL); 1004 if (nTimestamp == NULL) { 1005 ALOGE("Unable to get array for getTimestamp()"); 1006 return (jint)AUDIO_JAVA_ERROR; 1007 } 1008 nTimestamp[0] = (jlong) timestamp.mPosition; 1009 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec); 1010 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0); 1011 } 1012 return (jint) nativeToJavaStatus(status); 1013 } 1014 1015 // ---------------------------------------------------------------------------- 1016 static jobject 1017 android_media_AudioTrack_native_getMetrics(JNIEnv *env, jobject thiz) 1018 { 1019 ALOGD("android_media_AudioTrack_native_getMetrics"); 1020 1021 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1022 1023 if (lpTrack == NULL) { 1024 ALOGE("Unable to retrieve AudioTrack pointer for getMetrics()"); 1025 jniThrowException(env, "java/lang/IllegalStateException", NULL); 1026 return (jobject) NULL; 1027 } 1028 1029 // get what we have for the metrics from the track 1030 MediaAnalyticsItem *item = NULL; 1031 1032 status_t err = lpTrack->getMetrics(item); 1033 if (err != OK) { 1034 ALOGE("getMetrics failed"); 1035 jniThrowException(env, "java/lang/IllegalStateException", NULL); 1036 return (jobject) NULL; 1037 } 1038 1039 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */); 1040 1041 // housekeeping 1042 delete item; 1043 item = NULL; 1044 1045 return mybundle; 1046 } 1047 1048 1049 // ---------------------------------------------------------------------------- 1050 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, 1051 jint loopStart, jint loopEnd, jint loopCount) { 1052 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1053 if (lpTrack == NULL) { 1054 jniThrowException(env, "java/lang/IllegalStateException", 1055 "Unable to retrieve AudioTrack pointer for setLoop()"); 1056 return (jint)AUDIO_JAVA_ERROR; 1057 } 1058 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); 1059 } 1060 1061 1062 // ---------------------------------------------------------------------------- 1063 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { 1064 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1065 if (lpTrack == NULL) { 1066 jniThrowException(env, "java/lang/IllegalStateException", 1067 "Unable to retrieve AudioTrack pointer for reload()"); 1068 return (jint)AUDIO_JAVA_ERROR; 1069 } 1070 return nativeToJavaStatus( lpTrack->reload() ); 1071 } 1072 1073 1074 // ---------------------------------------------------------------------------- 1075 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz, 1076 jint javaStreamType) { 1077 uint32_t afSamplingRate; 1078 // convert the stream type from Java to native value 1079 // FIXME: code duplication with android_media_AudioTrack_setup() 1080 audio_stream_type_t nativeStreamType; 1081 switch (javaStreamType) { 1082 case AUDIO_STREAM_VOICE_CALL: 1083 case AUDIO_STREAM_SYSTEM: 1084 case AUDIO_STREAM_RING: 1085 case AUDIO_STREAM_MUSIC: 1086 case AUDIO_STREAM_ALARM: 1087 case AUDIO_STREAM_NOTIFICATION: 1088 case AUDIO_STREAM_BLUETOOTH_SCO: 1089 case AUDIO_STREAM_DTMF: 1090 nativeStreamType = (audio_stream_type_t) javaStreamType; 1091 break; 1092 default: 1093 nativeStreamType = AUDIO_STREAM_DEFAULT; 1094 break; 1095 } 1096 1097 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType); 1098 if (status != NO_ERROR) { 1099 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d " 1100 "in AudioTrack JNI", status, nativeStreamType); 1101 return DEFAULT_OUTPUT_SAMPLE_RATE; 1102 } else { 1103 return afSamplingRate; 1104 } 1105 } 1106 1107 1108 // ---------------------------------------------------------------------------- 1109 // returns the minimum required size for the successful creation of a streaming AudioTrack 1110 // returns -1 if there was an error querying the hardware. 1111 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 1112 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 1113 1114 size_t frameCount; 1115 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, 1116 sampleRateInHertz); 1117 if (status != NO_ERROR) { 1118 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d", 1119 sampleRateInHertz, status); 1120 return -1; 1121 } 1122 const audio_format_t format = audioFormatToNative(audioFormat); 1123 if (audio_has_proportional_frames(format)) { 1124 const size_t bytesPerSample = audio_bytes_per_sample(format); 1125 return frameCount * channelCount * bytesPerSample; 1126 } else { 1127 return frameCount; 1128 } 1129 } 1130 1131 // ---------------------------------------------------------------------------- 1132 static jint 1133 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level ) 1134 { 1135 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1136 if (lpTrack == NULL ) { 1137 jniThrowException(env, "java/lang/IllegalStateException", 1138 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()"); 1139 return -1; 1140 } 1141 1142 status_t status = lpTrack->setAuxEffectSendLevel(level); 1143 if (status != NO_ERROR) { 1144 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d", 1145 level, status); 1146 } 1147 return (jint) status; 1148 } 1149 1150 // ---------------------------------------------------------------------------- 1151 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz, 1152 jint effectId) { 1153 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1154 if (lpTrack == NULL) { 1155 jniThrowException(env, "java/lang/IllegalStateException", 1156 "Unable to retrieve AudioTrack pointer for attachAuxEffect()"); 1157 return (jint)AUDIO_JAVA_ERROR; 1158 } 1159 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) ); 1160 } 1161 1162 static jboolean android_media_AudioTrack_setOutputDevice( 1163 JNIEnv *env, jobject thiz, jint device_id) { 1164 1165 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1166 if (lpTrack == 0) { 1167 return false; 1168 } 1169 return lpTrack->setOutputDevice(device_id) == NO_ERROR; 1170 } 1171 1172 static jint android_media_AudioTrack_getRoutedDeviceId( 1173 JNIEnv *env, jobject thiz) { 1174 1175 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1176 if (lpTrack == NULL) { 1177 return 0; 1178 } 1179 return (jint)lpTrack->getRoutedDeviceId(); 1180 } 1181 1182 static void android_media_AudioTrack_enableDeviceCallback( 1183 JNIEnv *env, jobject thiz) { 1184 1185 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1186 if (lpTrack == NULL) { 1187 return; 1188 } 1189 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 1190 thiz, javaAudioTrackFields.jniData); 1191 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) { 1192 return; 1193 } 1194 pJniStorage->mDeviceCallback = 1195 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref, 1196 javaAudioTrackFields.postNativeEventInJava); 1197 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback); 1198 } 1199 1200 static void android_media_AudioTrack_disableDeviceCallback( 1201 JNIEnv *env, jobject thiz) { 1202 1203 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1204 if (lpTrack == NULL) { 1205 return; 1206 } 1207 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 1208 thiz, javaAudioTrackFields.jniData); 1209 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) { 1210 return; 1211 } 1212 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback); 1213 pJniStorage->mDeviceCallback.clear(); 1214 } 1215 1216 static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) { 1217 return FCC_8; 1218 } 1219 1220 // Pass through the arguments to the AudioFlinger track implementation. 1221 static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz, 1222 jobject jconfig, jobject joperation) { 1223 // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java 1224 const int VOLUME_SHAPER_INVALID_OPERATION = -38; 1225 1226 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1227 if (lpTrack == nullptr) { 1228 return (jint)VOLUME_SHAPER_INVALID_OPERATION; 1229 } 1230 1231 sp<VolumeShaper::Configuration> configuration; 1232 sp<VolumeShaper::Operation> operation; 1233 if (jconfig != nullptr) { 1234 configuration = VolumeShaperHelper::convertJobjectToConfiguration( 1235 env, gVolumeShaperFields, jconfig); 1236 ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str()); 1237 } 1238 if (joperation != nullptr) { 1239 operation = VolumeShaperHelper::convertJobjectToOperation( 1240 env, gVolumeShaperFields, joperation); 1241 ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str()); 1242 } 1243 VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation); 1244 if (status == INVALID_OPERATION) { 1245 status = VOLUME_SHAPER_INVALID_OPERATION; 1246 } 1247 return (jint)status; // if status < 0 an error, else a VolumeShaper id 1248 } 1249 1250 // Pass through the arguments to the AudioFlinger track implementation. 1251 static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz, 1252 jint id) { 1253 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1254 if (lpTrack == nullptr) { 1255 return (jobject)nullptr; 1256 } 1257 1258 sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id); 1259 if (state.get() == nullptr) { 1260 return (jobject)nullptr; 1261 } 1262 return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state); 1263 } 1264 1265 static int android_media_AudioTrack_setPresentation( 1266 JNIEnv *env, jobject thiz, jint presentationId, jint programId) { 1267 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1268 if (lpTrack == NULL) { 1269 jniThrowException(env, "java/lang/IllegalStateException", 1270 "AudioTrack not initialized"); 1271 return (jint)AUDIO_JAVA_ERROR; 1272 } 1273 1274 return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId); 1275 } 1276 1277 // ---------------------------------------------------------------------------- 1278 // ---------------------------------------------------------------------------- 1279 static const JNINativeMethod gMethods[] = { 1280 // name, signature, funcPtr 1281 {"native_start", "()V", (void *)android_media_AudioTrack_start}, 1282 {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, 1283 {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, 1284 {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, 1285 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I", 1286 (void *)android_media_AudioTrack_setup}, 1287 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, 1288 {"native_release", "()V", (void *)android_media_AudioTrack_release}, 1289 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, 1290 {"native_write_native_bytes", 1291 "(Ljava/lang/Object;IIIZ)I", 1292 (void *)android_media_AudioTrack_write_native_bytes}, 1293 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, 1294 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, 1295 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, 1296 {"native_get_buffer_size_frames", 1297 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames}, 1298 {"native_set_buffer_size_frames", 1299 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames}, 1300 {"native_get_buffer_capacity_frames", 1301 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames}, 1302 {"native_set_playback_rate", 1303 "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, 1304 {"native_get_playback_rate", 1305 "()I", (void *)android_media_AudioTrack_get_playback_rate}, 1306 {"native_set_playback_params", 1307 "(Landroid/media/PlaybackParams;)V", 1308 (void *)android_media_AudioTrack_set_playback_params}, 1309 {"native_get_playback_params", 1310 "()Landroid/media/PlaybackParams;", 1311 (void *)android_media_AudioTrack_get_playback_params}, 1312 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, 1313 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, 1314 {"native_set_pos_update_period", 1315 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, 1316 {"native_get_pos_update_period", 1317 "()I", (void *)android_media_AudioTrack_get_pos_update_period}, 1318 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, 1319 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, 1320 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, 1321 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, 1322 {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, 1323 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, 1324 {"native_getMetrics", "()Landroid/os/PersistableBundle;", 1325 (void *)android_media_AudioTrack_native_getMetrics}, 1326 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, 1327 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, 1328 {"native_get_output_sample_rate", 1329 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, 1330 {"native_get_min_buff_size", 1331 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, 1332 {"native_setAuxEffectSendLevel", 1333 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, 1334 {"native_attachAuxEffect", 1335 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, 1336 {"native_setOutputDevice", "(I)Z", 1337 (void *)android_media_AudioTrack_setOutputDevice}, 1338 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, 1339 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback}, 1340 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback}, 1341 {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8}, 1342 {"native_applyVolumeShaper", 1343 "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I", 1344 (void *)android_media_AudioTrack_apply_volume_shaper}, 1345 {"native_getVolumeShaperState", 1346 "(I)Landroid/media/VolumeShaper$State;", 1347 (void *)android_media_AudioTrack_get_volume_shaper_state}, 1348 {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, 1349 }; 1350 1351 1352 // field names found in android/media/AudioTrack.java 1353 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 1354 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" 1355 #define JAVA_JNIDATA_FIELD_NAME "mJniData" 1356 #define JAVA_STREAMTYPE_FIELD_NAME "mStreamType" 1357 1358 // ---------------------------------------------------------------------------- 1359 // preconditions: 1360 // theClass is valid 1361 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, 1362 const char* constName, int* constVal) { 1363 jfieldID javaConst = NULL; 1364 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); 1365 if (javaConst != NULL) { 1366 *constVal = pEnv->GetStaticIntField(theClass, javaConst); 1367 return true; 1368 } else { 1369 ALOGE("Can't find %s.%s", className, constName); 1370 return false; 1371 } 1372 } 1373 1374 1375 // ---------------------------------------------------------------------------- 1376 int register_android_media_AudioTrack(JNIEnv *env) 1377 { 1378 // must be first 1379 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 1380 1381 javaAudioTrackFields.nativeTrackInJavaObj = NULL; 1382 javaAudioTrackFields.postNativeEventInJava = NULL; 1383 1384 // Get the AudioTrack class 1385 jclass audioTrackClass = FindClassOrDie(env, kClassPathName); 1386 1387 // Get the postEvent method 1388 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, 1389 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME, 1390 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 1391 1392 // Get the variables fields 1393 // nativeTrackInJavaObj 1394 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env, 1395 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J"); 1396 // jniData 1397 javaAudioTrackFields.jniData = GetFieldIDOrDie(env, 1398 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J"); 1399 // fieldStreamType 1400 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env, 1401 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I"); 1402 1403 env->DeleteLocalRef(audioTrackClass); 1404 1405 // Get the AudioAttributes class and fields 1406 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); 1407 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I"); 1408 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env, 1409 audioAttrClass, "mContentType", "I"); 1410 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); 1411 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, 1412 audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); 1413 1414 env->DeleteLocalRef(audioAttrClass); 1415 1416 // initialize PlaybackParams field info 1417 gPlaybackParamsFields.init(env); 1418 1419 gVolumeShaperFields.init(env); 1420 return res; 1421 } 1422 1423 1424 // ---------------------------------------------------------------------------- 1425