1 /*
2 * Copyright (C) 2015 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
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "audio-track-native"
19
20 #include "Blob.h"
21 #include "Gate.h"
22 #include "sl-utils.h"
23
24 #include <deque>
25 #include <utils/Errors.h>
26
27 // Select whether to use STL shared pointer or to use Android strong pointer.
28 // We really don't promote any sharing of this object for its lifetime, but nevertheless could
29 // change the shared pointer value on the fly if desired.
30 #define USE_SHARED_POINTER
31
32 #ifdef USE_SHARED_POINTER
33 #include <memory>
34 template <typename T> using shared_pointer = std::shared_ptr<T>;
35 #else
36 #include <utils/RefBase.h>
37 template <typename T> using shared_pointer = android::sp<T>;
38 #endif
39
40 using namespace android;
41
42 // Must be kept in sync with Java android.media.cts.AudioTrackNative.WriteFlags
43 enum {
44 WRITE_FLAG_BLOCKING = (1 << 0),
45 };
46
47 // TODO: Add a single buffer blocking write mode which does not require additional memory.
48 // TODO: Add internal buffer memory (e.g. use circular buffer, right now mallocs on heap).
49
50 class AudioTrackNative
51 #ifndef USE_SHARED_POINTER
52 : public RefBase // android strong pointers require RefBase
53 #endif
54 {
55 public:
AudioTrackNative()56 AudioTrackNative() :
57 mEngineObj(NULL),
58 mEngine(NULL),
59 mOutputMixObj(NULL),
60 mPlayerObj(NULL),
61 mPlay(NULL),
62 mBufferQueue(NULL),
63 mPlayState(SL_PLAYSTATE_STOPPED),
64 mNumBuffers(0)
65 { }
66
~AudioTrackNative()67 ~AudioTrackNative() {
68 close();
69 }
70
71 typedef std::lock_guard<std::recursive_mutex> auto_lock;
72
open(jint numChannels,jint channelMask,jint sampleRate,jboolean useFloat,jint numBuffers)73 status_t open(jint numChannels, jint channelMask,
74 jint sampleRate, jboolean useFloat, jint numBuffers) {
75 close();
76 auto_lock l(mLock);
77 mEngineObj = OpenSLEngine();
78 if (mEngineObj == NULL) {
79 ALOGW("cannot create OpenSL ES engine");
80 return INVALID_OPERATION;
81 }
82
83 SLresult res;
84 for (;;) {
85 /* Get the SL Engine Interface which is implicit */
86 res = (*mEngineObj)->GetInterface(mEngineObj, SL_IID_ENGINE, (void *)&mEngine);
87 if (res != SL_RESULT_SUCCESS) break;
88
89 // Create Output Mix object to be used by player
90 res = (*mEngine)->CreateOutputMix(
91 mEngine, &mOutputMixObj, 0 /* numInterfaces */,
92 NULL /* pInterfaceIds */, NULL /* pInterfaceRequired */);
93 if (res != SL_RESULT_SUCCESS) break;
94
95 // Realizing the Output Mix object in synchronous mode.
96 res = (*mOutputMixObj)->Realize(mOutputMixObj, SL_BOOLEAN_FALSE /* async */);
97 if (res != SL_RESULT_SUCCESS) break;
98
99 /* Setup the data source structure for the buffer queue */
100 SLDataLocator_BufferQueue bufferQueue;
101 bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
102 bufferQueue.numBuffers = numBuffers;
103 mNumBuffers = numBuffers;
104
105 /* Setup the format of the content in the buffer queue */
106
107 SLAndroidDataFormat_PCM_EX pcm;
108 pcm.formatType = useFloat ? SL_ANDROID_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM;
109 pcm.numChannels = numChannels;
110 pcm.sampleRate = sampleRate * 1000;
111 pcm.bitsPerSample = useFloat ?
112 SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16;
113 pcm.containerSize = pcm.bitsPerSample;
114 pcm.channelMask = channelMask;
115 pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
116 // additional
117 pcm.representation = useFloat ? SL_ANDROID_PCM_REPRESENTATION_FLOAT
118 : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
119 SLDataSource audioSource;
120 audioSource.pFormat = (void *)&pcm;
121 audioSource.pLocator = (void *)&bufferQueue;
122
123 /* Setup the data sink structure */
124 SLDataLocator_OutputMix locator_outputmix;
125 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
126 locator_outputmix.outputMix = mOutputMixObj;
127
128 SLDataSink audioSink;
129 audioSink.pLocator = (void *)&locator_outputmix;
130 audioSink.pFormat = NULL;
131
132 SLboolean required[1];
133 SLInterfaceID iidArray[1];
134 required[0] = SL_BOOLEAN_TRUE;
135 iidArray[0] = SL_IID_BUFFERQUEUE;
136
137 res = (*mEngine)->CreateAudioPlayer(mEngine, &mPlayerObj,
138 &audioSource, &audioSink, 1 /* numInterfaces */, iidArray, required);
139 if (res != SL_RESULT_SUCCESS) break;
140
141 res = (*mPlayerObj)->Realize(mPlayerObj, SL_BOOLEAN_FALSE /* async */);
142 if (res != SL_RESULT_SUCCESS) break;
143
144 res = (*mPlayerObj)->GetInterface(mPlayerObj, SL_IID_PLAY, (void*)&mPlay);
145 if (res != SL_RESULT_SUCCESS) break;
146
147 res = (*mPlayerObj)->GetInterface(
148 mPlayerObj, SL_IID_BUFFERQUEUE, (void*)&mBufferQueue);
149 if (res != SL_RESULT_SUCCESS) break;
150
151 /* Setup to receive buffer queue event callbacks */
152 res = (*mBufferQueue)->RegisterCallback(mBufferQueue, BufferQueueCallback, this);
153 if (res != SL_RESULT_SUCCESS) break;
154
155 // success
156 break;
157 }
158 if (res != SL_RESULT_SUCCESS) {
159 close(); // should be safe to close even with lock held
160 ALOGW("open error %s", android::getSLErrStr(res));
161 return INVALID_OPERATION;
162 }
163 return OK;
164 }
165
close()166 void close() {
167 SLObjectItf engineObj;
168 SLObjectItf outputMixObj;
169 SLObjectItf playerObj;
170 {
171 auto_lock l(mLock);
172 if (mPlay != NULL && mPlayState != SL_PLAYSTATE_STOPPED) {
173 (void)stop();
174 }
175 // once stopped, we can unregister the callback
176 if (mBufferQueue != NULL) {
177 (void)(*mBufferQueue)->RegisterCallback(
178 mBufferQueue, NULL /* callback */, NULL /* *pContext */);
179 }
180 (void)flush();
181 engineObj = mEngineObj;
182 outputMixObj = mOutputMixObj;
183 playerObj = mPlayerObj;
184 // clear out interfaces and objects
185 mPlay = NULL;
186 mBufferQueue = NULL;
187 mEngine = NULL;
188 mPlayerObj = NULL;
189 mOutputMixObj = NULL;
190 mEngineObj = NULL;
191 mPlayState = SL_PLAYSTATE_STOPPED;
192 }
193 // destroy without lock
194 if (playerObj != NULL) {
195 (*playerObj)->Destroy(playerObj);
196 }
197 if (outputMixObj != NULL) {
198 (*outputMixObj)->Destroy(outputMixObj);
199 }
200 if (engineObj != NULL) {
201 CloseSLEngine(engineObj);
202 }
203 }
204
setPlayState(SLuint32 playState)205 status_t setPlayState(SLuint32 playState) {
206 auto_lock l(mLock);
207 if (mPlay == NULL) {
208 return INVALID_OPERATION;
209 }
210 SLresult res = (*mPlay)->SetPlayState(mPlay, playState);
211 if (res != SL_RESULT_SUCCESS) {
212 ALOGW("setPlayState %d error %s", playState, android::getSLErrStr(res));
213 return INVALID_OPERATION;
214 }
215 mPlayState = playState;
216 return OK;
217 }
218
getPlayState()219 SLuint32 getPlayState() {
220 auto_lock l(mLock);
221 if (mPlay == NULL) {
222 return SL_PLAYSTATE_STOPPED;
223 }
224 SLuint32 playState;
225 SLresult res = (*mPlay)->GetPlayState(mPlay, &playState);
226 if (res != SL_RESULT_SUCCESS) {
227 ALOGW("getPlayState error %s", android::getSLErrStr(res));
228 return SL_PLAYSTATE_STOPPED;
229 }
230 return playState;
231 }
232
getPositionInMsec(int64_t * position)233 status_t getPositionInMsec(int64_t *position) {
234 auto_lock l(mLock);
235 if (mPlay == NULL) {
236 return INVALID_OPERATION;
237 }
238 if (position == NULL) {
239 return BAD_VALUE;
240 }
241 SLuint32 pos;
242 SLresult res = (*mPlay)->GetPosition(mPlay, &pos);
243 if (res != SL_RESULT_SUCCESS) {
244 ALOGW("getPosition error %s", android::getSLErrStr(res));
245 return INVALID_OPERATION;
246 }
247 // only lower 32 bits valid
248 *position = pos;
249 return OK;
250 }
251
start()252 status_t start() {
253 return setPlayState(SL_PLAYSTATE_PLAYING);
254 }
255
pause()256 status_t pause() {
257 return setPlayState(SL_PLAYSTATE_PAUSED);
258 }
259
stop()260 status_t stop() {
261 return setPlayState(SL_PLAYSTATE_STOPPED);
262 }
263
flush()264 status_t flush() {
265 auto_lock l(mLock);
266 status_t result = OK;
267 if (mBufferQueue != NULL) {
268 SLresult res = (*mBufferQueue)->Clear(mBufferQueue);
269 if (res != SL_RESULT_SUCCESS) {
270 return INVALID_OPERATION;
271 }
272 }
273
274 // possible race if the engine is in the callback
275 // safety is only achieved if the player is paused or stopped.
276 mDeliveredQueue.clear();
277 return result;
278 }
279
write(const void * buffer,size_t size,bool isBlocking=false)280 status_t write(const void *buffer, size_t size, bool isBlocking = false) {
281 std::lock_guard<std::mutex> rl(mWriteLock);
282 // not needed if we assume that a single thread is doing the reading
283 // or we always operate in non-blocking mode.
284
285 {
286 auto_lock l(mLock);
287 if (mBufferQueue == NULL) {
288 return INVALID_OPERATION;
289 }
290 if (mDeliveredQueue.size() < mNumBuffers) {
291 auto b = std::make_shared<BlobReadOnly>(buffer, size, false /* byReference */);
292 mDeliveredQueue.emplace_back(b);
293 (*mBufferQueue)->Enqueue(mBufferQueue, b->mData, b->mSize);
294 return size;
295 }
296 if (!isBlocking) {
297 return 0;
298 }
299 mWriteReady.closeGate(); // we're full.
300 }
301 if (mWriteReady.wait()) {
302 auto_lock l(mLock);
303 if (mDeliveredQueue.size() < mNumBuffers) {
304 auto b = std::make_shared<BlobReadOnly>(buffer, size, false /* byReference */);
305 mDeliveredQueue.emplace_back(b);
306 (*mBufferQueue)->Enqueue(mBufferQueue, b->mData, b->mSize);
307 return size;
308 }
309 }
310 ALOGW("unable to deliver write");
311 return 0;
312 }
313
logBufferState()314 void logBufferState() {
315 auto_lock l(mLock);
316 SLBufferQueueState state;
317 SLresult res = (*mBufferQueue)->GetState(mBufferQueue, &state);
318 CheckErr(res);
319 ALOGD("logBufferState state.count:%d state.playIndex:%d", state.count, state.playIndex);
320 }
321
getBuffersPending()322 size_t getBuffersPending() {
323 auto_lock l(mLock);
324 return mDeliveredQueue.size();
325 }
326
327 private:
bufferQueueCallback(SLBufferQueueItf queueItf)328 void bufferQueueCallback(SLBufferQueueItf queueItf) {
329 auto_lock l(mLock);
330 if (queueItf != mBufferQueue) {
331 ALOGW("invalid buffer queue interface, ignoring");
332 return;
333 }
334 // logBufferState();
335
336 // remove from delivered queue
337 if (mDeliveredQueue.size()) {
338 mDeliveredQueue.pop_front();
339 } else {
340 ALOGW("no delivered data!");
341 }
342 if (!mWriteReady.isOpen()) {
343 mWriteReady.openGate();
344 }
345 }
346
BufferQueueCallback(SLBufferQueueItf queueItf,void * pContext)347 static void BufferQueueCallback(SLBufferQueueItf queueItf, void *pContext) {
348 // naked native track
349 AudioTrackNative *track = (AudioTrackNative *)pContext;
350 track->bufferQueueCallback(queueItf);
351 }
352
353 SLObjectItf mEngineObj;
354 SLEngineItf mEngine;
355 SLObjectItf mOutputMixObj;
356 SLObjectItf mPlayerObj;
357 SLPlayItf mPlay;
358 SLBufferQueueItf mBufferQueue;
359 SLuint32 mPlayState;
360 SLuint32 mNumBuffers;
361 std::recursive_mutex mLock; // monitor lock - locks public API methods and callback.
362 // recursive since it may call itself through API.
363 std::mutex mWriteLock; // write lock - for blocking mode, prevents multiple
364 // writer threads from overlapping writes. this is
365 // generally unnecessary as writes occur from
366 // one thread only. acquire this before mLock.
367 Gate mWriteReady;
368 std::deque<std::shared_ptr<BlobReadOnly>> mDeliveredQueue; // delivered to mBufferQueue
369 };
370
371 /* Java static methods.
372 *
373 * These are not directly exposed to the user, so we can assume a valid "jtrack" handle
374 * to be passed in.
375 */
376
Java_android_media_cts_AudioTrackNative_nativeTest(JNIEnv *,jclass,jint numChannels,jint channelMask,jint sampleRate,jboolean useFloat,jint msecPerBuffer,jint numBuffers)377 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeTest(
378 JNIEnv * /* env */, jclass /* clazz */,
379 jint numChannels, jint channelMask, jint sampleRate, jboolean useFloat,
380 jint msecPerBuffer, jint numBuffers)
381 {
382 AudioTrackNative track;
383 const size_t frameSize = numChannels * (useFloat ? sizeof(float) : sizeof(int16_t));
384 const size_t framesPerBuffer = msecPerBuffer * sampleRate / 1000;
385
386 status_t res;
387 void *buffer = calloc(framesPerBuffer * numBuffers, frameSize);
388 for (;;) {
389 res = track.open(numChannels, channelMask, sampleRate, useFloat, numBuffers);
390 if (res != OK) break;
391
392 for (int i = 0; i < numBuffers; ++i) {
393 track.write((char *)buffer + i * (framesPerBuffer * frameSize),
394 framesPerBuffer * frameSize);
395 }
396
397 track.logBufferState();
398 res = track.start();
399 if (res != OK) break;
400
401 size_t buffers;
402 while ((buffers = track.getBuffersPending()) > 0) {
403 // ALOGD("outstanding buffers: %zu", buffers);
404 usleep(5 * 1000 /* usec */);
405 }
406 res = track.stop();
407 break;
408 }
409 track.close();
410 free(buffer);
411 return res;
412 }
413
Java_android_media_cts_AudioTrackNative_nativeCreateTrack(JNIEnv *,jclass)414 extern "C" jlong Java_android_media_cts_AudioTrackNative_nativeCreateTrack(
415 JNIEnv * /* env */, jclass /* clazz */)
416 {
417 return (jlong)(new shared_pointer<AudioTrackNative>(new AudioTrackNative()));
418 }
419
Java_android_media_cts_AudioTrackNative_nativeDestroyTrack(JNIEnv *,jclass,jlong jtrack)420 extern "C" void Java_android_media_cts_AudioTrackNative_nativeDestroyTrack(
421 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
422 {
423 delete (shared_pointer<AudioTrackNative> *)jtrack;
424 }
425
Java_android_media_cts_AudioTrackNative_nativeOpen(JNIEnv *,jclass,jlong jtrack,jint numChannels,jint channelMask,jint sampleRate,jboolean useFloat,jint numBuffers)426 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeOpen(
427 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack,
428 jint numChannels, jint channelMask, jint sampleRate,
429 jboolean useFloat, jint numBuffers)
430 {
431 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
432 if (track.get() == NULL) {
433 return (jint)INVALID_OPERATION;
434 }
435 return (jint) track->open(numChannels,
436 channelMask,
437 sampleRate,
438 useFloat == JNI_TRUE,
439 numBuffers);
440 }
441
Java_android_media_cts_AudioTrackNative_nativeClose(JNIEnv *,jclass,jlong jtrack)442 extern "C" void Java_android_media_cts_AudioTrackNative_nativeClose(
443 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
444 {
445 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
446 if (track.get() != NULL) {
447 track->close();
448 }
449 }
450
Java_android_media_cts_AudioTrackNative_nativeStart(JNIEnv *,jclass,jlong jtrack)451 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStart(
452 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
453 {
454 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
455 if (track.get() == NULL) {
456 return (jint)INVALID_OPERATION;
457 }
458 return (jint)track->start();
459 }
460
Java_android_media_cts_AudioTrackNative_nativeStop(JNIEnv *,jclass,jlong jtrack)461 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStop(
462 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
463 {
464 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
465 if (track.get() == NULL) {
466 return (jint)INVALID_OPERATION;
467 }
468 return (jint)track->stop();
469 }
470
Java_android_media_cts_AudioTrackNative_nativePause(JNIEnv *,jclass,jlong jtrack)471 extern "C" jint Java_android_media_cts_AudioTrackNative_nativePause(
472 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
473 {
474 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
475 if (track.get() == NULL) {
476 return (jint)INVALID_OPERATION;
477 }
478 return (jint)track->pause();
479 }
480
Java_android_media_cts_AudioTrackNative_nativeFlush(JNIEnv *,jclass,jlong jtrack)481 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeFlush(
482 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
483 {
484 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
485 if (track.get() == NULL) {
486 return (jint)INVALID_OPERATION;
487 }
488 return (jint)track->flush();
489 }
490
Java_android_media_cts_AudioTrackNative_nativeGetPositionInMsec(JNIEnv * env,jclass,jlong jtrack,jlongArray jPosition)491 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetPositionInMsec(
492 JNIEnv *env, jclass /* clazz */, jlong jtrack, jlongArray jPosition)
493 {
494 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
495 if (track.get() == NULL) {
496 return (jint)INVALID_OPERATION;
497 }
498 int64_t pos;
499 status_t res = track->getPositionInMsec(&pos);
500 if (res != OK) {
501 return res;
502 }
503 jlong *nPostition = (jlong *) env->GetPrimitiveArrayCritical(jPosition, NULL /* isCopy */);
504 if (nPostition == NULL) {
505 ALOGE("Unable to get array for nativeGetPositionInMsec()");
506 return BAD_VALUE;
507 }
508 nPostition[0] = (jlong)pos;
509 env->ReleasePrimitiveArrayCritical(jPosition, nPostition, 0 /* mode */);
510 return OK;
511 }
512
Java_android_media_cts_AudioTrackNative_nativeGetBuffersPending(JNIEnv *,jclass,jlong jtrack)513 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetBuffersPending(
514 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
515 {
516 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
517 if (track.get() == NULL) {
518 return (jint)0;
519 }
520 return (jint)track->getBuffersPending();
521 }
522
523 template <typename T>
writeToTrack(jlong jtrack,const T * data,jint offsetInSamples,jint sizeInSamples,jint writeFlags)524 static inline jint writeToTrack(jlong jtrack, const T *data,
525 jint offsetInSamples, jint sizeInSamples, jint writeFlags)
526 {
527 auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
528 if (track.get() == NULL) {
529 return (jint)INVALID_OPERATION;
530 }
531
532 const bool isBlocking = writeFlags & WRITE_FLAG_BLOCKING;
533 const size_t sizeInBytes = sizeInSamples * sizeof(T);
534 ssize_t ret = track->write(data + offsetInSamples, sizeInBytes, isBlocking);
535 return (jint)(ret > 0 ? ret / sizeof(T) : ret);
536 }
537
538 template <typename T>
writeArray(JNIEnv * env,jclass,jlong jtrack,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jint writeFlags)539 static inline jint writeArray(JNIEnv *env, jclass /* clazz */, jlong jtrack,
540 T javaAudioData, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
541 {
542 if (javaAudioData == NULL) {
543 return (jint)INVALID_OPERATION;
544 }
545
546 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL /* isCopy */);
547 if (cAudioData == NULL) {
548 ALOGE("Error retrieving source of audio data to play");
549 return (jint)BAD_VALUE;
550 }
551
552 jint ret = writeToTrack(jtrack, cAudioData, offsetInSamples, sizeInSamples, writeFlags);
553 envReleaseArrayElements(env, javaAudioData, cAudioData, 0 /* mode */);
554 return ret;
555 }
556
Java_android_media_cts_AudioTrackNative_nativeWriteByteArray(JNIEnv * env,jclass clazz,jlong jtrack,jbyteArray byteArray,jint offsetInSamples,jint sizeInSamples,jint writeFlags)557 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteByteArray(
558 JNIEnv *env, jclass clazz, jlong jtrack,
559 jbyteArray byteArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
560 {
561 ALOGV("nativeWriteByteArray(%p, %d, %d, %d)",
562 byteArray, offsetInSamples, sizeInSamples, writeFlags);
563 return writeArray(env, clazz, jtrack, byteArray, offsetInSamples, sizeInSamples, writeFlags);
564 }
565
Java_android_media_cts_AudioTrackNative_nativeWriteShortArray(JNIEnv * env,jclass clazz,jlong jtrack,jshortArray shortArray,jint offsetInSamples,jint sizeInSamples,jint writeFlags)566 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteShortArray(
567 JNIEnv *env, jclass clazz, jlong jtrack,
568 jshortArray shortArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
569 {
570 ALOGV("nativeWriteShortArray(%p, %d, %d, %d)",
571 shortArray, offsetInSamples, sizeInSamples, writeFlags);
572 return writeArray(env, clazz, jtrack, shortArray, offsetInSamples, sizeInSamples, writeFlags);
573 }
574
Java_android_media_cts_AudioTrackNative_nativeWriteFloatArray(JNIEnv * env,jclass clazz,jlong jtrack,jfloatArray floatArray,jint offsetInSamples,jint sizeInSamples,jint writeFlags)575 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteFloatArray(
576 JNIEnv *env, jclass clazz, jlong jtrack,
577 jfloatArray floatArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
578 {
579 ALOGV("nativeWriteFloatArray(%p, %d, %d, %d)",
580 floatArray, offsetInSamples, sizeInSamples, writeFlags);
581 return writeArray(env, clazz, jtrack, floatArray, offsetInSamples, sizeInSamples, writeFlags);
582 }
583