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