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