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