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