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 <stdio.h> 21 #include <unistd.h> 22 #include <fcntl.h> 23 #include <math.h> 24 25 #include <jni.h> 26 #include <JNIHelp.h> 27 #include <android_runtime/AndroidRuntime.h> 28 29 #include <utils/Log.h> 30 #include <media/AudioSystem.h> 31 #include <media/AudioTrack.h> 32 33 #include <binder/MemoryHeapBase.h> 34 #include <binder/MemoryBase.h> 35 36 #include <cutils/bitops.h> 37 38 #include <system/audio.h> 39 40 // ---------------------------------------------------------------------------- 41 42 using namespace android; 43 44 // ---------------------------------------------------------------------------- 45 static const char* const kClassPathName = "android/media/AudioTrack"; 46 47 struct fields_t { 48 // these fields provide access from C++ to the... 49 jmethodID postNativeEventInJava; //... event post callback method 50 int PCM16; //... format constants 51 int PCM8; //... format constants 52 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object 53 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack 54 }; 55 static fields_t javaAudioTrackFields; 56 57 struct audiotrack_callback_cookie { 58 jclass audioTrack_class; 59 jobject audioTrack_ref; 60 bool busy; 61 Condition cond; 62 }; 63 64 // keep these values in sync with AudioTrack.java 65 #define MODE_STATIC 0 66 #define MODE_STREAM 1 67 68 // ---------------------------------------------------------------------------- 69 class AudioTrackJniStorage { 70 public: 71 sp<MemoryHeapBase> mMemHeap; 72 sp<MemoryBase> mMemBase; 73 audiotrack_callback_cookie mCallbackData; 74 audio_stream_type_t mStreamType; 75 76 AudioTrackJniStorage() { 77 mCallbackData.audioTrack_class = 0; 78 mCallbackData.audioTrack_ref = 0; 79 mStreamType = AUDIO_STREAM_DEFAULT; 80 } 81 82 ~AudioTrackJniStorage() { 83 mMemBase.clear(); 84 mMemHeap.clear(); 85 } 86 87 bool allocSharedMem(int sizeInBytes) { 88 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); 89 if (mMemHeap->getHeapID() < 0) { 90 return false; 91 } 92 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); 93 return true; 94 } 95 }; 96 97 static Mutex sLock; 98 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; 99 100 // ---------------------------------------------------------------------------- 101 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 102 103 #define AUDIOTRACK_SUCCESS 0 104 #define AUDIOTRACK_ERROR -1 105 #define AUDIOTRACK_ERROR_BAD_VALUE -2 106 #define AUDIOTRACK_ERROR_INVALID_OPERATION -3 107 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16 108 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17 109 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18 110 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19 111 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20 112 113 114 jint android_media_translateErrorCode(int code) { 115 switch (code) { 116 case NO_ERROR: 117 return AUDIOTRACK_SUCCESS; 118 case BAD_VALUE: 119 return AUDIOTRACK_ERROR_BAD_VALUE; 120 case INVALID_OPERATION: 121 return AUDIOTRACK_ERROR_INVALID_OPERATION; 122 default: 123 return AUDIOTRACK_ERROR; 124 } 125 } 126 127 128 // ---------------------------------------------------------------------------- 129 static void audioCallback(int event, void* user, void *info) { 130 131 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; 132 { 133 Mutex::Autolock l(sLock); 134 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) { 135 return; 136 } 137 callbackInfo->busy = true; 138 } 139 140 if (event == AudioTrack::EVENT_MORE_DATA) { 141 // set size to 0 to signal we're not using the callback to write more data 142 AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; 143 pBuff->size = 0; 144 145 } else if (event == AudioTrack::EVENT_MARKER) { 146 JNIEnv *env = AndroidRuntime::getJNIEnv(); 147 if (user && env) { 148 env->CallStaticVoidMethod( 149 callbackInfo->audioTrack_class, 150 javaAudioTrackFields.postNativeEventInJava, 151 callbackInfo->audioTrack_ref, event, 0,0, NULL); 152 if (env->ExceptionCheck()) { 153 env->ExceptionDescribe(); 154 env->ExceptionClear(); 155 } 156 } 157 158 } else if (event == AudioTrack::EVENT_NEW_POS) { 159 JNIEnv *env = AndroidRuntime::getJNIEnv(); 160 if (user && env) { 161 env->CallStaticVoidMethod( 162 callbackInfo->audioTrack_class, 163 javaAudioTrackFields.postNativeEventInJava, 164 callbackInfo->audioTrack_ref, event, 0,0, NULL); 165 if (env->ExceptionCheck()) { 166 env->ExceptionDescribe(); 167 env->ExceptionClear(); 168 } 169 } 170 } 171 { 172 Mutex::Autolock l(sLock); 173 callbackInfo->busy = false; 174 callbackInfo->cond.broadcast(); 175 } 176 } 177 178 179 // ---------------------------------------------------------------------------- 180 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) 181 { 182 Mutex::Autolock l(sLock); 183 AudioTrack* const at = 184 (AudioTrack*)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 185 return sp<AudioTrack>(at); 186 } 187 188 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at) 189 { 190 Mutex::Autolock l(sLock); 191 sp<AudioTrack> old = 192 (AudioTrack*)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 193 if (at.get()) { 194 at->incStrong((void*)setAudioTrack); 195 } 196 if (old != 0) { 197 old->decStrong((void*)setAudioTrack); 198 } 199 env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)at.get()); 200 return old; 201 } 202 203 // ---------------------------------------------------------------------------- 204 static int 205 android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, 206 jint streamType, jint sampleRateInHertz, jint javaChannelMask, 207 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) 208 { 209 ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d", 210 sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes); 211 uint32_t afSampleRate; 212 size_t afFrameCount; 213 214 if (AudioSystem::getOutputFrameCount(&afFrameCount, (audio_stream_type_t) streamType) != NO_ERROR) { 215 ALOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); 216 return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; 217 } 218 if (AudioSystem::getOutputSamplingRate(&afSampleRate, (audio_stream_type_t) streamType) != NO_ERROR) { 219 ALOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate."); 220 return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; 221 } 222 223 // Java channel masks don't map directly to the native definition, but it's a simple shift 224 // to skip the two deprecated channel configurations "default" and "mono". 225 uint32_t nativeChannelMask = ((uint32_t)javaChannelMask) >> 2; 226 227 if (!audio_is_output_channel(nativeChannelMask)) { 228 ALOGE("Error creating AudioTrack: invalid channel mask."); 229 return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; 230 } 231 232 int nbChannels = popcount(nativeChannelMask); 233 234 // check the stream type 235 audio_stream_type_t atStreamType; 236 switch (streamType) { 237 case AUDIO_STREAM_VOICE_CALL: 238 case AUDIO_STREAM_SYSTEM: 239 case AUDIO_STREAM_RING: 240 case AUDIO_STREAM_MUSIC: 241 case AUDIO_STREAM_ALARM: 242 case AUDIO_STREAM_NOTIFICATION: 243 case AUDIO_STREAM_BLUETOOTH_SCO: 244 case AUDIO_STREAM_DTMF: 245 atStreamType = (audio_stream_type_t) streamType; 246 break; 247 default: 248 ALOGE("Error creating AudioTrack: unknown stream type."); 249 return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; 250 } 251 252 // check the format. 253 // This function was called from Java, so we compare the format against the Java constants 254 if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) { 255 ALOGE("Error creating AudioTrack: unsupported audio format."); 256 return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; 257 } 258 259 // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class 260 // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled 261 // in android_media_AudioTrack_native_write_byte() 262 if ((audioFormat == javaAudioTrackFields.PCM8) 263 && (memoryMode == MODE_STATIC)) { 264 ALOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \ 265 buff size of %dbytes, switching to 16bit, buff size of %dbytes", 266 buffSizeInBytes, 2*buffSizeInBytes); 267 audioFormat = javaAudioTrackFields.PCM16; 268 // we will need twice the memory to store the data 269 buffSizeInBytes *= 2; 270 } 271 272 // compute the frame count 273 int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1; 274 audio_format_t format = audioFormat == javaAudioTrackFields.PCM16 ? 275 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT; 276 int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample); 277 278 jclass clazz = env->GetObjectClass(thiz); 279 if (clazz == NULL) { 280 ALOGE("Can't find %s when setting up callback.", kClassPathName); 281 return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 282 } 283 284 if (jSession == NULL) { 285 ALOGE("Error creating AudioTrack: invalid session ID pointer"); 286 return AUDIOTRACK_ERROR; 287 } 288 289 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 290 if (nSession == NULL) { 291 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 292 return AUDIOTRACK_ERROR; 293 } 294 int sessionId = nSession[0]; 295 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 296 nSession = NULL; 297 298 // create the native AudioTrack object 299 sp<AudioTrack> lpTrack = new AudioTrack(); 300 301 // initialize the callback information: 302 // this data will be passed with every AudioTrack callback 303 AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); 304 lpJniStorage->mStreamType = atStreamType; 305 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 306 // we use a weak reference so the AudioTrack object can be garbage collected. 307 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 308 lpJniStorage->mCallbackData.busy = false; 309 310 // initialize the native AudioTrack object 311 switch (memoryMode) { 312 case MODE_STREAM: 313 314 lpTrack->set( 315 atStreamType,// stream type 316 sampleRateInHertz, 317 format,// word length, PCM 318 nativeChannelMask, 319 frameCount, 320 AUDIO_OUTPUT_FLAG_NONE, 321 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 322 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 323 0,// shared mem 324 true,// thread can call Java 325 sessionId);// audio session ID 326 break; 327 328 case MODE_STATIC: 329 // AudioTrack is using shared memory 330 331 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { 332 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); 333 goto native_init_failure; 334 } 335 336 lpTrack->set( 337 atStreamType,// stream type 338 sampleRateInHertz, 339 format,// word length, PCM 340 nativeChannelMask, 341 frameCount, 342 AUDIO_OUTPUT_FLAG_NONE, 343 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 344 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 345 lpJniStorage->mMemBase,// shared mem 346 true,// thread can call Java 347 sessionId);// audio session ID 348 break; 349 350 default: 351 ALOGE("Unknown mode %d", memoryMode); 352 goto native_init_failure; 353 } 354 355 if (lpTrack->initCheck() != NO_ERROR) { 356 ALOGE("Error initializing AudioTrack"); 357 goto native_init_failure; 358 } 359 360 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 361 if (nSession == NULL) { 362 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 363 goto native_init_failure; 364 } 365 // read the audio session ID back from AudioTrack in case we create a new session 366 nSession[0] = lpTrack->getSessionId(); 367 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 368 nSession = NULL; 369 370 { // scope for the lock 371 Mutex::Autolock l(sLock); 372 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData); 373 } 374 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field 375 // of the Java object (in mNativeTrackInJavaObj) 376 setAudioTrack(env, thiz, lpTrack); 377 378 // save the JNI resources so we can free them later 379 //ALOGV("storing lpJniStorage: %x\n", (int)lpJniStorage); 380 env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage); 381 382 return AUDIOTRACK_SUCCESS; 383 384 // failures: 385 native_init_failure: 386 if (nSession != NULL) { 387 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 388 } 389 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); 390 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); 391 delete lpJniStorage; 392 env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); 393 394 return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 395 } 396 397 398 // ---------------------------------------------------------------------------- 399 static void 400 android_media_AudioTrack_start(JNIEnv *env, jobject thiz) 401 { 402 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 403 if (lpTrack == NULL) { 404 jniThrowException(env, "java/lang/IllegalStateException", 405 "Unable to retrieve AudioTrack pointer for start()"); 406 return; 407 } 408 409 lpTrack->start(); 410 } 411 412 413 // ---------------------------------------------------------------------------- 414 static void 415 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) 416 { 417 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 418 if (lpTrack == NULL) { 419 jniThrowException(env, "java/lang/IllegalStateException", 420 "Unable to retrieve AudioTrack pointer for stop()"); 421 return; 422 } 423 424 lpTrack->stop(); 425 } 426 427 428 // ---------------------------------------------------------------------------- 429 static void 430 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) 431 { 432 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 433 if (lpTrack == NULL) { 434 jniThrowException(env, "java/lang/IllegalStateException", 435 "Unable to retrieve AudioTrack pointer for pause()"); 436 return; 437 } 438 439 lpTrack->pause(); 440 } 441 442 443 // ---------------------------------------------------------------------------- 444 static void 445 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) 446 { 447 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 448 if (lpTrack == NULL) { 449 jniThrowException(env, "java/lang/IllegalStateException", 450 "Unable to retrieve AudioTrack pointer for flush()"); 451 return; 452 } 453 454 lpTrack->flush(); 455 } 456 457 // ---------------------------------------------------------------------------- 458 static void 459 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) 460 { 461 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 462 if (lpTrack == NULL) { 463 jniThrowException(env, "java/lang/IllegalStateException", 464 "Unable to retrieve AudioTrack pointer for setVolume()"); 465 return; 466 } 467 468 lpTrack->setVolume(leftVol, rightVol); 469 } 470 471 // ---------------------------------------------------------------------------- 472 473 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 474 static void android_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) { 475 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); 476 if (lpTrack == NULL) { 477 return; 478 } 479 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); 480 lpTrack->stop(); 481 482 // delete the JNI data 483 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField( 484 thiz, javaAudioTrackFields.jniData); 485 // reset the native resources in the Java object so any attempt to access 486 // them after a call to release fails. 487 env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); 488 489 if (pJniStorage) { 490 Mutex::Autolock l(sLock); 491 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData; 492 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage); 493 while (lpCookie->busy) { 494 if (lpCookie->cond.waitRelative(sLock, 495 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 496 NO_ERROR) { 497 break; 498 } 499 } 500 sAudioTrackCallBackCookies.remove(lpCookie); 501 // delete global refs created in native_setup 502 env->DeleteGlobalRef(lpCookie->audioTrack_class); 503 env->DeleteGlobalRef(lpCookie->audioTrack_ref); 504 delete pJniStorage; 505 } 506 } 507 508 509 // ---------------------------------------------------------------------------- 510 static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) { 511 //ALOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz); 512 android_media_AudioTrack_native_release(env, thiz); 513 } 514 515 // ---------------------------------------------------------------------------- 516 jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, 517 jint offsetInBytes, jint sizeInBytes) { 518 // give the data to the native AudioTrack object (the data starts at the offset) 519 ssize_t written = 0; 520 // regular write() or copy the data to the AudioTrack's shared memory? 521 if (track->sharedBuffer() == 0) { 522 written = track->write(data + offsetInBytes, sizeInBytes); 523 } else { 524 if (audioFormat == javaAudioTrackFields.PCM16) { 525 // writing to shared memory, check for capacity 526 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { 527 sizeInBytes = track->sharedBuffer()->size(); 528 } 529 memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); 530 written = sizeInBytes; 531 } else if (audioFormat == javaAudioTrackFields.PCM8) { 532 // data contains 8bit data we need to expand to 16bit before copying 533 // to the shared memory 534 // writing to shared memory, check for capacity, 535 // note that input data will occupy 2X the input space due to 8 to 16bit conversion 536 if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) { 537 sizeInBytes = track->sharedBuffer()->size() / 2; 538 } 539 int count = sizeInBytes; 540 int16_t *dst = (int16_t *)track->sharedBuffer()->pointer(); 541 const int8_t *src = (const int8_t *)(data + offsetInBytes); 542 while (count--) { 543 *dst++ = (int16_t)(*src++^0x80) << 8; 544 } 545 // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide 546 // the 8bit mixer restriction from the user of this function 547 written = sizeInBytes; 548 } 549 } 550 return written; 551 552 } 553 554 // ---------------------------------------------------------------------------- 555 static jint android_media_AudioTrack_native_write_byte(JNIEnv *env, jobject thiz, 556 jbyteArray javaAudioData, 557 jint offsetInBytes, jint sizeInBytes, 558 jint javaAudioFormat) { 559 //ALOGV("android_media_AudioTrack_native_write_byte(offset=%d, sizeInBytes=%d) called", 560 // offsetInBytes, sizeInBytes); 561 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 562 if (lpTrack == NULL) { 563 jniThrowException(env, "java/lang/IllegalStateException", 564 "Unable to retrieve AudioTrack pointer for write()"); 565 return 0; 566 } 567 568 // get the pointer for the audio data from the java array 569 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 570 // a way that it becomes much more efficient. When doing so, we will have to prevent the 571 // AudioSystem callback to be called while in critical section (in case of media server 572 // process crash for instance) 573 jbyte* cAudioData = NULL; 574 if (javaAudioData) { 575 cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 576 if (cAudioData == NULL) { 577 ALOGE("Error retrieving source of audio data to play, can't play"); 578 return 0; // out of memory or no data to load 579 } 580 } else { 581 ALOGE("NULL java array of audio data to play, can't play"); 582 return 0; 583 } 584 585 jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); 586 587 env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); 588 589 //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", 590 // (int)written, (int)(sizeInBytes), (int)offsetInBytes); 591 return written; 592 } 593 594 595 // ---------------------------------------------------------------------------- 596 static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz, 597 jshortArray javaAudioData, 598 jint offsetInShorts, jint sizeInShorts, 599 jint javaAudioFormat) { 600 return (android_media_AudioTrack_native_write_byte(env, thiz, 601 (jbyteArray) javaAudioData, 602 offsetInShorts*2, sizeInShorts*2, 603 javaAudioFormat) 604 / 2); 605 } 606 607 608 // ---------------------------------------------------------------------------- 609 static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { 610 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 611 if (lpTrack == NULL) { 612 jniThrowException(env, "java/lang/IllegalStateException", 613 "Unable to retrieve AudioTrack pointer for frameCount()"); 614 return AUDIOTRACK_ERROR; 615 } 616 617 return lpTrack->frameCount(); 618 } 619 620 621 // ---------------------------------------------------------------------------- 622 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, 623 jint sampleRateInHz) { 624 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 625 if (lpTrack == NULL) { 626 jniThrowException(env, "java/lang/IllegalStateException", 627 "Unable to retrieve AudioTrack pointer for setSampleRate()"); 628 return AUDIOTRACK_ERROR; 629 } 630 return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz)); 631 } 632 633 634 // ---------------------------------------------------------------------------- 635 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { 636 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 637 if (lpTrack == NULL) { 638 jniThrowException(env, "java/lang/IllegalStateException", 639 "Unable to retrieve AudioTrack pointer for getSampleRate()"); 640 return AUDIOTRACK_ERROR; 641 } 642 return (jint) lpTrack->getSampleRate(); 643 } 644 645 646 // ---------------------------------------------------------------------------- 647 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, 648 jint markerPos) { 649 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 650 if (lpTrack == NULL) { 651 jniThrowException(env, "java/lang/IllegalStateException", 652 "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); 653 return AUDIOTRACK_ERROR; 654 } 655 return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) ); 656 } 657 658 659 // ---------------------------------------------------------------------------- 660 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { 661 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 662 uint32_t markerPos = 0; 663 664 if (lpTrack == NULL) { 665 jniThrowException(env, "java/lang/IllegalStateException", 666 "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); 667 return AUDIOTRACK_ERROR; 668 } 669 lpTrack->getMarkerPosition(&markerPos); 670 return (jint)markerPos; 671 } 672 673 674 // ---------------------------------------------------------------------------- 675 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, 676 jint period) { 677 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 678 if (lpTrack == NULL) { 679 jniThrowException(env, "java/lang/IllegalStateException", 680 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); 681 return AUDIOTRACK_ERROR; 682 } 683 return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) ); 684 } 685 686 687 // ---------------------------------------------------------------------------- 688 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { 689 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 690 uint32_t period = 0; 691 692 if (lpTrack == NULL) { 693 jniThrowException(env, "java/lang/IllegalStateException", 694 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); 695 return AUDIOTRACK_ERROR; 696 } 697 lpTrack->getPositionUpdatePeriod(&period); 698 return (jint)period; 699 } 700 701 702 // ---------------------------------------------------------------------------- 703 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, 704 jint position) { 705 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 706 if (lpTrack == NULL) { 707 jniThrowException(env, "java/lang/IllegalStateException", 708 "Unable to retrieve AudioTrack pointer for setPosition()"); 709 return AUDIOTRACK_ERROR; 710 } 711 return android_media_translateErrorCode( lpTrack->setPosition(position) ); 712 } 713 714 715 // ---------------------------------------------------------------------------- 716 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { 717 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 718 uint32_t position = 0; 719 720 if (lpTrack == NULL) { 721 jniThrowException(env, "java/lang/IllegalStateException", 722 "Unable to retrieve AudioTrack pointer for getPosition()"); 723 return AUDIOTRACK_ERROR; 724 } 725 lpTrack->getPosition(&position); 726 return (jint)position; 727 } 728 729 730 // ---------------------------------------------------------------------------- 731 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) { 732 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 733 734 if (lpTrack == NULL) { 735 jniThrowException(env, "java/lang/IllegalStateException", 736 "Unable to retrieve AudioTrack pointer for latency()"); 737 return AUDIOTRACK_ERROR; 738 } 739 return (jint)lpTrack->latency(); 740 } 741 742 743 // ---------------------------------------------------------------------------- 744 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, 745 jint loopStart, jint loopEnd, jint loopCount) { 746 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 747 if (lpTrack == NULL) { 748 jniThrowException(env, "java/lang/IllegalStateException", 749 "Unable to retrieve AudioTrack pointer for setLoop()"); 750 return AUDIOTRACK_ERROR; 751 } 752 return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); 753 } 754 755 756 // ---------------------------------------------------------------------------- 757 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { 758 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 759 if (lpTrack == NULL) { 760 jniThrowException(env, "java/lang/IllegalStateException", 761 "Unable to retrieve AudioTrack pointer for reload()"); 762 return AUDIOTRACK_ERROR; 763 } 764 return android_media_translateErrorCode( lpTrack->reload() ); 765 } 766 767 768 // ---------------------------------------------------------------------------- 769 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz, 770 jint javaStreamType) { 771 uint32_t afSamplingRate; 772 // convert the stream type from Java to native value 773 // FIXME: code duplication with android_media_AudioTrack_native_setup() 774 audio_stream_type_t nativeStreamType; 775 switch (javaStreamType) { 776 case AUDIO_STREAM_VOICE_CALL: 777 case AUDIO_STREAM_SYSTEM: 778 case AUDIO_STREAM_RING: 779 case AUDIO_STREAM_MUSIC: 780 case AUDIO_STREAM_ALARM: 781 case AUDIO_STREAM_NOTIFICATION: 782 case AUDIO_STREAM_BLUETOOTH_SCO: 783 case AUDIO_STREAM_DTMF: 784 nativeStreamType = (audio_stream_type_t) javaStreamType; 785 break; 786 default: 787 nativeStreamType = AUDIO_STREAM_DEFAULT; 788 break; 789 } 790 791 if (AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType) != NO_ERROR) { 792 ALOGE("AudioSystem::getOutputSamplingRate() for stream type %d failed in AudioTrack JNI", 793 nativeStreamType); 794 return DEFAULT_OUTPUT_SAMPLE_RATE; 795 } else { 796 return afSamplingRate; 797 } 798 } 799 800 801 // ---------------------------------------------------------------------------- 802 // returns the minimum required size for the successful creation of a streaming AudioTrack 803 // returns -1 if there was an error querying the hardware. 804 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 805 jint sampleRateInHertz, jint nbChannels, jint audioFormat) { 806 807 size_t frameCount = 0; 808 if (AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, 809 sampleRateInHertz) != NO_ERROR) { 810 return -1; 811 } 812 return frameCount * nbChannels * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1); 813 } 814 815 // ---------------------------------------------------------------------------- 816 static void 817 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level ) 818 { 819 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 820 if (lpTrack == NULL ) { 821 jniThrowException(env, "java/lang/IllegalStateException", 822 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()"); 823 return; 824 } 825 826 lpTrack->setAuxEffectSendLevel(level); 827 } 828 829 // ---------------------------------------------------------------------------- 830 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz, 831 jint effectId) { 832 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 833 if (lpTrack == NULL) { 834 jniThrowException(env, "java/lang/IllegalStateException", 835 "Unable to retrieve AudioTrack pointer for attachAuxEffect()"); 836 return AUDIOTRACK_ERROR; 837 } 838 return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) ); 839 } 840 841 // ---------------------------------------------------------------------------- 842 // ---------------------------------------------------------------------------- 843 static JNINativeMethod gMethods[] = { 844 // name, signature, funcPtr 845 {"native_start", "()V", (void *)android_media_AudioTrack_start}, 846 {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, 847 {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, 848 {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, 849 {"native_setup", "(Ljava/lang/Object;IIIIII[I)I", 850 (void *)android_media_AudioTrack_native_setup}, 851 {"native_finalize", "()V", (void *)android_media_AudioTrack_native_finalize}, 852 {"native_release", "()V", (void *)android_media_AudioTrack_native_release}, 853 {"native_write_byte", "([BIII)I", (void *)android_media_AudioTrack_native_write_byte}, 854 {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_native_write_short}, 855 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, 856 {"native_get_native_frame_count", 857 "()I", (void *)android_media_AudioTrack_get_native_frame_count}, 858 {"native_set_playback_rate", 859 "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, 860 {"native_get_playback_rate", 861 "()I", (void *)android_media_AudioTrack_get_playback_rate}, 862 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, 863 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, 864 {"native_set_pos_update_period", 865 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, 866 {"native_get_pos_update_period", 867 "()I", (void *)android_media_AudioTrack_get_pos_update_period}, 868 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, 869 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, 870 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, 871 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, 872 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, 873 {"native_get_output_sample_rate", 874 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, 875 {"native_get_min_buff_size", 876 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, 877 {"native_setAuxEffectSendLevel", 878 "(F)V", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, 879 {"native_attachAuxEffect", 880 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, 881 }; 882 883 884 // field names found in android/media/AudioTrack.java 885 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 886 #define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" 887 #define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" 888 #define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" 889 #define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" 890 #define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" 891 #define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" 892 #define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" 893 #define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" 894 #define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" 895 #define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" 896 #define JAVA_CONST_STREAM_DTMF_NAME "STREAM_DTMF" 897 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" 898 #define JAVA_JNIDATA_FIELD_NAME "mJniData" 899 900 #define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" 901 #define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager" 902 903 // ---------------------------------------------------------------------------- 904 // preconditions: 905 // theClass is valid 906 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, 907 const char* constName, int* constVal) { 908 jfieldID javaConst = NULL; 909 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); 910 if (javaConst != NULL) { 911 *constVal = pEnv->GetStaticIntField(theClass, javaConst); 912 return true; 913 } else { 914 ALOGE("Can't find %s.%s", className, constName); 915 return false; 916 } 917 } 918 919 920 // ---------------------------------------------------------------------------- 921 int register_android_media_AudioTrack(JNIEnv *env) 922 { 923 javaAudioTrackFields.nativeTrackInJavaObj = NULL; 924 javaAudioTrackFields.postNativeEventInJava = NULL; 925 926 // Get the AudioTrack class 927 jclass audioTrackClass = env->FindClass(kClassPathName); 928 if (audioTrackClass == NULL) { 929 ALOGE("Can't find %s", kClassPathName); 930 return -1; 931 } 932 933 // Get the postEvent method 934 javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID( 935 audioTrackClass, 936 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 937 if (javaAudioTrackFields.postNativeEventInJava == NULL) { 938 ALOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME); 939 return -1; 940 } 941 942 // Get the variables fields 943 // nativeTrackInJavaObj 944 javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID( 945 audioTrackClass, 946 JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I"); 947 if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) { 948 ALOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME); 949 return -1; 950 } 951 // jniData; 952 javaAudioTrackFields.jniData = env->GetFieldID( 953 audioTrackClass, 954 JAVA_JNIDATA_FIELD_NAME, "I"); 955 if (javaAudioTrackFields.jniData == NULL) { 956 ALOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME); 957 return -1; 958 } 959 960 // Get the format constants from the AudioFormat class 961 jclass audioFormatClass = NULL; 962 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); 963 if (audioFormatClass == NULL) { 964 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); 965 return -1; 966 } 967 if ( !android_media_getIntConstantFromClass(env, audioFormatClass, 968 JAVA_AUDIOFORMAT_CLASS_NAME, 969 JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16)) 970 || !android_media_getIntConstantFromClass(env, audioFormatClass, 971 JAVA_AUDIOFORMAT_CLASS_NAME, 972 JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) { 973 // error log performed in android_media_getIntConstantFromClass() 974 return -1; 975 } 976 977 return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); 978 } 979 980 981 // ---------------------------------------------------------------------------- 982