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