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