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 "android_media_AudioTrack.h"
21
22 #include <JNIHelp.h>
23 #include <JniConstants.h>
24 #include "core_jni_helpers.h"
25
26 #include "ScopedBytes.h"
27
28 #include <utils/Log.h>
29 #include <media/AudioSystem.h>
30 #include <media/AudioTrack.h>
31 #include <audio_utils/primitives.h>
32
33 #include <binder/MemoryHeapBase.h>
34 #include <binder/MemoryBase.h>
35
36 #include "android_media_AudioFormat.h"
37 #include "android_media_AudioErrors.h"
38 #include "android_media_PlaybackParams.h"
39 #include "android_media_DeviceCallback.h"
40
41 // ----------------------------------------------------------------------------
42
43 using namespace android;
44
45 // ----------------------------------------------------------------------------
46 static const char* const kClassPathName = "android/media/AudioTrack";
47 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
48
49 struct audio_track_fields_t {
50 // these fields provide access from C++ to the...
51 jmethodID postNativeEventInJava; //... event post callback method
52 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
53 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
54 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
55 };
56 struct audio_attributes_fields_t {
57 jfieldID fieldUsage; // AudioAttributes.mUsage
58 jfieldID fieldContentType; // AudioAttributes.mContentType
59 jfieldID fieldFlags; // AudioAttributes.mFlags
60 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
61 };
62 static audio_track_fields_t javaAudioTrackFields;
63 static audio_attributes_fields_t javaAudioAttrFields;
64 static PlaybackParams::fields_t gPlaybackParamsFields;
65
66 struct audiotrack_callback_cookie {
67 jclass audioTrack_class;
68 jobject audioTrack_ref;
69 bool busy;
70 Condition cond;
71 };
72
73 // keep these values in sync with AudioTrack.java
74 #define MODE_STATIC 0
75 #define MODE_STREAM 1
76
77 // ----------------------------------------------------------------------------
78 class AudioTrackJniStorage {
79 public:
80 sp<MemoryHeapBase> mMemHeap;
81 sp<MemoryBase> mMemBase;
82 audiotrack_callback_cookie mCallbackData;
83 sp<JNIDeviceCallback> mDeviceCallback;
84
AudioTrackJniStorage()85 AudioTrackJniStorage() {
86 mCallbackData.audioTrack_class = 0;
87 mCallbackData.audioTrack_ref = 0;
88 }
89
~AudioTrackJniStorage()90 ~AudioTrackJniStorage() {
91 mMemBase.clear();
92 mMemHeap.clear();
93 }
94
allocSharedMem(int sizeInBytes)95 bool allocSharedMem(int sizeInBytes) {
96 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
97 if (mMemHeap->getHeapID() < 0) {
98 return false;
99 }
100 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
101 return true;
102 }
103 };
104
105 static Mutex sLock;
106 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
107
108 // ----------------------------------------------------------------------------
109 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100
110
111 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16
112 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17
113 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18
114 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19
115 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20
116
117 // ----------------------------------------------------------------------------
audioCallback(int event,void * user,void * info)118 static void audioCallback(int event, void* user, void *info) {
119
120 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
121 {
122 Mutex::Autolock l(sLock);
123 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
124 return;
125 }
126 callbackInfo->busy = true;
127 }
128
129 switch (event) {
130 case AudioTrack::EVENT_MARKER: {
131 JNIEnv *env = AndroidRuntime::getJNIEnv();
132 if (user != NULL && env != NULL) {
133 env->CallStaticVoidMethod(
134 callbackInfo->audioTrack_class,
135 javaAudioTrackFields.postNativeEventInJava,
136 callbackInfo->audioTrack_ref, event, 0,0, NULL);
137 if (env->ExceptionCheck()) {
138 env->ExceptionDescribe();
139 env->ExceptionClear();
140 }
141 }
142 } break;
143
144 case AudioTrack::EVENT_NEW_POS: {
145 JNIEnv *env = AndroidRuntime::getJNIEnv();
146 if (user != NULL && env != NULL) {
147 env->CallStaticVoidMethod(
148 callbackInfo->audioTrack_class,
149 javaAudioTrackFields.postNativeEventInJava,
150 callbackInfo->audioTrack_ref, event, 0,0, NULL);
151 if (env->ExceptionCheck()) {
152 env->ExceptionDescribe();
153 env->ExceptionClear();
154 }
155 }
156 } break;
157 }
158
159 {
160 Mutex::Autolock l(sLock);
161 callbackInfo->busy = false;
162 callbackInfo->cond.broadcast();
163 }
164 }
165
166
167 // ----------------------------------------------------------------------------
getAudioTrack(JNIEnv * env,jobject thiz)168 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
169 {
170 Mutex::Autolock l(sLock);
171 AudioTrack* const at =
172 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
173 return sp<AudioTrack>(at);
174 }
175
setAudioTrack(JNIEnv * env,jobject thiz,const sp<AudioTrack> & at)176 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
177 {
178 Mutex::Autolock l(sLock);
179 sp<AudioTrack> old =
180 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
181 if (at.get()) {
182 at->incStrong((void*)setAudioTrack);
183 }
184 if (old != 0) {
185 old->decStrong((void*)setAudioTrack);
186 }
187 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
188 return old;
189 }
190
191 // ----------------------------------------------------------------------------
android_media_AudioTrack_getAudioTrack(JNIEnv * env,jobject audioTrackObj)192 sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
193 return getAudioTrack(env, audioTrackObj);
194 }
195
196 // This function converts Java channel masks to a native channel mask.
197 // validity should be checked with audio_is_output_channel().
nativeChannelMaskFromJavaChannelMasks(jint channelPositionMask,jint channelIndexMask)198 static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
199 jint channelPositionMask, jint channelIndexMask)
200 {
201 if (channelIndexMask != 0) { // channel index mask takes priority
202 // To convert to a native channel mask, the Java channel index mask
203 // requires adding the index representation.
204 return audio_channel_mask_from_representation_and_bits(
205 AUDIO_CHANNEL_REPRESENTATION_INDEX,
206 channelIndexMask);
207 }
208 // To convert to a native channel mask, the Java channel position mask
209 // requires a shift by 2 to skip the two deprecated channel
210 // configurations "default" and "mono".
211 return (audio_channel_mask_t)(channelPositionMask >> 2);
212 }
213
214 // ----------------------------------------------------------------------------
215 static jint
android_media_AudioTrack_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelPositionMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jint memoryMode,jintArray jSession,jlong nativeAudioTrack)216 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
217 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
218 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
219 jlong nativeAudioTrack) {
220
221 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
222 "nativeAudioTrack=0x%llX",
223 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
224 nativeAudioTrack);
225
226 sp<AudioTrack> lpTrack = 0;
227
228 if (jSession == NULL) {
229 ALOGE("Error creating AudioTrack: invalid session ID pointer");
230 return (jint) AUDIO_JAVA_ERROR;
231 }
232
233 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
234 if (nSession == NULL) {
235 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
236 return (jint) AUDIO_JAVA_ERROR;
237 }
238 audio_session_t sessionId = (audio_session_t) nSession[0];
239 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
240 nSession = NULL;
241
242 AudioTrackJniStorage* lpJniStorage = NULL;
243
244 audio_attributes_t *paa = NULL;
245
246 jclass clazz = env->GetObjectClass(thiz);
247 if (clazz == NULL) {
248 ALOGE("Can't find %s when setting up callback.", kClassPathName);
249 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
250 }
251
252 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
253 if (nativeAudioTrack == 0) {
254 if (jaa == 0) {
255 ALOGE("Error creating AudioTrack: invalid audio attributes");
256 return (jint) AUDIO_JAVA_ERROR;
257 }
258
259 if (jSampleRate == 0) {
260 ALOGE("Error creating AudioTrack: invalid sample rates");
261 return (jint) AUDIO_JAVA_ERROR;
262 }
263
264 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
265 int sampleRateInHertz = sampleRates[0];
266 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
267
268 // Invalid channel representations are caught by !audio_is_output_channel() below.
269 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
270 channelPositionMask, channelIndexMask);
271 if (!audio_is_output_channel(nativeChannelMask)) {
272 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
273 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
274 }
275
276 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
277
278 // check the format.
279 // This function was called from Java, so we compare the format against the Java constants
280 audio_format_t format = audioFormatToNative(audioFormat);
281 if (format == AUDIO_FORMAT_INVALID) {
282 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
283 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
284 }
285
286 // compute the frame count
287 size_t frameCount;
288 if (audio_is_linear_pcm(format)) {
289 const size_t bytesPerSample = audio_bytes_per_sample(format);
290 frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
291 } else {
292 frameCount = buffSizeInBytes;
293 }
294
295 // create the native AudioTrack object
296 lpTrack = new AudioTrack();
297
298 // read the AudioAttributes values
299 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
300 const jstring jtags =
301 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
302 const char* tags = env->GetStringUTFChars(jtags, NULL);
303 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
304 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
305 env->ReleaseStringUTFChars(jtags, tags);
306 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
307 paa->content_type =
308 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
309 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
310
311 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
312 paa->usage, paa->content_type, paa->flags, paa->tags);
313
314 // initialize the callback information:
315 // this data will be passed with every AudioTrack callback
316 lpJniStorage = new AudioTrackJniStorage();
317 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
318 // we use a weak reference so the AudioTrack object can be garbage collected.
319 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
320 lpJniStorage->mCallbackData.busy = false;
321
322 // initialize the native AudioTrack object
323 status_t status = NO_ERROR;
324 switch (memoryMode) {
325 case MODE_STREAM:
326
327 status = lpTrack->set(
328 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
329 sampleRateInHertz,
330 format,// word length, PCM
331 nativeChannelMask,
332 frameCount,
333 AUDIO_OUTPUT_FLAG_NONE,
334 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
335 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
336 0,// shared mem
337 true,// thread can call Java
338 sessionId,// audio session ID
339 AudioTrack::TRANSFER_SYNC,
340 NULL, // default offloadInfo
341 -1, -1, // default uid, pid values
342 paa);
343 break;
344
345 case MODE_STATIC:
346 // AudioTrack is using shared memory
347
348 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
349 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
350 goto native_init_failure;
351 }
352
353 status = lpTrack->set(
354 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
355 sampleRateInHertz,
356 format,// word length, PCM
357 nativeChannelMask,
358 frameCount,
359 AUDIO_OUTPUT_FLAG_NONE,
360 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
361 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
362 lpJniStorage->mMemBase,// shared mem
363 true,// thread can call Java
364 sessionId,// audio session ID
365 AudioTrack::TRANSFER_SHARED,
366 NULL, // default offloadInfo
367 -1, -1, // default uid, pid values
368 paa);
369 break;
370
371 default:
372 ALOGE("Unknown mode %d", memoryMode);
373 goto native_init_failure;
374 }
375
376 if (status != NO_ERROR) {
377 ALOGE("Error %d initializing AudioTrack", status);
378 goto native_init_failure;
379 }
380 } else { // end if (nativeAudioTrack == 0)
381 lpTrack = (AudioTrack*)nativeAudioTrack;
382 // TODO: We need to find out which members of the Java AudioTrack might
383 // need to be initialized from the Native AudioTrack
384 // these are directly returned from getters:
385 // mSampleRate
386 // mAudioFormat
387 // mStreamType
388 // mChannelConfiguration
389 // mChannelCount
390 // mState (?)
391 // mPlayState (?)
392 // these may be used internally (Java AudioTrack.audioParamCheck():
393 // mChannelMask
394 // mChannelIndexMask
395 // mDataLoadMode
396
397 // initialize the callback information:
398 // this data will be passed with every AudioTrack callback
399 lpJniStorage = new AudioTrackJniStorage();
400 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
401 // we use a weak reference so the AudioTrack object can be garbage collected.
402 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
403 lpJniStorage->mCallbackData.busy = false;
404 }
405
406 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
407 if (nSession == NULL) {
408 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
409 goto native_init_failure;
410 }
411 // read the audio session ID back from AudioTrack in case we create a new session
412 nSession[0] = lpTrack->getSessionId();
413 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
414 nSession = NULL;
415
416 {
417 const jint elements[1] = { (jint) lpTrack->getSampleRate() };
418 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
419 }
420
421 { // scope for the lock
422 Mutex::Autolock l(sLock);
423 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
424 }
425 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
426 // of the Java object (in mNativeTrackInJavaObj)
427 setAudioTrack(env, thiz, lpTrack);
428
429 // save the JNI resources so we can free them later
430 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
431 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
432
433 // since we had audio attributes, the stream type was derived from them during the
434 // creation of the native AudioTrack: push the same value to the Java object
435 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
436 if (paa != NULL) {
437 // audio attributes were copied in AudioTrack creation
438 free(paa);
439 paa = NULL;
440 }
441
442
443 return (jint) AUDIO_JAVA_SUCCESS;
444
445 // failures:
446 native_init_failure:
447 if (paa != NULL) {
448 free(paa);
449 }
450 if (nSession != NULL) {
451 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
452 }
453 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
454 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
455 delete lpJniStorage;
456 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
457
458 // lpTrack goes out of scope, so reference count drops to zero
459 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
460 }
461
462 // ----------------------------------------------------------------------------
463 static void
android_media_AudioTrack_start(JNIEnv * env,jobject thiz)464 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
465 {
466 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
467 if (lpTrack == NULL) {
468 jniThrowException(env, "java/lang/IllegalStateException",
469 "Unable to retrieve AudioTrack pointer for start()");
470 return;
471 }
472
473 lpTrack->start();
474 }
475
476
477 // ----------------------------------------------------------------------------
478 static void
android_media_AudioTrack_stop(JNIEnv * env,jobject thiz)479 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
480 {
481 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
482 if (lpTrack == NULL) {
483 jniThrowException(env, "java/lang/IllegalStateException",
484 "Unable to retrieve AudioTrack pointer for stop()");
485 return;
486 }
487
488 lpTrack->stop();
489 }
490
491
492 // ----------------------------------------------------------------------------
493 static void
android_media_AudioTrack_pause(JNIEnv * env,jobject thiz)494 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
495 {
496 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
497 if (lpTrack == NULL) {
498 jniThrowException(env, "java/lang/IllegalStateException",
499 "Unable to retrieve AudioTrack pointer for pause()");
500 return;
501 }
502
503 lpTrack->pause();
504 }
505
506
507 // ----------------------------------------------------------------------------
508 static void
android_media_AudioTrack_flush(JNIEnv * env,jobject thiz)509 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
510 {
511 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
512 if (lpTrack == NULL) {
513 jniThrowException(env, "java/lang/IllegalStateException",
514 "Unable to retrieve AudioTrack pointer for flush()");
515 return;
516 }
517
518 lpTrack->flush();
519 }
520
521 // ----------------------------------------------------------------------------
522 static void
android_media_AudioTrack_set_volume(JNIEnv * env,jobject thiz,jfloat leftVol,jfloat rightVol)523 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
524 {
525 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
526 if (lpTrack == NULL) {
527 jniThrowException(env, "java/lang/IllegalStateException",
528 "Unable to retrieve AudioTrack pointer for setVolume()");
529 return;
530 }
531
532 lpTrack->setVolume(leftVol, rightVol);
533 }
534
535 // ----------------------------------------------------------------------------
536
537 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioTrack_release(JNIEnv * env,jobject thiz)538 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
539 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
540 if (lpTrack == NULL) {
541 return;
542 }
543 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
544
545 // delete the JNI data
546 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
547 thiz, javaAudioTrackFields.jniData);
548 // reset the native resources in the Java object so any attempt to access
549 // them after a call to release fails.
550 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
551
552 if (pJniStorage) {
553 Mutex::Autolock l(sLock);
554 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
555 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
556 while (lpCookie->busy) {
557 if (lpCookie->cond.waitRelative(sLock,
558 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
559 NO_ERROR) {
560 break;
561 }
562 }
563 sAudioTrackCallBackCookies.remove(lpCookie);
564 // delete global refs created in native_setup
565 env->DeleteGlobalRef(lpCookie->audioTrack_class);
566 env->DeleteGlobalRef(lpCookie->audioTrack_ref);
567 delete pJniStorage;
568 }
569 }
570
571
572 // ----------------------------------------------------------------------------
android_media_AudioTrack_finalize(JNIEnv * env,jobject thiz)573 static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) {
574 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
575 android_media_AudioTrack_release(env, thiz);
576 }
577
578 // overloaded JNI array helper functions (same as in android_media_AudioRecord)
579 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)580 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
581 return env->GetByteArrayElements(array, isCopy);
582 }
583
584 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)585 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
586 env->ReleaseByteArrayElements(array, elems, mode);
587 }
588
589 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)590 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
591 return env->GetShortArrayElements(array, isCopy);
592 }
593
594 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)595 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
596 env->ReleaseShortArrayElements(array, elems, mode);
597 }
598
599 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)600 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
601 return env->GetFloatArrayElements(array, isCopy);
602 }
603
604 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)605 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
606 env->ReleaseFloatArrayElements(array, elems, mode);
607 }
608
609 static inline
interpretWriteSizeError(ssize_t writeSize)610 jint interpretWriteSizeError(ssize_t writeSize) {
611 if (writeSize == WOULD_BLOCK) {
612 return (jint)0;
613 } else if (writeSize == NO_INIT) {
614 return AUDIO_JAVA_DEAD_OBJECT;
615 } else {
616 ALOGE("Error %zd during AudioTrack native read", writeSize);
617 return nativeToJavaStatus(writeSize);
618 }
619 }
620
621 // ----------------------------------------------------------------------------
622 template <typename T>
writeToTrack(const sp<AudioTrack> & track,jint audioFormat,const T * data,jint offsetInSamples,jint sizeInSamples,bool blocking)623 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
624 jint offsetInSamples, jint sizeInSamples, bool blocking) {
625 // give the data to the native AudioTrack object (the data starts at the offset)
626 ssize_t written = 0;
627 // regular write() or copy the data to the AudioTrack's shared memory?
628 size_t sizeInBytes = sizeInSamples * sizeof(T);
629 if (track->sharedBuffer() == 0) {
630 written = track->write(data + offsetInSamples, sizeInBytes, blocking);
631 // for compatibility with earlier behavior of write(), return 0 in this case
632 if (written == (ssize_t) WOULD_BLOCK) {
633 written = 0;
634 }
635 } else {
636 // writing to shared memory, check for capacity
637 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
638 sizeInBytes = track->sharedBuffer()->size();
639 }
640 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
641 written = sizeInBytes;
642 }
643 if (written >= 0) {
644 return written / sizeof(T);
645 }
646 return interpretWriteSizeError(written);
647 }
648
649 // ----------------------------------------------------------------------------
650 template <typename T>
android_media_AudioTrack_writeArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jint javaAudioFormat,jboolean isWriteBlocking)651 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
652 T javaAudioData,
653 jint offsetInSamples, jint sizeInSamples,
654 jint javaAudioFormat,
655 jboolean isWriteBlocking) {
656 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
657 // offsetInSamples, sizeInSamples);
658 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
659 if (lpTrack == NULL) {
660 jniThrowException(env, "java/lang/IllegalStateException",
661 "Unable to retrieve AudioTrack pointer for write()");
662 return (jint)AUDIO_JAVA_INVALID_OPERATION;
663 }
664
665 if (javaAudioData == NULL) {
666 ALOGE("NULL java array of audio data to play");
667 return (jint)AUDIO_JAVA_BAD_VALUE;
668 }
669
670 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
671 // a way that it becomes much more efficient. When doing so, we will have to prevent the
672 // AudioSystem callback to be called while in critical section (in case of media server
673 // process crash for instance)
674
675 // get the pointer for the audio data from the java array
676 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
677 if (cAudioData == NULL) {
678 ALOGE("Error retrieving source of audio data to play");
679 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
680 }
681
682 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
683 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
684
685 envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
686
687 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
688 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
689 return samplesWritten;
690 }
691
692 // ----------------------------------------------------------------------------
android_media_AudioTrack_write_native_bytes(JNIEnv * env,jobject thiz,jbyteArray javaBytes,jint byteOffset,jint sizeInBytes,jint javaAudioFormat,jboolean isWriteBlocking)693 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
694 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
695 jint javaAudioFormat, jboolean isWriteBlocking) {
696 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
697 // offsetInBytes, sizeInBytes);
698 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
699 if (lpTrack == NULL) {
700 jniThrowException(env, "java/lang/IllegalStateException",
701 "Unable to retrieve AudioTrack pointer for write()");
702 return (jint)AUDIO_JAVA_INVALID_OPERATION;
703 }
704
705 ScopedBytesRO bytes(env, javaBytes);
706 if (bytes.get() == NULL) {
707 ALOGE("Error retrieving source of audio data to play, can't play");
708 return (jint)AUDIO_JAVA_BAD_VALUE;
709 }
710
711 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
712 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
713
714 return written;
715 }
716
717 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_size_frames(JNIEnv * env,jobject thiz)718 static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) {
719 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
720 if (lpTrack == NULL) {
721 jniThrowException(env, "java/lang/IllegalStateException",
722 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
723 return (jint)AUDIO_JAVA_ERROR;
724 }
725
726 ssize_t result = lpTrack->getBufferSizeInFrames();
727 if (result < 0) {
728 jniThrowException(env, "java/lang/IllegalStateException",
729 "Internal error detected in getBufferSizeInFrames() = " + result);
730 return (jint)AUDIO_JAVA_ERROR;
731 }
732 return (jint)result;
733 }
734
735 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_buffer_size_frames(JNIEnv * env,jobject thiz,jint bufferSizeInFrames)736 static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
737 jobject thiz, jint bufferSizeInFrames) {
738 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
739 if (lpTrack == NULL) {
740 jniThrowException(env, "java/lang/IllegalStateException",
741 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
742 return (jint)AUDIO_JAVA_ERROR;
743 }
744 // Value will be coerced into the valid range.
745 // But internal values are unsigned, size_t, so we need to clip
746 // against zero here where it is signed.
747 if (bufferSizeInFrames < 0) {
748 bufferSizeInFrames = 0;
749 }
750 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
751 if (result < 0) {
752 jniThrowException(env, "java/lang/IllegalStateException",
753 "Internal error detected in setBufferSizeInFrames() = " + result);
754 return (jint)AUDIO_JAVA_ERROR;
755 }
756 return (jint)result;
757 }
758
759 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv * env,jobject thiz)760 static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) {
761 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
762 if (lpTrack == NULL) {
763 jniThrowException(env, "java/lang/IllegalStateException",
764 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
765 return (jint)AUDIO_JAVA_ERROR;
766 }
767
768 return lpTrack->frameCount();
769 }
770
771 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_rate(JNIEnv * env,jobject thiz,jint sampleRateInHz)772 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
773 jint sampleRateInHz) {
774 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
775 if (lpTrack == NULL) {
776 jniThrowException(env, "java/lang/IllegalStateException",
777 "Unable to retrieve AudioTrack pointer for setSampleRate()");
778 return (jint)AUDIO_JAVA_ERROR;
779 }
780 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
781 }
782
783
784 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_rate(JNIEnv * env,jobject thiz)785 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
786 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
787 if (lpTrack == NULL) {
788 jniThrowException(env, "java/lang/IllegalStateException",
789 "Unable to retrieve AudioTrack pointer for getSampleRate()");
790 return (jint)AUDIO_JAVA_ERROR;
791 }
792 return (jint) lpTrack->getSampleRate();
793 }
794
795
796 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_params(JNIEnv * env,jobject thiz,jobject params)797 static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz,
798 jobject params) {
799 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
800 if (lpTrack == NULL) {
801 jniThrowException(env, "java/lang/IllegalStateException",
802 "AudioTrack not initialized");
803 return;
804 }
805
806 PlaybackParams pbp;
807 pbp.fillFromJobject(env, gPlaybackParamsFields, params);
808
809 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
810 pbp.speedSet, pbp.audioRate.mSpeed,
811 pbp.pitchSet, pbp.audioRate.mPitch,
812 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
813 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
814
815 // to simulate partially set params, we do a read-modify-write.
816 // TODO: pass in the valid set mask into AudioTrack.
817 AudioPlaybackRate rate = lpTrack->getPlaybackRate();
818 bool updatedRate = false;
819 if (pbp.speedSet) {
820 rate.mSpeed = pbp.audioRate.mSpeed;
821 updatedRate = true;
822 }
823 if (pbp.pitchSet) {
824 rate.mPitch = pbp.audioRate.mPitch;
825 updatedRate = true;
826 }
827 if (pbp.audioFallbackModeSet) {
828 rate.mFallbackMode = pbp.audioRate.mFallbackMode;
829 updatedRate = true;
830 }
831 if (pbp.audioStretchModeSet) {
832 rate.mStretchMode = pbp.audioRate.mStretchMode;
833 updatedRate = true;
834 }
835 if (updatedRate) {
836 if (lpTrack->setPlaybackRate(rate) != OK) {
837 jniThrowException(env, "java/lang/IllegalArgumentException",
838 "arguments out of range");
839 }
840 }
841 }
842
843
844 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_params(JNIEnv * env,jobject thiz,jobject params)845 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz,
846 jobject params) {
847 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
848 if (lpTrack == NULL) {
849 jniThrowException(env, "java/lang/IllegalStateException",
850 "AudioTrack not initialized");
851 return NULL;
852 }
853
854 PlaybackParams pbs;
855 pbs.audioRate = lpTrack->getPlaybackRate();
856 pbs.speedSet = true;
857 pbs.pitchSet = true;
858 pbs.audioFallbackModeSet = true;
859 pbs.audioStretchModeSet = true;
860 return pbs.asJobject(env, gPlaybackParamsFields);
861 }
862
863
864 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)865 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
866 jint markerPos) {
867 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
868 if (lpTrack == NULL) {
869 jniThrowException(env, "java/lang/IllegalStateException",
870 "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
871 return (jint)AUDIO_JAVA_ERROR;
872 }
873 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
874 }
875
876
877 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_marker_pos(JNIEnv * env,jobject thiz)878 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
879 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
880 uint32_t markerPos = 0;
881
882 if (lpTrack == NULL) {
883 jniThrowException(env, "java/lang/IllegalStateException",
884 "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
885 return (jint)AUDIO_JAVA_ERROR;
886 }
887 lpTrack->getMarkerPosition(&markerPos);
888 return (jint)markerPos;
889 }
890
891
892 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)893 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
894 jint period) {
895 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
896 if (lpTrack == NULL) {
897 jniThrowException(env, "java/lang/IllegalStateException",
898 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
899 return (jint)AUDIO_JAVA_ERROR;
900 }
901 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
902 }
903
904
905 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_pos_update_period(JNIEnv * env,jobject thiz)906 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
907 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
908 uint32_t period = 0;
909
910 if (lpTrack == NULL) {
911 jniThrowException(env, "java/lang/IllegalStateException",
912 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
913 return (jint)AUDIO_JAVA_ERROR;
914 }
915 lpTrack->getPositionUpdatePeriod(&period);
916 return (jint)period;
917 }
918
919
920 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_position(JNIEnv * env,jobject thiz,jint position)921 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
922 jint position) {
923 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
924 if (lpTrack == NULL) {
925 jniThrowException(env, "java/lang/IllegalStateException",
926 "Unable to retrieve AudioTrack pointer for setPosition()");
927 return (jint)AUDIO_JAVA_ERROR;
928 }
929 return nativeToJavaStatus( lpTrack->setPosition(position) );
930 }
931
932
933 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_position(JNIEnv * env,jobject thiz)934 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
935 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
936 uint32_t position = 0;
937
938 if (lpTrack == NULL) {
939 jniThrowException(env, "java/lang/IllegalStateException",
940 "Unable to retrieve AudioTrack pointer for getPosition()");
941 return (jint)AUDIO_JAVA_ERROR;
942 }
943 lpTrack->getPosition(&position);
944 return (jint)position;
945 }
946
947
948 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_latency(JNIEnv * env,jobject thiz)949 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) {
950 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
951
952 if (lpTrack == NULL) {
953 jniThrowException(env, "java/lang/IllegalStateException",
954 "Unable to retrieve AudioTrack pointer for latency()");
955 return (jint)AUDIO_JAVA_ERROR;
956 }
957 return (jint)lpTrack->latency();
958 }
959
960 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_underrun_count(JNIEnv * env,jobject thiz)961 static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) {
962 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
963
964 if (lpTrack == NULL) {
965 jniThrowException(env, "java/lang/IllegalStateException",
966 "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
967 return (jint)AUDIO_JAVA_ERROR;
968 }
969 return (jint)lpTrack->getUnderrunCount();
970 }
971
972 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_timestamp(JNIEnv * env,jobject thiz,jlongArray jTimestamp)973 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) {
974 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
975
976 if (lpTrack == NULL) {
977 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
978 return (jint)AUDIO_JAVA_ERROR;
979 }
980 AudioTimestamp timestamp;
981 status_t status = lpTrack->getTimestamp(timestamp);
982 if (status == OK) {
983 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
984 if (nTimestamp == NULL) {
985 ALOGE("Unable to get array for getTimestamp()");
986 return (jint)AUDIO_JAVA_ERROR;
987 }
988 nTimestamp[0] = (jlong) timestamp.mPosition;
989 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
990 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
991 }
992 return (jint) nativeToJavaStatus(status);
993 }
994
995
996 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_loop(JNIEnv * env,jobject thiz,jint loopStart,jint loopEnd,jint loopCount)997 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
998 jint loopStart, jint loopEnd, jint loopCount) {
999 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1000 if (lpTrack == NULL) {
1001 jniThrowException(env, "java/lang/IllegalStateException",
1002 "Unable to retrieve AudioTrack pointer for setLoop()");
1003 return (jint)AUDIO_JAVA_ERROR;
1004 }
1005 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
1006 }
1007
1008
1009 // ----------------------------------------------------------------------------
android_media_AudioTrack_reload(JNIEnv * env,jobject thiz)1010 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
1011 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1012 if (lpTrack == NULL) {
1013 jniThrowException(env, "java/lang/IllegalStateException",
1014 "Unable to retrieve AudioTrack pointer for reload()");
1015 return (jint)AUDIO_JAVA_ERROR;
1016 }
1017 return nativeToJavaStatus( lpTrack->reload() );
1018 }
1019
1020
1021 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_output_sample_rate(JNIEnv * env,jobject thiz,jint javaStreamType)1022 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz,
1023 jint javaStreamType) {
1024 uint32_t afSamplingRate;
1025 // convert the stream type from Java to native value
1026 // FIXME: code duplication with android_media_AudioTrack_setup()
1027 audio_stream_type_t nativeStreamType;
1028 switch (javaStreamType) {
1029 case AUDIO_STREAM_VOICE_CALL:
1030 case AUDIO_STREAM_SYSTEM:
1031 case AUDIO_STREAM_RING:
1032 case AUDIO_STREAM_MUSIC:
1033 case AUDIO_STREAM_ALARM:
1034 case AUDIO_STREAM_NOTIFICATION:
1035 case AUDIO_STREAM_BLUETOOTH_SCO:
1036 case AUDIO_STREAM_DTMF:
1037 nativeStreamType = (audio_stream_type_t) javaStreamType;
1038 break;
1039 default:
1040 nativeStreamType = AUDIO_STREAM_DEFAULT;
1041 break;
1042 }
1043
1044 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1045 if (status != NO_ERROR) {
1046 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1047 "in AudioTrack JNI", status, nativeStreamType);
1048 return DEFAULT_OUTPUT_SAMPLE_RATE;
1049 } else {
1050 return afSamplingRate;
1051 }
1052 }
1053
1054
1055 // ----------------------------------------------------------------------------
1056 // returns the minimum required size for the successful creation of a streaming AudioTrack
1057 // returns -1 if there was an error querying the hardware.
android_media_AudioTrack_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)1058 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
1059 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
1060
1061 size_t frameCount;
1062 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1063 sampleRateInHertz);
1064 if (status != NO_ERROR) {
1065 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1066 sampleRateInHertz, status);
1067 return -1;
1068 }
1069 const audio_format_t format = audioFormatToNative(audioFormat);
1070 if (audio_has_proportional_frames(format)) {
1071 const size_t bytesPerSample = audio_bytes_per_sample(format);
1072 return frameCount * channelCount * bytesPerSample;
1073 } else {
1074 return frameCount;
1075 }
1076 }
1077
1078 // ----------------------------------------------------------------------------
1079 static jint
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv * env,jobject thiz,jfloat level)1080 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1081 {
1082 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1083 if (lpTrack == NULL ) {
1084 jniThrowException(env, "java/lang/IllegalStateException",
1085 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
1086 return -1;
1087 }
1088
1089 status_t status = lpTrack->setAuxEffectSendLevel(level);
1090 if (status != NO_ERROR) {
1091 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1092 level, status);
1093 }
1094 return (jint) status;
1095 }
1096
1097 // ----------------------------------------------------------------------------
android_media_AudioTrack_attachAuxEffect(JNIEnv * env,jobject thiz,jint effectId)1098 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
1099 jint effectId) {
1100 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1101 if (lpTrack == NULL) {
1102 jniThrowException(env, "java/lang/IllegalStateException",
1103 "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
1104 return (jint)AUDIO_JAVA_ERROR;
1105 }
1106 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
1107 }
1108
android_media_AudioTrack_setOutputDevice(JNIEnv * env,jobject thiz,jint device_id)1109 static jboolean android_media_AudioTrack_setOutputDevice(
1110 JNIEnv *env, jobject thiz, jint device_id) {
1111
1112 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1113 if (lpTrack == 0) {
1114 return false;
1115 }
1116 return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1117 }
1118
android_media_AudioTrack_getRoutedDeviceId(JNIEnv * env,jobject thiz)1119 static jint android_media_AudioTrack_getRoutedDeviceId(
1120 JNIEnv *env, jobject thiz) {
1121
1122 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1123 if (lpTrack == NULL) {
1124 return 0;
1125 }
1126 return (jint)lpTrack->getRoutedDeviceId();
1127 }
1128
android_media_AudioTrack_enableDeviceCallback(JNIEnv * env,jobject thiz)1129 static void android_media_AudioTrack_enableDeviceCallback(
1130 JNIEnv *env, jobject thiz) {
1131
1132 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1133 if (lpTrack == NULL) {
1134 return;
1135 }
1136 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1137 thiz, javaAudioTrackFields.jniData);
1138 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1139 return;
1140 }
1141 pJniStorage->mDeviceCallback =
1142 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1143 javaAudioTrackFields.postNativeEventInJava);
1144 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1145 }
1146
android_media_AudioTrack_disableDeviceCallback(JNIEnv * env,jobject thiz)1147 static void android_media_AudioTrack_disableDeviceCallback(
1148 JNIEnv *env, jobject thiz) {
1149
1150 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1151 if (lpTrack == NULL) {
1152 return;
1153 }
1154 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1155 thiz, javaAudioTrackFields.jniData);
1156 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1157 return;
1158 }
1159 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1160 pJniStorage->mDeviceCallback.clear();
1161 }
1162
android_media_AudioTrack_get_FCC_8(JNIEnv * env,jobject thiz)1163 static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
1164 return FCC_8;
1165 }
1166
1167
1168 // ----------------------------------------------------------------------------
1169 // ----------------------------------------------------------------------------
1170 static const JNINativeMethod gMethods[] = {
1171 // name, signature, funcPtr
1172 {"native_start", "()V", (void *)android_media_AudioTrack_start},
1173 {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
1174 {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
1175 {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
1176 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
1177 (void *)android_media_AudioTrack_setup},
1178 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
1179 {"native_release", "()V", (void *)android_media_AudioTrack_release},
1180 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1181 {"native_write_native_bytes",
1182 "(Ljava/lang/Object;IIIZ)I",
1183 (void *)android_media_AudioTrack_write_native_bytes},
1184 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1185 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1186 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
1187 {"native_get_buffer_size_frames",
1188 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
1189 {"native_set_buffer_size_frames",
1190 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
1191 {"native_get_buffer_capacity_frames",
1192 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
1193 {"native_set_playback_rate",
1194 "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
1195 {"native_get_playback_rate",
1196 "()I", (void *)android_media_AudioTrack_get_playback_rate},
1197 {"native_set_playback_params",
1198 "(Landroid/media/PlaybackParams;)V",
1199 (void *)android_media_AudioTrack_set_playback_params},
1200 {"native_get_playback_params",
1201 "()Landroid/media/PlaybackParams;",
1202 (void *)android_media_AudioTrack_get_playback_params},
1203 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
1204 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
1205 {"native_set_pos_update_period",
1206 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
1207 {"native_get_pos_update_period",
1208 "()I", (void *)android_media_AudioTrack_get_pos_update_period},
1209 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
1210 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
1211 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
1212 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
1213 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
1214 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
1215 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
1216 {"native_get_output_sample_rate",
1217 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
1218 {"native_get_min_buff_size",
1219 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
1220 {"native_setAuxEffectSendLevel",
1221 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1222 {"native_attachAuxEffect",
1223 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
1224 {"native_setOutputDevice", "(I)Z",
1225 (void *)android_media_AudioTrack_setOutputDevice},
1226 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1227 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1228 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1229 {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
1230 };
1231
1232
1233 // field names found in android/media/AudioTrack.java
1234 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
1235 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
1236 #define JAVA_JNIDATA_FIELD_NAME "mJniData"
1237 #define JAVA_STREAMTYPE_FIELD_NAME "mStreamType"
1238
1239 // ----------------------------------------------------------------------------
1240 // preconditions:
1241 // theClass is valid
android_media_getIntConstantFromClass(JNIEnv * pEnv,jclass theClass,const char * className,const char * constName,int * constVal)1242 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1243 const char* constName, int* constVal) {
1244 jfieldID javaConst = NULL;
1245 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1246 if (javaConst != NULL) {
1247 *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1248 return true;
1249 } else {
1250 ALOGE("Can't find %s.%s", className, constName);
1251 return false;
1252 }
1253 }
1254
1255
1256 // ----------------------------------------------------------------------------
register_android_media_AudioTrack(JNIEnv * env)1257 int register_android_media_AudioTrack(JNIEnv *env)
1258 {
1259 // must be first
1260 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1261
1262 javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1263 javaAudioTrackFields.postNativeEventInJava = NULL;
1264
1265 // Get the AudioTrack class
1266 jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1267
1268 // Get the postEvent method
1269 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1270 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1271 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1272
1273 // Get the variables fields
1274 // nativeTrackInJavaObj
1275 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1276 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1277 // jniData
1278 javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1279 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1280 // fieldStreamType
1281 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1282 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1283
1284 env->DeleteLocalRef(audioTrackClass);
1285
1286 // Get the AudioAttributes class and fields
1287 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1288 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1289 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1290 audioAttrClass, "mContentType", "I");
1291 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1292 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1293 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
1294
1295 env->DeleteLocalRef(audioAttrClass);
1296
1297 // initialize PlaybackParams field info
1298 gPlaybackParamsFields.init(env);
1299
1300 return res;
1301 }
1302
1303
1304 // ----------------------------------------------------------------------------
1305