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>
34template <typename T> using shared_pointer = std::shared_ptr<T>;
35#else
36#include <utils/RefBase.h>
37template <typename T> using shared_pointer = android::sp<T>;
38#endif
39
40using namespace android;
41
42// Must be kept in sync with Java android.media.cts.AudioTrackNative.WriteFlags
43enum {
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
50class AudioTrackNative
51#ifndef USE_SHARED_POINTER
52        : public RefBase // android strong pointers require RefBase
53#endif
54{
55public:
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
67    ~AudioTrackNative() {
68        close();
69    }
70
71    typedef std::lock_guard<std::recursive_mutex> auto_lock;
72
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
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
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
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
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
252    status_t start() {
253        return setPlayState(SL_PLAYSTATE_PLAYING);
254    }
255
256    status_t pause() {
257        return setPlayState(SL_PLAYSTATE_PAUSED);
258    }
259
260    status_t stop() {
261        return setPlayState(SL_PLAYSTATE_STOPPED);
262    }
263
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
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
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
322    size_t getBuffersPending() {
323        auto_lock l(mLock);
324        return mDeliveredQueue.size();
325    }
326
327private:
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
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
377extern "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
414extern "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
420extern "C" void Java_android_media_cts_AudioTrackNative_nativeDestroyTrack(
421    JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
422{
423    delete (shared_pointer<AudioTrackNative> *)jtrack;
424}
425
426extern "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
442extern "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
451extern "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
461extern "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
471extern "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
481extern "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
491extern "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
513extern "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
523template <typename T>
524static 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
538template <typename T>
539static 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
557extern "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
566extern "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
575extern "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