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