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