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