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