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