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