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